2. 定点减法器设计

定点减法器设计 学习卡片

任务定义

设计一个参数化定点减法器,输入为两个补码格式的有符号定点数,输出为两者之差。减法通过“取反加一”转换为加法实现,需正确处理借位(即加法进位)及溢出标志。验证重点在于补码减法中借位与溢出的关系。

设计规格

  • 位宽参数:W(默认8位),数据格式为定点数,二进制小数点位置由外部约定,本设计不感知小数点位置,仅执行整数补码减法。
  • 输入:a[W-1:0]、b[W-1:0],均为二进制补码。
  • 输出:diff[W-1:0](补码结果)、borrow(借位标志,即加法进位)、overflow(溢出标志)。
  • 运算:diff = a - b = a + (~b + 1)。

RTL实现

module fixed_subtractor #(
    parameter W = 8
)(
    input  wire [W-1:0] a,
    input  wire [W-1:0] b,
    output wire [W-1:0] diff,
    output wire         borrow,
    output wire         overflow
);
    wire [W-1:0] b_not;
    wire [W-1:0] b_comp;      // b的补码
    wire         carry_out;   // 加法进位,即减法借位

    assign b_not  = ~b;
    assign b_comp = b_not + 1'b1;

    // 加法器:a + (~b + 1)
    assign {carry_out, diff} = a + b_comp;

    // 借位标志:加法进位即为减法借位
    assign borrow = carry_out;

    // 溢出检测:两个操作数符号相同且与结果符号不同
    // 减法溢出条件:a与b符号相反,且结果符号与a不同
    wire sign_a = a[W-1];
    wire sign_b = b[W-1];
    wire sign_r = diff[W-1];
    assign overflow = (sign_a != sign_b) && (sign_r != sign_a);

endmodule

测试平台与验证

以下测试平台覆盖三类关键场景:无借位正数减法、有借位正数减法、溢出场景(正减负溢出、负减正溢出)。

`timescale 1ns/1ps
module tb_fixed_subtractor;
    parameter W = 8;
    reg  [W-1:0] a, b;
    wire [W-1:0] diff;
    wire         borrow, overflow;

    fixed_subtractor #(.W(W)) dut (
        .a(a), .b(b),
        .diff(diff),
        .borrow(borrow),
        .overflow(overflow)
    );

    // 参考模型:使用更大位宽避免溢出干扰
    function [W:0] ref_sub;
        input [W-1:0] x, y;
        begin
            ref_sub = {1'b0, x} - {1'b0, y};
        end
    endfunction

    integer i;
    reg [W-1:0] ref_diff;
    reg         ref_borrow;

    initial begin
        $monitor("t=%0t a=%h b=%h diff=%h borrow=%b overflow=%b",
                 $time, a, b, diff, borrow, overflow);

        // 用例1:正数减法,无借位 (a=5, b=3)
        a = 8'h05; b = 8'h03; #10;
        // 用例2:正数减法,有借位 (a=3, b=5)
        a = 8'h03; b = 8'h05; #10;
        // 用例3:正减负,溢出 (a=127, b=-128)
        a = 8'h7F; b = 8'h80; #10;
        // 用例4:负减正,溢出 (a=-128, b=1)
        a = 8'h80; b = 8'h01; #10;
        // 用例5:负减负,无溢出 (a=-3, b=-5) = 2
        a = 8'hFD; b = 8'hFB; #10;

        // 自动比对
        for (i = 0; i < 5; i = i + 1) begin
            case (i)
                0: begin a = 8'h05; b = 8'h03; end
                1: begin a = 8'h03; b = 8'h05; end
                2: begin a = 8'h7F; b = 8'h80; end
                3: begin a = 8'h80; b = 8'h01; end
                4: begin a = 8'hFD; b = 8'hFB; end
            endcase
            #10;
            ref_diff = ref_sub(a, b);
            ref_borrow = (a < b) ? 1'b1 : 1'b0; // 无符号比较近似,仅用于正数场景
            if (diff !== ref_diff[W-1:0])
                $display("FAIL: a=%h b=%h diff=%h expected=%h", a, b, diff, ref_diff[W-1:0]);
            else
                $display("PASS: a=%h b=%h diff=%h", a, b, diff);
        end
        $finish;
    end
endmodule

验证方法与判定标准

  1. 运行方式:使用支持Verilog-2001的仿真器(如Vivado XSim、ModelSim、Icarus Verilog)编译并运行上述RTL与测试平台。
  2. 观测点:diff是否为正确补码差;borrow在a<b(无符号视角)时是否为1;overflow在用例3、4中是否为1,在用例1、2、5中是否为0。
  3. 判定标准:仿真器输出中所有PASS且无FAIL,同时用例3、4的overflow为1,用例1、2、5的overflow为0,即判定通过。

预期输出(确定性推导)

根据补码运算规则,可推导各用例预期结果:

  • 用例1:a=5, b=3 → diff=0x02, borrow=0, overflow=0
  • 用例2:a=3, b=5 → diff=0xFE(-2), borrow=1, overflow=0
  • 用例3:a=127, b=-128 → diff=0xFF(-1), borrow=0, overflow=1
  • 用例4:a=-128, b=1 → diff=0x7F(127), borrow=1, overflow=1
  • 用例5:a=-3, b=-5 → diff=0x02(2), borrow=0, overflow=0
a[W-1:0] b[W-1:0] ~b +1 加法器 a + (~b+1) diff[W-1:0] carry_out borrow 溢出检测 overflow

上图展示了减法器的数据通路:b先取反加一得到补码,再与a相加,加法进位直接映射为借位标志,溢出由操作数与结果符号关系独立判定。