zoukankan      html  css  js  c++  java
  • 异步FIFO总结

    异步FIFO总结

    异步FIFO的基本概念


    异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据

    FIFO的常见参数


    FIFO的宽度:即FIFO一次读写操作的数据位;
    FIFO的深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
    满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)。
    空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
    读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
    写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

    异步FIFO的设计难点


    如何同步异步信号,使触发器不产生亚稳态
    如何正确地设计空、满以及几乎满等信号的控制电路

    亚稳态问题的解决


    对写地址/读地址采用格雷码
    采用触发器来同步异步输入信号

    空/满标志的产生


    空/满标志产生的原则是:写满不溢出,读空不多读

    FIFO的缓冲空间是有限的,在设计中需要考虑缓冲区的溢出和空情况。
    缓冲区满,产生FULL信号,以阻塞数据继续向FIFO写入;
    缓冲区空,产生EMPTY信号,以阻塞数据继续从FIFO读出。

    方法:

    1.采用握手协议
    2.读时钟采样写指针,写时钟采样读指针,采样的写指针和读指针判断是否为EMPTY,采样的读指针和写指针判断是否为FULL

    Gray code counter


    FIFO 框图



    VCS仿真结果


    异步FIFO代码(verilog)


    RTL描述


    fifoif.v

    module fifoif
           (
           fifo_flush,
           data_out,
           full_out,
           empty_out,
           data_in,
           wren_in, wclk, wclr_in,
           rden_in, rclk, rclr_in,
           fifo_waddr, fifo_raddr,
           ram_douta,
           ram_dinb,
           ram_ada,
           ram_adb,
           ram_cena,
           ram_cenb,
           ram_clka,
           ram_clkb
           );
    
    parameter DSIZE = 32;
    parameter ASIZE = 7;
    
    input              fifo_flush;
    output [DSIZE-1:0] data_out;
    output             full_out;
    output             empty_out;
    input  [DSIZE-1:0] data_in;
    input              wren_in, wclk, wclr_in;
    input              rden_in, rclk, rclr_in;
    output [ASIZE-1:0] fifo_waddr, fifo_raddr;
    
    input  [DSIZE-1:0] ram_douta;
    output [DSIZE-1:0] ram_dinb;
    output [ASIZE-1:0] ram_ada;
    output [ASIZE-1:0] ram_adb;
    output             ram_cena;
    output             ram_cenb;
    output             ram_clka;
    output             ram_clkb;
    
    wire   [DSIZE-1:0] ram_douta;
    wire   [DSIZE-1:0] ram_dinb;
    wire   [ASIZE-1:0] ram_ada;
    wire   [ASIZE-1:0] ram_adb;
    wire               ram_cena;
    wire               ram_cenb;
    wire               ram_clka;
    wire               ram_clkb;
    
    wire  [DSIZE-1:0] data_out;
    wire  [ASIZE-1:0] waddr;
    wire  [ASIZE-1:0] raddr;
    wire    [ASIZE:0] wptr;
    wire    [ASIZE:0] rptr;
    wire    [ASIZE:0] wq2_rptr;
    wire    [ASIZE:0] rq2_wptr;
    wire  [ASIZE-1:0] fifo_waddr = waddr;
    wire  [ASIZE-1:0] fifo_raddr = raddr;
    
    sync_r2w #(ASIZE) u_sync_r2w(
        .wq2_rptr     (wq2_rptr),
        .rptr         (rptr),
        .wclk         (wclk),
        .wrst_n       (wclr_in),
        .wflush       (fifo_flush));
    
    sync_w2r #(ASIZE) u_sync_w2r(
        .rq2_wptr     (rq2_wptr),
        .wptr         (wptr),
        .rclk         (rclk),
        .rrst_n       (rclr_in),
        .rflush       (fifo_flush));
    
    fifomem #(DSIZE, ASIZE) u_fifomem(
        .rdata        (data_out),
        .wdata        (data_in),
        .waddr        (waddr),
        .raddr        (raddr),
        .wren         (wren_in),
        .wclk         (wclk),
        .rden         (rden_in),
        .rclk         (rclk),
        .ram_douta    (ram_douta),
        .ram_dinb     (ram_dinb),
        .ram_ada      (ram_ada),
        .ram_adb      (ram_adb),
        .ram_cena     (ram_cena),
        .ram_cenb     (ram_cenb),
        .CLKA         (ram_clka),
        .CLKB         (ram_clkb)
        );
    
    rptr_empty #(ASIZE) u_rptr_empty(
        .rempty       (empty_out),
        .raddr        (raddr),
        .rptr         (rptr),
        .rq2_wptr     (rq2_wptr),
        .rinc         (rden_in),
        .rclk         (rclk),
        .rrst_n       (rclr_in),
        .rflush       (fifo_flush)
        );
    
    wptr_full #(ASIZE) u_wptr_full(
        .wfull        (full_out),
        .waddr        (waddr),
        .wptr         (wptr),
        .wq2_rptr     (wq2_rptr),
        .winc         (wren_in),
        .wclk         (wclk),
        .wrst_n       (wclr_in),
        .wflush       (fifo_flush)
        );
    
    endmodule
    

    fifomem.v

    module fifomem(
           rdata, wdata, waddr, raddr, wren, wclk, rden, rclk,
    
           //memory interface
           ram_douta, ram_dinb, ram_ada, ram_adb, ram_cena, ram_cenb,
           CLKA, CLKB
           );
    parameter DATASIZE = 32; // Memory data word width
    parameter ADDRSIZE = 7; // Number of mem address bits
    
    output [DATASIZE-1:0] rdata;
    input  [DATASIZE-1:0] wdata;
    input  [ADDRSIZE-1:0] waddr, raddr;
    input                 wren, wclk;
    input                 rden, rclk;
    
    input  [DATASIZE-1:0] ram_douta;
    output [DATASIZE-1:0] ram_dinb;
    output [ADDRSIZE-1:0] ram_ada;
    output [ADDRSIZE-1:0] ram_adb;
    output                ram_cena;
    output                ram_cenb;
    output                CLKA;
    output                CLKB;
    
    wire                ram_cena,ram_cenb;
    wire                CLKA;
    wire                CLKB;
    wire [DATASIZE-1:0] ram_dinb,ram_douta;
    wire [ADDRSIZE-1:0] ram_ada, ram_adb;
    wire [DATASIZE-1:0] rdata,wdata;
    
    assign ram_cena = ~rden;
    assign ram_ada  = raddr;
    assign ram_cenb = ~wren;
    assign ram_adb  = waddr;
    assign ram_dinb = wdata;
    assign CLKA     = rclk;
    assign CLKB     = wclk;
    assign rdata    = ram_douta;
    
    endmodule
    

    ram_dp.v

    module ram_dp #(
                    parameter DATA_WIDTH = 32,
                    parameter ADDR_WIDTH = 7,
                    parameter RAM_DEPTH = 1 << ADDR_WIDTH
                    )
                    (
                     output  reg    [DATA_WIDTH-1:0]     data_out     ,
                     input          [DATA_WIDTH-1:0]     data_in      ,
                     input          [ADDR_WIDTH-1:0]     addr_a       ,
                     input          [ADDR_WIDTH-1:0]     addr_b       ,
                     input                               web          , //Write Enable/Read Enable,addr_b write enable, low active
                     input                               clka         , // write  Clock Input
                     input                               clkb         , // read   Clock Input
                     input                               cena         , //Chip Select
                     input                               cenb           //Chip Select
                    ); 
    
    
    
    //--------------Internal variables---------------- 
    reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];
    reg                  oe_r;
    
    //--------------Code Starts Here------------------ 
    
    // Memory Write Block 
    // Write Operation : When web = 0, cenb = 1
    always @ (posedge clkb)
    begin : MEM_WRITE
       if ((~cenb) && (~web) ) begin
           mem[addr_b] = data_in;
       end
    end
    
    // Memory Read Block 
    // Read Operation : When web = 1, cena = 1
    always @ (posedge clka)
    begin : MEM_READ
      if ((~cena) && web) begin
        data_out = mem[addr_a];
      end
    end
    
    endmodule // End of Module ram_dp
    

    sync_r2w.v

    module sync_r2w 
           #(parameter ADDRSIZE = 4)
           (
            output reg  [ADDRSIZE:0]    wq2_rptr,
            input       [ADDRSIZE:0]    rptr,
            input                       wclk,
            input                       wrst_n,
            input                       wflush
           );
    
    reg    [ADDRSIZE:0] wq1_rptr;
    
    always @(posedge wclk or negedge wrst_n)
      if (!wrst_n) 
          {wq2_rptr,wq1_rptr} <= 0;
      else if(wflush) 
          {wq2_rptr,wq1_rptr} <= 0;
      else 
          {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
    
    endmodule
    

    sync_w2r.v

    module sync_w2r 
           #(parameter ADDRSIZE = 4)
           (
            output    reg    [ADDRSIZE:0]   rq2_wptr,
            input            [ADDRSIZE:0]   wptr,
            input                           rclk,
            input                           rrst_n,
            input                           rflush
           );
    
    reg              [ADDRSIZE:0]   rq1_wptr;
    
    always @(posedge rclk or negedge rrst_n)
      if (!rrst_n) 
          {rq2_wptr,rq1_wptr} <= 0;
      else if(rflush) 
          {rq2_wptr,rq1_wptr} <= 0;
      else 
          {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
    
    endmodule
    

    rptr_empty.v

    module rptr_empty
           #(parameter ADDRSIZE = 4)
           (
            output    reg                    rempty,
            output          [ADDRSIZE-1:0]   raddr,
            output    reg   [ADDRSIZE :0]    rptr,
            input           [ADDRSIZE :0]    rq2_wptr,
            input                            rinc,
            input                            rclk,
            input                            rrst_n,
            input                            rflush
           );
    
    reg      [ADDRSIZE:0] rbin;
    wire     [ADDRSIZE:0] rgraynext, rbinnext;
    
    //-------------------
    // GRAYSTYLE2 pointer
    //-------------------
    always @(posedge rclk or negedge rrst_n)
      if (!rrst_n) 
          {rbin, rptr} <= 0;
      else if(rflush) 
          {rbin, rptr} <= 0;
      else 
          {rbin, rptr} <= {rbinnext, rgraynext};
    
    // Memory read-address pointer (okay to use binary to address memory)
    assign raddr = rbin[ADDRSIZE-1:0];
    
    assign rbinnext = rbin + (rinc & ~rempty);
    assign rgraynext = (rbinnext>>1) ^ rbinnext;
    
    //---------------------------------------------------------------
    // FIFO empty when the next rptr == synchronized wptr or on reset
    //---------------------------------------------------------------
    wire rempty_val = (rgraynext == rq2_wptr);
    
    always @(posedge rclk or negedge rrst_n)
      if (!rrst_n) 
          rempty <= 1'b1;
      else 
          rempty <= rempty_val;
    
    endmodule
    

    wptr_full.v

    module wptr_full
           #(parameter ADDRSIZE = 4)
           (
            output   reg                      wfull,
            output          [ADDRSIZE-1:0]    waddr,
            output   reg    [ADDRSIZE :0]     wptr,
            input           [ADDRSIZE :0]     wq2_rptr,
            input                             winc,
            input                             wclk,
            input                             wrst_n,
            input                             wflush
           );
    
    reg             [ADDRSIZE:0]      wbin;
    wire            [ADDRSIZE:0]      wgraynext, wbinnext;
    
    // GRAYSTYLE2 pointer
    always @(posedge wclk or negedge wrst_n)
      if (!wrst_n) 
          {wbin, wptr} <= 0;
      else if(wflush) 
          {wbin, wptr} <= 0;
      else 
          {wbin, wptr} <= {wbinnext, wgraynext};
    
    // Memory write-address pointer (okay to use binary to address memory)
    assign waddr = wbin[ADDRSIZE-1:0];
    
    assign wbinnext = wbin + (winc & ~wfull);
    assign wgraynext = (wbinnext>>1) ^ wbinnext;
    
    //------------------------------------------------------------------
    // Simplified version of the three necessary full-tests:
    // assign wfull_val=((wgnext[ADDRSIZE]    !=wq2_rptr[ADDRSIZE]  ) &&
    //                   (wgnext[ADDRSIZE-1]  !=wq2_rptr[ADDRSIZE-1]) &&
    //                   (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0]));
    //------------------------------------------------------------------
    
    wire wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
                                   wq2_rptr[ADDRSIZE-2:0]});
    
    always @(posedge wclk or negedge wrst_n)
      if (!wrst_n) 
          wfull <= 1'b0;
      else 
          wfull <= wfull_val;
    
    endmodule
    

    testbench


    fifo_tb.v

    module fifo_tb;
    
    reg                          fifo_flush;
    wire       [31:0]            data_out;
    wire                         full_out;
    wire                         empty_out;
    wire       [31:0]            data_in;
    wire                         wren_in;
    wire                         wclk;
    wire                         wr_rst_n;
    wire                         rd_rst_n;
    wire                         rden_in;
    wire                         rclk;
    wire       [6:0]             fifo_waddr;
    wire       [6:0]             fifo_raddr;
    
    // memory interface signal
    wire       [31:0]            if_ram_douta;
    wire       [31:0]            if_ram_dinb;
    wire       [6:0]             if_ram_ada;
    wire       [6:0]             if_ram_adb;
    wire                         if_ram_cena;
    wire                         if_ram_cenb;
    wire                         if_ram_clka;
    wire                         if_ram_clkb;
    
    //******************************************************************
    //  generate clk
    //******************************************************************
    clock #(.CLK_FREQ(100.0))
        u_clock_wr (
                    .clk    (   wclk  )
                   );
    
    clock #(.CLK_FREQ(70.0))
        u_clock_rd (
                    .clk    (   rclk  )
                   );
    
    assign  if_ram_clka  = rclk;
    assign  if_ram_clkb  = wclk;
    
    //******************************************************************
    //  generate read and write data
    //******************************************************************
    
    fifo_data #(.DATA_WIDTH(8))
                 u_fifo_data (
                              .wr_rst_n   (   wr_rst_n    ),
                              .wr_clk     (   wclk        ),
                              .wr_en      (   wren_in     ),
                              .wr_data    (   data_in     ),
                              .wr_full    (   full_out    ),
                              .rd_rst_n   (   rd_rst_n    ),
                              .rd_clk     (   rclk        ),
                              .rd_en      (   rden_in     ),
                              .rd_empty   (   empty_out   )
                             );
    
    //******************************************************************
    //  creat FSDB
    //******************************************************************
    initial begin
    	$fsdbDumpfile("tb.fsdb");
    	$fsdbDumpvars();
    end
    
    //===================================================
    // u_fifoif from rtl/fifoif.v                        
    //===================================================
    
    fifoif u_fifoif (
        .fifo_flush                 ( 1'b0 ),
        .data_out                   ( data_out ),
        .full_out                   ( full_out ),
        .empty_out                  ( empty_out ),
        .data_in                    ( data_in ),
        .wren_in                    ( wren_in ),
        .wclk                       ( wclk ),
        .wclr_in                    ( wr_rst_n ),
        .rden_in                    ( rden_in ),
        .rclk                       ( rclk ),
        .rclr_in                    ( rd_rst_n ),
        .fifo_waddr                 ( fifo_waddr ),
        .fifo_raddr                 ( fifo_raddr ),
    
         // Memory interface
        .ram_douta                  ( if_ram_douta ),
        .ram_dinb                   ( if_ram_dinb ),
        .ram_ada                    ( if_ram_ada ),
        .ram_adb                    ( if_ram_adb ),
        .ram_cena                   ( if_ram_cena ),
        .ram_cenb                   ( if_ram_cenb ),
        .ram_clka                   ( if_ram_clka ),
        .ram_clkb                   ( if_ram_clkb )
    );
    
    ram_dp u_ram_dp(
                    .data_out           (if_ram_douta[31:0]), //A data output, 32 bits
                    .data_in            (if_ram_dinb[31:0]),  //B data input , 32 bits
                    .addr_a             (if_ram_ada[6:0]),    //A adress, 7 bits
                    .addr_b             (if_ram_adb[6:0]),    //B adress, 7 bits
                    .web                (if_ram_cenb),        //Write Enable/Read Enable,addr_b write enable, low active
                    .clka               (if_ram_clka),        // write  Clock Input
                    .clkb               (if_ram_clkb),        // read   Clock Input
                    .cena               (if_ram_cena),        //Chip Select ,low active
                    .cenb               (if_ram_cenb)         //Chip Select ,low active
                    ); 
    
    endmodule
    

    clock.v

    module clock #(parameter   CLK_FREQ = 100.0)  //MHz
               (
                output    reg   clk
                );
    
        localparam  CLK_CYCLE = 1000.0 / CLK_FREQ;
        
        initial
        begin
            clk = 0;
            forever #(CLK_CYCLE/2)
                clk = ~clk;
        end
        
    endmodule
    

    fifo_data.v

    module fifo_data #(parameter DATA_WIDTH = 8)
                    (
                     output reg                      wr_rst_n,
                     input                           wr_clk,
                     output reg                      wr_en,
                     output reg  [DATA_WIDTH-1:0]    wr_data,
                     input                           wr_full,
                     output reg                      rd_rst_n,
                     input                           rd_clk,
                     output reg                      rd_en,
                     input                           rd_empty
                    );
        
        
        reg             normal_wr;
        reg             normal_rd;
        
        initial
        begin
            wr_rst_n  = 1'b0;
            rd_rst_n  = 1'b0;
            normal_wr = 1'b0;
            normal_rd = 1'b0;
            #492;
            wr_rst_n  = 1'b1;
            rd_rst_n  = 1'b1;
            #100;
    
            //only write FIFO
            normal_rd = 1'b0;
            normal_wr = 1'b1;
            repeat(500) @(negedge wr_clk);
            
            //only read FIFO
            normal_wr = 1'b0;
            normal_rd = 1'b1;
            repeat(500) @(negedge rd_clk);
            
            //read and write FIFO
            normal_rd = 1'b0;
            normal_wr = 1'b0;
    
            normal_wr = 1'b1;
            normal_rd = 1'b1;
            repeat(1000) @(negedge wr_clk);
            
            normal_wr = 1'b0;
            normal_rd = 1'b0;
            repeat(50) @(negedge rd_clk);
            $finish;
        end
        
        //******************************************************************
        //  write FIFO data generate
        //******************************************************************
        always @(negedge wr_clk or negedge wr_rst_n)
        begin
            if(wr_rst_n == 1'b0)
            begin
                wr_en   <= 1'b0;
                wr_data <= {(DATA_WIDTH){1'b0}};
            end
            else if(normal_wr == 1'b1)
            begin
                if(wr_full == 1'b0)
                begin
                    wr_en   <= 1'b1;
                    wr_data <= {$random%((1 << (DATA_WIDTH-1)))};
                    //wr_data <= $random;
                end
                else
                begin
                    wr_en   <= 1'b0;
                    wr_data <= {(DATA_WIDTH){1'b0}};
                end
            end
            else
            begin
                wr_en   <= 1'b0;
                wr_data <= {(DATA_WIDTH){1'b0}};
            end
        end
        
        //******************************************************************
        //  read FIFO data generate
        //******************************************************************
        always @(negedge wr_clk or negedge wr_rst_n)
        begin
            if(wr_rst_n == 1'b0)
                rd_en   <= 1'b0;
            else if(normal_rd == 1'b1)
            begin
                if(rd_empty == 1'b0)
                    rd_en   <= 1'b1;
                else
                    rd_en   <= 1'b0;
            end
            else
                rd_en   <= 1'b0;
        end
        
    endmodule
    

    参考资料


    [0].Simulation and Synthesis Techniques for Asynchronous FIFO Design
    [1].Memories
    [2].异步FIFO的FPGA实现
    [3].异步FIFO的设计
    [4].基于FPGA的异步FIFO设计
    [5].基于FPGA的异步FIFO验证

  • 相关阅读:
    spoj1812 Longest Common Substring II( 后缀自动机 )
    SPOJ8222 Substrings( 后缀自动机 + dp )
    BZOJ 2882: 工艺( 后缀自动机 )
    NOIP2015酱油记
    NOIP2015前
    BZOJ 1088: [SCOI2005]扫雷Mine( )
    BZOJ 1261: [SCOI2006]zh_tree( 区间dp )
    HDU4027 Can you answer these queries? 线段树
    ZOJ3261 Connections in Galaxy War 并查集
    POJ 1773 Parity game 带权并查集
  • 原文地址:https://www.cnblogs.com/OneFri/p/7141368.html
Copyright © 2011-2022 走看看