异步fifo的Verilog实现

 一、分析

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

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

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

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

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

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

换一种描述方法:

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

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

这里直接给出结论:

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

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

二、Verilog实现

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

时间: 2024-10-12 04:49:34

异步fifo的Verilog实现的相关文章

异步FIFO的编程

对于异步FIFO.最基本的两个方面是地址控制和空.满标志位的产生.首先地址控制分别为读地址和写地址,每次读写时能读写地址应该加1.计数次数为ram深度的2倍.当读写地址相等时则空标志位有效,当读写地址最高位互补其余位相等时则满标志位有效. 存储部分採用双口RAM实现. 以下是详细的Verilog代码: module afifo(r_clk,w_clk,rst_n,data_in,data_out,we,re,empty,full); input r_clk,w_clk,rst_n,re,we;

异步FIFO问题 二

同步FIFO, verilog: module fifo(clock,reset,read,write,fifo_in,fifo_out,fifo_empty,fifo_half,fifo_full); input clock,reset,read,write; input [15:0]fifo_in; output[15:0]fifo_out; output fifo_empty,fifo_half,fifo_full;//标志位 reg [15:0]fifo_out; reg [15:0]r

同步FIFO的verilog描述

1 /****************************************************** 2 A fifo controller verilog description. 3 ******************************************************/ 4 module fifo(datain, rd, wr, rst, clk, dataout, full, empty); 5 input [7:0] datain; 6 input

v3学院 FPGA专家 带你学习FPGA实现格雷码跨时钟域异步fifo

当由慢时钟域到快时钟域,肯定需要一个buffer做缓冲,这样才能完成时钟域的转换.一般这种情况都选择FIFO来做缓冲. 当读写FIFO的时钟不同那么称为异步FIFO,FIFO就是一个"环形存储器"读操作会把读指针指向下一个读数据,写操作会把写指针指向下一个写数据地址.当读指针追上写指针时称作读空,当写地址追上读地址时称作写满. 读空,写满标志的产生 格雷码地址编码产生 异步fifo整体逻辑框图 请扫二维码加入fpga圈 代码实现请持续关注,下次讲解! 本文章原创,来自v3学院 www.

异步FIFO设计的一些注意事项

异步FIFO的结构基本上是是按照下面的思路来设计: 1. 读写的指针分别用gray code sync到另外一个clock domain,sync的时候至少打2拍. 2. 读写指针最高位各加一个bit用来判断空和慢,对于FIFO来说,永远是读指针追写指针,当指针相同的时候,用加的这个最高bit来 判断是谁追上谁,继而判断空和满. 除了上面的设计要点,还有以下几点需要注意: 1. 当两个时候频率差别很大时 快时钟的读/写指针sync到慢时钟时,gray code就不是按照1次变化1个bit的方式进

异步FIFO为什么用格雷码

异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,将写地址同步到读时钟域再和读地址比较进行FIFO空状态判断(同步后的写地址一定是小于或者等于当前的写地址,所以此时判断FIFO为空不一定是真空,这样更保守),将读地址同步到写时钟域再和写地址比较进行FIFO满状态判断(同步后的读地址一定是小于或者等于当前的读地址,所以此时判断FIFO为满不一定是真空,这样更保守),这样可以保证FIFO的特性:FIFO空之后不能继续读取,FIFO满之

异步FIFO最小深度计算

计算FIFO深度是设计FIFO中常遇到的问题.常识告诉我们,当读速率慢于写速率时,FIFO便可被用作系统中的缓冲元件或队列.因此FIFO的大小基本上暗示了所需缓存数据的容量,该容量取决于读写数据的速率.据统计,系统的数据速率取决于系统的负载能力.因此为了保证FIFO的大小,我们需要考虑FIFO传输的最坏情况下.所谓最坏的情况就是使得写速率最大,读速率最小:通常是考虑突发传输. 一.异步FIFO最小深度计算原理 如果数据流连续不断则FIFO深度无论多少,只要读写时钟不同源同频则都会丢数:FIFO用

异步FIFO

一.FIFO简介 FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址. 用途1: 异步FIFO读写分别采用相互异步的不同时钟.在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电

异步fifo的读写

这里不讨论异步fifo是如何实现的,而是在实现fifo的前提下,对fifo的读写. 现在遇到的问题是:总线的数据不能写入fifo中,但是地址能加一. 代码如下: if( !fifo_tx_full && cpu_write_fifo_en ) begin                    fifo_r[ write_addr[ADDR_SIZE - 1'b1 : 0] ] <= cpu_2_fifo;                     write_addr <= wr