Al rastrear algunos resultados de simulación extraños, descubrí que el uso de las tareas de bloqueo en los bordes del reloj era el culpable. Más específicamente, diferentes simuladores parecen tratarlos de manera diferente.
Toma el siguiente ejemplo:
module test;
reg clk, foo, bar;
initial
begin
clk = 0;
foo = 0;
bar = 0;
#10 $finish;
end
always
#1 clk = ~clk;
always @(posedge clk)
foo = ~foo;
always @(posedge clk)
bar <= foo;
endmodule
Ejecutar esto en Xilinx 'ISim proporciona la siguiente forma de onda:
Estoparecerazonable,aunquemepareceunpocoextrañoqueuncambioenfoo
enelbordedelrelojseadetectadoporlabarraenelmismobordedelreloj.
LasiguienteformadeondaseproducealejecutarelmismoejemploutilizandoIcarusVerilog:
Esto me parece completamente incorrecto. Un cambio en foo
de 0
a 1
no es recogido por bar
en el mismo borde del reloj. Sin embargo, un cambio en foo
de 1
a 0
hace que se recoja por bar
en el mismo borde del reloj (de lo contrario, bar
cambiaría a 1
en el segundo borde positivo del reloj, supongo).
Entonces, tengo dos preguntas:
- ¿Cuál, si alguno, de estos simuladores es correcto? En otras palabras, ¿cómo debe comportarse el código anterior?
- ¿Las diferencias entre los simuladores son algo de lo que siempre debemos estar conscientes? Si es así, ¿existen pautas para escribir código que sean deterministas en diferentes simuladores?
Nota: soy consciente del hecho de que usar asignaciones de bloqueo en always @
blocks no es una buena idea y que usar una asignación de no bloqueo en mi ejemplo proporciona resultados lógicos y consistentes. Estoy haciendo esta pregunta porque actualmente estoy depurando un código de simulación de terceros.