Cosa significa "tempo" in Verilog
Verilog non ha un concetto integrato di secondi. Il simulatore fa avanzare delle "unità di tempo", cioè tick interi arbitrari, e il tuo codice usa #N per attenderne N. La direttiva `timescale è ciò che collega quei tick al tempo reale.
`timescale 1ns / 1ps
module test;
initial begin
$display("t = %0t", $time); // 0
#5;
$display("t = %0t", $time); // 5 ns
#1.5;
$display("t = %0t", $time); // 6500 ps (or 6.5 ns)
$finish;
end
endmodule
Due parametri:
- Unità (primo numero): cosa significa
#1.1nsdice che un tick è un nanosecondo. - Precisione (secondo numero): con quale finezza la simulazione traccia il tempo all'interno di quell'unità.
1psdice che i ritardi frazionari vengono arrotondati ai picosecondi.
La precisione non può essere più grossolana dell'unità (1ps / 1ns non è valido). Le scelte più comuni:
1ns / 1ps: lo standard di fatto. Tutto in nanosecondi, con precisione sotto il nanosecondo per eventuali ritardi di porta.1ps / 1ps: quando modelli circuiti estremamente veloci o quando ogni ritardo è inferiore al nanosecondo.1us / 1ns: per simulazioni lente in stile embedded (tempi di byte UART, protocolli lenti).
Dove metterlo
`timescale è una direttiva del compilatore, non un costrutto a livello di modulo. Va proprio in cima al file, prima di qualsiasi module:
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
Il suo ambito va "da questo punto in avanti nell'ordine di compilazione, fino al successivo `timescale o alla fine della compilazione". Questo ha una conseguenza sottile: un file senza un `timescale esplicito eredita quello dichiarato dal file compilato prima, e quindi dipende dall'ordine in cui il compilatore legge i file. Evita la sorpresa: metti `timescale in cima a ogni file.
L'editor nel browser di queste guide imposta un timescale predefinito al posto tuo (di solito 1ns / 1ps), ed è per questo che gli esempi delle guide precedenti funzionavano senza dichiararne uno. In un progetto reale conviene essere espliciti.
#delay in pratica
Dopo `timescale 1ns / 1ps:
#5 // wait 5 ns
#100 // wait 100 ns
#1.5 // wait 1.5 ns (precision allows it)
#0.001 // wait 1 ps (just barely above precision)
#0 // zero-delay; useful for ordering events at the same time
Dentro un blocco initial o always, #N blocca il flusso procedurale per N unità di tempo. Il simulatore mette in pausa questo blocco (gli altri blocchi concorrenti continuano a girare) e riprende dopo il ritardo.
Puoi anteporre un ritardo a un'assegnazione:
#10 a = 1; // wait 10 ns, then assign
data <= #2 new_value; // schedule the non-blocking assignment 2 ns from now
La prima forma è un classico dei testbench. La seconda (non-blocking con ritardo) si usa nella simulazione a livello di porte per modellare il ritardo di propagazione.
Generare un clock
Il pattern classico:
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
Con timescale 1ns / 1ps, #5 vale 5 ns. Il clock commuta ogni 5 ns, con un periodo di 10 ns: un clock a 100 MHz. Per cambiare la frequenza, cambia il ritardo:
| Semiperiodo | Periodo | Frequenza |
|---|---|---|
#1 | 2 ns | 500 MHz |
#2.5 | 5 ns | 200 MHz |
#5 | 10 ns | 100 MHz |
#10 | 20 ns | 50 MHz |
#25 | 50 ns | 20 MHz |
#50 | 100 ns | 10 MHz |
Se vuoi un semiperiodo frazionario (#2.5), la precisione deve supportarlo: con precisione 1ps si scende fino a 0.001 ns, quindi qualsiasi frequenza ragionevole va bene.
Mescolare timescale tra file
Un progetto reale ha molti file, e possono dichiarare timescale diversi. Il simulatore usa il timescale di ciascun file per interpretare i ritardi di quel file. Se module_a.v dichiara `timescale 1ns / 1ps e usa #5, sono 5 ns. Se module_b.v dichiara `timescale 1us / 1ns e usa #5, sono 5 us.
Di solito non te ne accorgi, perché il simulatore presenta comunque un unico asse temporale globale, ma significa che lo stesso #N in due file può voler dire cose molto diverse. La soluzione: scegli un solo timescale (lo standard del settore è 1ns / 1ps) e mettilo in cima a ogni file. Non mescolarli.
I ritardi non sono sintetizzabili
Punto fondamentale: #delay esiste solo per la simulazione. Lo strumento di sintesi legge:
// In synthesizable RTL - WRONG
always @(posedge clk) begin
out <= #2 in;
end
…e ignora il #2 (la maggior parte degli strumenti) oppure rifiuta il costrutto (i linter più severi). Il timing dell'hardware reale è determinato dal clock e dal ritardo di propagazione delle porte, entrambi invisibili nel codice sorgente.
La regola: usa # solo nei testbench. L'RTL sintetizzabile non ha ritardi #. Se ti ritrovi a volere un ritardo nel codice sintetizzabile, in realtà ti serve un contatore che scende a ogni ciclo di clock: è così che l'hardware reale "aspetta".
$time vs $realtime
Due modi per leggere il tempo di simulazione corrente:
$timerestituisce un intero a 64 bit espresso nell'unità del timescale corrente.$realtimerestituisce unrealespresso nell'unità del timescale corrente, ma con precisione completa.
Per i log dei testbench, $time basta quasi sempre. Ricorri a $realtime solo quando ti serve una precisione inferiore al tick nelle istruzioni di stampa.
Consigli pratici
- Dichiara sempre
`timescalein cima a ogni file.1ns / 1psè il valore predefinito sicuro. - Usa
#delaysolo nei testbench. Considera la sua assenza nel codice sintetizzabile come una regola fissa. - Adatta il periodo di clock alla frequenza di destinazione. Se simuli un progetto a 50 MHz, usa un periodo di clock di 20 ns: periodi sbagliati possono nascondere bug sensibili al timing.
- Per gli stimoli contati a cicli, usa
@(posedge clk)invece di#. Resiste ai cambi del periodo di clock.
Cosa viene dopo
Hai visto tutte le guide di questi tutorial Verilog. Dai fondamenti del linguaggio (wire vs reg, moduli, operatori), passando per i blocchi procedurali e il controllo del flusso, fino alla progettazione sincrona e alle macchine a stati, e infine agli strumenti di testbench che dimostrano che tutto funziona. Ora tocca a te costruire qualcosa: il playground accanto a queste guide è lo stesso simulatore che abbiamo usato finora, pronto per qualsiasi modulo tu voglia abbozzare.
Domande frequenti
Cosa significa `timescale 1ns / 1ps in Verilog?
Dice al simulatore: 'un'unità di tempo in questo file è un nanosecondo, e i tempi vengono tracciati con precisione al picosecondo.' Dopo quella direttiva, #5 attende 5 ns, #1.5 attende 1.5 ns (arrotondati ai picosecondi) e $time viene riportato in nanosecondi. Il primo numero è l'unità, il secondo è la precisione.
Serve `timescale in ogni file Verilog?
Come buona pratica, sì. L'ambito della direttiva termina al successivo \timescaleo alla fine della compilazione, quindi i file che non ne hanno uno ereditano quello dichiarato dal file compilato prima. Così il timing diventa non deterministico tra una build e l'altra. Metti`timescale 1ns / 1ps` in cima a ogni file sorgente (è la convenzione più diffusa) e non avrai mai sorprese.
Cosa significa #5 in Verilog?
#5 fa avanzare il tempo di simulazione di 5 unità di tempo. L'unità viene dalla direttiva \timescaleattiva. Con`timescale 1ns / 1ps, #5vale 5 nanosecondi. Con`timescale 1us / 1ns, #5vale 5 microsecondi. Il numero può essere frazionario:#1.5` funziona se la precisione è più fine dell'unità.
#delay è sintetizzabile in Verilog?
No. #delay agisce solo sulla simulazione: lo strumento di sintesi lo ignora o lo rifiuta. Il timing dell'hardware reale dipende dal segnale di clock e dal ritardo di propagazione delle porte, non dalle istruzioni #. Usa # liberamente nei testbench, ma non scriverlo mai nell'RTL sintetizzabile.