异步fifo设计

异步FIFO:顾名思义就是读写时钟属于不同时钟域,为了解决读写两端时序一不一致而广泛应用。异步fifo相对于同步FIFO的难点就是处理fifo的读写指针,以及相对应的空满标志。

1、原理

1.1、异步fifo的结构图如下:

        由图可知,异步FIFO主要划分两个时钟域,

        写时钟域:

        mem_wr_en=wr_en & ~fifo_full,表示只要MEM非满,即可往MEM写数据;而fifo_full=(wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ,表示读写指针之间的位移等于FIFO的最大深度,至于gray码高2bit相反原因下面详解。

读时钟域:

        mem_rd_en=rd_en & ~fifo_empty,表示只要MEM非空,即可往MEM读数据;而fifo_empty=( rd_addr_gray == wr_addr_gray_d2 ),表示读指针追上写指针,即MEM内没有数据。

1.2、满标志判断,gray码高2bit相反分析

        由上图可知,当写指针填满整个FIFO,rd_addr=0011, rd_gray=0010, wr_addr=1011, wr_gray=1110, 故高2bit相反。

2、verilog 代码

module fifo_async#(
  parameter   data_width = 16,
  parameter   data_depth = 256,
  parameter   addr_width = 8
) (  
  input  wire                      	wr_clk, //write signal of write clock domain
  input  wire 				  		wr_rst_n,
  input  wire                     	wr_en,
  input  wire    [data_width-1:0] 	din,  
  
  input  wire                     	rd_clk, //read signal of read clock domain
  input  wire 				  		rd_rst_n,
  input  wire                     	rd_en,
  output reg [data_width-1:0] 		dout,
  
  output wire                     	empty, //fifo empty
  output wire                     	full   //fifo 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]; //store data

//--------------------------write side----------------------------
//write data to memory
always@(posedge wr_clk or negedge wr_rst_n) begin
  if(~wr_rst_n)
	fifo_ram[wr_addr] <= 'h0;  //fifo复位后输出总线上是0,并非ram中真的复位。可无
  else if(wr_en && (~full))
    fifo_ram[wr_addr] <= din;
end    
//count write pointer
always@(posedge wr_clk or negedge wr_rst_n) begin
  if(~wr_rst_n)
	wr_addr_ptr <= 'h0;
  else if(wr_en && (~full))
    wr_addr_ptr <= wr_addr_ptr + 1;
end
assign wr_addr = wr_addr_ptr[addr_width-1-:addr_width];
//gray code process
assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
always@(posedge wr_clk ) begin : sync_to_wr_domain
  rd_addr_gray_d1 <= rd_addr_gray;
  rd_addr_gray_d2 <= rd_addr_gray_d1;
end
assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同

//--------------------------write side----------------------------
//read data from memory
always@(posedge rd_clk or negedge rd_rst_n) begin
  if(~rd_rst_n)
    dout <= 'h0;
  else if(rd_en && (~empty))
    dout <= fifo_ram[rd_addr];
end
//count read pointer
always@(posedge rd_clk or negedge rd_rst_n) begin
  if(~rd_rst_n)
    rd_addr_ptr <= 'h0;
  else if(rd_en && (~empty))
    rd_addr_ptr <= rd_addr_ptr + 1;
end
assign rd_addr = rd_addr_ptr[addr_width-1-:addr_width];
//gray code process
assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;
always@(posedge rd_clk) begin : sync_to_rd_domain
  wr_addr_gray_d1 <= wr_addr_gray;
  wr_addr_gray_d2 <= wr_addr_gray_d1;
end
assign empty = ( rd_addr_gray == wr_addr_gray_d2 );

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值