文章目录

前言

本文是对王道计算机考研《计算机组成原理》课程的总结,主讲咸鱼学长讲的确实清晰。

王道考研《计算机组成原理》

由于我们学校已经开设过汇编和计算机体系结构,所以计组的笔记内容会比较精炼,高屋建瓴,不适合无基础人听。

如果有不理解的,可以回去看看我前面的CSAPP笔记和汇编语言笔记(不过我感觉还是没啥必要,我这篇文章更多的是总结性质,不适合入门学习)

CSAPP笔记:
第一卷:程序结构与执行——信息表示、指令、处理器、性能优化、储存层次
第二卷:在系统上运行程序——链接、异常控制流、虚拟内存
第三卷:程序间的交流与通信——系统级IO、网络编程、并发编程
汇编语言笔记:
汇编语言笔记——微机结构基础、汇编指令基础
汇编语言笔记——汇编程序开发、汇编大作业
汇编语言笔记——接口技术与编程

指令系统

第二章学了运算器,第三章学了储存器,第四章(指令系统)和第五章(CPU)就是要学控制器,后面67章学习IO。

指令系统概述

指令格式

在这里插入图片描述

  1. 地址码数量分类。需要注意,指令长度固定,所以地址越多,寻址能力越差,同时地址越多,其访存次数也会增加,学过流水线CPU的人应该会知道这会增加指令周期数量。比如三地址指令,取指1次,A1A2A3各一次,总共就是4次。
    在这里插入图片描述
  2. 指令长度和操作码长度通常放在一起说
    • 指令长度会影响取指时间,如下图。
    • 操作码长度影响译码灵活度与电路复杂度。长度固定,则译码电路简单,n位对应 2 n 2^n 2n条指令就可以,速度快;不固定就比较灵活,但是译码电路也复杂。
      在这里插入图片描述
  3. 操作类型如下。
    在这里插入图片描述

扩展操作码

扩展原理:通过前缀来区分不同的长度。判定的时候就像是一颗树一样:先判断前4为是否为1111,如果是1111,则判断中间4位,如果还是1111,继续判断,直到确定前缀是1111 1111 1111,那么指令就是0地址类型的。

在这里插入图片描述

本质上就是一个哈夫曼树,前缀匹配,和计网的CIDR子网划分异曲同工。CIDR中,IP分为网络号+主机号,而在这里,操作码=去掉地址码以后剩下的部分=前缀+指令实际可变部分,就是下图中去掉红色X后左边的部分。

和计网简直一模一样,实际CPU在识别指令的时候,会进行前缀匹配,匹配1直到遇到第一个0为止,通过1的数量确定是0地址还是123地址指令。这个判断过程遵循最长前缀原则,会尽量匹配长的前缀。

在这里插入图片描述

扩展操作码的本质:

用上一层操作码剩下来的组合,当做本层操作码可能的前缀。

如此就可以将两种操作码区分开来了,三地址操作码前缀为0000到1110,留下了1111当做二地址的扩展,那么只要非1111,则一定是三地址。

n代表一节的长度,这里为4,三地址留下了一个1111前缀,那么在下一层中,去掉一个地址并入操作码位数,就又会多出来 2 n 2^n 2n种组合

上一层每预留m个前缀,下一层总共可用的组合就是m× 2 n 2^n 2n

指令寻址方式

指令寻址

在这里插入图片描述

PC在取出指令的一瞬间(应该在译码阶段),就会结合指令长度进行自增变化,所以其实PC永远指向下一条指令,这就是顺序寻址

我们通常会写成PC=PC+1,然而自增没有那么简单:

  1. 如果编址不同,那么1会乘以一个倍数。一条指令长为一个字,假设字编址是+1,那么字节编址就得+2
  2. 如果指令长度不同,那么就是+n,n=指令长度,译码阶段可以得到这个信息,通过流水线转发到PC指针模块

除此之外,还有跳跃寻址。跳转指令的直接作用就是在执行阶段修改PC值,注意,此前PC已经自增一次了,所以修改PC值是基于自增结果的。即,目标相对于下一个指令的偏移量

这就是偏移的本质,无论PC在哪,目标=PC+(目标-PC),这就一定能跳过去,所以在偏移跳转的情况下,JMP后面跟的值是这个(目标-PC)的差值。本质上和直接跳没啥区别,可能就是电路的区别。做题的时候,两种情况都可能给出。

在这里插入图片描述

数据寻址

结合上学期学的汇编来看,指令寻址的方式是特别多的,如何区分一个地址是什么寻址方式呢?那就让地址=寻址特征+形式地址。其中,寻址特征表明了你是用什么方式去解读紧跟的形式地址的。

在这里插入图片描述

基本的寻址思路

假设我们要访问一个操作数A

立即寻址就是把A写在立即数,很明显位数受限

因此就要把他放在内存里,但是这样就要访存一次,即进行一次直接寻址
我不想访存,又不想受到位数限制,那你就放在寄存器里,即寄存器寻址

寄存器寻址其实就是减少一次访存,变成访问一次寄存器,同样的效果,用寄存器就可以减少一次访存,速度大大提升

上述过称再加一环,就是一次间接寻址,要访存两次。
同理,使用寄存器间接寻址可以减少一次访存。

下图给的访存次数忽略了取指的那一次访存。

在这里插入图片描述

偏移寻址

在这里插入图片描述

总结:

  1. 基址寻址。

    • EA=(BR)+A,BR为基,A为偏移
    • 其意义在于,可以让程序在内存中整体浮动而不影响指令地址码的解释。
    • 不论是BR还是通用寄存器,都是由操作系统管理的,所以这个寻址是计算机系统内部默认的寻址方式,会自动实现,不需要去写汇编指令
    • 附带的作用是可以扩大寻址范围,因为寄存器的位数更大
      在这里插入图片描述
  2. 变址寻址。

    • EA=(XI)+A,以A为基地址,XI为偏移,不断变化
    • 其意义在于,可以让用户可以便捷的编写循环指令
    • 实际中,因为基址寻址是OS默认的,所以实际上都是基址+变址复合的。
      在这里插入图片描述
  3. 相对寻址。

    • EA=(PC)+A,以PC为基,加上偏移(补码)
    • 其意义在于让代码段在可以内部浮动而不影响指令地址码的解释。
    • 注意,相对寻址不能保证数据的位置不变,为此需要将指令分段,分代码段,数据段。这样,代码段动就无所谓,数据段不可以动。
隐含寻址

在这里插入图片描述

指令中可以隐含操作数,不一定要用地址码指出。有的在寄存器(比如下面的ACC),有的在堆栈(比如push和pop指令)

在这里插入图片描述

堆栈的方向,一般是栈顶朝着0地址走,也就是说扩容是SP指针减小。
平时说的堆栈都是软堆栈,在我印象里,汇编语言中的浮点指令,有一些就使用的硬堆栈,是一组浮点寄存器。

在这里插入图片描述

程序的机器级代码表示

汇编基础

这一块因为我学过汇编,所以就过得很快。

ESI中,S=Source,I=Index。EDI中,D=Destination

需要注意的是,我似乎没有发现间接寻址的指令,实际上就是如此,因为间接寻址可以用寄存器间接寻址实现,有寄存器你不用,非要用间接寻址那不是傻吗。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

AT&T格式和Intel格式

在这里插入图片描述

需要注意一下偏移量的指令。我们前面学过基址寻址之类的,我们结合这片知识来理解一下汇编中的偏移量指令。

mov eax,[ebx+ecx*32+4]举例:

  1. 首先一个方框告诉我们,这是间接寻址,要去内存找数据
  2. 基址寻址默认就有,不需要显式写出来
  3. ebx是寄存器间接寻址的基本元素
  4. ecx其实应该用esi,这个是变址寻址,搭配一个放大因子,实际是比例变址
  5. 4的作用是进行元素内部的偏移

在这里插入图片描述

C语言控制结构的汇编表示

控制结构依赖于跳转指令,跳转指令和算术指令搭配使用。

算术指令,包括cmp之类的,会修改标志位(PSW,或者FLAG寄存器)。跳转指令读取标志位,进而决定是否跳转。

在这里插入图片描述
选择语句不需要你去写,你能看懂了这是个选择结构,并且知道哪一部分是IF,哪一部分是else就行了。if和else不一定哪个在前哪个在后,得仔细看看条件。

在这里插入图片描述
在这里插入图片描述

循环语句,for循环如下。while其实也类似,都可以写出来,会看就行。
实际应用中,loop语句更加简洁,可以直接实现for循环,loopz可以实现while循环。

在这里插入图片描述
在这里插入图片描述

函数调用

在这里插入图片描述

栈帧切换

我们且不论参数传递,局部变量,寄存器保护等栈帧具体操作,我们只关注一下调用和返回过程中,栈帧整体是怎么切换的。

首先要知道三个点:

  1. ebp。指向栈帧底部,这个位置内储存着上一个栈帧的esp旧值
  2. esp。指向栈帧顶部
  3. IP旧值。在上一个栈帧的顶部。

切换过程如下:

  1. call的时候,上一个栈帧会提前做好足够的准备,然后push一个IP后进行跳转。
    在这里插入图片描述
  2. 跳转后的第一件事情就是执行enter,把ebp旧值保存(push ebp),切换新值(mov ebp,esp),此后就是新栈帧的具体操作了。
    在这里插入图片描述
  3. ret之前,需要执行leave指令。在此之前,要先回退esp(mov exp,ebp),然后恢复ebp。说来神奇,就是新栈帧的ebp里面保存着旧栈帧的地址,所以回退毫无压力。
  4. 最后ret,恢复IP值
栈帧构造

在这里插入图片描述

  1. 上一层栈帧基址(ebp旧值)
  2. 寄存器保护。这个就暂且不论
  3. 定义局部变量。倒着来,靠后定义的,先压栈。
  4. 对齐。因为栈帧是16B的倍数,所以中间要填充一点
  5. 栈内传参。倒着来,参数列表中靠后的,先压栈。
    • 你会发现,这两个东西都是倒着压栈的,这是因为栈是倒着的,所以负负得正。结果就是,程序逻辑中,靠前的东西,其地址也就更小,很顺,便于我们小端法机器的数据读取。
    • 传参还可以通过寄存器
  6. IP旧值

下图为调用的具体全过程

在这里插入图片描述

CISC与RISC

在这里插入图片描述

  1. RISC的运算都是用寄存器,访存需要单独的指令加载到寄存器。
    • 如果一个运算指令里面有访存操作,那么一定是CISC。
    • 正因此,RISC寄存器特别多
  2. CISC就好比是封装好的类库,RISC就是原始的C语言。CISC开发效率高,但是不易优化,RISC更快,容易优化
  3. RISC的指令时间差不多,所以是一定会用流水线的。

中央处理器

在这里插入图片描述

CPU的功能和基本结构

在这里插入图片描述

运算器的基本结构

在这里插入图片描述

运算器内部,以ALU为核心,辅助有寄存器组与控制器件,他们需要和ALU连接,而最直接的思路是使用专用数据通路

为了不出现冲突,需要搭配多路选择器或者三态门实现同一时间只有一个寄存器的输入输出。三态门可以通过信号控制数据流向,以及是否可以流通,是很常用的控制器件。

这种方式缺点是成本大,而且线太多,布线很难。

在这里插入图片描述

解决的办法是使用CPU内部的单总线

给寄存器输入一个out信号,那么就可以把数据送到总线上,输入in信号,就可以把数据放到寄存器里。虽然一根总线可能会导致数据冲突,但是只要搭配暂存寄存器以及控制信号,就不会有问题。

重点理解一下暂存寄存器的逻辑。为什么要暂存?本质就是,ALU是一个组合逻辑器件,是实时计算的,必须保证两端同时有稳定的输入,才能给出稳定的结果。但是我们的数据总线同时只能有一个数据,所以另一个稳定数据得寄存器给出。

从下图可以看出,如果让ALU的输入和总线直接相联,那么在总线输入变化的一瞬间,ALU的输入也会跟着同步变化。我们把一个数据送到总线上后,要先把这个数据存起来,才能把另一个数据送到总线上,不然原来的数据会被破坏。同理,输出的数据,在放到总线前,肯定也要先暂存,等总线上的数据无效了,我们再把这个数据放上去。

具体到运算器,输入端的暂存寄存器,用于暂存来自总线的一个数据。输出端的暂存寄存器,用于暂存计算的最终结果,增加了移位功能后,变成了移位器。举个例子ADD R0,dword ptr[100h]。这个例子需要三步:

  1. CPU给访存信号,总线数据=来自主存的数据,使用时钟触发暂存寄存器,将数据暂存
  2. CPU给R0一个out信号,总线数据=寄存器R0,此时ALU的输入准备就绪
  3. ALU计算结果。此时如果没有移位寄存器的隔离,其不稳定的输出会干扰总线上的R0数据。
  4. 时钟触发移位器,暂存结果,总线数据=结果
  5. CPU给R0一个in信号,寄存器R0=总线数据

可以看到,总线上同时只允许一个数据存在,所以需要严格使用寄存器暂存,配合时钟来实现有条不紊地计算。

以上过程,虽然是5步,但是实际上只有两个时钟周期,一个用于访存,一个用于计算结果的输出,剩下的操作都是CPU发出信号,穿插在其中

在这里插入图片描述

再说其他的寄存器:

  1. ACC(累加寄存器),用于存放运算的中间结果。注意,ACC不是暂存寄存器,因为其和ALU不直接相联,中间有三态门进行控制,和其他的寄存器组是平级的。
  2. PSW(程序状态寄存器),又叫flag寄存器,作为指令执行的决策依据
  3. 计数器。常常和移位器搭配,实现乘除运算,控制乘除运算中的计算次数。

控制器的基本结构

整体连线如下图。

在这里插入图片描述

指令寄存器(IR)储存指令。

译码器(ID)负责翻译操作码,翻译结果送到微操作信号发生器里执行,这是控制器的核心。

微操作,其实是若干组控制信号,分批次送出。CPU里的组合逻辑早就摆在那里了,你只需要控制哪些数据可以导通,就可以产生对应的效果,结合时钟,就可以分几步执行完一套微操作。微操作发生器需要很多辅助信息:

  1. 译码器翻译结果。这个信息告诉微操作发生器我要做什么事情,比如je指令,我要去跳转,修改PC。
  2. 标志位辅助微操作发生器决策,比如je指令,我是要不要跳呢?
  3. 时钟,控制执行的节拍,因为总线只有一根,微操作要分几步有条不紊地执行,这就需要时钟来控制对外信号的切换了。

微操作发生器,以一定的步骤向外发出指挥信号,外面的电路导通,执行,得到结果。比如我们要访存,那就让AdIRout=1,MARin=1,此时就是把我们的地址码送到了MAR里,来一个时钟就可以进行访存了。

CPU整体结构

在这里插入图片描述

CPU:

  1. 运算器
    • ALU
    • 辅助寄存器与辅助电路
  2. 控制器
    • CU(译码器+微操作信号发生器)
    • 辅助寄存器与辅助电路
  3. 内部总线与外部总线,中断系统

换一种表示方式如下图,CPU=ALU+CU+寄存器+中断系统。

寄存器中,上图橙色标出的是用户可见寄存器,凡是用户可以通过指令直接修改的,都算。比如cmp指令修改PSW,jmp修改pc,其他那几个更不用说,直接mov。

在这里插入图片描述

指令执行过程

在这里插入图片描述

指令周期

结合流水线的知识,我们知道CPU的执行其实是可以划分阶段的,理论上,每个阶段应该时间差不多,这样才不会出现浪费。

我们不论5级流水线,但从指令执行时间来看,其实取指是很费时间的,要访存,译码反而只是一个组合逻辑,所以取指+译码放一起。执行可能很复杂,所以单独成立一个阶段,至于附带的写回,时间也很快。

注意,这个阶段不是时钟周期,要区分三个概念:指令周期,CPU周期(机器周期),CPU时钟周期(时钟周期)

一般来说,一个指令周期内部,机器周期数量不等。一个机器周期内部,时钟周期数量不等。

在这里插入图片描述

  1. 空指令只占一个机器周期,通常时间周期数量也比较少,就是用来控制步调。
  2. 取指周期+执行周期。加法指令比较常规
  3. 乘法指令执行时间更长,所以执行周期比较长
  4. 间址周期。间接寻址因为多一次访存,所以给访存单独设置一个机器周期
  5. 中断周期。正常来说,都会留一个时间检查中断,除非CPU被OS关了中断。

在这里插入图片描述

把上面这几类指令结合起来,变成所有指令通用的流程图,如下:

在这里插入图片描述
使用4个触发器储存当前处于的状态,是one-hot编码,含义如下:

  1. FE:FEtch
  2. IND:INDerect
  3. EX:EXecute
  4. INT:INTerrupt

总的来说,只要分给一个机器周期,就都可能有访存周期,这个是操作中最费时间的。不同周期的访存目的不同,但是都有可能。

指令周期的数据流

取指周期:

  1. 先送地址(地址线)。这一步要一个周期,触发MAR,把MAR的值更新为PC的值
  2. 再读取。分一个来回,CU一个周期就可以完成
    • 送控制信号(控制线)
    • 回传数据(数据线)
  3. 接收+复制。这一步两个时钟周期
    • 触发MDR寄存器保存总线上的值
    • 触发IR寄存器,保存MDR传来的数据
  4. PC自增。CU一个周期

总的来说,要5时钟周期(我猜的)

间址周期:

  1. 更新MAR。一个时钟周期
    • MDR可以提前传送到MAR中。
  2. 再读取。分一个来回,CU一个周期就可以完成
    • 送控制信号(控制线)
    • 回传数据(数据线)
  3. 接收。一个周期
    • 触发MDR寄存器保存总线上的值
  4. 修改地址。一个周期,这个可选。

在这里插入图片描述

执行周期比较复杂,无统一数据流。

直接看中断周期,中断周期的执行逻辑和call指令一样:

  1. 确定写地址,送到MAR。看起来是两步,其实都可以有CU一步完成。
  2. 确定写内容,PC值送到MDR,MDR的数据上数据线
  3. CU发出写信号,信号上控制线,内存写入
  4. 调用中断处理程序,修改PC值,这个值从中断向量表里来。

在这里插入图片描述

指令执行方案

  1. 单指令周期比较简单,大家的周期数都一样,浪费就浪费了。
  2. 多(种)指令周期电路复杂,但是效率高
  3. 流水线是现在的主流,我感觉其实也结合了多指令的思想。具体很复杂,后面会讲。

在这里插入图片描述

数据通路的功能和基本结构

在这里插入图片描述

数据流动方向整体有三类:

  1. 寄存器之间
  2. 寄存器和主存之间
  3. 寄存器和ALU之间

这些数据都要经过数据通路。有三种方案:

  1. 单总线。部件挂载到总线上
  2. 多总线
  3. 专用数据通路。两个部件之间直接相联。

单总线结构

区分一下:

  1. 内部总线。CPU内部的单总线
  2. 系统总线。链接计算机整体的总线,CPU,内存,通道,IO等各种部件都挂载上面,比如我们前面说的数据总线和控制总线。

下面给出三种数据流的执行情况。

写题的时候,两个注意点:

  1. 主要写两个东西,左边一列是数据流,右边一列是控制信号和注解。
  2. (PC)指的是PC寄存器的内容,如果是要取内存中的东西,那就要((R0)) 这样去做了。当然这个不是特别重要,因为这里也不是按照指令写。可以粗暴的理解为,多加一层括号

在这里插入图片描述

逐一解释:

  1. 寄存器那个比较简单。
  2. 主存和CPU。在CU发出控制信号的时候,其实还缺一个MARoutE有效。在读入的时候,应该是MDRinE有效,因为是从系统总线读入的。
  3. 寄存器和ALU的算术加法。这个CU加命令,其实更像是触发移位器的时钟,因为ALU是个逻辑器件,在上一步完成的时候就开始计算结果了,我这个加命令,只是让移位器把ALU的结果储存起来罢了。

来个例子,MemR就是我们CU给出的读取信号。PC自增可以直接写到访存取指那个地方,因为只要访存了,原来的PC就没用了,同步自增就好。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

专用通路结构

单总线同一时间只能进行一个数据的传输,而多总线结构可以同时进行n个数据的传输,只要这些数据在逻辑上可以并行传输,多总线就可以做到。

此外,想实现这种并行传输,专用通路结构也是一种思路,任何两个需要流动数据的部件之间,都加一条通路,通过信号控制是否流通。很朴实很粗暴的思路,编写命令也很简单,缺点是线太多了,而且不好布线。

一个例子如下。如果要进行加法指令的话,除了取数据的一轮指令以外,还有就是激发C7,C6,把数据送到ALU,然后再触发寄存器储存结果。

在这里插入图片描述

更多的例题,后面再讲,暂时略过,TODO

控制器的功能和工作原理

在这里插入图片描述

控制器是如何实现控制的呢?

假设已经铺好数据通路,那么控制器说白了就是要在特定指令的特定机器周期的特定节拍,发出特定的控制信号。

只要信号按照逻辑给出,CPU内部的电路就会按部就班地处理数据,执行指令。所以关键在于,在什么时机给出控制信号,以及控制信号以什么形式给出。

硬布线控制器和微程序控制器是两种思路。前者是纯粹使用硬件实现,后者要搭配ROM,以储存程序的思路去实现。

硬布线控制器

在这里插入图片描述

硬布线控制器的思想

首先思考一下时机问题。当前发出什么微命令,由指令操作码,机器周期,节拍信号,机器状态条件综合给定。

0-k号微命令,每个微命令会有一个单独的布尔函数

C i ( 操作码,机器周期,节拍信号,机器状态 ) C_i(操作码,机器周期,节拍信号,机器状态) Ci(操作码,机器周期,节拍信号,机器状态)

所以有可能会出现两三个微命令并行执行的情况,也就是我们的微指令。

在这里插入图片描述

怎么确定这个布尔函数呢?直观的举个例子。首先,可以看到布尔函数由3大部分组成,分别对应3个机器周期(其实最多可能有4个)。每一个部分,都要参考节拍信号和状态信号(图中没给出),最后在括号里补上指令操作码。

可以看出,一个布尔函数要考虑4个方面的信号,因此需要先列出一张表,罗列出每个指令在每个机器周期的每个节拍都有可能执行哪个微命令,也就是写一张自变量到所有微命令的综合真值表,最后变成若干布尔函数。

在这里插入图片描述

具体要一步一个脚印:

  1. 顺序列出所有指令在所有机器周期可能的微操作(不考虑划分节拍)
  2. CPU控制方式,我们选择定长周期,每个3周期节拍
  3. 使用第2步规定的节拍数量,去划分第1步列出的微操作
  4. 写表,把每个微操作的表达式综合出来

在这里插入图片描述

微操作罗列

这一步只管列出就行,按顺序,不用管到底需要几个时钟周期。

取指和间址周期中断周期都是一样的,而不同指令的执行周期也各不相同

在这里插入图片描述

时序安排

三个原则:

  1. 微操作先后顺序不得随意更改
    • 有前后关系的微操作顺序必须保持不变。比如PC->MAR,M(MAR)->MDR
  2. 被控对象不同的微操作,尽量并行,比如(PC)-> MAR,1->R
  3. 不太消耗时间的微命令放在一个时钟周期内,允许有先后顺序

在这里插入图片描述

执行周期的时序安排如下,看看得了,不用记。

在这里插入图片描述

操作时间表与微操作信号综合

理论上要一个大表,实际上可以按照机器周期分开。

先看FE阶段:

前面的就正常,最后两行是额外的。有的指令,FE完了就直接EX,但是有的还需要先进入间址周期IND,因此在FE表的最后两行还加上了对于机器周期状态的修改微命令,修改的原则就是看I状态位,而I状态位又取决于指令的地址特征位

对于非访存指令,就不会有间址周期,而对于可能访存的,如果其I位为1,那么就要发出进入间址周期的命令。

在这里插入图片描述
再看IND阶段:

非访存指令没有IND,所以操作时间表里没有1。而访存指令就都有。注意最后一行,IND阶段是否要转入EX阶段,要看间接寻址过程是否已经完毕,有的指令可能会进行多次间址。只有等IND位=0,我们才能转入EX阶段。

在这里插入图片描述

再看EX阶段:

在这里插入图片描述

最后,如何从这三张表里,综合出一个微操作对应的布尔函数呢?

看下图,三个表,对应三大项。每一项的格式=机器周期·节拍·条件·(若干指令)

最后可以进行化简,这就是一个布尔函数。如此,把所有布尔函数都写出来,整体来看,就是若干输入,若干输出,最后进行电路综合就可以。

在这里插入图片描述

微指令控制器

在这里插入图片描述

微程序思想

前面的硬布线,是将控制逻辑固化在硬件里了,通过输入实时产生输出。我们可不可以提前把输出储存在ROM里,只要给一个输入,我们就把对应的输出逐个提取出来执行呢?这就是微程序的思想。

程序>指令=微程序>微指令>微命令=微操作

宏观上,一个软件程序,执行的过程就是CPU和内存的交互,CPU一条一条取出内存的指令,执行。对于一条指令来说,它也是一个微程序,CU内部也有类似于CPU的东西,把一条一条的微指令从ROM取出,输出控制信号。

控制信号储存在微指令的操作控制字段,具体怎么存,怎么解释,就是后话了。而那个顺序控制字段,是为了便于微指令逐条执行以及跳转的,也是后话。

在这里插入图片描述

微程序控制器的结构与工作原理

在这里插入图片描述

下图为基本结构:

在这里插入图片描述

CU相当于一个微型计算机。整体来看,CU外部给一个指令操作码OP,然后经过内部的运算,按照节拍分批输出控制信号。

CU内部相当于一个微缩版本的CPU与内存,属实是套娃了:

  1. 首先看,CM——控制储存器,里面以微指令的格式储存了控制信号。每条指令对应一段的微指令(一个微程序)。
  2. CMAR(μPC),地址译码,CMDR,这三个部件负责与CM进行直接交互
  3. 微地址形成部件,负责根据OP给出这条指令的微指令段首地址
  4. 顺序逻辑,负责结合标志位和时钟来决定下一条微指令的地址,是顺序还是要跳跃。

实际上,因为每个指令的取指,间址,中断的微指令段都是一样的,不如就公用,然后EX段各不相同,所以实际上CM的结构如下:

在这里插入图片描述

不考虑取指,单纯讨论EX微指令段具体的执行过程,则流程如下:

  1. 给定了OP,通过微地址形成部件获取EX微指令段首地址,经过顺序逻辑送到CMAR中
  2. 取微指令:CMAR,译码,从CM中取出微指令到CMDR中
  3. 解析CMDR中微指令:
    • 操作控制字段经过解析后输出
    • 下地址送到顺序逻辑中,分析得出下一个CMAR
  4. 2,3步循环,直到执行完当前指令EX微指令段所有微指令。

其实其他段,也是类似的执行过程,感觉就是一个有限自动机,就是个小CPU。熟悉了EX段以后,我们宏观上梳理一下连续执行若干条指令的过程中,这几个段如何切换:

  1. 取指周期(FE)
    • 刷新IR。这个周期是最先开始的,FE段的微指令逐条执行,送出一个又一个信号,执行完毕后,IR会被刷新成为最新的指令。
    • 确定EX段首地址。IR刷新后,OP也已经准备好,微地址形成部件此时就产生了该指令对应的EX段首地址,但是此时不一定马上就跳到EX了,还需要确认是否要IND。
    • 跳转EX或者执行IND。取指周期结束后,有两种可能,一种是直接到EX,另一种是先执行IND,之后再跳到间址周期。实际上,取指周期结束后,我们把下地址送到顺序逻辑后,顺序逻辑会根据标志位决定下一个CMAR,是IND微指令段首地址,还是OP对应的EX微指令段的首地址。
  2. 间址周期(IND)
    • 这一步有可能不被执行,但是无论是否执行了IND,执行路径一定是FE->(IND)->EX
  3. 执行周期(EX)
    • 执行操作。按前面所说,执行完EX段的微指令
    • 跳转FE或者执行INT。EX段的最后一个微指令的下地址是0。此时顺序逻辑会根据中断信号,决定是跳转到FE还是INT
  4. 中断周期(INT)
    • 理论上是

总的来说,就是FE刷新OP,然后用新的OP执行EX,之后再跳到FE刷新OP,执行EX······执行路径为->FE->(IND)->EX->(INT)->

在这里插入图片描述

最后讨论一下微程序个数的问题。

我们最开始觉得,一个指令对应一个微程序,一个微程序就是一段。但是实际上,我们把微程序的公用部分拆出来放在前面,构成3个额外的微程序,所以理论上应该是至少n+1个微程序,考虑到间址周期和中断周期,那么就是n+3个微程序。

为了兼容前面的说法,我们从物理和逻辑的角度理解微程序:

  1. 物理上,下图有n+3个微程序,一段就是一个微程序
  2. 逻辑上,一条指令的微程序=FE+IND+INT+EX这4段的总和

所以,一条指令确实对应一条微程序,但是物理上来看,微程序个数不等于指令数。

在这里插入图片描述

微指令的设计

在这里插入图片描述

一个微程序有多条微指令组成,一条微指令由多个相容的微命令(微操作)组成
微指令的格式主要就是操作控制这个字段如何设计,如何去解释为微命令输出到CPU?

总的来说,分为三种思路,水平型就类似于CISC,一条微指令多个操作,而垂直型类似于RISC,通过多条微指令组合构成复杂操作

在这里插入图片描述

水平型微指令

在这里插入图片描述

直接编码很简单粗暴,使用nbit来分别表示n个微命令,需要哪个微命令,哪个位就是1。

缺点是控存又大又贵,INTEL的控存就很大,如果用直接编码就会更大,所以应该简化。简化思路就是,有一些微命令不可能同时出现(互斥的微命令),那就把他们放到一起用译码器解析。

每一个译码器可以表达k+1种状态,k个互斥命令,1指的是啥也不干的状态

在这里插入图片描述

下图为两种思路的对比。

在这里插入图片描述

微指令的地址形成方式

给定一个下地址,如何生成下一条CMAR呢?

  1. 跳转:通过转移方式字段声明,转移类的指令才会有转移方式字段,一般指令没有。
  2. 顺序:其实测试网络就是前面说的顺序逻辑,指针自增
  3. 初始化:硬件产生的入口地址,就是说CU的执行虽然是一个循环,但是也应该有一个开端,那就是从#0号地址开始执行。

在这里插入图片描述

微程序控制单元的设计

在这里插入图片描述

首先要分析微操作序列,然后安排节拍。这就行了,后面就和硬布线完全不一样了。微程序不需要你去写表,不需要综合,每个指令互不干扰,而硬布线是互相影响的,你直接把每个指令安排好的节拍写进CM的微程序段就可以。

举个例子:

对于取指微程序,先调整顺序,写出节拍安排,之后插入两类特殊节拍:

  1. 根据微指令下地址确定下一条微指令地址
  2. 根据指令的OP确定EX段首地址。
    • 这一步也可能写成OP(IR)->微地址形成部件->CMAR,毕竟连续的短暂操作是可以自由拆分的。

显然,微程序比硬布线多了一倍的节拍,确实慢多了。

在这里插入图片描述

EX和FE的类似,每条微指令后同样要插入一个通过下地址字段获取CMAR的节拍。

在这里插入图片描述

做完以上工作后,就需要确定微指令格式

  1. 微指令格式=操作码+下地址
    1. 确定操作码。假定操作码使用字段直接编码格式,具体分配几个字段,每个字段多长,最后算出来操作码的字长。
    2. 确定下地址。根据CM中所有微程序的总长度确定下地址又用多少位编码
  2. 微指令字长=操作码字长+下地址字长

最后编写微指令码点,写到CM中。

在这里插入图片描述

硬布线和微程序的对比

在这里插入图片描述

硬布线控制器是一个逻辑器件,所以一个节拍对应的微操作控制信号马上产生。微程序是储存起来的,需要消耗比硬布线多一倍的时钟周期读取,所以慢很多。

在这里插入图片描述

虽然微程序更慢,但是其编写难度小,而且易于扩充,比如下面说的EPROM就可以实现轻松的扩展指令集。而且微程序还可以继续套娃,变成毫微程序,不过感觉比较麻烦了。

在这里插入图片描述

指令流水线

在这里插入图片描述

流水线基本概念

在这里插入图片描述

同一时间,不同指令使用不同功能部件,所以可以同时有多条指令执行。

下图给出了总耗时估算的思路:

总耗时=前n-1条指令执行时间(流水线模式)+最后1条指令(非流水线)

在这里插入图片描述

流水线的表示方法有两种,过程图和时空图看起来有点像。

过程图仅仅代表时间顺序,是横着看的,一条横线代表一条指令的全过程,其长度就是这条指令的生命周期。过程图又叫甘特图。

时空图类似于os里面访问内存页置换算法FIFO模式那个图。

我们是竖着看的,用一道竖线,竖线上对应CPU的各个部件的占用情况,竖线从左往右移动,代表着时间推移,可以看到,随着时间推移,不断有新的指令从下面进来,一条指令所处的阶段在不断地被推上去,直到推出去。

在这里插入图片描述

过程图也就看一乐,使用时空图才便于计算流水线性能指标,有吞吐率,加速比,效率。

时空图中,总时间=装入时间+(中间时间+排空时间)。装入时间只执行一条指令,而其他时间都是每个时钟周期执行一条指令。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

指令流水线的具体情形

首先要理解流水线的结构:周期一致,使用流水线寄存器暂存结果,时钟触发后流水线整体前进一次。具体数据通路需要熟悉。

假设cache每次都命中,这样就不会出现因为访存导致的流水线断流。

在这里插入图片描述

指令流水线影响因素

影响指令流的因素和解决方法大致如下,之后一一解读:

在这里插入图片描述

首先是资源冲突。说白了就是每一个CPU资源都是临界资源,是互斥的。对于寄存器的资源冲突,采取延迟指令的方法解决。对于取指和访存的资源冲突,采用资源冗余的方法解决,分别弄一个储存器,互不干扰。

在这里插入图片描述

数据冲突。这个又叫做数据冒险,学过体系结构的同学会很熟悉,当时可比这个复杂多了,数据旁路是最常使用的一种方法。

对于阻塞方法,空指令NOP是编译器插入的,CPU只需要顺着执行指令流就可以,是软件插入。bubble是cpu插入的,CPU检测到数据冒险则开始暂停。

最后,编译优化其实就是循环展开,调整指令顺序是循环展开的基本思想,搭配资源冗余可以实现k×k路展开。

在这里插入图片描述

控制冲突。比如一条条件转移指令,他的比较结果只有在EX阶段完毕,进入MEM阶段的时候才能得到,那么到此为止流水线已经装入3条指令,这三条指令可能就是两个分支的其中一支。

如果预测对了,那么就不需要额外操作,如果错了,就需要给这三条指令加bubble消除的同时再装入正确的指令。

为了提高这个过程的效率,就有很多方法用于辅助,有提高预测准确率,有加快条件码生成,有把两个分支的指令全部预取。

在这里插入图片描述

指令流水线分类与多发技术

这一章比较抽象,理解理解得了。

在这里插入图片描述

流水线按照使用级别分类,就是说结构上的细分程度。我们前面说的五段式流水线,是在CPU级别上划分流水线,往细了我们还可以在部件功能级划分,就是把一个部件内部划分为多个阶段,往宏观了还可以在处理机间划分流水线,不同处理机分工实现流水线。

流水线按照完成功能分类,就是说它能做的事情。单功能流水线功能固定,每次执行用的部件都是一模一样的,比如我只支持浮点数加法。而指令流水线就是多功能流水线,可以执行不同的指令,对应的部件各不相同。

流水线按照连接方式分类,就是说不同部件是否可以执行不同任务。单功能流水线肯定是静态流水线。多功能是动态的,比如取指阶段执行的是浮点加法指令,而访存阶段执行的是写内存指令。

流水线按照有无回路分类。我们的数据旁路流水线就是有回路的。

再来说一下多发技术,多发技术基于我们前面说的流水线分类。

首先是超标量,这是并发执行技术,基于空间上的资源冗余。

不可以调整两束超标量指令之间的执行顺序,但是可以调整一束超标量指令内部的执行顺序,反正是并行的,有的CPU就支持乱序发射。

在这里插入图片描述

超流水技术其实是将一个机器周期分段,比如EX周期内部分成3个时钟周期。和超标量技术对标,一个是空间的冗余,另一个是时间上的重复利用。

在这里插入图片描述

最后就是这个超长指令字,非常抽象,是编译时进行的优化。比如编译器发现有三条指令的EX阶段可以放在一起并行执行,那么就把这三条指令合并成一条超长指令,然后超长指令字CPU就会在EX阶段把任务分配给三个EX部件。

在这里插入图片描述

五段式指令流水线精讲

精讲指的是,我们要结合一些指令,具体研究其执行和数据流动过程,所以下面这个图中的锁存器要更加清晰,
梦回流水线CPU设计的那段难过的日子:

  1. IF段对应指令寄存器
  2. ID段对应AB和imm这三个锁存器
  3. EX阶段对应Store和结果锁存器
  4. M阶段有一个访存结果锁存器,还有一个锁存器储存EX阶段的计算结果,看似冗余,实则是为了对齐时钟周期。

在这里插入图片描述

接下来依次解读不同的指令:

运算类指令。如下图,在MIPS指令集中,运算类指令里面不存在内存的直接寻址,只允许用立即数和寄存器,运算指令和内存毫无关系,所以M阶段一定是空段。

但是注意,M阶段虽然是空段,但是并不代表啥都不干。M阶段的周期中,数据从EX阶段的ALU结果锁存器流入M阶段的一个锁存器,这个锁存器就是用来对齐时钟周期的。

在这里插入图片描述

再看LOAD和STORE指令,在RISC指令集中,只有这俩指令和内存有关系。这里默认DataCache一定命中了。

LOAD指令是唯一一条用满5个部件的指令,其他指令都有空的。

在这里插入图片描述

STORE指令中,Store锁存器终于派上了用场。在EX周期内,A和imm送到ALU计算,B送到Store锁存器中。之后M阶段写入内存就可以。

STORE指令没有W周期,不需要修改寄存器的值。

在这里插入图片描述

涉及到PC的指令比较特殊,条件转移指令和直接跳转指令都是不需要写回寄存器的,只需要把PC改过来以后,后面的阶段就都不需要操作了,本质上是一样的。

那么就有两个问题:

  1. 分支的PC值在哪里计算?
    • 不在ALU,而是在PC内部有分支模块用于计算下次可能的PC值,不需要经过ALU
  2. 什么时候写回PC?
    • 写回PC用到WrPC模块,用于选择下次的PC值到底是哪个。这个模块独立于5个阶段,只要PC值选哪个确定了,就可以进行选择。因为WrPC很快,所以可以嵌入任何一个阶段的初期。
    • 所以对于条件转移指令,在EX阶段比较出结果后,就可以在M阶段进行写回PC。对于直接跳转指令,在ID阶段取出imm后,就可以在EX阶段写回PC

在这里插入图片描述
在这里插入图片描述

最后来一道例题:

问题1比较简单,就是数据相关。问题二涉及到流水线的结构本身,即锁存器。

在ID阶段插入bubble的时候,不可以取新指令,防止指令寄存器被覆盖。只有等当前指令ID阶段结束后,下一条指令才可以进来。

有兴趣可以回顾一下体系结构那篇对应的文章,会有更深入的理解(现在我是看不动了)。

在这里插入图片描述

多处理器

多处理器的基本概念

在这里插入图片描述

在这里插入图片描述

SISD是最简单的,就是我们前面说的CPU。

那这个I和D的意义是什么呢?指令流和数据流,指令流就是一串连续的指令序列,数据流同理。

为什么要强调连续呢?假如一个单进程双线程CPU核心执行两个任务,两个线程对应两串指令序列,但是他们是交错运行的,同时仍然只是一串在执行,所以仍然是SISD。

什么叫数据流的连续呢?就是说来源于一个储存器的,就是一条数据流。

我们顺着推导SIMD,既然是MD,那么就肯定有多个储存器,这才能产生多个数据流,这些储存器是局部储存器,每个局部储存器都对应一个ALU,MDR,MAR。

这些运算部件与储存部件,被一个CU整体管控,共享一个主储存器。

后面说的向量处理器,其实是SIMD的一个加强版。

在这里插入图片描述

MIMD其实主要就是现代的CPU,多核处理器。L1L2缓存属于核心,而LLC(一般是L3)缓存所有核心共享。

在这里插入图片描述

MIMD除了多处理器系统外,还有一种多计算机系统。多计算机系统一般用于分布式场景。

其区别在于,LOAD指令只能访问当前计算机的储存,而要想访问其他计算机,就要先把数据传到当前计算机的主存。

在这里插入图片描述
至于MISD,这种计算机不存在。

多处理器的补充概念

除了基本的四种处理器以外,还有一个向量处理器,向量处理器是SIMD的进阶。

在这里插入图片描述

这种处理器是比较高级的,ALU是向量的,寄存器组也是向量的,指令也是向量指令。因为数据读写量大,所以主存都是多端口的交叉储存器。

在这里插入图片描述

最后就是多核处理器和共享内存多处理器,其实就是MIMD,只是说法不同,侧重不同。多核侧重于核心是多个,而共享内存侧重于多核心共享一个内存。

在这里插入图片描述在这里插入图片描述

硬件多线程的实现

软件多线程需要进行线程切换,需要保存和恢复寄存器组状态,代价较大。

硬件多线程代表着资源的冗余,真正的并行。因此需要多个寄存器组,多个IR,多个执行部件。

在这里插入图片描述

实际中,硬件多线程有三种方式

在这里插入图片描述
在这里插入图片描述

对于硬件多线程,你的猜想可能是左边的IR放线程1的指令,右边的IR放线程2的指令,实际并非如此。

细粒度方式下,同一时间执行一个线程的两条指令。这不是和软件多线程一样了吗?确实很像,因为线程之间是不并行的,但是我们不需要切换寄存器组,所以开销很小,这是硬件多线程的本质。

粗粒度多线程的区别仅仅在于切换时机不一样,细粒度是每周期切换,而粗粒度只有在流水线阻塞才切换,至于代价为什么大,记住就行,不必深究。

同时多线程才是我们真正理想的多线程,实现了真正的线程级并行。

总线

在这里插入图片描述

总线概述

在这里插入图片描述

总线定义

计网局域网里面学过总线拓扑,其实总线和局域网一个道理,原理很简单。

在这里插入图片描述

定义:总线是一组能为多个部件分时共享公共信息的传输线路

这样的结构同时只允许一个写(加压),可以多个读,其具有两大特点:

  1. 分时。同一时间只能有一个写,想多个写就得分时。
  2. 共享。所有设备都可以挂到总线上,都可以读取总线信息。

之所以用总线,是因为外部设备逐渐增加,专用连线太死板,不可以灵活地增减设备。

总线特性和分类

总线的特性和计网的物理层一模一样,说白了计网物理层的线缆本来就是总线的一种(通信总线)

在这里插入图片描述

在这里插入图片描述

串行总线和并行总线很难说谁好谁坏,各有千秋吧。

串行总线成本低,抗干扰。缺点就是数据的拆分与装配(串并转换)需要专门的部件。

并行总线成本高,没那么抗干扰,优点是逻辑上便于实现。

最开始并行总线是要更快的,毕竟同时传输的数据量是串行的n倍,但是随着总线频率不断上升,并行总线抗干扰的劣势就凸显了,频率提不上去(否则就会干扰),而串行总线可以肆无忌惮地提升频率,速度就被磨平了,所以谁快谁慢说不准,未来串行总线或许会取代并行总线。

在这里插入图片描述

片内总线,我们之前学CPU的时候,内部连接有两种方式,一是单总线方式,二是专用通路结构。无论是哪种,都是片内总线。

补充:数据通路是逻辑上的通道,而总线是物理媒介。

系统总线。数据总线是双向的,而地址总线一定是CPU发出,所以是单向的。控制总线是双向的,这是因为主存/外设也可以向CPU反馈信号。

通信总线(外部总线)。网线就是一种。这里有一个疑点,是不是可以在计算机外部插拔的都算外部总线,比如数据线?TODO

在这里插入图片描述

系统总线结构

系统总线负责连接计算机部件,非常复杂,所以也有很多种设计思路。

首先是单总线。注意,单指的是一组,包括数据线,地址线,控制线,而不是一根。

优点就是简单,缺点就是速度慢,尤其是会有很大的速度差,慢速设备(IO)占领了总线后,会浪费总线性能,这也是速度慢的根源。

在这里插入图片描述

为了平衡速度,就有了双总线+通道的模式。

通道可以理解为慢速设备的一个cache,是一个阉割版的CPU,通道使用IO总线统一管理慢速设备,把慢速数据集中起来,然后统一送到主存总线,不浪费总线性能。

这种模式是最快的。

此外,所谓的突发传送,原理就是局部性原理,因为空间局部性,所以CPU给一个地址,内存一次性读出连续的数据送到CPU,就很合理。

在这里插入图片描述

双总线模式下,所有的IO设备都通过通道管理,走IO总线,然而有一些高速IO设备的速度就被拖慢了,比如硬盘。

这些设备速度比较特殊,直接挂在内存总线会拖慢速度,但是挂载IO总线又浪费,所以三总线模式给高速IO设备专门开了一个DMA总线,通过DMA部件直接送到内存中。

其实去掉高速IO设备后,剩下的IO设备传输的数据量是很少的,用通道就又浪费了,不如直接挂在IO总线上直连CPU,响应速度更快,也不拖累整体速度。

以上就是三总线的思路。其优点在于能够平衡各部件的效率,让IO效率更高,缺点就是会拖慢内存总线,降低系统工作效率。

这是因为,同一时间只能有一条总线执行。比如内存是单通道的,主存总线与DMA总线不能同时对主存进行存取,更深层次的原因比较复杂,就记住这个就可以了。

在这里插入图片描述
现代CPU结构,把北桥集成到CPU中,然后设置南桥芯片,让PCI通道挂载在南桥上,让PCIE跳过南桥挂在北桥上。

总之,距离CPU越近,速度越快。

在这里插入图片描述

总线性能指标

八大指标。重点注意两个周期的理解。

传输周期(总线周期),完成一次总线传输需要的时间。
时钟周期一般是受到CPU时钟控制,有时候也受CPU北桥控制(分频)

这两个周期的关系,可以是一个传输周期=多个时钟周期,可以是一个传输周期=一个时钟周期,可以是多个传输周期=一个时钟周期。

第三种怎么理解呢?传统的数据是在上升沿传输,而有一种技术(似乎叫SDDR技术),可以在上升沿和下降沿都传输一次,所以一个时钟周期对应两个传输周期。

之后的频率就是周期的倒数了。

在这里插入图片描述

至于位宽,带宽,注意是用工作频率乘。而且这个是信号率,不是有效信号率。

总线复用。可以节省成本,但是速度下降,比如如果有数据+地址线,那么一个时钟周期就可以把数据和地址都传过去,但是复用以后就需要两个周期。

信号线数=所有总线的信号线之和。

最后来道例题。注意,传送一次数据需要用到一个传输周期,而我们题目告诉的是时钟周期,要进行转化。

比较怪的地方在于,发送一个首地址就得1个时钟周期,但是发送一次数据只要半个时钟周期,为什么这里不统一?TODO

在这里插入图片描述

总线定时

在这里插入图片描述

这一节比较关键,我们前面说的都是宏观的指标和特性,为什么会有这种特性,还要落脚到如何传输这个问题上。

总线传输要经历4个阶段,在传输阶段,主从双方如何配合节奏,这就是总线定时,这实际上是一种传输协议。

在这里插入图片描述
接下来来依次看一下吧。

同步通信

直接看时序图。

同步通信的前提是主设备已经申请到了总线使用权。之后经历如下4个时钟周期:

  1. 主设备把地址放到地址线上
  2. 主设备把读命令放到控制线上
  3. 从设备把数据放到数据线上
  4. 最后一个周期撤销读命令。

在这里插入图片描述

这里忽略总线仲裁的时间,那么一次完整的传送实际上就是上面那4个周期,构成一个总线周期。一个总线周期传送一次数据,部件之间不需要相互等待,只需要用一个总时钟调控就可以。

优点是速度很快,逻辑简单。

缺点在于,这种同步是强制同步,没有考虑设备本身的承载力,如果主存(被读的)频率比较低,那么有可能数据还没有准备好,而且也没有时间检验数据正确性。

因为不可靠,所以适用于总线长度较短的情景,信号比较可靠。因为是强制同步,所以最好让所接部件的存取频率比较接近。

异步通信

异步通信没有一个统一的时钟,时钟蕴含在传输的信息之中。分为三种模式:

  1. 不互锁模式,请求者发出去就不管了,接收方收到了进行回复后也就不管了。
  2. 半互锁模式,能确保接收方收到了确认信息。
  3. 全互锁模式,能确保接收方和发送方都收到了确认信息。这个就是TCP三次握手的原理。

在这里插入图片描述

异步方式的优点在于灵活可靠,可以自适应时间配合,缺点是更复杂更慢。

半同步通信

把同步和异步结合起来就是半同步。

基于同步模式,假设数据没准备好,从设备就发出一个WAIT信号,此时主设备就会增加WAIT周期,保持住地址和命令信号。

等WAIT信号撤了,下一个时钟周期把数据放上数据线,这个总线传输周期就可以继续推进了。

说白了,就是让给从设备增加了一个异步通信机制,一旦数据没准备好,就进行一次异步的反馈(WAIT信号),结果就是总线传输周期的长度自适应可变。

在这里插入图片描述

分离式通信

其实半同步仍然是有缺陷的,虽然引入WAIT信号可以保证数据读取准备好了再读取,但是在等待数据准备的时间内,总线是空闲的。

所以分离式通信分为两段占用期和一段释放期:

  1. 主设备通知从设备(占用总线),发送地址和命令,之后释放总线
  2. 总线空闲,被其他设备占领,此时从设备准备数据。
  3. 从设备通知主设备(占用总线),传输数据,之后释放总线

在分离式中,从设备也具有申请总线的能力,而且具有记忆主设备的能力,所以也算是一种主设备。

分离式通信算是这几种通信方式的集大成者,比异步快,比同步稳定,总线的利用率还高。

在这里插入图片描述

总线仲裁和标准

总线仲裁,似乎说不考,那我就不看了,简单感觉一下就是,这玩意似乎不消耗时钟周期,怪,反正我没看到题里算时钟周期算过仲裁的时间。

略过。

总线标准有空可以看看,有助于理解计算机部件。

在这里插入图片描述

输入/输出系统

I/O系统基本概念

在这里插入图片描述

基本概念

IO设备在计算机外部,包括我们插的硬盘其实也算是外部设备,和外部设备的交流就是IO。

IO要经过IO接口,这个接口实际上承担了IO控制器的作用,一般来说,IO控制器都是集成在主板上的(南桥)

在这里插入图片描述

IO控制方式概述

这一节把os那边的知识迁移过来,从宏观层次介绍不同IO控制方式的逻辑,后面会针对性的从硬件角度逐一精讲。

在这里插入图片描述

上下两图介绍了程序查询方式和程序中断方式。这两种方式下,数据都是要经过CPU才能到内存的。

为了便于分析,将IO分为两个阶段:

  1. IO准备:从外部到IO控制器。
  2. IO传送:从控制器到主存

程序查询方式中,IO准备的过程中,CPU被阻塞,不断查询。IO传送的时候,CPU用于传送数据。也就是说,程序查询方式中,IO准备和IO传送都要持续占用CPU。

中断方式中,IO准备的过程中,CPU去干别的了。只有在IO传送的过程中,CPU才执行中断处理程序。中断方式把CPU从IO准备过程中解放了出来。

在这里插入图片描述

中断方式只是解放了IO准备阶段,如果IO传送阶段比较费时间(比如大文件IO),就会持续占用CPU,DMA方式将CPU从IO传送阶段中解放了出来。

DMA具体原理是将控制和传输分离。控制字是通过IO总线发出的,具体数据传输是通过DMA总线转移的。

在这里插入图片描述

再来进一步分析一下DMA工作的时序。

  1. CPU通过IO总线给DMA发送读写命令
  2. DMA通过DMA总线向内存发起读写,以字位单位,反复n次,每次挪用一个内存周期,直到读写完一整块(里面有n个字)
  3. DMA通过IO总线请求中断

如上,CPU就实现了一个块的读写,对于一个大文件,其由若干块组成,CPU即使通过DMA方式也要执行若干次中断,所以说DMA方式是解放了IO传送阶段,但是不是很彻底。

或者说,DMA方式只是解放了一个块的IO传送

在这里插入图片描述

如果文件很多,DMA方式就受不了了,这种情景常见于云盘服务器,大量的高速IO请求下,即使DMA也要产生大量的CPU中断,所以需要进一步解放。

通道方式应运而生,通道是一个阉割版的CPU,但是比DMA要更牛逼。

DMA一次任务是传送一块,而通道可以直接执行内存中关于IO的指令流,即预先编辑好的通道程序,从而执行一整个任务,比如一个大文件的读写。

在这里插入图片描述

总的来说,你会发现从中断方式,到DMA,到通道,这三者无非就是增大IO控制器发出中断请求的间隔,从一个字,到一个块,到一个文件。

IO系统概述

在这里插入图片描述

IO硬件已经说了很多了,前面我们主要说了控制器和总线,后面会将外部设备。

IO软件里先说一下IO指令吧。IO指令其实也属于CPU指令集,也有操作码,告诉CPU对IO接口的操作。命令码具体细化,指明IO接口要对外部设备做什么操作,目标吗指出要对哪个IO设备操作。

IO软件里要重点说一下驱动程序。不同厂家的硬件不一样,所以同一个功能对应的命令吗各不相同,此时就需要驱动程序把同一个OS操作转换成自家的命令吗。

比如都是亮灯指令,A键盘的命令吗和B键盘命令吗就是不一样的,这就需要软件通过驱动来把OS的同一个操作转换成自家的硬件指令。

IO外部设备

简称外设。一听这个词我可就来劲了,炫一炫我组的两把键盘:keydous nj80+极地狐轴+只此青绿键帽,keydous nj68+冰静轴+带蓝敦煌键帽。

外设知识可真是没少学,很实用很有意思。

在这里插入图片描述

显示器

先说说显示器参数。

灰度级和色深有联系。灰度级=颜色数量,所以8bit色深的RGB= 2 24 2^{24} 224灰度级(24位)

VRAM的储存容量=一张图片的容量=一张图片的像素点×每个像素点的bit数

上面只是VRAM的最小容量,VRAM实际会很大,因为要还要存放即将渲染的图片,所以现在的显卡显存都挺大。对于集显来说,就占用内存当显存。

说回显示器分类。

LED和LCD。LCD的单元是液晶,LED的单元是发光二极管。现在手机使用LCD屏幕的很少,虽然这个很护眼,但是画面还是差一些,更多的使用的是OLED屏幕,属于LED大类,LED的强势点就在画面。

CRT显示器最为原始,可以从中解读出一些原始的显示器的显示原理。

CRT显示器

CRT显示器的原理是个考点,单独拿出来记录一下。

最原始的显示器是字符显示器,其显示原理并不像现在的显示器一样自由,是吧一个屏幕分成若干方块,每个方块里放一个点阵,点阵可以显示字符。点阵的显示基于位代码矩阵。矩阵里面1代表点阵亮。

这种亮暗信息构成了一个字,一个位代码矩阵对应一个字,而这种bit位矩阵可以用16进制储存,即字模信息(字形码)

在这里插入图片描述

其实字符显示器只是一种思想,细节上可以衍生出各种各样的显示器,比如CRT显示器,其显示过程如下

  1. 把要显示的内容(ASCII码)存到RAM(显存)中
  2. ROM(字符发生器)中预存每个字符的字形码
  3. CRT控制器结合RAM中的ASCII码,在ROM中将ASCII转换成字形码
  4. 字形码通过转换电路,转换为CRT的控制信号,调节阴极射线管的发射。

你会发现,其实显示原理大差不差,只需要变一下转换电路,就可以转换成LED,LCD的控制信号。

如果想要字符的显示更加自由,不被拘束在某一个方框中,可以再增加位置信息,总之就是这么个思路。

在这里插入图片描述

图中的分类都是针对CRT显示器的。

也就是说CRT显示器可以分成字符显示器,图形显示器(矢量图形,对颜色之类的没有要求,重在形状),图像显示器(显示内容丰富多彩,色彩要求高)。

还可以分成光栅扫描显示器和随机扫描显示器。

打印机

击打式打印机,物理击打会留下痕迹,仿制的时候要同时仿制油墨和痕迹,难度大,公家单位用的多。

非击打式使用喷墨+静电控制技术。

针式打印机其实就是击打式打印机的一种。

喷墨打印机和激光打印机容易混淆,激光打印机的控制是通过激光实现的,激光通过一系列操作在纸上留下字形的静电,最后仍然要喷墨。

所以可以理解为,激光打印机是喷墨的加强版,字形更清晰。

I/O接口

在这里插入图片描述

IO接口作用

数据寄存器起到缓冲作用,可以平衡内外速度,同时这个缓冲也需要串并转换电路的支持。

状态寄存器的值会随着IO状态而改变,这个是内部电路负责实现的。

控制寄存器负责储存控制信号,时钟信号。

控制寄存器是正向的,状态寄存器是反向的。

在这里插入图片描述

IO接口的核心是那个控制芯片,而其两端分别连接主机和外部设备,连接主机的叫内部接口,连接外部的叫外部接口。

比如一个USB模块,外面有好多USB接口,但是内部接口就只是一条金手指,内外之间有一个控制芯片,里面就是下图的电路。

在这里插入图片描述

结构和工作原理

在这里插入图片描述
挺复杂的。

你可以把IO控制器理解为一个简陋的CPU,其中控制逻辑模块就对应CU,与每一个部件都有双向交流。

三条线的作用:

  1. 控制线指明干什么操作
    • 这是双向的,反向信号有中断请求信号
  2. 地址线指明要对哪个部件进行操作
  3. 数据线指明操作内容

举一个打印的例子:

  1. 发命令。这一步是CPU到外设。
    • 地址线指明状态/控制寄存器
    • 控制线指明要写入
    • 数据线把控制字送到目标寄存器。不同厂家的控制字各不相同,这就需要驱动把OS的这一个命令转换成对应厂家的控制字了(命令字=控制字)。此时控制逻辑根据控制字生成对外设的控制信号,激活外设。
  2. 读状态。这一步是外设到CPU。
    • 外设激活,给控制逻辑反馈状态
    • 控制逻辑把状态写入更新到状态/控制寄存器中
    • CPU获取状态,如果是轮询,则不需要控制线反馈,如果是中断,就需要通过控制线反馈一个中断请求,同时通过数据线标明中断类型号。
  3. 工作。工作过程其实就是12两步的循环,CPU发命令,设备工作,反馈。

需要补充的是,之所以把控制和命令两个寄存器做到一起,是因为两个在时间上是错开的,所以不冲突。

如果有多个IO设备,如何指明呢?

一种思路是通过地址线指明外设,但是这就和寄存器地址冲突了,所以要分两次。

另一种思路是把外设信息融入到寄存器地址中,具体做法是给每一个设备分配一组寄存器,然后给每一个寄存器都编一个地址,这样一个地址就可以同时指定寄存器种类和外设了。下面会具体给出案例。

IO端口

在这里插入图片描述

可以看到,只要我指定了一个寄存器,就附带指明了其对应的IO控制器(甚至还可以附带指明其对应的外设,如果IO控制器对应的外设有很多)。总之原理是一样的。

统一编制常见与在risc指令集中,指令少,所以就用地址区分。其LOAD和STORE指令不仅可以访问内存,还可以访问IO端口(寄存器)

独立编制常见于CISC指令集中,通过指令来区分,我是要访问内存,还是访问IO端口。

在这里插入图片描述

统一编制的缺点在于,一旦确定了IO端口的地址范围,就不可以再更改,说白了就是绝对地址。

下面这些优缺点,我感觉本质上就是统一编制牺牲了内存,成全了IO,更简单,灵活,慢,而独立编制死板,复杂,快。

需要说的是,统一编址里面的“所有访存指令可以直接访问端口”,这个优点对于RISC来说并没有卵用,本来也就两条,真正有用的是CISC架构,所以有的CISC也是用的统一编制,就是为了这个灵活性。

在这里插入图片描述

IO接口分类

数据传送方式分类是针对外设和接口。主机和接口一侧,如果使用串行总线,那么也是串行传送的,不一定总是并行。

主机访问方式分类是针对主机和接口。其实就是按照CPU获取状态字的方式分类。

在这里插入图片描述

I/O控制方式

程序查询方式

在这里插入图片描述

IO状态分为可用和不可用,以打印机举例,打印或者未激活都是不可用,打印完当前字符就是可用。

轮询就是使用IN指令循环阻塞CPU,不断获取状态字,直到获取到了可用状态,就退出循环执行后面的任务。

补充一下,下图中启动外设需要写入控制字,其实结束外设也需要写入控制字。在进行具体的打印任务时,是一个大循环,其中轮询是小循环。CPU发出IO命令后状态字变为忙,CPU进入轮询循环。IO完毕后,外设会反馈,将状态字修改为空闲,此时轮询小循环检测到空闲,退出轮询,CPU继续发送IO命令。

在这里插入图片描述

不考虑处理时间,指的是不考虑CPU将数据从DBR挪到CPU寄存器的时间。

轮询间隔是可以自行设置的,比如每秒30次,这个就是所谓的采样率,合理设置轮询间隔,可以减少无意义的轮询次数。

每32bit就查询一次,这个说法听起来有点奇怪,CPU怎么知道传输满了32bit呢?实际上CPU不知道,32bit的意思是,在我已知传输速率以后,我人为指定一个时间间隔,可以保证这个时间间隔内IO设备已经把DBR(32bit)填满了,我这个时候询问一次就行。

选择合理的轮询间隔可以减少CPU的消耗,两次轮询之间CPU可以去干别的。这就是所谓的定时轮询

而我们最开始学的轮询,是独占轮询,这种模式CPU不停地轮询,不去干别的,忙等。

在这里插入图片描述

中断系统

在这里插入图片描述

指令周期的末尾有一个中断阶段,用于检查是否有中断请求信号。如果CPU检测到有中断请求,那么就进入中断响应流程,决定响应哪一个中断。确定了中断后,开始具体执行中断,分为准备,执行,善后三个阶段。

在这里插入图片描述

这里放一个8259A的结构图,这个给有接口技术基础的同学作参考,后面的讲解也会按着这个图来。

在这里插入图片描述

中断请求

中断是把CPU当前在做的事情打断了(原子操作)。有时候CPU也不希望被打断,所以就有关中断来屏蔽中断(PSW中的IF=0)。

原子操作以关中断为基础,进入原子操作前关中断,执行完毕后开中断

然而是否能屏蔽还是取决于中断请求的种类。

  1. CPU内部的中断,又叫做异常,你想都已经异常了,自然是不可以屏蔽。
  2. CPU外部的中断
    • 不可屏蔽外部中断:有一部分特别紧急的也不可以屏蔽,比如关机,断电。
    • 可屏蔽外部中断:包括IO中断,也就是我们研究的内容。

在这里插入图片描述

具体的中断请求标记储存在INTR中(在8259A中叫IRR),置1代表在请求中断。

在指令周期的中断阶段,CPU会发出命令检查IRR中是否有中断,进行中断流程。

在这里插入图片描述

中断响应

已经有中断请求的前提下,CPU首先检查是否关中断,关中断则无视中断请求信号。

如果开中断,则会进行中断判优,选定中断。中断判优分为软硬件两种方法:

在这里插入图片描述

这个硬件排队器确实挺有意思,这里仅作补充:当前优先级的只要是1,那么取反传递给下一个的必然是0,与后必然是0,到此为止这个中断的输出结果就已经确定了,后续的也是如此。

反过来,如果是0,那么取反传递给下一个的就是1,不影响下一个。

总的来说就是,输入=1则顺着屏蔽后续所有,0则不影响后续。当然,这种排队比较死板,就是连在左边的优先极高,事实上后面更复杂的排队(中断屏蔽字)应该也是以这个为基础的。

这个优先级是以什么原则设定的呢?

  1. 硬件高于软件,内部高于外部,不可屏蔽高于可屏蔽
  2. 在可屏蔽中断中
    • DMA高于走IO总线的,因为DMA是专门开出来的,能走DMA就走DMA
    • 实时性设备优于非实时性的。高速设备要求实时性,输入设备要求实时性,如果这些没有及时取走,前面的数据很容易就被后面的覆盖了。
中断处理

选定中断后,8259通过INT引脚向CPU发送中断请求,如果CPU没有关中断,则会通过INTA引脚进行第一次反馈。

此时进入中断处理阶段,被处理的中断,其IRR对应位置0,把ISR对应位置1,表明正在处理。

执行的时候,先执行中断隐指令,进行预处理,然后执行中断服务程序。等执行完毕后,CPU进行INTA第二次反馈,8259把ISR复位,一次中断彻底完毕。

中断隐指令过程如下:

  1. 为中断服务程序创造一个安全的环境(关中断)
  2. 保存断点(PC)
  3. 找到中断服务程序的入口(计算中断向量地址,获取中断服务程序入口地址)

找入口使用中断向量表,向量表是固定的,指向中断处理程序。ISR通过硬件电路可以生成向量地址,通过数据总线送到CPU里,CPU再通过地址找到中断服务程序入口。

关中断可前可后,因为它和另外两个操作是同时的。

在这里插入图片描述

再说中断服务程序。注意,中断服务程序≠中断程序本身,仍然有保护和恢复的额外操作。

在这里插入图片描述

多重中断

前面讲的是单重中断,一个中断在执行的时候,不可以被再次中断。本节介绍多重中断。

在这里插入图片描述

多重中断的核心在于中断屏蔽字,此时前面的IMR就派上用场了,IMR储存当前正在执行的中断源的中断屏蔽字。

中断屏蔽字:当前中断正在执行的时候,要屏蔽的中断

中断屏蔽技术本身是用来屏蔽的,在单中断场景中没有意义,因为已经关中断了,无差别屏蔽。只有在多重中断场景中中断屏蔽字才有用,可以用于手动调整优先级:

优先级小于等于自己的都屏蔽,设为1。优先级高于自己的设为0

来道例题。
在这里插入图片描述

到此为止,必要的知识已经讲完,再讲点细节的。在多重中断中,ISR里面可能有多个1,起到了记忆的作用。

当最高1对应的中断执行完毕后,ISR清除最高位的1,如果此时ISR还有1,那么顺位执行当前最高优先级的中断。

程序中断方式

了解了中断的原理和过程后,我们书接上回,直接给流程图,有几个注意点:

  1. 数据准备完毕到中断服务程序之间,还有一个中断隐指令
  2. 在中断服务程序数据传送完毕的一瞬间,CPU就可以再次发送IO指令,之后再继续执行中断服务程序。或者说CPU发出下一个IO指令就包括在中断服务程序的主体指令之中。

在这里插入图片描述

来道例题:

第一小问,如果要严格揪着算的话,最后一个字符取出来以后,CPU还需要5条指令进行中断结束的操作。其实加不加都可以,公式写清楚就行,只是理解方式的不同,你可以认为取出了数据,IO就结束了,也可以严格点,等最后一个中断结束才算IO结束。

第二小问,CPU有一部分时间是在干别的,即启动到请求的这一段时间,剩下的时间才是CPU真正用于IO的工作时间,这个时间没有歧义。

在这里插入图片描述

DMA方式

在这里插入图片描述

无论是定期轮询,还是中断方式,这种额外的损耗频率都比较高,以字为单位。DMA方式将这种损耗进一步降低,以块为单位进行中断,而在一个块的内部,损耗仅仅是挪用一个内存存取周期(三总线窃取情况)。

在这里插入图片描述

DMA结构与工作流程

如下给出一个单总线结构的DMA的大致工作流程:

  1. CPU通过IO总线,向DMA发出命令,初始化DMA。
    • 主存起始位置,外设读写地址,总共传几个字
  2. DMA传送,逐字传输,直到一个块传输完毕
    • 字传输前,先把数据读到数据寄存器中,之后向CPU请求总线使用权
    • 字传输时,确定目标地址,大小,进行传送
    • 字传送后,自动修改计数器
  3. DMA申请中断。此时计数器归零,DMA申请中断,报告执行结果

在这里插入图片描述

接下来细化一下,研究一下DMA结构。看起来挺复杂,实际还好。

在这里插入图片描述

和上面的简略图相比,额外有一个控制/状态逻辑,这个负责DMA的总控。

DMA请求触发器,如果数据就绪,则触发器=1。

触发器=1后,经过控制状态逻辑就可以向CPU申请总线使用权,走得是HRQ线,如果CPU允许,则通过HLDA线通知DMA,你可以用总线了。之后就是数据传送。

等WC=0后,会发送溢出信号到中断机构,进而申请DMA结束中断。

在这里插入图片描述

如此就传完了一个块,如果再想传一个块,那CPU就再启动一次DMA

三总线结构下的DMA

上面说的都是单总线结构,CPU决定总线控制权,在DMA使用总线的时候,CPU啥也干不了。

三总线结构下,当DMA向内存传数据时,虽然CPU不可以向内存传数据,但是CPU还可以用IO总线进行其他IO操作。

这种方式还有一个优点,就是传输一个块的数据过程中,CPU完全不需要管,单总线情况下还得负责回应DMA的总线申请请求。

在这里插入图片描述

说实话下面这三种方式,DMA周期挪用碾压前两种。

第一种方法还不如中断和定期轮询,失去了DMA的意义。第二种方式同样如此,一次性切割了主存50%的周期,如果CPU或者DMA在自己的周期内部不去访存,那么就是浪费了。

使用窃取方式,以DMA优先,可以充分提高主存利用率,有点动态调控的感觉。

注意,三总线情况下,CPU在DMA访存时,只是不能写存了,还可以干别的。

在这里插入图片描述

宏观对比

对比着理解。

首先看轮询和中断方式。

独占查询就是忙等,没有优势。但是定时查询和中断的关系,好比同步方式和异步方式,同步采用时钟,定期检查,而异步使用通知的方式。在这段时间之外,CPU都是可以去工作的,这两种模式孰优孰劣,不好评价。

同步模式下,虽然一次查询费时间比较少,但是整体上容易踩空,需要设置合理的间隔,太长了数据被覆盖,太短了数据还没准备好,浪费CPU周期。异步会产生中断程序的额外消耗,中断隐指令和中断服务程序的准备和退出都很费时间。

再说DMA和中断方式。

DMA整体上是碾压中断的,尤其是在传送大文件的时候。中断唯一的优势就是更加灵活,处理异常的单位是一个字,而DMA只有一个块传完了才能上报结果。

所以实际使用中,低速设备采用中断方式,保证可靠性,而高速设备使用DMA方式,提速。

在这里插入图片描述

Logo

汇聚原天河团队并行计算工程师、中科院计算所专家以及头部AI名企HPC专家,助力解决“卡脖子”问题

更多推荐