Menu

Puertos de módulo en Verilog: input, output e inout explicados

Cómo declarar puertos de módulo - input, output e inout - la lista de puertos de estilo ANSI y cuándo una salida debería ser wire vs reg.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

La interfaz de un módulo

La lista de puertos de un módulo es su interfaz - todo lo que el mundo exterior puede ver y tocar. Dentro del módulo, el cuerpo calcula salidas a partir de entradas. Los puertos son los wires que cruzan la frontera.

La declaración moderna de estilo ANSI pone dirección, tipo y ancho en línea:

module my_module(
    input  wire        clk,
    input  wire        reset,
    input  wire [7:0]  data_in,
    output reg  [7:0]  data_out,
    output wire        valid
);
    // body
endmodule

Esa es toda la forma. Cada puerto tiene:

  • Una dirección: input, output o inout.
  • Un tipo: wire o reg (o logic en SystemVerilog).
  • Un ancho: un rango como [7:0], o un solo bit si se omite.
  • Un nombre: el identificador que usarás dentro del cuerpo.

Las tres direcciones

input - excitado desde fuera

Las entradas siempre son wire. El que excita está fuera del módulo - o bien una señal de un módulo de nivel superior o el reg del testbench. Dentro de este módulo, puedes leer las entradas en expresiones pero nunca asignarles:

module reader(input wire [7:0] data);
    initial $display("data = %h", data);
endmodule

Escribir data = ... dentro del módulo sería un error.

output - excitado desde dentro

Las salidas las excita algo dentro de este módulo. Pueden ser o bien wire (excitado por assign o por la salida de un submódulo) o reg (excitado desde always/initial).

module driver(
    input  wire       a,
    input  wire       b,
    input  wire       clk,
    output wire       y,    // wire - excitado por assign
    output reg        q     // reg  - excitado por always
);
    assign y = a & b;       // OK porque y es wire

    always @(posedge clk)
        q <= a;             // OK porque q es reg
endmodule

Intentar asignar a y dentro del bloque always, o excitar q con un assign, sería un error de compilación. La elección de la palabra clave tiene que coincidir con quien la excita.

inout - bidireccional

Los puertos inout son wires que pueden ser excitados alternativamente por el módulo o por lo que esté conectado fuera. Aparecen en los límites del chip - líneas SDA de I²C, pines GPIO bidireccionales, buses de datos compartidos. Dentro del módulo, controlas la dirección con un patrón tri-state:

module bidir_pin(
    input  wire data_out,
    input  wire output_enable,
    output wire data_in,
    inout  wire pin
);
    assign pin     = output_enable ? data_out : 1'bz;
    assign data_in = pin;
endmodule

pin es el wire compartido. Cuando output_enable está en alto, el módulo excita pin a data_out. Cuando está en bajo, libera el pin a alta impedancia (z), permitiendo que un controlador externo lo use. data_in siempre observa lo que esté actualmente en el pin.

inout es raro en lógica puramente interna. Si estás construyendo un controlador de memoria, un núcleo de CPU o un pipeline de procesamiento de imagen, puede que nunca lo necesites. Es una característica para el anillo de I/O en los límites del chip.

Puertos de un solo bit vs multibit

El rango de ancho es opcional - omítelo para un puerto de un solo bit:

input wire valid,         // 1 bit
input wire [7:0] data,    // 8 bits
input wire [31:0] addr,   // 32 bits

Puedes usar parámetros para los anchos, que es como funcionan los módulos parametrizados:

module bus #(
    parameter WIDTH = 32
)(
    input  wire [WIDTH-1:0] in,
    output wire [WIDTH-1:0] out
);
    assign out = in;
endmodule

Estilos ANSI vs Verilog-1995

Ocasionalmente verás código antiguo que separa la lista de puertos y las declaraciones:

// Estilo Verilog-1995 antiguo - NO lo hagas en código nuevo
module foo(clk, data_in, data_out);
    input clk;
    input [7:0] data_in;
    output reg [7:0] data_out;
    // ...
endmodule

La lista de puertos contiene solo nombres; cada puerto luego se declara por separado dentro del cuerpo. Es verboso, propenso a errores "nombrado en la lista pero nunca declarado" y supone el doble de escribir. El estilo ANSI-2001 (el que se muestra a lo largo de estos docs) reemplaza ambos:

module foo(
    input  wire       clk,
    input  wire [7:0] data_in,
    output reg  [7:0] data_out
);
    // ...
endmodule

Usa el estilo ANSI. Las herramientas lo soportan desde 2001.

Un ejemplo completo

Ese único módulo tiene:

  • Un parámetro (WIDTH).
  • Entradas para reloj, reset, habilitación de carga, data y habilitación de shift.
  • Una salida reg (data_out) excitada dentro de un bloque always.
  • Una salida wire (msb_out) excitada por una asignación continua.
  • Una declaración completa de estilo ANSI que lista la dirección, el tipo y el ancho de cada puerto en línea.

Así es como está estructurado casi cada módulo sintetizable que vas a escribir.

Qué viene a continuación

El siguiente doc - Module Instantiation - muestra cómo coger este módulo y usarlo dentro de un diseño más grande. El shifter que acabas de escribir no es útil por sí solo; se gana su sitio cuando algo lo instancia y lo cablea en un sistema.

Preguntas frecuentes

¿Cuáles son las tres direcciones de puerto en Verilog?

input, output e inout. input se excita desde fuera del módulo. output se excita desde dentro del módulo. inout es bidireccional - útil para buses tri-state donde el módulo a la vez excita y lee el mismo pin. La gran mayoría de los puertos internos son input o output; inout solo aparece en los pines del chip.

¿Cuál es la diferencia entre output wire y output reg?

output wire significa que la salida se excita mediante una asignación continua o desde la salida de un submódulo - todo lo que está fuera de un bloque always. output reg significa que se excita desde un bloque procedural (initial o always). La palabra clave controla si puedes asignar a la señal desde dentro de un always, no si el hardware sintetizado contiene un flip-flop.

¿Qué es el estilo ANSI para puertos en Verilog?

El estilo ANSI declara la dirección y el tipo de cada puerto en línea dentro de la lista de puertos: module foo(input wire [7:0] data, output reg [7:0] result);. El estilo más antiguo de Verilog-1995 solo listaba nombres en la lista de puertos y los redeclaraba dentro del módulo. Usa siempre el estilo ANSI en código nuevo - es más corto, menos propenso a errores y estándar en todo lo moderno.

¿Se pueden tener varias entradas y salidas en un módulo Verilog?

Sí - un módulo puede tener tantos puertos como necesites, en cualquier combinación de direcciones. Sepáralos con comas en la lista de puertos. El orden en la lista de puertos determina el orden de conexión posicional al instanciar, pero casi siempre usas conexiones por nombre (.port(signal)) para no depender del orden.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR