verilog Samouczek
Pierwsze kroki z verilog
Szukaj…
Uwagi
Verilog to język opisu sprzętu (HDL), który służy do projektowania, symulacji i weryfikacji obwodów cyfrowych na poziomie behawioralnym lub transferu rejestru. Jest to godne uwagi z powodów, które odróżniają go od „tradycyjnych” języków programowania:
- Istnieją dwa rodzaje przypisywania, blokowania i nieblokowania, każdy z własnymi zastosowaniami i semantyką.
- Zmienne należy zadeklarować jako szerokość jednego bitu lub z jawną szerokością.
- Projekty są hierarchiczne, z możliwością tworzenia instancji modułów o pożądanym działaniu.
- W symulacji (zwykle nie w syntezie) zmienne
wire
mogą znajdować się w jednym z czterech stanów: 0, 1, zmiennoprzecinkowe (z
) i niezdefiniowane (x
).
Wersje
Wersja | Data wydania |
---|---|
Verilog IEEE-1364-1995 | 1995-01-01 |
Verilog IEEE-1364-2001 | 2001-09-28 |
Verilog IEEE-1364.1-2002 | 2002-12-18 |
Verilog IEEE-1364-2005 | 2006-04-07 |
SystemVerilog IEEE-1800-2009 | 2009-12-11 |
SystemVerilog IEEE-1800-2012 | 21.02.2013 |
Instalacja lub konfiguracja
Szczegółowe instrukcje konfiguracji lub instalacji Verilog zależą od używanego narzędzia, ponieważ istnieje wiele narzędzi Verilog.
Wprowadzenie
Verilog to język opisu sprzętu (HDL) używany do modelowania systemów elektronicznych. Najczęściej opisuje system elektroniczny na poziomie transferu rejestru (RTL) abstrakcji. Służy również do weryfikacji obwodów analogowych i obwodów sygnałów mieszanych. Jego struktura i główne zasady (jak opisano poniżej) mają na celu opisanie i skuteczne wdrożenie systemu elektronicznego.
- Sztywność
Obwód elektroniczny jest fizyczną jednostką o stałej strukturze i Verilog jest do tego przystosowany. Moduły (moduł), porty (wejście / wyjście / wejście), połączenia (przewody), bloki (@ zawsze), rejestry (reg) są ustalane w czasie kompilacji. Liczba jednostek i połączeń nie zmienia się dynamicznie. Na najwyższym poziomie zawsze znajduje się „moduł” reprezentujący strukturę chipa (do syntezy) i jeden na poziomie systemu do weryfikacji. - Równoległość
Nieodłączne jednoczesne operacje na fizycznym układzie scalonym są naśladowane w języku przez bloki zawsze (najbardziej wspólne), początkowe i rozwidlające / łączące.
module top();
reg r1,r2,r3,r4; // 1-bit registers
initial
begin
r1 <= 0 ;
end
initial
begin
fork
r2 <= 0 ;
r3 <= 0 ;
join
end
always @(r4)
r4 <= 0 ;
endmodule
Wszystkie powyższe instrukcje są wykonywane równolegle w tej samej jednostce czasu.
Czas i synchronizacja
Verilog obsługuje różne konstrukcje opisujące czasową naturę obwodów. Czasy i opóźnienia w obwodach można zaimplementować w Verilog, na przykład za pomocą konstrukcji #delay. Podobnie Verilog obsługuje również obwody i komponenty synchroniczne i asynchroniczne, takie jak klapy, zatrzaski i logika kombinatoryczna, wykorzystując różne konstrukcje, na przykład bloki „zawsze”. Zestaw bloków może być również synchronizowany za pomocą wspólnego sygnału zegarowego lub blok może być wyzwalany w oparciu o określony zestaw wejść.#10 ; // delay for 10 time units always @(posedge clk ) // synchronous always @(sig1 or sig2 ) // combinatorial logic @(posedge event1) // wait for post edge transition of event1 wait (signal == 1) // wait for signal to be 1
Niepewność
Verilog obsługuje niektóre z niepewności związanych z obwodami elektronicznymi. „X” oznacza nieznany stan obwodu. „Z” służy do reprezentowania stanu nienapędzonego obwodu.reg1 = 1'bx; reg2 = 1'bz;
Abstrakcja
Verilog obsługuje projektowanie na różnych poziomach abstrakcji. Najwyższy poziom abstrakcji dla projektu to Resister transfer Level (RTL), następny to poziom bramki, a najniższy poziom komórki (User Define Primitive), przy czym najczęściej używana jest abstrakcja RTL. Verilog obsługuje również poziom abstrakcji behawioralnej, bez względu na konstrukcyjną realizację projektu, wykorzystywaną przede wszystkim do weryfikacji.
// Example of a D flip flop at RTL abstraction
module dff (
clk , // Clock Input
reset , // Reset input
d , // Data Input
q // Q output
);
//-----------Input Ports---------------
input d, clk, reset ;
//-----------Output Ports---------------
output q;
reg q;
always @ ( posedge clk)
if (~reset) begin
q <= 1'b0;
end else begin
q <= d;
end
endmodule
// And gate model based at Gate level abstraction
module and(input x,input y,output o);
wire w;
// Two instantiations of the module NAND
nand U1(w,x, y);
nand U2(o, w, w);
endmodule
// Gate modeled at Cell-level Abstraction
primitive udp_and(
a, // declare three ports
b,
c
);
output a; // Outputs
input b,c; // Inputs
// UDP function code here
// A = B & C;
table
// B C : A
1 1 : 1;
0 1 : 0;
1 0 : 0;
0 0 : 0;
endtable
endprimitive
Istnieją trzy główne przypadki użycia Verilog. Określają strukturę kodu i jego interpretację, a także określają używane zestawy narzędzi. Wszystkie trzy aplikacje są niezbędne do pomyślnego wdrożenia dowolnego projektu Verilog.
- Projekt fizyczny / zaplecze
Tutaj Verilog służy przede wszystkim do postrzegania projektu jako matrycy połączonych bram realizujących logiczny projekt. RTL / logika / Projektowanie przechodzi przez różne etapy od syntezy -> umieszczenia -> budowy drzewa zegarowego -> routingu -> DRC -> LVS -> do tapeout. Dokładne kroki i sekwencje różnią się w zależności od dokładnego charakteru wdrożenia. - Symulacja
W tym przypadku podstawowym celem jest wygenerowanie wektorów testowych w celu zweryfikowania projektu zgodnie ze specyfikacją. Kod napisany w tym przypadku użycia nie musi być syntezowalny i pozostaje w sferze weryfikacji. Kod tutaj bardziej przypomina ogólne struktury oprogramowania, takie jak pętle for / while / do itp. - Projekt
Projektowanie obejmuje implementację specyfikacji obwodu zasadniczo na poziomie abstrakcji RTL. Kod Verilog jest następnie podawany do weryfikacji, a kod w pełni weryfikowany do fizycznej realizacji. Kod jest napisany przy użyciu wyłącznie syntezowalnych konstrukcji Verilog. Niektóre style kodowania RTL mogą powodować niedopasowanie symulacji w porównaniu do syntezy i należy dołożyć starań, aby ich uniknąć.
Istnieją dwa główne etapy wdrażania. Wpłyną również na sposób pisania i implementacji kodu Verilog. Niektóre style kodowania i pewne struktury są bardziej odpowiednie w jednym przepływie nad drugim.
- ASIC Flow (specyficzny dla aplikacji układ scalony)
- FPGA Flow (programowalna tablica bramek) - obejmuje FPGA i CPLD
Witaj świecie
W tym przykładzie użyto kompilatora icarus verilog.
Krok 1: Utwórz plik o nazwie hello.v
module myModule();
initial
begin
$display("Hello World!"); // This will display a message
$finish ; // This causes the simulation to end. Without, it would go on..and on.
end
endmodule
Krok 2. Kompilujemy plik .v przy użyciu programu icarus:
>iverilog -o hello.vvp hello.v
Przełącznik -o przypisuje nazwę do pliku obiektu wyjściowego. Bez tego przełącznika plik wyjściowy byłby nazywany a.out. Hello.v wskazuje plik źródłowy do skompilowania. Podczas kompilowania tego kodu źródłowego nie powinno być praktycznie żadnych wyników, chyba że wystąpią błędy.
Krok 3. Jesteś gotowy do symulacji tego programu Verilog Hello World. Aby to zrobić, wywołaj jako taki:
>vvp hello.vvp
Hello World!
>
Instalacja kompilatora Icarus Verilog dla Mac OSX Sierra
- Zainstaluj Xcode z App Store.
- Zainstaluj narzędzia programistyczne Xcode
> xcode-select --install
Zapewni to podstawowe narzędzia wiersza poleceń, takie jak gcc
i make
- Zainstaluj porty Mac
https://www.macports.org/install.php
Pakiet instalacyjny OSX Sierra zapewni metodę open source do instalowania i aktualizowania dodatkowych pakietów oprogramowania na platformie Mac. Pomyśl yum
lub apt-get
dla Maca.
- Zainstaluj icarus przy użyciu portów Mac
> sudo port install iverilog
- Sprawdź instalację z wiersza polecenia
$ iverilog
iverilog: no source files.
Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]
[-g1995|-g2001|-g2005] [-g<feature>]
[-D macro[=defn]] [-I includedir] [-M depfile] [-m module]
[-N file] [-o filename] [-p flag=value]
[-s topmodule] [-t target] [-T min|typ|max]
[-W class] [-y dir] [-Y suf] source_file(s)
See the man page for details.
$
Jesteś teraz gotowy do skompilowania i symulacji pierwszego pliku Verilog na komputerze Mac.
Zainstaluj GTKWave do graficznego wyświetlania danych symulacji w Mac OSx Sierra
GTKWave to w pełni funkcjonalny graficzny pakiet przeglądający, który obsługuje kilka graficznych standardów przechowywania danych, ale zdarza się również, że obsługuje VCD, który jest formatem, który vvp
. Tak więc, aby podnieść GTKWave, masz kilka opcji
- Idź do http://gtkwave.sourceforge.net/gtkwave.zip i pobierz go. Ta wersja jest zazwyczaj najnowsza.
- Jeśli zainstalowałeś MacPorts ( https://www.macports.org/ ), po prostu uruchom
sudo port install gtkwave
. Prawdopodobnie będzie chciał zainstalować na zależnościach. Uwaga: ta metoda zazwyczaj zapewnia starszą wersję. Jeśli nie masz zainstalowanych MacPorts, na tej stronie znajduje się przykład konfiguracji instalacji. Tak! Będziesz potrzebował wszystkich narzędzi programistycznych xcode, ponieważ te metody „zbudują” ci GTKWave ze źródła.
Po zakończeniu instalacji możesz zostać poproszony o wybranie wersji Pythona. Zainstalowałem już wersję 2.7.10, więc nigdy nie „wybrałem” nowego.
W tym momencie możesz uruchomić gtkwave z wiersza poleceń za pomocą gtkwave
. Po uruchomieniu może zostać wyświetlony monit o instalację lub aktualizację XQuarts. Zrób tak. W moim przypadku XQuarts 2.7.11 jest zainstalowany.
Uwaga: naprawdę potrzebowałem zrestartować się, aby poprawnie pobrać XQuarts, potem ponownie gtkwave
i aplikacja się uruchomi.
W następnym przykładzie utworzę dwa niezależne pliki, testbench i moduł do testowania, i użyjemy gtkwave do przeglądania projektu.
Korzystanie z Icarus Verilog i GTKWaves do symulacji i wyświetlania projektu graficznie
W tym przykładzie wykorzystano Icarus i GTKWave. Instrukcje instalacji tych narzędzi w systemie OSx znajdują się w innym miejscu na tej stronie.
Zacznijmy od projektu modułu. Ten moduł jest wyświetlaczem BCD do 7 segmentów. Projekt zakodowałem w tępy sposób, aby dać nam coś, co łatwo zepsuć, i możemy spędzić trochę czasu na graficznym naprawie. Mamy więc zegar, reset, 4 dane wejściowe reprezentujące wartość BCD i 7 bitowe wyjście, które reprezentują siedmiosegmentowy wyświetlacz. Utwórz plik o nazwie bcd_to_7seg.v i umieść w nim źródło poniżej.
module bcd_to_7seg (
input clk,
input reset,
input [3:0] bcd,
output [6:0] seven_seg_display
);
parameter TP = 1;
reg seg_a;
reg seg_b;
reg seg_c;
reg seg_d;
reg seg_e;
reg seg_f;
reg seg_g;
always @ (posedge clk or posedge reset)
begin
if (reset)
begin
seg_a <= #TP 1'b0;
seg_b <= #TP 1'b0;
seg_c <= #TP 1'b0;
seg_d <= #TP 1'b0;
seg_e <= #TP 1'b0;
seg_f <= #TP 1'b0;
seg_g <= #TP 1'b0;
end
else
begin
seg_a <= #TP ~(bcd == 4'h1 || bcd == 4'h4);
seg_b <= #TP bcd < 4'h5 || bcd > 6;
seg_c <= #TP bcd != 2;
seg_d <= #TP bcd == 0 || bcd[3:1] == 3'b001 || bcd == 5 || bcd == 6 || bcd == 8;
seg_e <= #TP bcd == 0 || bcd == 2 || bcd == 6 || bcd == 8;
seg_f <= #TP bcd == 0 || bcd == 4 || bcd == 5 || bcd == 6 || bcd > 7;
seg_g <= #TP (bcd > 1 && bcd < 7) || (bcd > 7);
end
end
assign seven_seg_display = {seg_g,seg_f,seg_e,seg_d,seg_c,seg_b,seg_a};
endmodule
Następnie potrzebujemy testu, aby sprawdzić, czy ten moduł działa poprawnie. Moim zdaniem stwierdzenie przypadku w środowisku testowym jest łatwiejsze do odczytania i bardziej jasne, co robi. Ale nie chciałem umieszczać tego samego stwierdzenia przypadku w projekcie ORAZ w teście. To zła praktyka. Raczej dwa niezależne projekty są używane do wzajemnej weryfikacji.
Korzystając z poniższego kodu, zauważysz dwie linie $dumpfile("testbench.vcd");
i $dumpvars(0,testbench);
. Te wiersze tworzą plik wyjściowy VCD, który zostanie wykorzystany do graficznej analizy projektu. Jeśli je pominiesz, nie zostanie wygenerowany plik VCD. Utwórz plik o nazwie testbench.v i umieść w nim źródło poniżej.
`timescale 1ns/100ps
module testbench;
reg clk;
reg reset;
reg [31:0] ii;
reg [31:0] error_count;
reg [3:0] bcd;
wire [6:0] seven_seg_display;
parameter TP = 1;
parameter CLK_HALF_PERIOD = 5;
// assign clk = #CLK_HALF_PERIOD ~clk; // Create a clock with a period of ten ns
initial
begin
clk = 0;
#5;
forever clk = #( CLK_HALF_PERIOD ) ~clk;
end
initial
begin
$dumpfile("testbench.vcd");
$dumpvars(0,testbench);
// clk = #CLK_HALF_PERIOD ~clk;
$display("%0t, Reseting system", $time);
error_count = 0;
bcd = 4'h0;
reset = #TP 1'b1;
repeat (30) @ (posedge clk);
reset = #TP 1'b0;
repeat (30) @ (posedge clk);
$display("%0t, Begin BCD test", $time); // This displays a message
for (ii = 0; ii < 10; ii = ii + 1)
begin
repeat (1) @ (posedge clk);
bcd = ii[3:0];
repeat (1) @ (posedge clk);
if (seven_seg_display !== seven_seg_prediction(bcd))
begin
$display("%0t, ERROR: For BCD %d, module output 0b%07b does not match prediction logic value of 0b%07b.",$time,bcd, seven_seg_display,seven_seg_prediction(bcd));
error_count = error_count + 1;
end
end
$display("%0t, Test Complete with %d errors", $time, error_count);
$display("%0t, Test %s", $time, ~|error_count ? "pass." : "fail.");
$finish ; // This causes the simulation to end.
end
parameter SEG_A = 7'b0000001;
parameter SEG_B = 7'b0000010;
parameter SEG_C = 7'b0000100;
parameter SEG_D = 7'b0001000;
parameter SEG_E = 7'b0010000;
parameter SEG_F = 7'b0100000;
parameter SEG_G = 7'b1000000;
function [6:0] seven_seg_prediction;
input [3:0] bcd_in;
// +--- A ---+
// | |
// F B
// | |
// +--- G ---+
// | |
// E C
// | |
// +--- D ---+
begin
case (bcd_in)
4'h0: seven_seg_prediction = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F;
4'h1: seven_seg_prediction = SEG_B | SEG_C;
4'h2: seven_seg_prediction = SEG_A | SEG_B | SEG_G | SEG_E | SEG_D;
4'h3: seven_seg_prediction = SEG_A | SEG_B | SEG_G | SEG_C | SEG_D;
4'h4: seven_seg_prediction = SEG_F | SEG_G | SEG_B | SEG_C;
4'h5: seven_seg_prediction = SEG_A | SEG_F | SEG_G | SEG_C | SEG_D;
4'h6: seven_seg_prediction = SEG_A | SEG_F | SEG_G | SEG_E | SEG_C | SEG_D;
4'h7: seven_seg_prediction = SEG_A | SEG_B | SEG_C;
4'h8: seven_seg_prediction = SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G;
4'h9: seven_seg_prediction = SEG_A | SEG_F | SEG_G | SEG_B | SEG_C;
default: seven_seg_prediction = 7'h0;
endcase
end
endfunction
bcd_to_7seg u0_bcd_to_7seg (
.clk (clk),
.reset (reset),
.bcd (bcd),
.seven_seg_display (seven_seg_display)
);
endmodule
Teraz, gdy mamy dwa pliki, testbench.v i bcd_to_7seg.v, musimy skompilować, opracować za pomocą Icarusa. Aby to zrobić:
$ iverilog -o testbench.vvp testbench.v bcd_to_7seg.v
Następnie musimy przeprowadzić symulację
$ vvp testbench.vvp
LXT2 info: dumpfile testbench.vcd opened for output.
0, Reseting system
6000, Begin BCD test
8000, Test Complete with 0 errors
8000, Test pass.
W tym momencie, jeśli chcesz sprawdzić poprawność pliku, który jest naprawdę testowany, przejdź do pliku bcd_2_7seg.v i przenieś trochę logiki i powtórz pierwsze dwa kroki.
Jako przykład zmieniam wiersz seg_c <= #TP bcd != 2;
do seg_c <= #TP bcd != 4;
. Ponowna kompilacja i symulacja wykonuje następujące czynności:
$ iverilog -o testbench.vvp testbench.v bcd_to_7seg.v
$ vvp testbench.vvp
LXT2 info: dumpfile testbench.vcd opened for output.
0, Reseting system
6000, Begin BCD test
6600, ERROR: For BCD 2, module output 0b1011111 does not match prediction logic value of 0b1011011.
7000, ERROR: For BCD 4, module output 0b1100010 does not match prediction logic value of 0b1100110.
8000, Test Complete with 2 errors
8000, Test fail.
$
Teraz zobaczmy symulację za pomocą GTKWave. Z wiersza polecenia wydaj a
gtkwave testbench.vcd &
Kiedy pojawi się okno GTKWave, w lewym górnym polu zobaczysz nazwę modułu testbench. Kliknij to. Ujawni to podmoduły, zadania i funkcje związane z tym plikiem. Przewody i rejestry pojawią się również w lewym dolnym polu.
Teraz przeciągnij, clk, bcd, error_count i seven_seg_display do pola sygnału obok okna kształtu fali. Sygnały zostaną teraz wykreślone. Error_count pokaże, które konkretne wejście BCD wygenerowało nieprawidłowe wyjście Seven_seg_display.
Jesteś teraz gotowy do graficznego rozwiązywania problemów z błędem Verilog.