信息在传递的过程中可能会因为噪声的干扰而发生比特位的跳变,为了检查数据传输过程中是否发生了错误,我们会使用一系列的校验码来检查收到的信息。奇偶校验码就是一种简单的手段。假设要传输的信息7bit,我们在最高位添加上1bit的校验位。奇校验要求再添加过校验位之后,整个8bit信息中字符“1”的个数是奇数;偶校验则要求该数值为偶数。
对于偶校验,计算机想要得到校验位只需要将原本的所有信息位做异或即可。同样,若要检查收到的信息是否出错,只需要将接收到的8bit的信息做异或,结果为“1”则出错,但是结果为“0”并不代表没有错误,这是因为如果有偶数个bit发生跳变,异或的结果是不变的。因此奇偶校验码无法检测出偶数位错误。
ALU是运算器的核心部件,它的主要功能是做一些算术运算和逻辑运算。我们可以通过最基本的逻辑门电路,组合出自己需要的电路来实现不同的运算。
我们所说的比特0或1是通过给电子元器件加电压来实现的,低电压为0,高电压为1.最基本的三种基本的逻辑运算为逻辑与、逻辑或、逻辑非,将他们的门电路组合起来就可以得到我们想要的不同逻辑运算。其中逻辑异或与加法、偶校验有着天然的对应关系。
假设A、B分别表示要相加的两个二进制数, A i 、 B i A_{i}、B_{i} Ai、Bi表示A和B的第i位, S i S_{i} Si表示第i位和。如果我们简单的认为 S i = A i + B i S_{i}=A_{i}+B_{i} Si=Ai+Bi,这是有问题的,因为低位向本位的进位也是必须要考虑的一个因素,我们将它记为 C i − 1 C_{i-1} Ci−1,那么第i位和的正确表达是应该是 S i = A i + B i + C i − 1 S_{i}=A_{i}+B_{i}+C_{i-1} Si=Ai+Bi+Ci−1。我们来统计一下这个表达式的可能取值:
A i A_{i} Ai | B i B_{i} Bi | C i − 1 C_{i-1} Ci−1 | S i S_{i} Si | C i C_{i} Ci |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
可以看出,第i位和 S i S_{i} Si只有在 A i 、 B i 和 C i − 1 A_{i}、B_{i}和C_{i-1} Ai、Bi和Ci−1三者中有奇数个“1” 时为“1”,否则为“0”.这个逻辑很“异或”。
第i为向高位产生的进位 C i C_{i} Ci在两种情况下为“1”:1. A i 、 B i A_{i}、B_{i} Ai、Bi都为“1”时。2. A i 、 B i A_{i}、B_{i} Ai、Bi有一个为“1”并且 C i − 1 C_{i-1} Ci−1为“1”时。
于是便可以写出 S i S_{i} Si和 C i C_{i} Ci的逻辑表达式:
S i = A i ⊕ B i ⊕ C i − 1 S_{i}=A_{i}\oplus B_{i}\oplus C_{i-1} Si=Ai⊕Bi⊕Ci−1
C i = A i B i + ( A i ⊕ B i ) C i − 1 C_{i}=A_{i}B_{i}+(A_{i}\oplus B_{i})C_{i-1} Ci=AiBi+(Ai⊕Bi)Ci−1
有了逻辑表达式也就有了电路,接下来只需要按照逻辑表达式将对应的门电路连接好就可以得到一位全加器。
串行加法器的实现方式是在一个全加器的基础上添加一个进位触发器,用来保存第i位产生的进位。将 S i S_{i} Si作为该触发器的输入端, C i − 1 C_{i-1} Ci−1作为该触发器的输出端,这就使得想要计算两个数的和必须要逐位计算。若计算nbit的数相加,就需要分n次计算,计算的结果也需要串行地、逐位地存入寄存器。
串行进位的并行加法器就是将n个全加器串接起来,这样就可以直接进行两个nbit的数相加。这种加法器的缺点也比较明显:高位需要等待低位来的进位才能够计算,因此效率并不高。
由于串行进位的并行加法器效率不高的主要原因是来自低位的进位需要等待低位的计算,因此想要提高计算速度,可以从该处入手。
根据之前计算过的逻辑表达式 C i = A i B i + ( A i ⊕ B i ) C i − 1 C_{i}=A_{i}B_{i}+(A_{i}\oplus B_{i})C_{i-1} Ci=AiBi+(Ai⊕Bi)Ci−1不难看出,该表达式可以递归,只需要将 C i − 1 C_{i-1} Ci−1继续向下逐层展开代入,最终会展开到关于A、B的每一位和 C 0 C_{0} C0表达式,而这些信息都是一开始就知道的,这也就意味着我们需要的进位信息可以直接得到,不需要等待低位的计算,这样就大大地提高了计算效率。
对于补码加法来说,前边设计的加法器完全可以满足需求。而对于补码的减法,在「计算机组成原理」数据的表示和运算(一)3.2.2中提到过,我们要先将减法转换成加法,方法就是全部位按位取反末位+1。
全部位按位取反我们只需要在加法器的其中一个加数的位置连接一个多路选择器,其功能是保证做减法时,先把操作数按位取反。
末位+1我们可以设置一个控制信号Sub,将其与多路选择器和低位的进位 C i n C_{in} Cin相连,其值为1时代表减法,值为0时代表加法。这样就实现了按位取反末位+1的操作。
由于无符号数的加减运算和补码的加减运算方式一样,因此此套电路也适用于无符号数的加减运算。
除了之前的几个输出信息之外,标志位也是一个加法器重要的输出信息。主要的标识为有4个,分别是OF、SF、ZF和CF.其中OF和SF仅对有符号数的运算才有意义,CF仅对无符号数的运算才意义。
定点数的移位运算可以快速的扩大或缩小定点数的倍数。对于有符号数,可以使用算数移位。对于无符号数,可以使用逻辑移位或循环移位。
由于正数的三码一样,因此他们算数移位的方式也是一样的:右移舍弃低位,高位补0;左移舍弃高位,低位补0. 由于会有舍弃,因此若舍弃的位不为0,会出现误差甚至严重的错误(因为左移舍弃的是主要的高位)
对于负数来说就要分为原码、反码和补码三种情况分别讨论:
1.负数的原码:右移舍弃低位,高位补0;左移舍弃高位,低位补0.
2.负数的反码:右移舍弃低位,高位补1;左移舍弃高位,低位补1.
3.负数的补码:右移舍弃低位,高位补1;左移舍弃高位,低位补0.
原码和反码的补位方法很容易会想到。补码之所以高位补1低位补0是因为补码是在反码的基础上+1得到的,因此补码的最低位一定是与原码一致的,因此要左移遵循原码的方式。而右移相当于扩展的是反码的位置,因此需要遵循反码的方式补1.其实稍作分析就会发现,负数补码从低位向高位的第一个“1”及其右边的所有位和原码一样;该“1”左边的所有位同反码一样。
总结一下:
码制 | 填补代码 | |
---|---|---|
正数 | 原码、补码、反码 | 0 |
负数 | 原码 | 0 |
反码 | 1 | |
补码 | 右移补1 | |
左移补0 |
逻辑移位一般是对于无符号数来说的,他的规则比较简单,无论左右还是右移,舍弃相应的位然后在新位置补0即可。
循环移位分为两种:带进位位CF的循环移位和不带进位位CF的循环移位。都很简单,顾名思义,就是把移出的部分补到空出的部分。带CF那CF就跟着一起移动;不带CF那CF就不要动,让剩下所有位进行循环移位。