接着上一个章节,继续介绍FIFO。异步FIFO读/写指针在不同的时钟域产生,如何根据异步指针信号对FIFO的空/满状态进行正确的判断则成为重点。
二进制指针由地址位和状态位组成,地址位随相应的操作递增,指针由内存的以后w位置返回到初始位置时状态位取反。
eg:当读指针从0111到1000变化时,指针的所有位都发生了变换,如果写指针刚好在读指针变换时采样,写得到的读指针有可能是从0000到1111的任意数值。
为了能够在不同时钟域内直接同步指针,可采用格雷码的编码方式,也就是指针每次移动只变化一位,这样就避免额由于指针多位同时变化而无法直接同步的问题。
异步FIFO的FPGA实现,先以在读时钟域下产生状态为例,分为以下几个步骤:
1.将写指针(二进制)转化为格雷码
2.将写指针(格雷码)通过跨时钟转换到读时钟域
3.将写格雷码指针转换为写二进制指针
4.写指针与读指针通过二进制进行比较产生状态
代码如下,写时钟域的状态产生同理而言
// Current read pointer
always @(posedge clkb or posedge arstb )
if (arstb )
cur_rptr <= {C_AW+1{1'b0}};
else
cur_rptr <= next_rptr;
// Change binary read pointer to gray code
generate
// genvar i;
for (i=0;i<C_AW+1;i=i+1)
begin :RBIN2GRAY_GEN
wire m;
assign m = (i == C_AW) ? 1'b0 : cur_rptr[(i+1)%(C_AW+1)];
always @(posedge clkb or posedge arstb )
if (arstb )
gray_rptr[i] <= 1'b0;
else
gray_rptr[i] <= cur_rptr[i] ^ m;
end
endgenerate
// Write pointer to Read clock domain
always @(posedge clkb or posedge arstb)
if (arstb )
begin
gray_awptr <= {C_AW+1{1'b0}};
gray_swptr <= {C_AW+1{1'b0}};
end
else
begin
gray_awptr <= gray_wptr ;
gray_swptr <= gray_awptr ;
end
// Convert the Gray pointer to binary
generate
// genvar i;
for (i=0;i<C_AW+1;i=i+1)
begin :WGRAY2BIN_GEN
always @(posedge clkb or posedge arstb)
if (arstb )
bin_wptr[i] <= 1'b0;
else
bin_wptr[i] <= ^gray_swptr[C_AW:i];
end
endgenerate
// empty flag
always @(posedge clkb or posedge arstb)
if (arstb )
rempty <= 1'b1;
else
rempty <= (next_rptr == bin_wptr);//(rdiff == 0);
// almost empty flag
always @(posedge clkb or posedge arstb)
if (arstb )
arempty <= 1'b1;
else
arempty <= (rdiff < (C_HAE + 1));
always @(posedge clkb or posedge arstb)
if (arstb )
rleft <= {C_AW+1{1'b0}};
else
rleft <= rdiff ;
always @(posedge clkb or posedge arstb)
if (arstb )
uflw <= 1'b0;
else
uflw <= rempty & re;