数字前端/FPGA设计——握手与反压问题


 声明:本文来自0431大小回


前言:在芯片设计或者FPGA设计过程中,流水设计是经常用到的,但是考虑数据安全性,需要与前后级模块进行握手通信,这时候就需要对流水数据进行反压处理,本文将具体介绍握手与反压。

目录

  • 握手协议
  • 握手与反压
  • 反压
  • 带存储体的反压
    • 字节的问题
    • 代码分析
    • 逐级反压与跨级反压
  • 不带存储体的反压
    • 代码分析

握手协议

本文讲述valid-ready握手,下面列出三种握手情况,目的是解释清楚握手的时序。

  • valid先发起请求

  • ready先发起请求

  • 同时发起请求

  • 分析

仔细观察上述3幅时序图,我们了解valid-ready握手机制需要注意三件事:

  1. valid与ready不可过度依赖,比如valid不可以等待ready到达再拉高,但是在axi协议中的握手信号,ready是可以等待valid拉高再拉高的,valid不可依赖ready的原因是防止死锁(deadlock),本文的代码他俩彼此可以互相独立发出请求拉高;
  2. valid拉高时与有效数据同步,时钟要对齐;
  3. 当数据计算好后,valid可以拉高等待ready拉高,但是每当握手成功之后,数据需要更新,如果此时没有新的有效数据,valid要拉低。

握手与反压

当入口流量大于出口流量,这时候就需要反压,或者,当后级未准备好时,如果本级进行数据传递,那么它就需要反压前级,所以此时前级需要将数据保持不动,直到握手成功才能更新数据。而反压在多级流水线中就变得稍显复杂,原因在于,比如我们采用三级流水设计,如果我们收到后级反压信号,我们理所当然想反压本级输出信号的寄存器,但是如果只反压最后一级寄存器,那么会面临一个问题,就是最后一级寄存器数据会被前两级流水冲毁,导致数据丢失,引出数据安全问题,所以我们此时需要考虑反压设计。

反压

常用的反压方法有三种:

  • 不带存储体的反压

也就是后级反压信号对本级模块中所有流水寄存器都进行控制,由于不包含存储体,为了保证数据安全性,后级反压信号可以同时反压本模块中所有流水寄存器。

优点:节省面积资源

缺点:寄存器端口控制复杂

适用情况:流水线深度较大时

  • 带存储体的逐级反压

如果流水级数不深,可以在每一需要握手交互模块增加存储体,原理上相当于,如果后级发出反压信号,可以直接对本级流水线数据源头进行反压,其余中间级不需控制,但后级需要包含RAM或FIFO等存储体,可以接收流水,并需设置水线(water line),确定反压时间,防止数据溢出,保证数据安全性。

优点:各级流水寄存器端口控制简单

缺点:需要额外存储体

适用情况:流水线深度较小,每一模块都包含存储体时

  • 带存储体的跨级反压

很多时候在具体设计过程中,颗粒度划分不精细,反压这时候是对模块而言,而不是说模块内部有多少级流水。此外,并不是每一模块都带有存储体。比如,其中可能a模块没有存储体,b模块没有存储体,但ab模块内部还有多级流水,如果c模块有存储体,并且需要反压前级模块,这时候可以选择反压a模块的源头输入数据,然后将ab的流水都存储到带有存储体的c模块,但是如果ab不是都没有存储体的话,就不应该跨级反压,而应该逐级反压,具体原因后续会讲。

优点:控制简单方便

缺点:需要额外存储体,模块间耦合度高

适用情况:某些模块带存储体,某些模块不带存储体时

带存储体的反压

如上文所述,很多时候我们不喜欢对每一级细分流水都进行反压,所以可以选择带存储体的反压,也就是增加RAM或者FIFO,在反压上级模块的同时,本级有足够的深度来存储上一级的流水数据。具体内容如下图所示:

此时我们假设车库就是最后一级模块,需要对前一级进行反压,车库是带有存储体的模块,那么此时就需要设计水线waterline,也就是当waterline为多少时开始反压前一级模块(车库入口闸机)。由上图可知,当waterline最大为90时,必须向上一模块发出反压请求,因为在途的还有10辆车,这个就相当于我们设计的10级流水,其中有10级寄存器有流水数据输出,所以车库的剩余容量还可以存储住途中流水,保证了数据安全。

举个例子——字节的问题

问题:设计一个并行6输入32比特加法器,输出1个带截断的32比特加法结果,要求用三级流水设计,带前后反压。

主要输入:

  1. 6个32bit数据
  2. 上一级的valid_i
  3. 下一级的ready_i

输出:

  1. 1个32bit结果
  2. 给上一级的ready_o
  3. 给下一级的valid_o
  • 分析

其实在多级流水设计中,如果每一级只有一个寄存器,并且都在一个模块中,也就是说当颗粒度划分的很细的时候,一般使用带存储体的反压,比如六级流水,那么就设计好水线,在FIFO未满时提前发出反压信号,一般水线设为FIFO_DEPTH - 流水级数,下面是我设计的代码。

核心思想就是如果FIFO未达到水线(WATERLINE)时,给上一级的反压信号ready_o就持续拉高,否则拉低;FIFO非空时就可以给下一级valid_o拉高,然后下一级的反压信号ready_i可以作为FIFO的读使能信号,具体请参考下文代码。

//还未仿真,欢迎指出问题
module handshake_fifo #(parameter           FIFO_DATA_WIDTH = 32,parameter           FIFO_DEPTH = 8
)(input  wire         clk,input  wire         rst,input  wire         valid_i,input  wire         ready_i,input  wire  [31:0] a,input  wire  [31:0] b,input  wire  [31:0] c,input  wire  [31:0] d,input  wire  [31:0] e,input  wire  [31:0] f,output logic [31:0] dout,output logic        ready_o,output logic        valid_o);localparam          WATERLINE = FIFO_DEPTH - 3; //three levels' pipelinelogic               handshake;logic               handshake_ff1;logic               handshake_ff2;logic               wr_en;assign handshake = ready_o & valid_i;always @ (posedge clk or posedge rst) beginif(rst) beginhandshake_ff1 <= '0;handshake_ff2 <= '0;endelse beginhandshake_ff1 <= handshake;handshake_ff2 <= handshake_ff1;endendreg [31 : 0] r1_ab;always @ (posedge clk or posedge rst) beginif(rst) beginr1_ab <= '0;endelse if(handshake)beginr1_ab <= a + b;endendreg [31 : 0] r1_cd;always @ (posedge clk or posedge rst) beginif(rst) beginr1_cd <= '0;endelse if(handshake)beginr1_cd <= c + d;endendreg [31 : 0] r1_ef;always @ (posedge clk or posedge rst) beginif(rst) beginr1_ef <= '0;endelse if(handshake)beginr1_ef <= e + f;endendreg [31 : 0] r2_abcd;always @ (posedge clk or posedge rst) beginif(rst) beginr2_abcd <= '0;endelse if(handshake_ff1) beginr2_abcd <= r1_ab + r1_cd;endendreg [31 : 0] r2_ef;always @ (posedge clk or posedge rst) beginif(rst) beginr2_ef <= '0;endelse if(handshake_ff1) beginr2_ef <= r1_ef;endendreg [31 : 0] r3;always @ (posedge clk or posedge rst) beginif(rst) beginr3 <= '0;endelse if(handshake_ff2) beginr3 <= r2_ef + r2_abcd;endendalways @ (posedge clk or posedge rst) beginif(rst) beginwr_en <= 1'b0;endelse if(handshake_ff2) beginwr_en <= 1'b1;endelse beginwr_en <= 1'b0;endendalways_ff @(posedge clk)beginif(rst)beginready_o <= 1'b0;endelse if(usedw > WATERLINE)beginready_o <= 1'b0;endelse beginready_o <= 1'b1;endendassign valid_o = ~empty;sync_fifo # (.MEM_TYPE   ("auto"         ),.READ_MODE  ("fwft"         ),.WIDTH      (FIFO_DATA_WIDTH),.DEPTH      (FIFO_DEPTH     ))fifo_inst(.clk    (clk                ), // input  wire.rst_n  (rst_n              ), // input  wire.wren   (wr_en              ), // input  wire.din    (r3                 ), // input  wire [WIDTH-1:0].rden   (ready_i            ), // input  wire.dout   (dout               ), // output reg  [WIDTH-1:0].empty  (empty              ), // output wire.usedw  (usedw              ));endmodule

逐级反压与跨级反压

这时候的反压包括逐级反压和跨级反压,具体区别可以参考下图:

由上图可见,3个模块都包含存储体,我们假设此时module3到达了它的水线,那么它有两种方式反压前面的模块,一种是从最源头进行反压,另一种是逐级反压。

建议:当每一模块都有存储体时,建议逐级反压。

原因:如果逐级反压的话,方法就是module3到达水线则反压module2,module2到达水线反压module1,每一级的水线和存储体大小设计就如上文车库模型所述,简单清晰。但是,如果选择跨级反压,那么module3的存储体深度 = waterlie3 + 在途1 + waterline1 + 在途2 + waterline2 + 在途3,可见每一级的存储体会变大并且水线计算复杂,另外路径变长,模块间耦合度增高,不利于复用与维护。但是,如果不是每一模块都包含存储体,那么可以选择跨级反压。

不带存储体的反压

分析与代码

同样是上文字节的问题,如果此时要求不允许使用FIFO,那应该怎么设计呢?

核心思想就是保证每一级流水中的每一级寄存器的数据安全,可以把每一级寄存器当成一个深度为1的FIFO,下一级有无数据可以看对应的valid信号。下一级无数据或者下一级已经准备好了,那么就可以向上一级取数据,本质上是pre-fetch结构。具体内容可以参考如下代码,注意:我没有把重复部分的代码写全,但是写出了所有核心代码,可供参考。

module handshake_pb #(
)(input  wire         clk,input  wire         rst,input  wire         valid_i,output wire         ready_o,input  wire  [31:0] a,input  wire  [31:0] b,input  wire  [31:0] c,input  wire  [31:0] d,input  wire  [31:0] e,input  wire  [31:0] f,output wire  [31:0] dout,input  wire         ready_i,output reg          valid_o);assign ready_o = ~valid_r1 || ready_r1;//pre_fetch结构//valid_r1为0代表下一级无数据//ready_r1代表下一级准备好了读always_ff @ (posedge clk) begin if(rst)beginvalid_r1    <= 1'b0;endelse if(ready_o)beginvalid_r1    <= valid_i;endendalways_ff @ (posedge clk) beginif(ready_o & valid_i)beginr1_ab       <= a + b;   //数据信号不复位endendassign ready_r1 = ~valid_r2 || ready_r2;reg [31 : 0] r2_abcd;always_ff @ (posedge clk) begin if(rst)beginvalid_r2    <= 1'b0;endelse if(ready_r1)beginvalid_r2    <= valid_r1;endendalways_ff @ (posedge clk) beginif(ready_r1 & valid_r1)beginr2_abcd     <= r1_ab + r1_cd;endendassign ready_r2 = ~valid_r3 || ready_i;reg [31 : 0] r3;always_ff @ (posedge clk) begin if(rst)beginvalid_r3    <= 1'b0;endelse if(ready_r2)beginvalid_r3    <= valid_r2;endendalways_ff @ (posedge clk) beginif(ready_r2 & valid_r2)beginr3          <= r2_ef + r2_abcd;endendassign dout     = r3;assign valid_o  = valid_r3;
endmodule

值得大家注意的是ready和valid的输入输出,此外就是每一级流水的握手信号处理。

对于不带存储体的反压,

不带存储体的反压的主要缺点还是timing差,不适合长流水。流水线最下游的ready会一路传导到最上游,会导致组合逻辑过长,引起时序问题。可以考虑中间再加一级寄存器,输出一级寄存器,可以参考xilinx的axis handshake ip。

此外,这里的代码按照握手协议,valid不能够看ready信号,但是ready是可以等valid来了再拉高的。如果下一级ready信号初始一直为0,输出的valid也会一直为0,这样就死锁了。这个代码主要还是为了解决这道题,不能用作标准模块。

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

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

相关文章

C++大学教程(第九版)5.25去除break语句 5.27去除cintinue语句

5.25题目 (去除break和continue)break和continue 语句遭到质疑的原因是它们的非结构化性。实际上,break和continue 语句总能用结构化的语句取代。请详述如何从程序的一条循环语中去除break语句&#xff0c;并用某种结构化的手段替代。提示:break 语句用于在循环体内离开一个循…

高光谱分类论文解读分享之HybridSN:基于 3-D–2-D CNN 的高光谱分类(经典回顾)

IEEE GRSL 2019&#xff1a;HybridSN&#xff1a;基于 3-D–2-D CNN 的高光谱分类 题目 HybridSN: Exploring 3-D–2-D CNN Feature Hierarchy for Hyperspectral Image Classification 作者 Swalpa Kumar Roy, Student Member, IEEE, Gopal Krishna, Shiv Ram Dubey , Mem…

数模百科】一篇文章讲清楚灰色预测模型GM(1,n)附python代码

本篇文章摘录自GM(1,n) - 数模百科&#xff0c;如果你希望了解更多关于灰色模型的知识&#xff0c;请移步 灰色预测模型 - 数模百科 在阅读本篇文章之前&#xff0c;强烈建议先阅读这篇文章 【数模百科】一篇文章讲清楚灰色预测模型GM&#xff08;1&#xff0c;1&#xff09;附…

【寒假每日一题·2024】AcWing 5396. 棋盘(补)

文章目录 一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、时间复杂度3、代码详解 三、知识风暴 一、题目 1、原题链接 5396. 棋盘 2、题目描述 二、解题报告 1、思路分析 &#xff08;1&#xff09;首先初始棋盘均为白色的棋子&#xff0c;也就是棋盘数组默认为…

商家转账到零钱开通条件有哪些?转账场景

商家转账到零钱是什么&#xff1f; 【商家转账到零钱】可以说是【企业付款到零钱】的升级版&#xff0c;商家转账到零钱可以为商户提供同时向多个用户微信零钱转账的能力&#xff0c;支持分销返佣、佣金报酬、企业报销、企业补贴、服务款项、采购货款等自动向用户转账的场景。…

Nvidia-docker的基础使用方法

安装&#xff1a; 安装nvidia-docker&#xff1a; distribution$(. /etc/os-release;echo $ID$VERSION_ID)curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.l…

区域入侵检测AI边缘计算智能分析网关V4如何通过ssh进行服务器远程运维

智能分析网关V4是一款高性能、低功耗的AI边缘计算硬件设备&#xff0c;它采用了BM1684芯片&#xff0c;集成高性能8核ARM A53&#xff0c;主频高达2.3GHz&#xff0c;并且INT8峰值算力高达17.6Tops&#xff0c;FB32高精度算力达到2.2T&#xff0c;每个摄像头可同时配置3种算法&…

nodejs下载安装

一、node下载安装 官网下载 官网 根据自己电脑系统选择合适的版本进行下载&#xff0c;我这里选择window 64 位 下载完点击安装 打开cmd查看安装 此处说明下&#xff1a;新版的Node.js已自带npm&#xff0c;安装Node.js时会一起安装&#xff0c;npm的作用就是对Node.js…

【小笔记】算法训练基础超参数调优思路

【学而不思则罔&#xff0c;思维不学则怠】 本文总结一下常见的一些算法训练超参数调优思路&#xff08;陆续总结更新&#xff09;&#xff0c;包括&#xff1a; batchsize学习率epochsdropout&#xff08;待添加&#xff09; Batch_size 2023.9.29 简单来说&#xff0c;较…

速通——决策树(泰坦尼克号乘客生存预测案例)

一、决策树 1、概述 树中每个内部节点表示一个特征上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff0c;每个叶子节点代表一种分类结果 2、建立过程 1. 特征选择&#xff1a;选取有较强分类能力的特征。 2. 决策树生成&#xff1a;根据选择的特征生成决策树。 3.…

论文笔记(三十九)Learning Human-to-Robot Handovers from Point Clouds

Learning Human-to-Robot Handovers from Point Clouds 文章概括摘要1. 介绍2. 相关工作3. 背景3.1. 强化学习3.2. 移交模拟基准 4. 方法4.1. Handover Environment4.2. 感知4.3. 基于视觉的控制4.4. 师生两阶段培训 (Two-Stage Teacher-Student Training) 5. 实验5.1. 模拟评估…

配置zabbix平台对数据库以及主从状态的监控

引言&#xff1a;明人不说暗话&#xff0c;今天分享下配置zabbix平台对数据库以及主从状态的监控 准备好zabbix监控平台&#xff08;zabbix-server端&#xff09;例10.12.153.235 db1客户端&#xff08;zabbix-agent&#xff09;例10.12.153.73 1.安装Zabbix存储库 # rpm -Uv…