【问题标题】:Proper way to synchronize two state machines with slightly skewed clock in Verilog在 Verilog 中同步两个状态机与稍微倾斜时钟的正确方法
【发布时间】:2018-06-25 21:11:43
【问题描述】:

我正在为 Verilog 中的 ADC 实现接收器。每第 21 个时钟周期获取一个样本。

接收器生成控制信号以及 ADC 的占空比采样时钟。 ADC 按顺序发回数据,但为了解决延迟问题,它还发回占空比采样时钟的偏差匹配副本。该时钟用于记录数据。

代码应该适用于两个时钟之间的零延迟以及更大的延迟。 (但延迟不会大于几个时钟周期)。

我不知道最好的方法是因为:

  1. 综合禁止将变量写入具有(可能)不同时钟的不同always @(posedge...) 块中。
  2. 为数据提供时钟的部分没有真正的时钟(它是占空比的!),因此它无法自行维持状态。它需要以某种方式从控制 FSM 获取它处于哪个周期的信息
  3. 读取采样值后,需要将其传输回原始的未倾斜时钟域以进行进一步处理。

这显示了我的方法的一个最小示例:

// 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
  • 状态只是实现为 21 状态计数器变量 statenextState
  • 如果状态为零,则开始新的采样间隔。接收器逻辑(见下文)将通过 sync_cnv 变化这一事实来识别这一点。
  • 在全局复位时,FSM 进入已知状态。此外,reset_rx 设置为 1 以通知接收器逻辑(见下文)有关复位。它保持为 1,直到被确认 (reset_rx_ack)。

接收逻辑:

reg [14:0] counter = 0; // just for dummy data. Increments every sample interval
reg sampling_done = 0; // raised when sampling is done
reg [15:0] cbuf; // holds data during data reception

always @(posedge rxclk) begin
    if ( reset_rx == 1) begin
        reset_rx_ack <= 1;
        sync_sdo <= sync_cnv;
        counter <= 0;
    end else begin
        reset_rx_ack <= 0;

        if (sync_cnv != sync_sdo) begin
            // A new sampling interval begins

            sync_sdo <= sync_cnv;

            counter <= counter + 1;
            sampling_done <= 1;
            data <= cbuf;
        end else begin
            // normal operation
            cbuf <= counter;
            sampling_done <= 0;
        end
    end
end

// synchronize "sampling_done" back to the unskewed clock.
// if data_valid, then data can be read the next cycle of clk
always @(posedge clk) begin
    r1 <= sampling_done;    // first stage of 2-stage synchronizer
    r2 <= r1;               // second stage of 2-stage synchronizer
    r3 <= r2;               // edge detector memory
end

assign data_valid = (r2 && !r3);   // pulse on rising edge

此代码在模拟中完美运行(有和没有偏差)。它也适用于 FPGA大部分时间。但是,重置后的数据值是不可预测的:大多数数据以 0 开头(如预期的那样),但有时以 1 和/或任意数字(可能来自重置前的最后一个周期)。

【问题讨论】:

  • 为什么需要在两个 FSM 之间共享状态?采样时钟是一个采样时钟。你说它“已经占空比”,但是你用它来计时计数器和其他各种东西。这太复杂了 - 重新开始,如果您有问题,请给我们 ADC 部件号。
  • 你是对的,它太复杂了。我重新开始,现在开始工作了。关键不是共享状态,但我只是将这些位计时到一个 FIFO 寄存器并读取最后 16 位。以前我认为我需要一个状态,因为如果重置发生在中间,比如第 5 位,会发生什么?我想我需要知道这个词什么时候结束。事实证明这并不重要,因为 FIFO 会处理它:(不完整的)5 位只是再次移出。
  • 那很好。作为练习,您应该尝试摆脱 FIFO,这是(大量)矫枉过正。将传入的数据加载到rxclk 上的寄存器中。现在,假设rxclk 实际上是 1/16 频率,使用您的同步电路(可能有 3 个阶段)将rxclk 采样到clk 域,并为clk 生成一个 1 周期负载使能脉冲领域。现在将采样的输入数据重新采样到 clk 域中,您就完成了,没有 FIFO。

标签: verilog


【解决方案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 
   ....

在重置时,您将所有寄存器设置为零。这样一开始就没有“假”样本。在所有时钟域中进行相同的异步复位是最简单的。处理时钟域中的重置是一个相当(大)的主题,需要 A4 页面来简洁地解释。在您的情况下,21 个时钟周期内什么都没有发生,所以您是安全的。

替代方案是使用标准异步 FIFO 在时钟域之间传输数据。如果您的时钟完全独立(即时钟可以比另一个时钟更慢或更快),这是最好的解决方案 我相信你可以在 WWW 上找到它的代码。 另一个方向的异步 FIFO 可用于向您的 ADC 发送控制信息。

【讨论】:

  • 为什么说没有“真正的同步”?见最后一个always。这是一个两阶段同步,它对异步sampling_done 进行采样,在第二阶段的输出上具有逻辑以产生data_valid。当然,data_valid 是不用的,反正 2 个阶段还不够。
  • 抱歉,我可能没有 100% 理解您的代码,但似乎 sync_cnv 没有遵守规则。
  • 感谢您的回答。尽管对我的问题没有帮助,但它对我的一般理解仍然有用。正如我在上面的评论中所写(并且由@​​EML 正确假设),我不需要第二个“时钟域”中的状态。我只是将这些位计时到一个 FIFO 中,没有状态。当主逻辑采样周期结束时,我读取了 FIFO。然后我只读出最后 16 位。
  • 答案不完整。适当的时钟域交叉还需要约束以及供应商特定属性,以确保综合工具不会通过优化破坏 CDC。
猜你喜欢
  • 1970-01-01
  • 2013-09-21
  • 2019-07-23
  • 1970-01-01
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
  • 2023-02-22
  • 1970-01-01
相关资源
最近更新 更多