本文目录
前言
本系列文章将梳理《计算机组成原理》这门课的相关知识点,教材使用的是《计算机组成原理:硬件/软件接口》MIPS版,原书第五版。
本文所属章节为算术运算章节,重点内容为整数乘法和除法的改进版原理、IEEE754标准以及浮点加法和乘法。
一. 整数表示
这部分不是重点,补码反码这些都在《数字逻辑》里面学过了,读者应该已经掌握。我们重点讲一下符号扩展和大小端编址两个概念。
1. 符号扩展
什么是符号扩展?我们以addi $t0,$t1,100
为例。
因为addi的第一个操作数是32位的补码寄存器,第二个操作数却是16位的补码立即数,所以需要进行符号扩展后才能运算。具体怎么操作呢?只需要将最高位,即符号位复制到高16位即可。
注意:无符号数加法指令addiu指令,也是按照符号位进行扩展!所以如果要加载32位立即数,不能用addiu指令替代ori指令!
2. 大小端编址
大端编址:高位放低地址。以FFF0为例子,内存从上往下看是内存地址降低的方向,那么从上到下依次是F0、FF。
小端编址:高位放到高地址。
二. 定点加减法
1. 溢出的概念
- 上溢:正+正,正-负,即超出对符号位产生进位。
- 下溢:负+负、负-正,即对符号位产生借位。
- 双符号位判断溢出:
- 00:结果为正,无溢出
- 01:结果为正,正溢。
- 10:负溢。
- 11:结果为负,无溢出。
- 溢出的处理:
- 由 ALU中的硬件进行溢出检测
- 产生一个异常(中断)
- 将产生溢出的指令地址保存到异常程序计数器(EPC)中
- 跳到一个预先设定好的地址去执行相应的异常处理程序
- 溢出的产生:
- 有符号数会加法和减法会产生,无符号数不会!
这里补充两条关于字节的处理指令:
lbu:取无符号字节,将一个字节装入寄存器的最低8位。寄存器空余位由‘0’填满
lb: 取字节(有符号),将一个字节装入寄存器的最低8位,寄存器空余24位由符号位扩展填满
2. ALU(非重点)
这里只是简单过一遍ALU的原理。这块基本不太考察硬件设计。
2.1 加法器复习
2.2 ALU控制线
这块内容在第四章也有涉及到。
2.3 ALU的特点
- 32位ALU:行波进位。
- 低位进位输出指向高位进位输入。最低位的进位输入控制减法。
- 为支持slt指令,如果rs<rt成立,将1从最高位指向最低位的Less信号。
- 为实现溢出检测,最高位ALU会输出溢出信号。
- 改进ALU:超前进位
- 32位的ALU行波进位意味着每次都要通过AND和OR各一次,产生64个门延迟。
- 超前进位加法器的门延迟:要执行n位的加法,求出n是4的几次方向上取整,再乘以2加1 (具体推导有点复杂,记住结论)
- 例如, l o g 4 32 ? = 3 , 3 ∗ 2 + 1 = 7 log_{4}32?=3,3*2+1=7 log432?=3,3∗2+1=7,只需要7个门延迟。加速比高达9倍。
- 信号:overflow为1表示溢出,zero为1表示结果为0.
三. 乘法运算及其改进
1. 未改进版乘法分析
- 乘法运算的基础知识:
- 第一个乘数:被乘数。
- 第二个乘数:乘数。
- 结果:中间积。
- 未改进版算法:流程图如下图所示
- 测试乘数第0位。
- 如果是1,将被乘数加到积上,结果放到积寄存器。
- 将被乘数寄存器左移1位。
- 乘数寄存器右移1位。
- 判断是否重复了32次,如果是就结束。否则回到第一步。
- 电路特点:三个寄存器。
- 64位的积寄存器。
- 32位乘数寄存器。
- 64位的被乘数寄存器(因为需要一直左移)
- ALU:64位的ALU
2. 改进版乘法运算
- 前面的算法缺陷:
- 空间:被乘数寄存器左移后,低32位就空闲了,有点浪费空间。
- 时间:三个执行步骤会执行32轮,大约需要100个时钟周期。
思考:积寄存器是增长的,乘数寄存器是减小的,能不能综合利用?
- 改进版:
- 将乘积寄存器和乘数寄存器拼接为乘积乘数寄存器。
- 而被乘数只有32位,并且不动。
- 因为移位是相对的,只需要让积相对于被乘数右移即可。
- 将ALU改进为32位。
- 门电路:
-
改进后的算法步骤:
-
以2x3为例,展示算法流程:
- 初始化:被乘数0010一直不变,积/乘数0000/0011,一共4位,该进行4轮运算。
- 第一轮:判断,得知积/乘数寄存器最右边是1
- 积+=被乘数:注意是加在高位哦。0010/0011
- 寄存器右移(针对的都是积/乘数寄存器):0001/0001
- 第二轮:依旧是积/乘数寄存器最右边是1
- 积+=被乘数:0011/0001
- 寄存器右移:0001/1000
- 第三步:最右是0,右移即可,0000 1100
- 第四步,最右是0,右移即可,0000 0110
- 所以,答案就是0110啦!
3. MISP乘法指令
- 使用两个32位寄存器存放64位的乘积:Hi,Lo
- 两条乘法指令(R型,rd=0):
- 乘法:mult $S2, $S3
- 无符号乘法:multu $S2, $S3
- 注意:64位乘积存放在Hi,Lo中!
- 两条取乘积指令(R型,rs=0,rt=0) :
- 从Lo寄存器取数:mflo $S1
- 从Hi寄存器取数:mfhi $S1
四. 除法运算及其改进
1. 未改进版除法
- 原理:我们以手算除法为案例,本质上就是比较余数和除数的大小关系,从而决定商的大小,即:
- 硬件结构:
- 硬件解释:
- 32位除数:放在除数寄存器的左半边,每次迭代都要右移。
- 被除数:放到64位余数寄存器中,每次迭代向商寄存器的最低位添加0或1.
- 一共要进行33次迭代。
- 算法分析:每次迭代中
- 余数 - 除数
- 根据结果,决定继续往下或者恢复余数
- 根据结果,确定商为0或者1
2. 改进版除法(重点)
- 硬件结构:
- 商寄存器和余数寄存器的右半部分拼接在一起。
- 被除数放到余数/商寄存器的右半部分。
- 余数和商一起左移。
- 案例分析:以7/2为例。
最后,左边是余数,右边是商。
说实话我也不太理解这个原理,但是就考试而言,只需要记住推演流程:
除数不变,被除数初始放在余数商寄存器的右边,然后每次左移,如果说余数>0,就设置商最后一位是1,如果说余数小于0,就回复余数,确定最后一位是0。
一共只需要迭代n次,即对于32位的除法,一共只需要迭代32次。
3. MISP中的除法指令
- 两个32位寄存器:
- Hi 存放余数
- Lo 存放商
- 除法:div $S2, $S3
- 无符号除法:divu $S2, $S3
五. 浮点数表示及运算
1. 浮点数规格化
我们用-0.75为例子,首先需要知道如何将十进制数转换为二进制数。
其实非常简单,就是不断乘以2,然后取最高位即可。转换后是-0.11。
然后,我们将二进制数字转换为以2为基数的科学计数法,这个过程就叫做规格化。
在上面的例子中,也就是 − 1.1 ∗ 2 − 1 -1.1 * 2^{-1} −1.1∗2−1。可以发现,规格后的数字,小数点左边一定是1。
对于浮点数,有三个重要信息需要知晓:
- 正负—符号位
- 尾数—精度
- 指数—大小
2. IEEE754 单/双精度浮点数表示
IEEE754标准如下:
对于单精度,其位数如下:
对于双精度,其位数如下:
拿上面的例子来说明,-0.11的符号位是1,指数位置是
−
1
+
127
=
126
-1+127=126
−1+127=126,用IEEE754表示为尾数部分是1后面全0,即1 01111110 1000....000
Q:偏阶是怎么来的?
A:8位,我们的偏阶是 2 7 − 1 2^7-1 27−1。如果我们是双精度,也就是11位的指数域,那么偏阶就是 2 10 − 1 = 1023 2^{10}-1=1023 210−1=1023
- IEEE表示注意事项:
- 指数域为0,尾数为0,整个数就是0。
- 指数域为0,尾数非0,非规格化数
- 指数1-254,表示真正浮点数。
- 指数255,如果尾数是0,表示INF
- 指数255,尾数非0,表示NaN
2.1 浮点数表示范围
- 对于单精度:
- 最小:1.(23个0) * 2 − 126 2^{-126} 2−126
- 最大:1.(23个1) * 2 127 2^{127} 2127
- 对于双精度:
- 最小值:1.(52个0) * 2 − 1022 2^{-1022} 2−1022
- 最大值:1.(52个1) * 2 1023 2^{1023} 21023
3. 浮点加法、浮点乘法
3.1 浮点的溢出
- 上溢:正的指数太大而导致指数域放不下的情况。
- 下溢:负的指数太大而导致指数域放不下的情况。
3.2 浮点加法
- 算法分析:
- 第一步,小对大。指数小的转换为大的。
- 第二步,尾数相加。
- 第三步,规格化。
- 第四步,查询四舍五入以及指数是否溢出。高于127叫做上溢,低于-126叫做下溢。(注意,加法是运算结果溢出,这里是指数的溢出。)
以0.5+0.4375为例,首先规格化一下,0.5 = 1.0 ∗ 2 − 1 1.0 * 2^{-1} 1.0∗2−1,0.4375 = 1.11 ∗ 2 − 2 1.11 * 2^{-2} 1.11∗2−2
下面给出流程图供参考:
3.3 浮点乘法
- 算法分析:
- 第一步,将真正的指数相加。
- 第二步,有效位相乘。
- 第三步,规格化。
- 第四步,四舍五入以及溢出检查。
- 第五步,定符号,我们是一开始记住符号的。
3.4 MISP浮点指令(了解)
- 寄存器:
- 存放32位单精度浮点数: $f0, $f1, … $f31
- 存放双精度浮点数: f 0 / f0/ f0/f1, f 2 / f2/ f2/f3, …
- 存取指令:
- lwcl, ldcl, swcl, sdcl(e.g
ldcl $f8, 32($sp
- lwcl, ldcl, swcl, sdcl(e.g
- 运算指令:
- 单精度:add.s, sub.s, mul.s, div.s
- 双精度:add.d, sub.d, mul.d, div.d
- 比较:c.x.s,c.x.d
- 分支:bclt( cond=1时跳转)、bclf( cond=0时跳转)
至此,第三章算术运算的内容全部梳理完成。