算术逻辑单元

复习

  1. 第七章:设计完成了加法器,可以处理三个输入(两个加数和一个进位),并可以串联构成多位加法器
  2. 第八章:掌握了原码、反码和补码的概念,理解了补码可以统一加减法运算
  3. 第九章:设计完成了减法器,本质是加上一个负数(补码)
  4. 第十章:设计完成了乘法器,本质是多次加法,也可以用 Booth 算法等进行优化
  5. 第十一章:设计完成了除法器,本质是移位和减法的组合

TL;DR

  • 算术逻辑单元(ALU)是计算机的核心运算部件
  • ALU 可以完成算术运算(加减乘除)和逻辑运算(与或非等)
  • ALU 通过控制信号选择不同的运算功能

正文

引言

  回想一下,现在有了什么:加法,减法,乘法,除法。可以做一些基础的运算了。除此之外,还有一些逻辑操作:与,或,非,异或,移位等等。

  注意,花十秒钟想一下,上面那些东西是怎么来的。如果不能清楚明白地了解其组成和原理,说明还没有学懂,建议回头再看一次。 这里简单提示一下:

  1. 晶体管组成逻辑门(与,或,非,异或等);
  2. 部分逻辑门的计算逻辑与加法一致,所以组成加法器;
  3. 分出一位作为符号,在逻辑上弥补二进制减法的问题,使用 诱骗加法器在形式上做减法,形成减法器;
  4. 用加法器和减法器,组成乘法器和除法器,如果愿意,其中可以有移位器。

  利用上面的模块,一个简单的 ALU(Arithmetic Logic Unit,算术逻辑单元)就产生了。

设计简单 ALU

  我们可以尝试简单设计一个 4 位 ALU。

准备基本部件

  设计的 ALU 预计有 12 个功能:加、减、乘、除、取余、与、或、非、与非、或非、异或、同或。

  先列出需要的部件,同时列出输入和输出。

  注意:

  1. 因为非门只有一个输入,所以此处硬编码为 A 引脚,需要取反的数值应该永远连接到 A 输入上。
  2. 加法器、减法器等等运算部件,还有一个额外的输出:溢出,所以我们也需要将它连接到输出。但因为连接这些溢出输出会使电路变得复杂,所以我们暂时不考虑连接。

连接运算电路

  现在很明显问题来了。

  1. 对于这些运算的输出,连接到哪里?
  2. 所有部件都在运算,我们怎么知道哪种运算是我们需要的?换而言之,我们怎么控制 ALU 做特定的运算?
对于第一个问题

  最简单的方法是所有运算的输出都连接到同一根输出线上,只要控制好不要数据冲突就好了,这也是最简单的方式了。

  为此,我们需要一个控制开关:由一个控制信号 Enable 控制计算结果是否输出。

  它的构成也很简单:

  对于 1 位开关,真值表一画,其实就是一个与门。

输入 Data控制信号 Enable输出
000
010
100
111

  对于多位开关,就是位宽个与门(4 位就是 4 个,8 位就是 8 个)。每个与门的输入都是:控制信号 + 数据信号的其中 1 位。

  加上控制开关,ALU 大概是这样。

对于第二个问题

  最简单的方法是每一种运算单独连一根线,然后根据线的通断来决定做什么运算:加法一根线,减法一根线,乘法一根线,除法一根线,等等。

  但是!仔细考虑一下,这个简单的 ALU 就有 加、减、乘、除、取余、与、或、非、与非、或非、异或、同或 这 12 种运算,按照这个方法,就需要 12 根线。要是功能更强大的 ALU,运算种类更多,线的数量就会爆炸式增长,有什么办法能优化吗?

  想一想,每一根线对应一个运算控制,而每一根线只有通电或者不通电两种状态……好像和二进制又有些像?

  如果我们把这些线的通电状态转化为二进制数,不就可以大大压缩线的数量了吗?2¹¹ 甚至能表示 2048 种运算,但此处的 12 种运算实际上只需要 4 根线:2³(8) < 12 < 2⁴(16)

添加选择逻辑

  我们可以用 4 根线来表示 12 种运算。为了方便表示,我们规定:

控制信号功能
0000加法
0001减法
0010乘法
0011除法
0100取余
0101
0110
0111
1000与非
1001或非
1010异或
1011同或

  恭喜你!我们现在已经初步接触了“指令”的概念。我们可以把上面的控制信号当作指令,告诉 ALU 做什么运算。

  那么,我们应该怎么翻译这些指令呢?

  有一个元件叫做译码器(Decoder),作用是把输入的二进制位翻译成特定的对象(如逻辑电平等)。看图就懂了。

  而上面规定的控制信号指令,恰好也递增。

  所以我们可以额外添加一个输出 op_code(操作码),告诉 ALU 做什么运算,然后用译码器解码这个指令 op_code。

  然后,ALU 就完成了。

验证 ALU

  验证我们所涉及的 ALU 是否正确:设 A = 4(0100),B = 2(0010),根据操作码计算结果。如果正确的话,应该结果为下面这张表。

加法减法乘法除法取余与非或非异或同或
011000101000001000000000011010111111100101101001

  稍微把这个电路改造一下,让操作码自增,依次观察结果。

  结果正确。

状态标志

  现代 ALU 在运算过程中会设置一些状态标志位,用于表示运算的特殊情况:

  • 零标志(Zero Flag,ZF):结果为零时置 1
  • 符号标志(Sign Flag,SF):结果为负数时置 1
  • 进位标志(Carry Flag,CF):产生进位时置 1
  • 溢出标志(Overflow Flag,OF):结果超出表示范围时置 1

  CF 和 OF 的区别在于,CF 对无符号数运算有意义,而 OF 对有符号数运算有意义。

  • 4 位有符号数范围:-8 到 7,无符号数范围:0 到 15(F)
  • 0111(7) + 0001(1) = 1000(无符号数 8,有符号数 -8)
    • 执行后 CF 为 0,OF 为 1(对于无符号数,结果在范围内,没有进位;对于有符号数,结果超出范围,OF 为 1)
  • 0111(7) + 1001(无符号 9,有符号 -7) = (1)0000(无符号数 16,因进位溢出,故输出为 0;有符号数为 0)
    • 执行后 CF 为 1,OF 为 0(对于无符号数,最高位进位,CF 为 1,对于有符号数,结果没有超出范围,OF 为 0)
  • 1000(无符号 8,有符号 -8) + 1111(无符号 15,有符号 -1) = (1)0111(无符号数 23,因进位溢出,故输出为 7;有符号数 -9,因进位溢出,输出为 7)
    • 执行后 CF 为 1,OF 为 1(对于无符号数,CF 为 1;对于有符号数,结果超出范围,OF 为 1)

  其实现代 ALU 还有其他很多状态标志位,比如奇偶标志(Parity Flag,PF)等,这里仅列出四个常用的标志位。

  现代 ALU 还能进行比较运算,如比较两个数的大小,将在后面章节介绍。

状态标志的生成

  要实现这些标志位也很简单,只需要额外添加一些逻辑门即可。

  • 零标志:有符号数无符号数的 0 都只有一个编码,所以确保每一位都为 0 即可,即每一位相或结果为 0 时置 1(或非门)
  • 符号标志:直接使用最高位符号位的值
  • 进位标志:使用进位输出
  • 溢出标志:两个操作数数符号位相同,且结果符号位与原符号位不同时置 1(两个操作数符号位不同,则 OF 永远不可能为 1)

  添加逻辑后,ALU 就完成了。

小结

知识点

  • ALU 的组成
  • 设计简单 ALU
  • 简单的“指令”概念

参考资料

推荐

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪