Menu

Blocco initial in Verilog: codice di setup all'avvio della simulazione

In cosa i blocchi initial differiscono da always, perché esistono solo in simulazione e i pattern comuni in cui si usano: stimoli, setup delle forme d'onda, intestazioni dei log.

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

Un blocco procedurale che si esegue una volta

initial begin ... end è il fratello di always. La differenza: initial si esegue esattamente una volta, partendo dal tempo 0, e poi termina. Non ha lista di sensibilità e non si ripete.

Esiste per un solo motivo: il setup del testbench. Impostare valori iniziali, avviare gli stimoli, aprire file di log, chiamare $finish dopo un tempo di simulazione prefissato: tutte cose che vuoi fare una volta all'inizio di un'esecuzione, in un ordine noto.

Ecco cosa fa il simulatore, passo per passo:

  1. Il tempo avanza fino a 0.
  2. Il blocco initial parte. Il primo $display stampa.
  3. data viene impostato a 8'hA5.
  4. #10 fa avanzare il tempo simulato di 10 unità.
  5. Il secondo $display stampa.
  6. $finish termina la simulazione.

Il blocco è stato eseguito una volta. Non c'è un secondo passaggio.

Lo scheletro canonico di un testbench

Quasi ogni testbench che scriverai usa un blocco initial come questo:

Guarda la struttura: è il modello di testbench a cui ricorrerai ogni volta:

  1. Apri il file di dump VCD ($dumpfile, $dumpvars), trattato in Dumpfile and VCD.
  2. Imposta i valori iniziali e tieni attivo il reset per qualche ciclo.
  3. Genera gli stimoli con una serie di cambiamenti intervallati da ritardi #.
  4. $finish per terminare la simulazione in modo pulito.

Tutto qui. Ogni test Verilog che vedrai, compresi gli esempi dei produttori, segue questo pattern.

Più blocchi initial sono paralleli

Più blocchi initial nello stesso modulo partono tutti al tempo 0 contemporaneamente. Ognuno esegue le proprie istruzioni nella propria porzione di tempo:

Il simulatore avvia entrambi i blocchi al tempo 0. Il primo $display del blocco B viene eseguito subito. Il blocco A aspetta 5 unità prima di stampare. Il blocco B stampa di nuovo al tempo 2. La seconda stampa del blocco A scatta al tempo 15. Il $finish del blocco B termina la simulazione al tempo 22.

Dividere setup, generazione del clock e stimoli in blocchi initial separati è uno stile comune: ogni blocco è breve e fa una sola cosa.

Inizializzazione nella dichiarazione

Una forma compatta molto diffusa: inizializzare un reg direttamente nella dichiarazione. La maggior parte dei simulatori la tratta come un initial:

reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;

Equivale a:

reg clk;
reg reset;
reg [7:0] count;

initial begin
    clk = 0;
    reset = 1;
    count = 0;
end

La forma compatta è quella che vedrai nei testbench: mette il valore iniziale accanto alla dichiarazione, dove è facile da individuare.

initial vale solo in simulazione

Gli strumenti di sintesi ignorano i blocchi initial. L'hardware reale non ha un "momento zero" in cui gira del codice di setup: ha l'accensione, i segnali di reset e la configurazione che deriva da questi eventi. Se ti serve che un registro parta da uno stato noto nell'hardware reale, pilotalo con un segnale di reset dentro un always @(posedge clk):

always @(posedge clk) begin
    if (reset) state <= IDLE;
    else       state <= next_state;
end

Quel ramo if (reset) è l'equivalente sintetizzabile di initial state = IDLE. Il reset è la risposta alla domanda "come inizializzo un registro sull'hardware reale?".

Alcuni flussi per FPGA accettano un initial limitato per i valori di reset dei registri (per esempio gli strumenti Xilinx), ma è un'estensione specifica del produttore. Non farci affidamento nel codice portabile.

Cosa trovi dentro initial

Alcuni pattern comuni oltre allo scheletro standard del testbench:

Terminazione dopo un ritardo

initial begin
    #1000 $finish;   // safety net: kill the sim after 1000 time units
end

Un initial separato il cui unico compito è porre un limite massimo alla simulazione. Anche se il blocco principale degli stimoli resta bloccato in attesa di un segnale che non arriva mai, questo blocco scatta e termina l'esecuzione.

Setup delle forme d'onda

initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, test);   // dump everything under the `test` scope
end

Queste due righe dicono al simulatore di scrivere un file VCD con tutti i segnali dello scope di test. Senza di esse non ottieni nessuna forma d'onda.

Immagine iniziale della memoria

reg [7:0] memory [0:255];

initial begin
    $readmemh("image.hex", memory);
end

$readmemh carica nell'array un file in formato esadecimale. Si usa nei testbench delle CPU per precaricare la memoria delle istruzioni. Vale solo in simulazione.

Errori comuni

Usare initial per la logica sintetizzabile. Non si sintetizza. Usa i segnali di reset.

Dimenticare $finish. Senza, il simulatore continua finché qualcos'altro non lo ferma (un limite di tempo predefinito, un'interruzione manuale e così via). Per un test veloce va bene; in uno script di regressione usa sempre $finish.

Dimenticare i #delay tra le assegnazioni degli stimoli. Se scrivi a = 0; b = 1; senza ritardo, entrambe avvengono nello stesso tempo di simulazione e il DUT potrebbe vederle insieme invece che come eventi separati. Inserisci #1 o più tra eventi di stimolo distinti.

Cercare di pilotare un wire da initial. Stessa regola di always: solo reg è una destinazione valida.

Cosa viene dopo

Hai visto le due varianti di blocco procedurale. La prossima pagina tratta l'argomento che confonde di più chi inizia con Verilog: Blocking vs Non-blocking Assignment. Sapere quando usare = e quando <= fa la differenza tra un flip-flop funzionante e un pasticcio di race condition.

Domande frequenti

Cos'è un blocco initial in Verilog?

initial begin ... end è un blocco procedurale che si esegue esattamente una volta all'avvio della simulazione, al tempo 0. È il posto standard per preparare lo stato del testbench: inizializzare i segnali, aprire i file di log, chiamare $dumpfile/$dumpvars, generare gli stimoli e chiudere la simulazione con $finish. In un modulo possono coesistere più blocchi initial; partono tutti al tempo 0, in parallelo.

Qual è la differenza tra initial e always in Verilog?

initial si esegue una volta al tempo 0 e poi termina. always si riesegue all'infinito: ha una lista di sensibilità e si risveglia ogni volta che cambiano i segnali elencati. initial si usa quasi esclusivamente nei testbench. always è il cavallo da tiro sia dei testbench sia dell'RTL sintetizzabile.

Il blocco initial è sintetizzabile?

Non nel Verilog semplice. Gli strumenti di sintesi ignorano i blocchi initial, perché l'hardware reale non ha un 'tempo zero' in cui gira del codice di setup. Alcune toolchain per FPGA accettano una forma limitata per impostare i valori di reset dei registri, ma in generale vale solo per la simulazione. Tieni i blocchi initial nei testbench e usa i segnali di reset per inizializzare la logica sintetizzabile.

Si possono avere più blocchi initial in un modulo Verilog?

Sì. Ogni blocco initial parte al tempo 0 e arriva alla fine in modo indipendente. Dividere il setup in più blocchi è un pattern comune nei testbench: un blocco per generare il clock, uno per gli stimoli, uno per il dump delle forme d'onda. Girano in modo concorrente dal tempo 0 e il simulatore ne alterna le istruzioni man mano che il tempo avanza.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA