IC设计入门——异步FIFO

在异步FIFO中,数据读取和写入操作使用不同的时钟频率。由于写入和读取时钟不同步,因此称为异步FIFO。通常,这些用于数据需要从一个时钟域传递到另一个时钟域的系统中,这通常称为“时钟域交叉”。因此,异步FIFO有助于在两个工作于不同时钟的系统之间同步数据流。

异步 FIFO 使用

异步FIFO框图

异步FIFO

信号:

wr_en:写使能

wr_data:写入数据

full:FIFO 已满

empty:FIFO 为空

rd_en:读取启用

rd_data:读取数据

b_wptr:二进制写入指针

g_wptr:格雷码写入指针

b_wptr_next:二进制写入指针 下一页

g_wptr_next: 下一个格雷码写指针

b_rptr:二进制读取指针

g_rptr:格雷码读取指针

b_rptr_next: 下一个二进制读取指针

g_rptr_next:下一个格雷码读取指针

b_rptr_sync:二进制读取指针同步

b_wptr_sync:二进制写入指针同步

异步FIFO操作

在同步FIFO的情况下,写入和读取指针在同一时钟上生成。但是,在异步FIFO的情况下,写入指针与写入时钟域对齐,而读取指针与读取时钟域对齐。因此,它需要域交叉来计算FIFO的满条件和空条件。这会导致实际设计中的亚稳态。为了解决这种亚稳态问题,可以使用2个触发器或3个触发器同步器来传递写入和读取指针。为了解释,我们将使用 2 个触发器同步器。请注意,单个“两拍同步器”只能解析一位的亚稳态。因此,根据写入和读取指针的不同,需要多个 两拍 同步器。

module synchronizer 
#(parameter WIDTH=3)
(input clk, 
       rst_n, 
       [WIDTH:0] d_in, 
output reg [WIDTH:0] d_out
);
  reg [WIDTH:0] q1;
  always@(posedge clk) begin
    if(!rst_n) begin
      q1 <= 0;
      d_out <= 0;
    end
    else begin
      q1 <= d_in;
      d_out <= q1;
    end
  end
endmodule

在异步FIFO中使用二进制到格雷码转换器,反之亦然

到目前为止,我们讨论了如何在各自的时钟域中获取异步写入和读取指针。但是,我们不应该传递二进制格式的写入和读取指针值。由于亚稳态,整体写入或读取指针值可能不同。

示例:当写入时钟域的二进制值 wr_ptr = 4'b1101 通过 2FF 同步器传输时,在读取时钟域wr_ptr值可能接收为 4'b1111 或任何其他不可接受的值。而灰色代码可以保证与以前的值相比只有一位变化。因此,写入和读取指针都需要首先转换为相应域中的等效格雷码,然后将它们传递到相反的域。要检查另一个域中的FIFO满条件和空条件,我们有两种方法。

方式 1

将收到的格雷码格式指针转换为二进制格式,然后检查完整和空条件。

FIFO 满载条件
g2b_converter g2b_wr(g_rptr_sync, b_rptr_sync);
wrap_around = b_rptr_sync[PTR_WIDTH] ^ b_wptr[PTR_WIDTH];
wfull = wrap_around & (b_wptr[PTR_WIDTH-1:0] == b_rptr_sync[PTR_WIDTH-1:0]);
FIFO 空条件
g2b_converter g2b_rd(g_wptr_sync, b_wptr_sync);
rempty = (b_wptr_sync == b_rptr_next);

方式 2

在接收到的灰色编码写和读指针的帮助下,直接检查满和空条件。这很有效,因为它不需要额外的硬件来将格雷编码的写入和读取指针转换为等效的二进制形式。

FIFO 满载条件
wfull = (g_wptr_next == {~g_rptr_sync[PTR_WIDTH:PTR_WIDTH-1], g_rptr_sync[PTR_WIDTH-2:0]});

FIFO 空条件
rempty = (g_wptr_sync == g_rptr_next);

异步FIFO Verilog码

写入指针处理程序

同步器g_rptr_sync的输出作为“写入指针处理程序”模块的输入,用于生成FIFO满条件。如果二进制写入指针 (b_wptr) 满足 (w_en & !full) 条件,则该指针递增。该b_wptr值被馈送到fifo_mem模块,以将数据写入FIFO。

module wptr_handler #(parameter PTR_WIDTH=3) (
  input wclk, wrst_n, w_en,
  input [PTR_WIDTH:0] g_rptr_sync,
  output reg [PTR_WIDTH:0] b_wptr, g_wptr,
  output reg full
);

  reg [PTR_WIDTH:0] b_wptr_next;
  reg [PTR_WIDTH:0] g_wptr_next;
   
  reg wrap_around;
  wire wfull;
  
  assign b_wptr_next = b_wptr+(w_en & !full);
  assign g_wptr_next = (b_wptr_next >>1)^b_wptr_next;
  
  always@(posedge wclk or negedge wrst_n) begin
    if(!wrst_n) begin
      b_wptr <= 0; // set default value
      g_wptr <= 0;
    end
    else begin
      b_wptr <= b_wptr_next; // incr binary write pointer
      g_wptr <= g_wptr_next; // incr gray write pointer
    end
  end
  
  always@(posedge wclk or negedge wrst_n) begin
    if(!wrst_n) full <= 0;
    else        full <= wfull;
  end

  assign wfull = (g_wptr_next == {~g_rptr_sync[PTR_WIDTH:PTR_WIDTH-1], g_rptr_sync[PTR_WIDTH-2:0]});

endmodule

读取指针处理程序

同步器g_wptr_sync的输出作为“读取指针处理程序”模块的输入,以生成FIFO空条件。如果二进制读取指针 (b_rptr) 满足 (r_en & !empty) 条件,则该指针递增。该b_rptr值被馈送到fifo_mem模块,以从FIFO读取数据。

module rptr_handler #(parameter PTR_WIDTH=3) (
  input rclk, rrst_n, r_en,
  input [PTR_WIDTH:0] g_wptr_sync,
  output reg [PTR_WIDTH:0] b_rptr, g_rptr,
  output reg empty
);

  reg [PTR_WIDTH:0] b_rptr_next;
  reg [PTR_WIDTH:0] g_rptr_next;

  assign b_rptr_next = b_rptr+(r_en & !empty);
  assign g_rptr_next = (b_rptr_next >>1)^b_rptr_next;
  assign rempty = (g_wptr_sync == g_rptr_next);
  
  always@(posedge rclk or negedge rrst_n) begin
    if(!rrst_n) begin
      b_rptr <= 0;
      g_rptr <= 0;
    end
    else begin
      b_rptr <= b_rptr_next;
      g_rptr <= g_rptr_next;
    end
  end
  
  always@(posedge rclk or negedge rrst_n) begin
    if(!rrst_n) empty <= 1;
    else        empty <= rempty;
  end
endmodule

FIFO存储器

基于二进制编码的写入和读取指针,数据分别写入FIFO或从FIFO读取。

module fifo_mem #(parameter DEPTH=8, DATA_WIDTH=8, PTR_WIDTH=3) (
  input wclk, w_en, rclk, r_en,
  input [PTR_WIDTH:0] b_wptr, b_rptr,
  input [DATA_WIDTH-1:0] data_in,
  input full, empty,
  output reg [DATA_WIDTH-1:0] data_out
);
  reg [DATA_WIDTH-1:0] fifo[0:DEPTH-1];
  
  always@(posedge wclk) begin
    if(w_en & !full) begin
      fifo[b_wptr[PTR_WIDTH-1:0]] <= data_in;
    end
  end
  /*
  always@(posedge rclk) begin
    if(r_en & !empty) begin
      data_out <= fifo[b_rptr[PTR_WIDTH-1:0]];
    end
  end
  */
  assign data_out = fifo[b_rptr[PTR_WIDTH-1:0]];
endmodule

顶部模块

`include "synchronizer.v"
`include "wptr_handler.v"
`include "rptr_handler.v"
`include "fifo_mem.v"

module asynchronous_fifo #(parameter DEPTH=8, DATA_WIDTH=8) (
  input wclk, wrst_n,
  input rclk, rrst_n,
  input w_en, r_en,
  input [DATA_WIDTH-1:0] data_in,
  output reg [DATA_WIDTH-1:0] data_out,
  output reg full, empty
);
  
  parameter PTR_WIDTH = $clog2(DEPTH);
 
  reg [PTR_WIDTH:0] g_wptr_sync, g_rptr_sync;
  reg [PTR_WIDTH:0] b_wptr, b_rptr;
  reg [PTR_WIDTH:0] g_wptr, g_rptr;

  wire [PTR_WIDTH-1:0] waddr, raddr;

  synchronizer #(PTR_WIDTH) sync_wptr (rclk, rrst_n, g_wptr, g_wptr_sync); //write pointer to read clock domain
  synchronizer #(PTR_WIDTH) sync_rptr (wclk, wrst_n, g_rptr, g_rptr_sync); //read pointer to write clock domain 
  
  wptr_handler #(PTR_WIDTH) wptr_h(wclk, wrst_n, w_en,g_rptr_sync,b_wptr,g_wptr,full);
  rptr_handler #(PTR_WIDTH) rptr_h(rclk, rrst_n, r_en,g_wptr_sync,b_rptr,g_rptr,empty);
  fifo_mem fifom(wclk, w_en, rclk, r_en,b_wptr, b_rptr, data_in,full,empty, data_out);

endmodule

 

testbench代码

module async_fifo_TB;

  parameter DATA_WIDTH = 8;

  wire [DATA_WIDTH-1:0] data_out;
  wire full;
  wire empty;
  reg [DATA_WIDTH-1:0] data_in;
  reg w_en, wclk, wrst_n;
  reg r_en, rclk, rrst_n;

  // Queue to push data_in
  reg [DATA_WIDTH-1:0] wdata_q[$], wdata;

  asynchronous_fifo as_fifo (wclk, wrst_n,rclk, rrst_n,w_en,r_en,data_in,data_out,full,empty);

  always #10ns wclk = ~wclk;
  always #35ns rclk = ~rclk;
  
  initial begin
    wclk = 1'b0; wrst_n = 1'b0;
    w_en = 1'b0;
    data_in = 0;
    
    repeat(10) @(posedge wclk);
    wrst_n = 1'b1;

    repeat(2) begin
      for (int i=0; i<30; i++) begin
        @(posedge wclk iff !full);
        w_en = (i%2 == 0)? 1'b1 : 1'b0;
        if (w_en) begin
          data_in = $urandom;
          wdata_q.push_back(data_in);
        end
      end
      #50;
    end
  end

  initial begin
    rclk = 1'b0; rrst_n = 1'b0;
    r_en = 1'b0;

    repeat(20) @(posedge rclk);
    rrst_n = 1'b1;

    repeat(2) begin
      for (int i=0; i<30; i++) begin
        @(posedge rclk iff !empty);
        r_en = (i%2 == 0)? 1'b1 : 1'b0;
        if (r_en) begin
          wdata = wdata_q.pop_front();
          if(data_out !== wdata) $error("Time = %0t: Comparison Failed: expected wr_data = %h, rd_data = %h", $time, wdata, data_out);
          else $display("Time = %0t: Comparison Passed: wr_data = %h and rd_data = %h",$time, wdata, data_out);
        end
      end
      #50;
    end

    $finish;
  end
  
  initial begin 
    $dumpfile("dump.vcd"); $dumpvars;
  end
endmodule

波形

  • 38
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮Adam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值