Что значит "время" в Verilog
У Verilog нет встроенного понятия секунд. Симулятор продвигает "единицы времени" - произвольные целочисленные тики - и твой код использует #N, чтобы ждать N таких. Директива `timescale - то, что отображает эти тики в реальное время.
`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 (или 6.5 ns)
$finish;
end
endmodule
Два параметра:
- Единица (первое число): что значит
#1.1nsговорит, что один тик - одна наносекунда. - Precision (второе число): насколько тонко симуляция отслеживает время внутри этой единицы.
1psговорит, что дробные задержки округляются до пикосекунд.
Precision не может быть грубее единицы (1ps / 1ns нелегально). Частые выборы:
1ns / 1ps- de facto стандарт. Всё в наносекундах, sub-нс precision для gate delays.1ps / 1ps- при моделировании очень быстрых схем или когда все задержки sub-нс.1us / 1ns- для медленных embedded-симуляций (UART byte times, медленные протоколы).
Куда её ставить
`timescale - это директива компилятора, не module-конструкция. Идёт в самом верху файла, до любого module:
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
Её scope - "с этого момента дальше в порядке компиляции, до следующего `timescale или конца компиляции". Это имеет тонкое следствие: файл без явного `timescale наследует то, что объявил предыдущий скомпилированный файл, что зависит от порядка чтения файлов компилятором. Избегай сюрприза: ставь `timescale наверху каждого файла.
Редактор в браузере в этих документах задаёт дефолтный timescale за тебя (обычно 1ns / 1ps), поэтому примеры в более ранних документах работали без объявления. В реальном проекте нужно быть явным.
#delay на практике
После `timescale 1ns / 1ps:
#5 // ждать 5 нс
#100 // ждать 100 нс
#1.5 // ждать 1.5 нс (precision позволяет)
#0.001 // ждать 1 пс (чуть выше precision)
#0 // нулевая задержка; полезно для упорядочивания событий в одно время
Внутри блока initial или always #N блокирует процедурный поток на N единиц времени. Симулятор приостанавливает этот блок (другие параллельные продолжают идти) и возобновляет после задержки.
Можно префиксить присваивание задержкой:
#10 a = 1; // ждать 10 нс, потом присвоить
data <= #2 new_value; // запланировать non-blocking присваивание через 2 нс
Первая форма - staple testbench. Вторая (delayed non-blocking) используется в gate-level-симуляции для моделирования propagation delay.
Генерация clock
Классический паттерн:
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
С timescale 1ns / 1ps #5 - это 5 нс. Clock переключается каждые 5 нс, давая период 10 нс - clock 100 МГц. Чтобы поменять частоту, меняй задержку:
| Полу-период | Период | Частота |
|---|---|---|
#1 | 2 нс | 500 МГц |
#2.5 | 5 нс | 200 МГц |
#5 | 10 нс | 100 МГц |
#10 | 20 нс | 50 МГц |
#25 | 50 нс | 20 МГц |
#50 | 100 нс | 10 МГц |
Если хочешь дробный полу-период (#2.5), precision должен поддерживать - 1ps precision держит всё до 0.001 нс, так что любая разумная частота - норм.
Смешивание timescale между файлами
В реальном проекте много файлов, и они могут объявлять разные timescale. Симулятор использует свой timescale каждого файла для интерпретации задержек в нём. Если module_a.v говорит `timescale 1ns / 1ps и использует #5, это 5 нс. Если module_b.v говорит `timescale 1us / 1ns и использует #5, это 5 мкс.
Это в основном невидимо, потому что симулятор представляет глобальную ось времени независимо - но значит, что одно и то же #N в двух файлах может значить дико разные вещи. Фикс: выбери один timescale (промышленный стандарт - 1ns / 1ps) и ставь его наверху каждого файла. Не смешивай.
Задержки не синтезируются
Критический момент: #delay существует только для симуляции. Синтезатор читает:
// В синтезируемом RTL - НЕПРАВИЛЬНО
always @(posedge clk) begin
out <= #2 in;
end
…и либо игнорирует #2 (большинство тулов), либо отвергает конструкцию (более строгие линтеры). Тайминг реального железа определяется clock и propagation delay вентилей - оба невидимы для исходника.
Правило: используй # только в testbench. У синтезируемого RTL нет #-задержек. Если хочется задержки в синтезируемом коде, на самом деле тебе нужен счётчик, тикающий вниз по clock - так реальное железо "ждёт".
$time vs $realtime
Два способа прочитать текущее симуляционное время:
$timeвозвращает 64-битный integer в единицах текущего timescale.$realtimeвозвращаетrealв единицах текущего timescale, но с полной precision.
Для логирования testbench $time почти всегда хватает. К $realtime тянись только когда нужна sub-tick precision в принтах.
Практичные подсказки
- Всегда объявляй
`timescaleнаверху каждого файла.1ns / 1ps- безопасный дефолт. - Используй
#delayтолько в testbench. Считай его отсутствие в синтезируемом коде статическим правилом. - Сопоставляй периоды clock целевой частоте. Если симулируешь дизайн на 50 МГц, используй период clock 20 нс - несовпадающие периоды могут скрыть timing-sensitive баги.
- Для cycle-counted стимулов используй
@(posedge clk)вместо#. Это устойчиво к изменениям периода clock.
Что дальше
Ты теперь увидел каждый документ в этих туториалах по Verilog. От основ языка (wire vs reg, modules, операторы), через процедурные блоки и control flow, в синхронный design и state machines, и наконец инструменты testbench, доказывающие, что всё работает. Время что-нибудь построить - playground рядом с этими документами использует тот же симулятор, что мы использовали всё это время, готовый к любому module, который ты набросаешь.
Часто задаваемые вопросы
Что значит `timescale 1ns / 1ps в Verilog?
Это говорит симулятору: 'одна единица времени в этом файле - это одна наносекунда, а времена отслеживаются с точностью до пикосекунды'. После этой директивы #5 ждёт 5 нс, #1.5 ждёт 1.5 нс (округлённое до пикосекунд), и $time репортится в наносекундах. Первое число - единица; второе - precision.
Нужен ли `timescale в каждом Verilog-файле?
Best practice: да. Scope директивы кончается на следующей \timescaleили в конце компиляции, так что файлы без неё наследуют то, что объявил предыдущий скомпилированный файл. Это делает timing недетерминированным между сборками. Ставь`timescale 1ns / 1ps` наверху каждого исходника - это самое распространённое соглашение - и сюрпризов не будет.
Что значит #5 в Verilog?
#5 продвигает симуляционное время на 5 единиц времени. Единица приходит из активной директивы \timescale. С `timescale 1ns / 1ps, #5- это 5 наносекунд. С`timescale 1us / 1ns, #5- это 5 микросекунд. Число может быть дробным -#1.5` работает, если precision тоньше единицы.
Синтезируется ли #delay в Verilog?
Нет. #delay влияет только на симуляцию - синтезатор игнорирует или отвергает его. Тайминг реального железа приходит от clock-сигнала и propagation delay вентилей, не от операторов #. Используй # свободно в testbench; никогда не пиши его в синтезируемом RTL.