Menu

Initial-блок в Verilog: настроечный код при старте симуляции

Чем initial-блоки отличаются от always, почему они существуют только в симуляции, и частые паттерны - стимулы, настройка waveform, заголовки логов - для которых их используют.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Одноразовый процедурный блок

initial begin ... end - родственник always. Разница: initial срабатывает ровно один раз, начиная с момента 0, и затем завершается. Никакого sensitivity list и никакого зацикливания.

Он существует ради одной вещи: настройки testbench. Установка начальных значений, запуск стимулов, открытие лог-файлов, вызов $finish после фиксированного времени симуляции - всё, что должно произойти один раз в начале запуска в известном порядке.

Шагом за шагом, что делает симулятор:

  1. Время продвигается к 0.
  2. Запускается initial-блок. Первый $display печатает.
  3. data устанавливается в 8'hA5.
  4. #10 продвигает симулированное время на 10 единиц.
  5. Второй $display печатает.
  6. $finish завершает симуляцию.

Блок отработал один раз. Второго прохода нет.

Канонический скелет testbench

Почти каждый testbench, который ты напишешь, использует initial-блок вот так:

Посмотри на форму - это шаблон testbench, к которому будешь тянуться каждый раз:

  1. Открыть VCD dump-файл ($dumpfile, $dumpvars) - разбираем в Dumpfile and VCD.
  2. Установить начальные значения и подержать reset несколько тактов.
  3. Подать стимулы серией изменений, перемежающихся #-задержками.
  4. $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; симулятор переплетает их операторы по мере продвижения времени.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ