我在 Verilog 中有一个基于本文的 FIFO 实现:http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
当将此 FIFO 用作 CDC FIFO 时,读取侧的时钟频率为 100Mhz,写入侧的时钟频率为 8Mhz,我遇到了时序问题。请注意,无论是行为、综合后还是实现后,该问题都无法在模拟中重现。只能在Vivado工具给出的时序报告中看到。
以下是与该问题相关的代码片段:
module CDC_FIFO #(
parameter DSIZE = 28,
parameter ASIZE = 10
)(
output [DSIZE-1:0] rdata,
output wfull,
output rempty,
output r_almost_empty,
input [DSIZE-1:0] wdata,
output [ASIZE:0] wr_diff,
input winc,
input wclk,
input wrst_n,
input rinc,
input rclk,
input rrst_n
);
wire [ASIZE-1:0] waddr, raddr;
wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr;
localparam AWAIT_RINC = 0;
localparam AWAIT_W_TO_R = 1;
reg [ASIZE:0] rptr_save = 0;
reg [1:0] state = AWAIT_RINC;
reg r_to_w = 0;
wire w_to_r;
always @(posedge rclk) begin
case (state)
AWAIT_RINC: begin
if (rinc && !w_to_r) begin
state <= AWAIT_W_TO_R;
r_to_w <= 1;
rptr_save <= rptr;
end
else begin
r_to_w <= 0;
end
end
AWAIT_W_TO_R: begin
if (w_to_r) begin
state <= AWAIT_RINC;
r_to_w <= 0;
end
end
default: ;
endcase
end
sync_r2w #(.ADDRSIZE(ASIZE)) sync_r2w
(
.r_to_w(r_to_w),
.w_to_r(w_to_r),
.wq2_rptr(wq2_rptr),
.rptr(rptr_save),
.wclk(wclk),
.wrst_n(wrst_n)
);
// Other instantiations of other modules
endmodule;
module sync_r2w #(
parameter ADDRSIZE = 4
)(
output reg w_to_r = 0,
input r_to_w,
output [ADDRSIZE:0] wq2_rptr,
input [ADDRSIZE:0] rptr,
input wclk, wrst_n
);
reg [ADDRSIZE:0] wq1_rptr = 0;
reg [ADDRSIZE:0] wq2_rptr_reg = 0;
assign wq2_rptr = wq2_rptr_reg;
always @(posedge wclk)
begin
if (!wrst_n)
begin
{ wq2_rptr_reg,wq1_rptr} <= 0;
end
else
begin
if (w_to_r || r_to_w) begin
{ wq2_rptr_reg,wq1_rptr} <= {wq1_rptr,rptr};
end
end
end
always @(posedge wclk) begin
if (!wrst_n) begin
w_to_r <= 0;
end
else begin
w_to_r <= r_to_w;
end
end
endmodule
当读指针必须通过sync_r2w模块传递到写域时,就会出现问题。因此,我向 CDC_FIFO 添加了一个状态机,以启动两者之间的某种握手,以确保 rptr_save 保持其值足够长的时间,以便写入端处理它。然而,这似乎并没有解决我的问题。
这是当前失败的关键路径的照片(rptr_save 的其中一个位)
我的逻辑到底有什么问题,仍然无法修复时序?在我看来,当前的 FSM 基本上是等待写入端断言它确实已获取 rptr_save 值,然后“关闭”握手条件以防止进一步更新。
谢谢
Vivado 时序分析器在所有具有约束的时钟域中的所有路径之间进行计时。如果您有跨域的路径,该工具会将其显示为失败路径。这样做的目的是向用户显示“嘿,您已经跨越了时钟域”。如果您仔细设计了处理 CDC 的逻辑(作为来自 Cummings 的 CDC 处理 fifo),则可以忽略该路径,因为您已经处理了 CDC 。静态时序的本意仅在同一时钟域内才有意义。
右键单击时序分析器中的失败路径并执行“设置错误路径”,将错误路径保存到时钟约束所在的同一约束文件中。再次运行工具,路径将不会被标记为一个错误。可能会弹出更多路径,如果您确信 RTL 逻辑可以处理 CDC,请对这些路径执行相同的操作。
更通用的方法是将时钟组设置为异步如何何时为什么将时钟组设置为异步。如果此链接还不够,请搜索“异步设置时钟组”以获取更多信息。 Xilinx 将为新设计设置约束的过程称为“基线” 这里有解释 Baselined-the-Design