异步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验证