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