参考以下帖子:

https://blog.csdn.net/hengzo/article/details/49683707

https://blog.csdn.net/Times_poem/article/details/51917648

https://www.cnblogs.com/aslmer/p/6114216.html

https://www.cnblogs.com/ylsm-kb/p/9068449.html

https://blog.csdn.net/Pieces_thinking/article/details/78026326

1.定义

  FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据, 其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

  FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集, 另一端是计算机的PCI总线,假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps,而PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为 1056Mbps,在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而 DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

  FIFO的分类根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。

  FIFO设计的难点在于怎样判断FIFO的空/满状态。为了保证数据正确的写入或读出,而不发生溢出或读空的状态出现,必须保证FIFO在满的情况下,不能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题。

2.同步FIFO之Verilog实现

  同步FIFO的意思是说FIFO的读写时钟频率相同,不同于异步FIFO,异步FIFO的读写时钟频率是不同的。同步FIFO的对外接口包括时钟,清零,读请求,写请求,数据输入总线,数据输出总线,空以及满信号。下面分别对同步FIFO的对外接口信号作一描述:

1. 时钟,输入,用于同步FIFO的读和写,上升沿有效;
2. 清零,输入,异步清零信号,低电平有效,该信号有效时,FIFO被清空;
3. 写请求,输入,高电平有效,该信号有效时,表明外部电路请求向FIFO写入数据;
4. 读请求,输入,高电平有效,该信号有效时,表明外部电路请求从FIFO中读取数据;
5. 数据输入总线,输入,当写信号有效时,数据输入总线上的数据被写入到FIFO中;
6. 数据输出总线,输出,当读信号有效时,数据从FIFO中被读出并放到数据输出总线上;
7. 空,输出,高电平有效,当该信号有效时,表明FIFO中没有任何数据,全部为空;
8. 满,输出,高电平有效,当该信号有效时,表明FIFO已经满了,没有空间可用来存贮数据。

下面的框图主要描述同步FIFO的内部结构 :

verilog 代码和波形文件如下:

fifo_sync.v

/*use a extra counter to calcuate current fifo filled number
its bit is fifo width + 1, such as fifo depth is 8, then it is 0,...,7, we use 4 bits counter, if counter = 8, then express full,
counter is 0, then it is empty
*/
module fifo_sync
#(
parameter FIFO_WIDTH = 32, //every fifo unit 's bit number, default is 32bits, a dword
parameter ADDR_WIDTH = 3, //2^3=FIFO_DEPTH, so addr with is 3 bits if depth=8
parameter FIFO_DEPTH = 8 //fifo depth, first in, first out.
)
(
input clk,
input rst_n,
input [FIFO_WIDTH-1:0] wr_data,
input rq, //read request
input wq, //write request
output reg [FIFO_WIDTH-1:0] rd_data,
output full,
output empty
);
//internal signal
reg[FIFO_WIDTH-1:0] fifo_mem[FIFO_DEPTH-1:0];
reg[ADDR_WIDTH:0] counter; //extra one bit for counter
reg[ADDR_WIDTH-1:0] rd_ptr;
reg[ADDR_WIDTH-1:0] wr_ptr; //set full and empty
assign full=(counter==FIFO_DEPTH);
assign empty=(counter==0); //set current fifo counter value
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
counter<=0;
else if((wq && !full)&&(rq && !empty)) // read and write meanwhile,counter keep no change
counter <= counter;
else if(rq&&!empty)
counter <= counter - 1;
else if(wq&&!full)
counter <= counter + 1;
else
counter <= counter; //no read, no write, keep no change
end
//read data if no empty and read enable
always @(posedge clk or negedge rst_n )
begin
if(!rst_n) begin
rd_data <= 0;
end
if(rq && !empty)
rd_data <= fifo_mem[rd_ptr];
end
//write data if no full and write enable
always @(posedge clk)
begin
if(wq && !full)
fifo_mem[wr_ptr] <= wr_data;
end
//update read and write ptr
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
wr_ptr <= 0;
rd_ptr <= 0;
end
else
begin
if(!full && wq)
begin
wr_ptr <= wr_ptr + 1;
// we can omit these two lines, for it will change to
// 0 if overflow.
//if(wr_ptr==(FIFO_DEPTH-1))
// wr_ptr<=0;
end
if(!empty && rq)
begin
rd_ptr <= rd_ptr + 1;
//if(rd_ptr==(FIFO_DEPTH-1))
// rd_ptr<=0;
end
end
end endmodule

fifo_sync_tb.v


module fifo_sync_tb;
reg clk,rst_n;
reg wq,rq;
reg [7:0] wr_data; // data input to be pushed to buffer
wire [7:0] rd_data; // port to output the data using pop.
wire empty,full; // buffer empty and full indication fifo_sync #(.FIFO_WIDTH(8), .ADDR_WIDTH(3), .FIFO_DEPTH(8)) fifo_inst(.clk(clk),.rst_n(rst_n),.wr_data(wr_data),.rq(rq),.wq(wq),.rd_data(rd_data),.full(full),.empty(empty)); always #10 clk = ~clk; reg [7:0] tempdata = 0;
initial begin
clk = 0;
rst_n = 0;
wq = 0;
rq = 0;
wr_data = 0;
#15;
rst_n = 1; push(1);
fork
push(2);
pop(tempdata);
join //push and pop together
push(10);
push(20);
push(30);
push(40);
push(50);
push(60);
push(70);
push(80);
push(90);
push(100);
push(110);
push(120);
push(130); pop(tempdata);
push(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(140);
pop(tempdata);
push(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
pop(tempdata);
push(5);
pop(tempdata);
#1000 $finish;
end initial
begin
$dumpfile("dump.vcd");
$dumpvars;
$fsdbDumpfile("dump.fsdb");
$fsdbDumpvars("+all");
end
task push (input [7:0] data);
if(full)
$display("---Cannot push %d: Buffer Full---",data);
else begin
$display("Push",,data);
wr_data = data;
wq = 1;
@(posedge clk);
#5 wq = 0;
end
endtask task pop(output[7:0] data);
if(empty)
$display("---Cannot Pop: Buffer Empty---");
else begin
rq = 1;
@(posedge clk);
#3 rq = 0;
data = rd_data;
$display("------Poped:",,data);
end
endtask endmodule



Makefile


# VCS flags, if want to use dump fsdb in verilog file, need to add args -fsdb, otherwise will be compiled fail
VCS_FLAGS = -sverilog -full64 -fsdb -debug_all +v2k -timescale=1ns/1ns # Source files
SRC_FILES = fifo_sync.v \
fifo_sync_tb.v # Source directories
INCDIR = +incdir+./ all:
vcs $(VCS_FLAGS) $(INCDIR) $(SRC_FILES)
clean:
rm -rf ./csrc *.daidir ./csrc *.log novas.* *.vpd *.vdb simv* *.key *race.out* *vcd *fsdb
debug:
verdi -sv -ssf dump.fsdb -f verdi.f &



verdi.f


fifo_sync.v
fifo_sync_tb.v



从波形中可以看出执行结果是正确的。

3.异步FIFO之Verilog实现

FIFO (先进先出队列)是一种在电子系统得到广泛应用的器件,通常用于数据的缓存和用于容纳异步信号的频率或相位的差异。FIFO的实现通常是利用双口RAM和读写地址产生模块来实现的。FIFO的接口信号包括异步的写时钟(wr_clk)和读时钟(rd_clk)、与写时钟同步的写有效(wren)和写数据(wr_data)、与读时钟同步的读有效(rden)和读数据(rd_data)。为了实现正确的读写和避免FIFO的上溢或下溢,通常还应该给出与读时钟和写时钟同步的FIFO的空标志(empty)和满标志(full)以禁止读写操作。

1 异步FIFO功能描述

  下图给出了FIFO的接口信号和内部模块图。

  由图可以看出,写地址产生模块根据写时钟和写有效信号产生递增的写地址,读地址产生模块根据读时钟和读有效信号产生递增的读地址。FIFO的操作如下:在写时钟wr_clk的上升沿,当wren有效时,将wr_data写入双口RAM中写地址对应的位置中;始终将读地址对应的双口RAM中的数据输出到读数据总线上。这样就实现了先进先出的功能。

这里写图片描述

  写地址产生模块还根据读地址和写地址关系产生FIFO的满标志。当wren有效时,若写地址+2=读地址时,full为1;当wren无效时,若写地址+ 1=读地址时,full为1。读地址产生模块还根据读地址和写地址的差产生FIFO的空标志。当rden有效时,若写地址-1=读地址时,empty为 1;当rden无效时,若写地址=读地址时,empty为1。按照以上方式产生标志信号是为了提前一个时钟周期产生对应的标志信号。

  由于空标志和满标志控制了FIFO的操作,因此标志错误会引起操作的错误。如上所述,标志的产生是通过对读写地址的比较产生的,当读写时钟完全异步时,对读写地址进行比较时,可能得出错误的结果。例如,在读地址变化过程中,由于读地址的各位变化并不同步,计算读写地址的差值,可能产生错误的差值,导致产生错误的满标志信号。若将未满标志置为满标志时,可能降低了应用的性能,降低写数据速率;而将满置标志置为未满时,执行一次写操作,则可能产生溢出错误,这对于实际应用来说是绝对应该避免的。空标志信号的产生也可能产生类似的错误。

2 异步FIFO的改进设计

  从以上分析中可以看出,异步FIFO之所以会发生错误是因为在地址变化时,由于多位地址各位变化时间不同,异步时钟对其进行采样时数值可能为不同于地址变化后数值的其它值,异步产生错误的空标志和满标志,以致于产生FIFO的操作错误。

 格雷码是一种在相邻计数值之间只有一位发生变化的编码方式。可以看出,若读写地址采用格雷码编码方式,就可以解决上面的问题。
 为了应用的灵活,还增加了两个标志信号,将满(almosf_full)标志和空(almost_empty)标志分别定义如下:当写地址与读地址的距离小于某个预先定义数值时,almost_full为1;当读地址与写地址的距离小于这个预先定义的数值时,almost_empty为1。

格雷码细节:https://www.cnblogs.com/mikewolf2002/

 3 异步FIFO的Verilog

…………………………………………………………………………………………………………………………………………………………………………………………………………………………….

异步FIFO的Verilog代码 之一,与之前的用RAM实现的同步FIFO的程序相比,异步更为复杂。增加了读写控制信号的跨时钟域的同步。此外,判空与判满的也稍有不同。满空判断以及格雷码生成的细节在https://www.cnblogs.com/mikewolf2002/

fifo_async.v

module fifomem
#(
parameter DATASIZE = 8, // Memory data word width
parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full
) // Number of mem address bits
(
output [DATASIZE-1:0] read_data,
input [DATASIZE-1:0] write_data,
input [ADDRSIZE-1:0] waddr, raddr,
input wq, wfull, wclk
); localparam DEPTH = 1<<ADDRSIZE; //depth is 2^4
reg [DATASIZE-1:0] mem [0:DEPTH-1]; //fifo mem
assign read_data = mem[raddr];
always @(posedge wclk)
if (wq && !wfull)
mem[waddr] <= write_data;
endmodule module sync_r2w
#(
parameter ADDRSIZE = 4
)
(
output reg [ADDRSIZE:0] wq2_rptr, //read ptr sync to write clock domain
input [ADDRSIZE:0] rptr, //gray code pointer
input wclk, wrst_n
); reg [ADDRSIZE:0] wq1_rptr; always @(posedge wclk or negedge wrst_n)
if (!wrst_n) begin
wq1_rptr <= 0;
wq2_rptr <= 0;
end
else begin
wq1_rptr<= rptr;
wq2_rptr<=wq1_rptr;
end
endmodule module sync_w2r
#(parameter ADDRSIZE = 4)
(
output reg [ADDRSIZE:0] rq2_wptr, //
input [ADDRSIZE:0] wptr, //
input rclk, rrst_n
); reg [ADDRSIZE:0] rq1_wptr; always @(posedge rclk or negedge rrst_n)
if (!rrst_n)begin
rq1_wptr <= 0;
rq2_wptr <= 0;
end
else begin
rq1_wptr <= wptr;
rq2_wptr <= rq1_wptr;
end endmodule module rptr_empty
#(
parameter ADDRSIZE = 4
)
(
output reg rempty,
output [ADDRSIZE-1:0] raddr, //binary ptr
output reg [ADDRSIZE :0] rptr, //gray code ptr
input [ADDRSIZE :0] rq2_wptr, //pointer after sync
input rq, rclk, rrst_n
);
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext, rbinnext;
// GRAYSTYLE2 pointer
// sync graycode read pointer with binary read pointer
always @(posedge rclk or negedge rrst_n)
if (!rrst_n) begin
rbin <= 0;
rptr <= 0;
end
else begin
rbin<=rbinnext;
rptr<=rgraynext; //output to sync_r2w.v
end
// Memory read-address pointer (okay to use binary to address memory)
assign raddr = rbin[ADDRSIZE-1:0]; //
assign rbinnext = rbin + (rq & ~rempty); //not empty and read request, then plus 1
assign rgraynext = (rbinnext>>1) ^ rbinnext; //graycode
// FIFO empty when the next rptr == synchronized wptr or on reset
assign rempty_val = (rgraynext == rq2_wptr); //empty when read graycode equal write prt after sync
always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
rempty <= 1'b1;
else
rempty <= rempty_val; endmodule
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 wq, wclk, wrst_n
);
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
{wbin, wptr} <= {wbinnext, wgraynext};
// Memory write-address pointer (okay to use binary to address memory)
assign waddr = wbin[ADDRSIZE-1:0];
assign wbinnext = wbin + (wq & ~wfull);
assign wgraynext = (wbinnext>>1) ^ wbinnext; //
//-----------------------------------------------------------------
//msb not equal msb -1 bit, and other bits are same, then full
assign 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
module fifo_async
#(
parameter DSIZE = 8,
parameter ASIZE = 4
)
(
input rclk, rrst_n,rq,
input wclk, wrst_n,wq,
input [DSIZE-1:0] write_data,
output [DSIZE-1:0] read_data,
output wfull,
output rempty
); wire [ASIZE-1:0] waddr, raddr;
wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr;
// synchronize the read pointer into the write-clock domain
sync_r2w sync_r2w
(
.wq2_rptr (wq2_rptr),
.rptr (rptr ),
.wclk (wclk ),
.wrst_n (wrst_n )
); // synchronize the write pointer into the read-clock domain
sync_w2r sync_w2r
(
.rq2_wptr(rq2_wptr),
.wptr(wptr),
.rclk(rclk),
.rrst_n(rrst_n)
); //this is the FIFO memory buffer that is accessed by both the write and read clock domains.
//This buffer is most likely an instantiated, synchronous dual-port RAM.
//Other memory styles can be adapted to function as the FIFO buffer.
fifomem
#(DSIZE, ASIZE)
fifomem
(
.read_data(read_data),
.write_data(write_data),
.waddr(waddr),
.raddr(raddr),
.wq(wq),
.wfull(wfull),
.wclk(wclk)
); //this module is completely synchronous to the read-clock domain and contains the FIFO read pointer and empty-flag logic.
rptr_empty
#(ASIZE)
rptr_empty
(
.rempty(rempty),
.raddr(raddr),
.rptr(rptr),
.rq2_wptr(rq2_wptr),
.rq(rq),
.rclk(rclk),
.rrst_n(rrst_n)
); //this module is completely synchronous to the write-clock domain and contains the FIFO write pointer and full-flag logic
wptr_full
#(ASIZE)
wptr_full
(
.wfull(wfull),
.waddr(waddr),
.wptr(wptr),
.wq2_rptr(wq2_rptr),
.wq(wq),
.wclk(wclk),
.wrst_n(wrst_n)
);
endmodule

fifo_async_tb.v

module fifo_async_tb;
reg wclk,wrst_n;
reg rclk,rrst_n;
reg wq,rq;
reg [7:0] write_data; // data input to be pushed to buffer
wire [7:0] read_data; // port to output the data using pop.
wire rempty,wfull; // buffer empty and full indication fifo_async #(.DSIZE(8), .ASIZE(4)) fifo_inst(
.rclk(rclk),.rrst_n(rrst_n),.rq(rq),
.wclk(wclk),.wrst_n(wrst_n),.wq(wq),
.write_data(write_data),.read_data(read_data),
.wfull(wfull),.rempty(rempty)); always #10 wclk = ~wclk;
always #20 rclk = ~rclk; reg [7:0] tempdata = 0;
initial begin
rclk = 0;
wclk = 0;
#5000 $finish;
end
initial begin
wrst_n = 1;
#2;
wrst_n = 0;
#60;
wrst_n = 1;
end initial begin
rrst_n = 1;
#2;
rrst_n = 0;
#120;
rrst_n = 1;
end
always @(posedge wclk or negedge wrst_n)
begin
if(wrst_n==1'b0)
begin
wq <= 0;
rq <= 0;
end
else begin
wq <= $random;
end
end always @(posedge rclk or negedge rrst_n)
if(rrst_n==1'b0)
rq <= 0;
else
rq <= $random; always@(*)
if(wq == 1)
begin
write_data= $random ;
end
else
write_data = 0; always @(write_data,read_data)
begin
$monitor($time, "write data:%d, read data:%d",write_data,read_data);
end
initial
begin
//$dumpfile("dump.vcd");
//$dumpvars;
$fsdbDumpfile("dump.fsdb");
$fsdbDumpvars("+all");
end endmodule

Makefile

# VCS flags, if want to use dump fsdb in verilog file, need to add args -fsdb, otherwise will be compiled fail
VCS_FLAGS = -sverilog -full64 -fsdb -debug_all +v2k -timescale=1ns/1ns # Source files
SRC_FILES = fifo_async.v \
fifo_async_tb.v # Source directories
INCDIR = +incdir+./ all:
vcs $(VCS_FLAGS) $(INCDIR) $(SRC_FILES)
clean:
rm -rf ./csrc *.daidir ./csrc novas.* *.log *.vpd *.vdb simv* *.key *race.out* *vcd *fsdb
debug:
verdi -sv -ssf dump.fsdb -f verdi.f &

verdi.f

fifo_async.v
fifo_async_tb.v

下面的fifo_async.v中,所有的代码在一个module中,接口和功能和前面一样。

module fifo_async
#(
parameter DSIZE = 8,
parameter ASIZE = 4
)
(
input wclk,wrst_n,rclk,rrst_n,rq,wq,
input [DSIZE-1:0] write_data,
output [DSIZE-1:0] read_data,
output reg wfull,rempty ); reg [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr, wq1_rptr,rq1_wptr;
reg [ASIZE:0] rbin, wbin;
reg [DSIZE-1:0] mem[0:(1<<ASIZE)-1];
wire [ASIZE-1:0] waddr, raddr;
wire [ASIZE:0] rgraynext, rbinnext,wgraynext,wbinnext;
wire rempty_val,wfull_val;
//double port ram
assign read_data=mem[raddr];
always@(posedge wclk)
if (wq && !wfull) mem[waddr] <= write_data;
//rprt sync to write clock domain
always @(posedge wclk or negedge wrst_n)
if (!wrst_n) {wq2_rptr,wq1_rptr} <= 0;
else {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
//wptr sync to read clock domain
always @(posedge rclk or negedge rrst_n)
if (!rrst_n) {rq2_wptr,rq1_wptr} <= 0;
else {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
//rempty and rptr generate
always @(posedge rclk or negedge rrst_n) // GRAYSTYLE2 pointer
begin
if (!rrst_n) {rbin, rptr} <= 0;
else {rbin, rptr} <= {rbinnext, rgraynext};
end
// Memory read-address pointer (okay to use binary to address memory)
assign raddr = rbin[ASIZE-1:0];
assign rbinnext = rbin + (rq & ~rempty);
assign rgraynext = (rbinnext>>1) ^ rbinnext;
// FIFO empty when the next rptr == synchronized wptr or on reset
assign rempty_val = (rgraynext == rq2_wptr);
always @(posedge rclk or negedge rrst_n)
begin
if (!rrst_n) rempty <= 1'b1;
else rempty <= rempty_val;
end
//---------------wfull and wprt generate
always @(posedge wclk or negedge wrst_n) // GRAYSTYLE2 pointer
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[ASIZE-1:0];
assign wbinnext = wbin + (wq & ~wfull);
assign wgraynext = (wbinnext>>1) ^ wbinnext;
assign wfull_val = (wgraynext=={~wq2_rptr[ASIZE:ASIZE-1], wq2_rptr[ASIZE-2:0]}); //:ASIZE-1]
always @(posedge wclk or negedge wrst_n)
if (!wrst_n) wfull <= 1'b0;
else wfull <= wfull_val;
endmodule

异步FIFO的Verilog代码 之二
  与前一段异步FIFO代码的主要区别在于,空/满状态标志的不同算法。

  第一个算法:Clifford E. Cummings的文章中提到的STYLE #1,构造一个指针宽度为N+1,深度为2^N字节的FIFO(为便方比较将格雷码指针转换为二进制指针)。当指针的二进制码中最高位不一致而其它N位都 相等时,FIFO为满(在Clifford E. Cummings的文章中以格雷码表示是前两位均不相同,而后两位LSB相同为满,这与换成二进制表示的MSB不同其他相同为满是一样的)。当指针完全相 等时,FIFO为空。

  这种方法思路非常明了,为了比较不同时钟产生的指针,需要把不同时钟域的信号同步到本时钟域中来,而使用Gray码的目的就是使这个异步同步化的过 程发生亚稳态的机率最小,而为什么要构造一个N+1的指针,Clifford E. Cummings也阐述的很明白,有兴趣的读者可以看下作者原文是怎么论述的,Clifford E. Cummings的这篇文章有Rev1.1 \ Rev1.2两个版本,两者在比较Gray码指针时的方法略有不同,个Rev1.2版更为精简。

  第二种算法:Clifford E. Cummings的文章中提到的STYLE #2。它将FIFO地址分成了4部分,每部分分别用高两位的MSB 00 、01、 11、 10决定FIFO是否为going full 或going empty (即将满或空)。如果写指针的高两位MSB小于读指针的高两位MSB则FIFO为“几乎满”,若写指针的高两位MSB大于读指针的高两位MSB则FIFO 为“几乎空”。

  它是利用将地址空间分成4个象限(也就是四个等大小的区域),然后观察两个指针的相对位置,如果写指针落后读指针一个象限(25%的距离,呵呵), 则证明很可能要写满,反之则很可能要读空,这个时候分别设置两个标志位dirset和dirrst,然后在地址完全相等的情况下,如果dirset有效就 是写满,如果dirrst有效就是读空。
  这种方法对深度为2^N字节的FIFO只需N位的指针即可,处理的速度也较第一种方法快。

  这段是说明的原话,算法一,还好理解。算法二,似乎没有说清楚,不太明白。有兴趣的可以查查论文,详细研究下。

  总之,第二种写法是推荐的写法。因为异步的多时钟设计应按以下几个原则进行设计:
  1,尽可能的将多时钟的逻辑电路(非同步器)分割为多个单时钟的模块,这样有利于静态时序分析工具来进行时序验证。
  2,同步器的实现应使得所有输入来自同一个时钟域,而使用另一个时钟域的异步时钟信号采样数据。
  3,面向时钟信号的命名方式可以帮助我们确定那些在不同异步时钟域间需要处理的信号。
  4,当存在多个跨时钟域的控制信号时,我们必须特别注意这些信号,保证这些控制信号到达新的时钟域仍然能够保持正确的顺序。

第二种异步fifo实现方式代码如下,testbench文件和Makefile文件和前面的第一种方法相同。

fifo_async.v

module fifo_async (read_data, wfull, rempty, write_data,
wq, wclk, wrst_n, rq, rclk, rrst_n);
parameter DSIZE = 8;
parameter ASIZE = 4;
output [DSIZE-1:0] read_data;
output wfull;
output rempty;
input [DSIZE-1:0] write_data;
input wq, wclk, wrst_n;
input rq, rclk, rrst_n;
wire [ASIZE-1:0] wptr, rptr;
wire [ASIZE-1:0] waddr, raddr;
async_cmp #(ASIZE) async_cmp(.aempty_n(aempty_n),
.afull_n(afull_n),
.wptr(wptr), .rptr(rptr),
.wrst_n(wrst_n));
fifomem2 #(DSIZE, ASIZE) fifomem2(.read_data(read_data),
.write_data(write_data),
.waddr(wptr),
.raddr(rptr),
.wq(wq),
.wclk(wclk));
rptr_empty2 #(ASIZE) rptr_empty2(.rempty(rempty),
.rptr(rptr),
.aempty_n(aempty_n),
.rq(rq),
.rclk(rclk),
.rrst_n(rrst_n));
wptr_full2 #(ASIZE) wptr_full2(.wfull(wfull),
.wptr(wptr),
.afull_n(afull_n),
.wq(wq),
.wclk(wclk),
.wrst_n(wrst_n));
endmodule module fifomem2
#(
parameter DATASIZE = 8, // Memory data word width
parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full
) // Number of mem address bits
(
output [DATASIZE-1:0] read_data,
input [DATASIZE-1:0] write_data,
input [ADDRSIZE-1:0] waddr, raddr,
input wq, wclk
);
localparam DEPTH = 1<<ADDRSIZE; // DEPTH = 2**ADDRSIZE
reg [DATASIZE-1:0] MEM [0:DEPTH-1];
assign read_data = MEM[raddr];
always @(posedge wclk)
if (wq)
MEM[waddr] <= write_data;
endmodule module async_cmp
#(
parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full
)
(
input [ADDRSIZE-1:0] wptr, rptr,
input wrst_n,
output aempty_n, afull_n
); localparam N = ADDRSIZE-1;
//internal signals
reg direction;
wire high = 1'b1;
wire dirset_n = ~( (wptr[N]^rptr[N-1]) & ~(wptr[N-1]^rptr[N]));
wire dirclr_n = ~((~(wptr[N]^rptr[N-1]) & (wptr[N-1]^rptr[N]))| ~wrst_n);
always @(posedge high or negedge dirset_n or negedge dirclr_n)
if (!dirclr_n)
direction <= 1'b0;
else if(!dirset_n)
direction <= 1'b1;
else
direction <= high;
assign aempty_n = ~((wptr == rptr) && !direction);
assign afull_n = ~((wptr == rptr) && direction); endmodule module rptr_empty2
#(
parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full
)
(
input aempty_n,
input rq,rclk,rrst_n,
output reg rempty,
output reg [ADDRSIZE-1:0] rptr
);
reg [ADDRSIZE-1:0] rbin;
reg rempty2;
wire [ADDRSIZE-1:0] rgnext, rbnext;
//---------------------------------------------------------------
// GRAYSTYLE2 pointer
//---------------------------------------------------------------
always @(posedge rclk or negedge rrst_n)
if (!rrst_n)
begin
rbin <= 0;
rptr <= 0;
end
else
begin
rbin <= rbnext;
rptr <= rgnext;
end
//---------------------------------------------------------------
// increment the binary count if not empty
//---------------------------------------------------------------
assign rbnext = !rempty ? rbin + rq : rbin;
assign rgnext = (rbnext>>1) ^ rbnext; // binary-to-gray conversion
always @(posedge rclk or negedge aempty_n)
if (!aempty_n) {rempty,rempty2} <= 2'b11;
else {rempty,rempty2} <= {rempty2,~aempty_n};
endmodule module wptr_full2
#(
parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full
)
(
input afull_n, wq, wclk,wrst_n,
output reg wfull,
output reg [ADDRSIZE-1:0] wptr
); reg [ADDRSIZE-1:0] wbin;
reg wfull2;
wire [ADDRSIZE-1:0] wgnext, wbnext;
//---------------------------------------------------------------
// GRAYSTYLE2 pointer
//---------------------------------------------------------------
always @(posedge wclk or negedge wrst_n)
if (!wrst_n) begin
wbin <= 0;
wptr <= 0;
end
else begin
wbin <= wbnext;
wptr <= wgnext;
end
//---------------------------------------------------------------
// increment the binary count if not full
assign wbnext = !wfull ? wbin + wq : wbin;
assign wgnext = (wbnext>>1) ^ wbnext; // binary-to-gray conversion
always @(posedge wclk or negedge wrst_n or negedge afull_n)
if (!wrst_n ) {wfull,wfull2} <= 2'b00;
else if (!afull_n) {wfull,wfull2} <= 2'b11;
else {wfull,wfull2} <= {wfull2,~afull_n};
endmodule

同步fifo与异步fifo的更多相关文章

  1. 怎么用Verilog语言描述同步FIFO和异步FIFO

    感谢 知乎龚大佬 打杂大佬 网上几个nice的博客(忘了是哪个了....) 前言 虽然FIFO都有IP可以使用,但理解原理还是自己写一个来得透彻. 什么是FIFO? Fist in first out ...

  2. 异步FIFO及verilog原码

    这几天看了Clifford E. Cummings的两篇大作<Simulation and Synthesis Techniques for Asynchronous FIFO Design&g ...

  3. 异步FIFO总结

    异步FIFO总结 异步FIFO的基本概念 异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据 FIFO的常见参数 FIFO的宽度:即FIFO ...

  4. 异步FIFO总结+Verilog实现

    异步FIFO简介 异步FIFO(First In First Out)可以很好解决多比特数据跨时钟域的数据传输与同步问题.异步FIFO的作用就像一个蓄水池,用于调节上下游水量. FIFO FIFO是一 ...

  5. 异步fifo的设计

    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一.FIFO简单讲解 FIFO的本质是RAM, 先进先出 重要参数:fifo深度(简单来说就是需要存多少个数据)           ...

  6. 异步FIFO为什么用格雷码

    异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,将写地址同步到读时钟域再和读地址比较进行FIFO空状态判断(同步后的写地址一定是小于 ...

  7. Verilog设计异步FIFO

    转自http://ninghechuan.com 异步FIFO有两个异步时钟,一个端口写入数据,一个端口读出数据.通常被用于数据的跨时钟域的传输. 同步FIFO的设计.一个时钟控制一个计数器,计数器增 ...

  8. 异步fifo的Verilog实现

     一.分析 由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题,如何解决? 跨时钟域的问题:由于读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO ...

  9. 异步FIFO空满设计延迟问题

    由于设计的时候读写指针用了至少两级寄存器同步,同步会消耗至少两个时钟周期,势必会使得判断空或满有所延迟,这会不会导致设计出错呢? 异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域 ...

随机推荐

  1. csp 201709-2 优先队列模拟

    数据规模: 用优先队列对各个事件的发生先后记录即可: #include<iostream> #include<queue> using namespace std; ]; st ...

  2. 10-tensorflow-tf.concat()

    Concatenates tensors along one dimension. t1 = [[1, 2, 3], [4, 5, 6]] t2 = [[7, 8, 9], [10, 11, 12]] ...

  3. 批处理教程之cls、pause命令

    cls 命令     清除屏幕.执行该命令后,屏幕上的所有信息都被清除,光标重新定位至屏幕左上角. REM 和 :: REM为注释命令,一般用来给程序加上注解,该命令后的内容不被执行,但能回显. 其次 ...

  4. 关于==和equals的区别和联系,面试这么回答就可以

    长篇大论的话,我这里就不多写了,相信大家入门java 的时候就知道个大概了,这里想表述的是,如果面试官问你<关于==和equals的区别>,该怎么回答完美呢?可以这样说 总结的来说: 1) ...

  5. RaxML使用

    1.下载 https://github.com/stamatak/standard-RAxML 2.How many Threads shall I use? 重要的是要知道,RAxML PThrea ...

  6. 第02组 Beta冲刺(3/4)

    队名:十一个憨批 组长博客 作业博客 组长黄智 过去两天完成的任务:了解整个游戏的流程 GitHub签入记录 接下来的计划:继续完成游戏 还剩下哪些任务:完成游戏 燃尽图 遇到的困难:没有美术比较好的 ...

  7. [LeetCode] 874. Walking Robot Simulation 走路机器人仿真

    A robot on an infinite grid starts at point (0, 0) and faces north.  The robot can receive one of th ...

  8. [LeetCode] 377. Combination Sum IV 组合之和之四

    Given an integer array with all positive numbers and no duplicates, find the number of possible comb ...

  9. UDF——读取文件作为边界条件(类似profile的效果)

    本文编译工具:VC++ UDF Studio 该插件可以直接在Visual Studio中一键编译.加载.调试UDF源码,极大提高编写排错效率,且支持C++,MFC,Windows API和第三方库, ...

  10. JVM系列之六:内存溢出、内存泄漏 和 栈溢出

    1. OOM && SOF OutOfMemoryError异常: 除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能, 内存 ...