Constantes con un giro
Un parameter es una constante - el compilador elige su valor y lo hornea en el diseño antes de que empiece la simulación. El giro es que quien instancia el módulo puede cambiar el valor. El mismo archivo fuente del módulo puede producir circuitos de distintos tamaños dependiendo de cómo lo parametricen los que lo llaman.
Así es como construyes IP reutilizable. Un módulo FIFO con parámetros WIDTH y DEPTH puede servir a cada equipo en una empresa. Un contador con un parámetro WIDTH es el mismo código fuente tanto si cuenta hasta 16 como hasta 4 mil millones.
Declaración básica de parámetros
Tres piezas de sintaxis nuevas:
- Bloque de parámetros:
#( parameter WIDTH = 8 )entre el nombre del módulo y la lista de puertos. El valor por defecto8es lo que el módulo usa cuando nadie sobrescribe. - Sitio de uso:
output reg [WIDTH-1:0] count. El parámetro es solo una constante - lo metemos en el rango de bits. El mismo código fuente del módulo produce una salida de 8 bits o 16 bits dependiendo deWIDTH. - Sintaxis de override:
counter #(.WIDTH(16)) dut16(...)en tiempo de instanciación. El bloque#(...)va antes del nombre de la instancia, después del nombre del módulo. Los parámetros que no mencionas mantienen sus valores por defecto.
localparam: constantes internas
Hay constantes que no quieres que los que llaman sobrescriban. Las codificaciones de estado son el caso clásico:
localparam hace IDLE, RUNNING, DONE disponibles dentro del módulo pero no sobrescribibles desde la instanciación. Esa es la elección correcta - cambiar qué valor de estado significa IDLE desde fuera del módulo sería aterrador.
Usa parameter para cosas que los que llaman deberían configurar (anchos, profundidades, opciones de comportamiento) y localparam para todo lo demás. Un patrón común es derivar localparams a partir de parameters:
parameter WIDTH = 32;
localparam WIDTH_M1 = WIDTH - 1; // computed once; not overrideable
Anchos parametrizados en la práctica
El uso más común de los parámetros es hacer que los anchos de bus sean flexibles. Aquí hay un sumador que funciona con cualquier ancho:
Un archivo fuente, dos instancias, dos anchos distintos, sin copiar y pegar. Ese es todo el beneficio de los parámetros.
Varios parámetros, override por nombre
Para módulos más grandes a menudo tendrás varios parámetros. Sobrescríbelos por nombre en cualquier orden:
module fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16,
parameter AFULL = DEPTH - 2
)(
input wire clk,
input wire reset,
// ... ports ...
);
// ...
endmodule
// At the call site:
fifo #(.WIDTH(32), .DEPTH(1024)) cmd_queue (.clk(clk), .reset(reset), ...);
No tienes que sobrescribir cada parámetro; los no mencionados mantienen sus valores por defecto. El valor por defecto de AFULL en el ejemplo se calcula a partir de DEPTH, lo que significa que si sobrescribes DEPTH, AFULL lo sigue automáticamente - exactamente el tipo de dependencia que localparam también manejaría si no quisieras que los que llaman pudieran sobrescribir AFULL de forma independiente.
Errores comunes
Olvidar el # en el override. counter (.WIDTH(16)) dut(...) parece un override pero Verilog lee (.WIDTH(16)) como una conexión de puerto. Necesitas counter #(.WIDTH(16)) dut(...).
Usar parámetros donde no son lo bastante constantes. Los parámetros se resuelven en tiempo de elaboración, antes de que existan las señales. No puedes parametrizar con una señal de runtime - si el valor depende de lo que una entrada está haciendo en tiempo de simulación, no es un parámetro, es lógica.
Confundir parameter y localparam en las listas de puertos. Solo parameter va en el bloque #(...) de arriba. localparam vive dentro del cuerpo. El compilador te lo dirá si los intercambias.
Qué viene a continuación
Ya puedes hacer módulos cuyo tamaño se establece en tiempo de compilación. Los dos siguientes docs cubren las reglas para escribir constantes literales (8'h1F, 4'b1010, 32'd100) y los valores x y z que aparecen cuando las señales están sin excitar o en contención. Después de eso pasamos a los operadores - todo lo que puedes hacer con los vectores y parámetros que ahora has visto.
Preguntas frecuentes
¿Qué es un parameter en Verilog?
Un parameter es una constante en tiempo de compilación declarada dentro de un módulo. Puede usarse en cualquier sitio donde se permita una constante - anchos de bus, profundidades de memoria, codificaciones de estado, valores por defecto. Lo crucial: cada instancia del módulo puede sobrescribir el parámetro, así que el mismo archivo fuente puede producir, por ejemplo, un contador de 8 bits en un sitio y un contador de 32 bits en otro.
¿Cuál es la diferencia entre parameter y localparam en Verilog?
Los valores parameter pueden sobrescribirse desde fuera del módulo cuando se instancia. Los valores localparam no - son constantes internas y nada más. Usa localparam para codificaciones de estado y constantes derivadas que el autor del módulo no quiere que los que lo llamen toqueteen.
¿Cómo se sobrescribe un parameter en Verilog?
Cuando instancias el módulo, añade un bloque de override de parámetro antes del nombre de la instancia: counter #(.WIDTH(16)) my_inst (.clk(clk), .count(count));. La sintaxis .WIDTH(16) establece ese parámetro específico; los parámetros que no listes mantienen sus valores por defecto. Varios overrides se separan por comas dentro del #(...).
¿Qué es un módulo parametrizado?
Un módulo que expone una o más declaraciones parameter que los que lo llaman pueden sobrescribir. Una FIFO parametrizada podría tener parámetros WIDTH y DEPTH para que el mismo código fuente produzca una FIFO de 32 bits por 16 de profundidad en un sitio y una de 8 bits por 1024 en otro. Así es como se escriben las bibliotecas de bloques de IP reutilizables.