En realidad es bastante fácil. Primero, todos los bucles se desenrollan (el código no se puede sintetizar si no se pueden desenrollar) y todas las funciones y tareas están en línea. Después de eso, solo te quedan las declaraciones y asignaciones de caso y caso (las declaraciones de caso y si son esencialmente las mismas). Las declaraciones if se pueden transformar en multiplexores con las condiciones en las entradas seleccionadas. Por ejemplo
if (cond1) begin
if (cond2)
foobar <= expr1;
end else
foobar <= expr2;
es lo mismo que
foobar <= cond1 ? (cond2 ? expr1 : foobar) : expr2;
(observe cómo se usa el valor antiguo de foobar en el caso cond1 & &! cond2). Por supuesto, hay un par de pasos adicionales para manejar las asignaciones de bloqueo, reinicios asíncronos, etc. Pero esta es la idea básica.
Recomiendo usar una herramienta de síntesis en fragmentos de código simples para aprender más sobre la relación entre su código y el circuito generado. Por supuesto, puede usar cualquier herramienta que esté usando, pero no puedo escribir una respuesta a su pregunta sin mencionar Yosys , La herramienta de síntesis de Verilog que escribí. El comando yosys -p "proc;; show" test.v
traducirá el módulo en test.v a una lista de red RTL y mostrará el circuito (necesita graphviz y xdot instalados). Esto, por ejemplo, transformará esto en this .
Actualización: Farhad Yusufali preguntó en los comentarios sobre el bloqueo de tareas.
Considere el siguiente código:
always @(posedge clk) begin
a = x;
if (b) begin
c <= c + a;
a = y;
end
d <= a;
a = a + 1;
e <= a;
end
ahora podemos escribir esto como dos bloques siempre usando solo una asignación sin bloqueo como esta:
always @* begin
a_1 <= x;
if (b)
a_2 <= y;
else
a_2 <= a_1;
a_3 <= a_2 + 1;
end
always @(posedge clk) begin
if (b)
c <= c + a_1;
d <= a_2;
e <= a_3;
end
I.e. cuando se usa a
en el lado izquierdo de una asignación, movemos la asignación al bloque asíncrono y cambiamos a
a a_<index>
. Siempre que se use a
en el lado derecho, lo reemplazamos con el último a_<index>
que creamos. Cuando nos bifurcamos (como en if (b)
) y creamos un nuevo a_<index>
en una de las ramas, entonces debe asegurarse de que la última asignación a una versión de a
en cada rama vaya al mismo índice, agregar asignaciones adicionales según sea necesario (como la asignación de a_2
en la rama else).
Esta notación de reemplazar una variable con múltiples variables con índices de esta manera es una variación de lo que se conoce como formulario SSA en la comunidad de diseño del compilador.