FPGA实现异步FIFO

一、设计目的

设计一个读写时钟不同的异步FIFO。

二、设计思路

FIFO设计的重难点是如何产生空满标志,在之前的同步FIFO设计中采用的是将地址扩展一位,通过比较最高位以及其他低位来得到空满标志,当最高位相反低位相同时说明写指针追了读指针一圈,FIFO写满;当所有位相同时,已将FIFO内所有数据读出,此时为空。

而异步FIFO也将采取这种思路,但是由于涉及到写指针在写时钟域下产生,读指针在读时钟域产生,比较时我们需要将两者放在一个时候域下进行比较,那此时就分为如下两种情况:

1.在读时钟域下读写指针比较,产生empty标志;

2.在写时钟域下读写指针比较,产生empty标志;

这么做的原因在于,将写指针同步到读时钟域后,此时和读指针比较其实已经不是现在实时的写指针了,意味着此时同步过来的写指针可能是5,而实际的已经变为了7,但是进行比较之后如果读指针也恰为5则产生empty,相当于更加“保险”,避免了读“空”的情况。将读指针同步到写时钟域下,读指针会滞后实际值几个周期,但这样避免了写满之后再写的情况。

考虑到多bit数据(即异步FIFO中的读写地址)跨时钟域时出现亚稳态概率高,再由于读写地址每次加1为连续变化,因此在将读写地址同步到另一个时钟域时先对其转换变为格雷码,因为格雷码每次只变化一位,可以有效改善亚稳态情况。

所以设计过程一共分为如下几个部分:

①读写地址产生;

②读写地址二进制转格雷码;

③读写地址同步到对方时钟域;

④空满标志判断;

三、接口信号定义
信号方向说明
w_ckin写时钟
r_clkin读时钟
rst_nin复位信号
wr_enin写使能
rd_enin读使能
w_data[7:0]out8bit写数据
r_data[7:0]out8bit读数据
fullout满信号
emptyout空信号
四、设计代码
module asy_fifo
  #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
  )
  (
  input w_clk,
  input r_clk,
  input rst_n,
  input wr_en,
  input rd_en,
  input [WIDTH-1:0] w_data,
  output reg [WIDTH-1:0] r_data,
  output full,
  output empty
  );

  localparam  addr_width = $clog2(DEPTH);
  
  reg w_rst_n;
  reg r_rst_n;
  reg r_ff1;
  reg w_ff1;
  reg [WIDTH-1:0] fifo_data[DEPTH-1:0];
  wire [addr_width-1:0] waddr;
  reg [addr_width:0] waddr_bin;
  wire [addr_width:0] waddr_gray;
  reg [addr_width:0] waddr_gray_f1;
  reg [addr_width:0] waddr_gray_f2;
  //reg [addr_width:0] waddr_bin_2;

  //复位信号处理
  always @(posedge w_clk or negedge rst_n) begin
    if(~rst_n) begin
      {w_rst_n,w_ff1} <= 2'b0;
    end else begin
      {w_rst_n,w_ff1} <= {w_ff1,1'b1};
    end
  end
  
  always @(posedge w_clk or negedge rst_n) begin
    if(~rst_n) begin
      {r_rst_n,r_ff1} <= 2'b0;
    end else begin
      {r_rst_n,r_ff1} <= {r_ff1,1'b1};
    end
  end
  
  assign waddr = waddr_bin[addr_width-1:0];
  //写地址产生
  always @(posedge w_clk or negedge w_rst_n) begin
    if(~w_rst_n) begin
      waddr_bin <= 0;
    end else if(wr_en & ~full)begin
      waddr_bin <= waddr_bin + 1'b1;
    end
  end
  //写地址转换成格雷码
  assign waddr_gray = waddr_bin ^ (waddr_bin>>1);
//同步处理
  always @(posedge r_clk or negedge r_rst_n) begin 
    if(~r_rst_n) begin
      waddr_gray_f1 <= 0;
      waddr_gray_f1 <= 0;
    end else begin
      waddr_gray_f1 <= waddr_gray;
      waddr_gray_f2 <= waddr_gray_f1;
    end
  end
  
  wire [addr_width-1:0] raddr;
  reg [addr_width:0] raddr_bin;
  wire [addr_width:0] raddr_gray;
  reg [addr_width:0] raddr_gray_f1;
  reg [addr_width:0] raddr_gray_f2;
  //reg [addr_width-1:0] raddr_bin_2;
  
  assign raddr = raddr_bin[addr_width-1:0];
  //读地址产生
  always @(posedge r_clk or negedge r_rst_n) begin
    if(~r_rst_n) begin
      raddr_bin <= 0;
    end else if(rd_en & ~empty)begin
      raddr_bin <= raddr_bin + 1'b1;
    end
  end
  //读地址转换成格雷码
  assign raddr_gray = raddr_bin ^ (raddr_bin>>1);
  //同步处理
  always @(posedge w_clk or negedge w_rst_n) begin 
    if(~w_rst_n) begin
      raddr_gray_f1 <= 0;
      raddr_gray_f2 <= 0;
    end else begin
      raddr_gray_f1 <= raddr_gray;
      raddr_gray_f2 <= raddr_gray_f1;
    end
  end

  //产生空满标志
  assign empty = (waddr_gray_f2 == raddr_gray)? 1'b1:1'b0;
  assign full = (raddr_gray_f2[addr_width:addr_width-1] == ~waddr_gray[addr_width:addr_width-1] && raddr_gray_f2[addr_width-2:0]==waddr_gray[addr_width-2:0] )? 1'b1:1'b0;
  
  //写数据
  integer i;
  always @(posedge w_clk or negedge w_rst_n) begin 
    if(~w_rst_n) begin
	    for(i=0;i<DEPTH;i=i+1)
	      fifo_data[i] <= 0;
	end 
    else if(wr_en & (~full)) begin
      fifo_data[waddr] <= w_data;
    end
  end
  
  //读数据
  always @(posedge r_clk or negedge r_rst_n) begin 
    if(~r_rst_n) begin
      r_data <= 0;
    end else if(rd_en & (~empty)) begin
      r_data <= fifo_data[raddr];
    end
  end
endmodule 
五、testbench文件
module tb_ast_fifo();
  reg w_clk;
  reg r_clk;
  reg rst_n;
  reg wr_en;
  reg rd_en;
  reg [7:0] w_data;
  wire   [7:0] r_data;
  wire  full;
  wire  empty;

  always#15 w_clk = ~w_clk;   
  always#10 r_clk = ~r_clk;
  always#30 w_data = $random; 
  initial begin
    rst_n = 0;
    w_clk = 0;
    r_clk = 0;
    #10 rst_n =1;
    #40 wr_en = 1;
      rd_en = 0;
      #500 rd_en = 1;
      #2000 $stop;
  end
  
    asy_fifo #(
        .WIDTH(8),
        .DEPTH(16)
      ) inst_asy_fifo (
        .w_clk  (w_clk),
        .r_clk  (r_clk),
        .rst_n  (rst_n),
        .wr_en  (wr_en),
        .rd_en  (rd_en),
        .w_data (w_data),
        .r_data (r_data),
        .full   (full),
        .empty  (empty)
      );

endmodule
六、仿真结果


在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值