打印

异步FIFO的实现

[复制链接]
2129|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
星星之火红|  楼主 | 2012-11-6 22:40 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
以下是一个异步FIFO的实现代码,实现的是一个FWFT的异步FIFO功能,与Xilinx CORE Generator生成FIFO功能、时序一模一样。其中 ,simple_dual_port_ram 是一个Xilinx的简单双端口RAM,可以很简单的移植到其他厂商的FPGA中。在产生空满信号的判断时,需要将读操作侧的地址传递到写操作侧,将写操作地址传递读操作侧,为了减少读写地址的位翻转率,从而减少亚稳态的发生,在将地址跨越时钟域之前首先转换成格雷码,跨越时钟域并正常采样以后再恢复成二进制的地址。由于模块中多次使用到将格雷码转换成二进制码以及将二进制码转换成格雷码,所以将格雷码与二进制码的转换使用了两个函数(function)来实现,方便在代码中不同处调用。跨越时钟域时,为了隔断亚稳态的传递,首先要用两级寄存器对另一时钟域产生的地址打两拍,这样子即使有亚稳态发生,也能被阻隔在两级寄存器中。尽管打两拍能阻断亚稳态的传递,但是不能避免跨时钟域时采样错误的发生,使用格雷码的作用是使得错误仅发生在其中一个位上。
       代码中RAM输入和输出都没有使用到寄存器,读者可以根据自己的需要定制满足自己要求的FIFO,在写操作侧或读操作侧加寄存器。代码中数据的位宽和地址的位宽,亦可以根据读者的需要修改。尽管本文提供了异步FIFO的完整实现代码,博主还是建议各位读者在使用FIFO时尽量多用CORE Generator生成的FIFO,那样会更加可靠稳定,使用起来也更方便。

相关帖子

沙发
星星之火红|  楼主 | 2012-11-6 22:40 | 只看该作者
// ****************************************************************************************************************
// Copyright(c) 2012,  Technology Endless - Creative Boundless , All right reserved
// Filename        :    asynfifo.v
// Author            :    lucien
// Email             :    intellectuallib@126.com
// Date              :    Apr 22th, 2012
// Version          :    1.0
// Company       :   
// Description     :    asynchronous FIFO(first in first out)
// Modification History
// Date            By            Revision        Change Description
// ---------------------------------------------------------------------------------------
// 2012/04/22      Lucien        1.0             Original
// ****************************************************************************************************************

`timescale 1ns/1ps

module asynfifo(
          rst                 ,
          wr_clk           ,
          wr_en            ,
          din                ,
          full                ,

          rd_clk           ,
          rd_en            ,
          dout             ,
          empty
         );

input   wire            rst      ;//reset signal
input   wire            wr_clk ;//clock of read clock field
input   wire            wr_en  ;//write enable
input   wire [7:0]     din      ;
output  wire            full     ;

input   wire            rd_clk  ;//clock of write clock field
input   wire            rd_en   ;
output  wire [7:0]   dout     ;
output  wire           empty  ;

/**************************************************************************************************************/
/**************************************************************************************************************\
*        define internal signals                                                                                                  *
\**************************************************************************************************************/

reg     [7:0]   waddr;                     //write address in write clock field
reg     [7:0]   raddr;                      //read  address in read  clock field
wire    [7:0]   waddr_gray_wclk;    //the gray code of write address in write clock field
wire    [7:0]   raddr_gray_rclk;       //the gray code of read address in read clock field
reg     [7:0]   waddr_gray_rclk_0;  //the dealyed gray codes of write address in read clock field
reg     [7:0]   waddr_gray_rclk_1;
reg     [7:0]   raddr_gray_wclk_0;  //the dealyed gray codes of read address in write clock field
reg     [7:0]   raddr_gray_wclk_1;
wire    [7:0]   waddr_rclk;             //write address in read clock field
wire    [7:0]   raddr_wclk;             //read address in write clock field
reg     [8:0]   count_wclk;            //the data counter in write clock field
reg     [8:0]   count_rclk;             //the data counter in read clock field

function [7:0] binary_to_gray;
input   [7:0]   binary_code ;
begin
      binary_to_gray[0]=binary_code[0]^binary_code[1];
      binary_to_gray[1]=binary_code[1]^binary_code[2];
      binary_to_gray[2]=binary_code[2]^binary_code[3];
      binary_to_gray[3]=binary_code[3]^binary_code[4];
      binary_to_gray[4]=binary_code[4]^binary_code[5];
      binary_to_gray[5]=binary_code[5]^binary_code[6];
      binary_to_gray[6]=binary_code[6]^binary_code[7];
      binary_to_gray[7]=binary_code[7];
end
endfunction

function [7:0] gray_to_binary;
input   [7:0]   gray_code ;
begin
      gray_to_binary[7]=waddr_gray_rclk_1[7];
      gray_to_binary[6]=waddr_gray_rclk_1[6]^gray_to_binary[7];
      gray_to_binary[5]=waddr_gray_rclk_1[5]^gray_to_binary[6];
      gray_to_binary[4]=waddr_gray_rclk_1[4]^gray_to_binary[5];
      gray_to_binary[3]=waddr_gray_rclk_1[3]^gray_to_binary[4];
      gray_to_binary[2]=waddr_gray_rclk_1[2]^gray_to_binary[3];
      gray_to_binary[1]=waddr_gray_rclk_1[1]^gray_to_binary[2];  
      gray_to_binary[0]=waddr_gray_rclk_1[0]^gray_to_binary[1];   
end
endfunction

simple_dual_port_ram U_simple_dual_port_ram(
            .clka     (wr_clk            ),
            .wea      (wr_en            ),
            .addra    (waddr            ),
            .dina      (din                ),
            .clkb      (rd_clk            ),
            .addrb    (raddr             ),
            .doutb    (dout              )
            );

assign empty = (count_rclk[8:0] == 9'h0);
assign full  = (count_rclk[8:0] == 9'h100);

assign waddr_gray_wclk = binary_to_gray(waddr);
assign raddr_gray_rclk = binary_to_gray(raddr);
assign waddr_rclk = gray_to_binary(waddr_gray_rclk_1);
assign raddr_wclk = gray_to_binary(raddr_gray_wclk_1);

always@(posedge wr_clk or posedge rst)
begin
      if(rst == 1'b1)
           waddr <= 8'h0;
     else
           if(wr_en==1'b1)
                waddr <= waddr + 8'h1;
           else
                waddr <= waddr;
end

always@(posedge rd_clk or posedge rst)
begin
      if(rst == 1'b1)
          raddr <= 8'h0;
    else
          if(wr_en==1'b1)
                raddr <= raddr + 8'h1;
          else
                raddr <= raddr;
end

always@(posedge wr_clk or posedge rst)
begin
      if(rst == 1'b1)
          begin  
                raddr_gray_wclk_0<=8'b0;
                raddr_gray_wclk_1<=8'b0;
          end
    else
         begin
               raddr_gray_wclk_0<=raddr_gray_rclk;
               raddr_gray_wclk_1<=raddr_gray_wclk_0;
        end
end

always@(posedge rd_clk or posedge rst)
begin
      if(rst == 1'b1)
           begin
                 waddr_gray_rclk_0<=8'b0;
                 waddr_gray_rclk_1<=8'b0;
           end
    else
           begin
                 waddr_gray_rclk_0<=waddr_gray_wclk;
                 waddr_gray_rclk_1<=waddr_gray_rclk_0;
           end
end

always@(posedge wr_clk or posedge rst)
begin
      if(rst == 1'b1)
            count_wclk <= 9'h0;
      else
            if(waddr < raddr_wclk)
                  count_wclk <= waddr + 9'h100 - raddr_wclk;
            else if((count_wclk > 9'h0) && (waddr == raddr_wclk))
                  count_wclk <= 9'h100;
            else
                  count_wclk <= waddr - raddr_wclk;
end

always@(posedge rd_clk or posedge rst)
begin
       if(rst == 1'b1)
            count_rclk <= 9'h0;
       else
            if(waddr_rclk < raddr)
                  count_rclk <= waddr_rclk + 9'h100 - raddr;
            else if((count_rclk > 9'h0) && (waddr_rclk == raddr))
                  count_rclk <= 9'h100;
            else
                  count_rclk <= waddr_rclk - raddr;
end

endmodule

使用特权

评论回复
板凳
GoldSunMonkey| | 2012-11-6 23:06 | 只看该作者
直接例化IP不好么?

使用特权

评论回复
地板
drentsi| | 2012-11-7 12:52 | 只看该作者
直接例化IP不好么?
GoldSunMonkey 发表于 2012-11-6 23:06

我已经抛弃xilinx的异步fifo了,
使用BLOCK FIFO要占用BRAM,划不来,多数情况下不需要那么大的深度。
使用cg生成的fifo,不够灵活,而且占的资源也多,速度也慢。

这个fifo控制器,关于full和empty的赋值需要改进,会严重影响速度的。

使用特权

评论回复
5
wmsk| | 2012-11-8 00:21 | 只看该作者
我已经抛弃xilinx的异步fifo了,
使用BLOCK FIFO要占用BRAM,划不来,多数情况下不需要那么大的深度。
使用cg生成的fifo,不够灵活,而且占的资源也多,速度也慢。

这个fifo控制器,关于full和empty的赋值需要改进 ...
drentsi 发表于 2012-11-7 12:52
厉害

使用特权

评论回复
6
GoldSunMonkey| | 2012-11-8 00:31 | 只看该作者
我已经抛弃xilinx的异步fifo了,
使用BLOCK FIFO要占用BRAM,划不来,多数情况下不需要那么大的深度。
使用cg生成的fifo,不够灵活,而且占的资源也多,速度也慢。

这个fifo控制器,关于full和empty的赋值需要改进 ...
drentsi 发表于 2012-11-7 12:52
小FIFO肯定不值得一用

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

101

主题

1782

帖子

22

粉丝