Verilog to język opisu sprzętu (HDL – Hardware Description Language), który służy do modelowania, projektowania i symulacji układów cyfrowych. Obok VHDL jest to najpopularniejszy standard HDL wspierany przez praktycznie wszystkie współczesne systemy EDA/CAD przeznaczone do projektowania systemów cyfrowych.

Popularność Veriloga wynika z prostej, czytelnej składni przypominającej język C oraz z hierarchicznej struktury projektów, która ułatwia opisywanie nawet złożonych systemów.

Zastosowania Veriloga

Najczęstsze zastosowania języka w praktyce projektowej obejmują:

  • symulację – opis zachowania układów logicznych w celu ich weryfikacji przed implementacją;
  • syntezę logiczną – przekształcanie kodu HDL w rzeczywisty sprzęt (netlisty, konfiguracje FPGA);
  • projektowanie RTL – tworzenie architektury na poziomie transferu rejestrowego;
  • modelowanie na FPGA – implementację i testy systemów wbudowanych na platformach programowalnych.

Nie wszystkie konstrukcje Veriloga są syntezowalne – część służy wyłącznie do symulacji i weryfikacji, co pozwala szybciej wykrywać błędy projektowe.

Poziomy abstrakcji w Verilogu

Verilog pozwala opisywać układy na kilku poziomach szczegółowości:

  • Poziom tranzystorów – najniższy poziom abstrakcji;
  • Poziom bramek logicznych – opis za pomocą bramek AND, OR, NOT itp.;
  • Poziom równań logicznych – reprezentacja funkcji boolowskich;
  • Poziom transferu rejestrowego (RTL) – operacje między rejestrami;
  • Poziom zachowań (behawioralny) – opis algorytmiczny, niezależny od implementacji;
  • Poziom strukturalny (systemowy) – łączenie komponentów wyższego poziomu.

Każdy z tych poziomów znajduje zastosowanie na innym etapie projektu; wybór zależy od celu i wymaganego stopnia szczegółowości.

Podstawowe konstrukcje Veriloga

Moduły – fundamentalna jednostka kodu

Moduł jest podstawową konstrukcją języka Verilog. Reprezentuje blok logiki z ustalonym zbiorem wejść i wyjść; rozpoczyna się słowem kluczowym module i kończy endmodule.

Struktura modułu obejmuje:

  • nazwę modułu,
  • interfejs (porty wejścia/wyjścia),
  • deklaracje sygnałów wewnętrznych,
  • kod opisujący funkcjonalność.

Moduły mogą być parametryzowane (np. zmienna szerokość danych), co ułatwia tworzenie wielokrotnego użytku, elastycznych komponentów.

Sygnały i rejestry

Rejestry (reg) – przechowują wartość do czasu jej zmiany w bloku proceduralnym; przypominają zmienne w językach programowania, ale odzwierciedlają zachowanie sprzętu.

Przewody (wire) – reprezentują połączenia między komponentami, odwzorowując ciągłe powiązania logiczne w układzie.

Deklaracje mogą obejmować wektory bitowe; przykładowo 1‑bitowy sygnał zadeklarujesz tak: wire a;

Wartości logiczne

W Verilogu każdy bit może przyjmować jedną z czterech wartości:

  • 0 – logiczne zero bądź fałsz;
  • 1 – logiczna jedynka albo prawda;
  • X – wartość nieokreślona (nieistotna) – może być zarówno 0, jak i 1;
  • Z – stan wysokiej impedancji (stan trzeci, stan pływający).

Czterowartościowa logika jest kluczowa w symulacjach, ponieważ pozwala odróżnić nieznane stany od wartości zdefiniowanych.

Procesy i bloki proceduralne

Proces w Verilogu to blok kodu uruchamiany na zmianę wskazanych sygnałów; definiuje się go słowem kluczowym always i można w nim stosować instrukcje warunkowe oraz pętle.

Przykład procesu reagującego na zmiany sygnałów:

always @(a, b, c) begin
d = 0;
if (a) d = 1;
if (b) d = 2;
if (c) d = 3;
end

Kluczową cechą Veriloga jest równoległość wykonywania – procesy i instancje modułów działają równolegle, co wiernie odwzorowuje naturę sprzętu cyfrowego.

Praktyczne przykłady

Multiplekser

Multiplekser wybiera jedno z wielu wejść na podstawie sygnału selekcji. Przykładowa implementacja:

module mux1(sel, din1, din2, din3, din4, dout);
input [1:0] sel; // selektor
input din1; // wejścia
input din2;
input din3;
input din4;
output reg dout;

always @(sel or din1 or din2 or din3 or din4)
if (sel == 2'd0) dout = din1;
else if (sel == 2'd1) dout = din2;
else if (sel == 2'd2) dout = din3;
else dout = din4;
endmodule

Licznik

Licznik inkrementuje lub dekrementuje swoją wartość na każdym zboczu zegara. Przykładowy kod:

always @(posedge clk or negedge reset)
if (!reset) counter <= {length{1'b0}}; // reset asynchroniczny
else if (load) counter <= data; // wpisanie równoległe
else if (dir) counter <= counter + 1'b1; // zliczanie w górę
else counter <= counter - 1'b1; // zliczanie w dół

Przerzutnik

Przerzutnik jest podstawowym elementem pamięci w logice sekwencyjnej. Przykładowa definicja modułu parametryzowanego:

module flip_flop #(parameter dlength = 8) // parametr
(d, clk, reset, e, q);
input [dlength-1:0] d; // wejście D
input clk; // zegar
input reset; // asynchroniczny reset
input e; // enable
output reg [dlength-1:0] q; // wyjście

always @(posedge clk or posedge reset)
// kod logiki przerzutnika
endmodule

Cykl projektowania systemów cyfrowych

Typowy przebieg projektu z użyciem Veriloga obejmuje etapy:

  1. Specyfikacja – określenie wymagań funkcjonalnych;
  2. Schemat blokowy – wizualizacja architektury systemu;
  3. Projekt niskiego poziomu – szczegółowy opis poszczególnych modułów;
  4. Kodowanie RTL – implementacja na poziomie transferu rejestrowego;
  5. Weryfikacja – testowanie poprawności działania poprzez symulację;
  6. Synteza – konwersja kodu na netlist logiczny, a następnie na układ fizyczny.

Verilog a FPGA

Verilog jest szczególnie popularny w projektach na platformach FPGA (programowalne macierze bramek). FPGA można rekonfigurować po produkcji, dzięki czemu jeden układ realizuje wiele funkcji. Verilog pozwala efektywnie opisywać złożone systemy, które następnie implementuje się w tych strukturach.