Um bloco procedural one-shot
initial begin ... end é o irmão do always. A diferença: initial roda exatamente uma vez, começando no tempo 0, e depois termina. Não há lista de sensibilidade e não há looping.
Ele existe por uma razão: setup de testbench. Definir valores iniciais, disparar estímulo, abrir arquivos de log, chamar $finish depois de um tempo fixo de simulação - todas as coisas que você quer que aconteçam uma vez no início de uma execução, em uma ordem conhecida.
Passo a passo do que o simulador faz:
- O tempo avança para 0.
- O bloco
initialcomeça. O primeiro$displayimprime. dataé setado para8'hA5.#10avança o tempo simulado em 10 unidades.- O segundo
$displayimprime. $finishtermina a simulação.
O bloco rodou uma vez. Não há uma segunda passagem.
O esqueleto canônico de testbench
Quase todo testbench que você vai escrever usa um bloco initial assim:
Olhe para a forma - é o template de testbench que você vai usar toda vez:
- Abre o arquivo de dump VCD (
$dumpfile,$dumpvars) - coberto em Dumpfile and VCD. - Define valores iniciais e segura reset por alguns ciclos.
- Aciona estímulo através de uma série de mudanças intercaladas com delays
#. $finishpara terminar a simulação limpamente.
É isso. Cada teste Verilog que você vai ver, incluindo os exemplos dos fornecedores, segue esse padrão.
Múltiplos blocos initial são paralelos
Vários blocos initial no mesmo module começam todos no tempo 0 simultaneamente. Cada um roda suas próprias declarações em sua própria fatia de tempo:
O simulador inicia ambos os blocos no tempo 0. O primeiro $display do bloco B roda imediatamente. O bloco A espera 5 unidades antes de imprimir. O bloco B imprime de novo no tempo 2. O segundo print do bloco A dispara no tempo 15. O $finish do bloco B mata a simulação no tempo 22.
Dividir setup, geração de clock e estímulo em blocos initial separados é um estilo comum - cada bloco é curto e faz uma coisa.
Inicialização inline em declarações
Uma forma compacta comum: inicializar um reg no momento da declaração. A maioria dos simuladores trata isso como um initial:
reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;
Isso é equivalente a:
reg clk;
reg reset;
reg [7:0] count;
initial begin
clk = 0;
reset = 1;
count = 0;
end
A forma compacta é o que você vai ver em testbenches - coloca o valor inicial logo ao lado da declaração, onde é fácil de escanear.
initial é só de simulação
Ferramentas de síntese ignoram blocos initial. Hardware real não tem um "momento zero" quando código de setup roda - tem power-on, sinais de reset e a configuração que vem desses eventos. Se você precisa que um register comece em um estado conhecido em hardware real, acione-o com um sinal de reset dentro de um always @(posedge clk):
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
Esse ramo if (reset) é o equivalente sintetizável de initial state = IDLE. Reset é a resposta para "como inicializo um register em hardware real?"
Algumas flows de FPGA aceitam um initial restrito para valores de reset de register (ferramentas Xilinx, por exemplo), mas isso é uma extensão por fornecedor. Não confie nisso em código portável.
Coisas que você verá dentro de initial
Alguns padrões comuns além do esqueleto padrão de testbench:
Terminação com delay
initial begin
#1000 $finish; // rede de seguranca: mata a sim depois de 1000 unidades de tempo
end
Um initial separado cujo único trabalho é colocar um limite superior duro na simulação. Mesmo se seu bloco principal de estímulo travar esperando por um sinal que nunca vem, esse bloco dispara e termina a execução.
Setup de forma de onda
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test); // dump tudo sob o escopo `test`
end
Essas duas linhas dizem ao simulador para escrever um arquivo VCD com cada sinal no escopo de teste. Sem elas, sem forma de onda.
Imagem de memória inicial
reg [7:0] memory [0:255];
initial begin
$readmemh("image.hex", memory);
end
$readmemh carrega um arquivo formatado em hex no array. Usado em testbenches de CPU para pré-carregar memória de instruções. Só de simulação.
Erros comuns
Usar initial para lógica sintetizável. Não vai sintetizar. Use sinais de reset no lugar.
Esquecer $finish. Sem ele o simulador roda até algo mais o parar (um limite de tempo padrão, interrupção manual, etc.). Para um teste rápido isso está bom; para um script de regressão, sempre use $finish.
Esquecer #delay entre atribuições de estímulo. Se você escrever a = 0; b = 1; sem delay, ambos acontecem no mesmo tempo de simulação e o DUT pode vê-los simultaneamente em vez de como eventos separados. Insira #1 ou mais longo entre eventos de estímulo distintos.
Tentar conduzir um wire a partir de initial. Mesma regra do always: só reg é um alvo legal.
O que vem a seguir
Você viu os dois sabores de bloco procedural. O próximo doc cobre o tópico mais confuso do Verilog para iniciantes: Blocking vs Non-blocking Assignment. Saber quando usar = e quando usar <= é a diferença entre um flip-flop funcionando e uma bagunça de race conditions.
Perguntas frequentes
O que é um initial block em Verilog?
initial begin ... end é um bloco procedural que roda exatamente uma vez no início da simulação, no tempo 0. É o lugar padrão para configurar o estado do testbench: inicializar sinais, abrir arquivos de log, chamar $dumpfile/$dumpvars, acionar estímulo e finalizar a simulação com $finish. Múltiplos blocos initial podem coexistir em um module; todos começam no tempo 0 em paralelo.
Qual a diferença entre initial e always em Verilog?
initial roda uma vez no tempo 0 e depois termina. always re-roda para sempre - tem uma lista de sensibilidade e acorda sempre que os sinais listados mudam. initial é usado quase exclusivamente em testbenches. always é o burro de carga tanto de testbenches quanto de RTL sintetizável.
Bloco initial é sintetizável?
Não em Verilog puro. Ferramentas de síntese ignoram blocos initial porque hardware real não tem um momento 'tempo zero' quando código de setup roda. Algumas toolchains de FPGA aceitam uma forma restrita para definir valores de reset de register, mas o caso geral é só de simulação. Mantenha blocos initial em testbenches; use sinais de reset para inicializar lógica sintetizável.
Posso ter múltiplos blocos initial em um module Verilog?
Sim. Cada bloco initial começa no tempo 0 e roda até o fim independentemente. Dividir setup em múltiplos blocos é um padrão comum de testbench - um bloco para geração de clock, um para estímulo, um para dump de forma de onda. Eles rodam concorrentemente a partir do tempo 0; o simulador intercala suas declarações conforme o tempo avança.