关于异步FIFO的关键技术,有两个,一个是格雷码减小亚稳态,另一个是指针信号跨异步时钟域的传递。我在自己写异步FIFO的时候也很疑惑,地址指针在同步化的时候,肯定会产生至少两个周期的延迟,如果是从快时钟域到慢时钟域,快时域的地址指针并不能都被慢时域的时钟捕获,同步后的指针比起实际的指针延迟会更大。如果以此来产生fifo_empty和fifo_full 信号会非常不准器。
查找资料和仿真后发现,数字电路的世界真的很神奇,还有很多的东西需要去学习。非常巧妙,FIFO中的一个潜在的条件是write_ptr总是大于或者等于read_ptr;分为两种情况,写快读慢和写慢读快。
1.在写时钟大于读时钟时,产生fifo_empty信号,需要将write_ptr同步到读时钟域,写指针会有延时,可能比实际的写地址要小,如果不满足fifo_empty的产生条件,没问题。如果满足fifo_empty的触发条件,说明此时同步后的write_ptr == read_ptr,即实际的write_ptr >= read_ptr,最坏的情况就是write_ptr > read_ptr,像这种FIFO非空而产生空标志信号的情况称为“虚空”,但是也并不影响FIFO的功能。
2.在写时钟大于读时钟时,产生fifo_full信号,需要将read_ptr同步到写时钟域,读指针会有延时,可能比实际的读地址要小,如果不满足fifo_full的产生条件,没问题。如果满足fifo_full的触发条件,说明此时同步后的read_ptr == write_ptr - fifo_depth,即实际的read_ptr >= read_ptr - fifo_depth,最坏的情况就是read_ptr > read_ptr - fifo_depth,像这种FIFO非满而产生满标志信号的情况称为“虚满”,但是也并不影响FIFO的功能。
写慢读快的情况也同上,并没有大的差异,不再分析。
关于格雷码减小亚稳态,如果读写时钟差距过大,从快时钟域同步到慢时钟域的信号,时钟捕获的相邻两个数据变化并不是只有一个bit位的改变,可能导致格雷码失去原来的意义,嗯,目前的理解是这样。
————————————————
版权声明:本文为CSDN博主「alangaixiaoxiao」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/alangaixiaoxiao/article/details/81432144
module fifo
( input wclk ,
rclk ,
w_en ,
r_en ,
rst_n,
input [Wsize-1:0] wdata,
output fifo_full ,
fifo_empty,
output [Wsize-1:0] rdata
);
parameter Wsize=8,Dsize=32;
wire fifo_empty_vail,fifo_full_vail;
//定义变量
reg fifo_empty,fifo_full;
reg [Wsize-1:0] mem [Dsize-1:0];
wire [Dsize-1:0] w_addr,r_addr;
reg [Dsize:0] w_bin,r_bin;
wire [Dsize:0] r_bin_next,w_bin_next;
reg [Dsize:0] r_gray,w_gray;
wire [Dsize:0] r_gray_next,w_gray_next;
reg [Dsize:0] w_2_r_gray1,w_2_r_gray2,r_2_w_gray1,r_2_w_gray2;
//定义双端口RAM
assign rdata = mem[r_addr];
always@(posedge wclk)
if (w_en && !fifo_full) mem[w_addr] <= wdata;
//产生读地址r_addr,同时把二进制转化为格雷码
always@(posedge rclk or negedge rst_n)
if (!rst_n) {r_bin,r_gray} <= 0;
else
{r_bin,r_gray} <= {r_bin_next,r_gray_next};
assign r_addr = r_bin[Dsize-1:0];
assign r_bin_next = r_bin + (r_en && !fifo_empty);
assign r_gray_next = r_bin ^ (r_bin >> 1);
//产生写地址
always@(posedge wclk or negedge rst_n)
if (!rst_n) {w_bin,w_gray} <= 0;
else
{w_bin,w_gray} <= {w_bin_next,w_gray_next};
assign w_addr = w_bin[Dsize-1:0];
assign w_bin_next = w_bin + (w_en && !fifo_empty);
assign w_gray_next = w_bin ^ (w_bin >> 1);
//同步写地址到读时钟域,两个触发器同步
always@(posedge rclk or negedge rst_n)
begin
if(!rst_n)
{w_2_r_gray2,w_2_r_gray1} <= 0;
else
{w_2_r_gray2,w_2_r_gray1} <= {w_2_r_gray1,w_gray};
end
//同步读地址到写时钟域,两个触发器同步
always@(posedge rclk or negedge rst_n)
begin
if(!rst_n)
{r_2_w_gray2,r_2_w_gray1} <= 0;
else·
{r_2_w_gray2,r_2_w_gray1} <= {r_2_w_gray1,r_gray};
end
//产生读空信号
assign fifo_empty_vail = (w_2_r_gray2 == r_gray_next);
always@(posedge rclk or negedge rst_n)
begin
if(!rst_n)
fifo_empty <= 1'b0;
else
fifo_empty <= fifo_empty_vail;
end
//产生写满信号
//assign r_2_w_gray2_reg = { ~ r_2_w_gray2 [Dsize:Dsize-1] , r_2_w_gray2 [Dsize-2:0]};
assign fifo_full_vail = (({ ~ r_2_w_gray2 [Dsize:Dsize-1] , r_2_w_gray2 [Dsize-2:0]}) == w_gray_next)?1:0;
always@(posedge wclk or negedge rst_n)
begin
if(!rst_n)
fifo_full <= 1'b0;
else
fifo_full <= fifo_full_vail;
end
endmodule