这些比较重要的数字电路模块,可以适当的记一记

本系列主要是《数字设计和计算机体系结构》和一些相关书籍的读书笔记,并没有专门为读者的阅读体验认真做设计,敬请谅解(时间不多)。

下边这些数字电路模块是构成CPU的基础,在了解CPU执行指令过程之前,需要把这些模块的原理都搞清楚。

加法器

前边说过处理单个二进制位相加的加法器:

这些比较重要的数字电路模块,可以适当的记一记

但是我们使用的加法是好多位的,这也好说,把多个全加器串联起来就好了:

这些比较重要的数字电路模块,可以适当的记一记

从右到左看这个电路图,这是一个32位二进制加法的电路图。初始的时候进位为0,然后每一位相加的进位输出当作高一位相加的进位输入,这样就跟接力棒一样,以此经过各个全加器之后,最后 C31S31S30S29S28…S2S1S0 就是最后的加法结果。我们把这个电路画个简单的示意图:

这些比较重要的数字电路模块,可以适当的记一记

这种加法器由于需要先使用低位的加法器,把进位输出当作高一位的进位输入类似接力棒的方式传递进位,所以效率有点慢。后来人们又发明了些别的加法器电路,以求更快的实现二进制加法运算,比如先行进位加法器、行波进位加法器啥的,由于时间有限,我就不想看了,等以后时间充裕了再研究。

对于减法来说,某种程度上可以把它转换成一种加法。比方说我们现在处理10以内的减法,比方说下边这个式子:

5 - 2 = 3

其实这个减法可以转换成这样的写法:

5 + (10 - 2) - 10 = 5 + 8 - 10

其中,2和8是相对于10的补数,a-b相当于a和b补数的和再减去进位。对于二进制减法,我们也可以用这个方式。比方说我们做4位二进制内的减法,比方说这样

1001 - 0010 = 1001 + (10000 - 0010) - 10000

怎么求一个给定位数的二进制数的补数呢?这个超级简单:

  1. 先把一个二进制数的每一位都取反,比方说这样0010的每一位都取反就是1101,
  2. 这样0010 + 1101 = 1111,但是两个互为补数的和是10000,所以让取反后的结果再加1就好了,也就是0010的4位二进制补数就是1101 + 1 = 1110。

所以:

1001 - 0010 = 1001 + 1110 - 10000

用通俗一点的语言描述一下,比方说求A-B的结果,我们把B取反后的结果记为B,然后B的4为二进制数的补数就是B + 1。所以:

A - B = A + B + 1 - 10000

对于4位二进制数的加法,我们只有4个全加器,最后的进位会被放到C3中,我们只需要忽略这个最高位的进位,就相当于减去了10000。

所以我们可以这样修改一下上边的加法器来让它可以做减法:

这些比较重要的数字电路模块,可以适当的记一记

与上边的加法器电路相比,改了两个地方:

  1. 初始的进位输入从0改为了1。
  2. 减数B会被取反。

那怎么表示负数呢?-5不就是相当于0 - 5么,也就是说负数相当于0和这个数对应的绝对值的减法,二进制的负数也是这个意思。0减去一个数相当于和这个数的补数相加后减去进位,0作为加数没有什么意义,也就是说负数相当于它的绝对值的补数减去进位。因为进位没啥意义,我们不关心,所以一个负数就相当于它绝对值的补数。我们以4位二进制数为例,看看怎么表示十进制数-5:

  1. 首先十进制数5用4位二进制数表示出来就是0101。
  2. 求0101的补数。先取反得1010,再加1,得1011。

也就是说这个十进制数-5用4位二进制数表示得结果就是1011。可是我们怎么区别1011表示得是-5还是11呢?规定:首位为1的就是负数,首位为0得就是正数,这样就成功的把负数表示出来了。

但是使用首位来区分正负数的话,4位二进制数就不能表示0 ~ 15这16个非负数了,表示范围变成了-8~7。8位、16位、32位、64位的数值表示和减法运算都可以参照4位二进制数来计算。

比较器

对于两个给定的输入,我们怎么知道这两个给定的输入是否完全相同呢?先把问题简化一下,怎么判断两个二进制位是否相同?我们可以规定:如果输出为1则相同,为0则不相同。很容易想到这个异或门的定义:输入相同得0,输入不同得1:,所以我们使用异或非门(NXOR)就可以达到我们得目的:

这些比较重要的数字电路模块,可以适当的记一记

如果需要比较得两个输入中分别有n位,那意味着只要n位中有一位比较输出为0,那么整个比较输出就是0,很容易想到用与门来完成这个汇聚各个位比较结果的任务(以4位比较器为例):

这些比较重要的数字电路模块,可以适当的记一记

算术逻辑单元

不知道你有没有觉得我们不知不觉学了好多电路模块,现在我们提到加法器,只需要知道把输入和输出接上去,然后这个电路自动就帮我们把结果输出来了,对于一个需要使用加法器得小朋友来说,他就不需要理解这个加法器内部到底是怎么构造出来的,使用了哪些逻辑门,我们再一次强调抽象的好处:你不需要了解过多的实现细节,照着说明书(提供的接口)使用就行了。这个抽象的过程我们有时候也称为封装,用户不关心某个模块的内部实现,只需要调用这个模块提供的接口就好。对于我们前边说的一些电路模块,我们想把它们再次进行一次抽象,以提供更简单的使用接口,这就是我们所说的ALU(算术逻辑单元),直接看下边这个ALU的一个实现电路图:

这些比较重要的数字电路模块,可以适当的记一记

这个电路共有5个输入,可以被分为两种类型

  • 数据输入,包括:输入A和输入B
  • 控制输入,包括:输入F0、F1和F2,图中F0和F1写在了一起,以这种符号表示:F1:0。

这个所谓的ALU就是把各种运算模块集中到了一起,用户可以通过修改控制信号F0、F1和F2的输入来控制对数据输入A和B进行何种运算,各个控制信号对应的运算类型如下:

控制信号F2:0的值运算类型000A AND B001A OR B010A + B001未使用100A AND B101A AND B110A - B111判断A<B是否成立

我们以减法运算为例看一下上边的ALU是怎么工作的。从表格中可以看到,处理减法运算的控制信号为:F2=1,F1=1,F0=0。

  1. 当F2=1时,最上边的2-1选择器会输出将信号B取反后的结果,并且加法器的进位输入的值也为1。所以该加法器实际执行的是A - B的操作。
  2. 当F1=1,F0=0时,最下边的4-1选择器会选择输出加法器的运算输出,也就是输出Y=A - B。

其他类型的运算的道理是一样的,就不一一推导了。最后再说一下,控制信号F2=1,F1=1,F0=1的情况表示判断A<B是否成立,如果A<B成立,则Y=1,否则Y=0。这是怎么做到的呢?还是利用了加法器做了A - B的操作,如果结果为负,意味着加法器的输出的符号为为1,那图中的0扩展的电路黑盒就是把符号位作为Y的最后一位,其他位补0的作用,所以当Y=1时意味着A<B。反之当Y=0时,A<B不成立。

所以之后我们再用到这个ALU时,就不用再考虑它里边复杂的电路是怎么画的了,只需要用下边简单的示意图代替就好:

这些比较重要的数字电路模块,可以适当的记一记

如此简洁。。。抽象的强大作用,以后我们想做点简单的运算时就可以直接把这个ALU给拿出来,输入对应的数据和控制性好就可以得到我们的结果,而不用关心它里边用了啥神器的魔术,666666666。更复杂的ALU中需要的电路更多,控制信号也会更多,我们这个只是做个演示,让大家知道原理的一个简易版ALU。

移位器

我们编程序的时候会经常使用到下边几种移位操作:

  • 左移:将所有二进制位向左移动若干位,在低位补0。
  • 逻辑右移:将所有二进制位向右移动若干位,在高位补0。
  • 算术右移:将所有二进制位向右移动若干位,在高位补与符号为相同的值,也就是正数的算数右移高位补0,负数的算数右移高位补1。

下边以4位二进制数的左移为例来看一下电路图:

这些比较重要的数字电路模块,可以适当的记一记

A3A2A1A0是我们指定的4位二进制数,shamt1:0是我们需要左移的位数,它连接着下边的4个4-1选择器。大家可以自己分析一下当移动位数从0~3的这几种情况分别会发生什么。

乘法器

二进制乘法比十进制乘法更加简单,因为我们都不用再背九九乘法表,它的单个位相乘的运算规则超级简单:

×01000101

这样的规律和与门的运算是一样一样的,所以我们可以很轻易的使用与门去完成单个位的乘法运算。对于多个位相乘的运算只不过是在重复但个位的运算规律而已,如下:

这些比较重要的数字电路模块,可以适当的记一记

0100和0101分别代表十进制的4和5,最后的结果1010代表十进制的20。4位二进制相乘的通用示意图可以这样表示:

这些比较重要的数字电路模块,可以适当的记一记

结果就是P7P6P5P4P3P2P1P0。下边是一个电路实现:

这些比较重要的数字电路模块,可以适当的记一记

是的,很复杂,当位数多一点的话更复杂,所以计算乘法的时候还是比较耗时的。

除法器

除法器更麻烦,就不说了。反正最终还是可以通过电路实现的,我们调用接口就好了(抽象的好处~)。

计数器

有时候我们需要计数,也就是数数,0,1,2,3,4,5,6…可以使用下边这个电路:

这些比较重要的数字电路模块,可以适当的记一记

其中复位信号是让寄存器R1的输出Q为0,然后每当时钟上升沿到来时,加法器的输出值都加1。也就是说这个电路可以对时钟周期进行计数。这个很有用。


分享到:


相關文章: