数字电路之乘法器 (一)

目录

十进制乘法

初级二进制乘法

无符号数乘法

有符号数乘法


十进制乘法

[3:0] a-multiplicand

[3:0] x-multiplier

x= 10^3 * x3 + 10^2 * x2 +10^1 * x1 +10^0 * x0

a * x = 10^3 * x3 * a + 10^2 * x2 * a +10^1 * x1 * a+10^0 * x0 * a

这种做法就是小学学习的阵列乘法

初级二进制乘法

无符号数乘法

类似于无符号十进制乘法。在计算x_j*a*2^j时,我们不需要用一个乘法器。x的每一位要么是0,要么是1,如果是1,那么在计算加法的时候,就加上这一项,如果是0,则舍弃这一项。a*2^j,可以用移位来实现。

用Verilog实现以上过程,我们需要一个8bit的寄存器存储product的结果,和一个8bit位宽的加法器。

module basic_multi_u #(
    parameter DW0 = 10,
              DW1 = 10
)(

    input   wire clk,
    input   wire rst_n,

    input   wire           start,
    input   wire [DW0-1:0] din0,
    input   wire [DW1-1:0] din1,

    output  reg               prod_end,
    output  reg  [DW0+DW1-1:0] prod
);

localparam CNTW= $clog2(DW1);

reg [CNTW-1:0] mult_cnt;
wire busy;
wire [DW0+DW1-1:0] din0_lsft; // din1 left shift

assign busy = start || (mult_cnt != 0);

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        mult_cnt <= 0;
    else if (mult_cnt == (DW1-1))
        mult_cnt <= 0;
    else if(busy)
        mult_cnt <= mult_cnt + 1'b1;
end

assign din0_lsft = din1[mult_cnt] ? din0 << mult_cnt : 0;

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        prod <= 0;
    else if(prod_end)
        prod <= 0;
    else if (busy) begin
        prod <= prod + din0_lsft;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        prod_end <= 1'b0;
    else if(mult_cnt == (DW1-1))
        prod_end <= 1'b1;
    else 
        prod_end <= 1'b0;
end


endmodule

有符号数乘法

乘数和被乘数最高位为符号位。

与无符号数乘法的区别有两个地方,一是x0a2^0等的每一项是有符号数,在做加法时需要手动扩符号位,或者转成signed类型。二是在计算x3a2^3时,由于x3是符号位,因此,权重是-3,这一项修改成x3a2^(-3)。

为了便于计算,x3a2^(-3)->x3(-a)2^3。-a是a的补码,由a取反加1得到。

下图是一个(-5)*(-3)的例子

用Verilog实现以上过程,同样需要一个8bit的寄存器存储product的结果,和一个8bit位宽的加法器。

module basic_multi_s #(
    parameter DW0 = 10,
              DW1 = 10
) (
    input   wire           clk,
    input   wire           rst_n,

    input   wire           start,
    input   wire [DW0-1:0] din0,
    input   wire [DW1-1:0] din1,

    output  reg                prod_end,
    output  reg  [DW0+DW1-1:0] prod
);

localparam CNTW= $clog2(DW1);

reg [CNTW-1:0] mult_cnt;
wire busy;
wire [DW0:0] din0_temp; // din0  
wire [DW0-1:0] din0_c;
wire [DW0+DW1-1:0] din0_lsft; // din0 left shift

assign busy = start || (mult_cnt != 0);

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        mult_cnt <= 0;
    else if (mult_cnt == (DW1-1))
        mult_cnt <= 0;
    else if(busy)
        mult_cnt <= mult_cnt + 1'b1;
end

// if MSB, din0_temp = -din0; else, din0_temp = din0;
assign din0_c = ~din0 + 1'b1;
assign din0_temp = (mult_cnt == (DW1-1)) ? {1'b0,din0_c} : {din0[DW0-1],din0};
assign din0_lsft = din1[mult_cnt] ? {
   
   {(DW1-1){din0_temp[DW0-1]}},din0_temp} << mult_cnt : 0; //din0 is a signed number

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        prod <= 0;
    else if(prod_end)
        prod <= 0;
    else if (busy) begin
        prod <= prod + din0_lsft;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(~rst_n)
        prod_end <= 1'b0;
    else if(mult_cnt == (DW1-1))
        prod_end <= 1'b1;
    else 
        prod_end <= 1'b0;
end
    
endmodule

注意:该两个Verilog示意仅用于理解基本乘法器实现原理,并非实际电路中乘法器的实现方式。

猜你喜欢

转载自blog.csdn.net/zhong_ethan/article/details/121453349