异步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 widthparameter ADDRSIZE = 7; // Number of mem address bitsoutput [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 = 1always @ (posedge clkb)begin : MEM_WRITE if ((~cenb) && (~web) ) begin mem[addr_b] = data_in; endend// Memory Read Block // Read Operation : When web = 1, cena = 1always @ (posedge clka)begin : MEM_READ if ((~cena) && web) begin data_out = mem[addr_a]; endendendmodule // 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 pointeralways @(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 signalwire [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].
[1]. [2]. [3]. [4]. [5].