Comprender las declaraciones Case en Verilog: Implementación eficiente de multiplexores
La declaración case verifica si la expresión dada coincide con una de las otras expresiones en la lista y ramifica en consecuencia. Se usa típicamente para implementar un multiplexor. La construcción if-else puede no ser adecuada si hay muchas condiciones para verificar y sintetizaría en un codificador de prioridad en lugar de un multiplexor.
Sintaxis
Una declaración case de Verilog comienza con la palabra clave case y termina con endcase. La expresión entre paréntesis se evalúa exactamente una vez y se compara con la lista de alternativas en el orden en que están escritas, y se ejecutan las declaraciones para las cuales la alternativa coincide con la expresión dada. Un bloque de múltiples declaraciones debe agruparse y estar dentro de begin y end.
// Aquí 'expresión' debe coincidir con uno de los items (item 1,2,3 o 4)
case (<expresión>)
case_item1 : <declaración_simple>
case_item2, case_item3 : <declaración_simple>
case_item4 : begin
<múltiples declaraciones>
end
default : <declaración>
endcase
Si ninguno de los case items coincide con la expresión dada, se ejecutan las declaraciones dentro del item default. La declaración default es opcional, y solo puede haber una declaración default en una declaración case. Las declaraciones case pueden anidarse.
La ejecución saldrá del bloque case sin hacer nada si ninguno de los items coincide con la expresión y no se da una declaración default.
Ejemplo
El módulo de diseño mostrado a continuación tiene una señal de selección de 2 bits para enrutar una de las otras tres entradas de 3 bits a la señal de salida llamada out. Se usa una declaración case para asignar la entrada correcta a la salida basada en el valor de sel. Dado que sel es una señal de 2 bits, puede tener 22 combinaciones, 0 a 3. La declaración default ayuda a establecer la salida a 0 si sel es 3.
module my_mux (
input [2:0] a, b, c, // Tres entradas de 3 bits
input [1:0] sel, // Señal de selección de 2 bits para elegir a, b, c
output reg [2:0] out // Señal de salida de 3 bits
);
// Este bloque always se ejecuta siempre que a, b, c o sel cambian de valor
always @ (a, b, c, sel) begin
case(sel)
2'b00 : out = a; // Si sel=0, salida es a
2'b01 : out = b; // Si sel=1, salida es b
2'b10 : out = c; // Si sel=2, salida es c
default : out = 0; // Si sel es cualquier otra cosa, out es siempre 0
endcase
end
endmodule
Esquema de hardware
El código RTL se elabora para obtener un esquema de hardware que representa un multiplexor 4 a 1.
Observe que la salida es cero cuando sel es 3 y corresponde a las entradas asignadas para otros valores.
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0b11 out=0x0 [10] a=0x5 b=0x5 c=0x5 sel=0b10 out=0x5 [20] a=0x1 b=0x5 c=0x6 sel=0b01 out=0x5 [30] a=0x5 b=0x4 c=0x1 sel=0b10 out=0x1 [40] a=0x5 b=0x2 c=0x5 sel=0b11 out=0x0 ncsim: *W,RNQUIE: Simulation is complete.
En una declaración case, la comparación solo tiene éxito cuando cada bit de la expresión coincide con una de las alternativas incluyendo 0, 1, x y z. En el ejemplo mostrado arriba, si cualquiera de los bits en sel es x o z, la declaración default se ejecutará porque ninguna de las otras alternativas coincidió. En tal caso, la salida será todo ceros.
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x0 [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x0 [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x0 [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x0 [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x0 [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x0 [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x0 [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0 [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x0 [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x0 ncsim: *W,RNQUIE: Simulation is complete.
Si la declaración case en el diseño tiene x y z en las alternativas de case items, los resultados serían bastante diferentes.
module my_mux (
input [2:0] a, b, c,
input [1:0] sel,
output reg [2:0] out
);
// Los case items tienen x y z y sel debe coincidir con el valor exacto para
// que la salida se asigne con la entrada correspondiente
always @ (a, b, c, sel) begin
case(sel)
2'bxz : out = a;
2'bzx : out = b;
2'bxx : out = c;
default : out = 0;
endcase
end
endmodule
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x1 [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x5 [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x1 [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x6 [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x5 [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x6 [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x7 [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0 [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x4 [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x5 ncsim: *W,RNQUIE: Simulation is complete.
¿En qué se diferencia un case de if-else?
La declaración case es diferente de if-else-if en dos aspectos:
- Las expresiones dadas en un bloque
if-elseson más generales, mientras que en un bloquecase, una sola expresión se compara con múltiples items caseproporcionará un resultado definitivo cuando hay valores X y Z en una expresión
Verilog
- Dominando la sentencia switch en C#: Sintaxis, ejemplos y mejores prácticas
- Sentencia break en C#: Cómo y cuándo usarla
- Dominando la sentencia continue en C#: Cómo saltar iteraciones de bucles eficazmente
- Dominando switch-case en C++: Sintaxis, uso y ejemplos prácticos
- Fundamentos de Verilog: Diseñando tu primera compuerta AND
- Dominar Verilog: Tutorial completo para diseño de circuitos digitales
- Dominar la concatenación en Verilog: Guía práctica
- Comprender las asignaciones en Verilog: tipos, sintaxis y mejores prácticas
- Bloqueante vs. No bloqueante en Verilog: Diferencias clave explicadas
- Dominar las funciones en Verilog para diseño RTL eficiente
- Comprender las Tasks en Verilog: Funciones vs Tasks, sintaxis y uso