FIFO 包括同步 FIFO 和异步 FIFO 两种,同步 FIFO 有一个时钟信号,读和写逻辑全部使用这一个时钟信号,异步 FIFO 有两个时钟信号,读和写逻辑用的各种的读写时钟,本节说的全部是异步 FIFO。
异步 FIFO 有两个时钟信号,读和写接口分别采用不同时钟,这两个时钟可能时钟频率不同,也可能时钟相位不同,可能是同源时钟,也可能是不同源时钟。
在现代逻辑设计中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简便、快捷的解决方案,使用异步 FIFO 可以在两个不同时钟系统之间快速而方便地传输实时数据。
一般异步 FIFO 的地址传递需要使用格雷码,每次地址变化格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到 gray 码的转换电路,将地址值转换为相应的 gray 码,然后将该 gray 码同步到另一个时钟域进行对比,作为空满状态的检测。
使用 gray 码解决了指针采样错误的问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。
对于“空”的判断依然依据二者完全相等(包括 MSB);
而对于“满”的判断,如下图,由于 gray 码除了 MSB 外,具有镜像对称的特点,当读指针指向 7,写指针指向 8 时,除了 MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在 gray 码上判断为满必须同时满足以下 3 条:
(1)wptr 和同步过来的 rptr 的 MSB 不相等,因为 wptr 必须比 rptr 多折回一次。
(2 )wptr 与 rptr 的次高位不相等,如下图位置 7 和位置 15,转化为二进制对应的是 0111 和 1111,MSB不同说明多折回一次,111 相同代表同一位置。
(3)剩下的其余位完全相等。
异步FIFO空满状态
异步 FIFO 系统框图
顶层程序:
`timescale 1ns / 1ps
module shousififo_top
#( parameter ASIZE=4, //地址位宽
parameter DSIZE=8 //数据位宽
)
(
input [DSIZE-1:0] wdata,
input winc,wclk,wrst_n,//写请求信号、写时钟、写复位
input rinc,rclk,rrst_n,
output[DSIZE-1:0] rdata,
output wfull,
output rempty
);
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;
sync_r2w I1_sync_r2w(
.wq2_rptr (wq2_rptr),
.rptr (rptr ),
.wclk (wclk ),
.wrst_n (wrst_n )
);
sync_w2r I2_sync_w2r(
.rq2_wptr(rq2_wptr),
.wptr (wptr ),
.rclk (rclk ),
.rrst_n (rrst_n )
);
DualRAM #(DSIZE,ASIZE)I3_DualRAM(
.raddr (raddr) ,
.waddr (waddr) ,
.wdata (wdata) ,
.rdata (rdata) ,
.wclken(winc) ,
.wclk (wclk)
);
rptr_empty #(ASIZE) I4_rptr_empty(
.rempty (rempty ) ,
.radddr (radddr ) ,
.rptr (rptr ) ,
.rq2_wptr (rq2_wptr) ,
.rinc (rinc ) ,
.rclk (rclk ) ,
.rrst_n (rrst_n )
);
wptr_full#(ASIZE) I5_wptr_full(
. wfull (wfull ),
. waddr (waddr ),
. wptr (wptr ),
. wq2_rptr (wq2_rptr ),
. winc (winc ),
. wclk (wclk ),
. wrst_n (wrst_n )
);
endmodule
读到写的同步:
`timescale 1ns / 1ps
module sync_r2w
#(parameter ADDRSIZE=4
)
(
output reg [ADDRSIZE:0] wq2_rptr ,
input [ADDRSIZE:0] rptr ,
input wclk ,wrst_n
);
reg [ADDRSIZE:0] wq1_rptr ;
always@(posedge wclk or negedge wrst_n)begin
if(!wrst_n)
{wq2_rptr,wq1_rptr}<=0;
else
{wq2_rptr,wq1_rptr}<={wq1_rptr,rptr} ;
end
endmodule
写到读的同步:
`timescale 1ns / 1ps
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)begin
if(!rrst_n)
{rq2_wptr,rq1_wptr}<=0;
else
{rq2_wptr,rq1_wptr}<={rq1_wptr,wptr} ;
end
endmodule
双端口ram:
`timescale 1ns / 1ps
module DualRAM
#(parameter DATA_SIZE=8,//数据位宽
parameter ADDR_SIZE=4//地址位宽
)
(
input wclken,wclk,
input [ADDR_SIZE-1:0] raddr, //ram读地址
input [ADDR_SIZE-1:0] waddr, //ram写地址
input [DATA_SIZE-1:0] wdata, //数据输入
input [DATA_SIZE-1:0] rdata //数据输出
);
localparam RAM_DEPTH=1<<ADDR_SIZE;
reg [DATA_SIZE-1:0] Men [RAM_DEPTH-1:0]; //ram的深度=2^ADDR_SIZE
always@(posedge wclk)begin
if (wclken)
Men[waddr]<=wdata;
end
assign rdata=Men[raddr];
endmodule
读空:
`timescale 1ns / 1ps
module rptr_empty
#(parameter ADDRSIZE=4
)
(
output reg rempty ,
output [ADDRSIZE-1:0] radddr ,
output reg [ADDRSIZE:0] rptr ,
input [ADDRSIZE:0] rq2_wptr ,
input rinc,rclk,rrst_n );
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext,rbinnnext;
wire rempty_val;
always@(posedge rclk or negedge rrst_n)begin
if (!rrst_n)begin
rbin<=0;
rptr<=0;
end
else begin
rbin<=rbinnnext;
rptr<=rgraynext;
end
end
//gray码计数逻辑
assign rbinnnext=!rempty?(rbin+rinc):rbin;
assign rgraynext=(rbinnnext>>1)^rbinnnext;
assign radddr=rbin[ADDRSIZE-1:0];
assign rempty_val=(rgraynext==rq2_wptr);
always@(posedge rclk or negedge rrst_n)
if (!rrst_n)
rempty<=1'b1;
else
rempty<=rempty_val;
endmodule
写满:
`timescale 1ns / 1ps
module wptr_full
#(
parameter ADDRSIZE=4
)
(
output reg wfull,
output [ADDRSIZE-1:0] waddr,
output reg [ADDRSIZE-1:0] wptr,
input [ADDRSIZE:0] wq2_rptr,
input winc,wclk,wrst_n );
reg[ADDRSIZE:0] wbin;
wire [ADDRSIZE:0]wgraynext,wbinnext;
wire wfull_val;
always@(posedge wclk or negedge wrst_n)begin
if (!wrst_n)begin
wbin<=0;
wptr<=0;
end
else begin
wbin<=wbinnext;
wptr<=wgraynext;
end
end
assign wbinnext=!wfull?wbin+winc:wbin;
assign wgraynext=(wbinnext>>1)^wbinnext;
assign waddr=wbin[ADDRSIZE-1:0];
assign wfull_val=(wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]});
always@(posedge wclk or negedge wrst_n)begin
if(!wrst_n)
wfull<=1'b0;
else
wfull<=wfull_val;
end
endmodule