Menu

Pętla for w Verilogu: rozwijana podczas kompilacji

Czym pętle for w Verilogu różnią się od swoich odpowiedników z programowania: syntezator rozwija je w równoległy sprzęt, zamiast wykonywać je iteracyjnie w czasie działania.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Podobna do pętli z programowania

Pętla for w Verilogu to kopia tej z C:

for (i = 0; i < 8; i = i + 1) begin
    // body
end

Te same trzy części: inicjalizacja, warunek, inkrementacja. Ciało wykonuje się ponownie, dopóki warunek jest spełniony.

W testbenchu działa to dokładnie tak, jak można się spodziewać po programowaniu. Symulator przechodzi przez kolejne iteracje po kolei:

Cztery iteracje, cztery linie wyjścia. Bez niespodzianek.

Niespodzianka pojawia się, gdy umieścisz pętlę for w kodzie syntezowalnym.

Rozwijanie

Pętla for w syntezowalnym bloku always nie staje się w sprzęcie pętlą wykonywaną w czasie działania. Syntezator rozwija ją na etapie elaboracji, czyli zamienia ją w N kopii ciała, gdzie N to liczba iteracji:

To wygląda jak pętla. W symulacji symulator faktycznie wykonuje osiem iteracji. W syntezie pętla jest rozwijana w osiem równoległych sprawdzeń od data[0] do data[7], które dzieją się jednocześnie. Syntezator widzi:

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;

…a potem zamienia tę sekwencję w drzewo sumatorów. W działaniu oznacza to "spójrz na wszystkie 8 bitów naraz i policz, ile z nich to 1", w jednym kombinacyjnym przebiegu.

Wniosek: pętla for w syntezowalnym Verilogu nie jest darmowa. Pętla z 64 iteracjami staje się w sprzęcie 64 kopiami ciała. Jeśli ciało jest złożone, właśnie powstaje duży blok kombinacyjny. Używaj pętli, gdy N jest małe (od kilku do kilkudziesięciu). Przy większych liczbach zwykle potrzebujesz taktowanego licznika i maszyny stanów.

Wymagane są stałe granice

Syntezator może rozwinąć pętlę tylko wtedy, gdy zna N na etapie elaboracji. Oznacza to, że granice pętli muszą być stałymi:

// Works - bound is constant
for (i = 0; i < 8; i = i + 1) ...

// Works - bound is a parameter
for (i = 0; i < WIDTH; i = i + 1) ...

// Doesn't synthesize - bound depends on a runtime signal
for (i = 0; i < dynamic_count; i = i + 1) ...

Ostatnia forma może działać w symulacji, ale syntezator ją odrzuci. Jeśli naprawdę potrzebujesz pętli z liczbą iteracji znaną dopiero w czasie działania, zbuduj ją z taktowanej maszyny stanów i rejestru licznika. Sprzęt nie ma pętli o zmiennej liczbie przebiegów w takim sensie jak oprogramowanie.

generate for a proceduralne for

Osobną, ale pokrewną konstrukcją jest generate for, która używa genvar i znajduje się poza blokami 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

To tworzy 8 instancji bit_inverter (więcej w artykule Tworzenie instancji modułów). Jest to konstrukcja strukturalna: mówisz "zrób 8 kopii tego podmodułu", a nie opisujesz zachowania.

Krótkie rozróżnienie:

  • Proceduralne for (wewnątrz always): rozwija instrukcje w obrębie jednego bloku behawioralnego.
  • for w generate (poza always): powiela całe konstrukcje strukturalne, czyli instancje, instrukcje assign, nazwane bloki.

Użyj tej, która pasuje do tego, co powielasz.

Gdzie for błyszczy: operacje na wektorach

Pętle sprawdzają się najlepiej, gdy wykonujesz tę samą operację na każdym bicie wektora. Liczenie jedynek, parzystość, odwracanie bajtów, generowanie tablic przeglądowych:

32 iteracje, każda z przypisaniem jednego bitu, a to czyta się dużo łatwiej niż 32 ręcznie wypisane przypisania. Syntezator czysto je rozwija.

while, repeat, forever

Poza for Verilog ma trzy inne konstrukcje pętli, głównie do testbenchy:

// Run until a condition fails
while (~done) begin
    @(posedge clk);
    cycles = cycles + 1;
end

// Run N times - simpler than for when you don't need a counter
repeat (8) @(posedge clk);

// Run forever - clock generators, monitoring loops
always #5 clk = ~clk;
forever begin
    @(posedge clk);
    $display("count=%0d", count);
end

while, repeat i forever są syntezowalne tylko w wąskich przypadkach (zwłaszcza repeat ze stałą liczbą i taktowanym ciałem). W testbenchach to przydatne narzędzia, a w syntezowalnym RTL lepiej wybrać for z licznikiem plus jawną maszynę stanów.

Proceduralne for w testbenchach

W testbenchu pętle for zachowują się jak w programowaniu. Używaj ich swobodnie:

Zagnieżdżone pętle przechodzą przez każdą kombinację dwóch 2-bitowych wejść. Symulator wykonuje iteracje po kolei. Rozwijanie nie ma znaczenia, bo testbenchy nie są syntezowane.

Typowe błędy

Pętla for w kodzie syntezowalnym z niestałą granicą. Syntezator ją odrzuci. Jeśli granica jest znana dopiero w czasie działania, zbuduj licznik i maszynę stanów.

Zapominanie, że ciało pętli staje się równoległym sprzętem. Pętla z 64 iteracjami i mnożarką w ciele to 64 równoległe mnożarki, czyli prawdopodobnie nie to, czego chcesz. Przy szerokich ścieżkach danych zbuduj jedną mnożarkę i podawaj jej dane sekwencyjnie.

Mieszanie integer i i reg o nazwie i. To dwa różne zakresy, a wewnątrz pętli wygrywa integer. Wybieraj jasne nazwy, żeby uniknąć pomyłek.

Co dalej

Znasz już wszystkie konstrukcje proceduralne, jakie oferuje Verilog. Następny rozdział łączy je we wzorce, które projektanci układów cyfrowych naprawdę stosują: Logika taktowana, czyli przerzutniki, rejestry i potoki, oraz Maszyny stanów, czyli standardowy idiom każdego kontrolera z kilkoma trybami pracy.

Najczęściej zadawane pytania

Jak działają pętle for w Verilogu?

Składniowo wyglądają jak w C: for (i = 0; i < N; i = i + 1) statement;. Jednak w kodzie syntezowalnym pętla jest rozwijana na etapie elaboracji: syntezator rozwija ją w N kopii ciała. Nie ma licznika pętli w czasie działania ani zapętlania w sprzęcie. W testbenchach pętle for zachowują się jak w programowaniu, bo symulator może przechodzić przez nie po kolei.

Czy pętla for w Verilogu jest syntezowalna?

Tak, ale tylko wtedy, gdy granice pętli są stałymi znanymi na etapie elaboracji. Syntezator rozwija pętlę w N równoległych kopii ciała. Jeśli granice zależą od sygnału znanego dopiero w czasie działania, pętla nie jest syntezowalna i trzeba ją przerobić na taktowany projekt sekwencyjny.

Jaka jest różnica między for a generate for w Verilogu?

Pętla for wewnątrz bloku always to konstrukcja proceduralna, która syntezuje się przez rozwinięcie. Pętla generate for (z genvar) to jawna konstrukcja etapu elaboracji, która powiela sprzęt strukturalny: wiele instancji modułów, wiele przewodów, wiele instrukcji assign. Używaj for w blokach proceduralnych, a generate for poza nimi do powielania struktury.

Czy Verilog ma pętlę while?

Tak: while (condition) statement;. Jest syntezowalna tylko wtedy, gdy syntezator potrafi udowodnić, że pętla kończy się po ograniczonej liczbie iteracji. W praktyce zdarza się to rzadko, więc while pojawia się głównie w testbenchach i kodzie tylko do symulacji. Do syntezowalnych iteracji używaj zamiast tego pętli for z licznikiem.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ