少女祈祷中...

整数运算

算术逻辑单元ALU

计算机实际完成数据算术逻辑运算的部件

  • 数据由寄存器提交给ALU,运算结果也存于寄存器
  • ALU也可以产生一些与运算结果有关的标志值存储在寄存器中
  • 控制器提供控制ALU操作和数据传入送出ALU的信号

全加器

一位加法器:

X和Y表示加数,F表示当前位的结果,Cout表示向高位的进位,Cin表示上一位低位在本位的进位

Cin、X、Y经过逻辑运算可以计算出Cout和F

一位加法器的结构:

假设与门、或门都需要1级门延迟(1ty),异或门需要3级门延迟(3ty),那么Cout的形成需要2ty,F的形成需要6ty。(这点很重要,因为一般只要直到了Cout就可以继续计算下一位加法,而不需要等到F也计算完)

也可以改进一位加法器的结构:

串行进位(行波进位)加法器

缺点:延迟慢

  • CnC_n需要至少2n级门延迟(每次都需要等待上一级的进位Cn1C_{n-1}计算完毕才可以继续计算,每个进位都需要2级门延迟)
  • FnF_n需要至少2n+1级门延迟(不是2n+4是因为计算F_n的六级门延迟是基于X、Y和Cn-1同时开启时的,而实际上在计算Fn时,Xn和Yn早就已经开启了,因此就可以提前通过与Xn和Yn有关的门,然后最后等到Cn-1也出现再通过剩下的门,就可以缩短门延迟至2n+1)

问题:高位的运算必须等待低位的“进位输出信号”。有没有办法能否提前计算出“进位输出信号” ?

全先行进位加法器

我们可以利用公式直接通过每位X和Y推导出Cn的值。

最后的全先行进位加法器是这样的:

延迟为1ty+2ty+3ty = 6ty,并且延迟与加法器的位数无关。(计算方法:第一步算出PiP_iGiG_i,需要一级门延迟;然后对其继续与和或的操作算出CiC_i,需要两级门延迟.(与此同步进行的是X与Y的异或,它也需要3个门延迟,所以在计算出进位的同时这个异或也能够计算完成)最后通过一个异或门,将X与Y的异或结果再与刚刚计算出的进位进行异或操作,算出FiF_i,需要三级门延迟。因此最后就是1+2+3 = 6)

部分先行进位加法器

相当于结合了行波进位(RCA)和全先行进位(CLA)

  • 行波进位:65级门延迟(太大了)
  • 全先行进位:6级门延迟,但是难以实现,C32需要一个或门和32个与门,最大需要33输入的与门和或门

部分先行进位加法器:串联4个8位CLA,12级门延迟

12级门延迟的计算方法:首先,由于PiP_iQiQ_i都是需要每一部分的最初进位的,而最初只有最右边的C0C_0最初进位是已知的,但是当前所有的xix_iyiy_i都是已知的,因此第一级门延迟我们就可以计算出所有PiP_iQiQ_i。然后再经过二级门延迟计算出最右边的最终进位C8C_8。这样,第二个加法器的最初进位C8C_8就已知了,而由于所有的PiP_iQiQ_i都已经计算出来了,所以只需要再经过二级门延迟就可以计算出C16C_{16},以此类推,直到计算出C24C_{24},此时消耗了3+2+2=7级门延迟。而最后我们只需要计算进位和本位的结果。计算进位需要2级门延迟,而计算本位需要使用它计算出的进位来进行异或操作,因此在计算出这个全加器中的所有的C之后还需要额外的三级门延迟计算出本位的结果。最后一个加法器总体就是3+2=5级门延迟。因此,总体的部分先行加法器的延迟就是3+2+2+5 = 12

计算

加法:补码相加,结果直接截取后n位,忽略进位。

如何判断溢出?

可以通过X与Y与Fn或Cn与Cn-1的逻辑运算算出是否溢出

减法:X-Y相当于X+(-Y)的补码

溢出与加法相同

乘法:与竖式计算方式相同

存在的问题:在整体计算的时候需要能够支持n位右移(乘数右移判断当前位数)2n位左移的寄存器(被除数左移,让被乘数整体基数变大),以及2n位加法器(用于将被乘数与部分积相加)

优化方法:
1、加法和移位并行

  • 时钟上升沿到来前先取出数据,之后寄存器的变化不会影响取出的数据

图中将左移、右移、加法操作设为了并行。

2、减少不必要的硬件
可以使用一个支持2n位的寄存器来实现乘数和乘积共用。现在只需要一个n位被乘数寄存器和一个2n位寄存器,以及n位加法器

最初部分积在乘数左边,为0,然后每次根据乘数的最后一位对部分积实施加或不加被乘数,然后最后总体为0。

简化了整体的操作,不再需要对多个寄存器进行移位,只需要对2n位寄存器整体移位

当然,寄存器中值的改变和移位寄存器还是需要先后顺序

乘法存在的问题:无法处理负数的情况

原码一位乘法

  • 被乘数与乘数用原码表示,数值位和符号位分开计算
  • 最后结果再改为补码表示

补码一位乘法:布斯乘法

它与普通乘法的不同在于:判断的是乘数最后两位的情况,对部分积实施加或减(加补码)

判断是否溢出:

简单来说就是判断高n位的情况。

阵列乘法器:

除法

需要根据被除数和除数的情况做特殊处理:

手工演算除法:在被除数的左侧补充符号位,将除数的最高位与被除数的次高位对齐;从被除数中减去除数,若够减,则上商为1;若不够减,则上商为0;然后右移除数

除法运算过程:

简单来讲就是,每次判断余数是否大于除数,如果大于,就减去除数,上商为1,否则不做,然后上商为0;然后除数右移一位

优化方法:将余数和商放在一起形成一个支持左移的2n位寄存器,然后使用一个n位寄存器表示除数。

最初这个2n位寄存器中存放的是被除数补码符号扩展的结果

异号除法:使用补码加法来表示减法。商需要取补码,并且余数本来就是补码

通常,除法分为恢复余数除法和不恢复余数除法

恢复余数除法

先把被除数减去除数,如果够减,就不做,如果不够减,就加回刚刚减去的数。然后整体左移一位

  • 如何判断“够减”:就是看余数在减(加)完后会不会变号;如果会变号,说明不够减。(注意:异号除法中用补码加法表示减)

计算过程:

除数寄存器始终不变,每次减完除数后根据余数的符号变化执行具体操作

不恢复余数除法

恢复余数成本过高,因此设计了一种办法,不需要恢复余数,而是在下一步中执行不同的操作来实现。

这里面的Ri都是表示已经减去除数Y过的余数。如果余数足够大,那么新的余数就是Ri直接右移一位然后减去除数Y。如果余数不够大,那么就应该在移位前先加一个除数Y,然后再移位,再减去Y。所以最后化成公式就是2Ri+Y,也就是相当于如果不够减,在下一步减去除数时改为加上除数即可。

因此,最后的除法流程就可以变成这样:

注意在完成步骤后,还需要进行商的修正与余数的修正。

这样的修正,简单来说就是:商需要左移,左移补的位数由余数和除数的符号决定,应该是对两者异或取反。然后最后如果被除数和除数异号,还得+1。然后余数的修正就是:正常情况下余数符号和被除数应该相等,如果现在符号不同了,那就得加一个除数或减一个除数,把它符号搞的和被除数一样。

一般一个变量除以2n2^n时,采取的是右移操作。如果不能整除,那么最后的位直接朝零舍入(直接去掉)。

阵列除法器: