顺序乘法器,含verilog代码
我们需要明确的是两个D_WIDTH位宽的数相乘,结果位宽为2D_WIDTH, 对于负数乘法,可以利用乘数和被乘数的符号位进行异或得到积的符号位,通过判断符号位得到乘数和被乘数的绝对值,将负数乘法转为无符号数乘法进行运算,首先初始化乘数寄存器和被乘数寄存器Multiplicand,被乘数寄存器的低D_WIDTH位为输入的被乘数的绝对值,高D_WIDTH位为0,初始化乘积寄存器product[2D_WIDTH-1:0]=0,
if (Multiplier>0)
product< = Multiplier[0]? Multiplicand+ product : product;
Multiplicand <={ Multiplicand [2*D_WIDTH-2:0] ,1’b0}
Multiplier<= {1’b0,Multiplier[D_WIDTH-1:1]};
end
上图中可以看到,二进制计算乘法比较简单,由于每一位上只有0和1,乘法计算其实就简化成了位移和加法,乘数每一位和被乘数相乘,结果不是完全复制就是0,只不过对应着不同的位移。
为了节约晶体管,只需从低位开始将被乘数逐次左移一位,乘数逐次右移一位,不断将计算结果加在上一步的结果上,直到乘数为0不能再右移为止。
这种方式虽然节约了电路但由于下一步加法依赖上一步加法计算结果,下一步位移也依赖上一步位移结果,因此只能顺序进行,结果就是运算比较慢。无法用单纯的组合逻辑电路形成,因为被乘数寄存器和乘积寄存器内容发生变化需要时序控制
module multi_seq#(parameter D_WIDTH=8)(
input clk,
input rst_n,
input [D_WIDTH-1:0] mul_A,
//被乘数,5位,有符号补码
input [D_WIDTH-1:0] mul_B,
//乘数5位,有符号补码
input start,
output reg [2*D_WIDTH-1:0] result,
output reg done
);
reg[1:0] state;
reg[2*D_WIDTH-1:0] multi_A;
reg[D_WIDTH-1:0] multi_B;
reg[2*D_WIDTH-1:0] result_tmp;
reg neg;
wire [D_WIDTH-1:0] abs_A;
wire [D_WIDTH-1:0] abs_B;
assign abs_A = mul_A[D_WIDTH-1] ? ~mul_A+1 : mul_A ;
assign abs_B = mul_B[D_WIDTH-1] ? ~mul_B[D_WIDTH-1:0]+1 : mul_B ;
always@(posedge clk)
if(~rst_n) begin
multi_A <= 0;
multi_B <= 0;
neg <= 0;
done <= 0;
result_tmp <= 0;
state <= 0;
result <= 0;
end
else if(start)
case(state)
0: begin //{D_WIDTH{1'b0}}
multi_A <= {{D_WIDTH{1'b0}},abs_A};
multi_B <= abs_B;
neg <= mul_A[D_WIDTH-1]^mul_B[D_WIDTH-1];
done <= 0;
state <= state+1;
result_tmp <= 0;
end
1:begin
if(multi_B>0) begin
result_tmp<=multi_B[0]?(result_tmp + multi_A) : result_tmp;
multi_B <= {1'b0,multi_B[D_WIDTH-1:1]};
multi_A <={multi_A[2*D_WIDTH-2:0],1'b0};
end
else begin
state <= state + 1;
end
end
2:begin
done<=1'b1;
result<= neg ? (~result_tmp+1): result_tmp;
state <= state+1;
end
3: begin
done<=1'b0;
state<=0;
end
endcase
endmodule
至于testbench,可以用上一篇的,在此记录一下