Passa al contenuto principale

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

  1. dimensione in bit
  2. formato di rappresentazione
  3. 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.

OperatoreTipo di operazione
&and
~&nand
|or
~|nor
^xor
~^xnor
~not
Come scrivere la tilde ~

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:

  1. passare al layout a QWERTY internazionale
  2. imparare scorciatoie alternative, che dipendono dal sistema operativo
  3. 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.

OperatoreTipo 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;
Raggruppare fili non ha nessun costo

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]};