使用加法和移位的 8 位顺序乘法器

8 bit sequential multiplier using add and shift

我正在使用 Verilog 设计一个 8 位有符号顺序乘法器。输入是clk(时钟),rst(复位),a(8位乘数),b(8位被乘数),输出是p(乘积)和rdy(就绪信号,表示乘法结束)。对于负输入,我进行符号扩展并将其保存在 15 位寄存器变量 multipliermultiplicand 中。这是我的代码:

module seq_mult (p, rdy, clk, reset, a, b);
   input clk, reset;
   input [7:0] a, b;
   output [15:0] p;
   output rdy;
   
   reg [15:0] p;
   reg [15:0] multiplier;
   reg [15:0] multiplicand;
   reg rdy;
   reg [4:0] ctr;

always @(posedge clk or posedge reset) begin
    if (reset) 
    begin
    rdy     <= 0;
    p   <= 0;
    ctr     <= 0;
    multiplier <= {{8{a[7]}}, a};
    multiplicand <= {{8{b[7]}}, b};
    end 
    else 
    begin 
      if(ctr < 16) 
          begin
          if(multiplier[ctr]==1)
              begin
              multiplicand = multiplicand<<ctr;
              p <= p + multiplicand;
              end
          ctr <= ctr+1;
          end
       else 
           begin
           rdy <= 1;
           end
    end
  end //End of always block
    
endmodule

这是我的测试平台:

`timescale 1ns/1ns
`define width 8
`define TESTFILE "test_in.dat"

module seq_mult_tb () ;
    reg signed [`width-1:0] a, b;
    reg             clk, reset;

    wire signed [2*`width-1:0] p;
    wire           rdy;

    integer total, err;
    integer i, s, fp, numtests;

    // Golden reference - can be automatically generated in this case
    // otherwise store and read from a file
    wire signed [2*`width-1:0] ans = a*b;

    // Device under test - always use named mapping of signals to ports
    seq_mult dut( .clk(clk),
        .reset(reset),
        .a(a),
        .b(b),
        .p(p),
        .rdy(rdy));

    // Set up 10ns clock
    always #5 clk = !clk;

    // A task to automatically run till the rdy signal comes back from DUT
    task apply_and_check;
        input [`width-1:0] ain;
        input [`width-1:0] bin;
        begin
            // Set the inputs
            a = ain;
            b = bin;
            // Reset the DUT for one clock cycle
            reset = 1;
            @(posedge clk);
            // Remove reset 
            #1 reset = 0;

            // Loop until the DUT indicates 'rdy'
            while (rdy == 0) begin
                @(posedge clk); // Wait for one clock cycle
            end
            if (p == ans) begin
                $display($time, " Passed %d * %d = %d", a, b, p);
            end else begin
                $display($time, " Fail %d * %d: %d instead of %d", a, b, p, ans);
                err = err + 1;
            end
            total = total + 1;
        end
    endtask // apply_and_check

    initial begin
        // Initialize the clock 
        clk = 1;
        // Counters to track progress
        total = 0;
        err = 0;

        // Get all inputs from file: 1st line has number of inputs
        fp = $fopen(`TESTFILE, "r");
        s = $fscanf(fp, "%d\n", numtests);
        // Sequences of values pumped through DUT 
        for (i=0; i<numtests; i=i+1) begin
            s = $fscanf(fp, "%d %d\n", a, b);
            apply_and_check(a, b);
        end
        if (err > 0) begin
            $display("FAIL %d out of %d", err, total);
        end else begin
            $display("PASS %d tests", total);
        end
        $finish;
    end

endmodule // seq_mult_tb

我还创建了一个名为 test_in.dat 的文件,其中存储了测试用例(第一行表示测试用例的数量):

10
5 5
2 3
10 1
10 2
20 20
-128 2
10 -128
-1 -1
10 0
0 2

现在的问题是:代码仅适用于前两个输入和后两个输入。对于剩余的输入,我得到的数字与预期不同。有人可以指出我的代码中导致此问题的任何逻辑错误吗?或者如果有一个更简单的策略来做同样的事情,也请告诉我。

如果 multiplier[ctr] 为 1,则

multiplicand 在每次迭代中向左移动 ctr

但是ctr已经包含了之前的移动量,所以你移动的太远了。

你应该在每次迭代中无条件地将 multiplicand 移动 1:

multiplicand <= multiplicand << 1;
if (multiplier[ctr] == 1)
begin
    p <= p + multiplicand;
end
ctr <= ctr + 1;

您还应该为 multiplicand 使用非阻塞赋值。在将其添加到 p.

之后,您可能需要将其移动到