Operatori
Valori letterali (literal values)
In ogni linguaggio, i literal values sono quelle parti del codice che rappresentano valori costanti. Per ovvi motivi, in Verilog questi sono principalmente stringhe di bit.
La definizione (completa) di un valore letterale è data da
- dimensione in bit
- formato di rappresentazione
- valore
Per esempio, 4'b0100
indica un valore di 4 bit, espressi in notazione binaria, il cui valore in binario è 0100
.
Le altre notazioni che useremo sono d
per decimale (4'd7
corrisponde al binario 0111
) e h
per esadecimale (8'had
corrisponde al binario 10101101
).
Estensione e troncamento
Verilog automaticamente estende e tronca i letterali la cui parte valore è sovra o sottospecificata rispetto al numero di bit.
Per esempio, 4'b0
viene automaticamente esteso a 4'b0000
, mentre 6'had
viene automaticamente troncato a 6'b101101
.
Operatori aritmetici
Il Verilog supporta molti degli operatori comuni, che possiamo usare in espressioni combinatorie: +
, -
, *
, /
, %
, <
, >
<=
, >=
, ==
.
Prestare attenzione, però, ai dimensionamenti in bit degli operandi e a come Verilog gli estende per eseguire le operazioni.
Operatori logici e bitwise
Verilog supporta i classici operatori logici &&
, ||
e !
.
Questi lavorano su valori booleani (0
è false
, diverso da zero è true
), e producono un solo bit come risultato.
Questi vanno distinti dagli operatori bitwise (in italiano bit a bit), lavorano per un bit alla volta (e per bit corrispondenti) producendo un risultato delle stesse dimensioni degli operandi.
Operatore | Tipo di operazione |
---|---|
& | and |
~& | nand |
| | or |
~| | nor |
^ | xor |
~^ | xnor |
~ | not |
~
Nel layout di tastiera QWERTY internazionale, la tilde ha un tasto dedicato, a sinistra dell'1.
Nel layout di tastiera QWERTY italiano, invece, la tilde non è presente. Ci sono 3 opzioni:
- passare al layout a QWERTY internazionale
- imparare scorciatoie alternative, che dipendono dal sistema operativo
- usare scripting come AutoHotkey per personalizzare il layout
L'opzione 1 richiede di imparare un layout diverso, ma è consigliabile per tutti gli usi di programmazione (risolve altri problemi come il backtick `
e rende più semplici da scrivere []{};
).
L'opzione 2 varia da sistema a sistema. Su Windows, la combinazione di tasti è alt + 126
, facendo attenzione a digitare il numero usando il tastierino numerico e non la riga dei numeri.
L'opzione 3 non è utilizzabile all'esame. Per uso personale, vedere qui.
Reduction operators
I reduction operators applicano un'operazione tra tutti i bit di un elemento di più bit, producendo un risultato su un solo bit.
Sia per esempio x
di valore 4'b0100
, allora la sua riduzione and &x
, equivalente a x[3] & x[2] & x[1] & x[0]
, varrà 1'b0
; mentre la sua riduzione or, |x
, varrà 1'b1
.
Le riduzioni possono rendere alcune espressioni combinatorie più semplici da scrivere.
Operatore | Tipo di riduzione |
---|---|
& | and |
~& | nand |
| | or |
~| | nor |
^ | xor |
~^ | xnor |
Operatore di selezione [...]
Quando si dichiara un elemento, come un wire
, si utilizza la notazione [N:0]
per indicare l'elemento ha N+1 bit, indicizzati da 0 a N.
Per esempio, per dichiarare un filo da 8 bit, scriveremo
wire [7:0] x;
Possiamo poi utilizza l'operatore per selezionare uno o più bit di un tale componente.
Per esempio, possiamo scrivere x[2]
, che seleziona il bit di posizione 2 (bit-select), e x[6:3]
, che seleziona i quattro bit dalla posizione 6 alla posizione 3 (part-select).
Operatore di concatenazione {...}
L'operatore di concatenazione viene utilizzato per combinare due o più espressioni, vettori, o bit in un'unica entità.
input [3:0] a, b;
wire [7:0] ab;
assign ab = {a, b};
L'operatore può anche essere usato a sinistra di un assegnamento.
input [7:0] x;
wire [3:0] xh, xl;
assign {xh, xl} = x;
Questo operatore corrisponde, circuitalmente, al semplice raggruppare dei fili assieme.
Non è un'operazione combinatoria, e per questo non consuma tempo.
È per questo che negli esempi sopra gli assign
non hanno alcun ritardo #T
.
Operatore di replicazione N{...}
L'operatore di ripetizione semplifica il tipico caso d'uso di ripetere un bit o un gruppo di bit N
volte.
Si può utilizzare solo all'interno di un concatenamento che sia a destra di un assegnamento e con N
costante.
È equivalente a scrivere N
volte ciò che si vuole ripetere.
input [3:0] x;
wire [15:0] x_repeated_4_times;
assign x_repeated_4_times = {4{x}}; // equivalente a {x, x, x, x}
Il suo uso più comune è l'estensione di segno di interi, mostrato più avanti.
Operazioni comuni
Estensione di segno
Quando si estende un numero su più bit bisogna considerare se il numero è un naturale o un intero. Per estendere un naturale, basta aggiungere degli zeri.
wire [7:0] x_8;
wire [11:0] x_12;
assign x_12 = {4'h0, x_8};
Per estendere un intero, dobbiamo invere replicare il bit più significativo.
wire [7:0] x_8;
wire [11:0] x_12;
assign x_12 = {4{x_8[7]}, x_8};
Shift a destra e sinistra
Per fare shift a destra e sinistra ci basta utilizzare gli operatori di selezione e concatenamento. Lo shift a sinistra è lo stesso per numeri naturali e interi, posto che non ci sia overflow.
input [7:0] x;
wire [7:0] x_mul_4;
assign x_mul_4 = {x[5:0], 2'b0};
Lo shift a destra richiede invece di considerare il segno, se stiamo lavorando con interi.
input [7:0] x; // rappresenta un numero naturale
wire [7:0] x_div_4;
assign x_div_4 = {2'b0, x[7:2]};
input [7:0] x; // rappresenta un numero intero
wire [7:0] x_div_4;
assign x_div_4 = {2{x[7]}, x[7:2]};