A sósia de software
O loop for do Verilog é uma cópia do C:
for (i = 0; i < 8; i = i + 1) begin
// corpo
end
As mesmas três partes: inicializador, condição, incremento. O corpo re-roda enquanto a condição se mantiver.
Em um testbench, isso se comporta exatamente como você espera de software. O simulador passa por cada iteração:
Quatro iterações, quatro linhas de saída. Sem surpresas.
A surpresa vem quando você coloca um loop for em código sintetizável.
O unroll
Um loop for em um bloco always sintetizável não se torna um loop em runtime no hardware. O sintetizador o desenrola em tempo de elaboração - ele expande o loop em N cópias do corpo, onde N é a contagem de iteração:
Isso parece um loop. Em simulação, o simulador de fato faz um loop por oito iterações. Em síntese, o loop é desenrolado em oito verificações paralelas de data[0] até data[7], todas acontecendo ao mesmo tempo. O sintetizador vê:
count = 0;
if (data[0]) count = count + 1;
if (data[1]) count = count + 1;
if (data[2]) count = count + 1;
...
if (data[7]) count = count + 1;
…e então transforma a sequência em uma árvore de somadores. O comportamento em runtime é "olhe todos os 8 bits de uma vez e conte quantos são 1", em uma única varredura combinacional.
A implicação: um loop for em Verilog sintetizável não é grátis. Um loop de 64 iterações se torna 64 cópias do corpo em hardware. Se o corpo é complexo, você acabou de construir um grande bloco combinacional. Use loops quando N é pequeno (alguns até algumas dúzias). Para contagens maiores, você geralmente quer um contador clocked e uma máquina de estado.
Limites constantes são exigidos
O sintetizador só consegue desenrolar o loop se souber N em tempo de elaboração. Isso significa que os limites do loop precisam ser constantes:
// Funciona - limite e constante
for (i = 0; i < 8; i = i + 1) ...
// Funciona - limite e um parameter
for (i = 0; i < WIDTH; i = i + 1) ...
// Nao sintetiza - limite depende de um sinal de runtime
for (i = 0; i < dynamic_count; i = i + 1) ...
A última forma ainda pode funcionar em simulação, mas o sintetizador vai rejeitar. Se você genuinamente precisa de um loop contado em runtime, constrói com uma máquina de estado clocked e um register contador - hardware não tem loops com contagem-de-trip variável do jeito que software tem.
generate for vs for procedural
Uma construção separada mas relacionada é generate for, que usa um genvar e vive fora de blocos always:
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : g
bit_inverter inv(.x(in[i]), .y(out[i]));
end
endgenerate
Isso estampa 8 instâncias de bit_inverter (coberto em Module Instantiation). É estrutural - você está dizendo "faça 8 cópias deste submódulo" - não comportamental.
Distinção rápida:
forprocedural (dentro dealways) - desenrola declarações dentro de um único bloco comportamental.generate for(fora dealways) - replica construções estruturais inteiras: instâncias, declaraçõesassign, blocos nomeados.
Use o que combinar com o que você está replicando.
Quando for brilha: Operações em vetores
Loops estão no seu melhor quando você está fazendo a mesma operação em cada bit de um vetor. Population count, paridade, reversão de bytes, geração de lookup-table:
32 iterações, cada uma fazendo uma atribuição de bit - muito mais legível do que escrever 32 atribuições de wire à mão. O sintetizador desenrola limpinho.
while, repeat, forever
Além de for, Verilog tem três outras construções de loop - principalmente para testbenches:
// Roda ate uma condicao falhar
while (~done) begin
@(posedge clk);
cycles = cycles + 1;
end
// Roda N vezes - mais simples que for quando voce nao precisa de um contador
repeat (8) @(posedge clk);
// Roda para sempre - geradores de clock, loops de monitoramento
always #5 clk = ~clk;
forever begin
@(posedge clk);
$display("count=%0d", count);
end
while, repeat e forever são sintetizáveis só em casos estreitos (notavelmente repeat com uma contagem constante e um corpo clocked). Em testbenches são ferramentas úteis; em RTL sintetizável prefira um for contado mais uma máquina de estado explícita.
for procedural em testbenches
Em um testbench, loops for se comportam do jeito que software faz. Use-os livremente:
Loops aninhados varrem cada combinação de duas entradas de 2 bits. O simulador roda as iterações sequencialmente. Sem preocupações de desenrolamento - testbenches não sintetizam.
Erros comuns
Usar um loop for em código sintetizável com limite não constante. O sintetizador vai rejeitar. Se o limite é de runtime, construa um contador e uma máquina de estado.
Esquecer que o corpo do loop vira hardware paralelo. Um loop de 64 iterações com um multiplicador no corpo é 64 multiplicadores paralelos - provavelmente não é o que você quer. Para datapaths largos, construa um único multiplicador e alimente-o sequencialmente.
Misturar integer i e um reg chamado i. Os dois são escopos diferentes; o integer vence dentro do loop. Escolha nomes claros para evitar a confusão.
O que vem a seguir
Você agora tem cada construção procedural que o Verilog oferece. O próximo capítulo junta tudo nos padrões que projetistas digitais de fato entregam: Clocked Logic - flip-flops, registers e pipelines - e Finite State Machines - o idioma padrão para qualquer controlador com múltiplos modos de operação.
Perguntas frequentes
Como loops for funcionam em Verilog?
Sintaticamente parecem com C: for (i = 0; i < N; i = i + 1) statement;. Mas para código sintetizável, o loop é desenrolado em tempo de elaboração - o sintetizador expande em N cópias do corpo. Não há contador de loop em runtime e nem looping no hardware. Para testbenches, loops for se comportam como seus primos de software porque o simulador pode passar por eles sequencialmente.
Um loop for é sintetizável em Verilog?
Sim, mas só quando os limites do loop são constantes conhecidas em tempo de elaboração. O sintetizador desenrola o loop em N cópias paralelas do corpo. Se os limites dependem de um sinal de runtime, o loop não é sintetizável - você precisa convertê-lo em um design sequencial clocked.
Qual a diferença entre for e generate for em Verilog?
Um loop for dentro de um bloco always é uma construção procedural que sintetiza por desenrolamento. Um loop generate for (com genvar) é uma construção explícita em tempo de elaboração que estampa hardware estrutural - múltiplas instâncias de module, múltiplos wires, múltiplas declarações assign. Use for dentro de blocos procedurais; use generate for fora deles para replicar estrutura.
Verilog tem loop while?
Sim - while (condition) statement;. Só é sintetizável quando o sintetizador consegue provar que o loop termina com um número limitado de iterações. Na prática isso é raro, então while aparece principalmente em testbenches e código só de simulação. Para iteração sintetizável, use um loop for contado.