Menu

Vettori e array in Verilog: i segnali multi-bit spiegati

Come dichiarare segnali multi-bit con [7:0], estrarne porzioni, combinarli, e la differenza tra un vettore packed e un array di memoria.

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

Un wire o tanti

Finora ogni segnale che abbiamo visto era largo un bit. I progetti reali non sono quasi mai così: gli indirizzi sono a 16 o 32 bit, i bus dati a 8 o 64, i pixel RGB a 24. Verilog ti offre un unico meccanismo per rendere multi-bit qualsiasi segnale: aggiungere un range tra parentesi quadre.

wire [7:0] data;       // 8-bit wire, bit 7 is MSB, bit 0 is LSB
reg  [15:0] address;   // 16-bit reg
output reg [31:0] result; // 32-bit module output

I numeri tra parentesi quadre sono le posizioni del bit più significativo e di quello meno significativo. [7:0] significa "questo segnale ha bit numerati da 7 a 0", cioè 8 bit in totale. Il primo numero è l'indice alto, il secondo quello basso.

Ogni tanto vedrai [0:7]: stesso numero di bit, endianness opposta per quanto riguarda gli slice. La forma [high:low] è la convenzione nettamente prevalente nel settore: usala, a meno che tu non abbia un buon motivo per fare altrimenti.

Un esempio completo: un sommatore a 8 bit

Ogni + somma i due vettori a 8 bit e produce un risultato a 9 bit. I letterali numerici come 8'd10 significano "un valore decimale 10 largo 8 bit": li vedremo in Letterali numerici.

Slicing: estrarre bit

Una volta che hai un vettore, puoi estrarne singoli bit o range contigui:

Non farti distrarre dalla riga force del testbench: ci serve solo un modo per iniettare un valore e mostrare lo slicing. La parte interessante sono gli slice.

Alcune regole:

  • La direzione dello slice deve corrispondere alla dichiarazione. Se hai dichiarato [7:0], estrai con [high:low]. Invertire la direzione è un errore di sintassi.
  • Uno slice fuori range produce x (sconosciuto) in simulazione. Lo strumento di sintesi può dare un avviso o un errore.
  • La selezione dei bit usa l'indice che hai scritto tu, partendo da zero: data[0] è il bit chiamato 0, che (per una dichiarazione [7:0]) è l'LSB.

Slice a base variabile: +: e -:

Un'esigenza comune: "dammi 8 bit a partire dal bit N". Non puoi scrivere direttamente data[N+7:N], perché Verilog richiede che entrambi gli estremi del range siano costanti. La sintassi che risolve il problema:

data[base +: width]   // width bits starting at `base`, going UP
data[base -: width]   // width bits starting at `base`, going DOWN

La larghezza è costante (prendiamo 8 bit alla volta), ma la base può essere un'espressione calcolata a runtime. È proprio ciò che serve per memorie indirizzabili a byte, prese di uno shift register e casi simili.

Array: un passo oltre i vettori

Un vettore è un singolo segnale multi-bit. Un array è un insieme di vettori, indicizzati in modo indipendente:

reg [31:0] mem [0:1023];

Quella dichiarazione ha due range, e i due range significano cose diverse:

  • [31:0] è la dimensione packed: la larghezza di ogni singola parola.
  • [0:1023] è la dimensione unpacked: quante parole ci sono.

Quindi mem sono 1024 registri separati da 32 bit. Accedi a uno di essi con un singolo indice:

mem[5] = 32'hCAFE_BABE;       // write word 5
data   = mem[address];        // read the word at `address`

È una minuscola memoria che contiene dei quadrati. I progetti reali usano lo stesso pattern per register file, tabelle di lookup, FIFO e qualsiasi altra memoria on-chip più grande di un singolo vettore.

Packed e unpacked: perché conta

La distinzione tra dimensioni packed e unpacked compare ovunque. Sapere quale è quale ti risparmia molto debug:

  • Un vettore packed è un solo segnale. Puoi trattarlo tutto come un numero: data + 1 funziona, data == 32'h0 funziona, data[7:0] funziona.
  • Un array unpacked è fatto di tanti segnali. Non puoi trattarlo tutto come un numero: mem + 1 è un errore di sintassi. Devi prima estrarre una parola specifica.

Sono ammesse anche più dimensioni packed:

reg [3:0][7:0] regs;   // 4 bytes packed together into a 32-bit signal

regs[0] è un byte (il byte basso). regs nel suo insieme è a 32 bit. SystemVerilog usa molto questa forma.

Più dimensioni unpacked creano una memoria 2D:

reg [31:0] frame [0:479][0:639];  // 480x640 of 32-bit pixels

Accedi a un singolo pixel con frame[y][x]. Ecco come appare un buffer d'immagine in HDL.

Cosa viene dopo

Ora sai dichiarare e manipolare segnali di qualsiasi larghezza. La prossima guida, Parametri, mostra come rendere configurabili queste larghezze, così lo stesso modulo funziona a 8 bit in un'istanza e a 32 in un'altra. Poi passeremo alle regole per scrivere i numeri letterali (8'b1010_1100, 32'hDEAD_BEEF) e ai valori x/z che compaiono ogni volta che qualcosa non è pilotato.

Domande frequenti

Cos'è un vettore in Verilog?

Un vettore è un segnale multi-bit. Lo dichiari aggiungendo un range a un wire o a un reg: wire [7:0] data è un wire a 8 bit. I numeri tra parentesi quadre sono le posizioni dei bit: in questo caso il bit 7 è il più significativo e il bit 0 il meno significativo. Puoi estrarre singoli bit (data[3]) o range contigui (data[7:4]).

Cosa significa [7:0] in Verilog?

[7:0] dichiara un range che va dal bit 7 al bit 0, estremi inclusi: un segnale a 8 bit con il bit 7 come bit più significativo. Il primo numero è l'indice alto, il secondo quello basso. Puoi anche scrivere [0:7] per un'indicizzazione little-endian, ma la forma [high:low] è di gran lunga la convenzione più diffusa nel codice industriale.

Come si estraggono bit in Verilog?

Con l'indicizzazione tra parentesi quadre. data[3] prende un singolo bit. data[7:4] prende i quattro bit alti come vettore a 4 bit. Lo slice deve avere la stessa direzione della dichiarazione: se hai dichiarato [7:0], estrai con [high:low]. SystemVerilog aggiunge anche data[3 +: 4] per slice a base variabile e larghezza costante.

Qual è la differenza tra array packed e unpacked in Verilog?

Un array packed è un unico bus contiguo: reg [31:0] word è un solo segnale a 32 bit. Un array unpacked (o 'memoria') è un insieme di parole indipendenti: reg [31:0] mem [0:1023] sono 1024 registri separati da 32 bit. Puoi leggere o scrivere una parola intera di un array unpacked, ma non puoi operare sull'insieme come se fosse un unico segnale.

Come si dichiara una memoria in Verilog?

reg [31:0] mem [0:1023]; dichiara una memoria di 1024 elementi, ciascuno largo 32 bit. La prima coppia di parentesi è la larghezza della parola (packed), la seconda è il numero di parole (unpacked). Accedi a un elemento con mem[address], e puoi leggere o scrivere una porzione di quell'elemento con mem[address][7:0] quando l'indicizzazione di SystemVerilog-2005 è abilitata.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA