基本算法

移位相加即是日常我们使用的手算算法,移位相加的描述如下

  • 设置积的初值为0
  • 若乘数的最低位为0,则积不变,否则累加被乘数
  • 若乘数的第一位为0,则积不变,否则累加向左移位一位的被乘数
  • 若乘数的第n位(最高位)为0,则积不变,否则累加向左移位n位的被乘数

    RTL代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    module serial_shiftadder_multipcation #  (
    parameter WIDTH = 4
    )(
    input clk, // Clock
    input rst_n, // Asynchronous reset active low

    input multiplier_valid,
    input [WIDTH - 1:0]multiplier1,
    input [WIDTH - 1:0]multiplier2,

    output reg product_valid,
    output reg [2 * WIDTH - 1:0]product
    );
    接口定义部分,采用参数化设计,WIDTH为乘数/被乘数的位宽,multiplier_valid拉高时表示输入有效,并开始计算。product_valid被拉高时表示计算完成,当前的输出是有效的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /*****************buffer and shift*******************/
    reg [WIDTH - 1:0]min_mult;
    reg [2 * WIDTH - 1:0]max_mult;
    always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    {max_mult,min_mult} <= 'b0;
    end else if(multiplier_valid == 1'b1) begin
    if(multiplier1 > multiplier2) begin
    max_mult <= '{multiplier1};
    min_mult <= multiplier2;
    end else begin
    max_mult <= '{multiplier2};
    min_mult <= multiplier1;
    end
    end else if(min_mult != 'b0) begin
    max_mult <= max_mult << 1;
    min_mult <= min_mult >> 1;
    end else begin
    max_mult <= max_mult;
    min_mult <= min_mult;
    end
    end
    移位部分,有输入时比较两个输入的大小,使用小的数控制迭代数量,减小时间消耗。若较小的乘数不为0,则将较大的数向左移位,较小的的数向右位移。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /******************adder********************/
    always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    {product_valid,product} <= 'b0;
    end else if(min_mult[0] == 1'b1) begin
    product <= product + max_mult;
    product_valid <= 1'b0;
    end else if(min_mult != 'b0) begin
    product <= product;
    product_valid <= 1'b0;
    end else if(multiplier_valid == 1'b1) begin
    product <= 'b0;
    product_valid <= 1'b0;
    end else begin
    product <= product;
    product_valid <= 1'b1;
    end
    end

    endmodule
    累加部分,若较小的乘数最低位为0,保持积不变,否则累加当前的大乘数,当小乘数为0是,表示运算已经结束,输出有效拉高。

    测试

    使用自动化测试,使用高层次方法计算积,再与输出比较看是否相等
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    module mult_tb (
    );

    parameter WIDTH = 4;

    logic clk,rst_n;
    logic multiplier_valid;
    logic [WIDTH - 1:0]multiplier1;
    logic [WIDTH - 1:0]multiplier2;

    logic product_valid;
    logic [2 * WIDTH - 1:0]product;

    serial_shiftadder_multipcation # (
    .WIDTH(WIDTH)
    ) dut (
    .clk(clk), // Clock
    .rst_n(rst_n), // Asynchronous reset active low

    .multiplier_valid(multiplier_valid),
    .multiplier1(multiplier1),
    .multiplier2(multiplier2),

    .product_valid(product_valid),
    .product(product)
    );

    initial begin
    clk = 1'b0;
    forever begin
    # 50 clk = ~clk;
    end
    end

    initial begin
    rst_n = 1'b1;
    # 5 rst_n = 1'b0;
    # 10 rst_n = 1'b1;
    end

    initial begin
    {multiplier_valid,multiplier1,multiplier2} = 'b0;
    forever begin
    @(negedge clk);
    if(product_valid == 1'b1) begin
    multiplier1 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
    multiplier2 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
    multiplier_valid = 1'b1;
    end else begin
    multiplier_valid = 1'b0;
    end
    end
    end

    logic [2 * WIDTH - 1:0]exp;
    initial begin
    forever begin
    @(posedge product_valid);
    exp = multiplier1 * multiplier2;
    if(exp == product) begin
    $display("successfully, mult1=%d mult2=%d product=%d",multiplier1,multiplier2,product);
    end else begin
    $display("failed,mult1=%d mult2=%d product=%d exp=%d",multiplier1,multiplier2,product,exp);
    end
    end
    end

    endmodule