【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

FIFO是英文FirstInFirstOut的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。作用:FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集,另一端是计算…

大家好,又见面了,我是你们的朋友全栈君。

FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

作用: FIFO用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集, 另一端是计算机的PCI总线,假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为 1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而 DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的

 

分类:FIFO的分类根均FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

若输入输出总线为同一时钟域,FIFO只是作为缓存使用,用同步FIFO即可,此时,FIFO在同一时钟下工作,FIFO的写使能、读使能、满信号、空信号、输入输出数据等各种信号都在同一时钟沿打入或输出。

若输入输出为不同时钟域,FIFO作时钟协同作用,需要采用异步FIFO,此时,FIFO在读与写分别在各自时钟下工作,FIFO的写使能、写满信号、输入数据等各种输入信号都在同一输入时钟沿打入或输出。读使能、读空信号、输出数据等各种输出信号都在同一输出时钟沿打入或输出。

 

设计:FIFO设计的难点在于怎样判断FIFO的空/满状态。为了保证数据正确的写入或读出,而不发生益处或读空的状态出现,必须保证FIFO在满的情况下,不 能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题。

       读写指针的工作原理
  读指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)。

  写指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)

       FIFO的“空”/“满”检测
  FIFO设计的关键:产生可靠的FIFO读写指针和生成FIFO“空”/“满”状态标志。

  当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时,或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,如下图所示:

          【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

 

  当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈,折回来(wrapped around)又追上了读指针,如下图:

        

                                        【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

       为了区分到底是满状态还是空状态,可以采用以下方法:

      在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。

       如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
       如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;
 

………………………………………………………………………………………………………………………..

一、同步FIFO的Verilog代码  

在modlesim中验证过。

/******************************************************
A fifo controller verilog description.
******************************************************/
module fifo(datain, rd, wr, rst, clk, dataout, full, empty);
input [7:0] datain;
input rd, wr, rst, clk;
output [7:0] dataout;
output full, empty;
wire [7:0] dataout;
reg full_in, empty_in;
reg [7:0] mem [15:0];
reg [3:0] rp, wp;
assign full = full_in;
assign empty = empty_in;
// memory read out
assign dataout = mem[rp];
// memory write in
always@(posedge clk) begin
    if(wr && ~full_in) mem[wp]<=datain;
end
// memory write pointer increment
always@(posedge clk or negedge rst) begin
    if(!rst) wp<=0;
    else begin
      if(wr && ~full_in) wp<= wp+1'b1;
    end
end
// memory read pointer increment
always@(posedge clk or negedge rst)begin
    if(!rst) rp <= 0;
    else begin
      if(rd && ~empty_in) rp <= rp + 1'b1;
    end
end
// Full signal generate
always@(posedge clk or negedge rst) begin
    if(!rst) full_in <= 1'b0;
    else begin
      if( (~rd && wr)&&((wp==rp-1)||(rp==4'h0&&wp==4'hf)))
          full_in <= 1'b1;
      else if(full_in && rd) full_in <= 1'b0;
    end
end
// Empty signal generate
always@(posedge clk or negedge rst) begin
    if(!rst) empty_in <= 1'b1;
    else begin
      if((rd&&~wr)&&(rp==wp-1 || (rp==4'hf&&wp==4'h0)))
        empty_in<=1'b1;
      else if(empty_in && wr) empty_in<=1'b0;
    end
end
endmodule

 

二、异步FIFO

(1)由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?

  跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较

  解决方法加两级寄存器同步 + 格雷码(目的都是消除亚稳态)

 

1.使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如下图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接崩溃。

【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

 

2.将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

       那么,多位二进制码如何转化为格雷码?

【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

换一种描述方法:

【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

verilog代码实现就一句:assign  gray_code = (bin_code>>1)  ^  bin_code;

 

 (2)在格雷码域如何判断空与满?

 这里直接给出结论:

  判断读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针rd2_wp每一位完全相同;

  判断写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针wr2_rp高两位不相同,其余各位完全相同;

                    【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现「建议收藏」

 

(3)Verilog实现

这个是基于RAM的异步FIFO代码,个人认为代码结构简单易懂,非常适合于考试中填写。


module  fifo
#(
  parameter WSIZE = 8;
  parameter DSIZE = 32;
)
(
  input wr_clk,
  input rst,
  input wr_en,
  input [WSIZE-1 : 0]din,
  input rd_clk,
  input rd_en,
  output [WSIZE-1 : 0]dout,
  output reg rempty,
  output reg wfull
);

//定义变量
reg [WSIZE-1 :0] mem [DSIZE-1 : 0];
reg [WSIZE-1 : 0] waddr,raddr;
reg [WSIZE : 0] wbin,rbin,wbin_next,rbin_next;
reg [WSIZE : 0] wgray_next,rgray_next;
reg [WSIZE : 0] wp,rp;
reg [WSIZE : 0] wr1_rp,wr2_rp,rd1_wp,rd2_wp;
wire rempty_val,wfull_val;

//输出数据
assign dout = mem[raddr];

//输入数据
always@(posedge wr_clk)
  if(wr_en && !wfull)
    mem[waddr] <= din;

//1.产生存储实体的读地址raddr; 2.将普通二进制转化为格雷码,并赋给读指针rp
always@(posedge rd_clk or negedge rst_n)
  if(!rst_n)
    {rbin,rp} <= 0;
  else
    {rbin,rp} <= {rbin_next,rgray_next};

assign raddr = rbin[WSIZE-1 : 0];
assign rbin_next = rbin + (rd_en & ~rempty);
assign rgray_next = rbin_next ^ (rbin_next >> 1);

//1.产生存储实体的写地址waddr; 2.将普通二进制转化为格雷码,并赋给写指针wp
always@(posedge wr_clk or negedge rst_n)
  if(!rst_n)
    {wbin,wp} <= 0;
  else
    {wbin,wp} <= {wbin_next,wgray_next};

assign waddr = wbin[WSIZE-1 : 0];
assign wbin_next = wbin + (wr_en & ~wfull);
assign wgray_next = wbin_next ^ (wbin_next >> 1);

//将读指针rp同步到写时钟域
always@(posedge wr_clk or negedge rst_n)
  if(!rst_n)
    {wr2_rp,wr1_rp} <= 0;
  else
    {wr2_rp,wr1_rp} <= {wr1_rp,rp};

//将写指针wp同步到读时钟域
always@(posedge rd_clk or negedge rst_n)
  if(!rst_n)
    {rd2_wp,rd1_wp} <= 0;
  else
    {rd2_wp,rd1_wp} <= {rd1_wp,wp};

//产生读空信号rempty
assign rempty_val = (rd2_wp == rgray_next);
always@(posedge rd_clk or negedge rst_n)
  if(rst_n)
    rempty <= 1'b1;
  else
    rempty <= rempty_val;

//产生写满信号wfull
assign wfull_val = ((~(wr2_rp[WSIZE : WSIZE-1]),wr2_rp[WSIZE-2 : 0]) == wgray_next);
always@(posedge wr_clk or negedge rst_n)
  if(!rst_n)
    wfull <= 1'b0;
  else
    wfull <= wfull_val;

endmodule

参考博文:https://www.cnblogs.com/ylsm-kb/p/9068449.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/129088.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 浏览器被hao.360.cn劫持怎么办

    浏览器被hao.360.cn劫持怎么办特么的现在互联网太没节操了,一大早发现我的浏览器被hao.360.cn劫持了,弄了好久都没弄好,后来一想可能是因为qvod的原因,这可是哥当年看片的神器啊……废话不说:1,进入:C:\ProgramData\QvodPlayer\QvodWebBase2,点开1.0.0.53(或者其它)文件夹3,直接删除里面的文件是删除不了的。更改QvodWebBase64.dll后缀名为QvodWebB…

    2022年7月14日
    15
  • mysql查询语句执行过程及运行原理命令_MySQL常用命令

    mysql查询语句执行过程及运行原理命令_MySQL常用命令Mysql查询语句执行原理数据库查询语句如何执行?DML语句首先进行语法分析,对使用sql表示的查询进行语法分析,生成查询语法分析树。 语义检查:检查sql中所涉及的对象以及是否在数据库中存在,用户是否具有操作权限等 视图转换:将语法分析树转换成关系代数表达式,称为逻辑查询计划; 查询优化:在选择逻辑查询计划时,会有多个不同的表达式,选择最佳的逻辑查询计划; 代码生成:必须将逻辑查…

    2025年6月9日
    0
  • Java枚举详解

    Java枚举详解枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。枚举在曰常生活中很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是7天中的一个等。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。在JDK1.5之前没有枚举类型,那时候一般用接口常量来替代。而使用Java枚举类型enum可以更贴近地表示这种常量。声明枚举声明枚举时必须使用enu…

    2022年6月4日
    27
  • Maven项目打包为jar的几种方式[通俗易懂]

    Maven项目打包为jar的几种方式[通俗易懂]Maven项目打包为jar的几种方式这里收集整理下以往打包MAVEN项目为JAR包的各种方式直接打包,不打包依赖包直接打包,不打包依赖包,仅打包出项目中的代码到JAR包中。在POM中添加如下plugin即可,随后执行maveninstall&amp;lt;plugin&amp;gt;&amp;lt;groupId&amp;gt;org.apach…

    2022年6月9日
    53
  • 使用 Java8的 stream对list数据去重,使用filter()过滤列表,list转map「建议收藏」

    使用 Java8的 stream对list数据去重,使用filter()过滤列表,list转map「建议收藏」list去重,根据对象某个属性、某几个属性去重去除List中重复的StringListunique=list.stream().distinct().collect(Collectors.toList());去除List中重复的对象//Person对象publicclassPerson{privateStringid;…

    2022年5月9日
    396
  • pytest重试_手机qq插件加载失败

    pytest重试_手机qq插件加载失败安装:pip3installpytest-rerunfailures重新运行所有失败用例要重新运行所有测试失败的用例,请使用–reruns命令行选项,并指定要运行测试的最大次数:$py

    2022年7月31日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号