Menu

Porte dei moduli in Verilog: input, output e inout spiegati

Come dichiarare le porte di un modulo (input, output e inout), l'elenco delle porte in stile ANSI e quando un'uscita deve essere wire o reg.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

L'interfaccia di un modulo

L'elenco delle porte di un modulo è la sua interfaccia: tutto ciò che il mondo esterno può vedere e toccare. All'interno del modulo, il corpo calcola le uscite a partire dagli ingressi. Le porte sono i fili che attraversano il confine.

La dichiarazione moderna in stile ANSI mette direzione, tipo e larghezza direttamente in linea:

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

Questa è tutta la struttura. Ogni porta ha:

  • Una direzione: input, output o inout.
  • Un tipo: wire o reg (o logic in SystemVerilog).
  • Una larghezza: un intervallo come [7:0], oppure un solo bit se omessa.
  • Un nome: l'identificatore che userai nel corpo.

Le tre direzioni

input: pilotata dall'esterno

Gli ingressi sono sempre wire. Il driver è fuori dal modulo: il segnale di un modulo di livello superiore oppure il reg del testbench. Dentro questo modulo puoi leggere gli ingressi nelle espressioni, ma non assegnarli mai:

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

Scrivere data = ... dentro il modulo sarebbe un errore.

output: pilotata dall'interno

Le uscite sono pilotate da qualcosa dentro questo modulo. Possono essere wire (pilotate da assign o dall'uscita di un sottomodulo) oppure reg (pilotate da always/initial).

module driver(
    input  wire       a,
    input  wire       b,
    input  wire       clk,
    output wire       y,    // wire: pilotato da assign
    output reg        q     // reg: pilotato da always
);
    assign y = a & b;       // OK perché y è wire

    always @(posedge clk)
        q <= a;             // OK perché q è reg
endmodule

Cercare di assegnare y dentro il blocco always, o di pilotare q con un assign, darebbe un errore di compilazione. La scelta della parola chiave deve corrispondere al driver.

inout: bidirezionale

Le porte inout sono wire che possono essere pilotati, alternativamente, dal modulo o da ciò che è collegato all'esterno. Compaiono ai confini del chip: linee SDA dell'I²C, pin GPIO bidirezionali, bus dati condivisi. Dentro il modulo controlli la direzione con un pattern 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 è il wire condiviso. Quando output_enable è alto, il modulo pilota pin al valore di data_out. Quando è basso, rilascia il pin in alta impedenza (z) e lascia che lo usi un driver esterno. data_in osserva sempre ciò che si trova sul pin in quel momento.

inout è raro nella logica puramente interna. Se stai costruendo un controller di memoria, un core di CPU o una pipeline di elaborazione di immagini, potresti non averne mai bisogno. È una funzionalità per l'anello di I/O ai confini del chip.

Porte da un bit e a più bit

L'intervallo di larghezza è facoltativo: omettilo per una porta da un bit:

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

Per le larghezze puoi usare dei parametri, ed è così che funzionano i moduli parametrici:

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

Stile ANSI e stile Verilog-1995

Ogni tanto vedrai codice più vecchio che separa l'elenco delle porte dalle dichiarazioni:

// Vecchio stile Verilog-1995: NON usarlo nel codice nuovo
module foo(clk, data_in, data_out);
    input clk;
    input [7:0] data_in;
    output reg [7:0] data_out;
    // ...
endmodule

L'elenco delle porte contiene solo i nomi; ogni porta viene poi dichiarata a parte nel corpo. È prolisso, soggetto a errori del tipo "nominata nell'elenco ma mai dichiarata" e richiede il doppio della battitura. Lo stile ANSI-2001 (quello usato in tutte queste pagine) sostituisce entrambe le parti:

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

Usa lo stile ANSI. Gli strumenti lo supportano dal 2001.

Un esempio completo

Questo singolo modulo contiene:

  • Un parametro (WIDTH).
  • Ingressi per clock, reset, abilitazione al caricamento, dati e abilitazione allo shift.
  • Un'uscita reg (data_out) pilotata dentro un blocco always.
  • Un'uscita wire (msb_out) pilotata da un'assegnazione continua.
  • Una dichiarazione completa in stile ANSI che elenca in linea direzione, tipo e larghezza di ogni porta.

Quasi ogni modulo sintetizzabile che scriverai ha questa forma.

Cosa viene dopo

La prossima pagina, Module Instantiation, mostra come prendere questo modulo e usarlo dentro un progetto più grande. Lo shifter che hai appena scritto da solo non serve a molto: diventa utile quando qualcosa lo istanzia e lo collega a un sistema.

Domande frequenti

Quali sono le tre direzioni delle porte in Verilog?

input, output e inout. input è pilotata dall'esterno del modulo. output è pilotata dall'interno del modulo. inout è bidirezionale: serve per i bus tri-state in cui il modulo sia pilota sia legge lo stesso pin. La stragrande maggioranza delle porte interne è input o output; inout compare solo sui pin del chip.

Qual è la differenza tra output wire e output reg?

output wire significa che l'uscita è pilotata da un'assegnazione continua o dall'uscita di un sottomodulo, cioè da qualsiasi cosa fuori da un blocco always. output reg significa che è pilotata da un blocco procedurale (initial o always). La parola chiave stabilisce se puoi assegnare il segnale dall'interno di un always, non se l'hardware sintetizzato contiene un flip-flop.

Cos'è lo stile ANSI per le porte in Verilog?

Lo stile ANSI dichiara direzione e tipo di ogni porta direttamente nell'elenco delle porte: module foo(input wire [7:0] data, output reg [7:0] result);. Il vecchio stile Verilog-1995 elencava solo i nomi e li ridichiarava dentro il modulo. Nel codice nuovo usa sempre lo stile ANSI: è più corto, meno soggetto a errori e standard in tutti gli strumenti moderni.

Un modulo Verilog può avere più ingressi e uscite?

Sì: un modulo può avere tutte le porte che ti servono, in qualsiasi combinazione di direzioni. Separale con virgole nell'elenco delle porte. L'ordine nell'elenco determina l'ordine dei collegamenti per posizione nell'istanziazione, ma quasi sempre si usano i collegamenti per nome (.port(signal)) per non dipendere dall'ordine.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA