我正在Verilog中实现ADC的接收器 . 在每个21个时钟周期之后获得一个样本 .
接收器为ADC生成控制信号以及占空比的采样时钟 . ADC按顺序发送数据,但为了说明延迟,它还会发回一个占空比的采样时钟 skew matched copy . 该时钟用于计时数据 .
该代码应该适用于两个时钟之间的零延迟以及更大的延迟 . (但延迟不会超过几个时钟周期) .
我不知道最好的方法,因为:
-
综合禁止将变量写入具有(可能)不同时钟的不同
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
changes 来识别这一点 . -
在全局复位时,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和/或任意数字(可能是从复位前的最后一个循环开始) .
1 回答
在时钟域之间使用NRZ信号是已知的方法 . 但是你没有真正的同步器 . 要安全地在时钟之间切换,您需要两个寄存器,第三个用于边沿检测:
在复位时,将所有寄存器设置为零 . 这样你一开始就没有'假'样本 . 在所有时钟域中进行相同的异步复位是最简单的 . 处理时钟域中的重置是一个相当(大)的主题,需要A4页面来简洁地解释 . 在你的情况下,21个时钟周期没有任何反应,所以你是安全的 .
另一种方法是使用标准异步FIFO在时钟域之间传输数据 . 如果您的时钟完全独立(这可能比另一个更慢或更快),这是最好的解决方案 . 我相信你可以在WWW上找到它的代码 . 另一个方向的异步FIFO可用于向ADC发送控制信息 .