Systemy wbudowane Projektowanie systemów wbudowanych na bazie układów CPLD/FPGA Język opisu sprzętu Verilog cz.1
System Quartus II Opis projektu Tekstowy (język opisu sprzętu np. Verilog) Graficzny Wykresy czasowe Kompilacja Symulacja Analiza czasowa Programowanie
Nowy projekt Menu File -> New project Wizard
Nadanie nazwy projektu
Dodawanie plików do projektu
Wybór układu programowalnego Dodatkowo w menu Assignments -> Device: - Configuration nie wybierać żadnego Układu - Unused Pins zaznaczyć Opcję As Input tri stated
Kompilacja projektu Menu Processing -> Start Compilation
Wyniki kompilacji
Przypisanie wyprowadzeń zewnętrznych W menu Assignments -> Assign Pins
Powtórna kompilacja - raport Raport po powtórnej kompilacji pozwala stwierdzić prawidłowe przypisanie wyprowadzeń zewnętrznych
Programowanie układu Menu Tools --> Programmer Dalej : Auto Detect Dodać plik z rozszerzeniem *.sof Zaznaczyć configure I kliknąć Start
Język opisu sprzętu Verilog Opisuje układy cyfrowe (trwają prace nad opisem układów analogowych) Opis układu cyfrowego możliwy na poziomie: funkcjonalnym (behavioral), strukturalnym (przesłań międzyrejestrowych RTL, bramkowym - gate level), DataFlow równania boolowskie Sposób opisu układu w Verilog: Bottom-up tradycyjne hierarchiczne projektowanie Top-down możliwe modelowanie i testowanie układu od najwyższego poziomu
Historia Lata '80 potrzeba stworzenia jednego języka do symulacji na rożnych poziomach opisu Język Verilog stworzony przez Phila Moore'a w 1983-4 w Gateway Design Automation wykorzystuje elementy Języków Modula, Stimula, C 1989 Gateway Design Automation (i prawa do Verilog) przejęta przez Cadence rok później język Verilog upubliczniony (public domain) 1992 rozpoczęcie prac nad standaryzowaniem 12.1995 Verilog stał się międzynarodowym standardem IEEE Std.1364-1995 2001 rozszerzenie standardu IEEE Std. 1364-2001 Ostatnie lata Verilog stał się językiem opisu sprzętu HDL SystemVerilog język do opisu i do testowania IEEE Std. 1800-2005
Opis układu cyfrowego Układ cyfrowy może być opisany za pomocą: współbieżnych sygnałów (concurrency) struktury bloków (structure) procedur i komend (procedural statements) zdarzeń w czasie Np sumator 1-bitowy: Równanie boolowskie sumy: s = a xor b xor cin = a b cin Równanie boolowskie przeniesienia: cout = (a and b) or (a and cin) or (b and cin) = (a b) + (a cin) + (b cin)
Opis za pomocą Verilog module suma (a,b,cin,s,cout); // porty wej/wyj input a,b,cin; output s,cout; // ciało funkcji assign s = a ^ b ^ cin; assign cout = (a & b) (a & cin) (b & cin); endmodule
Identyfikatory {[A-Z], [a-z], [0-9], _, $} Nie może zaczynać się od $ lub cyfry [0-9] Przykłady: shiftreg_a busa_index error_condition merge_ab _bus3 n$657 Rozróżnia wielkość liter (case sensitive) myid Myid Nazwa nie może być dłuższa niż 1024 znaki Z przykładu sumatora: a b cin s cout
Komentarze // komentarz do końca linii /* komentarz zamknięty bloku tekstu */ /* Nie można /* zagnieżdżać komentarzy*/ blokowych*/ Białe znaki są ignorowane (znak spacji, tabulacji, nowej linii)
Typy danych W urządzeniach cyfrowych rodzaje danych reprezentują dane zapamiętane oraz przesyłane pomiędzy elementami Wartości sygnałów: 0 wartość logiczna zero lub fałsz 1 wartość logiczna jeden lub prawda x wartość logiczna nieznana z wartość wysokiej impedancji Rozróżnia się dwa rodzaje danych: sieć (net) zmienna (variable) Schemat deklaracji: Type [range] signal_name {,signal_name}
Sieć Sieć (net) reprezentuje fizyczne połączenia pomiędzy elementami Nie jest elementem pamiętającym (wyjątek: trireg) Wartość jest ustalana na podstawie sygnałów zasilających Domyślna wartość portów wej/wyj: wire Jeżeli nie ma połączenia zasilającego, to jest w stanie wysokiej impedancji z (!) Typy sieci: wire, tri węzeł, węzeł trójstanowy supply0, supply1 stała wartość logiczna wand, wor iloczyn, suma na drucie trior, triand, tri0, tri1, trireg module suma (a,b,cin,s,cout); // porty wej/wyj input a,b,cin; output s,cout; wire a,b,c,s,cout; // domyslny typ // ciało funkcji assign s = a ^ b ^ cin; assign cout = (a & b) (a & cin) (b & cin); endmodule
Zmienne Zmienna (variable) jest abstrakcyjnym elementem pamiętającym wartość do czasu następnej zmiany W układzie cyfrowym zmienna realizowana jest jako przerzutnik (ale nie zawsze!) Typy zmiennych: reg (do opisu układów cyfrowych), integer, real, time, realtime (do symulacji) Zmienne inicjowane są wartością nieznaną x (!) Przykład: reg A, C; // deklaracja // przypisania są wewnątrz procesu A = 1; C = A; // C ma wartość logiczną 1 A = 0; // C ma wciąŝ 1 C = 0; // C ma teraz wartość 0
Liczby [sign][size][ radix] value [sign] znak + - [size] liczba bitów zapisana liczbą dziesiętną ['radix] podstawa systemu liczbowego b B liczba dwójkowa o O liczba ósemkowa d D liczba dziesiętna system domyślny h H liczba szesnastkowa value znaki określające wartość 0..9 a..f A..F x X z Z? Liczby bez określonej długości bitowej są zapisywane na 32 bitach 659 // liczba dziesiętna h 837FF // liczba szesnastkowa o7460 // liczba ósemkowa Liczba bez określonej podstawy jest liczbą dziesiętną Nie może być znaku spacji między znakiem apostrofu i podstawy Liczby o określonej długości bitowej: 4'b101, 5'D 3, 3'b01x, 16'hz Liczby ujemne są kodowane w U2
Parametry Parametry przypisują nazwom określone wartości Używa się do pisania kodu bardziej czytelnego, łatwiejszego w modyfikacji, dostosowania do innego projektu Za pomocą parametrów koduje się stany automatu Przykłady: parameter e = 25, f = 9; //stałe 32-bitowe parameter r = 5.7; //stała typu real parameter byte_size = 8, byte_mask = byte_size - 1; parameter average_delay = (r + f) / 2; parameter signed [3:0] mux_selector = 0; // stany automatu parameter [2:0] idle = 3'd0, init = 3'd1, store = 3'd5; Stałe localparam N=4
Wektory Zmienna lub sieć bez zadeklarowanego zakresu range jest skalarem wektorem o długości jednego bitu Zakres range jest częścią typu nie można w jednej deklaracji podać rożnych zakresów Deklaracja szyny danych wire [3:0] busa; reg [1:4] busb; reg [1:0] busc; Liczba po lewej stronie zakresu określa najbardziej znaczący bit MSB Wektor traktowany jako wartość bez znaku Ze znakiem, jeżeli zadeklarowany typu signed lub dołączony do sygnału ze znakiem
Macierze Macierz może składać się z elementów sieci (net) lub zmiennych (variable) typu skalar lub wektor Przykłady deklaracji: reg x[11:0]; // macierz 12 skalarow typu reg wire [0:7] y[5:0]; // macierz 6 wektorow 8-bitowych typu wire reg [31:0] x [127:0]; // macierz 128 wektorow 32 bitowych typu reg reg [7:0] x [127:0], y [63:0]; // dwie macierze wektorow 8-bitowych typu reg Jednowymiarowa macierz z elementami typu reg to jest pamięć (ROM, RAM) Można przypisać wartość dowolnemu n-bitowemu elementowi macierzy (słowu) Nie można przypisać wartości całej lub części pamięci
Operatory Jednoargumentowe (unary) arytmetyczne: +, - redukcji: &, ~&,, ~, ^, ~^ (^~) negacja logiczna:! negacja bitowa: ~ Dwuargumentowe (binary) arytmetyczne: +, -, *, /, % relacyjne: <, >, <=, >=, ==,!=, ===,!=== logiczne: &&, bitowe: &,, ^, ~^ Trojargumentowe (ternary) Warunkowy:?:
Operatory przesunięcia << >> przesunięcie logiczne <<< >>> przesunięcie arytmetyczne W operacji przesunięcia logicznego i arytmetycznego w lewo (<<, <<<) bity uzupełnione są zerami W operacji przesunięcia logicznego w prawo (>>) bity uzupełnione są zerami W operacji przesunięcia arytmetycznego w prawo (>>>) bity uzupełnione są bitem znaku reg [3:0] start, result; reg signed [3:0] start, result; start = 1; start = 4 b1000; result = (start << 2); result = (start >>> 2); //start=0001, result=0100 //start=1000, result=1110
Operatory relacji > większy niż < mniejszy niż >= większy lub równy <= mniejszy lub równy Wynikiem operacji jest skalar Jeżeli w jednym z operatorów jest x lub z to wynikiem jest 1-bitowy skalar x Przykład: 1 > 0 // wynikiem jest 1 b1x1 <= 0 // wynikiem jest x 10 < z // wynikiem jest x
Operatory porównania == równość logiczna wynikiem jest 0, 1 lub x!= nierówność logiczna === równość stanów bitów wynikiem jest 0 lub 1!== nierówność stanów bitów Wynikiem operacji jest skalar W porównaniu logicznym jeżeli występuje x lub z to wynikiem jest 1-bitowy skalar x Przykład: 4 b 1z0x == 4 b 1z0x // wynikiem jest x 4 b 1z0x!= 4 b 1z0x // wynikiem jest x 4 b 1z0x === 4 b 1z0x // wynikiem jest 1 4 b 1z0x!== 4 b 1z0x // wynikiem jest 0
Operatory bitowe & AND OR ^ XOR ^~ ~^ XNOR ~ NOT Jeżeli jeden z operatorów jest krótszy to jest on uzupełniony zerami na najstarszych pozycjach Przykład: a = 4'b1010 d = ~a // 0101 b = 4'b1100 e = a & b // 1000 c = 2'b11 f = b ^ c // 1100 XOR 0011 = 1111
Operatory logiczne && logiczny AND logiczny OR! logiczna negacja Przykład 1: alfa = 237, beta = 0 rega = alpha && beta; // (true AND false) rega jest 0 regb = alpha beta; // (true OR false) regb jest 1 Przykład 2: Użycie nawiasów dla lepszej czytelności a < size-1 && b!= c && index!= lastone (a < size-1) && (b!= c) && (index!= lastone) Przykład 3: Użycie operatora! if (!inword) // częściej używane... if (inword == 0)
Operator warunkowy cond_expr? true_expr : false_expr Przykład mux 4-1: o = (s == 2'b00)? I0 : ((s == 2'b01)? I1 : (s == 2'b10)? I2 : I3 ); Przykład ALU: d = (f==add)? (a+b) : ((f==substract)? (a-b) : ((f==compl)? ~a : ~b));
Operatory łączenia (konkatenacji) { } Łączy dwa lub więcej wektorów Każdy z wektorów musi mieć określoną długość Przykład: {a, b[3:0], w, 3 b101} // odpowiada kolejnej linii {a, b[3], b[2], b[1], b[0], w, 1 b1, 1 b0, 1 b1} Zły {a, 1} // Liczba 1 nie ma określonej długości Przykład powielania: {4{w}} // odpowiada {w, w, w, w} {b, {3{a, b}}} // odpowiada {b, a, b, a, b, a, b} nie może być zerem, x lub z
Specyfikacja układu module module name [(port name{, port name})]; [parameter declarations] [input declarations] [output declarations] [inout declarations] interfejs [wire or tri declarations] [reg or integer declarations] [function or task declarations] [assign continuous assignments] [initial block] [always blocks] [gate instantiations] [module instantiations] endmodule Logika ciało funkcji
Cechy modułu Wszystkie sygnały muszą być zadeklarowane jako: input, output lub inout Domyślnie porty są typu wire nie trzeba dodatkowej deklaracji Wyjście można zadeklarować jako reg Przykłady: module uklad1(we1, we2, wy1, wy2); input [3..0] we1; input we2; output wy1, wy2; reg wy1; module uklad1 ( input [3..0] we1, input we2, output reg wy1, output wy2 );
Równoległość Dwa typy przypisań: Przypisania ciągłe assign identyfikator = wyrazenie; assign s = a ^ b ^ cin; assign cout = (a & b) (a & cin) (b & cin); Przypisanie jest wykonywane, gdy wyrażenie zmieni swoją wartość, niezależnie od kolejności umieszczenia przypisania Przypisanie proceduralne w bloku always s = a ^ b ^ cin; cout = (a & b) (a & cin) (b & cin); Kolejność wykonane zależna od kolejności zapisu, podobnie jak w języku C.
Przypisanie ciągłe Opis układu na poziomie strukturalnym (bramkowym) lub funkcjonalnym Przykładem wyrażeń równoległych są instancje bramek lub tranzystorów, które opisują strukturę układu. W przypadku opisu układu za pomocą sieci bramek o jednym wyjściu (and, or, xor, nand, nor, xnor), pierwszym argumentem na liście jest wyjście bramki, natomiast drugim i ewentualnie kolejnymi argumentami są wejścia. Bramką o jednym wejściu i wielu wyjściach jest bramka not i buf.
Opis funkcjonalny module suma (a,b,cin,s,cout); // porty wej/wyj input a,b,cin; output s,cout; // sygnały // cialo funkcji assign s = a ^ b ^ cin; assign t1 = a & b; // deklaracja niejawna assign t2 = a & cin; assign t3 = b & cin; assign cout = t1 t2 t3; endmodule module suma (a,b,cin,s,cout); input a,b,cin; output s,cout; // sygnały wire t1, t2, t3; // deklaracja jawna // ciało funkcji assign s = a ^ b ^ cin; assign t1 = a & b; assign t2 = a & cin; assign t3 = b & cin; assign cout = t1 t2 t3; endmodule module suma (a,b,cin,s,cout); input a,b,cin; output s,cout; assign s = a ^ b ^ cin, // przypisania t1 = a & b, // tego t2 = a & cin, // samego t3 = b & cin, // typu cout = t1 t2 t3; endmodule
Opis strukturalny module suma (a,b,cin,s,cout); // porty wej/wyj input a,b,cin; output s,cout; // ciało funkcji xor(s, a, b, cin); and(t1, a, b); // deklaracja niejawna and(t2, a, cin); // dla t1, t2 i t3 and(t3, b, cin); or(cout, t1, t2, t3); endmodule module suma (a,b,cin,s,cout); // porty wej/wyj input a,b,cin; output s,cout; // ciało funkcji xor(s, a, b, cin); and(t1, a, b), // deklaracja tego (t2, a, cin), // samego (t3, b, cin); // typu or(cout, t1, t2, t3); endmodule
Przypisania proceduralne Przypisania proceduralne są wykonywane sekwencyjnie w kolejności umieszczenia ich w kodzie (w odróżnieniu od równoległego wykonywania przypisań ciągłych), podobnie jak ma to miejsce w językach programowania, na przykład w języku C. Przypisania proceduralne muszą być umieszczone wewnątrz instrukcji always lub initial. Za pomocą przypisań proceduralnych możemy opisać działanie układu cyfrowego na poziomie funkcjonalnym korzystając z instrukcji sterujących jak case czy if.
Przypisanie proceduralne opis funkcjonalny module suma (a,b,cin,s,cout); // porty wej/wyj input a,b,cin; output reg s,cout; // przypisania proceduralne always@* begin s = a ^ b ^ cin; cout = (a & b) (a & cin) (b & cin); end endmodule
Bloki proceduralne Verilog ma dwa typy bloków proceduralnych Instrukcja always Służy do opisu układu Jest syntetyzowalna Posiadać listę zmiennych (czułości), której zmiana wartości aktywuje wykonanie bloku Lista czułości może być pominięta Instrukcja initial - Służy do inicjacji zmiennych w programach testujących - Nie jest syntetyzowalna
Instrukcja always Instrukcja always rozpoczyna się w czasie = 0 Może być wykonywana wielokrotnie Przykład always begin // begin/end do wykonania grupy instrukcji instrukcja_1; instrukcja_2; // wykonana po instrukcja_1 end
Zdarzenia Służą do kontroli/wykrycia zdarzeń w czasie Używane np. w instrukcji always @ <zdarzenie> @(a or b) // reaguje na zmiany sygnału a lub b (lista czułości) @(a, b) @(posedge clk) // reaguje zboczem narastającym @(negedge clk) // reaguje zboczem opadającym @* // Verilog-2001 : lista czułości wszystkich sygnałów układu kombinacyjnego
Przypisania proceduralne Przypisanie blokujące, jak zmienna w języku C [variable_name] = [expresion] Przypisanie nieblokujące [variable_name] <= [expresion] Zasady użycia: Blokujące dla opisu układów kombinacyjnych Nieblokujące dla układów sekwencyjnych W jednym bloku always nie umieszczamy obu typów przypisań
Przypisanie blokujące i nieblokujące module block (x1, x2, x3, clock, f, g); input x1, x2, x3, clock; output f, g; reg f, g; always @(posedge clock) begin f = x1 & x2; g = f x3; end endmodule module nonblock (x1, x2, x3, clock, f, g); input x1, x2, x3, clock; output f, g; reg f, g; always @(posedge clock) begin f <= x1 & x2; g <= f x3; end endmodule
Przypisania blokujące i nieblokujące dla układów kombinacyjnych always@(*) begin f = a1 & a0; f = f (a2 & a1); end otrzymujemy równanie f=a1a0 + a2a1 always@(*) begin f <= a1 & a0; f <= f (a2 & a1); end ostatnie przypisanie nadpisuje poprzednie otrzymujemy f = f +a2a1 funkcja kombinacyjna ma sprzężenie zwrotne! brak realizacji!
Przypisania proceduralne i ciągłe dla układów kombinacyjnych Przykład: a & b & c module funkcja_and(a,b,c,y); input a,b,c; output reg y; always@* begin y = a; y = y & b; y = y & c; end endmodule Poprawna realizacja w sprzęcie module funkcja_and(a,b,c,y); input a,b,c; output reg y; assign y = a; assign y = y & b; assign y = y & c; endmodule Brak realizacji w sprzęcie sprzężenie zwrotne w układzie kombinacyjnym!