【发布时间】:2018-06-25 21:11:43
【问题描述】:
我正在为 Verilog 中的 ADC 实现接收器。每第 21 个时钟周期获取一个样本。
接收器生成控制信号以及 ADC 的占空比采样时钟。 ADC 按顺序发回数据,但为了解决延迟问题,它还发回占空比采样时钟的偏差匹配副本。该时钟用于记录数据。
代码应该适用于两个时钟之间的零延迟以及更大的延迟。 (但延迟不会大于几个时钟周期)。
我不知道最好的方法是因为:
- 综合禁止将变量写入具有(可能)不同时钟的不同
always @(posedge...)块中。 - 为数据提供时钟的部分没有真正的时钟(它是占空比的!),因此它无法自行维持状态。它需要以某种方式从控制 FSM 获取它处于哪个周期的信息
- 读取采样值后,需要将其传输回原始的未倾斜时钟域以进行进一步处理。
这显示了我的方法的一个最小示例:
// 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 状态计数器变量
state和nextState - 如果状态为零,则开始新的采样间隔。接收器逻辑(见下文)将通过
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