Правильный способ синхронизации двух конечных автоматов с немного искаженными часами в Verilog - PullRequest
0 голосов
/ 26 июня 2018

Я использую приемник для АЦП в Verilog.Одна выборка получается после каждого 21-го тактового цикла.

Приемник генерирует сигналы управления, а также тактовые импульсы выборки для АЦП.АЦП отправляет данные обратно последовательно, но для учета задержки, он также отправляет обратно асимметричную копию тактовых импульсов выборки.Эти часы должны использоваться для синхронизации данных.

Код должен работать для нулевой задержки между двумя часами, а также для больших задержек.(Но задержка не будет больше, чем несколько тактов).

Я не знаю лучшего способа сделать это, потому что:

  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 состояния 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 и / или произвольного числа (возможно, от последнего цикла до сброса).

1 Ответ

0 голосов
/ 26 июня 2018

Использование сигнала 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 
   ....

При сбросе вы устанавливаете все регистры на ноль.Таким образом, у вас нет «ложной» выборки в начале.Проще всего иметь одинаковый асинхронный сброс во всех доменах часов.Работа с перезагрузками в доменах часов является довольно (большой) темой, которая кратко объяснит страницу А4.В вашем случае ничего не происходит в течение 21 тактового цикла, поэтому вы в безопасности.

Альтернативой является использование стандартного асинхронного FIFO для передачи данных между тактовыми доменами.Это лучшее решение, если ваши часы полностью независимы (то есть часы могут быть медленнее или быстрее других). Я уверен, что вы можете найти код для них в WWW.Асинхронный FIFO в другом направлении может использоваться для отправки управляющей информации на ваш АЦП.

...