Anatomia di un letterale con dimensione
Un letterale Verilog ha fino a tre parti:
8'b1010_1100
│ │ │
│ │ └─ digits in the chosen base
│ └─── base specifier: b (binary), h (hex), d (decimal), o (octal)
└────── width in bits (decimal)
Da sinistra a destra: "un valore a 8 bit, scritto in binario, con le cifre 10101100". Sono 172 in decimale e 0xAC in esadecimale: gli stessi bit, comunque tu li scriva. La larghezza dice al compilatore esattamente quanti bit allocare; la base gli dice come interpretare le cifre.
I trattini bassi sono separatori visivi. 32'b1010_1100_0011_0101 equivale a 32'b1010110000110101. Usali.
Le quattro basi
L'esadecimale (h) è lo standard per tutto ciò che supera i 4 bit. Il binario (b) è lo standard quando vuoi leggere direttamente gli schemi di bit. Il decimale (d) serve per i conteggi che hanno senso per le persone. L'ottale (o) esiste, ma non lo vedrai quasi mai.
Le cifre esadecimali non distinguono tra maiuscole e minuscole: 8'hAB e 8'hab sono identici.
Cosa succede se il valore non ci sta?
Se le cifre non riempiono la larghezza dichiarata, Verilog estende con zeri a sinistra:
8'b101 // equivalent to 8'b00000101 (= 5)
8'hF // equivalent to 8'h0F (= 15)
Se le cifre superano la larghezza dichiarata, Verilog tronca i bit alti con un avviso:
4'hFF // truncates to 4'hF, simulator warns
4'b10000 // truncates to 4'b0000
L'avviso è tuo amico. Non disattivarlo.
Con e senza dimensione
Un letterale senza larghezza:
'd10 // unsized, takes default size (≥32 bits)
10 // also unsized - no base means decimal
Nella maggior parte degli strumenti i letterali senza dimensione valgono per default 32 bit. È l'origine di un'intera categoria di bug:
reg [7:0] count;
if (count == -1) ... // -1 is unsized 32-bit; comparison gets weird
La soluzione è scrivere 8'hFF invece di -1, oppure 8'd255, oppure {8{1'b1}}. Dai sempre una dimensione ai letterali usati in espressioni con larghezze precise.
L'unico caso in cui i letterali senza dimensione vanno bene sono i contatori interi nei cicli for dei testbench, dove l'intero è abbastanza largo da non riservare sorprese.
Letterali con segno
Per default i letterali con dimensione sono senza segno:
8'd255 // unsigned 255
8'b1111_1111 // unsigned 255 - same bit pattern, just written differently
Se vuoi che il letterale sia interpretato con segno, aggiungi s dopo l'apostrofo:
8'sd10 // signed 10
8'sb1111_1111 // signed -1 (two's complement)
Il flag s conta per:
- Gli operatori
<<<e>>>(shift aritmetici), che estendono il segno degli operandi con segno. - Gli operatori di confronto su espressioni miste con segno.
- I cast
$signed/$unsigned.
Per la normale aritmetica sui segnali di un bus, lascerai la s da parte.
Letterali numerici con X e Z
Una cifra di un letterale può essere x (sconosciuta) o z (alta impedenza):
8'bx è la forma breve di "tutti e 8 i bit sconosciuti": il valore viene propagato fino a riempire la larghezza dichiarata. Le cifre x sono comuni nei casi default delle macchine a stati e nell'inizializzazione. Ne vediamo la semantica in X and Z Values.
Dove compaiono i letterali con dimensione nella pratica
Appena inizi a scrivere moduli veri, i letterali con dimensione compaiono ovunque:
// Bus widths
input wire [31:0] addr;
wire [31:0] zero = 32'h0;
wire [31:0] all_ones = 32'hFFFF_FFFF;
// State encodings
localparam IDLE = 3'd0;
localparam BUSY = 3'd1;
localparam DONE = 3'd2;
// Masks
wire is_msb_set = data & 32'h8000_0000;
// Comparisons
if (counter == 8'd255) ...
// Reset values
always @(posedge clk) begin
if (reset) data_out <= 8'd0;
else data_out <= data_in;
end
Lo schema è sempre lo stesso: scrivi la larghezza, scrivi la base, scrivi le cifre. Ogni letterale che userai ha questa forma.
Una prova su strada
Ora hai tutto ciò che ti serve per scrivere qualsiasi costante di cui un modulo Verilog possa avere bisogno. L'ultimo tassello del discorso sui tipi di dati è cosa succede quando un segnale non è uno 0 o un 1 pulito: i valori x e z.
Domande frequenti
Cosa significa 8'b1010 in Verilog?
È un letterale binario con dimensione: un valore a 8 bit la cui rappresentazione binaria è 00001010. Il numero prima dell'apostrofo è la larghezza in bit; la lettera dopo l'apostrofo è la base (b binario, h esadecimale, d decimale, o ottale); le cifre successive sono il valore. Se le cifre non riempiono la larghezza, il letterale viene completato con zeri a sinistra.
Qual è la differenza tra numeri con e senza dimensione in Verilog?
Un letterale con dimensione come 8'd10 è largo esattamente 8 bit. Un letterale senza dimensione come 'd10 o semplicemente 10 vale per default 32 bit, di solito troppi. Mescolare letterali senza dimensione in espressioni con larghezze precise causa bug subdoli: preferisci ovunque i letterali con dimensione, tranne che nel codice di test usa e getta.
Come si scrive un numero esadecimale in Verilog?
Usa lo specificatore di base 'h: 8'hFF è un valore a 8 bit pari a 255. Le cifre esadecimali non distinguono tra maiuscole e minuscole: 8'hff e 8'hFF sono identici. Puoi raggruppare le cifre con trattini bassi per la leggibilità: 32'hDEAD_BEEF. Il parser ignora i trattini bassi.
Come funzionano i trattini bassi nei numeri Verilog?
I trattini bassi sono separatori visivi che il parser ignora. 32'b1010_1100_0011_0101 ha esattamente lo stesso valore di 32'b1010110000110101, ma è molto più facile da leggere. Il primo carattere dopo il prefisso di base non può essere un trattino basso, ma puoi metterli ovunque nel resto delle cifre.
Come si scrive un numero con segno in Verilog?
Aggiungi s dopo l'apostrofo: 8'sd10 è il decimale 10 con segno a 8 bit e 8'sb1111_1111 vale -1 con segno. Per default i letterali con dimensione sono senza segno; il modificatore s cambia questo comportamento. La maggior parte dell'aritmetica usa operandi senza segno: ricorri ai valori con segno solo quando ti serve davvero che i valori negativi si propaghino attraverso <, >>> e operatori simili.