Samouczek tablic w Verilogu: od podstaw po zaawansowane techniki SystemVerilog

目次

1. Wstęp

Verilog jest powszechnie używany jako język opisu sprzętu (HDL) i jest nieodzowny w projektowaniu układów dla FPGA i ASIC. Aby efektywnie projektować w Verilog, kluczowe jest solidne zrozumienie tablic. Wykorzystując tablice, możesz zarządzać zbiorami danych w zwięzły i intuicyjny sposób, co poprawia czytelność i utrzymywalność opisów układów. Tablice są szczególnie przydatne przy grupowaniu wielu sygnałów lub reprezentowaniu struktur pamięci, takich jak RAM. Ten artykuł koncentruje się na słowie kluczowym „Verilog arrays” i zapewnia kompleksowe wyjaśnienie, począwszy od podstaw definicji tablic, po praktyczne techniki zastosowań. Omówimy typy tablic, ulepszenia SystemVerilog, typowe błędy oraz FAQ, aby pogłębić Twoją wiedzę. Nawet początkujący znajdą ten przewodnik przystępny, ponieważ w całym tekście zamieszczamy praktyczne przykłady kodu. Czytaj do końca, aby uzyskać pełny przegląd.

2. Podstawowe typy danych w Verilogu

Zanim zaczniesz pracować z tablicami w Verilogu, ważne jest zrozumienie podstawowych typów danych. Verilog oferuje kilka kluczowych typów danych służących do obsługi sygnałów logicznych używanych w projektowaniu układów.

Różnica między reg a wire

Najczęściej używanymi typami danych w Verilogu są „reg” (rejestr) i „wire”. Należy je stosować odpowiednio do zachowania sygnałów logicznych.
  • typ wire – wire jest używany jako linia połączeniowa między modułami lub układami. Zawsze musi być sterowany przez inny sygnał, a przypisania dokonuje się przy użyciu instrukcji assign. Nadaje się do wyjść układów kombinacyjnych. Przykład:
  wire a;
  assign a = b & c;
  • typ reg – reg jest używany jako zmienna do tymczasowego przechowywania wartości. Jest przypisywany wewnątrz bloków procesowych, takich jak always, i zazwyczaj służy do modelowania elementów pamięci (przerzutników lub latch’y). Przykład:
  reg q;
  always @(posedge clk) begin
      q <= d;
  end

Typy danych używalne w tablicach

W Verilogu tablice są najczęściej definiowane przy użyciu typu reg, choć tablice typu wire również mogą być stosowane w niektórych przypadkach. Wcześniejsze wersje Verilogu nie obsługiwały tablic wielowymiarowych. Ograniczenie to zostało znacząco poprawione w SystemVerilog. Oto prosty przykład składni tablicy:
reg [7:0] data_array [0:15];  // An array storing 16 elements, each 8 bits wide
Rozumiejąc podstawy typów danych, możesz uniknąć nieporozumień przy deklarowaniu i używaniu tablic. Niewłaściwe użycie reg i wire może prowadzić do błędów symulacji lub syntezy, więc zachowaj ostrożność.

3. Podstawowe pojęcia o tablicach

W Verilogu tablica jest używana, gdy chcesz zarządzać wieloma sygnałami tego samego typu zbiorczo. Tablice ułatwiają organizację sygnałów, poprawiając czytelność i możliwość ponownego użycia kodu.

Deklarowanie tablic

Verilog głównie obsługuje jednowymiarowe tablice. Składnia wygląda następująco:
reg [bit-width] array_name [index-range];
Przykład:
reg [7:0] data_array [0:15];  // An array of 16 elements, each storing 8-bit data
W tym przypadku data_array ma 16 elementów indeksowanych od 0 do 15, przy czym każdy element przechowuje wartość 8‑bitową (1 bajt).

Dostęp do elementów tablicy

Do każdego elementu tablicy możesz uzyskać dostęp, określając jego numer indeksu. Podobnie jak w C, indeksy tablic zaczynają się od 0.
data_array[0] = 8'hFF;   // Assign hexadecimal FF to the first element
data_array[1] = 8'd12;   // Assign decimal 12 to the second element
Możesz także użyć pętli wewnątrz bloku always, aby inicjalizować lub manipulować tablicami.
integer i;
always @(posedge clk) begin
    for (i = 0; i < 16; i = i + 1) begin
        data_array[i] <= 8'd0;
    end
end

Zalety tablic

  • Przetwarzanie wsadowe : Dzięki pętli for możesz zastosować tę samą operację do wielu sygnałów jednocześnie.
  • Strukturalne układy : Tablice pomagają uporządkować wiele rejestrów lub sygnałów, utrzymując przejrzystą reprezentację układu.
  • Modelowanie pamięci : Możesz zaimplementować proste struktury pamięci, takie jak RAM lub ROM (wyjaśnione w następnym rozdziale).

Uwagi

W Verilogu nie możesz przypisać wartości do całej tablicy bezpośrednio (np. data_array = value). Zamiast tego operacje muszą być wykonywane element po elemencie. Ponadto, w początkowych wersjach Verilog obsługiwane były tylko jednowymiarowe tablice, więc do tablic wielowymiarowych potrzebny jest Verilog 2001 lub SystemVerilog.

4. Użycie tablic wielowymiarowych

Tablice w Verilogu upraszczają projektowanie i pomagają organizować struktury układów. Dzięki tablicom wielowymiarowym możesz efektywnie obsługiwać bardziej złożone struktury danych. Należy jednak zauważyć, że starszy Verilog (IEEE 1364‑1995) nie obsługiwał tablic wielowymiarowych. Zostały one oficjalnie wprowadzone w Verilog 2001. Dla jeszcze większej elastyczności zaleca się SystemVerilog.

Deklarowanie tablic wielowymiarowych

Od Verilog 2001 możesz definiować tablice wielowymiarowe, podając wiele indeksów dla jednej zmiennej. Podstawowa składnia wygląda tak:
reg [7:0] matrix [0:3][0:3];  // Defines a 4×4 matrix of 8-bit elements
Deklaracja ta tworzy dwuwymiarową tablicę o nazwie matrix, zawierającą 16 elementów, z których każdy ma szerokość 8 bitów.

Dostęp i przypisywanie elementów

Możesz odczytywać i przypisywać konkretne elementy tablicy wielowymiarowej, używając indeksów:
matrix[0][0] = 8'hA5;
matrix[2][3] = 8'd255;

Użycie pętli for

Tablice wielowymiarowe można manipulować przy pomocy zagnieżdżonych pętli for. Przykład inicjalizacji:
integer i, j;
always @(posedge clk) begin
    for (i = 0; i < 4; i = i + 1) begin
        for (j = 0; j < 4; j = j + 1) begin
            matrix[i][j] <= 8'd0;
        end
    end
end

Zastosowania tablic wielowymiarowych

  • Przydatne w projektach wymagających operacji macierzowych lub przetwarzania filtrów.
  • Mogą być wykorzystywane w przetwarzaniu obrazu lub cyfrowym przetwarzaniu sygnałów (DSP), do obsługi danych na poziomie pikseli.
  • Stosowane w strukturach bloków ROM/RAM oraz do organizacji par adres‑dane.

Ograniczenia i uwagi

  • Sprawdź kompatybilność z narzędziami syntezy, ponieważ niektóre z nich mogą nie w pełni obsługiwać tablice wielowymiarowe.
  • Ograniczenia mogą wystąpić przy łączeniu z instancjami lub interfejsami.
  • Zrozum różnice w stosunku do SystemVerilog, aby uniknąć problemów z kompatybilnością (wyjaśnione później).

5. Modelowanie pamięci przy użyciu tablic

W Verilogu możesz modelować proste struktury pamięci przy użyciu tablic. Umożliwia to opis i symulację obwodów pamięci, takich jak RAM i ROM, w sposób zwięzły i elastyczny. Szczególnie modele pamięci oparte na jednowymiarowych tablicach są powszechnie wykorzystywane w projektach CPU oraz systemach komunikacyjnych.

Podstawowa składnia modeli pamięci

Poniższy przykład przedstawia prosty RAM z 32‑bitowymi słowami i 1024 adresami (0–1023):
reg [31:0] memory [0:1023];  // 32-bit × 1024-word memory
Deklaracja ta tworzy tablicę o nazwie memory, w której każdy indeks (adres) przechowuje 32‑bitowe słowo.

Zapis i odczyt z pamięci

Operacje odczytu/zapisu w pamięci przy użyciu tablic można opisać w następujący sposób:
// Write operation
always @(posedge clk) begin
    if (we) begin
        memory[addr] <= data_in;
    end
end

// Read operation
assign data_out = memory[addr];
Kluczowe punkty:
  • Operacje zapisu są synchroniczne z posedge clk i warunkowe (sterowane sygnałem we, write enable).
  • Operacje odczytu są zazwyczaj realizowane przy pomocy assign dla odczytów kombinacyjnych (asynchronicznych).

Inicjalizacja pamięci

Dla testbenchów lub ustawiania początkowego stanu możesz inicjalizować tablice wewnątrz bloku initial:
integer i;
initial begin
    for (i = 0; i < 1024; i = i + 1) begin
        memory[i] = 32'd0;
    end
end
Możesz również ładować wartości początkowe z pliku zewnętrznego za pomocą $readmemh lub $readmemb:
initial begin
    $readmemh("rom_init.hex", memory);  // Initialize from a hex file
end

Praktyczne przypadki użycia

  • Pliki rejestrów w CPU lub mikrokontrolerach
  • Behawioralna symulacja Block RAM (BRAM) wewnątrz FPGA
  • Weryfikacja zachowania pamięci cache
  • Symulacja odczytu danych z ROM

Punkty do uwagi

  • W miarę jak rozmiar tablicy rośnie, czas symulacji i zasoby syntezy również rosną.
  • Wybieraj ostrożnie czas odczytu ( synchronny vs. asynchroniczny ) na podstawie specyfikacji projektu i narzędzi.
  • Podczas kierowania syntezy, przestrzegaj specyficznych reguł wnioskowania pamięci zalecanych przez dostawcę narzędzia.

6. Ulepszenia tablic w SystemVerilog

Do Verilog 2001 funkcjonalność tablic była ograniczona, co często czyniło projekty uciążliwymi. Aby to rozwiązać, SystemVerilog wprowadził znaczące ulepszenia, umożliwiając bardziej elastyczne i potężne obsługiwanie tablic. Ta sekcja wyjaśnia trzy główne typy tablic dostępne w SystemVerilog: dynamiczne tablice, asocjacyjne tablice i kolejki. Omówimy ich cechy i przypadki użycia.

Dynamiczne tablice

Cechy

  • Rozmiar tablicy można zmieniać w czasie wykonywania.
  • Przydatne, gdy rozmiar jest nieznany z góry lub zmienny podczas wykonywania.

Deklaracja i przykład

int dyn_array[];             // Declare a dynamic array
dyn_array = new[10];         // Initialize with 10 elements
dyn_array[0] = 100;

Przypadki użycia

  • Tymczasowe przechowywanie danych w testbenchach
  • Zarządzanie buforami o zmiennym rozmiarze

Asocjacyjne tablice

Cechy

  • Indeksy mogą być dowolnymi wartościami (całkowitymi, ciągami znaków itp.).
  • Działa jak tabela haszująca.

Deklaracja i przykład

int assoc_array[string];     // Associative array using strings as keys
assoc_array["id_001"] = 42;

Przypadki użycia

  • Przechowywanie wartości konfiguracyjnych dla parametrów
  • Wyszukiwanie wartości według ID lub nazwy

Kolejki

Cechy

  • Zachowuje się jak struktura FIFO (First-In, First-Out).
  • Obsługuje łatwe wstawianie i usuwanie, idealne dla dynamicznych strumieni danych.

Deklaracja i przykład

int queue_array[$];          // Declare a queue array

queue_array.push_back(10);   // Add element at the end
queue_array.push_front(5);   // Add element at the front
int val = queue_array.pop_front();  // Remove element from the front

Przypadki użycia

  • Tymczasowe przechowywanie danych (bufor FIFO)
  • Obsługa zdarzeń i zarządzanie transakcjami

Porównanie i użycie

Typ tablicyResizableTyp indeksuNajlepsze dla
Dynamiczna tablicaTakIntegerGdy rozmiar jest nieznany lub zmienny
Tablica asocjacyjnaTakDowolny (int, string, itp.)Wyszukiwania podobne do tablicy haszującej
QueueTakAutomatyczny (przód/tył)Częste wstawianie/usuwanie

Punkty do uwagi

  • Te ulepszone tablice to funkcje tylko SystemVerilog i niedostępne w Verilog .
  • Zakres wsparcia syntezy zależy od narzędzia, a są one głównie używane do testbenchów .
  • Jeśli celujesz w FPGA lub ASIC, zawsze sprawdź, czy te funkcje są obsługiwane przed implementacją.

7. Najlepsze praktyki dla operacji na tablicach

Podczas pracy z tablicami w Verilog lub SystemVerilog, skupienie się na efektywnym i czytelnym kodowaniu bezpośrednio prowadzi do wyższej jakości projektów sprzętowych. Ten rozdział podkreśla najlepsze praktyki obsługi tablic bezpiecznie i efektywnie.

Wyjaśnij intencję za pomocą komentarzy i nazewnictwa

Chociaż tablice są skalowalne i wygodne, nie zawsze jest oczywiste, co reprezentuje każdy element. Aby uniknąć zamieszania, postępuj zgodnie z tymi wytycznymi:
  • Używaj znaczących nazw dla tablic: reg [7:0] sensor_data [0:7];
  • Dodawaj komentarze opisujące cel lub jednostki:
// Stores 8-bit data from 8 sensors
reg [7:0] sensor_data [0:7];

Bądź ostrożny z granicami pętli

Podczas używania pętli for do manipulacji tablicami, kluczowe jest zdefiniowanie granic indeksów poprawnie:
  • Nieprawidłowe górne limity → dostęp poza zakresem (błędy logiczne lub ostrzeżenia symulatora)
  • Starannie zdecyduj, czy użyć < czy <= dla zakończenia pętli
Przykład:
integer i;
always @(posedge clk) begin
    for (i = 0; i < 8; i = i + 1) begin
        sensor_data[i] <= 8'd0;
    end
end

Zawsze inicjalizuj jawnie

Leaving arrays uninitialized can affect simulation results. This is especially critical for arrays modeling RAM or register banks. Always initialize explicitly:
initial begin
    for (i = 0; i < 256; i = i + 1)
        mem[i] = 32'd0;
end
In SystemVerilog, constructors or foreach loops allow even simpler initialization.

Design Reusable Modules

Array-based designs can adapt flexibly to changes in instance count or bit width. To maximize reusability, consider:
  • Using parameter to make array sizes configurable:
parameter DEPTH = 16;
reg [7:0] buffer [0:DEPTH-1];
  • Allowing external parameter passing improves module reusability.

Consider Synthesizability

When using arrays, always take synthesis tool compatibility into account:
  • Verilog one-dimensional reg arrays: generally synthesizable
  • SystemVerilog dynamic/associative/queue arrays: not synthesizable (for simulation only)
Therefore, for synthesizable circuits, stick to traditional reg arrays.

Use Arrays vs. Modules Appropriately

While arrays can reduce code size, overly complex designs may benefit more from splitting functionality into separate modules for maintainability.
  • Small, identical operations → arrays with for loops
  • Different functionality or large-scale designs → modular and hierarchical design

8. Frequently Asked Questions (FAQ)

When using arrays in Verilog or SystemVerilog, beginners to intermediate users often encounter the same stumbling blocks. Here we address three common questions about “Verilog arrays,” providing practical answers and insights from real‑world design experience.

Q1. I get an error when using multidimensional arrays in Verilog. Why?

A1.

This happens because Verilog 1995 and earlier versions of Verilog 2001 either did not support multidimensional arrays at all or only supported them in a limited way. For example, the following code will cause a compile error in Verilog 1995:
reg [7:0] matrix [0:3][0:3];  // Supported only in Verilog 2001 or later
Solution:
  • Ensure your development environment is compliant with Verilog 2001 or later .
  • If limitations remain, rewrite using one-dimensional arrays with calculated indices.
reg [7:0] matrix_1d [0:15];  // Flattened 4x4 array, accessed with (i*4 + j)

Q2. If I describe RAM using arrays in Verilog, will it work on hardware?

A2.

Yes. RAM described using arrays in Verilog is supported by most synthesis tools. A common example:
reg [31:0] mem [0:255];  // 32-bit × 256-word RAM
Points to watch:
  • The synthesis tool must recognize this description as a block RAM inference .
  • Read/write timing (synchronous vs. asynchronous) and access style must follow specific templates or the tool may not infer memory correctly.
Solution:
  • Follow the synthesis guide provided by your FPGA/ASIC vendor.
  • If behavior differs between simulation and hardware, check logs and debug step by step.

Q3. Can I use SystemVerilog’s dynamic arrays, associative arrays, or queues in actual hardware?

A3.

In general, dynamic arrays, associative arrays, and queues are not synthesizable (simulation only). Although they provide flexible coding, they cannot be directly mapped to hardware logic. Therefore, these arrays are mainly used for:
  • Temporary data storage in testbenches
  • Randomization or scoreboard implementations in verification environments
  • Complex transaction descriptions
Implementation Notes:
  • Any design code using these array types will be ignored or cause errors in synthesis tools.
  • If hardware implementation is required, convert them into reg or fixed‑length one‑dimensional arrays.

9. Conclusion

W tym artykule skupiliśmy się na frazie „tablice Verilog” i wyjaśniliśmy ich zastosowanie od podstaw po zaawansowane aplikacje. Opanowanie tablic w projektowaniu układów bezpośrednio przyczynia się do wydajności, czytelności i utrzymywalności.

Kluczowe wnioski

  • Rozumiejąc podstawowe typy danych Verilog (reg i wire), możesz unikać błędów przy pracy z tablicami.
  • Jednowymiarowe tablice są niezbędnymi strukturami do grupowania danych i modelowania pamięci.
  • Wielowymiarowe tablice są obsługiwane w Verilog 2001 i nowszych, umożliwiając projekty w stylu macierzy.
  • SystemVerilog wprowadza elastyczne struktury, takie jak dynamiczne tablice, tablice asocjacyjne i kolejki (głównie do symulacji i weryfikacji).
  • Stosując najlepsze praktyki obsługi tablic (inicjalizacja, nazewnictwo, ponowne użycie, uwagi dotyczące syntezy), możesz pisać kod wyższej jakości.

Praktyczne wskazówki

Tablice są potężne, ale nie powinny być używane ślepo do wszystkiego. Przy pisaniu kodu syntezowalnego lub współpracy w zespołach, zawsze przestrzegaj ograniczeń i wytycznych stylu. Zaawansowane funkcje tablic SystemVerilog powinny być stosowane z myślą o symulacji, aby maksymalnie wykorzystać ich zalety. Dobrzy projektanci wiedzą, jak wybrać odpowiednią strukturę do właściwego celu.

Polecane kolejne tematy

Jeśli teraz rozumiesz podstawy tablic, zalecamy zgłębienie następujących powiązanych tematów:
  • Używanie instrukcji generate do dynamicznego generowania układów z tablicami
  • Łączenie interface z tablicami w projektowaniu magistrali
  • Implementacja FIFO, buforów pierścieniowych i optymalizacji ROM przy użyciu tablic
Opanowanie tablic to kluczowy pierwszy krok jako projektanta Verilog. Pogłębiając swoją wiedzę, będziesz w stanie z pewnością radzić sobie z coraz bardziej złożonymi projektami układów.