在Verilog中用稍微倾斜的时钟同步两个状态机的正确方法
我正在Verilog中为ADC实现一个接收器。每21个时钟周期后获得一个样本 接收器为ADC生成控制信号和占空比采样时钟。ADC按顺序发回数据,但为了考虑延迟,它还发回占空比采样时钟的倾斜匹配副本。该时钟用于对数据进行时钟输入 该代码应适用于两个时钟之间的零延迟以及更大的延迟。(但延迟不会超过几个时钟周期) 我不知道最好的方法是什么,因为:在Verilog中用稍微倾斜的时钟同步两个状态机的正确方法,verilog,Verilog,我正在Verilog中为ADC实现一个接收器。每21个时钟周期后获得一个样本 接收器为ADC生成控制信号和占空比采样时钟。ADC按顺序发回数据,但为了考虑延迟,它还发回占空比采样时钟的倾斜匹配副本。该时钟用于对数据进行时钟输入 该代码应适用于两个时钟之间的零延迟以及更大的延迟。(但延迟不会超过几个时钟周期) 我不知道最好的方法是什么,因为: 综合禁止将变量写入具有(可能)不同时钟的不同始终@(posedge…块中 数据中的时钟部分没有真正的时钟(它是占空比的!),因此它无法自行维持状态。它需要以
始终@(posedge…
块中// Used to synchronize state between domains
reg sync_cnv = 0; // toggled by TX side when new sampling cycle starts
reg sync_sdo = 0; // synchronized by the RX side
reg reset_rx = 0; // Notify RX side of a global reset
reg reset_rx_ack = 0; // acknowledgement thereof
reg [4:0] state = 0;
reg [4:0] nextState = 0;
always @(posedge clk) begin
if (reset == 1) begin // global reset
state <= 0;
sync_cnv <= 0;
reset_rx <= 1;
end else begin
state <= nextState;
// new sampling cycle starts. Inform RX logic
if (state == 0) begin
sync_cnv <= ~sync_cnv;
end
// If RX acknowledges the reset, we can turn if off again
if (reset_rx_ack == 1) begin
reset_rx <= 0;
end
end
end
// Normally, would generate all kinds of status/control signal for the ADC here
always @(*) begin
if (state == 20) begin
nextState = 0;
end else begin
nextState = state + 1;
end
end
这段代码在模拟中完美地工作(有无扭曲)。它大部分时间也在FPGA上工作。但是,重置后的数据值是不可预测的:大多数数据以0开始(如预期),但有时以1和或任意数字开始(可能是重置前的最后一个周期)。在时钟域之间使用NRZ信号是已知的方法。但是你没有一个真正的同步器。为了安全地在时钟之间切换,需要两个寄存器和第三个用于边缘检测的寄存器:
// Clock domain 1:
nrz <= ~nrz;
// Clock domain 2:
reg nrz_meta,nrz_sync,nrz_old;
....
nrz_meta <= nrz;
nrz_sync <= nrz_meta;
// nrz_sync is the signal you can safely use!
// Do NOT use nrz_sync ^ nrz_meta, it is not reliable!
nrz_old <= nrz_sync; // required to 'find' an edge
if (nrz_old ^ nrz_sync)
begin
// Process data
....
//时钟域1:
nrz你为什么说没有“真正的同步机”?查看最后一个始终。这是一个两级同步器,它对异步采样完成
,在第二级的输出上使用逻辑来产生数据有效
。当然,data\u valid
未使用,而且2个阶段实际上还不够。抱歉,也许我无法100%理解您的代码,但sync\u cnv似乎没有遵守规则。谢谢您的回答。尽管对我的问题没有帮助,但它对我的总体理解仍然有用。正如我在上面的评论中所写的(由@EML正确假设),我不需要第二个“时钟域”中的状态。我只是将位时钟输入FIFO,没有状态。当主逻辑采样周期结束时,我读取FIFO。然后我读出最后16位,答案不完整。正确的时钟域交叉还需要约束以及特定于供应商的属性,以确保合成工具不会通过优化破坏CDC。为什么需要在两个FSM之间共享状态?采样时钟就是一个采样时钟。你说它“已经循环工作了”,但你用它来计时计数器和其他各种东西。这太复杂了-重新开始,如果您有问题,请给我们ADC零件号。您是对的,它太复杂了。我重新开始,现在开始工作了。密钥不是共享状态,但我只是将这些位输入FIFO寄存器并读取最后16位。以前我认为我需要一个状态,因为如果重置发生在中间,比如比特5,会发生什么?我想我需要知道这个词什么时候结束。事实证明这并不重要,因为FIFO会处理它:5位(不完整的)被再次移出。这很好。作为练习,你应该试着摆脱FIFO,这是(大规模的)过度使用。将传入数据加载到rxclk
上的寄存器中。现在,假设rxclk
实际上是1/16频率,使用同步电路(可能有3级)将rxclk
采样到clk
域,并为clk
域生成1周负载启用脉冲。现在,将采样的输入数据重新采样到clk
域中,就完成了,无需FIFO。
// Clock domain 1:
nrz <= ~nrz;
// Clock domain 2:
reg nrz_meta,nrz_sync,nrz_old;
....
nrz_meta <= nrz;
nrz_sync <= nrz_meta;
// nrz_sync is the signal you can safely use!
// Do NOT use nrz_sync ^ nrz_meta, it is not reliable!
nrz_old <= nrz_sync; // required to 'find' an edge
if (nrz_old ^ nrz_sync)
begin
// Process data
....