Verilog编程-2. 流水线乘法器设计
1. 背景
在Verilog中,我们一般使用乘法器时直接用*
来直接完成,或者调用相关IP核来生成高性能乘法器,但是归根到底Verilog描述的是硬件电路,从数字电路而不是高层次语法角度来实现乘法器可以让我们对于乘法器的运行有着更深入的理解。
2. 设计思路
二进制乘法与我们熟悉的十进制乘法类似,其原理都是被乘数与乘数的每一位按位相乘并进行移位,其原理示意图如下图所示:
据此,我们自然可以想到,先将被乘数进行扩位到乘积的位宽,同时被乘数和乘数进行移位(被乘数左移,乘数右移),通过判断乘数最低位进而累加被乘数,从而可以得到最终的乘积结果。由于乘积的宽度不会大于被乘数和乘数位宽之和,所以就首先将被乘数扩位到两者之和的位宽即可。再向前一步,由于移位累加需要的周期数至少是乘数的位宽,所以我们可以采用流水线的方式,将每一步累加的结果都保存下来,进而给下次的乘法腾出计算空间,这样可以提升乘法器运行的效率。
3. 代码
所有程序编辑平台为vscode,仿真平台为ubuntu系统中的vcs工具,分别包括源文件mult_low.v
,mult_cell.v
,mult_pipeline.v
,仿真文件mult_low_tb.v
,mult_pipeline_tb.v
,路径名列表文件mult_low.f
,mult_pipeline.f
和makefile
文件,其中源文件和仿真文件的原始来源是 6.7 Verilog流水线。文件具体内容如下
源文件 mult_low.v
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
);
// 下面的always过程块很重要,会让cnt=0保持两个时钟周期,从而让计算不出错
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
reg [M-1:0] mult2_shift;
reg [N+M-1:0] mult1_shift;
reg [N+M-1:0] mult1_acc;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
mult2_shift <= 'b0;
mult1_shift <= 'b0;
mult1_acc <= 'b0;
end
// 初始化的过程,所以在前