Я использую приемник для АЦП в Verilog.Одна выборка получается после каждого 21-го тактового цикла.
Приемник генерирует сигналы управления, а также тактовые импульсы выборки для АЦП.АЦП отправляет данные обратно последовательно, но для учета задержки, он также отправляет обратно асимметричную копию тактовых импульсов выборки.Эти часы должны использоваться для синхронизации данных.
Код должен работать для нулевой задержки между двумя часами, а также для больших задержек.(Но задержка не будет больше, чем несколько тактов).
Я не знаю лучшего способа сделать это, потому что:
- Синтез запрещает, чтобы переменные записывались вразные блоки
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 и / или произвольного числа (возможно, от последнего цикла до сброса).