Cosa significa "operatore" in Verilog
Gli operatori prendono espressioni che trasportano segnali e producono nuove espressioni che trasportano segnali. a + b è hardware: il sintetizzatore lo trasforma in un sommatore. a == b è hardware: costruisce un comparatore. Niente di tutto questo è "codice che viene eseguito"; sono tutti "circuiti descritti dall'operatore".
L'elenco completo degli operatori è abbastanza breve da memorizzare. Questa pagina tratta gli operatori aritmetici, di confronto, logici e condizionale. Bitwise and Reduction tratta gli operatori a livello di bit che non hanno equivalenti diretti nel software.
Aritmetici: + - * / %
Tre cose da sapere:
- La divisione è intera.
7/2vale3, non3.5. Verilog non ha un tipo in virgola mobile (a dire il vero c'èreal, ma vale solo in simulazione). - Divisione e modulo sono costosi in hardware. Un
/4va bene (il sintetizzatore lo trasforma in uno shift a destra), ma un/nconnvariabile a runtime costruisce un divisore a più cicli che non vuoi quasi mai. - L'overflow riparte da zero. Sommare due valori senza segno a N bit che superano gli N bit tronca il risultato a N bit. Se vuoi catturare il riporto, dichiara il risultato largo un bit in più:
wire [7:0] a, b;
wire [8:0] sum = a + b; // 9-bit sum captures carry out
Confronto: == != < > <= >=
Tutti e sei gli operatori di confronto restituiscono un risultato di un bit: 1 se vero, 0 se falso, x se uno dei due operandi ha un bit x o z. Per gestire il caso x/z, usa === e !== (gli operatori di uguaglianza case), trattati in X and Z Values.
Qui <= è l'operatore di confronto (minore o uguale). Gli stessi caratteri indicano anche l'assegnazione non-blocking in un blocco procedurale (q <= d). È il contesto a distinguerli: dentro un'espressione è un confronto; a sinistra di un valore che viene assegnato è l'assegnazione. La sovrapposizione confonde, ma per il parser non è ambigua.
Logici: && || !
Gli operatori logici trattano gli operandi come booleani (qualsiasi valore diverso da zero è vero, zero è falso) e producono un risultato di un bit:
La distinzione fondamentale da assimilare: l'&& logico non è l'& bitwise.
4'b1100 && 4'b0011 // 1 (both are non-zero, logical AND is true)
4'b1100 & 4'b0011 // 4'b0000 (no bit positions are 1 in both)
Lo stesso vale per || e |. Usa gli operatori logici nelle condizioni degli if e nei predicati di ?:; usa quelli bitwise quando vuoi che ogni posizione di bit faccia AND/OR in modo indipendente.
&& e || sono a cortocircuito come in C: il lato destro non viene valutato se il risultato è già determinato dal lato sinistro. Conta nei testbench che controllano puntatori o chiamate a funzione; conta raramente nell'RTL sintetizzabile, dove comunque tutto avviene in parallelo.
L'operatore condizionale: ?:
Il ternario è il tuo migliore amico per costruire mux ed espressioni condizionali:
Il pattern cond ? a : b in hardware è un multiplexer 2 a 1 con selezione a 1 bit. Metterli in catena va bene per selezioni a 3 e 4 vie, ma oltre un'istruzione case (trattata in Case Statement) è decisamente più leggibile.
L'operatore ?: è anche il modo standard per dare a un registro un valore predefinito che sovrascrivi solo in certi casi:
next_value = update ? new_data : next_value;
Regole sulle larghezze: la trappola per chi inizia
Le regole di Verilog sulle larghezze degli operatori aritmetici sono famose per le loro sorprese. In breve:
- La larghezza del risultato è la massima tra le larghezze degli ingressi.
- Se il risultato viene assegnato a qualcosa di più largo, viene esteso con zeri (o con il segno, se è con segno).
- Anche i calcoli intermedi hanno la larghezza del risultato, quindi l'overflow avviene già nel passaggio intermedio.
La prima moltiplicazione va in overflow perché entrambi gli operandi sono larghi 8 bit, quindi anche il risultato è largo 8 bit, e 40000 non ci sta. La seconda funziona perché abbiamo allargato esplicitamente a prima di moltiplicare.
Le tecniche per correggere:
- Usa l'operatore di concatenazione
{8'b0, a}per estendere con zeri. - Usa
$unsigned(a)o$signed(a)per influenzare l'interpretazione. - Nel dubbio, porta le espressioni intermedie a larghezze maggiori.
Precedenza degli operatori
Non memorizzerai la tabella, ma ti affiderai ad alcune regole:
*,/,%hanno precedenza su+,-.- Il confronto ha precedenza sugli operatori logici (
&&,||). - L'operatore condizionale
?:ha una precedenza molto bassa: nelle espressioni più grandi di solito servono parentesi attorno ai suoi rami. - Nel dubbio, metti le parentesi. Il simulatore non si fa pagare a carattere.
out = (mode == 2'd0) ? a + b : a - b; // clearer with parens
Cosa viene dopo
Hai visto tutti gli operatori che somigliano ai loro cugini software. La prossima pagina, Bitwise and Reduction, tratta gli operatori che non hanno equivalenti diretti nel software: AND/OR/XOR bit per bit, più le forme di riduzione che comprimono un intero vettore in un solo bit.
Domande frequenti
Quali operatori supporta Verilog?
Verilog ha quattro grandi famiglie di operatori: aritmetici (+, -, *, /, %), di confronto (==, !=, <, >, <=, >=), logici (&&, ||, !) e bitwise/di riduzione (&, |, ^, ~, ~&, ~|, ~^). In più ci sono gli operatori di shift (<<, >>, <<<, >>>), la concatenazione {}, la replicazione {N{...}} e il condizionale ?:.
Qual è la differenza tra && e & in Verilog?
&& è l'AND logico: tratta ogni operando come un booleano (zero o diverso da zero) e restituisce un risultato di 1 bit. & è l'AND bitwise: accoppia i bit posizione per posizione. 4'b1100 && 4'b0011 vale 1 (entrambi sono diversi da zero); 4'b1100 & 4'b0011 vale 4'b0000. Usa && nelle condizioni e & per le operazioni a livello di bit.
Come funziona la divisione in Verilog?
È una divisione intera: la parte frazionaria viene scartata. 7 / 2 vale 3, non 3.5. L'operatore % restituisce il resto: 7 % 2 vale 1. La divisione per zero produce x (sconosciuto) in simulazione. Entrambi gli operatori sono tecnicamente sintetizzabili, ma su FPGA e ASIC producono hardware molto grande e lento: evitali nei percorsi sintetizzabili, a meno che il divisore sia una potenza di 2.
Cos'è l'operatore condizionale in Verilog?
?: è l'operatore ternario o condizionale, copiato dal C. cond ? a : b vale a se cond è vera (diversa da zero) e b altrimenti. È il costrutto più usato per la logica di tipo multiplexer: out = sel ? in1 : in0 costruisce un mux 2 a 1. Le catene di ?: costruiscono mux più larghi, anche se oltre i due ingressi case di solito è più chiaro.