Verilog гонки с делителем часов с использованием флопов

Я сделал основной пример на игровой площадке eda, который получил. Допустим, у меня есть два часа 1x и 2x. 2x делится с 1x с использованием делителя флопа.

У меня есть два регистра а и б. А тактируется в 1х, б синхронизируется в 2х.

б значение выборки а.

Когда у нас нарастающий фронт в 1x и 2x тактах, b не принимает ожидаемое значение a, а принимает значение следующего цикла.

Это из-за этой схемы деления часов, если мы делаем деление, используя icgs и en, оно работает нормально. Но есть ли способ заставить его работать, используя эту схему делителей часов с флопами?

Ссылка на игровую площадку EDA: https://www.edaplayground.com/x/map

module race_test;

  logic clk1x = 0;
  logic clk2x = 0;

  always
    #5ns clk1x = !clk1x;

  int a, b;


  always @(posedge clk1x) begin
    a <= a+1;
    clk2x <= !clk2x;
  end

  // Problem here is that b will sample postpone value of a
  // clk2x is not triggering at the same time than clk1x but a bit later
  // This can be workaround by putting blocking assignment for clock divider
  always @(posedge clk2x) begin
    b <= a;
  end

  initial begin
    $dumpfile("test.vcd");
    $dumpvars;
    #1us
    $stop;
  end
endmodule

2 ответа

Решение

Делители цифровых часов представляют проблемы как с симуляцией, так и с физическим временем.

Неблокирующий оператор присваивания Verilog предполагает, что все читающие и записывающие одни и те же переменные синхронизируются с одним и тем же событием синхронизации. С помощью письма НБА clk2xсдвинуло чтение a в другое время дельты *, и, как вы обнаружили, a уже был обновлен.

В реальном оборудовании существуют значительные задержки распространения, которые обычно избегают этой ситуации. Тем не менее, вы используете тот же D-флоп, чтобы назначить clk2xтак что там также будут задержки распространения. Ты последний always блок теперь представляет проблему пересечения домена часов. Таким образом, в зависимости от перекоса между двумя часами, у вас все еще может быть состояние гонки.

Один из способов исправить это - использовать модуль тактового генератора с еще более высокой частотой

always #2.5ns clk = !clk;

always @(posedge clk) begin
       clk1x <= !clk1x;
       if (clk1x == 1)
         clk2x = !clk2x;

Конечно, вы решили эту проблему, но я думаю, что есть лучший способ. В книге говорится, что можно использовать блокировку присваивания, чтобы избежать гонки. Но блокировка присваивания вызывает ошибки при проверке Synopsys lint. Таким образом, один из способов избежать проблемы гонки без ошибки lint — использовать фиктивную логику, например

      wire [31:0] a_logic;
wire dummy_sel;
assign dummy_sel = 1'b0;
assign a_logic = dummy_sel ? ~a : a;
always @(posedge clk2x) begin
  b <= a_logic;
end
Другие вопросы по тегам