Menu

Concatenazione e replicazione in Verilog: gli operatori {}

Come unire segnali con {} e copiare un pattern N volte con {N{...}}: gli operatori di Verilog indispensabili per costruire bus più larghi a partire da pezzi.

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

Due piccoli operatori che valgono oro

Verilog ha due forme di {}:

  1. Concatenazione, {a, b, c}: unisce più segnali in uno più largo.
  2. Replicazione, {N{pattern}}: copia un pattern N volte.

Li userai entrambi di continuo. Ogni volta che un bus va diviso, unito, riempito o esteso con il segno, la risposta è uno di questi due operatori.

Concatenazione: {a, b, c}

Il caso più semplice: impilare due segnali in uno solo:

Leggendo da sinistra a destra: a è la metà alta, b quella bassa. Il primo operando dentro {} è il più significativo. La larghezza del risultato è la somma delle larghezze degli operandi.

Puoi concatenare quanti operandi vuoi:

wire [31:0] word = {byte3, byte2, byte1, byte0};

E gli operandi possono avere larghezze diverse:

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1 bit      8 bits     1 bit   2 bits  = 12

Una regola comune: tutti gli operandi di una concatenazione devono avere una larghezza esplicita. I letterali senza dimensione (1 invece di 1'b1) causano errori, perché il parser non sa quanti bit assegnare. Scrivi sempre 1'b0, 1'b1, 4'd0, mai 0 o 1 nudi dentro {}.

Concatenazione sul lato sinistro

La concatenazione sul lato sinistro di un'assegnazione divide un segnale largo nelle sue parti:

È il pattern canonico del sommatore con riporto. {carry_out, sum_bits} è un'unica destinazione a 9 bit a sinistra dell'assegnazione e i bit vengono distribuiti: il bit più alto va a carry_out, gli 8 bassi a sum_bits.

La concatenazione a sinistra funziona sia in assign sia nei blocchi procedurali:

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

Replicazione: {N{pattern}}

L'operatore di replicazione copia un pattern un numero fisso di volte. La forma è {N{pattern}}: un conteggio, poi il pattern dentro la sua coppia di graffe:

N deve essere una costante: il compilatore deve conoscere la larghezza del risultato durante l'elaborazione. Il pattern può essere un letterale, un parametro o qualsiasi espressione di larghezza nota.

Non dimenticare le graffe interne. {8 1'b1} è un errore di sintassi; {8{1'b1}} è corretto. Le {} esterne sono l'operatore; le {...} interne racchiudono il pattern da replicare.

Combinarli: estensione del segno e con zeri

I due operatori si combinano in modo naturale per costruire valori più larghi:

{ {8{a_negative[7]}}, a_negative } è il pattern standard di estensione del segno. Leggilo così: replica otto volte il bit di segno, poi aggiungi gli 8 bit originali. Risultato: la rappresentazione in complemento a due a 16 bit dello stesso numero.

Per allargare valori senza segno puoi affidarti all'assegnazione: Verilog estende automaticamente con zeri quando la destinazione è più larga:

wire [15:0] zext_auto = a_unsigned;   // works, top 8 bits are 0

L'estensione del segno invece non avviene mai in modo implicito, a meno che sia l'operando sia la destinazione siano dichiarati signed. L'idioma esplicito con la replicazione è più sicuro.

Pattern utili

Costruire una maschera di N uno

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// e.g. with N=5, mask = 32'h0000_001F

Invertire l'ordine di un vettore

Verilog non ha una funzione di inversione integrata, ma puoi scriverne una in linea con la concatenazione:

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

Per vettori più larghi è più pulito un ciclo generate o una function.

Raccogliere flag in un byte di stato

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] reserved
    1'b0,          // [1] reserved
    valid          // [0]
};

Gli slot riservati da 1 bit hanno larghezze esplicite: mai 0 nudo dentro {}.

Errori comuni

Letterali senza dimensione dentro {}. {a, 0} è un errore di sintassi oppure uno zero largo 32 bit. Specifica sempre la dimensione: {a, 1'b0}.

Dimenticare le graffe interne nella replicazione. {8 1'b1} non viene analizzato; {8{1'b1}} sì.

Confondere l'ordine. {a, b} mette a nella parte alta e b in quella bassa. Se ragioni al contrario, da qualche parte ti ritroverai un ordine dei byte invertito.

Replicare zero volte. {0{...}} non è valido nel Verilog standard. SystemVerilog lo permette (e produce un risultato di larghezza zero). Il Verilog semplice lo rifiuta.

Cosa viene dopo

Ora hai visto tutti gli operatori di Verilog. Il prossimo capitolo cambia marcia e si concentra sulla struttura: come i moduli si collegano tra loro, le regole delle porte e la sintassi per istanziare sottomoduli e costruire progetti più grandi.

Domande frequenti

Come si concatenano i segnali in Verilog?

Con le parentesi graffe: {a, b, c} produce un unico vettore largo unendo gli operandi da sinistra a destra. L'MSB di a diventa l'MSB del risultato; l'LSB di c diventa l'LSB. La larghezza del risultato è la somma delle larghezze degli operandi. La concatenazione funziona sia sul lato destro di un'assegnazione (per costruire un valore) sia sul lato sinistro (per dividerlo).

Cosa significa {N{pattern}} in Verilog?

È l'operatore di replicazione: produce N copie di pattern concatenate tra loro. {8{1'b1}} è un valore a 8 bit tutto a uno (8'hFF). {4{2'b10}} è il valore a 8 bit 8'b10101010. La replicazione serve a estendere con zeri, estendere il segno o costruire qualsiasi pattern di bit ripetuto senza scriverlo a mano.

Come si estende il segno di un segnale in Verilog?

Usa la replicazione per ripetere il bit di segno: { {24{a[7]}}, a } estende un a a 8 bit fino a 32 bit replicando 24 volte il bit 7 (il bit di segno) e aggiungendo poi il valore originale. Per l'estensione con zeri di valori senza segno, replica 1'b0 oppure lascia che lo faccia implicitamente l'assegnazione quando la destinazione è più larga.

Si può usare la concatenazione sul lato sinistro di un'assegnazione?

Sì: è il modo per assegnare più destinazioni da un'unica sorgente larga. {carry, sum} = a + b; mette il risultato della somma in carry (il bit alto) e sum (il resto) con una sola istruzione. Ogni destinazione mantiene la sua larghezza e il parser distribuisce di conseguenza i bit del lato destro.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA