【Verilog教程】6.7 Verilog流水线

关键词:流水线,乘法器
硬件描述语言的一个突出优点就是指令执行的并行性。多条语句能够在相同时钟周期内并行处理多个信号数据。

但是当数据串行输入时,指令执行的并行性并不能体现出其优势。而且很多时候有些计算并不能在一个或两个时钟周期内执行完毕,如果每次输入的串行数据都需要等待上一次计算执行完毕后才能开启下一次的计算,那效率是相当低的。流水线就是解决多周期下串行数据计算效率低的问题。

流水线
流水线的基本思想是:把一个重复的过程分解为若干个子过程,每个子过程由专门的功能部件来实现。将多个处理过程在时间上错开,依次通过各功能段,这样每个子过程就可以与其他子过程并行进行。

假如一个洗衣店内洗衣服的过程分为 4 个阶段:取衣、洗衣、烘干、装柜。每个阶段都需要半小时来完成,则洗一次衣服需要 2 小时。

考虑最差情况,洗衣店内只有一台洗衣机、一台烘干机、一个衣柜。如果每半小时送来一批要洗的衣服,每次等待上一批衣服洗完需要 2 小时,那么洗完 4 批衣服需要的时间就是 8 小时。

图示如下:
**加粗样式
**
对这个洗衣店的装备进行升级,一共引进 4 套洗衣服的装备,工作人员也增加到 4 个,每个人负责一个洗衣阶段。所以每批次的衣服,都能够及时的被相同的人放入到不同的洗衣机内。由于时间上是错开的,每批次的衣服都能被相同的人在不同的设备与时间段(半小时)内洗衣、烘干和装柜。图示如下。
在这里插入图片描述
可以看出,洗完 4 批衣服只需要 3 个半小时,效率明显提高。

其实,在 2 小时后第一套洗衣装备已经完成洗衣过程而处于空闲状态,如果此时还有第 5 批衣服的送入,那么第一套设备又可以开始工作。依次类推,只要衣服批次不停的输入,4 台洗衣设备即可不间断的完成对所有衣服的清洗过程。且除了第一批次洗衣时间需要 2 小时,后面每半小时都会有一批次衣服清洗完成。

衣服批次越多,节省的时间就越明显。假如有 N 批次衣服,需要的时间为 (4+N) 个半小时。

当然,升级后洗衣流程也有缺点。设备和工作人员的增加导致了投入的成本增加,洗衣店内剩余空间也被缩小,工作状态看起来比较繁忙。

和洗衣服过程类似,数据的处理路径也可以看作是一条生产线,路径上的每个数字处理单元都可以看作是一个阶段,会产生延时。

流水线设计就是将路径系统的分割成一个个数字处理单元(阶段),并在各个处理单元之间插入寄存器来暂存中间阶段的数据。被分割的单元能够按阶段并行的执行,相互间没有影响。所以最后流水线设计能够提高数据的吞吐率,即提高数据的处理速度。

流水线设计的缺点就是,各个处理阶段都需要增加寄存器保存中间计算状态,而且多条指令并行执行,势必会导致功耗增加。

下面,设计一个乘法器,并对是否采用流水线设计进行对比。
一般乘法器设计
前言

也许有人会问,直接用乘号 * 来完成 2 个数的相乘不是更快更简单吗?

如果你有这个疑问,说明你对硬件描述语言的认知还有所不足。就像之前所说,Verilog 描述的是硬件电路,直接用乘号完成相乘过程,编译器在编译的时候也会把这个乘法表达式映射成默认的乘法器,但其构造不得而知。

例如,在 FPGA 设计中,可以直接调用 IP 核来生成一个高性能的乘法器。在位宽较小的时候,一个周期内就可以输出结果,位宽较大时也可以流水输出。在能满足要求的前提下,可以谨慎的用 * 或直接调用 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 个加法器(当然乘以 15 可以用移位相减的方式)。

有时候数字电路在一个周期内并不能够完成多个变量同时相加的操作。所以数字设计中,最保险的加法操作是同一时刻只对 2 个数据进行加法运算,最差设计是同一时刻对 4 个及以上的数据进行加法运算。

如果设计中有同时对 4 个数据进行加法运算的操作设计,那么此部分设计就会有危险,可能导致时序不满足。

此时,设计参数可配、时序可控的流水线式乘法器就显得有必要了。

设计原理

和十进制乘法类似,计算 13 与 5 的相乘过程如下所示:
在这里插入图片描述
由此可知,被乘数按照乘数对应 bit 位进行移位累加,便可完成相乘的过程。

假设每个周期只能完成一次累加,那么一次乘法计算时间最少的时钟数恰好是乘数的位宽。所以建议,将位宽窄的数当做乘数,此时计算周期短。

乘法器设计

考虑每次乘法运算只能输出一个结果(非流水线设计),设计代码如下。

实例

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 counterreg [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 ;endend//multiplyreg [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) beginif (!rstn) beginmult2_shift    <= 'b0 ;mult1_shift    <= 'b0 ;mult1_acc      <= 'b0 ;endelse 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 ;endelse if (cnt != M) beginmult1_shift    <= mult1_shift << 1 ;  //被乘数乘2mult2_shift    <= mult2_shift >> 1 ;  //乘数右移,方便判断//判断乘数对应为是否为1,为1则累加mult1_acc      <= mult2_shift[0] ? mult1_acc + mult1_shift : mult1_acc ;endelse beginmult2_shift    <= 'b0 ;mult1_shift    <= 'b0 ;mult1_acc      <= 'b0 ;endend//resultsreg [M+N-1:0]        res_r ;reg                  res_rdy_r ;always @(posedge clk or negedge rstn) beginif (!rstn) beginres_r          <= 'b0 ;res_rdy_r      <= 'b0 ;end  else if (cnt == M) beginres_r          <= mult1_acc ;  //乘法周期结束时输出结果res_rdy_r      <= 1'b1 ;endelse beginres_r          <= 'b0 ;res_rdy_r      <= 'b0 ;endendassign res_rdy       = res_rdy_r;assign res           = res_r;endmodule

testbench实例

`timescale 1ns/1nsmodule test ;parameter    N = 8 ;parameter    M = 4 ;reg          clk, rstn;//clockalways beginclk = 0 ; #5 ;clk = 1 ; #5 ;end//resetinitial beginrstn      = 1'b0 ;#8 ;      rstn      = 1'b1 ;end//no pipelinereg                  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 ;beginwait(!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 stateendendtask//driverinitial 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) ;endmult_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 finishinitial beginforever begin#100;if ($time >= 10000)  $finish ;endendendmodule // test

仿真结果如下。

由图可知,输入的 2 个数据在延迟 4 个周期后,得到了正确的相乘结果。算上中间送入数据的延迟时间,计算 4 次乘法大约需要 20 个时钟周期。

**加粗样式
**
流水线乘法器设计
下面对乘法执行过程的中间状态进行保存,以便流水工作,设计代码如下。

单次累加计算过程的代码文件如下(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 [N+M-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            <= 1'b1 ;mult2_shift    <= mult2 >> 1 ;mult1_o        <= mult1 << 1 ;if (mult2[0]) begin//乘数对应位为1则累加mult1_acco  <= mult1_acci + mult1 ;  endelse beginmult1_acco  <= mult1_acci ; //乘数对应位为1则保持endendelse beginrdy            <= 'b0 ;mult1_o        <= 'b0 ;mult1_acco     <= 'b0 ;mult2_shift    <= 'b0 ;endendendmodule

顶层例化

多次模块例化完成多次累加,代码文件如下(mult_man.v ):

实例

module    mult_man#(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))u_mult_step0(.clk              (clk),.rstn             (rstn),.en               (data_rdy),.mult1            ({{(M){1'b0}}, mult1}),.mult2            (mult2),.mult1_acci       ({(N+M){1'b0}}),//output.mult1_acco       (mult1_acc_t[0]),.mult2_shift      (mult2_t[0]),.mult1_o          (mult1_t[0]),.rdy              (rdy_t[0]) );//多次模块例化,用 generate 语句genvar               i ;generatefor(i=1; i<=M-1; i=i+1) begin: mult_stepxmult_cell      #(.N(N), .M(M))u_mult_step(.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]),//output.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

testbench

将下述仿真描述添加到非流水乘法器设计例子的 testbench 中,即可得到流水式乘法运算的仿真结果。

2 路数据为不间断串行输入,且带有自校验模块,可自动判断乘法运算结果的正确性。

实例

reg          data_rdy ;
reg [N-1:0]  mult1 ;
reg [M-1:0]  mult2 ;
wire                 res_rdy ;
wire [N+M-1:0]       res ;//driver
initial 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];endend
endgenerate//自校验
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 ;end
end//module instantiation
mult_man  #(.N(N), .M(M))u_mult(.clk              (clk),.rstn             (rstn),.data_rdy         (data_rdy),.mult1            (mult1),.mult2            (mult2),.res_rdy          (res_rdy),.res              (res));

仿真结果

前几十个时钟周期的仿真结果如下。

由图可知,仿真结果判断信号 error_flag 一直为 0,表示乘法设计正确。

数据在时钟驱动下不断串行输入,乘法输出结果延迟了 4 个时钟周期后,也源源不断的在每个时钟下无延时输出,完成了流水线式的工作。
**加粗样式
**

相对于一般不采用流水线的乘法器,乘法计算效率有了很大的改善。

但是,流水线式乘法器使用的寄存器资源也大约是之前不采用流水线式的 4 倍。

所以,一个数字设计,是否采用流水线设计,需要从资源和效率两方面进行权衡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/143215.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美篇作文网教学资源源码-自带作文数据

非常漂亮的UI设计和页面排版&#xff01; 自适应手机pc端 页面内容均支持自定义 可以用来做网站矩阵&#xff0c;或者增强你其他网站板块&#xff0c;或者单独运营都可以。 可以通过广告方式变现&#xff0c;或者引流等等 友好的seo&#xff0c;更容易被浏览器收录 关注青狐…

LINUX|ubuntu常用指令

文章目录 查看IP显示当前路径下所有文件安装编译工具GCC、调试工具GDB、连接工具SSHmkdir 创建目录export命令显示当前系统定义的所有环境变量echo $PATH命令输出当前的PATH环境变量的值当前命令行添加环境变量&#xff0c;关闭失效&#xff0c;防止多版本库冲突时使用sudo su打…

新移科技发布基于联发科MT8390(Genio 700)平台的物联网 AI 核心板

新移科技研发的XY8390物联网 AI 核心板是一款高度集成、功能强大的平台&#xff0c;该核心板专为各种人工智能 (AI) 和物联网 (IoT) 用例而设计。 处理器采用了 Arm DynamIQ™ 技术&#xff0c;结合了高性能 Cortex-A78 内核和高能效 Cortex-A55 内核&#xff0c;并配备了 Arm …

二维平面扭曲的python实现及思路

二维平面扭曲的python实现及思路 缘起原理实现代码 缘起 工作需要&#xff0c;需要一个尝试改变设备布点的方法&#xff0c;在csdn闲逛时&#xff0c;偶然间发现这样的一篇文章 二维扭曲&#xff0c;参考这位博主的文章&#xff0c;我对其内容进行复现和进一步挖掘。若有侵权或…

四川玖璨电子商务有限公司抖音电商界的领跑者

在当今的电商市场中&#xff0c;四川玖璨电子商务有限公司以其卓越的表现和领先的地位&#xff0c;被广大消费者和业内人士所认可。作为抖音电商领跑者&#xff0c;该公司以其精湛的产品和服务&#xff0c;创新的营销策略&#xff0c;及客户至上的理念&#xff0c;成为这个充满…

爬取北京新发地当天货物信息并展示十五天价格变化(三)---获取物品十五天内的价格

。。。。。。。。。。。。。。。。。。。。。。 1.网页请求一下内容2.通过爬虫进行请求3.获取商品十五天详细数据并绘制折线图4.项目详细代码 1.网页请求一下内容 通过抓包我们发现一共七个参数 limit: 20 # 一页多少数据 current: …

JPA的注解@Field指定为Keyword失败,导致查询不到数据

一、背景 使用 jpa 对es操作&#xff0c;查询条件不生效&#xff0c;需求是批量查询课程编号。说白了&#xff0c;就是一个In集合的查询。在es里&#xff0c;如果是精准匹配是termQuery&#xff0c;比如&#xff1a; queryBuilder.filter(QueryBuilders.termQuery(“schoolId…

C++ placement new使用

placement new重载来原来的operator new&#xff0c;且placement new不能被即需重载 placement new是在原有的一块地址上继续创建一个对象&#xff0c;注意对象类型要一致&#xff0c;这样的操作的优势有两个&#xff1a; 1、不用花时间在找合适的空间存放新对象&#xff0c;…

华为云云耀云服务器L实例评测|使用华为云耀云服务器L实例的CentOS部署Docker并运行Tomcat应用

目录 前言 步骤1&#xff1a;登录到华为云耀云服务器L实例 步骤2&#xff1a;安装Docker 并验证Docker安装 步骤3&#xff1a;拉取Tomcat镜像并运行Tomcat容器 步骤4&#xff1a;放行8080端口 步骤5&#xff1a;访问tomcat 步骤6&#xff1a;管理Tomcat容器 小结 前言 …

【QT+CUDA】QT中使用cuda,QT+VS+cuda下载安装配置

文章目录 相关网址汇总&#xff1a; 一、软件安装&#xff1a;VS、CUDA、QT1 安装VS1.1 下载1.2 vs2017安装1.3 vs2015安装 2 安装CUDA2.1 下载2.2 安装2.3 测试2.4 卸载 3 安装QT3.1 下载3.2 安装 二、QT使用cuda1 .pro文件 三、常用操作1 NVIDIA控制面板&#xff1a;显卡、驱…

数据分析技能点-正态分布和其他变量分布

在数据驱动的世界里,了解和解释数据分布是至关重要的。不同类型的数据分布,如正态分布、二项分布和泊松分布,具有不同的特性和应用场景。这些分布不仅在统计学和数据科学中有广泛应用,而且在日常生活和商业决策中也起着关键作用。 文章目录 正态分布正态分布和偏差其他常见…

如何快速搭建一个react项目?如何使用react脚手架快速搭建项目?

如何使用react脚手架快速搭建项目&#xff1f; 一、前提 电脑已经安装了node和npm环境。 react文档中要求Node > 8.10 和 npm > 5.6&#xff0c;查看版本&#xff1a;node -v&#xff1b;npm -v&#xff1b; 二、步骤 1、在合适的文件夹中打开命令行窗口cmd 2、全局安…

【设计模式】六、建造者模式

文章目录 需求介绍角色应用实例建造者模式在 JDK 的应用和源码分析java.lang.StringBuilder 中的建造者模式 建造者模式的注意事项和细节 需求 需要建房子&#xff1a;这一过程为打桩、砌墙、封顶房子有各种各样的&#xff0c;比如普通房&#xff0c;高楼&#xff0c;别墅&…

C语言进阶---动态内存管理

动态内存管理 前言&#xff1a;一、为什么存在动态内存分配&#xff1f;二、动态内存函数的介绍1.数据在不同区域的储存&#xff1a;2、malloc和free3、calloc4、realloc 三、常见的动态内存错误1、对NULL指针的解引用操作2、对动态开辟空间的越界访问3、对非动态内存开辟的空间…

C# 集合

C# 集合 集合集合接口和类型列表队列栈链表有序表字典LoopupHashSet位数组 集合 数组的大小是固定的。如果元素个数是动态的&#xff0c;就应使用集合类。List 和 ArrayList 是与数组相当的集合类。还有其他类型的集合&#xff1a;队列、栈、链表和字典。 集合接口和类型 集…

无线振弦采集仪在岩土工程安全监测中优化成本支出

无线振弦采集仪在岩土工程安全监测中优化成本支出 随着城市的快速发展以及建筑业的不断壮大&#xff0c;岩土工程的安全监测变得越来越重要。在岩土工程中&#xff0c;振弦是一种重要的监测手段&#xff0c;可以有效地评估土体的力学性质和变形情况。因此&#xff0c;无线振弦…

一文了解VR全景在城市园区和电子楼书的应用

引言&#xff1a; 虚拟现实&#xff08;VR&#xff09;技术在日常生活中越发普及&#xff0c;已经成为众多行业的宣传工具&#xff0c;房地产行业近些年来热度较低&#xff0c;VR全景为房地产展示带来了新方式&#xff0c;为购房者提供更真实、更直观的体验。 一&#xff0e;…

康耐视visionpro脚本CogRectangleAffine ,CogPolygon图形限定框,边界显示(划痕缺陷案例分享)

目录 1.划痕缺陷整体方案设计:2.测试一效果图:3.测试一脚本编写​:4.测试二效果图:5.测试二脚本编写:6.测试三效果图:7.​测试三脚本编写:测试版本:康耐视visionpro9.0 1.划痕缺陷整体方案设计: 2.测试一效果图: 3.测试一脚本编写​: CogRectangleAffine Rectangle…

Spring Boot 如何使用Liquibase 进行数据库迁移

在现代的应用程序开发中&#xff0c;数据库迁移是一个不可或缺的环节。它使开发人员能够有效地管理数据库模式的变化&#xff0c;确保应用程序与数据库之间的一致性。Liquibase 是一个流行的开源工具&#xff0c;用于管理数据库的版本控制和迁移。本文将介绍如何在Spring Boot应…

网络安全攻防:软件逆向之反汇编

网络安全是当今社会中一个非常重要的问题&#xff0c;而软件逆向工程是网络安全攻防中常用的一种技术手段。在软件逆向工程中&#xff0c;反汇编是一种基础而重要的技术。通过反汇编&#xff0c;我们可以将二进制程序转换为汇编语言&#xff0c;从而更好地理解程序的执行流程和…