计算机组成原理
写在前面:
本文是针对《计算机组成原理》(唐朔飞编著 / 第三版) 一书的总结, 也是博主应对期末考试的知识梳理.
本文中使用了大量的图片, 它们大多来自课上的PPT, 在这里对于PPT作者致以诚挚的谢意, 如有侵权, 请联系作者进行博文的删除操作.
文中如果提到书目页数, 指向的也是上文提到的教材.此外, 由于博主的课堂教授顺序与书中不尽然相同, 因此会按照课堂的教授顺序进行梳理, 章节名称以及序号会以书中的名称 / 序号为准. 读者可根据目录快速查询到相关章节.
Chap.1 计算机系统概论
1.1 计算机系统的组成
1)软硬件概念
- 硬件:各类实体、主机 / 外设等
- 软件:由具有各类特殊功能的信息(程序)组成
- 系统软件:用于管理整个计算机系统
- 应用软件
2)解题过程 & 层次结构
计算机的程序运行过程主要经过以下过程:
高级语言程序->翻译为机器语言程序->运行->输出结果
根据该解题过程,可以将计算机分为如下层次结构:
- 虚拟机器M4:用高级语言书写,利用 编译程序 翻译为汇编语言程序。
- 虚拟机器M3:用汇编语言书写,利用 汇编程序 翻译为机器语言程序。
- 操作系统机器M2:用机器语言解释操作系统。
- 实际机器M1:用微指令解释机器指令。
- 微程序机器M0:用硬件直接执行微指令。
3)计算机体系结构 Vs 计算机组成
- 计算机体系结构:包含的主要是程序员能够见到的计算机系统属性。 概念性的结构与功能 。指令集、寻址技术等。
- 计算机组成:包含如何 实现计算机体系结构 涉及到的属性,但这些属性对于程序员而言大部分是透明的。
1.2 计算机的基本组成
1)冯·诺依曼计算机的特点
- 由五大部件组成
- 运算器:算术运算、逻辑运算,并暂存结果
- 存储器:存放数据、程序
- 控制器:控制、指挥程序和数据的输入、运行,并处理运算结果
- 输入设备:将人类能识别的信息转化为机器信息
- 输出设备:将机器信息(运算结果)转化为人类可识别的信息
- 指令、数据以 同等地位 存于存储器,可按地址寻访
- 指令和数据均用二进制表示
- 指令由操作码和地址码组成
- 指令在存储器内按顺序存放
- 机器以运算器为中心
2)现代计算机框图
- 主机
- CPU
- ALU运算器
- CU控制单元
- 主存储器
- CPU
- I/O设备
3)计算机工作步骤
- 上机前的准备
- 建立数学模型
- 确定计算方法
- 编制解题程序(不同的算法对应的解题程序完全不同)
如:计算 ax^2 + bx + c –> (ax + b)x + c
- 解题过程
(1)存储器的基本组成:
①存储体:
存储体->存储单元->存储元件
类比为:一栋大楼->一个房间->一个床位
一个存储元件能表示一位二进制数。
一个存储单元代表着一串二进制代码,这被称为一个字。
②MAR & MDR:
MAR被称为地址寄存器,反应存储单元的个数。
MDR被称为数据寄存器,反应存储字长。
举例:MAR:4位,MDR:8位
代表有16个存储单元(2^4),一个存储单元由八位二进制数组成。
(2)运算器的基本组成及操作过程:
加法:
- [M] -> X
- [ACC]+[X] -> ACC
减法:
- [M] -> X
- [ACC]-[X] -> ACC
对于加减法,无需使用MQ,即乘商寄存器,只需要将最终结果暂存在ACC中即可。
乘法:
- [M] -> MQ
- [ACC] -> X
- 0 -> ACC
- [X]*[MQ] -> ACC//MQ
对于乘法,由于十分可能出现溢出,因此需要将ACC与MQ串在一起存储结果,我们记成 ACC//MQ ,这里的MQ是乘商寄存器。
除法:
- [M] -> X
- [ACC]/[X] -> MQ
- R -> ACC
对于除法,将结果暂存在乘商寄存器中,将余数存在ACC内。
(3)控制器的基本组成:
- PC:程序计数器,存放当前需要执行指令的地址
- IR:指令寄存器,存放当前即将执行的指令
- CU:控制单元
完成一条指令需要三步:
- 取指:PC(PC直接与MAR相连,方便取指)
- 分析:IR(IR的高位,即操作码会送到CU,记为OP(IR)->CU;IR的低位,即地址码会送到MAR,记作Ad(IR)->MAR)
- 执行:CU(发送各种位操作命令序列)
例子:
1.3 计算机硬件的技术指标
1)机器字长
CPU一次能处理数据的位数。
2)存储容量
$$ 存储容量 = 存储单元个数 * 存储字长 $$
存储单元个数由MAR体现,即:2^MAR 个存储单元 ;而MDR反应存储字长, 字长就是MDR位数本身 。
常用的存储单位:
1 byte = 2^3 bit = 8 bit
1 KB = 2^10 byte
1 MB = 2^10 KB
1 GB = 2^10 MB
3)运算速度
- 主频
- 吉普森法: $ T_M = \sum_{i=1}^n f_i * t_i $ 其中TM为机器运行速度,fi表示第i种指令占全部操作的占比数,ti表示该种指令的运行时间。
- MIPS:每秒执行百万条指令
- CPI:执行一条指令所需时钟周期数
- FLOPS:每秒浮点运算次数
Chap.6 计算机的运算方法
6.1 无符号数和有符号数
1)无符号数
顾名思义,无符号数无正负,所有的位数都用于表示数字的大小。
例:8位无符号数能表示的范围:0~255($ 2^8 -1 $)
2)有符号数
利用单独的一位符号位来表示正负,用0表示正,用1表示负。
下面提三种表示方法:
①原码表示法
最简单的一种方法,符号位+数值位,数值位上就是 真值的绝对值 。
严谨定义如下:
$$ x_{整数}[原] = \begin{cases}
x, 2^n > x \geq 0 \\
2^n - x, 0 \geq x > -2^n\
\end{cases} $$
$$ x_{小数}[原] = \begin{cases}
x, 1 > x \geq 0 \\
1 - x, 0 \geq x > -1\
\end{cases} $$
其实不用摁记原始定义,在实际换算上,整数使用带余除法,小数利用不断乘2,取整数位的操作来获得即可。
需要注意的是,在x = 0时,正零和负零利用原码的表示是不同的:
$ [+0]_原 = 0,0000 $
$ [-0]_原 = 1,0000 $
②补码表示法
计算机为了归一加法和减法,利用了另一种表示方法,即 补码 的概念。
补码的根本概念需要说起同余,即涉及到了带余除法的概念,具体而言,-3与+9在除以12时,余数是一样的,因此在模12的意义下,这俩是一个数。
因此,我们类比上面的思路,可以将负数转化为相对应的模数。
给出严谨定义如下:
$$ x_{整数}[补] = \begin{cases}
x, 2^n > x \geq 0 \\
2^{n+1} + x \space(mod \space 2^{n+1}), 0 > x \geq -2^n\
\end{cases} $$
$$ x_{小数}[补] = \begin{cases}
x, 1 > x \geq 0 \\
2 + x \space (mod \space 2), 0 > x \geq -1\
\end{cases} $$
需要说明的是,式子里的x是真值。
还是不用摁记定义,有一种快捷的方式可以求补码。
显然,正数的补码不用管,就是它自身;
负数的补码可以通过 除符号位每位取反,最后在末位再加1求得 。
两个特殊的案例:
首先,根据定义(这里不要用快捷的求补码方式),+0 和 -0 的补码是一样的,都是 0,0000
第二,关于多出来的 1,0000这个值怎么办,这个值原先是 -0的原码,但是现在-0被占据了,那就将其指向最小的那个负数,这也就是为什么补码可以比原码表示的负数个数多一个。
举个例子,8位原码表示的范围是: $ -2^7+1 \to 2^7-1 $
而8位补码能表示的范围是: $ -2^7 \to 2^7-1 $
③反码表示法
反码表示法的严谨定义如下:
$$ x_{整数}[反] = \begin{cases}
x, 2^n > x \geq 0 \\
(2^{n+1} - 1) + x \space(mod \space 2^{n+1} - 1), 0 \geq x > -2^n\
\end{cases} $$
$$ x_{小数}[反] = \begin{cases}
x, 1 > x \geq 0 \\
(2 - 2^{-n}) + x \space(mod \space 2 - 2^{-n}), 0 \geq x > -1 \
\end{cases} $$
这里n是指小数的位数
同样的,有一种快速求反码的方式,即 除了符号位之外各位取反 即可。
我们把三种东西总结一下:
最后,提一嘴关于移码的内容。
移码是为了解决补码无法非常直观的看出数字大小而发明的。
其定义为:
$$ x[移] = 2^n + x $$
n是整数的位数。
其快捷算法即 先将补码算出来,改一个符号位即可 。
显然,根据移码的定义,移码这里也没有+0,-0的区分。
而对于对应数位上最小的那个值,其移码为全0。
举个例子:n=5时, $ -2^5[移] = 0,00000 $
这也与其补码相匹配,毕竟 $ -2^5[补] = 1,00000 $
6.2 数的定点表示和浮点表示
1)定点表示
即将小数点位置固定在某一位置,这样的方法容易在进行运算时产生溢出风险,尤其是在处理的数字不是纯整数或纯小数时。
2)浮点表示
浮点数是当前更多采用的方法,其一般形式为:
$$ N = S * r^j $$
- S: 尾数,小数,可正可负
- r: 基数,一般取2,4,8,16等
- j: 阶码,整数,可正可负
n,是尾数的位数,反映了浮点数的精度
m,是阶码的位数,反映了浮点数的表示范围
浮点数的规格化:即将类似 0.00001 这类的小数转化成 $ 0.10000 * 2^{1,0100} $ 这样的德行(这里全用的二进制机器数),说的再简单点,就是把小数点后面能用阶码表示的0全去了。
具体怎么规格化呢,先将真值对应的二进制数字进行规格化,比如: $ -0.0001011 = 2^{-3} * -0.1011$ ,而后再进行对应的机器数转换,后面这个转换过程中不需要再做任何移位操作。
需要注意的是,规格化之后也涉及到补码、反码之类的一堆形式,一般会给要求,如 阶原尾补 之类的,即阶码用原码表示,尾数用补码表示。
关于机器0:当尾数为0时,不论阶码为何值,均按照机器0处理。
6.3 定点运算
计算机中的乘法是通过类似笔算乘法的方式通过移位来实现的:
这里A是 被乘数 , B是 乘数 .
因此我们将其化为下面的竖式:
上面的步骤可以总结为:
- 利用加法和移位进行乘法运算.
- 由乘数的末尾决定是否与被乘数相加, 而后进行 整体右移 , 乘数最后一位丢弃, 同时结果(即原先部分积)右移一位, 将放不下的那一位放在 乘数右移空出的高位 上.
- 总次数取决于乘数的位数
6.3.1 原码一位乘
原码一位乘与上述笔算乘法思想完全相同, 不过需要额外考虑 符号位 的问题, 这里一般采取 符号位和数值位分开处理 的方法.
最终将符号位 ( $ 1 \bigoplus 0 = 1 $ ) 加上即可得到最终结果: 1.10110110.
6.3.2 原码两位乘
两位乘法是为了后续为了节省时间而设计出来的算法, 但是两位乘法涉及到 加三个被乘数 , 而计算机对于三这个数字并不高效, 因此通过单独一个进位位来进行处理:
需要额外说明的是, 由于原码二位乘涉及到进位问题( 详细来说, 就是可能需要加两倍的被乘数[即左移一位], 或者减去一倍的被乘数[符号位全部置1] ) 因此在原码两位乘时, 我们需要 写三个符号位 .
原码两位乘的符号位仍然需要单独处理, 乘法是否结束看的是乘数位数( x位需要右移 $ \frac{x}{2} $ 次, 每次右移两位 ).
6.3.3 补码乘法
补码一位乘:
补码一位乘即利用补码的形式进行与上述模式类似的优化乘法, 运算规则如下:
这里着重介绍Booth算法:
Booth算法 无需考虑被乘数和乘数的符号 , 这里不对其原理进行详细介绍, 仅给出计算规则:
请额外注意: Booth算法的乘数需要带着符号位 , 也正因如此, Booth算法的最后一步加法无需右移 .
此外, Booth算法的右移是 补码右移 , 补上的数字 同当前计算结果的符号位 .
给个例子:
6.4 浮点四则运算
本节需要明确的问题是处理两个规格化的浮点数之间的运算.
对于浮点数:
$$ x = S_x * 2^{j_x} $$
$$ y = S_y * 2^{j_y} $$
6.4.1 浮点加减
可以分为三步来进行:
- 对阶: 相当于正常运算中的对齐对应位操作
- 尾数求和: 利用对应的补码加减定点运算进行
- 规格化
- 舍入: 如果规格化过程中出现了 尾数末尾丢失 的情况, 要考虑舍入
1.对阶:
显然, 阶数小的浮点数应当向阶数大的浮点数看齐.
为了方便, 这里应当 尽可能使用补码运算 .
2.尾数求和:
利用化完的补码进行加减即可.
3.规格化:
规格化, 意思为需要将加(减)完的数转变为规格化数.
由于我们这里通常使用补码加, 因此要记住补码的规格化数特点: 符号位和第一数位不同 .
延续上例:
4.舍入:
即考虑被舍弃的尾数应当如何处理:
- 0舍1入: 被舍弃的值是0则直接舍弃, 是1则将舍弃后尾数加1(精度较高)
- 恒置1: 不论舍弃的低位是什么, 舍弃后的最低位 恒置为1
6.4.2 浮点乘除
与加减不同, 浮点乘除不需要考虑对阶, 直接进行运算即可:
除法运算并不要求掌握, 这里不细说了.
6.5 算术逻辑单元ALU
6.5.1 ALU概述
因为这不是数字电路基础的复习, 因此对于ALU的详细电路组成不会进行非常详细的讲解.
中间的ALU其实是相当复杂的组合逻辑电路, 典型的四位ALU芯片为74181, 在数字电路中有所提及.
这里只需要明确, ALU是 根据 $ K_i $ 的不同取值对 $ A_i, B_i $ 进行相应的操作, 从而使 $ F_i $ 输出对应的运算结果 的器件即可(明确一下, 这里i使可以取多位的, 要不然ALU只能进行一位数的两种运算也太low了).
6.5.2 快速进位链
我们需要详细了解的是ALU的快速进位链, 即ALU进位的过程.
1.并行加法器:
这种进位是最慢的, 因为它需要等待前面的位运算完毕产生进位信号 $ C_i $ 后, 才能进行自己的运算.
2.串行进位链:
我们先看正常全加器中 $ C_i $ 的逻辑表达式
可见, 将其进位公式抽象成了:
$$ C_i = d_i + t_i C_{i-1}, d_i = A_i B_i, t_i = A_i+B_i $$
显然, $ d_i, t_i $ 都是可以先算出来的, 而 $ C_{i-1} $ 需要等待下位传递.
因此, 一个像这样的串行进位链就出来了:
如果一个与非门的判断延迟时间: $ t_y $
- 一位进位信号产生时间: $ 2t_y $
- n位进位信号产生时间: $ 2nt_y $
3.并行进位链:
其实将上面的 $ C_0, C_1, C_2 $ 等元素进行递推, 就可以发现并行进位链的效率可以进一步提高(相当于 $ C_{-1} $ 的进位信号来了, 就可以一次产生4个进位信号).
这样的结构需要额外在每个进位位前面加一个 与或非门 , 我们假设其判断时间为: $ 1.5t_y $
可见, 仅需要 $ 1.5t_y + t_y = 2.5t_y $ 的时间, 就可以产生四位的进位信号, 效率很高.
但是肉眼可见的, 这种进位的电路相当的复杂, 因此, 只能成组使用.
(1) 单重分组跳跃进位:
可见, 将四位为1组, 每组可以在 $ 2.5t_y $ 的时间内产生进位信号, 并将最后一位的进位信号传向下一组.
(2) 双重分组跳跃进位链:
在上述分组的基础上, 每小组再分成一个大组:
我们以第八小组为例, 给出进位逻辑:
可见, 可以通过进一步拆解递推公式, 发现一大组中的四个小组的进位信号 $ C_i $ 都可以通过前一个大组传来的最终进位信号 $ C_{-1} $ 得到, 进而进一步设计电路节省时间.
小组分组电路中, 由于第8小组的 最高位进位 $ C_3 $ 可以根据 $ T_8, D_8 $ 得到, 而 $ T_8, D_8 $ 还可以进一步用于后续小组(第5, 6, 7小组)的进位 , 因此不再输出 $ C_3 $ , 转而输出 $ T_8, D_8 $
可见, 双重分组跳跃进位链产生16位进位的时间进一步缩短到 $ 7.5t_y $ , 究其根本原因在于每一个小组的最高位进位( $ C_15, C_11, C_7, C_3 $ )通过电路提前根据 $ C_{-1} $ 算出来了, 而不像单重分组那样需要等待前几个分组的信号.
Chap.4 存储器
4.1 存储器概述
4.1.1 存储器分类
存储器有多种分类方式:
- 按存储介质分类
- 半导体存储器: TTL / MOS
- 磁表面存储器: 磁头, 载磁体
- 磁芯存储器: 硬磁材料, 环状原件
- 光盘存储器: 激光, 激光材料
- 按存取方式分类:
- 存取时间与物理地址无关(随机访问)
- 随机存储器( 可读可写 )
- 只读存储器( 只读 )
- 存取时间与物理地址有关(串行访问)
- 顺序存取存储器(磁带)
- 直接存取存储器(磁盘)
- 存取时间与物理地址无关(随机访问)
- 按在计算机中的作用分类 :
- 主存储器:
- RAM
- 静态RAM: SRAM
- 动态RAM: DRAM, SDRAM
- ROM
- MROM
- PROM
- EPROM
- EEPROM
- RAM
- Flash Memory(闪存)
- 高速缓冲存储器(Cache)
- 辅助存储器: 磁盘, 磁带, 光盘
- 主存储器:
4.1.2 存储器的层次结构
由于目前比较常见的说法都按照在计算机中的作用分类, 因此我们通常利用该种分类方式来描述存储器的主要特性.
根据上图的层次性质对比, 可以将计算机的存储层次归纳为:
$$ CPU \iff 缓存 \iff 主存 \iff 辅存 $$
这其中:
- $ 缓存 \iff 主存 $ 层次主要解决CPU与主存速度不匹配的问题
- $ 主存 \iff 辅存 $ 层次主要解决存储系统的容量问题
4.2 主存储器
4.2.1 概述
主存的基本组成 如下图所示:
MAR / MDR 即地址寄存器和数据寄存器在第一章中有所提及, CPU向MAR, MDR发送数据, 而后MAR, MDR分别通过地址总线和数据总线与内存的相应电路相连, 并以此达到读或写的目的.
4.2.2 主存中存储单元的地址
地址线 决定了计算机的寻址空间: 一根地址线能够一个二进制位.
如果按照字节编址: 寻址范围为 $ 2^{24} = 16M $
如果按照字编址: 寻址范围则缩小为 $ \frac{2^{24}}{\frac{x}{8}}, x是字的位数 $ , 即 $ \frac{原寻址范围}{一个字包含的字节数} $
读者可以将按照字编制理解为将多个字节打了个包, 只能寻找到一个包裹而不能单独将包裹内的字节拿出来, 因此寻址范围也需要相应减少.
4.2.3 主存的技术指标
主要有三种存储器指标可供参考:
- 存储容量: 主存存放二进制代码的总位数
- 存储速度
- 存取时间: 包括 访问时间, 读出时间, 写入时间
- 存取周期: 连续两次 独立 的存储器操作所需的最小间隔时间
- 存储器的带宽: 位/秒
存储器带宽的算法要会, 具体而言, $ 单次取出位数*存储器频率 $ , 通常可能给出一个存取周期, 给出单次取出位数, 会让算存储器带宽.
4.2.4 半导体存储芯片
目前主流的半导体存储芯片利用地址线通过译码驱动电路指向存储矩阵中的内存单元, 再由数据线将内存单元中的数据取出.
可以直接根据地址线和数据线的数量来计算出总芯片容量, $ 容量 = 2^{地址线位数} * 数据线位数 $ , 即 总共多少个存储单元 * 一个存储单元内多少位 .
片选线 用于选取存储芯片(后文会提及), 读写控制线 用于决定进行何种(读 / 写)操作.
上图也能证明, 很多存储器是由 多片容量更小的存储器组合在一起 组成的.
4.2.5 半导体存储芯片的译码驱动方式
1.线选法:
线选法原理简单, 如上图所示, 很多的存储单元组成 $ m*n $ 位的矩阵, 地址译码器先将地址线的二进制数译码为对应的行数, 而位线再通过与对应列数对齐将一行的所有位输出.
2.重合法:
重合法通过二维的地址译码器选中一个特定的存储单元. 两侧的译码单元作用与上面的线选法原理相同.
4.2.6 静态RAM及芯片举例
静态RAM通过触发器与放大器等元器件进行数据读写, 这里不对具体的原理电路进行说明, 进进行芯片举例:
Intel 2114
总体思路与重合法(矩阵单元选择)的方法很像, 但2114的列地址译码器可以一次选择一行中的四位(即 $ x, x+16, x+32, x+48 $ 四列), 从而达到一次传输出4位二进制数据的效果.
这里以写操作举例:
4.2.7 动态RAM及芯片举例
动态RAM通过电容存储电荷的原理来寄存信息. 电容上的电荷仅能持续 1~2 ms, 因此, 必须每2ms对所有存储单元恢复一次原状态, 这被称为 再生或刷新 .
由于动态RAM的基本单元性质, 它读出的高低电平与 原存信息反相 .
Intel 1103
动态RAM的刷新
由于电容的特性, 动态RAM需要定时进行刷新.
1.集中刷新
即在一段时间后单独拉出一段整时间进行全体刷新:
需要明确的是, 刷新是跟行地址强相关的, 即一次存取周期刷新一整行.
图中两个概念:
- 死区: 用于刷新的时间, 即 $ 存取周期 * 矩阵行数 $
- 死时间率: 死区时间占比, 即 $ \frac{死区时间}{刷新时间间隔} $
2.分散刷新
分散刷新将刷新分布在了存取周期内, 即花一半的周期进行读 / 写, 另一半的周期用于刷新.
由于刷新按行进行, 因此只需要 $ (0.5+0.5)*128 = 128\mu s $ 就能完成一整次刷新. 并且不会存在停止读写的死区时间.
代价是存取周期更长了, 整个系统的速度受影响.
3.异步刷新
异步刷新想将分散刷新与集中刷新结合起来, 其做到每行隔 2ms 刷新一次, 平均到每行, 即 $ \frac{2*10^3}{128} = 15.6 \mu s $ 刷新一行. 仍有死区, 但死区很短, 并且对系统速度影响较小.
4.2.8 动态RAM与静态RAM的比较
4.2.9 只读存储器ROM
- MROM
- PROM(一次性编程)
- EPROM(多次性编程)
- EEPROM(电可擦写多次性编程)
- Flash Memory(闪存)
- 便宜
- 电可擦洗
- 比EEPROM快
4.3 存储器与CPU的连接
4.3.1 存储容量扩展
1.位扩展(增加存储字长)
2.字扩展(增加存储字(也可理解为存储单元)的数量)
3.字 / 位同时扩展(上述二者结合)
由上述说明可知, 通过地址线的增加来实现字扩展(扩大寻址空间), 通过数据线的增加来实现位扩展(扩大存储单元位数)
4.3.2 存储器与CPU的连接
例题: 见书P94~99 例4.1 / 4.2 / 4.3
通常可以分为四步:
- 写出要求主存地址空间分配的二进制码
- 确定使用的芯片数量及类型
- 分配CPU的地址线
- 形成片选信号
需要注意的是, 这里题目中给出的4K, 8K往往代表 寻址空间 而并非 总容量 (举例而言, 如果题目希望分配4K地址的系统程序区, 而系统又有8根数据线, 你应当分配一个4K*8位的存储芯片, 而并非是一个256bit*8位的存储芯片), 即这里的地址指的都是编好地址的一个个存储单元, 而并非单独的一位.
4.4 提高访存速度的措施
总体而言, 提高访存速度有三种方法:
- 采用高速器件
- 采用层次结构(Cache-主存)
- 调整主存结构
从主存结构说起:
1.单体多字系统
这种设计的目的在于单次从存储体中某一地址取出多条指令, 并在此后每隔一定时间送给CPU一条指令, 这极大程度上提高了主存的带宽, 但显然, 这种设计的前提在于指令必须是连续存储的.
劣势: 遇到 转移指令 或 操作数不能连续存放的指令 时, 效果不明显
2.多体并行系统
相当于将主存再度分层, 分为N个模块, 每个模块都有自己的MAR, MDR可供独立使用, 这使得它们可以并行工作, 又可以交叉工作.
上图所谓 高位交叉 即高位地址表示体号, 低位地址表示体内地址. 这种编址可以使不同的请求源同时访问不同的体, 实现 多体并行操作 ;
与高位交叉相反的思路是 低位交叉 , 即低位地址表示体号, 高位地址表示体内地址, ( 将所有内存分成几个体, 就称作模几交叉, 比如上图就是一个模4的交叉编址 ) 这种方式能够在不改变存取周期的前提下增加存储器的带宽:
如上图, 低位交叉可以通过硬件设计使得 不同的体先后在同一个访存周期的不同时段启动 , 以此达到一个访存周期传递多条指令的效果(上例中读顺序: 0->1->2->3->0->1->2->3)
存储器控制部件(存控)
存控中含有四个部件:
- 控制线路
- 节拍发生器
- 存控标记触发器
- 排队器
其中, 排队器是相当重要的, 它能根据请求源的紧急程度为CPU处理顺序排序, 具体顺序: 易发生代码丢失的请求源->严重影响CPU工作的请求源->其他请求源;
高性能存储芯片
SDRAM(同步DRAM): 能在系统时钟的控制下进行写入和读出, 这使得 CPU无需等待 ;
带Cache的DRAM: 继承了一个由SRAM组成的Cache, 有利于猝发式读取;
4.5 高速缓冲存储器
简称 缓存 , 位于CPU与主存之间, 负责解决CPU的 空等 现象, 容量比主存小, 速度比主存高;
4.5.1 Cache工作原理
主存和缓存均按照块存储(类似于前面的多体并行系统), 块的大小相同, 一个块中有很多存储单元;
缓存内共有C块, 主存共有M块, 显然, M>>C;
因此, 需要根据某种规则将主存的块与缓存的块建立联系, 这就衍生出两个名词:
- 命中: 主存块成功调入了缓存, 建立了对应关系;
- 未命中: 主存块未调入缓存, 对应关系未能建立;
4.5.2 Cache的命中率以及效率
即CPU希望访问的信息在Cache中的比率.
$$ Cache命中率 \space h = \frac{Nc}{Nc+Nm} $$
上式中: Nc表示访问Cache的次数, Nm表示访问主存的次数;
可以通过命中率推断主存系统的平均访问时间:
设:
- 访问Cache的时间 $ t_c $
- 访问主存的时间 $ t_m $
- Cache命中率 $ h $
则主存系统的平均访问时间:
$$ t_a = h * t_c + (1-h) * t_m $$
上面这个式子的意义就是: 命中缓存, 则直接从缓存里取; 未命中缓存, 则需要进一步访问主存;
进一步推出 Cache-主存系统的效率 :
$$ Efficiency = \frac{访问Cache的时间}{平均访问时间} * 100 % = \frac{t_c}{h * t_c + (1-h) * t_m} * 100 % $$
4.5.3 Cache的读写
下列框图为Cache的读操作
在写操作上, 则有两种方法:
- 写直达法: 写操作时 既将数据写入Cache, 也写入主存 , 花费时间为 访问主存的时间
- 写回法: 写操作时 只把数据写入Cache , 当Cache数据需要被替换出去后再写回主存, 花费时间是 访问Cache的时间
4.5.4 Cache-主存的地址映射
1.直接映射
直接映射很容易理解, 就是一个取模操作.
我们上面提到过的变量这里列一下:
- m: 主存地址中存储块数的位数, 这说明主存总共有 $ M = 2^m $ 个块
- c: Cache地址中用于存储块数的位数, 这说明Cache中共有 $ C = 2^c $ 个块
- i: Cache中的块号
- j: 主存中的块号
我们可以得到:
$$ i = j(mod \space C) \space 或者 i = j(mod\space 2^c) $$
此时, CPU传来的主存地址的高m位被分成了两部分: 低c位指的就是对应Cache的字块地址, 而高(m-c)位则代表 主存字块标记 , 表示当前Cache中存放的是主存的第几组数据( 相当于把主存按照Cache的总字块数量再度分了个类 : 0[C-1], C[2C-1], …, [M-C]~[M-1]). 如果读者看过上一篇博文, 或者了解过数论相关的知识, 这就是 同余类 的概念.
当CPU传过来一个对应地址时, Cache先找到对应的同余类, 而后看 主存字块标记 字段能否与对应Cache地址的 标记 字段匹配, 如果匹配则命中缓存, 直接调用Cache中的数据, 如果不匹配则仍需要进一步访问主存, 并同时替换Cache的对应字块, 并修改该块Cache的 标记 .
这种映射方式容易理解, 但不够灵活.
2.全相联映射
即Cache中每一个字块都可以与主存的任何字块建立对应关系. 这意味着 主存字块标记字段 以及 Cache对应的 标记 字段均 上升到了m位 , 并且CPU每传来一个主存地址, 都需要与Cache的每个 标记 全部比较一遍, 效率无疑是比较低的.
3.组相联映射
组相联映射是对上面两种方式的一种折中.
具体而言, 组相联映射对Cache也进行了分组, 将Cache分成Q组, 每组有R块.
借鉴直接映射, 我们还是有这个式子:
$$ i = j(mod \space Q) $$
相当于将主存对应组别映射到Cache对应同余类的组别中.
我们假设Q = 16, R = 2, C = Q*R = 32;(Cache中总共32块, 2块为一组, 共16组)
则主存标号为 0, 16, 32, …, M-16 的字块对应Cache的第0组, 它们中任意一个字块都能存储到这一组2个块中的任意一个之中.
即: 主存的第j块映射到Cache的第i组内(直接映射), 同时, 又能存在第i组R个字块中的任意一个中(全相联映射).
这种映射方式仍然将CPU传来的主存地址的高m位分类了, 组地址 $ q = c-r $ 表示对应Cache的第几组, 前面 $ m-q = m-c+r $ 位代表主存字块标记, 需要与Cache中的对应组 $ 2^r = R $ 个标记字段相比较.
上面这一段可以说是存储器中最不好理解的一段, 主要是涉及到的变量确实很多, 读者还请尽力理解.
给几个例子:
Chap.7 指令系统
7.1 机器指令
7.1.1 指令的一般格式
计算机的指令一般由 操作码 和 地址码 两部分构成.
总共有三种设计方式:
长度固定指令: 主要用于指令字长较长的情况;
长度可变指令: 操作码分散在不同字段中, 显然, 这会增加指令译码 / 分析 / 控制器设计的难度;
扩展操作码指令: 随着地址数减少, 操作码的位数增加, 这里通过16位字长指令来解释一下这个设计思路:
可见, 每将一种三地址指令分解, 则可以分解成 $ 2^4 $ 个二地址指令, $ 2^8 $ 个一地址指令
(这里一地址, 二地址, 三地址指的是地址数目)
7.1.2 地址码的设置机制
一条指令中的地址数量有很多种情况, 通常分为:
四地址
顾名思义, 四地址中含有四个地址;
通常情况下, 这分别表示 第一操作数 / 第二操作数 / 结果存储 / 下一条指令的地址 ;三地址
三地址区别于四地址, 它将下一条指令的地址隐去了, 通常隐含在程序计数器PC中(程序计数器见第一章);二地址
二地址进一步简化指令, 它将结果存储的位置也隐去, 这意味着它必须将结果存在:- $ A_1 $ / $ A_2 $ 中
- 累加器ACC中
前者的情况, 一条指令需要访存4次(取指令, 读两个数, 存一个数), 而如果仅存在ACC中, 则只需要访存3次(取指令, 取两个数).
一地址
一地址进一步简化, 直接省去了一个操作数的位置, 因为它默认另一个操作数处于累加器ACC中;
它会固定将结果存在ACC中;零地址
零地址没有地址码, 这意味着它无法执行正常的运算操作, 一般是空操作 / 停机 / 返回 / 中断返回等.
需要指明的是, 并不是地址数越多越好, 因为地址数越少, 它能包含的位数就越多, 这说明其寻址范围就越大.
7.1.3 指令字长的设置
从上面的描述应该不难看出, 指令字长取决于:
- 操作码的长度
- 操作数地址的长度
- 操作数地址的个数
一般而言, 指令字长可以有两种方法来设计:
- 指令字长固定: 即 $ 指令字长 = 存储字长 $ ;
- 指令字长可变: 即 $ 指令字长按照字节的倍数来变化 $ ;
7.2 操作数类型及操作种类
7.2.1 操作数类型
机器中的操作类型有: 地址 / 数字 / 字符 / 逻辑数等.
它们的表示方法如下:
- 地址: 无符号整数
- 数字: 定点数 / 浮点数 / 十进制数
- 字符: ASCⅡ
- 逻辑数: 逻辑运算
7.2.2 存储器中的数据存放
7.2.3 操作类型
操作类型大体可分为以下四类:
数据传送: 从 寄存器或存储器 将数据送往 存储器或寄存器 的操作;
算术逻辑操作: 在运算器ALU中进行的各类计算操作;
移位: 算术移位(保留符号位, 适用于有符号数), 逻辑移位(不保留符号位, 适用于无符号数), 循环移位(将移出的位数重新移入, 分带进位 / 不带进位)等:
- 不带进位的循环移位: 直接将移出的值原样移到另一侧即可;
- 带进位的循环移位: 即在原先循环移位的基础上加了一个 进位位, 当移位时, 将 进位位 的值赋给空出的位置, 并将被移出的值赋给 进位位 ;
转移:
- 无条件转移: JMP
- 条件转移:
- 结果为零转移(Z = 1): JZ
- 结果溢出转移(O = 1): JO
- 结果有进位转移(C = 1): JC
- 跳过一条指令: SKP, 其通常格式为: SKP DZ, 表示如果完成触发器D为0, 则跳过下一条指令;
陷阱(Trap)与陷阱指令: 陷阱一般指一种意外事故导致的中断, 这种类型的指令一般不提供给用户直接使用. 而是在出现事故时, 由CPU自动产生并执行.
输入输出:
- 输入指令: 从 端口地址 -> CPU的寄存器;
- 输出指令: 从 CPU的寄存器 -> 端口地址;
7.3 寻址方式
寻址方式, 指的是如何 确定本条指令操作数的存放地址, 以及确定下一条欲执行指令的存放地址 ;
更简单地讲, 就是完成了这两件事:
- 指令寻址
- 数据寻址
7.3.1 指令寻址
指令寻址涉及到的操作相对简单, 分为 顺序寻址 和 跳跃寻址 两种.
顺序寻址即 直接在程序计数器PC上加1, 得到新一条指令的地址 .
跳跃寻址需要通过 转移指令 给出下一条指令的地址(转移指令的定义见上一节).
7.3.2 数据寻址
数据寻址相对于指令寻址要复杂很多, 这里我们给出两个名词:
- 形式地址A: 即指令字中的地址;
- 有效地址EA: 操作数的真实地址;
在数据寻址中, 一般会将一个指令字分为 操作码 / 寻址特征 / 形式地址A 三部分. 机器通过寻址特征来进行相关操作, 进而找到有效地址.
1.立即寻址:
所谓立即寻址, 即 形式地址A就是操作数 , 这也被称为 立即数 , 显然, 这种形式的好处在于无需访存, 但A的位数也限制了操作数的大小.
2.直接寻址:
直接寻址即 有效地址由形式地址直接给出 , 显然, 这种方式需要访存一次, A的位数决定了本次指令操作数的寻址范围.
$$ EA = A $$
3.隐含寻址:
隐含寻址, 说明有某一个操作数的地址时隐含在操作码中的, 其实就是上面一地址指令的思路 .
举个例子: 比如8086中的MUL指令, 其被乘数是隐含在AX寄存器或AL寄存器中的, 而这个地址不用给出, 而是通过操作码(MUL)给出的.
这种方式使得指令字中少了一个地址字段, 可以有效缩短指令字长.
4.间接寻址
间接寻址, 即 有效地址由形式地址间接提供 , 换言之, 即需要通过形式地址对应主存块的内容多次跳转才能得到有效地址.(读者如果有过编程基础, 其实就是 指针 的思想)
$$ EA = (A) $$
类似的, 指针也分单层和多层, 上图右侧即通过多次反复跳转进行寻址的过程. 这种方式的好处是扩大了寻址范围, 但每一次寻址都需要耗费对应的访存时间.
间接寻址其实运用的十分广泛, 典型代表比如当用户调用子程序时, 会通过间接寻址的方式给出子程序的起始地址.
5.寄存器寻址
寄存器寻址, 即 有效地址是寄存器编号, 由形式地址直接给出 .
$$ EA = R_i $$
这种方式省去了访存的时间成本, 只需要访问寄存器即可, 执行较快, 并且可以有效缩短指令字长(给一个编号与给一个地址的长度必然前者更短一些), 但寄存器个数有限, 代表着这种方式也有其局限性.
6.寄存器间接寻址
与第四种间接寻址的想法十分类似, 只不过这次有效地址存在寄存器中, 形式地址仍然只给出寄存器编号.
$$ EA = (R_i) $$
这种方式相比于间接寻址能够减少一次访存时间开销(因为第一次只需要访问寄存器即可).
7.基址寻址
基址寻址通过一个寄存器作为 基址寄存器(BR) , 其有效地址表示为:
$$ EA = (BR) + A $$
通常, 在程序执行的过程中, BR内的内容是不变的, 这个值由操作系统或管理程序决定. 这种方式可以一定程度上扩大寻址范围, 有利于多道程序的运行.
相应的, 作为基址寄存器的寄存器也分两类:
- 采用专用寄存器作为基址寄存器
- 采用通用寄存器作为基址寄存器(此时, 哪个通用寄存器作为基址寄存器由用户指定)
8.变址寻址
变址寻址的思路与基址寻址的思路恰好反过来, 有这么一个变址寄存器IX(可专用可通用), 在程序执行的过程中, IX的内容由用户给定, 并且 可以变化 , 而对应的, 形式地址A不再变化.
$$ EA = (IX) + A $$
这种方式同样可以扩大寻址范围, 同时非常适合 处理数组问题 .
9.相对寻址
相对寻址更特殊一点, 通过给出 操作数与当前指令的地址差值 来寻找操作数.
$$ EA = (PC) + A $$
这种方式对于书写程序浮动的情况相当有利, 也广泛被用于转移指令中.
10.堆栈寻址
堆栈寻址使用了 数据结构中的栈结构 , 其通过先进后出的思路来设计寻址.
其通过一个变量 SP 来维护栈顶地址, 每当入栈一个指令后, 栈顶-1; 对应的, 每出栈一条指令, 则栈顶+1(注: 这是通过字编址的情况, 如果通过字节编址则需要加减相应指令以字节计数的长度);
7.4 指令格式
这一节主要讲述设计指令时需要考虑的各类因素, 但它对考试不是很重要, 这里不在枚举了, 仅引出一个概念:
平均码长:
$$ \sum_{i} F_i * B_i $$
其中:
- $ i $ : 表示指令
- $ F_i $ : 表示指令的频率
- $ B_i $ : 表示编码该条指令所用的位数
给个例子:
本题中, 编码方式涉及到了一些霍夫曼树的知识, 简单来说, 频率越高的指令, 码长应当越短;
本章中, 涉及到了RISC与CISC的对比, 在博文中不在列举, 请见PPT
Chap.8 CPU的结构与功能
8.1 CPU的结构
8.1.1 CPU的功能
在第一章中, 提及过CPU的两个重要组成部分: 控制器(CU) 与 运算器(ALU) ;
- 控制器
- 取指令
- 分析指令
- 执行指令 / 发出各种操作命令
- 控制程序输入 / 结果输出
- 总线管理
- 处理异常 / 特殊请求
- 运算器
- 实现算术运算 / 逻辑运算
总而言之, CPU实现的功能分五大类: 指令控制(PC, IR) / 操作控制(CU, 时序电路) / 时间控制(CU, 时序电路) / 数据加工(ALU, 寄存器) / 处理中断(中断系统)
8.1.2 CPU的结构框图
结构框图
内部结构
关于CPU的寄存器:
- 通用寄存器: 存放操作数
- 数据寄存器: 存放操作数
- 地址寄存器: 存放地址
- 条件码寄存器: 存放条件码(一般是程序分支的依据)
通常的工作顺序:
$$ PC \to MAR \to M \to MDR \to IR $$
8.2 指令周期
8.2.1 指令周期的概念
指令周期, 指 取出并完成一条指令所需的全部时间 .
其基本组成过程分为:
- 取指周期: 取指令
- (间指周期): 取有效地址
- 执行周期: 取操作数
- (中断周期): 保存程序断点
8.2.2 指令周期的数据流
1.取指周期数据流
- PC存放现行指令的地址, 该地址被送到MAR, 并随后被送至地址总线
- CU向存储器发读命令
- 对应MAR指向单元的内容经过数据总线送到MDR, 再送至IR
- CU控制PC+1
2.间指周期数据流
- CU检查IR中的内容, 确定本条指令是否有间指操作
- 如果有间指操作, 则MDR中的右侧N位(被称作 Ad(MDR) )会被送到MAR, 并被进一步送至地址总线
- CU向存储器发读命令
- 获取有效地址, 存入MDR
3.执行周期数据流
不同指令的执行周期数据流往往差别较大, 因此这里无法利用框图表示.
4.中断周期数据流
- CU将用于保存程序断点的存储器特殊地址(典型案例, 比如此前在堆栈寻址中讲到的SP, 即指向栈顶的指针)送往MAR, 进一步送往地址总线
- CU向存储器发写命令
- 将PC中的内容(程序断点)送到MDR, 并经过数据总线写入存储器
- CU将中断程序的入口地址送至PC, 为下一个取指周期做好准备
8.3 指令流水
8.3.1 系统并行性
所谓指令流水, 是提高 系统并行性 的一种称呼, 即希望两个或两个以上事件在同一时间段发生.
并行性分等级:
- 过程级: 指作业, 进程之间的并行, 这可以通过软件实现
- 指令级: 指不同指令之间, 或指令内部的操作之间的并行, 这需要通过硬件实现
8.3.2 指令流水的具体设计
最简单的方式: 串行执行
串行指令是我们最容易想到的, 它的工作方式就是一条条指令进行下去, 没有任何并行性, 效率也自然很低.
进一步设计: 二级流水
第二种想法是将取指令和执行指令分开:
这种方式也被称为 指令预取 , 如果取指阶段和执行阶段在时间上完全重叠, 那么理论上指令周期可以减半, 速度可以提高一倍.
但很遗憾, 指令的执行时间一般大于取指时间, 因此二者速度其实并不十分匹配; 另外, 遇到一些条件转移指令时, 往往下一步是不可知的, 必须等待上一条指令执行阶段结束, 判断出条件真伪后才能进行下一阶段的取指.
再度分级: 六级流水
为了解决二级流水遇到的以上问题, 我们将指令的处理过程继续细化分解:
- FI(Fetch Instruction): 取指
- DI(Decode Instruction): 指令译码
- CO(Calculate operand address): 计算操作数地址
- FO(Fetch operand): 取操作数
- EI(Execute instruction): 执行指令
- WO(Write back operand): 写操作数
我们考虑一个含有六条指令的程序, 如果串行执行: 需要6*9 = 54个时间单位, 而通过上述流水线的思路, 则只需要14个时间单位!
8.3.3 流水线性能评估
1.吞吐率
即 单位时间内流水线所完成指令的数量
设 m 段的流水线隔段时间为 $ \Delta t $
(1) 最大吞吐率
$$ T_{pmax} = \frac{1}{\Delta t} $$
(2) 实际吞吐率: 连续处理n条指令的吞吐率
$$ T_p = \frac{n}{m * \Delta t + (n-1) * \Delta t} $$
2.加速比
设流水线各段时间为 $ \Delta T $ , 每条指令共 m 段;
加速比即 非流水线的速度与 m 段的流水线的速度之比
则:
$$ S_p = \frac{n * m * \Delta t}{m * \Delta t + (n-1) * \Delta t} = \frac{n * m}{m+n-1} $$
3.效率
流水线自身的效率, 即 流水线中各功能段的利用率
$$ 效率 = \frac{m * n * \Delta t}{m * (m+n-1) * \Delta t} = \frac{n}{m+n-1} $$
8.4 中断系统
本节本来放到了 I/O 设备后讲述, 在本文中为了整体性将其与第八章其余内容放在一块, 读者也可以先不看这一节
8.4.1 概述
引发中断的因素有很多:
- 人为设置的中断(转管指令)
- 程序性事故(移出, 操作码不能识别, 除法非法)
- 硬件故障
- I/O 设备
- 外部事件(用键盘中断现行程序)
每个可能出现中断的请求源, 都有一个 INTR(中断请求标记触发器) , 其分散在多个中断源的接口电路中, 也集中在CPU的中断系统内
8.4.2 中断判优逻辑
1.分散在各个中断源的接口电路中, 比如 I/O 设备的链式排队器(见第五章 5.5)
2.集中在CPU内:
3.通过软件轮流查询:
8.4.3 中断服务程序入口地址的寻找
- 通过硬件直接形成中断向量寻找(见5.4)
- 通过软件查询
8.4.4 中断响应的条件与时间
见5.5
8.4.5 多重中断
即优先级别高的中断源有权中断优先级别低的中断源
需要说明的是, 保护多重中断中, 因为 存在多次保护现场操作 , 因此保护现场的暂存地址不可能永远是那个特殊的0地址了, 每次新中断时, 要将 原先0地址内的内容转存 .
8.4.6 屏蔽技术
重点理解 屏蔽字与优先等级的改变 , 具体而言, 屏蔽字为1 代表 对应中断源的中断请求会被本中断请求屏蔽掉
如上图所示, 响应优先级是不能被更改的, 因此A处理完后, 还是会优先响应B, 但是 C的处理优先级更高, 所以C可以打断B的响应 , 同理, D打断C的响应, 并最先处理完, 随后C处理完, B处理完.
Chap.9 控制单元的功能
9.1 操作命令的分析
回忆第八章的内容, 指令的执行过程分四个周期, 在这里将每个周期实现的操作再度利用比较标准的语言描述一下:
1.取指周期
- $ PC \to MAR \to 地址线 $
- $ 1 \to R $
- $ M(MAR) \to MDR $
- $ MDR \to IR $
- $ OP(IR) \to CU $
- $ (PC) + 1 \to PC $
2.间指周期
- $ 指令形式地址 \to MAR $ 即 $ Ad(IR) \to MAR $
- $ 1 \to R $
- $ M(MAR) \to MDR $
- $ MDR \to Ad(IR) $
3.执行周期
(1)非访存指令:
- CLA(将ACC清零): $ 0 \to ACC $
- COM(ACC取反): $ \overline{ACC} \to ACC $
- SHR(算术右移): $ L(ACC) \to R(ACC), ACC_0 \to ACC_0 $ (后面一步代表符号位不变)
- CSL(循环左移): $ R(ACC) \to L(ACC), ACC_0 \to ACC_n $
- STP(停机): $ 0 \to G $
(2)访存指令:
- 加法指令: $ ADD \space X $
- $ Ad(IR) \to MAR $
- $ 1 \to R $
- $ M(MAR) \to MDR $
- $ (ACC) + (MDR) \to ACC $
- 存数指令: $ STA \space X $
- $ Ad(IR) \to MAR $
- $ 1 \to W $
- $ ACC \to MDR $
- $ MDR \to M(MAR) $
- 取数指令: $ LDA \space X $
- $ Ad(IR) \to MAR $
- $ 1 \to R $
- $ M(MAR) \to MDR $
- $ MDR \to ACC $
(3)转移指令(不访存)
- 无条件转: $ JMP \space X $
- $ Ad(IR) \to PC $
- 条件转移: $ BAN \space X $ , 这里假设 $ A_7 = 1 $ 为满足条件
- $ A_7 * Ad(IR) + \overline{A_7} * (PC) \to PC $
上述指令的执行周期总结如下:
4.中断周期
中断分两种:
- 关中断
- 程序中途中断
二者区别只在需要执行的第一个操作
- 保存程序断点:
- 关中断: $ 0 \to MAR $ , 即断点存入0地址
- 程序中途中断: $ (SP)-1 \to SP \to MAR $ , 即断点进栈
- $ 1 \to W $
- $ PC \to MDR $
- $ MDR \to M(MAR) $
- $ 中断程序入口地址M \to PC $
- $ 0 \to EINT $
9.2 控制单元的功能
9.2.1 控制单元的外特性
从图中, 我们整理一下控制单元的输入 / 输出信号来源:
- 输入信号
- 时钟: CU受到时钟控制
- 指令寄存器 ( $ OP(IR) \to CU $ ), 与操作码相关
- 标志: CU收到各类标志寄存器的控制
- 外来信号: 如INTR(中断请求) / HRQ(总线请求)
- 输出信号
- CPU内的各种控制信号
- 来自寄存器的
- 来自PC的
- 来自ALU的
- 送到控制总线的信号
- $ \overline{MREQ} $ , 访存控制信号
- $ \overline{IO} / M $ , 访问IO / 存储器的控制信号
- $ \overline{RD} $ , 读命令
- $ \overline{WR} $ , 写命令
- $ INTA $ , 中断响应信号
- $ HLDA $ , 总线响应信号
- CPU内的各种控制信号
9.2.2 控制信号举例
详见书 $ P_{380} $
这里仅对是否使用CPU内部总线进行说明, CPU内部总线是一根将所有器件串连起来的总线, 相当于所有的信号都可以通过内部总线传递给其他部件.
9.2.3 多级时序系统
1.机器周期
机器周期是 所有指令执行过程中的一个基准时间 ;
这个基准时间的确定需要考虑:
- 完成最复杂的指令功能的时间
- 访问一次存储器的时间
通常, 访存时间比CPU中的处理要长很多, 因此在 指令字长 = 存储字长 的前提下, 取指周期 = 机器周期
2.时钟周期
一个机器周期内可以完成若干个微操作, 因此将 机器周期进一步细分为若干个时间相等的时钟周期
时钟周期是 计算机操作的最小单位时间 , 可以利用它产生一个或几个微操作命令
3.多级时序系统
一个指令周期包含多个机器周期, 一个机器周期包含多个时钟周期.
4.机器主频
机器主频越快:
- 机器速度越快
- 一个机器周期中包含的时钟周期数越多
- 一个指令周期中包含的机器周期数越多
9.2.4 控制方式
1.同步控制方式
- 采用定长的机器周期: 即以最长的微操作序列以及最繁的微操作作为标准, 每个机器周期内节拍数目相同
- 采用不定长的机器周期: 每个机器周期内节拍数不等
- 中央控制+局部控制: 将大部分指令安排在统一 / 较短的机器周期内完成, 对于少数较复杂的指令, 采用局部控制方法完成
2.异步控制方式
- 无基准时标信号
- 无固定应答节拍和严格的时钟同步
- 采用应答方式
3.联合控制方式
- 同步+异步
4.人工控制方式
- Reset
- 连续+单条指令执行转换开关
- 符合停机开关
Chap.10 控制单元的设计
10.1 微程序设计
10.1.1 微程序设计思想
将一条机器指令进一步细分, 一条机器指令对应一个 微程序 , 一个微程序对应很多条 微指令 .
如果预先将一些操作可预测的微程序写进ROM中, 则可以大大提高执行速度.
10.1.2 微程序控制单元工作原理
这其中:
- 控制存储器(控存): 是核心部件, 负责存放全部微程序
- CMAR(控存地址寄存器): 存放欲读出的微指令地址
- CMDR(控存数据寄存器): 存放从控存读出的微指令
- 顺序逻辑: 用于控制微指令序列
微指令的基本格式中:
- 操作控制字段负责发出控制信号
- 顺序控制字段负责指出下一条微指令的地址(简称 下地址 )
给出工作方式:
- 取指阶段
- 取指周期微程序首地址 $ M \to CMAR $
- 将对应控存中的第一条微指令读到控存数据寄存器 $ CM(CMAR) \to CMDR $
- 根据微指令的操作控制字段发出控制信号, 如 $ PC \to MAR, 1 \to R $ 等诸如此类
- 根据微指令的顺序控制字段找到下一条微指令, 即 $ Ad(CMDR) \to CMAR $
- 回到第二步, 循环进行, 直到相应指令已经存入IR中为止
- 执行阶段
- 将操作码送至微地址形成部件, 其输出即第一条微指令的地址, $ OP(IR) \to 微地址形成部件 \to CMAR $
- $ CM(CMAR) \to CMDR $
- 发出控制信号, $ Ad(CMDR) \to CMAR $ …
- 直至指令执行结束为止
微指令有两个很重要的问题:
- 操作控制字段如何形成微操作命令
- 后续地址如何形成
10.1.3 微指令的编码方式(控制方式)
即 微指令的操作控制字段如何产生应当产生的控制信号 , 解决第一个问题.
1.直接编码(直接控制方式)
这种方式代表 在操作控制字段中 每一位代表一个微操作命令 , 该位为1则代表该控制信号有效;
2.字段直接编码方式
这种方式将控制字段中的位分成了若干段, 每一段经过单独译码后发出控制信号, 每段中的命令是互斥的
其缩短了微指令字长, 但增加了译码时间
为什么缩短了控制时间? 假设有7个互斥微操作, 第一种方法需要7位, 第二种方法只需要3位, 因为 $ 2^3 = 8>7 $
3.字段间接编码方式
这种方式代表 一个字段的某些微操作还需要另一个字段中的某些微命令来解释 , 可以进一步缩短微指令字长, 但削弱了微指令的并行控制能力.
10.1.4 微指令序列地址的形成
通常有四种形成方式:
- 下地址字段指出
- 根据机器指令的操作码形成
- 增量计数器, 即 $ (CMAR) + 1 \to CMAR $
- 分支转移:
- 测试网络:
- 直接由硬件产生: 通常常见于 第一条微指令地址的产生 以及 中断周期的中断周期微程序
10.1.5 一些杂项
- 水平型微指令: 一次能定义并执行多个并行操作
- 垂直型微指令: 类似机器指令操作码, 根据操作码字段规定微指令的功能
相比而言, 水平型微指令 并行操作能力强, 灵活性强, 执行一条机器指令需要的水平型微指令 数目更少, 速度更快 , 其用较短的微程序结构换取了较长的微指令结构.
- 静态微程序: 采用ROM, 微程序不需要改变
- 动态微程序: 采用EPROM, 可以通过改变微指令和微程序来改变机器指令
毫微程序设计
相当于对微指令进行了进一步细分, 好比微指令相对于机器指令
串行微程序与并行微程序
10.2 微程序设计举例
所有微指令的涉及到的微命令都在9.1节中有所提及, 这里相当于将其整合, 根据每一条指令需要做的事情列出一个表.
具体表格见书 $ P_{419} $ , 这里强调几点:
- 书中所有的0均省去了
- 书中的微指令地址是八进制, 这意味着后续顺序控制字段三位代表八进制一位字符
10.3 组合逻辑设计
10.3.1 组合逻辑控制单元框图
10.3.2 微操作的节拍安排
采用 同步控制方式
一个机器周期内有 3个节拍(即3个时钟周期)
安排微操作时序的原则如下:
- 微操作的先后顺序不得随意更改
- 被控对象不同的微操作尽量安排在一个节拍内完成
- 占用时间较短的微操作尽量安排在一个节拍内完成, 并允许有先后顺序
具体各个周期以及执行周期各个命令的节拍安排, 见书 $ P_{396} $ ~ $ P_{398} $
各个指令在 取指 / 间指 / 执行阶段的不同节拍内分别干了啥, 请见书 $ P_{402} $
10.3.3 组合逻辑设计步骤
1.给出操作时间表(类似书 $ P_{402} $ )
2.写出微操作命令的最简表达式:
举例而言, 对于 $ M(MAR) \to MDR $ 这条微操作, 可以写出这样的表达式:
$$ M(MAR) \to MDR = \\
FE * T_1 + IND * T_1(ADD+STA+LDA+JMP+BAN) \\
T_1 \lbrace FE+IND(ADD+STA+LDA+JMP+BAN)+EX(ADD+LDA) \rbrace $$
3.画出逻辑图
Chap.5 输入输出系统
5.1 概述
5.1.1 输入输出系统概况
- 早期输入输出系统
- 分散连接方式
- CPU 与 I/O 设备串行工作, 采用程序查询方式
- 接口模块 / DMA阶段
- 总线连接方式
- CPU 与 I/O 设备并行工作, 采用中断方式 / DMA 方式
- 具有通道结构的阶段
- 具有 I/O 处理机的阶段
5.1.2 输入输出系统组成
- I/O 软件
- I/O 指令, 是CPU指令的一部分
- 通道指令, 指通道自身的指令
- I/O 硬件
- 设备 & I/O接口
- 设备 / 设备控制器 / 通道
5.1.3 I/O 设备与主机的联系方式
- I/O 设备编址方式
- 统一编址: 使用取数 / 存数指令即可
- 不统一编址: 需要使用专门的 I/O 指令
- 设备选址
- 用设备选择电路识别是否被选中
- 传送方式
- 串行传送
- 并行传送
- 联络方式
- 立即响应
- 异步工作, 采用应答信号(常用于IO设备与主机工作速度不匹配时)
- 同步工作, 采用同步时标( 要求 I/O 设备与CPU工作速度完全同步 )
- 连接方式
- 辐射式: 每台设备有一套专门的控制线路和信号线
- 总线连接: 通过总线连接所有的 I/O 设备( 便于增删设备 )
5.1.4 I/O 设备与主机信息传送的控制方式
- 程序查询方式: CPU 和 IO 串行工作, 踏步等待;
- 程序中断方式: CPU不查询, I/O 设备准备好后向 CPU 发送中断请求, CPU 暂停当前程序, 进入中断服务程序;
- DMA方式: 通过周期挪用(周期窃取)使得 CPU 和 I/O 设备并行工作;
5.2 I/O 设备
外部设备大致分为三类
- 人机交互设备: 键盘 / 鼠标 / 打印机 / 显示器
- 计算机信息存储设备: 磁盘 / 光盘 / 磁带
- 机-机通信设备: 调制解调器
不是计组的主要讲述内容, 这里不进行详细阐述
5.3 I/O 接口
所谓 IO 接口, 指的是主机与 IO 设备之间设置一个硬件电路以及其相应的软件控制.
设置接口的目的主要是
- 实现设备选择
- 实现数据缓冲达到速度匹配
- 实现数据串-并格式转换
- 实现电平转换
- 传送控制命令
- 反应设备状态
5.3.1 接口的功能和组成
1.总线连接方式
其组成主要为:
- 设备选择电路
- 命令寄存器 / 命令译码器
- 完成触发器 D
- 工作触发器 B
- 中断请求触发器 INTR
- 屏蔽触发器 MASK
- 数据缓冲寄存器
- 设备状态标记
5.4 程序查询方式
5.4.1 程序查询流程
对于单个设备而言, 程序查询方式十分简单:
多个设备, 则需要将很多个这样的电路组合在一起:
对于程序查询这种方式, 总流程如下:
5.4.2 程序查询的接口电路和具体流程:
见书 $ P_{191} $ ~ $ P_{192} $
5.5 程序中断方式
中断的概念前面有提及, 这里不再详细叙述了, 简而言之, 就是 暂存程序断点->进入中断服务程序->返回程序断点继续执行 这样的过程.
5.5.1 I/O 中断的产生
5.5.2 中断方式的接口电路
由于计算机连接的 I/O 设备肯定不止一个, 因此不同的中断请求需要被排序, 这涉及到 排队器 的设计.
在中断被触发后, 需要根据对应的中断请求给出相应的中断程序地址(比如打印机的中断请求需要对应打印机的中断服务程序), 因此还涉及到 中断向量地址形成部件 ;
关于 中断方式的完整接口电路 , 见书 $ P_{196} $
此外, 有一道关于中断排队器以及中断向量地址的综合设计例题, 请看书 $ P_{196} $ 的例5.2
5.5.3 I/O 中断处理过程
- CPU相应中断的条件
- 允许中断触发器 EINT = 1;
- EINT的更改可以通过:
- 开中断: 1 -> EINT
- 关中断: 0 -> EINT
- 硬件自动复位: 0 -> EINT
- CPU相应中断的时间
- D = 1 且 MASK = 0;
- 在每条指令执行阶段的结束前CPU发送中断查询信号(相当于在上面的中断接口电路中将 INTR 置1)
关于 终端服务程序的流程 :
- 保护现场
- 中断服务
- 恢复现场
- 中断返回
关于 单重中断与多重中断 :
- 单重中断: 一旦某一中断程序开始, 则必须运行完后才能进行下一次中断处理
- 多重中断: 允许级别更高的中断源 中断 现行的中断服务程序
5.6 DMA方式
5.6.1 DMA方式的特点
DMA方式与前两者的数据通路并不相同, I/O 设备的DMA接口直接与主存相连.
因此, DMA有三种与主存交换数据的方式:
- 停止CPU访问主存: 即让CPU暂停一段时间, 把总线的控制权交给DMA
- 周期挪用(周期窃取):
- CPU此时不访存, 则DMA直接拿到控制权
- CPU此时正在访存, DMA等待
- CPU与DMA同时请求访存, 则CPU将总线控制权交给DMA;
- CPU与DMA交替访问: 将CPU的工作周期分为两部分, 一部分专供DMA访存, 另一部分供CPU访存.(这种方式 不需要申请建立和归还总线的使用权 )
5.6.2 DMA接口的功能和组成
DMA接口需要实现的功能如下:
- 向CPU申请DMA传送
- 处理总线控制权的移交
- 管理系统总线 / 控制数据传送
- 确定数据传送的首地址 / 长度
- 传送结束后, 给出操作完成信号
其 工作过程 主要分三步:
- 预处理
- CPU向DMA输出指令, 预置一些必要信息
- 主存起始地址
- 设备地址
- 传送数据个数
- 可看作是DMA接口的初始化过程
- CPU向DMA输出指令, 预置一些必要信息
- 数据传送
- 以数据块为单位传送数据
- CPU此时继续执行主程序
- 传送结束后DMA向CPU申请中断
- 后处理
- CPU停止主程序的执行, 进入中断服务程序
- 进行DMA的结束工作
- 是否继续传送
- 继续传送则初始化
- 否则停止外设
- 检查传送过程是否出错
- 是否继续传送
5.6.3 DMA方式与中断方式的比较
5.6.4 DMA接口类型
- 选择型: 物理上连接多个设备, 但逻辑上只能同时连接一个设备
- 多路型: 物理上连接多个设备, 逻辑上允许多个设备同时工作
Chap.3 系统总线
3.1 总线的基本概念
总线, 即 各个部件共享的传输介质, 分串行传送和并行传送.
总线结构的计算机举例:
1.面向CPU的双总线:
2.单总线:
3.面向存储器的双总线:
3.2 总线分类
大体上, 总线可分为
- 片内总线: 即芯片内部的总线
- 系统总线
- 数据总线: 双向, 与机器字长 / 存储字长有关
- 地址总线: 单向, 与存储地址 / I/O 地址有关
- 控制总线: 有出有入
- 通信总线: 用于 计算机系统之间 或 计算机系统与其它系统之间
3.3 总线的特性与性能指标
3.3.1 总线的物理实现
总线位于 主板 上, 而连接不同设备是由 插板 实现的.
3.3.2 总线的特性
总线有以下几种值得关注的特性
- 机械特性: 尺寸 / 形状 / 管脚数目 / 排列顺序
- 电气特性: 传输方向 / 有效电平范围
- 功能特性: 每根传输线的功能
- 时间特性: 信号的时序关系
3.3.3 总线的性能指标
- 总线宽度: 数据线根数
- 总线带宽: 每秒传输的最大字节数(MBps)
- 时钟类型: 同步 / 异步
- 总线复用: 地址线与数据线复用
- 信号线数: 地址线 / 数据线 / 控制线的总和
- 总线控制方式: 并发 / 自动 / 仲裁 / 逻辑 / 计数
- 其他指标: 负载能力等
3.4 总线结构
3.4.1 单总线结构
前面有所提及:
3.4.2 多总线结构
1.双总线:
2.三总线:
有两种方式.
3.四总线
3.5 总线控制
3.5.1 总线判优控制
主线中的模块有优先之分:
- 主设备(模块): 它对总线有控制权
- 从设备(模块): 它响应从主设备发来的总线命令
- 判优控制有两种方式
- 集中式
- 链式查询
- 计数器定时查询
- 独立请求方式
- 分布式
- 集中式
1.链式查询:
通过BS / BR / BG三个信号来决定总线的操作
2.计数器定时查询:
通过计数器进行计时, 定时查询各个接口是否需要进行操作
3.独立请求方式:
在总线控制部件内部内置排队器, 接受来自各个接口的请求, 通过排队器决定处理顺序
3.5.2 总线通信控制
通信控制的目的在于解决双方的 协调配合问题 .
总线传输周期有四个阶段:
- 申请分配阶段: 主模块申请, 总线仲裁决定
- 寻址阶段: 主模块向从模块给出地址 / 命令
- 传数阶段: 主模块和从模块交换数据
- 结束阶段: 主模块撤销有关信息
主模块通过总线与从模块的数据通信也分四种:
- 同步通信: 通过 统一时标 控制数据传送
- 异步通信: 采用 应答方式 , 没有公共时钟标准
- 半同步通信: 同步 + 异步
- 分离式通信: 为了充分挖掘总线潜力而出现
1.同步通信:
通过统一时钟进行数据交互
以同步式数据输入为例:
这种方式使得 速度快的模块必将需要等待速度慢的模块准备完成 才能进行下一步, 因此会造成资源浪费.
2.异步通信:
主模块发送请求信号时, 会一直等待从模块的应答信号, 而后再开始传输
- 不互锁: 主模块确认从模块接收到请求后, 就撤销请求申请
- 半互锁: 主模块收到来自从模块的回答信号后, 再撤销其请求申请
- 全互锁: 主模块必须等待从模块回答后, 撤销其请求信号; 同时, 从模块必须等待主模块已经撤销其请求信号后, 才能撤销其应答信号.
3.半同步通信:
增加一个 WAIT 信号, 即如果从模块速度过慢, 必须 在下一个时钟到来前给出 $ \overline{WAIT} $ 信号(低电平) , 主模块如果检测到这个低电平, 则会主动插入一个 $ T_w $ 进行等待, 直到 $ \overline{WAIT} $ 信号变为高电平了为止.
4.分离式通信
上述三种通信方法, 都分三个过程:
- 主模块发地址 / 命令(占用总线)
- 从模块准备数据( 占用总线, 但总线空闲 )
- 从模块向主模块发送数据(占用总线)
分离式通信希望将 第二个过程浪费的总线传输也利用起来 .
分离式通信将一个传输周期(总线周期)分成了两部分:
- $ T_1 $ : 主模块发送地址 / 命令 / 自己的编号(在有多个主模块时, 这很重要, 涉及到从模块在下半个周期中的寻址) , 然后立即放弃总线使用权
- $ T_2 $ : 从模块准备好后, 将收到主模块的编号 / 自己的地址 / 主模块所需数据发送到总线上, 供对应主模块接受.
该方法:
- 各模块都有权申请占用总线
- 采用同步方式通信, 不等对方回答
- 在准备数据时不会占用总线(节省了时间)
- 总线被占用时, 不会空闲, 一直在传输数据
充分提高了总线的有效占用.
3.6 几个名词
- 串行传送: 只有一条传输线, 按顺序传送表示一个数码的所有二进制位脉冲信号, 通常 第一位为最低位, 最后一位为最高位 ;
- 位时间: 一个二进制位在传输线上占用的时间长度;
- 波特率: 单位时间内传送码元(要用若干个比特表示的最小单位)的数目;
- 比特率: 单位时间内传送有效数据比特的数目;
这篇博文应该是至今以来最长的一篇, 并未分片.
至此,计算机组成原理这门课的大部分内容就梳理完毕了.
希望这篇博文能对后来的读者有所帮助, 此外, 博主限于水平有限, 难免出现各种知识上的纰漏与笔误, 还请各位谅解.
在这里再次对本文引用到PPT的作者致以诚挚的谢意.
这篇博文就到这里~