Nawigacja
Zróbmy sobie układ, część 2
- Drukuj
- 06 Feb 2018
- Poradniki
- 4938 czytań
- 0 komentarzy
HDL
Hardware Description Language. Język opisu sprzętu, czyli narzędzie, którym się można posłużyć przy projektowaniu układu, gdy nie chcemy składać całości z bramek i bloków funkcjonalnych.Z Wojny Języków pomiędzy VHDL i Verilogiem obronną ręką wyszły… oba języki. I do dziś niepodzielnie dzierżą prymat. VHDL (Very High Speed Integrated Circuits Hardware Description Language) sięga swoimi korzeniami do Ady lub jak niektórzy twierdzą Pascala. Verilog ma dla odmiany strukturę zbliżoną do C. Oba mogą służyć zarówno do symulacji obwodów, jak i do ich syntezy.
Który wybrać? Warto znać oba. Od którego zacząć? Na pewno od tego który zna NajbliższaZnającaJakiśHDLOsoba ;)
Można także zobaczyć opis tego samego układu napisany w obu językach i po prostu wybrać ten z języków, który wydaje się bardziej zrozumiały.
Symulacja i synteza … czyli jedna z pułapek tworzenia układów programowalnych
HDL to języki pierwotnie służące do opisu i symulacji układów. Możliwość syntezy została w obu dodana później. A zasymulować można dużo. Zarówno loty FTL jak i kulista krowa o punktowej masie dadzą się matematycznie opisać, można stworzyć przeróżne ich modele i wykorzystać w symulacjach. Problemy zaczynają się, gdy ktoś postanowi taki model zmaterializować. Nagle okazuje się, że w najbliższym OBI zabrakło wymaganej ilości materii egzotycznej do silnika FTL (najbliższa dostawa gdzieś w 2257, zapraszamy), a nawet najbardziej spasiona krowa masę owszem ma, ale punktową być bynajmniej nie zamierza.W przypadku HDL jest podobnie. Zasymulować można dużo, ale do syntezy można wykorzystać jedynie określony podzbiór języka.
Inną pułapką języków HDL, z którą musi się zmierzyć początkujący, jest ich podobieństwo do języków programowania. Pętle w HDL nie są tym, czym pętle w C/Pacalu. Kod nie opisuje kolejnych kroków, które wykonuje układ, ale jego zachowanie i reakcję na sygnały zewnętrzne, a poszczególne części kodu mogą działać – i działają - równolegle.
Uwaga na początek – jeśli gdzieś zobaczymy kod ‘
#100 $display("%b",c);
’ to nie liczmy na to, że maleńki CPLD automagicznie coś pośle po stu jednostkach czasu na wyświetlacz (czy nawet RSa), oraz pamiętajmy, że nie programujemy układu, ale opisujemy jego działanie.Verilog...w przykładach.
Na początek – bramka w czterech smakach. Zaczynamy nowy projekt, wybierając jako ‘Top-Level source type’ opcję HDL. Teraz ‘New source’, ’verilog module’, podajemy nazwę (bramki) i naciskając ‘next’ pomijamy wizarda.module bramki( ); endmodule
Tak wygląda pusty moduł w Verilogu.
module bramki( input clk, input x, input y, output out_a, output out_b, output out_c, output reg out_d ); endmodule
Ponieważ układ musi się komunikować ze światem zewnętrznym, trzeba dodać jakieś sygnały we/wy.
input i output określają kierunek sygnału. reg oznacza, że wyjście out_d przechodzi dodatkowo przez zatrzask.
Pora teraz wypełnić moduł treścią – czyli opisać działanie układu.
module bramki( input clk, input x, input y, output out_a, output out_b, output out_c, output reg out_d ); // nowy sygnał o nazwie iloczyn // który przybiera wartość będącą iloczynem sygnałów x i y. wire iloczyn; assign iloczyn = x & y; // nowy sygnał my_and // któremu od razu przypisujemy taką samą funkcję. wire my_and = x & y; // zatrzask / rejestr / pamięć o wielkości 1bit, // której wyjście wystawiamy na świat jako out_c reg r_out_c; assign out_c = r_out_c; // wartości początkowe zatrzasków initial begin out_d = 0; r_out_c = 0; end // ale co robi układ? // do wyjścia out_a podpinamy bloczek o nazwie brAnd, // typu AND2 (wzięty wprost z biblioteki elementów). AND2 brAnd(out_a,x,y); // wyjście out_b zwieramy z sygnałem iloczyn // (który został wyżej zdefiniowany jako x & y) assign out_b = iloczyn; // przy każdej zmianie sygnału x lub y, // zatrzask r_out_c przyjmuje wartość x & y always@(x or y) r_out_c = x & y; // przy każdej zmianie sygnału clk z 0 na 1 (zbocze narastające) // zatrzask/wyjście out_d przyjmuje wartość my_and always@(posedge clk) out_d <= my_and; endmodule
Moduł ma trzy sygnały wejściowe (zegar clk i dane x,y) oraz cztery wyjścia (out_a..d) z czego jedno jawnie posiada zatrzask (out_d), a jedno jest podpięte do zatrzasku zdefiniowanego w module (out_c).
Pojedyncze sygnały definiujemy przez wire. Dwa inne dające się zsyntetyzować rodzaje linii/sygnałów, to wand i wor (wired-and wired-or) czyli iloczyn i suma sygnałów ‘po drucie’.
O tym do czego się mogą przydać jeszcze wrócimy.
‘Wartość‘ sygnałów definiujemy przez assign - po prostu przypisujemy do sygnału wymaganą funkcję logiczną (tu: x & y). Można to zrobić od razu podczas deklarowania sygnału, można w dwóch krokach.
Blok initial określa wartości początkowe zatrzasków. Nie zawsze daje się on zsyntetyzować, zależy to od układu (xilinxy dają radę).
I wreszcie definicja czterech sygnałów wyjściowych.
Trzy układy kombinatoryczne:
Wyjście out_a zostało podpięte pod bramkę AND2 zdefiniowaną w bibliotece elementów (patrz poprzednia część). Warto jednak zawsze pamiętać starą zasadę – lepiej niepotrzebnie nie mieszać. Zwłaszcza że taki bloczek jest wpinany as-is, będzie to widać za chwilę.
Wyjście out_b zostało zwarte ze zdefiniowanym wcześniej sygnałem ‘iloczyn’. Oczywiście można by zapisać po prostu ‘
assign out_b = x & y;
’ ale skoro jest gotowy sygnał, czemu z niego skorzystać. Wyjście out_c odzwierciedla stan zatrzasku r_out_c. Natomiast sam zatrzask przyjmuje wartość (x & y) za każdym razem gdy zmieni się stan któregoś z wejść x lub y ( ‘
always@(x or y)
’ można zastąpić bardziej uniwersalnym 'always@(*)
' ) )Jeden układ synchroniczny :
Wyjście/zatrzask out_d przyjmuje wartość (x & y) przy każdym narastającym zboczu zegarowym.

Zobaczmy teraz, co z tego opisu zrobił układ syntezy :
Jak widać element z biblioteki został wbudowany w układ i pozostawiony bez dalszej optymalizacji.
Widać też, że w przypadku pozostałej części układu, ‘syntezator’ wykazał się pewnym sprytem.
Zauważył, że tak naprawdę cały czas operujemy na tym samym sygnale (x & y), oraz że zatrzask r_out_c nic nie daje (oprócz zbędnych opóźnień) – więc go wyeliminował.

Zobaczmy teraz jak będzie wyglądał układ fizyczny :
Kolejna niespodzianka - i coś co trzeba zapamiętać – do wejść / wyjść, które są połączone ze światem zewnętrznym, zawsze zostaną dodane bufory I/O i trzeba to brać pod uwagę. Dlatego jeśli ktoś wpadnie na pomysł żeby sobie zbudować generator pierścieniowy z trzech, czy pięciu bramek, to nie ma sprawy, będzie działać (pomińmy kwestie stabilności i temperatur… i jakby co – u mnie działał :) Jeśli ktoś wpadnie na pomysł żeby sobie wyprowadzić bramki na zewnątrz, zlinearyzować i podpiąć kwarc, powinien jeszcze raz przemyśleć układ, bo ten pomysł raczej nie zadziała.
module bramki_tb; // Inputs reg clk; reg x; reg y; // Outputs wire out_a; wire out_b; wire out_c; wire out_d; // Instantiate // the Unit Under Test (UUT) bramki uut ( .clk(clk), .x(x), .y(y), .out_a(out_a), .out_b(out_b), .out_c(out_c), .out_d(out_d) ); initial begin // Initialize Inputs clk = 0; x = 0; y = 0; // Wait 100 ns for // global reset to finish #100; // Add stimulus here end always #10 clk <= !clk; always #25 {x,y} <= {x,y}+1; endmodule
Zdefiniowany powyżej moduł można także wykorzystać jako bloczek - element większego układu albo w czymś, co nazywa się test bench i pozwala dokonać symulacji naszego układu.
Startujemy - ‘New source’ - ‘Verilog test fixture’ - nazwa ‘bramki_tb’ i jako source wybieramy ‘bramki’.
Program stworzył nam nowy moduł bramki_tb bez wejść i wyjść, z trzema zatrzaskami (po jednym na każde wejście badanego układu), czterema sygnałami out_* (po jednym na wyjście) oraz wstawił od razu instancję naszego modułu nazywając ją uut.
Warto i trzeba zauważyć, że przy wstawianiu ISE wykorzystał drugą metodę ‘podłączania’ modułu. Przy wstawianiu bramki (moduł biblioteczny AND2) wykorzystałem wiedzę, że sygnały modułu zostały zdefiniowane w postaci wyjście-wejście-wejście. ISE stosuje bezpieczniejszą metodę, nazywając wprost wejścia/wyjścia do których podpina sygnały (.x(sigX) – wejście .x układu łączymy z sygnałem sigX )
Jedyne co nam pozostało to dodanie sygnału zegarowego oraz zadbanie o zmianę sygnałów x i y.
Jak? Zegar najprościej przez ‘
always #20 clk <= !clk;
’ (nie, to się nie syntetyzuje!)A sygnały... Sygnały omówimy w następnym odcinku :)
Teraz, po kliknięciu w ‘simulation’ (nad okienkiem z drzewkiem plików), wybraniu poniżej ‘bramki_tb’ można wreszcie kliknąć na proces ‘iSim simulator’ by otrzymać wynik jak poniżej.

Jak widać, układ działa zgodnie z założeniami, a sygnał zegarowy jest zbyt wolny :)
Uwaga: iSim uruchamia symulację jedynie na okres czasu zdefiniowany w tym polu:

Można kontynuować symulacje po kawałeczku klikając na przyciski obok tego pola, lub wpisać w nie od razu dłuższy czas symulacji.
Garść linków
Czyli lektura dla chętnych.- Zestawienie VHDL z Verilog
- Verilog Cheat Sheet
- Tutorial na Reference Designer
- Tutorial na Asic World
- Kurs Verilog
- Verilog dla początkujących
- Porady - synteza
- Porady - Verilog
- Verilog - styl kodowania
- Embedded Micro - dużo tutoriali
- Oraz – wybaczcie – Wujek Gógiel :)
Internet jest pełen przeróżnej dokumentacji dotyczącej zarówno VHDLa jak i Veriloga. Nie krępujcie się szukać!
Co dalej
W kolejnej części rzucimy się na głęboką wodę – będą busy, rejestry, zagrzebiemy tristejta i stworzymy projekt czegoś bardziej praktycznego niż bramka logiczna.steev
Dodaj komentarz
Zaloguj się, aby móc dodać komentarz.
Oceny
Tylko zarejestrowani użytkownicy mogą oceniać zawartość strony
Zaloguj się , żeby móc zagłosować.
Zaloguj się , żeby móc zagłosować.
Brak ocen. Może czas dodać swoją?