Tiempo de software vs tiempo de hardware
En un programa, el tiempo avanza instrucción a instrucción. La CPU termina la línea 1, luego ejecuta la línea 2. Si quieres que dos cosas pasen a la vez, recurres a hilos, async o a una segunda máquina.
En hardware, todo sucede a la vez. Un circuito no se turna. El sumador siempre está sumando, el multiplexor siempre está seleccionando, el flip-flop siempre está observando el reloj. No hay contador de programa. No hay "línea actual".
Verilog tiene que describir ese mundo en texto. La forma en que lo hace es la fuente de toda la confusión en este capítulo.
Dos capas de concurrencia
Cuando lees un módulo Verilog, estás mirando dos cosas distintas a la vez:
- La descripción estática del circuito. Wires, registros, instancias de puertas, instancias de submódulos, asignaciones continuas. Todos existen simultáneamente. Su orden en el archivo no importa.
- Bloques procedurales -
initialyalways- que parecen pequeños programas que el simulador recorre. Dentro de uno de estos bloques, las sentencias sí se ejecutan en algún tipo de orden. Pero varios bloquesalwayspueden estar activos a la vez, cada uno en su propio pequeño hilo de tiempo simulado.
module example(input wire a, input wire b, output wire y, output wire z);
assign y = a & b; // existe en todo momento
assign z = a | b; // también existe en todo momento, en paralelo
always @(posedge a) begin
// un reactor "siempre encendido" separado que se despierta en cada
// flanco de subida de `a`
end
endmodule
Las dos líneas assign no son una secuencia. Describen dos piezas de lógica combinacional que la herramienta de síntesis puede disponer una al lado de la otra. El bloque always es una tercera cosa sucediendo en paralelo.
Qué significa "señal"
En software, una variable mantiene un valor hasta que la cambias. En Verilog, una señal mantiene un valor continuamente - y en cada instante depende de lo que la esté excitando.
Un wire se excita desde fuera (un assign, el puerto de salida de un submódulo, una conexión inout). Un reg se excita desde dentro de un bloque procedural. Ambos tienen un valor en todo momento. No existe lo de "no inicializado" en el sentido que tiene en C - las señales están o bien excitadas a un valor definido, al x especial de desconocido o a alta impedancia z. Cubriremos los dos últimos en Valores X y Z.
Los relojes lo cambian todo
En cuanto incorporas un reloj, el tiempo empieza a importar. Un flip-flop es una pieza diminuta de hardware que captura su valor de entrada en el flanco de subida (o de bajada) de un reloj y lo mantiene hasta el siguiente flanco. Lo que te permite construir contadores, máquinas de estados, pipelines - cualquier cosa que tenga memoria - es el reloj.
Fíjate en q <= d en vez de q = d. Eso es una asignación no bloqueante - el caballo de batalla de la lógica sincronizada por reloj. Dice "en el próximo flanco de reloj, programa que q se convierta en lo que d sea ahora". Profundizaremos en las reglas en Blocking vs Non-blocking; por ahora, reconoce que la asignación no está fingiendo ser una sentencia de software.
RTL: el modelo mental de transferencia entre registros
La mayor parte del Verilog sintetizable se escribe en un estilo llamado Register Transfer Level, o RTL. La idea es simple:
- Decide qué estado necesita tu circuito (los registros).
- Para cada registro, describe dos cosas: qué lo resetea y qué lógica combinacional calcula su siguiente valor.
- Conecta las salidas de la lógica combinacional a las entradas del registro y tienes un circuito funcionando.
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
always @(*) begin
case (state)
IDLE: next_state = start ? RUNNING : IDLE;
RUNNING: next_state = done ? IDLE : RUNNING;
default: next_state = IDLE;
endcase
end
Eso es una máquina de dos estados. El primer bloque always es sincronizado por reloj - es un flip-flop. El segundo es puramente combinacional - es solo una ecuación. Casi todas las máquinas de estados, contadores y pipelines que vas a escribir siguen esa forma.
Los hábitos que te dejan atascado
Si vienes del software, aquí tienes una lista corta de hábitos que conviene dejar atrás:
- "Las variables se actualizan cuando les asigno." No en un flanco de reloj - una asignación no bloqueante programa la actualización para el final del paso de tiempo.
- "Las sentencias se ejecutan de arriba abajo." Fuera de los bloques procedurales, no. Dentro de un bloque sincronizado por reloj, más o menos - pero bloqueante vs no bloqueante cambia lo que "en orden" significa realmente.
- "Reservo esto cuando lo necesite." El hardware no reserva. Cada registro y puerta tiene que existir en tiempo de síntesis. El tamaño de cada vector está fijado.
- "Este loop es rápido - es solo una operación." Un loop
foren Verilog sintetizable se desenrolla en hardware paralelo. Un loop de 64 iteraciones se convierte en 64 copias del cuerpo, no en una instrucción de CPU que se ejecuta 64 veces.
No estás aprendiendo a escribir un programa. Estás aprendiendo a describir un circuito. El instinto de leer de arriba abajo es exactamente el equivocado y lleva un tiempo reentrenarlo.
Qué viene a continuación
Los siguientes docs recorren la obtención de una cadena de herramientas local (opcional - el editor del navegador está bien), y luego escribir tu primer módulo desde cero. Volveremos al contraste hardware-vs-software cada vez que algo parezca raro, porque la mayoría de las partes "raras" tienen la misma raíz: esto no es software.
Preguntas frecuentes
¿Cuál es la diferencia entre hardware y software en términos de Verilog?
El software es una secuencia de instrucciones ejecutadas por una CPU, una tras otra. El hardware - lo que Verilog describe - es una red de puertas y wires que llevan señales al mismo tiempo. Un archivo Verilog describe esa red. El simulador imita el comportamiento paralelo; una herramienta de síntesis lo convierte en silicio real.
¿El código Verilog se ejecuta de arriba abajo como C?
No - y tratarlo así es el error más común de los principiantes. Las sentencias de nivel superior (assigns, instancias de módulo, bloques always) 'existen' todas a la vez. Solo dentro de los bloques procedurales - initial y always - sucede algo parecido a la ejecución secuencial, e incluso ahí, las asignaciones no bloqueantes rompen la ilusión.
¿Qué significa 'concurrente' en Verilog?
Significa que varias sentencias describen partes de un circuito que operan todas al mismo tiempo. Dos sentencias assign en el mismo módulo no son 'línea 1 luego línea 2' - son dos piezas de hardware funcionando en paralelo, ambas reaccionando a sus entradas continuamente.
¿Qué es el diseño RTL?
RTL significa Register Transfer Level. Es un estilo de escribir Verilog donde describes el circuito como registros (flip-flops) y la lógica combinacional que calcula sus siguientes valores. La mayor parte del Verilog sintetizable es RTL. El nivel superior es el comportamental; el nivel inferior es a nivel de puertas.