El bloque fundamental: un flip-flop D
Todo en diseño síncrono vuelve a una pequeña pieza de hardware: el flip-flop D. Tiene una entrada de reloj, una entrada de datos y una salida de datos. En cada flanco de subida del reloj, captura el valor de D y lo mantiene hasta el siguiente flanco. Eso es todo.
En Verilog:
always @(posedge clk) begin
q <= d;
end
Tres líneas, un flip-flop. El <= es no bloqueante, que es exactamente el comportamiento de los flip-flops reales (cubierto en Blocking vs Non-blocking). La sensibilidad posedge clk es lo que lo hace secuencial.
Apila muchos de estos con lógica combinacional entre medio y tienes cualquier circuito síncrono que quieras construir.
Un registro con reset
Los diseños reales siempre necesitan un reset - una forma de poner el sistema en un estado conocido al encender o bajo demanda:
Ese es un reset síncrono - la condición de reset se evalúa en un flanco de reloj como cualquier otra entrada. El sintetizador produce un flip-flop con un mux 2 a 1 en su entrada de datos: cuando reset está en alto, el mux alimenta cero; si no, alimenta d.
Para la mayoría de los diseños, el reset síncrono es la elección correcta. Tiene timing más simple, va bien con FPGAs, y la señal de reset puede venir de cualquier parte - no necesita su propia fuente alineada con el reloj.
Un registro con enable
A menudo quieres un registro que solo se actualice cuando algo se lo dice. Usa un if dentro del bloque sincronizado, y confía en el comportamiento implícito "mantener el valor anterior" del else ausente:
Este es el "registro con carga habilitada" canónico. Aparece por todas partes - registros de configuración, etapas de pipeline que solo avanzan cuando el downstream está listo, contadores que pausan y reanudan. La omisión del else final es deliberada y segura dentro de un bloque sincronizado: un flip-flop ya recuerda su valor anterior, así que "no hacer nada" significa "mantener".
En un bloque combinacional el mismo código inferiría un latch. Reglas distintas, misma sintaxis.
Un contador
Un contador es un registro cuyo siguiente valor es su valor actual más uno:
El contador se incrementa en cada ciclo de reloj cuando enable está en alto. Tras 16 ciclos vuelve a 0 (porque declaramos 4 bits y 15 + 1 desborda a 0). Ese wrap-around es intrínseco a la aritmética de N bits y es exactamente cómo se comporta un contador hardware real.
Un shift register
Apilar flip-flops te da un shift register. El truco es hacer todos los desplazamientos en una única sentencia no bloqueante:
El cuerpo es out <= {out[WIDTH-2:0], in} - concatena los bits inferiores del out actual con el nuevo in, y asigna todo. Como es no bloqueante, el RHS lee el out antiguo antes de que el LHS se actualice. El efecto es un shift limpio de N bits en un único ciclo de reloj.
Este es el patrón de shift register en su forma más pequeña. Se generaliza a LFSRs, transmisores serie, deserializadores - cualquier diseño donde los datos se mueven a través de una cadena de flip-flops en cada reloj.
Pipelines
Un pipeline es una cadena de registros separados por lógica combinacional. Cada etapa procesa datos de la etapa anterior y alimenta a la siguiente:
Tres etapas, tres ciclos de latencia, pero un resultado nuevo cada ciclo una vez que el pipeline está lleno. La latencia es de 3 ciclos porque los datos pasan por tres flip-flops; el throughput es de 1 operación por reloj porque las tres etapas trabajan simultáneamente con entradas distintas.
Así es como los diseños de alto rendimiento alcanzan sus objetivos de throughput: mantén las etapas cortas, segmenta más profundamente y deja que el paralelismo haga el trabajo.
Reset asíncrono (cuando lo necesites)
A veces no puedes esperar a un flanco de reloj para activar el reset - el chip se está apagando, el reloj está bloqueado, un watchdog externo está tirando de la línea. En esos casos:
always @(posedge clk or negedge reset_n) begin
if (~reset_n) q <= 0;
else q <= d;
end
La lista de sensibilidad ahora tiene tanto un flanco de reloj como un flanco de reset. El flip-flop responde inmediatamente a cualquiera. reset_n por convención es activo en bajo (activado cuando está en bajo), por eso el test es ~reset_n.
El reset asíncrono tiene contrapartidas: análisis de tiempo más difícil, puede causar metaestabilidad si no se maneja con cuidado al desactivarse, no portable entre todas las arquitecturas FPGA. Usa reset síncrono por defecto, async solo cuando el diseño lo requiera.
Qué viene a continuación
Ya puedes construir cualquier datapath síncrono. El siguiente doc - Finite State Machines - junta un registro sincronizado con una sentencia case para producir el modismo estándar de FSM: el caballo de batalla de cada controlador, motor de protocolo y bloque de toma de decisiones en diseño digital.
Preguntas frecuentes
¿Qué es la lógica sincronizada por reloj en Verilog?
Lógica cuyas actualizaciones están gobernadas por una señal de reloj. El modismo estándar es always @(posedge clk) target <= next_value; - en cada flanco de subida de clk, target captura next_value. Esa única línea describe un flip-flop en hardware. Apilar muchos de ellos con lógica combinacional entre medio construye contadores, shift registers, pipelines - todo lo síncrono.
¿Cuál es la diferencia entre reset síncrono y asíncrono en Verilog?
El reset síncrono usa always @(posedge clk) if (reset) ... - el reset se muestrea en un flanco de reloj como cualquier otra entrada. El reset asíncrono usa always @(posedge clk or negedge reset_n) if (~reset_n) ... - el bloque se dispara en un flanco de reloj o en la activación del reset, así que el reset tiene efecto inmediato. Síncrono es la elección por defecto; asíncrono se usa cuando el reset tiene que tener efecto incluso si el reloj está muerto.
¿Cómo se construye un pipeline en Verilog?
Apila varias etapas de always @(posedge clk) stageN_reg <= stageN_combinational; - la lógica combinacional de cada etapa alimenta al registro de la siguiente etapa, y cada registro captura en el mismo flanco de reloj. El resultado es un pipeline donde nuevos datos entran cada ciclo y emergen N ciclos después, con un throughput de un resultado por reloj.
¿Qué es un shift register en Verilog?
Una cadena de flip-flops donde la salida de cada uno alimenta la entrada del siguiente. Cada flanco de reloj desplaza cada bit una posición. La versión canónica en Verilog usa asignaciones no bloqueantes: out <= {out[N-2:0], in}; - esa única línea crea un shift register de N bits que toma un bit por ciclo de in.