异步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 interfaceram_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 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 activeinput clka , // write Clock Inputinput clkb , // read Clock Inputinput cena , //Chip Selectinput 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_WRITEif ((~cenb) && (~web) ) beginmem[addr_b] = data_in;end
end// Memory Read Block
// Read Operation : When web = 1, cena = 1
always @ (posedge clka)
begin : MEM_READif ((~cena) && web) begindata_out = mem[addr_a];end
endendmodule // 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;initialbeginclk = 0;forever #(CLK_CYCLE/2)clk = ~clk;endendmodule
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;initialbeginwr_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 FIFOnormal_rd = 1'b0;normal_wr = 1'b1;repeat(500) @(negedge wr_clk);//only read FIFOnormal_wr = 1'b0;normal_rd = 1'b1;repeat(500) @(negedge rd_clk);//read and write FIFOnormal_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)beginif(wr_rst_n == 1'b0)beginwr_en <= 1'b0;wr_data <= {(DATA_WIDTH){1'b0}};endelse if(normal_wr == 1'b1)beginif(wr_full == 1'b0)beginwr_en <= 1'b1;wr_data <= {$random%((1 << (DATA_WIDTH-1)))};//wr_data <= $random;endelsebeginwr_en <= 1'b0;wr_data <= {(DATA_WIDTH){1'b0}};endendelsebeginwr_en <= 1'b0;wr_data <= {(DATA_WIDTH){1'b0}};endend//******************************************************************// read FIFO data generate//******************************************************************always @(negedge wr_clk or negedge wr_rst_n)beginif(wr_rst_n == 1'b0)rd_en <= 1'b0;else if(normal_rd == 1'b1)beginif(rd_empty == 1'b0)rd_en <= 1'b1;elserd_en <= 1'b0;endelserd_en <= 1'b0;endendmodule
参考资料
[0].Simulation and Synthesis Techniques for Asynchronous FIFO Design
[1].Memories
[2].异步FIFO的FPGA实现
[3].异步FIFO的设计
[4].基于FPGA的异步FIFO设计
[5].基于FPGA的异步FIFO验证