异步fifo

一. 异步fifo电路设计思想

在这里插入图片描述

  1. 异步fifo的设计主要分为双端口ram写时钟域的地址管理读时钟域的地址管理读时钟域读地址到写时钟域的格雷码同步写时钟域写地址到读时钟域的格雷码同步这5大部分。
  2. 读写指针跨时钟域问题
    (1)需要跨时钟域的原因:我们需要通过比较读写指针来判断信号的空满状态,而读写指针不在同一个时钟域,是不能直接比较的,因为读写指针是一个信号,信号的值是相对于时域来说的,所以需要将读写指针的值同步到同一时钟域来比较。
    (2)先说最终方案:
    “写满”的判断:需要将读指针同步到写时钟域,再与写指针判断
    “读空“的判断:需要将写指针同步到读时钟域,再与读指针判断

分析:
1)全部同步到写时钟域:
①写满的判断:就是写指针超过了同步后的读指针一圈,因为读指针在同步到写时钟域的过程消耗了时间,所以同步后的读指针其实是小于等于真实的读指针的,所以当”写满“时,并没有真正写满,因为它实际可能多读了几个,这个过程是保守的,是正确。
②读空的判断:也就是同步后的读指针追上了写指针,因为在同步的过程中消耗了时间,所以同步后的读指针小于等于真实的读指针,所以当”读空“时,其实读空已经发生了,这是不正确的功能。

2)全部同步到读时钟域:
①写满的判断:同步后的写指针超过了读指针一圈,因为同步的过程消耗了时间,所以同步后写指针小于等于真实的写指针,当”写满“时,其实写满已经发生了,会覆盖掉有用的值,这是不正确的。
②读空的判断:读指针追上了同步后的写指针,当”读空“时,其实读指针还没有追上真实的写指针,系统没有发生读空,这也是一个保守的过程,是正确的。
所以根据分析,可以得出上面的结论:”写满“的判断需要将读指针同步到写时钟域;”读空“的判断需要将写指针同步到读时钟域。

  1. 二进制与格雷码
    我们知道如果跨时钟域没有处理好,会引起亚稳态的问题,造成指针值得异常,引发fifo的功能错误。那么应该如何将读写指针同步到对方的时钟域呢?
    答案:将二进制的指针转化成格雷码后再进行同步。
    1)原因:
    格雷码是一种非权重码,每次变换位数只有一位,这样能够有效避免在跨时钟域情况下发生亚稳态的概率。因为二进制码虽然在数值上相邻,但是有时需要变化4位bit数,比如0111变换到1000的过程,那么采样的值是任意的四位二进制数,如果在空满成立时没有触发,就会导致数据被覆盖掉或者重复读出。而使用格雷码,每次只改变一位信号 (消除亚稳态就可以使用单bit信号跨时钟域用到的电平同步器,即打两拍),比如格雷码从0001到0011时,即便没有采样到变化后的0011,但是也会采样到0001,这只会导致”不该报空满而报了空满“,功能还是正确的。
    2)如何用格雷码判断空满
    · 当最高位和次高位相同,其余位相同认为读空;
    · 当最高位和此高位不同,其余位相同认为写满。
    3)快时钟域同步到慢时钟域会发生漏采的情况,需要解决吗?
    不需要解决。
    ①读慢写快:
    ”写满“判断:将读指针同步到写时钟域,会发生重复采样,不影响。
    ”读空“判断:快时钟域同步到慢时钟域,会发生漏采,比如写指针从0写到10,但读时钟域只捕捉到了3、5、8这三个指针,当同步到8指针时,其真实的指针可能时10,当其认为读空其实没有读空。
    ②读快写慢:
    ”写满“判断:漏采,读指针从0读到10,但写时钟域只捕捉到了3、5、8这三个指针,当同步到8指针时,其真实的读指针可能是10,就是在写时钟域没有察觉的情况下,读时钟域可能读了数据,这对fifo功能没有影响。
    ”读空“判断:重复采样,不影响。

二. verilog代码思路

  1. 设计fifo的深度为16,16个地址需要4位二进制数表示,同时扩展一位作为指示位,所以指针的位宽需要5位,这里我直接设置fifo的宽度为5(可以改)。
  2. 读写指针二进制码的递增(在什么时候进行写操作和读操作)
  3. 分别将读写指针从二进制码转化成格雷码
  4. 将格雷码传递给指针
  5. 将指针信号通过打两拍进行同步化
  6. 在写时钟域判断”写满“
  7. 在读时钟域判断”读空“
  8. ram和读写时钟域的连接以及ram的实例化

module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 8
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	        wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	                rdata
);
 

 parameter ADDR_WIDTH = $clog2(DEPTH); //地址宽度
 
//读写指针的二进制码
  reg [ADDR_WIDTH:0]  waddr_bin;  
  reg [ADDR_WIDTH:0]  raddr_bin; 
  always@(posedge wclk or negedge wrstn) begin
     if(!wrstn) begin
      waddr_bin='d0;
     end
     else if(winc && !wfull) begin 
       waddr_bin<=waddr_bin+1'b1;
     end
   end
   always@(posedge rclk or negedge rrstn) begin
     if(!rrstn) begin
      raddr_bin='d0;
     end
     else if(rinc && !wfull) begin 
       raddr_bin<=raddr_bin+1'b1;
     end
   end
//二进制码转格雷码
  wire [ADDR_WIDTH:0]  waddr_gray;  
  wire [ADDR_WIDTH:0]  raddr_gray; 
  assign waddr_gray = waddr_bin^(waddr_bin>>1);
  assign raddr_gray = raddr_bin^(raddr_bin>>1);
//将格雷码传递到读写指针
  reg [ADDR_WIDTH:0]  wptr;  
  reg [ADDR_WIDTH:0]  rptr; 
  always@(posedge wclk or negedge wrstn) begin
     if(!wrstn)  
       wptr<='d0;
     else
       wptr<=waddr_gray;
     end
  always@(posedge rclk or negedge rrstn) begin
     if(!rrstn) 
       rptr<='d0;
     else
       rptr<=raddr_gray;
     end
//打两拍,实现读写指针的同步化
  reg [ADDR_WIDTH:0]  wptr_buff;  //通过一个寄存器后的值
  reg [ADDR_WIDTH:0]  rptr_buff;       
  reg [ADDR_WIDTH:0]  wptr_syn;  //通过二个寄存器后的值
  reg [ADDR_WIDTH:0]  rptr_syn;  
  always@(posedge wclk or negedge wrstn) begin
     if(!wrstn)begin  
       rptr_buff<='d0;
       rptr_syn <='d0;
     end
     else begin
       rptr_buff<=rptr;
       rptr_syn <=rptr_buff;
     end
   end
  always@(posedge rclk or negedge rrstn) begin
     if(!rrstn) begin
       wptr_buff<='d0;
       wptr_syn <='d0;
     end
     else begin
       wptr_buff<=wptr;
       wptr_syn <=wptr_buff;
     end
    end
//空满判断
assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});
assign rempty= (rptr == wptr_syn);

//ram
  wire wen;
  wire ren;
  wire [ADDR_WIDTH-1:0]  waddr;
  wire [ADDR_WIDTH-1:0]  raddr;

assign wen = winc & !wfull;
assign ren = rinc & !rempty;
assign waddr = waddr_bin[ADDR_WIDTH-1:0];
assign raddr = raddr_bin[ADDR_WIDTH-1:0];
//实例化ram模块

 ram_2 #(.DEPTH(DEPTH), .WIDTH(WIDTH))
       ram_fifo ( .wclk(wclk),
                  .wenc(wen),
                  .waddr(waddr), 
                  .wdata(wdata),    
                  .rclk(rclk),
                  .renc(ren),
                  .raddr(raddr),  
                  .rdata (rdata)
                 );

    
endmodule
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值