Verilog编程-2. 流水线乘法器设计
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) beginif (!rstn) begincnt <= 'b0 ;endelse if (data_rdy) begin //数据使能时开始计数cnt <= cnt_temp ;endelse if (cnt != 0 ) begin //防止输入使能端持续时间过短cnt <= cnt_temp ;endelse begincnt <= 'b0 ;endendreg [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) beginif (!rstn) beginmult2_shift <= 'b0;mult1_shift <= 'b0;mult1_acc <= 'b0; end// 初始化的过程,所以在前面添加的是0else if (data_rdy && cnt=='b0) beginmult1_shift <= {{(M){1'b0}}, mult1} << 1;mult2_shift <= mult2 >> 1;mult1_acc <= mult2[0] ? {{(M){1'b0}}, mult1} : 'b0;end// 就是移位累加的过程else if (cnt != M) beginmult1_shift <= mult1_shift << 1;mult2_shift <= mult2_shift >> 1;mult1_acc <= mult2_shift[0] ? mult1_acc + mult1_shift : mult1_acc;endelse beginmult2_shift <= 'b0;mult1_shift <= 'b0;mult1_acc <= 'b0;endendreg [N+M-1:0] res_r;reg res_rdy_r;always @(posedge clk or negedge rstn) beginif (!rstn) beginres_r <= 'b0;res_rdy_r <= 'b0;endelse if (cnt == M) beginres_r <= mult1_acc;res_rdy_r <= 1'b1; end else beginres_r <= 'b0;res_rdy_r <= 'b0; end endassign res = res_r;assign res_rdy = res_rdy_r;
endmodule
仿真文件 mult_low_tb.v
module mult_low_tb;parameter N = 8;parameter M = 4;reg clk, rstn;always beginclk = 0; #5;clk = 1; #5;endinitial beginrstn = 1'b0;#8 rstn = 1'b1;endreg [N-1:0] mult1;reg [M-1:0] mult2;reg data_rdy;wire [N+M-1:0] res;wire res_rdy;mult_low #(.N(N), .M(M)) inst1 (.clk (clk),.rstn (rstn),.data_rdy (data_rdy),.mult1 (mult1),.mult2 (mult2),.res (res),.res_rdy (res_rdy));// 使用任务周期激励task mult_data_in;input [M+N-1:0] mult1_task, mult2_task;wait(!mult_low_tb.inst1.res_rdy);@(negedge clk);data_rdy = 1'b1;mult1 = mult1_task;mult2 = mult2_task;@(negedge clk);data_rdy = 1'b0;wait(mult_low_tb.inst1.res_rdy);endtaskinitial beginmult_data_in(25, 5); mult_data_in(16, 10);mult_data_in(10, 4);mult_data_in(15, 7);mult_data_in(215, 9);endinitial begin#1000 $finish;endwire [3:0] cnt;assign cnt = inst1.cnt;endmodule
路径列表文件 mult_low.f
./mult_low.v
./mult_low_tb.v
源文件 mult_cell.v
module mult_cell #(parameter N = 4,parameter M = 4
)
(input clk,input rstn,input en,input [M+N-1:0] mult1,input [M-1:0] mult2,input [M+N-1:0] mult1_acci,output reg [M+N-1:0] mult1_o,output reg [M-1:0] mult2_shift,output reg [M+N-1:0] mult1_acco,output reg rdy
);always @(posedge clk or negedge rstn) beginif (!rstn) beginrdy <= 'b0;mult1_o <= 'b0;mult1_acco <= 'b0;mult2_shift <= 'b0; endelse if (en) beginrdy <= 'b1;mult2_shift <= mult2 >> 1;mult1_o <= mult1 << 1;if (mult2[0]) beginmult1_acco <= mult1_acci + mult1;endelse beginmult1_acco <= mult1_acci;endend else beginrdy <= 'b0;mult1_o <= 'b0;mult1_acco <= 'b0;mult2_shift <= 'b0; end endendmodule
源文件 mult_pipeline.v
module mult_pipeline #(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
);wire [N+M-1:0] mult1_t [M-1:0];wire [M-1:0] mult2_t [M-1:0];wire [N+M-1:0] mult1_acc_t [M-1:0];wire [M-1:0] rdy_t; // 初始化的数据和后面例化数据不同,不可以用generate例化mult_cell #(.N(N), .M(M)) inst0(.clk (clk),.rstn (rstn),.en (data_rdy),.mult1 ({{(M){1'b0}}, mult1}),.mult2 (mult2),.mult1_acci ({(N+M){1'b0}}),.mult1_acco (mult1_acc_t[0]),.mult2_shift (mult2_t[0]),.mult1_o (mult1_t[0]),.rdy (rdy_t[0]));genvar i;generatefor (i=1; i<=M-1; i=i+1) begin:mult_stepxmult_cell #(.N(N), .M(M)) inst (.clk (clk),.rstn (rstn),.en (rdy_t[i-1]),.mult1 (mult1_t[i-1]),.mult2 (mult2_t[i-1]),.mult1_acci (mult1_acc_t[i-1]),.mult1_acco (mult1_acc_t[i]),.mult1_o (mult1_t[i]),.mult2_shift (mult2_t[i]),.rdy (rdy_t[i]));endendgenerateassign res_rdy = rdy_t[M-1];assign res = mult1_acc_t[M-1];
endmodule
路径名列表文件 mult_pipeline.f
./mult_cell.v
./mult_pipeline.v
./mult_pipeline_tb.v
仿真文件 mult_pipeline_tb.v
module mult_low_tb;parameter N = 8;parameter M = 4;reg clk, rstn;always beginclk = 0; #5;clk = 1; #5;endinitial beginrstn = 1'b0;#8 rstn = 1'b1;endreg data_rdy ;reg [N-1:0] mult1 ;reg [M-1:0] mult2 ;wire res_rdy ;wire [N+M-1:0] res ;//driverinitial begin#55 ;@(negedge clk ) ;data_rdy = 1'b1 ;mult1 = 25; mult2 = 5;#10 ; mult1 = 16; mult2 = 10;#10 ; mult1 = 10; mult2 = 4;#10 ; mult1 = 15; mult2 = 7;mult2 = 7; repeat(32) #10 mult1 = mult1 + 1 ;mult2 = 1; repeat(32) #10 mult1 = mult1 + 1 ;mult2 = 15; repeat(32) #10 mult1 = mult1 + 1 ;mult2 = 3; repeat(32) #10 mult1 = mult1 + 1 ;mult2 = 11; repeat(32) #10 mult1 = mult1 + 1 ;mult2 = 4; repeat(32) #10 mult1 = mult1 + 1 ;mult2 = 9; repeat(32) #10 mult1 = mult1 + 1 ;end//对输入数据进行移位,方便后续校验reg [N-1:0] mult1_ref [M-1:0];reg [M-1:0] mult2_ref [M-1:0];always @(posedge clk) beginmult1_ref[0] <= mult1 ;mult2_ref[0] <= mult2 ;endgenvar i ;generatefor(i=1; i<=M-1; i=i+1) beginalways @(posedge clk) beginmult1_ref[i] <= mult1_ref[i-1];mult2_ref[i] <= mult2_ref[i-1];endendendgenerate//自校验reg error_flag ;always @(posedge clk) begin# 1 ;if (mult1_ref[M-1] * mult2_ref[M-1] != res && res_rdy) beginerror_flag <= 1'b1 ;endelse beginerror_flag <= 1'b0 ;endend//module instantiationmult_pipeline #(.N(N), .M(M)) inst1(.clk (clk),.rstn (rstn),.data_rdy (data_rdy),.mult1 (mult1),.mult2 (mult2),.res_rdy (res_rdy),.res (res));initial begin#10000 $finish;endendmodule
makefile文件
.PHONY: com_low sim_low com_pipeline sim_pipeline cleanVCS = vcs -sverilog +v2k -timescale=1ns/1ns \-debug_allcom_low:${VCS} -f mult_low.f -o simv_lowcom_pipeline:${VCS} -f mult_pipeline.f -o simv_pipelinesim_low:./simv_low -guisim_pipeline:./simv_pipeline -guiclean:rm -rf ./csrc ./DVEfiles *.daidir *.log simv* *.key *.vpd
程序中有一些需要注意的点:
1. 在源文件mult_low.v中第一个always块很重要,会让cnt==0时保持两个时钟周期,这样计算才会正确;
2. 源文件mult_pipeline.v中定义的二维数组使用的是wire,因为既要当输入又要当输出;
3. 源文件mult_pipeline.v中初始化例化不可以放在generate中,因为输入信号不同;
4. 从源文件mult_pipeline.v中初始化例化和generate中的例化可以看出,前一阶段输出是下一阶段输入,并被保存起来,符合流水线定义;
5. 路径名列表文件mult_pipeline.f中需要有mult_cell.v的路径,否则仿真的时候找不到会出错;
4. 仿真结果
下图是使用vcs软件仿真不具有流水线功能的乘法器得到的波形图
下图是使用vcs软件仿真具有流水线功能的乘法器得到的波形图
我们可以明显看到,使用了流水线结构之后,在一定时钟周期之后,每个时钟周期都会输出一个乘法结果,而没有采用流水线结构的乘法器,每得到一个结果都必须要等待一定的时钟周期,流水线结构对于提升计算效率的确有很大帮助。与此同时,流水线结构也消耗了大量的硬件存储资源(包括二维存储器),这是一个典型的用资源换效率的设计思路。
Verilog编程-2. 流水线乘法器设计相关推荐
- Verilog编程艺术(3)——第四部分 高级设计
Verilog编程艺术(3) 状态机设计(FSM) 状态机类型 状态机通常分为两种类型:Moore 和 Meadly. 对于Moore FSM,输出是当前状态的函数: 而对于Meadly FSM,输出 ...
- 流水线乘法器的原理及verilog代码
1.乘法原理 二进制数乘法的显著特点就是可以将乘法转换为移位,乘2就是左移一位,乘2^n就是左移n位.而一个二进制数又可以看成是由若干个2的i次方的和. 设被乘数和乘数分别为M.N,且都是32位的二进 ...
- verilog学习笔记之一--(简化)华莱士(Wallace)树形乘法器设计--(原代码出自用芯学项目)
verilog学习笔记之一–(简化)华莱士(Wallace)树形乘法器设计–(原代码出自用芯学项目) 学习准备1: 树形乘法器原理:参考<数字集成电路-电路.系统与设计(第二版)>–P43 ...
- 数字IC设计verilog编写——4流水线握手协议
1 流水线与握手协议 流水线在电路设计过程中,是必不可少的一种实现方式,其可以提高电路的性能,当我们需要设计高速电路的时候,就需要用到流水线: 流水线的设计,就是指对延时较大的组合逻辑插入寄存器,把较 ...
- [Verilog]如何使用signed wire/reg 进行乘法器设计
如何使用signed wire/reg 进行乘法器设计 Index 如何使用signed wire/reg 进行乘法器设计 纯组合逻辑 使用独立的signed wire 使用拼接的signed wir ...
- (88)FPGA乘法器设计(移位相加乘法器)
(88)FPGA乘法器设计(移位相加乘法器) 1 文章目录 1)文章目录 2)FPGA入门与提升课程介绍 3)FPGA简介 4)FPGA乘法器设计(移位相加乘法器) 5)技术交流 6)参考资料 2 F ...
- (25)FPGA乘法器设计(第5天)
(25)FPGA乘法器设计(第5天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)FPGA乘法器设计(第5天) 5)技术交流 6)参考资料 2 FPGA初级课程 ...
- (100)Verilog HDL:UART波特率设计
(100)Verilog HDL:UART波特率设计 1.1 目录 1)目录 2)FPGA简介 3)Verilog HDL简介 4)Verilog HDL:UART波特率设计 5)结语 1.2 FPG ...
- (99)Verilog HDL:呼吸灯设计
(99)Verilog HDL:呼吸灯设计 1.1 目录 1)目录 2)FPGA简介 3)Verilog HDL简介 4)Verilog HDL:呼吸灯设计 5)结语 1.2 FPGA简介 FPGA( ...
最新文章
- 十天学Linux内核之第九天---向内核添加代码
- android的findviewbyid,Android O预览findViewById编译错误
- python用语句输入一个3*3的二维矩阵_python中二维数组的建立,输入和输出
- CV Papers|计算机视觉论文推荐周报20200501期
- 云计算三重奏:SAAS、PAAS和IAAS
- 数据库的事务和隔离级别
- 月薪仅18K的NLP工程师,回炉重造吧!
- python模拟人工滑动_python selenium模拟滑动操作
- javascript的apply理解
- avr单片机流水灯程序c语言,AVR单片机综合流水灯C程序
- Day621.Spring Test 常见错误 -Spring编程常见错误
- staged changes
- Minecraft一些红石技巧(1)
- linux如何给脚本等创建一个桌面启动图标
- Google Earth Engine (GEE)——awesome-gee-community-catalog
- 回溯法解决力扣79题单词搜索
- switch开关的async和await初体验
- Win10笔记本外接显示器调整分辨率教程
- 网格交易法是什么意思,外汇投资网格交易法怎么操作?
- 查找算法之平衡查找树