一、前言:
verilog是描述硬件语言,直接使用乘号编译器会优化成乘法器IP。
位宽小,一个周期就可以输出结果,位宽大可以选择流水输出。但是乘法器IP也有限制,位宽限制,未知时序等。
常用乘法实现方式移位相加。例如
A = A<<1 ; //完成A * 2
A = (A<<1) + A ; //对应A * 3
A = (A<<3) + (A<<2) + (A<<1) + A ; //对应A * 15
用一个移位寄存器和一个加法器就可以完成乘以3操作,但是乘以15时就需要使用3个移位寄存器和3个加法器,反向思路也可以使用移位相减的方式。注意:有时候数字电路一个周期内并不能完成多个变量相加,多个相加会导致时序不满足。此时,流水线乘法器就有效果。
二、原理:和十进制类似
被乘数按照乘数对应bit位进行移位累加,即便完成相乘过程。假设一个时钟周期只能完成一次累加,那么乘法计算时间最少恰好是乘数的位宽,建议将位宽窄的数当做乘数。
三、verilog设计
module mult_low
#(parameter N=4,
parameter M=4)
(
input clk,
input rstn,
input data_rdy , //数据输入使能
input [N-1:0] mult1, //被乘数
input [M-1:0] mult2, //乘数
output res_rdy , //数据输出使能
output [N+M-1:0] res //乘法结果
);
//calculate counter
reg [31:0] cnt ;
//乘法周期计数器
wire [31:0] cnt_temp = (cnt == M)? 'b0 : cnt + 1'b1 ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ;
end
else if (data_rdy) begin //数据使能时开始计数
cnt <= cnt_temp ;
end
else if (cnt != 0 ) begin //防止输入使能端持续时间过短
cnt <= cnt_temp ;
end
else begin
cnt <= 'b0 ;
end
end
//multiply
reg [M-1:0] mult2_shift ;
reg [M+N-1:0] mult1_shift ;
reg [M+N-1:0] mult1_acc ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
mult2_shift <= 'b0 ;
mult1_shift <= 'b0 ;
mult1_acc <= 'b0 ;
end
else if (data_rdy && cnt=='b0) begin //初始化
mult1_shift <= {{(N){1'b0}}, mult1} << 1 ;
mult2_shift <= mult2 >> 1 ;
mult1_acc <= mult2[0] ? {{(N){1'b0}}, mult1} : 'b0 ;
end
else if (cnt != M) begin
mult1_shift <= mult1_shift << 1 ; //被乘数乘2
mult2_shift <= mult2_shift >> 1 ; //乘数右移,方便判断
//判断乘数对应为是否为1,为1则累加
mult1_acc <= mult2_shift[0] ? mult1_acc + mult1_shift : mult1_acc ;
end
else begin
mult2_shift <= 'b0 ;
mult1_shift <= 'b0 ;
mult1_acc <= 'b0 ;
end
end
//results
reg [M+N-1:0] res_r ;
reg res_rdy_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
res_r <= 'b0 ;
res_rdy_r <= 'b0 ;
end
else if (cnt == M) begin
res_r <= mult1_acc ; //乘法周期结束时输出结果
res_rdy_r <= 1'b1 ;
end
else begin
res_r <= 'b0 ;
res_rdy_r <= 'b0 ;
end
end
assign res_rdy = res_rdy_r;
assign res = res_r;
endmodule
四、testbench设计
`timescale 1ns/1ns
module test ;
parameter N = 8 ;
parameter M = 4 ;
reg clk, rstn;
//clock
always begin
clk = 0 ; #5 ;
clk = 1 ; #5 ;
end
//reset
initial begin
rstn = 1'b0 ;
#8 ; rstn = 1'b1 ;
end
//no pipeline
reg data_rdy_low ;
reg [N-1:0] mult1_low ;
reg [M-1:0] mult2_low ;
wire [M+N-1:0] res_low ;
wire res_rdy_low ;
//使用任务周期激励
task mult_data_in ;
input [M+N-1:0] mult1_task, mult2_task ;
begin
wait(!test.u_mult_low.res_rdy) ; //not output state
@(negedge clk ) ;
data_rdy_low = 1'b1 ;
mult1_low = mult1_task ;
mult2_low = mult2_task ;
@(negedge clk ) ;
data_rdy_low = 1'b0 ;
wait(test.u_mult_low.res_rdy) ; //test the output state
end
endtask
//driver
initial begin
#55 ;
mult_data_in(25, 5 ) ;
mult_data_in(16, 10 ) ;
mult_data_in(10, 4 ) ;
mult_data_in(15, 7) ;
mult_data_in(215, 9) ;
end
mult_low #(.N(N), .M(M))
u_mult_low
(
.clk (clk),
.rstn (rstn),
.data_rdy (data_rdy_low),
.mult1 (mult1_low),
.mult2 (mult2_low),
.res_rdy (res_rdy_low),
.res (res_low));
//simulation finish
initial begin
forever begin
#100;
if ($time >= 10000) $finish ;
end
end
endmodule // test
五、结果分析
最短的位宽为4,再加送数据时间,完成一次计算需要5个时钟周期