同步fifo的verilog代码_verilog 异步复位

同步fifo的verilog代码_verilog 异步复位  本文大部分内容来自CliffordE.Cummings的《SimulationandSynthesisTechniquesforAsynchronous FIFODesign》,经过自己的一些改变,理论部分为转载,代码自己完成。一、FIFO简介  FIFO是英文FirstInFirstOut的缩写,是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

  本文大部分内容来自Clifford E. Cummings的《Simulation and Synthesis Techniques for Asynchronous FIFO Design》,经过自己的一些改变,理论部分为转载,代码自己完成。

一、FIFO简介

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

用途1:

  异步FIFO读写分别采用相互异步的不同时钟。在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步FIFO是这个问题的一种简便、快捷的解决方案,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。

用途2:

  对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

二、分类

  同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作;

  异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

三、FIFO的常见参数

  • FIFO的宽度:即FIFO一次读写操作的数据位;
  • FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
  • 满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
  • 空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
  • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
  • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

===============================分   隔    符 ==============================

  1. 读写指针的工作原理

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

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

 

  1. FIFO的“空”/“满”检测

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

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

          同步fifo的verilog代码_verilog 异步复位

 

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

        同步fifo的verilog代码_verilog 异步复位

 

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

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

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

3.二进制FIFO指针的考虑

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

    同步fifo的verilog代码_verilog 异步复位

 

4.

  • 使用gray码进行对比,如何判断“空”与“满”

   使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。

  对于“空”的判断依然依据二者完全相等(包括MSB)

  而对于“满”的判断,如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:

  • wptr和同步过来的rptrMSB不相等,因为wptr必须比rptr多折回一次。
  • wptrrptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是01111111MSB不同说明多折回一次,111相同代表同一位置。
  • 剩下的其余位完全相等。

      同步fifo的verilog代码_verilog 异步复位

5.总体实现

    系统的总体框图如下:

        同步fifo的verilog代码_verilog 异步复位

 四、同步化分析

由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决?
  跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的,要是将读时钟域的读指针与写时钟域的写指针不做任何处理直接比较肯定是错误的,因此我们需要进行同步处理以后仔进行比较
  解决方法
加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
 
1.使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如下图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接崩溃。
同步fifo的verilog代码_verilog 异步复位

 

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

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

同步fifo的verilog代码_verilog 异步复位

 

换一种描述方法:

同步fifo的verilog代码_verilog 异步复位

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

 使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。 

这里直接给出结论:

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

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

assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同 assign empty = ( rd_addr_gray == wr_addr_gray_d2 );

五、Verilog实现

module fifo_async#(
                 parameter   data_width = 16,
                 parameter   data_depth = 256,
                 parameter   addr_width = 8
)
(
                  input                           rst,
                  input                           wr_clk,
                  input                           wr_en,
                  input      [data_width-1:0]     din,         
                  input                           rd_clk,
                  input                           rd_en,
                  output reg                     valid,
                  output reg [data_width-1:0]     dout,
                  output                          empty,
                  output                          full
    );


reg    [addr_width:0]    wr_addr_ptr;//地址指针,比地址多一位,MSB用于检测在同一圈
reg    [addr_width:0]    rd_addr_ptr;
wire   [addr_width-1:0]  wr_addr;//RAM 地址
wire   [addr_width-1:0]  rd_addr;

wire   [addr_width:0]    wr_addr_gray;//地址指针对应的格雷码
reg    [addr_width:0]    wr_addr_gray_d1;
reg    [addr_width:0]    wr_addr_gray_d2;
wire   [addr_width:0]    rd_addr_gray;
reg    [addr_width:0]    rd_addr_gray_d1;
reg    [addr_width:0]    rd_addr_gray_d2;


reg [data_width-1:0] fifo_ram [data_depth-1:0];

//=========================================================write fifo 
genvar i;
generate 
for(i = 0; i < data_depth; i = i + 1 )
begin:fifo_init
always@(posedge wr_clk or posedge rst)
    begin
       if(rst)
          fifo_ram[i] <= 'h0;//fifo复位后输出总线上是0,并非ram中真的复位。可无
       else if(wr_en && (~full))
          fifo_ram[wr_addr] <= din;
       else
          fifo_ram[wr_addr] <= fifo_ram[wr_addr];
    end   
end    
endgenerate    
//========================================================read_fifo
always@(posedge rd_clk or posedge rst)
   begin
      if(rst)
         begin
            dout <= 'h0;
            valid <= 1'b0;
         end
      else if(rd_en && (~empty))
         begin
            dout <= fifo_ram[rd_addr];
            valid <= 1'b1;
         end
      else
         begin
            dout <=   'h0;//fifo复位后输出总线上是0,并非ram中真的复位,只是让总线为0;
            valid <= 1'b0;
         end
   end
assign wr_addr = wr_addr_ptr[addr_width-1-:addr_width];
assign rd_addr = rd_addr_ptr[addr_width-1-:addr_width];
//=============================================================格雷码同步化
always@(posedge wr_clk )
   begin
      rd_addr_gray_d1 <= rd_addr_gray;
      rd_addr_gray_d2 <= rd_addr_gray_d1;
   end
always@(posedge wr_clk or posedge rst)
   begin
      if(rst)
         wr_addr_ptr <= 'h0;
      else if(wr_en && (~full))
         wr_addr_ptr <= wr_addr_ptr + 1;
      else 
         wr_addr_ptr <= wr_addr_ptr;
   end
//=========================================================rd_clk
always@(posedge rd_clk )
      begin
         wr_addr_gray_d1 <= wr_addr_gray;
         wr_addr_gray_d2 <= wr_addr_gray_d1;
      end
always@(posedge rd_clk or posedge rst)
   begin
      if(rst)
         rd_addr_ptr <= 'h0;
      else if(rd_en && (~empty))
         rd_addr_ptr <= rd_addr_ptr + 1;
      else 
         rd_addr_ptr <= rd_addr_ptr;
   end

//========================================================== translation gary code
assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;

assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同
assign empty = ( rd_addr_gray == wr_addr_gray_d2 );

endmodule

仿真

六、重要补充

关于异步FIFO的关键技术,有两个,一个是格雷码减小亚稳态,另一个是指针信号跨异步时钟域的传递。我在自己写异步FIFO的时候也很疑惑,地址指针在同步化的时候,肯定会产生至少两个周期的延迟,如果是从快时钟域到慢时钟域,快时域的地址指针并不能都被慢时域的时钟捕获,同步后的指针比起实际的指针延迟会更大。如果以此来产生fifo_empty和fifo_full 信号会非常不准器。
查找资料和仿真后发现,数字电路的世界真的很神奇,还有很多的东西需要去学习。非常巧妙,FIFO中的一个潜在的条件是write_ptr总是大于或者等于read_ptr;分为两种情况,写快读慢和写慢读快。
1.在写时钟大于读时钟时,产生fifo_empty信号,需要将write_ptr同步到读时钟域,写指针会有延时,可能比实际的写地址要小,如果不满足fifo_empty的产生条件,没问题。如果满足fifo_empty的触发条件,说明此时同步后的write_ptr == read_ptr,即实际的write_ptr >= read_ptr,最坏的情况就是write_ptr > read_ptr,像这种FIFO非空而产生空标志信号的情况称为“虚空”,但是也并不影响FIFO的功能。
2.在写时钟大于读时钟时,产生fifo_full信号,需要将read_ptr同步到写时钟域,读指针会有延时,可能比实际的读地址要小,如果不满足fifo_full的产生条件,没问题。如果满足fifo_full的触发条件,说明此时同步后的read_ptr == write_ptr – fifo_depth,即实际的read_ptr >= read_ptr – fifo_depth,最坏的情况就是read_ptr > read_ptr – fifo_depth,像这种FIFO非满而产生满标志信号的情况称为“虚满”,但是也并不影响FIFO的功能。
写慢读快的情况也同上,并没有大的差异,不再分析。

关于格雷码减小亚稳态,如果读写时钟差距过大,从快时钟域同步到慢时钟域的信号,时钟捕获的相邻两个数据变化并不是只有一个bit位的改变,可能导致格雷码失去原来的意义,嗯,目前的理解是这样。

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

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170177.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • fedora14安装教程_Linux下zip安装使用方法

    fedora14安装教程_Linux下zip安装使用方法LinuxFedora12下,安装SAMBA ,使用以下命令:yuminstallsambayuminstallsamba

  • 机器学习之支持向量回归(SVR)

    机器学习之支持向量回归(SVR)简介支持向量机(SupportVectorMachine)是由Vapnik等人于1995年提出来的,之后随着统计理论的发展,支持向量机SVM也逐渐受到了各领域研究者的关注,在很短的时间就得到了很广泛的应用。支持向量机是被公认的比较优秀的分类模型。同时,在支持向量机的发展过程中,其理论方面的研究得到了同步的发展,为支持向量机的研究提供了强有力的理论支撑。本实训项目主要围绕支持向量机的原理和技术进行介绍,并基于实际案例进行实战实训。线性支持向量机#encoding=utf8fromsk

  • 用拉普拉斯变换求零状态响应_什么是UPS?为什么用UPS?关于UPS电源的知识都在这里!…[通俗易懂]

    用拉普拉斯变换求零状态响应_什么是UPS?为什么用UPS?关于UPS电源的知识都在这里!…[通俗易懂]来源:电气设计圈如有侵权,请联系删除UPS种类、功能、原理1什么是UPSUPS-UninterruptedPowerSystem;利用电池化学能作为后备能量,在市电断电等电网故障时,不间断地为用户设备提供(交流)电能的一种能量转换装置。2为什么用UPSUPS的四大功能:1不停电功能,解决电网停电问题;2交流稳压功能,解决网压剧烈波动问题;3净化功能,解决电网与电源污染问题;4管理功能…

  • mysql优化 面试_数据库优化工具

    mysql优化 面试_数据库优化工具点赞是一种积极的生活态度!有支持才有动力!微信搜索公众号【达摩克利斯之笔】获取更多资源,文末有二维码!前言数据库优化是一个老生常谈的问题,刚入门的小白或者工作N年的光头对这个问题应该都不陌生,你要面试一个中高级工程师那么他就想”哥俩好”一样那么粘,面试官肯定会问这个问题,这篇文章我们就和它哥俩好!而且这个问题就是一个送分题,数据库的优化方案基本就是那些,答案也都是固定的,大家只要好好…

    2022年10月24日
  • 流水线设计思想_全自动流水线

    流水线设计思想_全自动流水线在硬件电路设计中,流水线设计思想是一种很重要的设计思想,这种思想是一种用面积换速度的思想,用更多的资源来实现高速。(面积就是需要的硬件数量,如触发器的数量)顾名思义,流水线思想,就像工厂中的流水线一样。假设是一个手机组装的流水线,一个三个步骤:A,将电池装入手机起来;B,将屏幕组装起来;C,将外壳组装起来。在上面的三个步骤中,流水线的实现就是:A步骤实现后,将手机发往B,然后A继续组装电池,而不会等待C完成再组装;B和C也是一样。流水线思想就是自己完成自己的功能,不会等待。这在硬件电路中就是一种并行的

  • 少儿编程网站源码和scratch课程

    随着智能时代的到来,随着国家双减政策的实施,少儿编程是很多教育从业者的首选。因为不仅国家重视,而且对于承认来说容易上手。但目前市面上的少儿编程,投入都极高,且都是租用有限的账号和功能,并不是拥有真正属于自己的系统。学生端所以一定要有源码,这样才能拥有真正属于自己的少儿编程教学平台和管理平台系统。(源码即和新文件,拥有源码就可以不受任何限制。)另外,系统要大量的投入教学使用,这样才能保证其成熟使用,最后还要有专业的技术团队去维护。学生端+课程视频教师端:可以一键推送学生作业给家长..

发表回复

您的电子邮箱地址不会被公开。

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