异步fifo整体模型:
优点:
分别在读、写时钟域判断空、满信号,即时使用状态信号控制fifo的操作,可有效避免读写溢出问题。
在判断空满状态时,本设计使用了格雷码进行比较,可有效避免数据变化时的竞争冒险问题。
在最坏情况下,会造成读、写操作延迟一个时钟周期(格雷码的优势),但不会出现数据读、写错误等现象。
各模块简要介绍:
FIFO Memory:双端口异步ram,fifo控制的存储器,数据缓冲区
FIFO wptr&full:写指针修改器&满信号生成器,控制写指针的操作,及fifo满信号的生成
FIFO rptr&empty :读指针修改器&空信号生成器,控制读指针操作,及fifo空信号的生成
sync_r2w,sync_w2r:两个2级寄存器构成的同步器,用于同步读写指针到各自的时钟域,进而判断空、满状态,
这样设计的意图在于避免毛刺
主要verilog hdl实现代码如下:
fifo1.v
/*
Filename: fifo1.v
Function: asynchronous fifo full implementation
Author : unkonwn
Note : traveler
Input :
wdata : data to write in
wclk : write side clock
wrst_n: write side reset signal low enable asynchronous
winc : write enable
rclk : read side clock
rrst_n: read side reset signal low enable asynchronous
rinc : read enable
Output :
rdata : output data corresponding to reading
wfull : write full signal
rempty: read empty signal
*/
module fifo1 #
(
//data bus width
parameter DSIZE = 8,
//address bus width corresponding to the fifo's volume
parameter ASIZE = 4
)
(
output [DSIZE-1:0] rdata,
output wfull,
output rempty,
input [DSIZE-1:0] wdata,
input winc, wclk, wrst_n,
input rinc, rclk, rrst_n
);
wire [ASIZE-1:0] waddr, raddr;
wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr;
//synchronous read pointer to write side by using a
//two-flip-flop-synchronizer controled by clock at write side
sync_r2w sync_r2w (.wq2_rptr(wq2_rptr),
.rptr(rptr),.wclk(wclk), .wrst_n(wrst_n));
//synchronous write pointer to read side by using a
//two-flip-flop-synchronizer controled by clock at read side
sync_w2r sync_w2r (.rq2_wptr(rq2_wptr),
.wptr(wptr),.rclk(rclk), .rrst_n(rrst_n));
//fifo controled dual-port RAM
//accessed by both the read write clock domains
fifomem #(DSIZE, ASIZE) fifomem(
.rdata(rdata), .wdata(wdata),.waddr(waddr), .raddr(raddr),
.wclken(winc), .wfull(wfull),.wclk(wclk));
//read side empty signal generator using gray-code
//increases read pointer
rptr_empty #(ASIZE) rptr_empty(.rempty(rempty),.raddr(raddr),
.rptr(rptr), .rq2_wptr(rq2_wptr),.rinc(rinc), .rclk(rclk),
.rrst_n(rrst_n));
//write side full signal generator using gray-code
//increases write pointer
wptr_full #(ASIZE) wptr_full(.wfull(wfull), .waddr(waddr),
.wptr(wptr), .wq2_rptr(wq2_rptr),.winc(winc), .wclk(wclk),
.wrst_n(wrst_n));
endmodule
fifomem.v
双端口ram
module fifomem #
(
parameter DATASIZE = 8, // Memory data word width
parameter ADDRSIZE = 4 // Number of mem address bits
)
(
output [DATASIZE-1:0] rdata,
input [DATASIZE-1:0] wdata,
input [ADDRSIZE-1:0] waddr, raddr,
input wclken, wfull, wclk
);
`ifdef VENDORRAM
// instantiation of a vendor's dual-port RAM
vendor_ram mem (.dout(rdata), .din(wdata),
.waddr(waddr), .raddr(raddr),
.wclken(wclken),
.wclken_n(wfull), .clk(wclk));
`else
// RTL Verilog memory model
localparam DEPTH = 1<<ADDRSIZE;
reg [DATASIZE-1:0] mem [0:DEPTH-1];
assign rdata = mem[raddr];
always @(posedge wclk)
if (wclken && !wfull) mem[waddr] <= wdata;
`endif
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, rclk, rrst_n
);
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext, rbinnext;
//synchronize output both binary and gray encoded read pointers
always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
{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];
//increases read address register if read enable while not read empty
assign rbinnext = rbin + (rinc & ~rempty);
//combinational logic converting binary to gray code
assign rgraynext = (rbinnext>>1) ^ rbinnext;
// FIFO empty when the next rptr == synchronized wptr or on reset
always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
rempty <= 1'b1;
else
//empty occurs when rptr == wptr (both applicative in binary and gray code)
rempty <= (rgraynext == rq2_wptr);
// rempty <= rempty_val;
// assign rempty_val = (rgraynext == rq2_wptr);
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, wclk, wrst_n);
reg [ADDRSIZE:0] wbin;
wire [ADDRSIZE:0] wgraynext, wbinnext;
//synchronize output both binary and gray encoded write pointers
always @(posedge wclk or negedge wrst_n)
if (!wrst_n)
{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];
//increases write address register if write enable while not write full
assign wbinnext = wbin + (winc & ~wfull);
//combinational logic converting binary to gray code
assign wgraynext = (wbinnext>>1) ^ wbinnext;
//full occurs when rptr == {~wptr[MSB],wptr[MSB-1:LSB]}
//I'm not so clearly about this detail,so ...
assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],
wq2_rptr[ADDRSIZE-2:0]});
//synchronize full signal
always @(posedge wclk or negedge wrst_n)
if (!wrst_n)
wfull <= 1'b0;
else
wfull <= wfull_val;
endmodule
2个读写指针同步器
太简单了 ,略过吧……
testbench文件:
module fifo1_tb ;
parameter ASIZE = 4 ;
parameter DSIZE = 8 ;
reg rrst_n ;
reg rclk ;
reg rinc ;
wire [DSIZE-1:0] rdata ;
reg wrst_n ;
reg wclk ;
reg winc ;
reg [DSIZE-1:0] wdata ;
wire rempty ;
wire wfull ;
fifo1 #(DSIZE,ASIZE )
DUT (
.rrst_n (rrst_n ) ,
.rclk (rclk ) ,
.rinc (rinc ) ,
.rdata (rdata ) ,
.wrst_n (wrst_n ) ,
.wclk (wclk ) ,
.winc (winc ) ,
.wdata (wdata ) ,
.rempty (rempty ) ,
.wfull (wfull ) );
initial
begin
rrst_n = 0;
wrst_n = 0;
rclk = 0;
wclk = 0;
winc = 0;
rinc = 0;
#20 rrst_n = 1;
wrst_n =1 ;
end
always #10 rclk = ~ rclk;
always #10 wclk = ~ wclk;
always @(posedge rclk)
rinc <= {$random} % 2;
always @(posedge wclk)
winc <= {$random} % 2;
always @(negedge wclk)
wdata <= {$random} % 256;
endmodule
功能仿真波形:
ps:
本设计是Clifford E. Cummings, Sunburst Design, Inc.在一篇论文里面实现的,我只是添加了一些自己的理解的注解,并做了简单的测试。
具体论文及modelsim工程可以参见我的csdn资源