Menu

For Loops em Verilog: Desenrolados em tempo de compilação

Como loops for em Verilog diferem de seus primos de software - são desenrolados pelo sintetizador em hardware paralelo, não executados iterativamente em runtime.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

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:

  • for procedural (dentro de always) - desenrola declarações dentro de um único bloco comportamental.
  • generate for (fora de always) - replica construções estruturais inteiras: instâncias, declarações assign, 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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR