Verilog编程-2. 流水线乘法器设计

1. 背景

​ 在Verilog中,我们一般使用乘法器时直接用*来直接完成,或者调用相关IP核来生成高性能乘法器,但是归根到底Verilog描述的是硬件电路,从数字电路而不是高层次语法角度来实现乘法器可以让我们对于乘法器的运行有着更深入的理解。

2. 设计思路

​ 二进制乘法与我们熟悉的十进制乘法类似,其原理都是被乘数与乘数的每一位按位相乘并进行移位,其原理示意图如下图所示:

据此,我们自然可以想到,先将被乘数进行扩位到乘积的位宽,同时被乘数和乘数进行移位(被乘数左移,乘数右移),通过判断乘数最低位进而累加被乘数,从而可以得到最终的乘积结果。由于乘积的宽度不会大于被乘数和乘数位宽之和,所以就首先将被乘数扩位到两者之和的位宽即可。再向前一步,由于移位累加需要的周期数至少是乘数的位宽,所以我们可以采用流水线的方式,将每一步累加的结果都保存下来,进而给下次的乘法腾出计算空间,这样可以提升乘法器运行的效率。

3. 代码

​ 所有程序编辑平台为vscode,仿真平台为ubuntu系统中的vcs工具,分别包括源文件mult_low.vmult_cell.vmult_pipeline.v,仿真文件mult_low_tb.vmult_pipeline_tb.v,路径名列表文件mult_low.fmult_pipeline.fmakefile文件,其中源文件和仿真文件的原始来源是 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. 流水线乘法器设计相关推荐

  1. Verilog编程艺术(3)——第四部分 高级设计

    Verilog编程艺术(3) 状态机设计(FSM) 状态机类型 状态机通常分为两种类型:Moore 和 Meadly. 对于Moore FSM,输出是当前状态的函数: 而对于Meadly FSM,输出 ...

  2. 流水线乘法器的原理及verilog代码

    1.乘法原理 二进制数乘法的显著特点就是可以将乘法转换为移位,乘2就是左移一位,乘2^n就是左移n位.而一个二进制数又可以看成是由若干个2的i次方的和. 设被乘数和乘数分别为M.N,且都是32位的二进 ...

  3. verilog学习笔记之一--(简化)华莱士(Wallace)树形乘法器设计--(原代码出自用芯学项目)

    verilog学习笔记之一–(简化)华莱士(Wallace)树形乘法器设计–(原代码出自用芯学项目) 学习准备1: 树形乘法器原理:参考<数字集成电路-电路.系统与设计(第二版)>–P43 ...

  4. 数字IC设计verilog编写——4流水线握手协议

    1 流水线与握手协议 流水线在电路设计过程中,是必不可少的一种实现方式,其可以提高电路的性能,当我们需要设计高速电路的时候,就需要用到流水线: 流水线的设计,就是指对延时较大的组合逻辑插入寄存器,把较 ...

  5. [Verilog]如何使用signed wire/reg 进行乘法器设计

    如何使用signed wire/reg 进行乘法器设计 Index 如何使用signed wire/reg 进行乘法器设计 纯组合逻辑 使用独立的signed wire 使用拼接的signed wir ...

  6. (88)FPGA乘法器设计(移位相加乘法器)

    (88)FPGA乘法器设计(移位相加乘法器) 1 文章目录 1)文章目录 2)FPGA入门与提升课程介绍 3)FPGA简介 4)FPGA乘法器设计(移位相加乘法器) 5)技术交流 6)参考资料 2 F ...

  7. (25)FPGA乘法器设计(第5天)

    (25)FPGA乘法器设计(第5天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)FPGA乘法器设计(第5天) 5)技术交流 6)参考资料 2 FPGA初级课程 ...

  8. (100)Verilog HDL:UART波特率设计

    (100)Verilog HDL:UART波特率设计 1.1 目录 1)目录 2)FPGA简介 3)Verilog HDL简介 4)Verilog HDL:UART波特率设计 5)结语 1.2 FPG ...

  9. (99)Verilog HDL:呼吸灯设计

    (99)Verilog HDL:呼吸灯设计 1.1 目录 1)目录 2)FPGA简介 3)Verilog HDL简介 4)Verilog HDL:呼吸灯设计 5)结语 1.2 FPGA简介 FPGA( ...

最新文章

  1. 十天学Linux内核之第九天---向内核添加代码
  2. android的findviewbyid,Android O预览findViewById编译错误
  3. python用语句输入一个3*3的二维矩阵_python中二维数组的建立,输入和输出
  4. CV Papers|计算机视觉论文推荐周报20200501期
  5. 云计算三重奏:SAAS、PAAS和IAAS
  6. 数据库的事务和隔离级别
  7. 月薪仅18K的NLP工程师,回炉重造吧!
  8. python模拟人工滑动_python selenium模拟滑动操作
  9. javascript的apply理解
  10. avr单片机流水灯程序c语言,AVR单片机综合流水灯C程序
  11. Day621.Spring Test 常见错误 -Spring编程常见错误
  12. staged changes
  13. Minecraft一些红石技巧(1)
  14. linux如何给脚本等创建一个桌面启动图标
  15. Google Earth Engine (GEE)——awesome-gee-community-catalog
  16. 回溯法解决力扣79题单词搜索
  17. switch开关的async和await初体验
  18. Win10笔记本外接显示器调整分辨率教程
  19. 网格交易法是什么意思,外汇投资网格交易法怎么操作?
  20. 查找算法之平衡查找树

热门文章

  1. jQuery如何禁用浏览器快捷键
  2. 国外photoshop教程_20个情人节Photoshop教程给您的启发
  3. Java String字符串删除指定字符
  4. 给大家分享两款实用的音乐播放器
  5. jquery抽奖转盘java_jquery实现九宫格大转盘抽奖
  6. abap开发注释快捷键_SAP ABAP 开发快捷键
  7. ubuntu 在使用U盘安装时出现的问题总结
  8. containerd安装和使用
  9. bp神经网络预测模型实例,BP神经网络预测模型
  10. MySQL列转行函数