- 1 1. ¿Qué es una función en Verilog? (Conceptos básicos y su papel)
- 2 2. Cómo escribir una función en Verilog (con ejemplo para principiantes)
- 3 3. Uso de funciones en Verilog (con ejemplos prácticos)
- 4 4. Ejemplos de aplicación de funciones en Verilog (Decodificadores y ALU)
- 5 5. Puntos a tener en cuenta al usar funciones en Verilog
- 6 6. Preguntas frecuentes (FAQ) sobre funciones en Verilog
1. ¿Qué es una función en Verilog? (Conceptos básicos y su papel)
Verilog HDL (Hardware Description Language) es un lenguaje de descripción de hardware utilizado para diseñar y simular circuitos digitales. Dentro de este lenguaje, una function (función) es un mecanismo que permite modularizar operaciones específicas para facilitar su reutilización.
Comprender las funciones en Verilog no solo mejora la legibilidad y el mantenimiento del código, sino que también contribuye a un diseño de circuitos más eficiente. En este artículo explicaremos los conceptos básicos de las funciones en Verilog y cómo se utilizan en la práctica.
¿Qué es una function?
Una function en Verilog es un bloque que realiza un cálculo u operación específica y devuelve un único valor. Gracias a su uso, es posible reducir código redundante y simplificar el diseño de circuitos.
Características de una function
- Acepta una o más entradas (solo se permite
input
) - Devuelve un único valor (valor de retorno)
- No puede incluir retardos de tiempo (
#10
, por ejemplo) - Siempre describe lógica combinacional dentro de la función
- Se define fuera de los bloques always y, a diferencia de las tasks, siempre se evalúa de manera inmediata
Casos de uso de las funciones en Verilog
Las functions en Verilog se utilizan principalmente en los siguientes escenarios:
1. Descripción de lógica combinacional
Dado que una función devuelve un resultado de inmediato en función de sus entradas, se utiliza frecuentemente para lógica combinacional (Combinational Logic).
Ejemplos: operaciones de suma, resta, codificadores y decodificadores.
2. Mejorar la reutilización del código
Permiten eliminar código redundante y encapsular procesos reutilizados varias veces.
Ejemplo: transformar una expresión condicional compleja en una función para mejorar la legibilidad del módulo.
3. Reducir errores de diseño
Al concentrar cálculos o expresiones lógicas en un único lugar, se minimizan errores al realizar cambios.
Ejemplo: cálculos de CRC (Comprobación de Redundancia Cíclica) o verificaciones de paridad.
Diferencias entre function y task
En Verilog también existe la estructura task (tarea). Aunque se parecen, tienen diferencias clave:
Aspecto | function | task |
---|---|---|
Salida | Única | Múltiples posibles |
Entradas | Sí | Sí |
Variables internas | Sí | Sí |
Retardos (#10 ) | No permitidos | Permitidos |
Uso dentro de always | Permitido | No permitido |
Forma de llamada | nombre_función(argumentos) | nombre_task(argumentos); |
Cuándo usar function
- Cuando se necesita un resultado inmediato
- Para lógica sin retardos
- Cuando basta con devolver un único valor
Cuándo usar task
- Procesos que incluyan retardos de tiempo (
#10
, por ejemplo) - Procesos que requieran múltiples salidas
- Procesos de depuración para simulación (como generación de logs)
Resumen
- Una function en Verilog recibe entradas y devuelve un único valor.
- Es adecuada para describir lógica combinacional y no admite retardos.
- Reduce la redundancia del código y mejora la legibilidad.
- Se diferencia de las tasks, por lo que conviene elegir la estructura adecuada según el caso.
2. Cómo escribir una función en Verilog (con ejemplo para principiantes)
En la sección anterior vimos los conceptos básicos de las funciones en Verilog. Ahora explicaremos en detalle cómo se escriben y su sintaxis.
Sintaxis básica de una función
Una función en Verilog se define con la siguiente estructura:
function [ancho_salida] nombre_funcion;
input [ancho_entrada] entrada1, entrada2, ...;
begin
nombre_funcion = expresión;
end
endfunction
Puntos clave
- Se declara con la palabra clave
function
- Una variable con el mismo nombre de la función sirve como valor de retorno
- Las entradas se declaran con
input
(no se permitenoutput
niinout
) - Dentro de
begin ... end
se escribe la lógica o el cálculo - Debe definirse fuera de los bloques
always
Ejemplo sencillo de función en Verilog
A continuación, un ejemplo de una función que realiza una suma de 8 bits:
module example;
function [7:0] add_function;
input [7:0] a, b;
begin
add_function = a + b;
end
endfunction
reg [7:0] x, y, sum;
initial begin
x = 8'b00001100; // 12
y = 8'b00000101; // 5
sum = add_function(x, y);
$display("Sum: %d", sum); // Sum: 17
end
endmodule
Explicación
add_function
recibe dos entradas de 8 bits (a
yb
) y devuelve su suma- Se invoca con
sum = add_function(x, y);
asignando el resultado a la variablesum
- Dentro del bloque
initial
se muestra el resultado con$display
Declaración de entradas y salidas en funciones
Entradas
Las funciones en Verilog solo permiten input
como parámetros.
function [7:0] my_function;
input [7:0] in1, in2;
begin
my_function = in1 & in2; // Operación AND
end
endfunction
Nota: No se puede declarar output
. El valor de salida se devuelve mediante la variable con el mismo nombre de la función.
Uso de estructuras condicionales
Dentro de una función también se pueden usar estructuras condicionales como if
o case
.
function [3:0] max_function;
input [3:0] a, b;
begin
if (a > b)
max_function = a;
else
max_function = b;
end
endfunction
Esta función devuelve el valor mayor entre a y b.
Resumen
- Las funciones en Verilog se definen con
function
y devuelven un único valor - Las entradas deben declararse como
input
exclusivamente - El resultado se asigna a la variable con el mismo nombre de la función
- Es posible usar condicionales (
if
,case
) dentro de una función

3. Uso de funciones en Verilog (con ejemplos prácticos)
En la sección anterior vimos la sintaxis básica de las funciones en Verilog. Ahora aprenderemos cómo usarlas en un diseño real mediante ejemplos prácticos.
Cómo invocar una función
Las funciones en Verilog se invocan igual que una variable, utilizando la sintaxis nombre_función(argumentos)
.
En el siguiente ejemplo se define una función XOR de 8 bits y se usa dentro de un módulo:
module function_example;
function [7:0] xor_function;
input [7:0] a, b;
begin
xor_function = a ^ b;
end
endfunction
reg [7:0] x, y, result;
initial begin
x = 8'b11001100;
y = 8'b10101010;
result = xor_function(x, y); // llamada a la función
$display("XOR Result: %b", result); // Resultado: 01100110
end
endmodule
Puntos clave
- Se invoca con la forma
variable = function(argumentos);
- Pueden usarse dentro de bloques
always
einitial
- Funcionan como lógica combinacional
Uso en lógica combinacional
Dado que las funciones se evalúan de inmediato, son útiles para implementar lógica combinacional.
Ejemplo: un decodificador 2 a 4 implementado con function:
module decoder_example;
function [3:0] decoder;
input [1:0] sel;
begin
case (sel)
2'b00: decoder = 4'b0001;
2'b01: decoder = 4'b0010;
2'b10: decoder = 4'b0100;
2'b11: decoder = 4'b1000;
default: decoder = 4'b0000;
endcase
end
endfunction
reg [1:0] select;
wire [3:0] decoded_output;
assign decoded_output = decoder(select);
initial begin
select = 2'b01;
#10;
$display("Decoded Output: %b", decoded_output); // 0010
end
endmodule
Explicación
- La función
decoder
convierte una entrada de 2 bits en una salida de 4 bits - El
case
selecciona la salida en función del valor de entrada - Con
assign
se conecta el resultado de la función a la salidadecoded_output
Comparación entre function y always
Las funciones y los bloques always
se usan para describir lógica, pero tienen diferencias en propósito y limitaciones.
Aspecto | function | always |
---|---|---|
Lugar de definición | Fuera de bloques always | Dentro de bloques always |
Entradas | Solo input | reg o wire |
Salida | Un solo valor | Puede actualizar múltiples señales |
Retardos (#10 ) | No permitidos | Permitidos |
Estado | No almacena estado (evaluación inmediata) | Puede almacenar estado (flip-flops) |
Uso principal | Lógica combinacional | Lógica secuencial o dependiente de eventos |
Claves para elegir
- Las functions son ideales para operaciones combinacionales simples
- Los bloques always se usan cuando se necesita almacenamiento o retardos
- Si se requiere un retardo (
#10
), debe usarsealways
, no function
Resumen del uso de funciones
✅ Se llaman con nombre_función(argumentos)
✅ Adecuadas para lógica combinacional
✅ Soportan estructuras como case
e if
✅ Útiles en decodificadores, operaciones aritméticas y de control
4. Ejemplos de aplicación de funciones en Verilog (Decodificadores y ALU)
Hasta ahora hemos visto la sintaxis y el uso básico de las funciones en Verilog. En esta sección mostraremos cómo aplicarlas en diseños de circuitos digitales reales, tomando como ejemplo un decodificador y una ALU.
Implementación de un decodificador 2 a 4 con function
Un decodificador convierte un número reducido de bits de entrada en múltiples salidas.
Por ejemplo, un decodificador 2 a 4 transforma 2 bits de entrada en 4 salidas. Con una function se puede escribir de forma compacta:
module decoder_example;
function [3:0] decoder;
input [1:0] sel;
begin
case (sel)
2'b00: decoder = 4'b0001;
2'b01: decoder = 4'b0010;
2'b10: decoder = 4'b0100;
2'b11: decoder = 4'b1000;
default: decoder = 4'b0000;
endcase
end
endfunction
reg [1:0] select;
wire [3:0] decoded_output;
assign decoded_output = decoder(select);
initial begin
select = 2'b00; #10;
$display("Decoded Output: %b", decoded_output);
select = 2'b01; #10;
$display("Decoded Output: %b", decoded_output);
select = 2'b10; #10;
$display("Decoded Output: %b", decoded_output);
select = 2'b11; #10;
$display("Decoded Output: %b", decoded_output);
end
endmodule
Implementación de una ALU con function (suma, resta, AND, OR)
Una ALU (Unidad Lógica Aritmética) es un bloque fundamental en una CPU que realiza operaciones aritméticas y lógicas. A continuación, un ejemplo de ALU simple de 8 bits implementada con function:
module alu_example;
function [7:0] alu;
input [7:0] a, b;
input [1:0] op; // señal de control de 2 bits
begin
case (op)
2'b00: alu = a + b; // Suma
2'b01: alu = a - b; // Resta
2'b10: alu = a & b; // AND
2'b11: alu = a | b; // OR
default: alu = 8'b00000000;
endcase
end
endfunction
reg [7:0] x, y;
reg [1:0] opcode;
wire [7:0] result;
assign result = alu(x, y, opcode);
initial begin
x = 8'b00001100; // 12
y = 8'b00000101; // 5
opcode = 2'b00; #10;
$display("Addition Result: %d", result); // 12 + 5 = 17
opcode = 2'b01; #10;
$display("Subtraction Result: %d", result); // 12 - 5 = 7
opcode = 2'b10; #10;
$display("AND Result: %b", result);
opcode = 2'b11; #10;
$display("OR Result: %b", result);
end
endmodule
Resumen
✅ Las functions se pueden aplicar a diseños combinacionales como decodificadores y ALUs
✅ Con case
es posible describir operaciones de forma flexible
✅ Mejoran la legibilidad y facilitan la reutilización del código
✅ Son óptimas para lógica combinacional, pero no para lógica secuencial (no admiten retardos)
5. Puntos a tener en cuenta al usar funciones en Verilog
Las funciones en Verilog son herramientas potentes para mejorar la legibilidad y reutilización del código, pero tienen ciertas limitaciones que deben considerarse. A continuación, se detallan los aspectos más importantes.
No se permite la recursividad
En Verilog no está permitido el uso de llamadas recursivas. Es decir, una función no puede llamarse a sí misma dentro de su definición.
❌ Ejemplo incorrecto (recursión)
function [3:0] factorial;
input [3:0] n;
begin
if (n == 0)
factorial = 1;
else
factorial = n * factorial(n - 1); // ❌ No permitido
end
endfunction
Este código provocará un error en simulación.
✅ Solución: usar bucles
Si se necesita comportamiento recursivo, debe implementarse con un bucle dentro de un bloque always o bien mediante una task.
task factorial_task;
input [3:0] n;
output [15:0] result;
integer i;
begin
result = 1;
for (i = 1; i <= n; i = i + 1)
result = result * i;
end
endtask
No se permiten retardos dentro de una función
Las funciones en Verilog se evalúan de manera inmediata (lógica combinacional), por lo que no se pueden incluir retardos (#10
, por ejemplo).
❌ Ejemplo incorrecto
function [7:0] delay_function;
input [7:0] in;
begin
#10; // ❌ No permitido
delay_function = in + 1;
end
endfunction
✅ Solución: usar always o task
Si se necesita un retardo, debe implementarse en un bloque always
o en una task
.
task delay_task;
input [7:0] in;
output [7:0] out;
begin
#10;
out = in + 1;
end
endtask
Diferencias clave entre function y task
En la siguiente tabla se resumen sus diferencias:
Aspecto | function | task |
---|---|---|
Salida | Única (valor de retorno) | Múltiples posibles |
Entradas | Solo input | input y output |
Variables internas | Permitidas | Permitidas |
Retardos (#10 ) | No | Sí |
Uso en always | Permitido | No permitido |
Llamada | nombre_función(arg) | nombre_task(arg); |
Cuándo usar function
✅ Cuando se necesita un resultado inmediato (suma, resta, operaciones lógicas)
✅ Para lógica combinacional sin retardos
✅ Cuando solo se requiere un valor de retorno
Cuándo usar task
✅ Cuando se necesitan retardos (#10
)
✅ Para procesos con múltiples salidas
✅ Para depuración en simulación (mensajes, monitoreo)
No se pueden definir funciones dentro de un bloque always
Las funciones deben declararse fuera de los bloques always
. Si se intentan definir dentro, se producirá un error de compilación.
❌ Ejemplo incorrecto
always @(a or b) begin
function [7:0] my_function; // ❌ No permitido
input [7:0] x, y;
begin
my_function = x + y;
end
endfunction
end
✅ Ejemplo correcto
function [7:0] add_function;
input [7:0] x, y;
begin
add_function = x + y;
end
endfunction
always @(a or b) begin
result = add_function(a, b);
end
Resumen
✅ Las funciones no admiten recursión (usar bucles o tasks)
✅ No permiten retardos (usar always o tasks)
✅ No pueden definirse dentro de bloques always
✅ Devuelven un solo valor (para múltiples salidas, usar tasks)
✅ Es fundamental elegir correctamente entre function y task según la necesidad
6. Preguntas frecuentes (FAQ) sobre funciones en Verilog
En las secciones anteriores revisamos los conceptos básicos, usos y limitaciones de las funciones en Verilog. A continuación, respondemos algunas de las preguntas más comunes sobre este tema.
¿Cuál es la diferencia entre function y task?
Q. ¿En qué se diferencian y cuándo debo usar cada una?
A. Una function devuelve un único valor de manera inmediata; una task puede tener múltiples salidas y admitir retardos.
Aspecto | function | task |
---|---|---|
Salida | Única | Múltiples |
Entradas | Solo input | input y output |
Variables internas | Permitidas | Permitidas |
Retardos (#10 ) | No permitidos | Permitidos |
Uso en always | Sí | No |
Llamada | nombre_función(arg) | nombre_task(arg); |
Cuándo usar function
✅ Para obtener un resultado inmediato (suma, resta, operaciones lógicas)
✅ Para lógica combinacional sin retardos
✅ Cuando solo se necesita un valor de salida
Cuándo usar task
✅ Cuando se necesitan retardos (#10
)
✅ Cuando hay múltiples salidas
✅ Para depuración en simulaciones (logs, monitoreo)
¿Se puede usar reg
dentro de una función?
Q. ¿Es posible declarar variables tipo reg
dentro de una función?
A. No, dentro de una función no se permiten variables reg
, pero sí integer
.
Ejemplo correcto usando integer
:
function [7:0] multiply;
input [3:0] a, b;
integer temp;
begin
temp = a * b;
multiply = temp;
end
endfunction
¿En qué situaciones se recomienda usar funciones?
Q. ¿Cuáles son los casos de uso más comunes?
A. Son ideales para operaciones simples y lógica combinacional.
Ejemplos típicos:
- Operaciones aritméticas (suma, resta, AND, OR)
- Decodificadores o codificadores
- Comparaciones (máximo/mínimo)
- Verificaciones (paridad, CRC)
No son adecuadas para lógica secuencial (flip-flops, contadores).
¿Se puede invocar una función desde otra función?
Q. ¿Es posible anidar llamadas de funciones?
A. Sí, es posible, pero hay que cuidar la dependencia entre funciones.
function [7:0] add;
input [7:0] a, b;
begin
add = a + b;
end
endfunction
function [7:0] double_add;
input [7:0] x, y;
begin
double_add = add(x, y) * 2;
end
endfunction
¿Cómo elegir entre function y always?
Q. ¿Cuál es la diferencia práctica entre una función y un bloque always?
A. Las functions son para lógica combinacional, los bloques always para lógica secuencial.
Aspecto | function | always |
---|---|---|
Retardos (#10 ) | No | Sí |
Estado | No almacena estado (evaluación inmediata) | Puede almacenar estado (flip-flops, registros) |
Uso principal | Lógica combinacional | Lógica secuencial |
Ejemplo con function
function [7:0] add;
input [7:0] a, b;
begin
add = a + b;
end
endfunction
Ejemplo con always
always @(posedge clk) begin
sum <= a + b;
end
Resumen
✅ Las funciones son ideales para lógica combinacional y operaciones simples
✅ Las tasks permiten retardos y múltiples salidas
✅ Los bloques always se usan para lógica secuencial
✅ Importante: elegir la estructura adecuada según el diseño