Altifigence Academy

51 / 63 · 概念

ALU 与 zero、negative、carry、overflow 标志

用共享加法器实现 ADD/SUB,区分进位与有符号溢出,并推导比较条件。

先修知识:应理解二进制补码二进制补码 用固定位宽位串表示有符号整数的方法。n 位范围为 −2^(n−1) 到 2^(n−1)−1。了解更多、加法器、组合逻辑 MUX多路选择器 根据选择信号,把多个输入之一连接到输出的组合电路。必须规定选择位与输入编号的对应关系。了解更多及位宽扩展。

同时产生结果与状态的 ALU

ALU 从各运算电路的结果中选择一个,并输出解释结果所需的状态位。本设计的输入 A,BA,B 与结果 RR 均为 WW 位,支持 ADD、SUB、AND、OR、XOR。算术结果始终截为低 WW 位。同一位串可按 unsignedunsigned 将位串解释为非负整数的规则。n 位范围为 0 到 2^n−1,可能与同一位串的 signed 解释不同。了解更多 或二进制补码 signed 解释,因此分别输出 carry 与 overflow。无需为不同符号解释各做一个加法器。

标志为 Z=(R=0)Z=(R=0)、N=RW−1N=R_{W-1}。Z 是对全部所选结果做 NOR 归约,N 是最高位。N=1 表示所存位串按 signed 解释为负数;若发生溢出,它可能与无限精度下真实和的符号不同。

区分 carry 与 overflow

加法中,扩展和 TT 的最高位就是 C。

T={0,A}+{0,B},R=T[W−1:0],C=T[W]T=\{0,A\}+\{0,B\},\qquad R=T[W-1:0],\quad C=T[W]

两个输入同号而结果变号时,V=1。将输入与结果的最高位分别记为 a,b,ra,b,r,则有:

Vadd=¬(a⊕b)∧(a⊕r)V_{\mathrm{add}}=\neg(a\oplus b)\land(a\oplus r)

八位运算中,255+1255+1 得到 R=0、C=1、V=0。按 signed 解释为 −1+1=0-1+1=0,没有超出表示范围。相反,127+1127+1 得到 R=0x80、C=0、V=1。unsigned 和 128 可以表示,但超出 signed 最大值 127。若把 C 与 V 合并,就无法区分这两种情况。

减法只对 W 位 B 取反取反 将 0 变为 1、1 变为 0 的 NOT 运算。表达式上方的横线表示对其覆盖的整个表达式应用 NOT。了解更多,随后在前面补 0 扩展,即 T={0,A}+{0,¬B}+1T=\{0,A\}+\{0,\neg B\}+1。若对整个 W+1 位取反,最高位进位的含义会改变。本课规定 C=1 表示没有借位,因此 unsigned 比较 A<BA<B 等于 ¬C\neg C。

Vsub=(a⊕b)∧(a⊕r)V_{\mathrm{sub}}=(a\oplus b)\land(a\oplus r)

只有异号相减才可能发生 signed 溢出。比较指令可只使用 SUB 的标志,不保存 R。signed 的 A<BA<B 为 N⊕VN\oplus V,A≤BA\le B 为 Z∨(N⊕V)Z\lor(N\oplus V)。例如 −128−1-128-1 的存储结果为 +127,但 V=1 可修正仅看 N 所造成的错误。

这是八位 ADD 的一个例子。unsigned 结果能够表示,但 signed 结果越界。请确认同一次加法中 C=0 与 V=1 可以同时成立。

在 127 + 1 中区分 C 与 V
  1. A=7F · B=01: signed 127 + 1
  2. 九位和 T: 0_10000000
  3. 八位结果 R: 80 → signed −128
  4. 状态标志: Z=0 · N=1 · C=0 · V=1

共享加法器与明确的输出规则

以下 RTL 以 W≥2 为前提。opcode 0~4 分别为 ADD、SUB、AND、OR、XOR。逻辑运算令 C、V 为 0。未使用 opcode 令 R=0,因此 Z=1,其余标志为 0。这些规则是此 ALU 的接口约定,并非仿照某个 CPU 指令集。

SystemVerilog
module alu #(parameter int W = 8) (
  input  logic [W-1:0] a, b,
  input  logic [2:0] op,
  output logic [W-1:0] y,
  output logic z, n, c, v
);
  logic sub;
  logic [W-1:0] bx;
  logic [W:0] sum;
  always_comb begin
    sub = (op == 3'd1);
    bx = b ^ {W{sub}};
    sum = {1'b0, a} + {1'b0, bx}
        + {{W{1'b0}}, sub};
    y = '0;
    c = 1'b0;
    v = 1'b0;
    case (op)
      3'd0, 3'd1: begin
        y = sum[W-1:0];
        c = sum[W];
        if (sub)
          v = (a[W-1] ^ b[W-1]) & (a[W-1] ^ y[W-1]);
        else
          v = ~(a[W-1] ^ b[W-1]) & (a[W-1] ^ y[W-1]);
      end
      3'd2: y = a & b;
      3'd3: y = a | b;
      3'd4: y = a ^ b;
      default: y = '0;
    endcase
    z = ~(|y);
    n = y[W-1];
  end
endmodule

有条件地对 B 取反并设置初始进位,使 ADD/SUB 共享一个加法结构。各分支先确定 y,再计算 Z、N,因此逻辑运算也有一致的状态标志。默认赋值定义所有输出路径,避免锁存器锁存器 控制信号有效期间跟随输入,无效时保持数值的存储元件。它不同于只在边沿存储的触发器。了解更多。这里没有寄存器寄存器 存储多位状态的电路。本课程中的同步寄存器在时钟边沿存储指定输入。了解更多,所以不存在以时钟周期计的计算延迟,但仍有实际信号传播延迟。运算选择 MUX 与零检测电路的结果,也必须在下一个寄存器的 setup 要求内到达。

验证时,将 0、最大 unsigned、最大及最小 signed,以及边界两侧的值组合测试。R 应与模 2W2^W 的参考值比较,C 与 unsigned 扩展和比较,V 与 signed 越界条件比较。即使 R 正确,标志定义也可能错误。

关于区分结果符号、零、unsigned 进位与 signed 溢出标志的实际案例,可参阅 Arm 条件标志说明。

自己试试

对八位 ALU,求 (1) 0x7F+0x01、(2) 0xFF+0x01、(3) 0x00−0x01、(4) 0x80−0x01 的 R、Z、N、C、V。并解释在 (4) 中按 signed 比较输入时,为什么只看 N 会出错。

阅读解释

按 (R,Z,N,C,V) 顺序,结果为 (1) (0x80,0,1,0,1),(2) (0x00,1,0,1,0),(3) (0xFF,0,1,0,0),(4) (0x7F,0,0,1,1)。(3) 发生 unsigned 借位,因此 C=0,而 signed 的 −1 可表示。(4) 按 unsigned 为 128−1=127,无借位,因此 C=1;按 signed 为 −128−1=−129,超出范围,V=1。存储结果 R 的 N=0 不能反映真实差值的符号,而 signed A<B 使用 N xor V=1,正确得到真。

你的选择适用于此浏览器,可随时在页脚更改。