Menu

Parametry w Verilogu: konfigurowalne moduły

Jak używać parameter i localparam do definiowania stałych czasu kompilacji, parametryzowania szerokości i głębokości oraz nadpisywania wartości przy tworzeniu instancji modułu.

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

Stałe z haczykiem

parameter to stała: kompilator ustala jej wartość i wbudowuje ją w projekt, zanim zacznie się symulacja. Haczyk polega na tym, że ten, kto tworzy instancję modułu, może tę wartość zmienić. Ten sam plik źródłowy modułu może dać układy różnej wielkości, zależnie od tego, jak wywołujący go sparametryzują.

Tak buduje się bloki IP wielokrotnego użytku. Moduł FIFO z parametrami WIDTH i DEPTH może obsłużyć każdy zespół w firmie. Licznik z parametrem WIDTH to ten sam kod, niezależnie od tego, czy liczy do 16, czy do 4 miliardów.

Podstawowa deklaracja parametru

Trzy nowe elementy składni:

  1. Blok parametrów: #( parameter WIDTH = 8 ) między nazwą modułu a listą portów. Wartość domyślna 8 jest używana, gdy nikt jej nie nadpisze.
  2. Miejsce użycia: output reg [WIDTH-1:0] count. Parametr to po prostu stała, którą wstawiamy w zakres bitów. Ten sam kod modułu daje wyjście 8-bitowe albo 16-bitowe, zależnie od WIDTH.
  3. Składnia nadpisania: counter #(.WIDTH(16)) dut16(...) przy tworzeniu instancji. Blok #(...) stoi przed nazwą instancji, a po nazwie modułu. Parametry, których nie wymienisz, zachowują wartości domyślne.

localparam: stałe wewnętrzne

Są stałe, których wywołujący nie powinni nadpisywać. Klasycznym przykładem jest kodowanie stanów:

localparam udostępnia IDLE, RUNNING i DONE wewnątrz modułu, ale nie pozwala ich nadpisać przy tworzeniu instancji. To właściwy wybór: zmiana z zewnątrz tego, jaka wartość stanu oznacza IDLE, byłaby przerażająca.

Używaj parameter do rzeczy, które wywołujący powinni konfigurować (szerokości, głębokości, opcje zachowania), a localparam do wszystkiego innego. Częsty wzorzec to wyprowadzanie localparam z parameter:

parameter WIDTH = 32;
localparam WIDTH_M1 = WIDTH - 1;   // computed once; not overrideable

Sparametryzowane szerokości w praktyce

Najczęstsze zastosowanie parametrów to elastyczne szerokości magistral. Oto sumator, który działa przy dowolnej szerokości:

Jeden plik źródłowy, dwie instancje, dwie różne szerokości, bez kopiowania i wklejania. Na tym polega cała korzyść z parametrów.

Wiele parametrów, nadpisywanie po nazwie

W większych modułach często masz kilka parametrów. Nadpisuj je po nazwie w dowolnej kolejności:

module fifo #(
    parameter WIDTH = 8,
    parameter DEPTH = 16,
    parameter AFULL = DEPTH - 2
)(
    input  wire             clk,
    input  wire             reset,
    // ... ports ...
);
    // ...
endmodule

// At the call site:
fifo #(.WIDTH(32), .DEPTH(1024)) cmd_queue (.clk(clk), .reset(reset), ...);

Nie musisz nadpisywać każdego parametru: niewymienione zachowują wartości domyślne. Domyślna wartość AFULL w przykładzie jest obliczana z DEPTH, więc jeśli nadpiszesz DEPTH, AFULL automatycznie za nim podąży. Taką samą zależność obsłużyłby localparam, gdyby wywołujący nie mieli móc nadpisywać AFULL niezależnie.

Typowe błędy

Brak # przy nadpisaniu. counter (.WIDTH(16)) dut(...) wygląda jak nadpisanie, ale Verilog odczytuje (.WIDTH(16)) jako połączenie portu. Potrzebujesz counter #(.WIDTH(16)) dut(...).

Używanie parametrów tam, gdzie wartość nie jest wystarczająco stała. Parametry są rozwiązywane na etapie elaboracji, zanim powstanie jakikolwiek sygnał. Nie da się parametryzować sygnałem z czasu działania. Jeśli wartość zależy od tego, co robi wejście w czasie symulacji, to nie jest parametr, tylko logika.

Mylenie parameter i localparam w listach portów. W bloku #(...) na początku umieszcza się tylko parameter. localparam znajduje się w ciele modułu. Jeśli je zamienisz, kompilator ci o tym powie.

Co dalej

Potrafisz już tworzyć moduły, których rozmiar jest ustalany podczas kompilacji. Kolejne dwa artykuły omawiają reguły zapisu stałych literałowych (8'h1F, 4'b1010, 32'd100) oraz wartości x i z, które pojawiają się, gdy sygnały nie są sterowane albo mają sprzeczne źródła. Potem przechodzimy do operatorów, czyli wszystkiego, co można zrobić z wektorami i parametrami, które już znasz.

Najczęściej zadawane pytania

Czym jest parametr w Verilogu?

parameter to stała czasu kompilacji zadeklarowana wewnątrz modułu. Można jej używać wszędzie tam, gdzie dozwolona jest stała: w szerokościach magistral, głębokościach pamięci, kodowaniu stanów, wartościach domyślnych. Co kluczowe, każda instancja modułu może nadpisać parametr, więc ten sam plik źródłowy może dać na przykład 8-bitowy licznik w jednym miejscu i 32-bitowy w innym.

Jaka jest różnica między parameter a localparam w Verilogu?

Wartości parameter można nadpisać spoza modułu przy tworzeniu jego instancji. Wartości localparam nie: to wyłącznie stałe wewnętrzne. Używaj localparam do kodowania stanów i stałych pochodnych, w które autor modułu nie chce, żeby wywołujący ingerowali.

Jak nadpisać parametr w Verilogu?

Przy tworzeniu instancji modułu dodaj blok nadpisania parametrów przed nazwą instancji: counter #(.WIDTH(16)) my_inst (.clk(clk), .count(count));. Składnia .WIDTH(16) ustawia ten konkretny parametr, a parametry, których nie wymienisz, zachowują wartości domyślne. Kilka nadpisań oddziela się przecinkami wewnątrz #(...).

Czym jest moduł sparametryzowany?

To moduł, który udostępnia jedną lub więcej deklaracji parameter, które wywołujący mogą nadpisać. Sparametryzowana kolejka FIFO może mieć parametry WIDTH i DEPTH, żeby ten sam kod dawał w jednym miejscu FIFO o szerokości 32 bitów i głębokości 16, a w innym o szerokości 8 bitów i głębokości 1024. Tak pisze się biblioteki bloków IP wielokrotnego użytku.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ