Одноразовый процедурный блок
initial begin ... end - родственник always. Разница: initial срабатывает ровно один раз, начиная с момента 0, и затем завершается. Никакого sensitivity list и никакого зацикливания.
Он существует ради одной вещи: настройки testbench. Установка начальных значений, запуск стимулов, открытие лог-файлов, вызов $finish после фиксированного времени симуляции - всё, что должно произойти один раз в начале запуска в известном порядке.
Шагом за шагом, что делает симулятор:
- Время продвигается к 0.
- Запускается
initial-блок. Первый$displayпечатает. dataустанавливается в8'hA5.#10продвигает симулированное время на 10 единиц.- Второй
$displayпечатает. $finishзавершает симуляцию.
Блок отработал один раз. Второго прохода нет.
Канонический скелет testbench
Почти каждый testbench, который ты напишешь, использует initial-блок вот так:
Посмотри на форму - это шаблон testbench, к которому будешь тянуться каждый раз:
- Открыть VCD dump-файл (
$dumpfile,$dumpvars) - разбираем в Dumpfile and VCD. - Установить начальные значения и подержать reset несколько тактов.
- Подать стимулы серией изменений, перемежающихся
#-задержками. $finishдля чистого завершения симуляции.
Это всё. Каждый Verilog-тест, который увидишь, включая вендорские примеры, следует этому паттерну.
Несколько initial-блоков параллельны
Несколько initial-блоков в одном module все стартуют в момент 0 одновременно. Каждый прокручивает свои операторы в своём временном куске:
Симулятор запускает оба блока в момент 0. Первый $display блока B выполняется сразу. Блок A ждёт 5 единиц перед печатью. Блок B печатает снова в момент 2. Второй $display блока A срабатывает в момент 15. $finish блока B убивает симуляцию в момент 22.
Разделение setup, генерации clock и стимулов на отдельные initial-блоки - частый стиль: каждый блок короткий и делает одно дело.
Inline-инициализация в объявлениях
Частая компактная форма - инициализировать reg прямо при объявлении. Большинство симуляторов трактует это как initial:
reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;
Эквивалентно:
reg clk;
reg reset;
reg [7:0] count;
initial begin
clk = 0;
reset = 1;
count = 0;
end
Компактная форма - то, что увидишь в testbench: она ставит начальное значение прямо рядом с объявлением, где его легко увидеть глазами.
initial - simulation-only
Синтезаторы игнорируют initial-блоки. У реального железа нет "момента ноль", когда выполняется setup-код - есть включение питания, reset-сигналы и конфигурация, приходящая от этих событий. Если нужно, чтобы регистр стартовал в известном состоянии в реальном железе, приводи его reset-сигналом внутри always @(posedge clk):
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
Ветка if (reset) - синтезируемый эквивалент initial state = IDLE. Reset - ответ на "как мне инициализировать регистр в реальном железе?"
Некоторые FPGA-потоки принимают ограниченный initial для reset-значений регистров (например, тулы Xilinx), но это per-vendor расширение. Не полагайся на это в портируемом коде.
Что встретишь внутри initial
Несколько частых паттернов кроме стандартного скелета testbench:
Завершение по таймеру
initial begin
#1000 $finish; // страховка: убиваем sim после 1000 единиц времени
end
Отдельный initial, единственная задача которого - поставить жёсткий верхний предел симуляции. Даже если твой главный блок стимулов залипнет, ожидая сигнал, который не придёт, этот блок сработает и закончит запуск.
Настройка waveform
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test); // дамп всего под scope `test`
end
Эти две строки говорят симулятору писать VCD-файл с каждым сигналом в scope test. Без них waveform не будет.
Начальный образ memory
reg [7:0] memory [0:255];
initial begin
$readmemh("image.hex", memory);
end
$readmemh загружает hex-форматированный файл в массив. Используется в testbench для CPU, чтобы предзагрузить instruction memory. Simulation-only.
Типичные ошибки
Использование initial для синтезируемой логики. Не синтезируется. Используй reset-сигналы.
Забыли $finish. Без него симулятор крутит, пока его что-то не остановит (дефолтный таймер, ручное прерывание и т.д.). Для быстрого теста - нормально; для regression-скрипта - всегда $finish.
Забыли #delay между присваиваниями стимулов. Если напишешь a = 0; b = 1; без задержки, оба произойдут в одно симуляционное время, и DUT может увидеть их одновременно, а не как разные события. Вставляй #1 или больше между разными событиями стимулов.
Попытка приводить wire из initial. То же правило, что и у always: легальная цель только reg.
Что дальше
Ты увидел оба процедурных блока. Следующий документ разбирает самую путающую тему для новичков в Verilog: Blocking vs Non-blocking Assignment. Знание, когда использовать =, а когда <=, - это разница между работающим flip-flop и race-condition-кашей.
Часто задаваемые вопросы
Что такое initial-блок в Verilog?
initial begin ... end - это процедурный блок, который срабатывает ровно один раз в начале симуляции, в момент времени 0. Это стандартное место для настройки testbench: инициализация сигналов, открытие лог-файлов, вызовы $dumpfile/$dumpvars, подача стимулов и завершение симуляции через $finish. В одном module может быть несколько initial-блоков; все они стартуют в момент 0 параллельно.
В чём разница между initial и always в Verilog?
initial срабатывает один раз в момент 0 и завершается. always перезапускается вечно - у него есть sensitivity list, и он просыпается, когда меняются перечисленные сигналы. initial используется почти исключительно в testbench. always - рабочая лошадка и testbench, и синтезируемого RTL.
Initial-блок синтезируется?
В чистом Verilog - нет. Синтезаторы игнорируют initial-блоки, потому что у реального железа нет 'момента ноль', когда выполняется setup-код. Некоторые FPGA-тулчейны принимают ограниченную форму для задания reset-значений регистров, но в общем случае это simulation-only. Держи initial-блоки в testbench; используй reset-сигналы для инициализации синтезируемой логики.
Можно ли иметь несколько initial-блоков в одном Verilog module?
Да. Каждый initial-блок стартует в момент 0 и доходит до завершения независимо. Разделение setup на несколько блоков - частый паттерн testbench: один блок для генерации clock, один для стимулов, один для дампа waveform. Они работают параллельно с момента 0; симулятор переплетает их операторы по мере продвижения времени.