Menu

Cicli for in Verilog: srotolati in fase di compilazione

In cosa i cicli for di Verilog differiscono dai loro cugini software: il sintetizzatore li srotola in hardware parallelo invece di eseguirli iterazione per iterazione a runtime.

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

Il sosia del software

Il ciclo for di Verilog è una copia di quello del C:

for (i = 0; i < 8; i = i + 1) begin
    // body
end

Le stesse tre parti: inizializzazione, condizione, incremento. Il corpo si ripete finché la condizione è vera.

In un testbench si comporta esattamente come ti aspetteresti dal software. Il simulatore esegue un'iterazione dopo l'altra:

Quattro iterazioni, quattro righe di output. Nessuna sorpresa.

La sorpresa arriva quando metti un ciclo for dentro codice sintetizzabile.

Lo srotolamento

Un ciclo for in un blocco always sintetizzabile non diventa un ciclo a runtime nell'hardware. Il sintetizzatore lo srotola durante l'elaborazione: espande il ciclo in N copie del corpo, dove N è il numero di iterazioni:

Sembra un ciclo. In simulazione il simulatore esegue davvero otto iterazioni. In sintesi il ciclo viene srotolato in otto controlli paralleli, da data[0] a data[7], tutti contemporanei. Il sintetizzatore vede:

count = 0;
if (data[0]) count = count + 1;
if (data[1]) count = count + 1;
if (data[2]) count = count + 1;
...
if (data[7]) count = count + 1;

…e poi trasforma la sequenza in un albero di sommatori. Il comportamento a runtime è "guarda tutti gli 8 bit insieme e conta quanti valgono 1", in un'unica passata combinatoria.

La conseguenza: un ciclo for in Verilog sintetizzabile non è gratis. Un ciclo di 64 iterazioni diventa 64 copie del corpo in hardware. Se il corpo è complesso, hai appena costruito un grosso blocco combinatorio. Usa i cicli quando N è piccolo (da pochi elementi a qualche decina). Per conteggi più grandi di solito ti servono un contatore sincronizzato e una macchina a stati.

Servono limiti costanti

Il sintetizzatore può srotolare il ciclo solo se conosce N durante l'elaborazione. Quindi i limiti del ciclo devono essere costanti:

// Works - bound is constant
for (i = 0; i < 8; i = i + 1) ...

// Works - bound is a parameter
for (i = 0; i < WIDTH; i = i + 1) ...

// Doesn't synthesize - bound depends on a runtime signal
for (i = 0; i < dynamic_count; i = i + 1) ...

L'ultima forma potrebbe funzionare in simulazione, ma il sintetizzatore la rifiuta. Se ti serve davvero un ciclo con conteggio a runtime, lo costruisci con una macchina a stati sincronizzata e un registro contatore: l'hardware non ha cicli con un numero variabile di iterazioni come il software.

generate for e for procedurale

Un costrutto distinto ma collegato è generate for, che usa un genvar e vive fuori dai blocchi always:

genvar i;
generate
    for (i = 0; i < 8; i = i + 1) begin : g
        bit_inverter inv(.x(in[i]), .y(out[i]));
    end
endgenerate

Questo crea 8 istanze di bit_inverter (vedi Module Instantiation). È strutturale, cioè stai dicendo "crea 8 copie di questo sottomodulo", non comportamentale.

In breve:

  • for procedurale (dentro always): srotola istruzioni all'interno di un singolo blocco comportamentale.
  • for di generate (fuori da always): replica interi costrutti strutturali, cioè istanze, istruzioni assign, blocchi con nome.

Usa quello che corrisponde a ciò che stai replicando.

Dove for dà il meglio: operazioni sui vettori

I cicli danno il meglio quando applichi la stessa operazione a ogni bit di un vettore: conteggio dei bit a uno, parità, inversione dei byte, generazione di tabelle di lookup:

32 iterazioni, ognuna con un'assegnazione di un bit: molto più leggibile che scrivere a mano 32 assegnazioni di wire. Il sintetizzatore lo srotola senza problemi.

while, repeat, forever

Oltre a for, Verilog ha altri tre costrutti di ciclo, usati soprattutto nei testbench:

// Run until a condition fails
while (~done) begin
    @(posedge clk);
    cycles = cycles + 1;
end

// Run N times - simpler than for when you don't need a counter
repeat (8) @(posedge clk);

// Run forever - clock generators, monitoring loops
always #5 clk = ~clk;
forever begin
    @(posedge clk);
    $display("count=%0d", count);
end

while, repeat e forever sono sintetizzabili solo in casi limitati (in particolare repeat con un conteggio costante e un corpo sincronizzato dal clock). Nei testbench sono strumenti utili; nell'RTL sintetizzabile preferisci un for con conteggio più una macchina a stati esplicita.

for procedurale nei testbench

In un testbench i cicli for si comportano come nel software. Usali liberamente:

I cicli annidati provano tutte le combinazioni di due ingressi a 2 bit. Il simulatore esegue le iterazioni in sequenza. Nessun problema di srotolamento: i testbench non vengono sintetizzati.

Errori comuni

Usare un ciclo for in codice sintetizzabile con un limite non costante. Il sintetizzatore lo rifiuta. Se il limite è noto solo a runtime, costruisci un contatore e una macchina a stati.

Dimenticare che il corpo del ciclo diventa hardware parallelo. Un ciclo di 64 iterazioni con un moltiplicatore nel corpo equivale a 64 moltiplicatori in parallelo: probabilmente non è quello che vuoi. Per datapath larghi, costruisci un solo moltiplicatore e alimentalo in sequenza.

Mescolare integer i e un reg chiamato i. Sono in scope diversi e dentro il ciclo vince l'intero. Scegli nomi chiari per evitare confusione.

Cosa viene dopo

Ora conosci tutti i costrutti procedurali che Verilog offre. Il prossimo capitolo li mette insieme nei pattern che i progettisti digitali usano davvero: Clocked Logic, cioè flip-flop, registri e pipeline, e Finite State Machines, l'idioma standard per qualsiasi controller con più modalità di funzionamento.

Domande frequenti

Come funzionano i cicli for in Verilog?

Dal punto di vista sintattico somigliano al C: for (i = 0; i < N; i = i + 1) statement;. Ma nel codice sintetizzabile il ciclo viene srotolato durante l'elaborazione: il sintetizzatore lo espande in N copie del corpo. Non c'è un contatore del ciclo a runtime e nell'hardware non avviene nessuna iterazione. Nei testbench, invece, i cicli for si comportano come i loro cugini software, perché il simulatore può eseguirli un passo alla volta.

Un ciclo for è sintetizzabile in Verilog?

Sì, ma solo quando i limiti del ciclo sono costanti note durante l'elaborazione. Il sintetizzatore srotola il ciclo in N copie parallele del corpo. Se i limiti dipendono da un segnale a runtime, il ciclo non è sintetizzabile: devi trasformarlo in un progetto sequenziale sincronizzato dal clock.

Qual è la differenza tra for e generate for in Verilog?

Un ciclo for dentro un blocco always è un costrutto procedurale che si sintetizza per srotolamento. Un ciclo generate for (con genvar) è un costrutto esplicito dell'elaborazione che crea hardware strutturale: più istanze di modulo, più wire, più istruzioni assign. Usa for dentro i blocchi procedurali e generate for fuori da essi per replicare la struttura.

Verilog ha un ciclo while?

Sì: while (condition) statement;. È sintetizzabile solo quando il sintetizzatore riesce a dimostrare che il ciclo termina con un numero limitato di iterazioni. In pratica succede di rado, quindi while compare soprattutto nei testbench e nel codice solo per la simulazione. Per le iterazioni sintetizzabili usa invece un ciclo for con conteggio.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA