En general, debe evitar que las entradas de cualquier registro cambien en el mismo instante que el borde del reloj activo. En circuitos físicos, esto podría llevar a problemas de metastabilidad, y los simuladores dan resultados que son confusos en el mejor de los casos.
En este caso, las señales clk
, enable
y d
todas cambiaron en el mismo instante en el banco de pruebas, por lo que en el momento en que se evaluó el módulo Nbit_register
, "vio" el borde ascendente de clk
con enable
= 1 y d
= 0xFFFFFFFF, por lo que es lo que capturó.
En un circuito real, las señales enable
y d
probablemente habrían sido impulsadas por el reloj y habrían cambiado ligeramente después del borde del reloj. Incluso con una simulación de retraso cero, esto podría haber cambiado el orden de evaluación en el simulador, dando resultados muy diferentes.
Si aumenta ligeramente la primera demora en su banco de pruebas, evitará este problema y obtendrá resultados consistentes desde el simulador.
initial
begin
d = 32'hABCDEFF;
#5.1 enable = 1;
d = 32'hFFFFFFFF;
#10 enable = 0;
d = 32'hAAAAAAAA;
end
EDITAR: dices, " #5.1
y #10
funcionan, pero desafortunadamente @(posedge clock)
da el mismo resultado que #5
"
Sí, esto no es sorprendente. Si bien esto hace que enable
y d
dependan del reloj, todavía son efectivamente simultáneos, con el énfasis en "efectivamente".
Tenga en cuenta que mientras el simulador está haciendo todo lo posible para presentar la ilusión de que está evaluando eventos de cambio en paralelo, sigue siendo fundamentalmente un programa software (secuencial), y solo puede evaluar uno declaración a la vez, creando un ordenamiento implícito entre eventos que son supuestamente "simultáneos" (es decir, dentro del mismo paso de tiempo de simulación). El orden real que se produce es "dependiente de la implementación", es decir, un disparo de mierda.
En este caso, no es del todo sorprendente que el simulador procese los eventos en la siguiente secuencia:
- En
#5
clk
va de bajo a alto, lo que desencadena todos los eventos marcados en @posedge(clk)
.
- Las declaraciones en el módulo testbench se evalúan a continuación, estableciendo
enable
high y estableciendo d
en 0xFFFFFFFF.
- Luego, las declaraciones en el módulo
Nbit_register
se evalúan, estableciendo q
en 0xFFFFFFFF.
En la mayoría de las situaciones, este ordenamiento implícito de eventos no importa mucho. Pero cuando lo hace, debe ser consciente de ello y esforzarse para especificar explícitamente la secuencia de eventos. La mayoría de los simuladores tienen el concepto de "demoras de unidad" (un paso de tiempo de simulación) para puertas y FF, solo para asegurarse de que sus salidas no cambien simultáneamente con sus entradas. Esto es lo que permite que las estructuras como los registros de desplazamiento funcionen correctamente.
EDIT # 2: un problema relacionado es que usó instrucciones de asignación de bloqueo ( =
en lugar del <=
no bloqueante) en su banco de pruebas. Encontré esta referencia que hace un buen trabajo al explicar el significado de esto.
Básicamente, las asignaciones de bloqueo surten efecto inmediatamente, durante el paso de tiempo de la simulación actual, mientras que las asignaciones no bloqueadas no tienen efecto hasta después de el paso de tiempo, es decir, después de todo Se han evaluado los eventos de cambio activados para el paso de tiempo.
Si hubieras dicho
@(posedge clk) enable <= 1;
d <= 32'hFFFFFFFF;
entonces las asignaciones a enable
y d
no habrían tenido efecto hasta después de la asignación a q
se había procesado, y q
no habría cambiado hasta el segundo reloj borde.