我在互联网上找到了以下代码,同时寻找良好的FIFO设计。来自链接SVN Code FIFO -Author Clifford E. Cummings。我做了一些研究,我无法弄清楚为什么设计中有三个指针?我可以阅读代码,但我错过了什么?
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)
if (!wrst_n) {wq2_rptr,wq1_rptr} <= 0;
else {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
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) {rq2_wptr,rq1_wptr} <= 0;
else {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr};
endmodule
答案 0 :(得分:9)
您在这里看到的是所谓的双列同步器。如前所述,这是一个异步FIFO。这意味着FIFO的读写侧不在同一个时钟域。
如您所知,触发器需要满足设置和保持时序要求才能正常工作。当您从一个时钟域驱动信号到另一个时钟域时,在一般情况下无法保证这一要求。
当你违反这些要求时,FF会进入所谓的“元稳定”状态,在这种情况下,一段时间内存在不确定性,然后(或多或少)随机地转为1或0.他们会这样做(而且这个很重要)在不到一个时钟周期内。
这就是为什么这两层失败的原因。第一个有机会进入稳定状态,但应该及时解决,以便第二组翻牌保持干净利落。
它本身并不足以跨时钟域传递多位值(地址指针)。如果同时有多个位发生变化,那么您无法确定另一侧的转换是否干净。所以在这些情况下你经常看到的是FIFO指针将通过灰色编码。这意味着计数器的每个增量一次最多改变一位。
e.g。而不是00 - &gt; 01 - &gt; 10 - &gt; 11 - &gt; 00 ...它将是00 - &gt; 01 - &gt; 11 - &gt; 10 - &gt; 00 ...
时钟域穿越是一个深刻而微妙的主题。即使是经验丰富的设计师也经常在没有仔细考虑的情况下弄乱他们。
BTW普通的Verilog仿真不会显示我刚才在零延迟模拟中所描述的内容。您需要使用实时模型进行带注释的SDF模拟。答案 1 :(得分:2)
在此示例中,地址通过shift register传递,以便将其延迟一个时钟周期。可能会有更多的“指针”,以便更多地延迟输出。
通常,如果您模拟设计并查看波形,则更容易理解发生了什么以及为什么会这样做。
此外,您可以查看以下一些优秀的FIFO实现: