Język opisu sprzętu VHDL (K Kołek) vhdl ksiazka


Język opisu sprzętu VHDL

Materiały pomocnicze dla przedmiotu

"Technika cyfrowa i mikroprocesorowa" II/III RA

Krzysztof Kołek

Katedra Automatyki, Akademia Górniczo-Hutnicza

Al. Mickiewicza 30, 30-059 Kraków

tel. (0-12) 617-28-55

kko@aquarium.ia.agh.edu.pl

Spis treści

1. Wstęp

  1. Wstęp

Język VHDL (skrót pochodzi od dwóch innych skrótów: V - Very High Speed Integrated Circuit oraz HDL - Hardware Description Language) rozwijany był na początku lat 80-tych do opisu niezależnych metod opisywania układów elektronicznych przez American Department of Defence (ang. DoD). Po raz pierwszy został zestandaryzowany w roku 1983, a następnie standaryzację powtórzono w latach 1987 oraz 1993.

Główne cechy języka VHDL to:

Programowanie w języku VHDL może odbywać się na różnych poziomach abstrakcji. Są to:

Każdy element opisywany w języku VHDL może posiadać dowolną liczbę definicji w każdej z omówionych warstw. Umożliwia to skupienie się w różnych fazach projektu na różnych jego aspektach.

Język przypomina języki programowania typu C lub Pascal jednak jego podstawowym celem nie jest wykonywanie obliczeń. Zadaniem języka jest wspieranie całości procesu projektowania układów cyfrowych poczynając od symulacji poprzez modelowanie, testowanie, implementacje oraz tworzenie dokumentacji. Podstawowym celem języka VHDL jest "zaprogramowanie" programowalnego układu logicznego niezależnie od technologii w jakiej ten układ został wytworzony (ASIC, FPGA, PLD, itp.). Dotyczy to również układów opracowywanych w przyszłości, których parametry nie są jeszcze aktualnie znane.

Podstawowym ograniczeniem języka VHDL jest, iż pierwotnie był projektowany wyłącznie do systemów cyfrowych. Aktualnie trwają prace nad standaryzacją wersji dla układów analogowych oraz nad opracowaniem analogowych programowalnych i konfigurowalnych układów scalonych o podobnym stopniu elastyczności jaki osiągnęły układy FPGA w technice cyfrowej.

Dostępne oprogramowanie wspomagające użytkowanie języka zwykle automatycznie tworzy i optymalizuje struktura komórek i połączeń podczas programowanie programowalnych układów logicznych. Język VHDL wydaje się być szczególnie użyteczny dla układów FPGA o 10-20 tysiącach komórek. Narzędzia języka HVDL dla układów FPGA kosztują od 1 do 20 tysięcy USD (w porównaniu z cenami 50-100 tysięcy USD dla układów ASIC).

Niniejsze opracowanie przedstawia skrótowy opis struktur języka VHDL ukierunkowany na wykorzystanie do programowania programowalnych układów logicznych firmy XILINX z wykorzystanie firmowego oprogramowania wspomagany przez pakiet oprogramowanie ORCAD w wersji 7.11. Należy zwrócić uwagę na występujące różnice między opisem standardu języka VHDL oraz jego implementacją w programie ORCAD. Praktyczna implementacja zawiera bowiem istotne ograniczenia w stosunku do ogólnej specyfikacji języka podyktowane kłopotami w realizacji niektórych cech w oparciu o aktualną technologię. Specyfikacja języka, będąc bardzo elastyczną, uwzględnia cechy, które aktualnie mogą być wyłącznie symulowane w sposób programowy (nie jest możliwa ich praktyczna realizacja z zastosowaniem okładów firmy XILINX) nie należy jednak wykluczyć ich praktycznej realizacji w oparciu o nowe rozwiązania technologiczne.

  1. Podstawowe pojęcia języka VHDL

Poniżej przedstawiono podstawowe pojęcia występujące w opisie języka VHDL. Należy pamiętać, iż język VHDL został zaprojektowany jako język opisu sprzętu. Powoduje to istotne różnice w stosunku do języków programowania ogólnego przeznaczenia. Język VHDL wykazuje duże podobieństwa składniowe do języków typu Pascal lub C niemniej podobnie wyglądające instrukcje mogą charakteryzować się dramatycznie odmiennym zachowaniem. W szczególności dotyczy to zasad określania zależności czasowych oraz pracy równoległej.

    1. Predefiniowane typy

Język VHDL posiada następujące predefiniowane typy danych:

Powyższe typy powinny być zdefiniowane w pakiecie STANDARD wchodzącym w skład języka. Definicja pewnych typów może być zależna od implementacji lub nie występować. Dotyczy to na przykład typu REAL, który ze względu na problemy implementacyjne nie zawsze jest dostępny.

    1. Logika wielowartościowa

Logika wielowartościowa posiada więcej typów niż tylko zero i jedynka logiczna. Pakiet Std_Logic_1164 wchodzący w skład języka VHDL zawiera definicję typów std_logic (typ "resolved") oraz std_ulogic (typ "unresolved") o następujących wartościach wraz z operującymi na nich funkcjami:

'U' - wartość nigdy dotychczas nie została określona,

'X' - wartość była znana ale aktualnie nie można podać jej konkretnej wartości; sygnał "strong drive",

'0' - sygnał logicznego 0 typu "strong drive",

'1' - sygnał logicznej 1 typu "strong drive",

'Z' - stan wysokiej impedancji; sygnał nie posiada "driver"-a,

'W' - wartość była znana ale aktualnie nie można podać jej konkretnej wartości; sygnał "weak drive"; rzadko używany,

'L' - sygnał logicznego 0 typu "weak drive",

'H' - sygnał logicznej 1 typu "weak drive",

'-' - wartość sygnału nie ma znaczenia.

Pakiet zawiera również definicje typów Std_logic_vector oraz Std_ulogic_vector.

Pakiet Std_logic zawarty jest w bibliotece IEEE. Poniższe instrukcje czynią elementy zdefiniowane w pakiecie Std_logic dostępnymi w projekcie:

library IEEE; -- Uczyń bibliotekę dostępną

use IEEE.Std_Logic_1164.all; -- Całość biblioteki dostępna

Jest możliwe podstawianie elementów typu std_ulogic do std_logic i vice-versa.

    1. Komponenty

Opis komponentów składowych projektu wykonywany jest za pomocą dyrektywy ENTITY opisującej sprzęg w strukturze hierarchicznej, bez definiowania zachowania. Jest odpowiednikiem symbolu na schemacie. Przykładowy opis komponenty przedstawiony jest poniżej.

ENTITY halfadd IS

PORT ( a,b : IN BIT;

sum, carry : OUT BIT);

END halfadd;

Blok ENTITY zawiera definicje sygnałów wejściowych i wyjściowych komponentu. Może również zawierać definicje parametrów i stałych. Opisany blok o nazwie halfadd posiada dwa wejścia (a oraz b) oraz dwa wyjścia (sum oraz carry). Wszystkie wejścia i wyjścia są typu BIT. Przedstawiony powyżej przykład odpowiada następującemu blokowi:

Zachowanie komponentu opisane jest w bloku ARCHITEKTURE.

ARCHITECTURE behave OF halfadd IS

BEGIN

sum <= a XOR b;

carry <= a AND b;

END behave;

Po słowie ARCHTECTURE znajduje się zdefiniowana przez użytkownika nazwa. Blok ARCHTECTURE jest zawsze związany z blokiem ENTITY. Znaki '<=' są symbolami przypisania nowych wartości do sygnałów. Każdy blok ENTITY może posiadać kilka, różniących się nazwami, bloków ARCHTECTURE. Umożliwia to opisanie projektu na różnych poziomach abstrakcji.

    1. Pakiety

W języku VHDL można pogrupować komponenty oraz ich właściwości w postaci pakietów. Pakiet jest zbiorem definicji, które mogą być dostępne przez wiele projektów w tym samym czasie. Jest odrębną jednostką projektową w VHDL. Występuje na zewnątrz rozważanych dotychczas jednostek (ENTITY i ARCHITECTURE). Może zawierać definicje stałych, typy definiowane przez użytkownika, deklaracje komponentów oraz podprogramy VHDL, które są dzielone pomiędzy różne projekty.

Pakiety definiuje się za pomocą dyrektywy PACKAGE.

Przykładowym pakietem jest Std_Logic_1164 zawierający między innymi definicje podstawowych typów.

    1. Podprogramy

Język VHDL umożliwia definicję zarówno funkcji jak i procedur, które ogólnie zwane są podprogramami. Przykładem działania funkcji jest transkodowanie, czyli zmiana zapisu liczby w jednym kodzie na inny (patrz przykład poniżej). Taka sama operacja może zostać zrealizowana za pomocą procedury (patrz drugi przykład). Różnica polega na zwracane wartości: procedura nie zwraca żadnej wartości i w związku z tym nie może zostać użyta np. w instrukcjach przypisania podczas gdy funkcja zwraca wartość określonego typu. Ewentualne wartości modyfikowane przez procedurę muszą zostać zadeklarowane na jej liście argumentów jako wyjściowe lub wejściowo/wyjściowe (out lub inout).

function Transcod(Value: in bit_vector(0 to 7)) return bit_vector is

begin

case Value is

when "00000000" => return "01010101";

when "01010101" => return "00000000";

when others => return "11111111";

end case;

end Transcod;

procedure Transcoder_1 (variable Value: inout bit_vector (0 to 7)) is

begin

case Value is

when "00000000" => Value:="01010101";

when "01010101" => Value:="00000000";

when others => Value:="11111111";

end case;

end procedure Transcoder_1;

    1. Operatory

Operatory dostępne w języku VHDL przedstawia poniższa tabela. Przedstawione operatory działają na standardowych typach wbudowanych w język. Mogą zostać niezależnie zdefiniowane dla typów definiowanych przez użytkownika.

Operator

Typy operandów

Typ wyniku

Logiczne

AND, OR, NAND, NOR, XOR, XNOR, NOT

BIT lub BOOLEAN

(BIT_VECTOR, STD_LOGIC, STD_LOGIC_VECTOR, STD_ULOGIC, STD_ULOGIC_VECTOR)

BIT lub BOOLEAN

(BIT_VECTOR, STD_LOGIC, STD_LOGIC_VECTOR, STD_ULOGIC, STD_ULOGIC_VECTOR)

Porównań

=, /=, <, <=, >, >=

Wszystkie typu

BOOLEAN

Przesunięć

SLL, SRL, SLA, SRA, ROL, ROR

Lewostronny: BIT lub wektor BOOLEAN; prawostronny: INTEGER

BOOLEAN

Dodawania

+, -, &

Tablica numeryczna lub element tablicy

Tego samego typu jak operandy

Znak

+, -

Numeryczny

Tego samego typu jak operandy

Mnożenia

*, /

INTEGER, REAL

Tego samego typu jak operandy

MOD, REM (reszta z dzielenia, różnie działają w zależności od znaku operandu)

INTEGER

Tego samego typu jak operandy

Inne

ABS

Numeryczny

Tego samego typu jak operandy

** (potęgowanie)

Lewostronny: numeryczny; prawostronny: INTEGER

Tego samego typu jak lewostronny operand

Spośród operatorów logicznych wszystkie oprócz NOT posiadają identyczny priorytet. Operator NOT posiada priorytet wyższy od pozostałych operatorów logicznych.

Dla argumentów typu tablicowego argumenty są wyrównywane w lewo i porównywane. Powoduje to, że '111' jest większe od '1011'. Oznacza to, że z wektorem nie jest związana żadna wartość numeryczne. To tylko zbiór elementów tego samego typu.

Podstawowo operatory arytmetyczne zdefiniowane są dla typów integer, real oraz time. Wykonywanie operacji arytmetycznych dla typów takich jak np. std_logic wymaga zdefiniowania funkcji definiujących operatory dla takich typów. Jest to możliwe ponieważ język VHDL umożliwia definiowanie znaczenia operatorów.

Dla operatorów arytmetycznych można wyspecyfikować zakres argumentów i wyniku:

entity ADD is

port(A, B : in integer range 0 to 7;

Z : out integer range 0 to 15);

end ADD;

architecture ARITHMETIC of ADD is

begin

Z <= A + B;

end ARITHMETIC;

Zmienne typu time można mnożyć i dzielić zarówno przez integer jak i real:

signal CLK : std_ulogic;

constant PERIOD : time := 50 ns;

wait for 5 * PERIOD;

wait for PERIOD * 1.5;

CLK <= not CLK after PERIOD/2;

    1. Sygnały, zmienne, stałe

Właściwe rozumienie cech oraz zasad użycia sygnałów oraz zmiennych jest najbardziej istotną cechą języka VHDL.

Sygnały w języku VHDL służą do reprezentacji rzeczywistych połączeń między elementami projektu. Odpowiadają więc połączeniom pojedynczym lub magistralom. Z sygnałami związane są opóźnienie w związku z propagacją sygnału oraz zdolność tłumienia sygnałów o czasie trwania krótszym od zadanego. Operatorem przypisania w stosunku do sygnałów jest '<='. Przykładowo instrukcja:

z <= a;

powoduje przypisanie do sygnały z wartości sygnału a.

Każdy sygnał ma z sobą związany wzmacniacz będący źródłem wartości sygnału. Przedstawione powyżej podstawienie odpowiada sytuacji:

Podstawienie wartości do sygnału oznacza w rzeczywistości ustawienie wartości wyjściowej ze wzmacniacza. Jeżeli występuje więcej niż jedno podstawienie do sygnału wówczas odpowiada to sytuacji gdy pojedynczy sygnał ma kilka związanych z nim wzmacniaczy. Pamiętając iż instrukcje mogą wykonywać się w sposób równoległy poniższy ciąg podstawień:

z <= a;

z <= b;

odpowiada sytuacji

Takie rozwiązanie jest zwykle niedopuszczalne. Wielokrotne podstawienie wartości do sygnału powoduje utworzenie wielu wzmacniaczy będących źródłem wartości sygnału. Jedynym przypadkiem dopuszczalności, a nawet dużej użyteczności takiego rozwiązania, jest zdefiniowanie sygnału z jako typu resolved. Oznacza to związanie z sygnałem z specjalnej funkcji, która określa jaka jest wartość sygnału w przypadku gdy jest on sterowany z dwóch wzmacniaczy.

W przypadku wielokrotnego podstawiania wartości do tego samego sygnału wykonywanego sekwencyjnie wykona się wyłącznie ostatnie podstawienie. Każde sekwencyjnie podstawienie wartości do sygnału "anuluje" poprzednie sekwencyjne podstawienia. Wszystkie takie podstawienia wpływają na wyjście tego samego wzmacniacza sygnału.

Podstawienia do sygnału w różnych sekwencyjnych fragmentach programu wykonywane jest równolegle i produkuje wielokrotne wzmacniacze dla podstawianego sygnału. W takim przypadku niezbędnym jest zdefiniowanie funkcji określającej wartość sygnału.

Przypisując wartość do sygnału można określić uzależnienia czasowe związane z taką operacją. Przypisania sygnałów mogą posiadać zdefiniowane opóźnienia typu inertial lub transport. Dodatkowo opóźnienie typu inertial może posiadac wyspecyfikowany parametr reject.

Podstawienie do zmiennej wykonywane jest za pomocą operatora <=. Instrukcja:

z <= a AFTER 5 NS;

powoduje przypisanie wartości sygnału a do sygnału z po 5 nanosekundach. Dodatkowo impulsy o długości poniżej 5 nanosekund pojawiające się w sygnale a nie powodują zmian wartości sygnału z.

Podstawienie

z <= REJECT 3 NS INERTIAL a AFTER 5 NS;

wprowadza opóźnienie 5 nanosekund i wycina impulsy o długości poniżej 3 nanosekund. Odpowiada to linii sygnałowej o zadanym opóźnieniu i ograniczonym paśmie.

Podstawienie

z <= TRANSPORT a AFTER 5 NS;

powoduje opóźnienie sygnału o 5 nanosekund. Dowolnie krótki impuls jest przenoszony. Odpowiada to linii o nieskończonym widmie i może np. posłużyć do modelowania linii opóźniających.

Podstawienie

z <= '1' AFTER 3 NS, '0' AFTER 7 NS, '1' AFTER 17 NS;

tworzy falę 1->0->1 zmieniającą wartości w chwilach czasu 3 ns, 7 ns oraz 17 ns.

Wszystkie podstawienia sygnałów wewnątrz bloku ARCHITECTURE wykonywane są w sposób równoległy.

Zmienne służą do zapamiętywania wartości chwilowych i w odróżnieniu od sygnałów nie mają bezpośredniego odniesienia w sprzęcie. Zmienne języka VHDL mogą być używane w sposób podobny do zmiennych klasycznych języków programowania.

Operatorem przypisania dla zmiennych jest ':='. Przykładowo:

z := a;

Stałe służą do deklaracji wartości określonych typów.

Zasady deklarowania, przypisywania i używania sygnałów, zmiennych oraz stałych zależą od tego czy wykonywane są w sekwencyjnej czy w równoległej części programu VHDL. Odpowiednie zasady przedstawia poniższa tabela.

Instrukcje wykonywane równolegle

Instrukcje wykonywane szeregowo

Deklaracja

Przypisanie

Użycie

Deklaracja

Przypisanie

Użycie

Sygnał

TAK

TAK

TAK

NIE

TAK

TAK

Zmienna

NIE

NIE

TAK

TAK

TAK

TAK

Stała

TAK

---

TAK

TAK

---

TAK

Zmienne zachowują swoją wartość między wywołaniami procesu.

Jest możliwe podstawianie sygnałów do zmiennych i odwrotnie choć ciągle obowiązuje zasada zgodności typów.

Zakresem ważności zmiennych jest TYLKO proces wewnątrz którego są zdefiniowane. Aby ich wartości wysłać na zewnątrz procesu należy je przypisać do sygnałów.

Obiekty typu variable są zmieniane natychmiast, są więc używane do przechowywania zmiennych tymczasowych posiadających wpływ na wykonanie procesu. Typowo, wejściowe sygnały przypisujemy do zmiennych (variable), wykonujemy są wszelkie obliczenia, a ich wyniki przypisujemy do sygnałów wyjściowych.

    1. Definicja i użycie typów

W języku VHDL występują typy definiowane przez użytkownika. Mają one charakter typu wyliczeniowego. Typy definiuje się wewnątrz bloków typu package, architecture lub process.

Przykładowa definicja typu wygląda w sposób następujący:

type MY_STATE is (RESET, IDLE, RW_CYCLE, INT_CYCLE);

...

signal STATE : MY_STATE;

...

STATE <= RESET;

Do sygnału danego typu można przypisać tylko wartość tego typu.

Zwykle elementy typów kodowane są na najmniejszej licznie bitów niezbędnych do zakodowania wszystkich elementów typu choć sposób kodowania zależy od implementacji języka.

Obiekty można przypisywać jeżeli są tego samego typu i tego samego rozmiaru:

signal Z_BUS:bit_vector(3 downto 0);

signal C_BUS:bit_vector(1 to 4);

Z_BUS <= C_BUS;

Z_BUS(3 downto 2) <= "00";

C_BUS(2 to 4) <= Z_BUS(3 downto 1);

Konkatenacja sygnałów w magistrale wykonywana jest za pomocą operatora &. Agregacja sygnałów również tworzy magistrale, ale jest przeprowadzana poprzez objęcie nawiasami oddzielonych przecinkiem elementów składowych:

signal Z_BUS : bit_vector(3 downto 0);

signal A, B, C, D : bit;

signal BYTE : bit_vector(7 downto 0);

Z_BUS <= A & B & C & D;

BYTE <= Z_BUS & Z_BUS;

Z_BUS <= (A, B, C, D);

-- equivalent to:

Z_BUS(3) <= A;

Z_BUS(2) <= B;

Z_BUS(1) <= C;

Z_BUS(0) <= D;

Specyfikacja pozycji może nastąpić przez wpis wartości na odpowiedniej pozycji pozycję lub poprzez podanie indeksu:

X <= (3=>'1', 1 downto 0=>'1', 2 => B);

Operator others umożliwia przypisanie wartości do niezdefiniowanych elementów:

X <= (3=>'1', 1=>'0', others => B);

Każdy sygnał, zmienna i stała muszą mieć określony typ.

    1. Równoległe oraz szeregowe wykonanie instrukcji. Koncepcja procesu

Równoległe (ang. conrurrent) wykonywane instrukcji odbywa się wewnątrz architektury, jednak na zewnątrz zdefiniowanych wewnątrz architektury procesów. Instrukcje wykonują się równolegle w tym samym czasie. Zachowanie instrukcji wykonywanych równolegle nie zależy od kolejności w jakiej są zapisane. Np. instrukcje:

X <= A + B;

Z <= C + X;

oraz instrukcje:

Z <= C + X;

X <= A + B;

dają TEN SAM REZULTAT. Rezultat obu operacji służy do policzenia wartości sygnału Z. Najlepiej widać to na poniższym schemacie przedstawiającym przepływ danych:

Pewne instrukcje mogą nie mieć sensu wówczas gdy wykonywane są szeregowo. Np.

X <= X + A;

opisuje sumator bez dodatkowego rejestru. Jest to więc logika kombinacyjna i jej realizacja sprzętowa wymagałaby następującej struktury:

Utworzona pętla algebraiczna uniemożliwia obliczenie wartości sygnału X.

Podczas pisanie programu w języku VHDL należy myśleć w sposób "zorientowany na sprzęt".

Instrukcje wykonują się sekwencyjnie wówczas gdy zostaną umieszczone wewnątrz procesu. Przykładowy proces przedstawiono poniżej:

MUX: process (A, B, SEL)

begin

if SEL = '1' then

Z <= A;

else

Z <= B;

end if;

end process MUX;

Ciało procesu zdefiniowane jest między słowami kluczowymi process oraz end process. MUX jest opcjonalną nazwą procesu. Po słowie process znajduje się lista inicjalizacyjna. Umieszczenie sygnału w liście inicjalizacyjnej powoduje, że proces będzie podlegał wykonaniu gdy zmieni się stan dowolnego sygnału z listy. W przeciwnym przypadku ciało procesu nie jest wykonywane. Typowo wewnątrz procesu zmieniane są wartości sygnałów. Jeżeli takie sygnały znajdują się na listach inicjalizacyjnych innych procesów jest to sposobem na "wywoływanie" jednych procesów przez inne.

Rozważmy przykład procesu realizującego funkcję multipleksera przedstawiony powyżej. Jeżeli sygnał SEL zostanie usunięty z listy inicjalizacyjnej wówczas zmiana sygnału SEL nie spowoduje zmiany wartości sygnału Z. Tylko zmiany A i B są w stanie wpłynąć na Z. Nie pracuje to wówczas jako poprawny multiplekser. Lista inicjalizacyjna powinna ZAWSZE być KOMPLETNA.

Instrukcje wewnątrz procesu wykonywane są sekwencyjnie - "jedna po drugiej".

Definicja procesu musi znajdować się w ciele architektury. Wewnątrz architektury może znajdować się dowolna liczba procesów:

architecture A of E is

begin

-- concurrent statements

P1 : process

begin

-- sequential statements

end process P1;

-- concurrent statements

P2 : process

begin

-- sequential statements

end process P2;

-- concurrent statements

end A;

Instrukcje wewnątrz procesów wykonywane są sekwencyjnie, ale procesy wykonują się w sposób równoległy. Oprócz procesów architektura może zawierać inne wykonywane równolegle instrukcje.

Sygnały zmieniane wewnątrz procesu uzyskują nową wartość dopiero po zakończeniu procesu. Zapobiega to podstawianiu wartości tymczasowych podczas wykorzystywania sygnałów po lewej stronie znaku przypisania. Jeżeli zachodzi potrzeba wykorzystania wartości tymczasowych należy używać zmiennych. Zmienne, w odróżnieniu od sygnałów, zmieniają swoje wartości natychmiast, bez oczekiwania na zakończenie procesu.

Te same instrukcje mogą posiadać różne znaczenie w zależności od tego czy są wykonywane wewnątrz czy na zewnątrz procesu.

    1. Zawieszanie wykonania procesu

Zawieszanie wykonania procesu oznacza wstrzymanie jego wykonania na określony czas lub do czasu zmiany wartości określonych sygnałów.

Występują dwa style zawieszania wykonania procesu w oczekiwaniu na zmiany sygnałów. Pierwszy wykorzystuje listę inicjalizacyjną:

process (A,B)

begin

if (A='1' or B='1') then

Z <= '1';

else

Z <= '0';

end if;

end process;

Proces podlega wykonaniu tylko wówczas gdy zmianie ulegnie któryś z sygnałów z listy inicjalizacyjnej po czym zatrzymuje się na końcu. Przedstawiony powyżej przykład jest równoważny:

process

begin

if (A='1' or B='1') then

Z <= '1';

else

Z <= '0';

end if;

wait on A, B;

end process;

Zdefiniowany proces nie ma listy inicjalizacyjnej. Instrukcja wait on w sposób jawny specyfikuje sygnały, których zmiana "odwiesi" proces. Proces zatrzymuje się na końcu i wznawia wykonanie gdy sygnał A lub B zmienią swoją wartość. Proces posiadający listę aktywacyjną nie może posiadać instrukcji wait.

Występuje kilka odmian instrukcji wait. Pierwsza z nich wstrzymuje wykonanie procesu na pewien okres czasu:

STIMULUS: process

begin

SEL <= '0';

BUS_B <= "0000";

BUS_A <= "1111";

wait for 10 ns;

SEL <= '1';

wait for 10 ns;

-- etc, etc

end process STIMULUS;

proces wznawia wykonanie po określonym w instrukcji wait for czasie.

Instrukcja wait on wstrzymuje wykonanie w oczekiwaniu na zmianę wartości sygnałów (zdarzenie zachodzące na sygnałach) wyspecyfikowanych w liście.

W poniższym przykładzie

process

begin

wait until CLK='1';

Q <= D;

end process;

instrukcja wait until czeka na zdarzenie na sygnale na moment gdy warunek jest prawdziwy. Gdy sygnał zmieni swoją wartość i dodatkowo spełniony jest warunek wykonanie procesu jest wznawiane.

Instrukcja wait wstrzymuje proces na zawsze. W przedstawionym poniżej przykładzie proces wykona się wyłącznie raz. Po wykonaniu instrukcji wait proces nie zostanie nigdy wznowiony:

STIMULUS: process

begin

SEL <= '0';

BUS_B <= "0000";

BUS_A <= "1111";

wait for 10 ns;

SEL <= '1';

wait;

end process STIMULUS;

    1. Instrukcje wykonywane szeregowo

Instrukcje for (instrukcja pętli z licznikiem), case (instrukcja wyboru jednej z kilku możliwości) oraz if (instrukcja rozgałęzienia) mogą być wykonywane WYŁĄCZNIE SEKWENCYJNIE. Oznacza to, że muszą znajdować się wewnątrz procesu.

Najprostsza forma instrukcji if umożliwiająca warunkowe wykonanie grupy instrukcji ma następującą postać:

if CONDITION then

-- sequential statements

end if;

W celu wykonania jednej z dwóch grup instrukcji należy wykorzystać instrukcję if w postaci:

if CONDITION then

-- sequential statements

else

-- sequential statements

end if;

Wykonanie jednej z kilku grup instrukcji możliwe jest dzięki instrukcji if w postaci:

if CONDITION1 then

-- sequential statements

elsif CONDITION2 then

-- sequential statements

elsif CONDITION3 then

-- sequential statements

else

-- sequential statements

end if;

Tylko pierwszy z warunków posiadający wartość logiczną prawdy służy do wyboru instrukcji podlegającej wykonaniu (w if może być więcej niż jeden prawdziwy warunek jak w przykładzie poniżej). Oznacza to występowanie wbudowanego priorytetu występującego dzięki wyborowi kolejności warunków instrukcji if.

process (A, B, C, X)

begin

if (X = "0000") then

Z <= A;

elsif (X <= "0101") then

Z <= B;

else

Z <= C;

end if;

end process;

Instrukcja case testuje wartość obiektu a następnie wykonuje jedną z grup instrukcji zależnie od aktualnej wartości obiektu. Ogólny format instrukcji wygląda w sposób następujący:

case OBJECT is

when VALUE_1 =>

-- statements

when VALUE_2 =>

-- statements

when VALUE_3 =>

--statements

--etc...

end case;

W instrukcji case każda możliwa wartość obiektu MUSI ZOSTAĆ WYSPECYFIOWANA i musi być wyspecyfikowana TYLKO JEDEN RAZ. Przykład instrukcji case przedstawiono poniżej:

process (A, B, C, X)

begin

case X is

when 0 to 4 =>

Z <= B;

when 5 =>

Z <= C;

when 7 | 9 =>

Z <= A;

when others =>

Z <= 0;

end case;

end process;

Ogólny format pętli z licznikiem for ma postać:

for I in 0 to 3 loop

-- statements

end loop;

Powoduje iteracyjne wykonanie ciała pętli dla wyspecyfikowanej wartości licznika pętli. Nie ma potrzeby oddzielnego definiowania licznika pętli. Przykład użycia pętli for przedstawiono poniżej:

entity EX is

port (A : in std_ulogic_vector(0 to 15);

SEL : in integer range 0 to 15;

Z : out std_ulogic);

end EX;

architecture RTL of EX is

begin

WHAT: process (A, SEL)

begin

for I in 0 to 15 loop

if SEL = I then

Z <= A(I);

end if;

end loop;

end process WHAT;

end RTL;

    1. Cykl "delta"

W każdej chwili czasu podczas wykonywania programu w języku VHDL występują dwie kolejki:

Pierwsza zawiera listę sygnałów, do których przypisano nowe wartości w procesie i które czekają na aktualizacje po zakończeniu procesu. Druga zawiera listę procesów, które czekają na wykonanie w związku ze zmianą wartości sygnałów na ich listach aktywacyjnych.

Jeżeli sygnał jest zmieniany to wszystkie procesy, które posiadają go w liście aktywacyjnej umieszczane są w kolejce procesów do wykonania. Z kolei każdy proces jest wykonywany, a uaktualniane sygnały nie są zmieniane natychmiast lecz umieszczane w kolejce sygnałów do zmiany. Po wykonaniu wszystkich procesów sygnały są uaktualniane. Jako wynik zmian sygnałów inne procesy mogą zostać umieszczone w kolejce procesów. Takie cykle przeplatają się wzajemnie

Jeden taki cykl jest zwany cyklem delta.

Wiele cykli delta może być wykonywanych w tym samym czasie symulacji (wykonania). Wykonanie w danym czasie kończy się wówczas gdy kolejki sygnałów do zmiany i procesów do wykonania są puste. Następnie przechodzi się do następnego kroku symulacji/wykonania.

    1. Atrybuty

Zmienne, funkcje, typy, sygnały oraz stałe posiadają związane z nimi atrybuty, które ułatwiają programowanie. W rozdziale xxxx zawarto opis wybranych atrybutów. Do atrybutu odwołuje się zapisując po nazwie obiektu apostrof i podając nazwę atrybutu. Przykładowo, zapis A'Event oznacza atrybut, który staje się prawdą wówczas gdy nastąpiła zmiana wartości sygnału A (zdarzenie na sygnale A). Poniżej przedstawiono przykład wykorzystania atrybutów do liczenia parzystości sygnału o dowolnym rozmiarze.

process (A)

variable TMP : std_ulogic;

begin

TMP := '0';

for I in A'low to A'high loop

TMP := TMP xor A(I);

end loop;

ODD <= TMP;

end process;

Atrybuty A'low oraz A'high oznaczają odpowiednio najniższy i najwyższy indeks sygnału A.

    1. Pozostałe cechy języka

Język VHDL nie interpretuje wielkości liter. Elementy języka posiadają takie samo znaczenie niezależnie od tego czy pisane są małymi czy dużymi literami.

Znakiem komentarze jest podwójny ciąg znaków minus: '--'. Pojawienie się tego ciągu w programie powoduje, że pozostała część linii programu traktowana jest jako komentarz.

Typowym rozszerzeniem plików zawierających programy w języku VHDL jest vhd. Kompilatory języka na podstawie plików źródłowych tworzą pliki binarne, które mogą służyć do symulacji lub programowania układów programowalnych. Najmniejsza kompilowalna jednostka nosi nazwę projektu.

VHDL traktuje wektor jako zbiór elementów tego samego typu, zgrupowanych razem dla wygody. Wektor (np. bitów) nie jest równoważny żadnej liczbie. Dlatego nie można wykonywać na wektorze bitów operacji arytmetycznych, a operacje relacyjne mają sens tylko wówczas gdy wektory są tej samej długości.

    1. Przeładowanie operatorów

Przeładowanie operatorów oznacza możliwość zdefiniowania kilku funkcji o tej samej nazwie ale różnych typach wejść i wyjść. W szczególności oznacza to możliwość wielokrotnego definiowania operatorów. Zwykle dostawcy oprogramowania dostarczają pakiety zawierające definicje typowych operatorów dla popularnych typów zmiennych. Poniżej przedstawiony przykład takiego pakietu.

package P_ARITHMETIC is

subtype slv is std_logic_vector;

function "+"(L:slv; R:slv) return integer;

function "+"(L:slv; R:slv) return slv;

function "+"(L:slv; R:integer) return slv;

function "+"(L:integer; R:slv) return slv;

end P_ARITHMETIC;

use work.P_ARITHMETIC.all;

entity OVERLOADED is

port (A_BUS,B_BUS: in slv (0 to 3);

A_INT,B_INT: in integer;

Y_BUS,Z_BUS: out slv (0 to 3);

Y_INT,Z_INT: out integer);

end OVERLOADED;

  1. Strukturalny opis sprzętu

Strukturalny opis układu cyfrowego polega na opisaniu w języku VHDL elementów składowych oraz połączeń między nimi. Zwykle definiuje się pewną liczbę elementów podstawowych (np. podstawowe bramki logiczne) i w oparciu o nie buduje struktury bardziej złożone. Zachowania funkcjonalne wynikają ze sposobu połączeń elementów składowych. Ich funkcje nie są nigdzie zapisane w sposób jawny co odróżnia ten sposób zapisu od behawioralnego opisu sprzętu.

Strukturalny opis sprzętu jest użyteczny np. w sytuacji gdy niezbędne jest oddanie struktury istniejącego już rozwiązania (układu elektronicznego lub układu scalonego) i znany jest jego schemat elektroniczny. Wówczas nie wnikając w jego funkcje oraz zależności czasowe między elementami można za pomocą opisu strukturalnego odzwierciedlić jego zachowanie w języku VHDL.

Strukturalny sposób opisu sprzętu jest używany głównie przez programy komputerowe do automatycznej generacji programów w języku VHDL na podstawie np. schematów układów (np. pochodzących z programu ORCAD). Umieszczenie na wyjściowym schemacie np. układów średniej skali integracji powoduje, iż program "rozpisuje" je na elementy składowe (zwykle bramki logiczne) tworząc kompletny strukturalny opis sprzętu umożliwiający jego praktyczną implementację.

Strukturalny opis sprzętu nie wydaje się być szczególnie efektywny przy opisie układu wykonywanym przez człowieka. W takim przypadku odpowiedniejszym sposobem wydaje się być behawioralny opis zachowań. Niemniej podkreślić należy, iż oba sposoby opisu są zupełne i za pomocą każdego z nich można opisać każdą strukturę logiczną.

    1. Sumator 4-bitowy. Opis strukturalny

Jako przykład realizacji funkcji logicznej opisanej w języku VHDL w sposób strukturalny przedstawimy 4-bitowy sumator binarny. Sumator 4-bitowy składa się z bloków sumatorów jednobitowych o strukturze przedstawionej na Rys.3.1. Wejścia do sumatora stanowią dwie dodawane liczby Ai i Bi oraz przeniesienie z poprzedniej pozycji Ci-1. Wyjściem jest wynik dodawania Si oraz przeniesienie na następną pozycję Ci.

Rys.3.1. Jednobitowy sumator binarny.

Tablice Karnouhg oraz funkcje logiczne dla wyjść przedstawiono poniżej. Odpowiednio dla wyjścia sumy Si jest to:

0x08 graphic
Ai Bi

Ci

Ci-1

00

01

11

10

0

0

0

1

0

1

0

1

1

1

Si = Ai and Bi or Ci-1 and Ai or Ci-1 and Bi =

(Ai nand Bi) nand (Ci-1 nand Ai) nand (Ci-1 nand Bi)

oraz dla wyjścia przeniesienia na następną pozycję Ci:

0x08 graphic
Ai Bi

Si

Ci-1`

00

01

11

10

0

0

1

0

1

1

1

0

1

0

Ci = Ai xor Bi xor Ci-1

Sumator 4-bitowy realizowany jest jako kaskadowe połączenie czterech sumatorów jednobitowych (Rys.3.2).

Rys.3.2. Kaskadowy sumator 4-bitowy.

Realizacja omawianej struktury sumatora 4-bitowego w programie ORCAD przedstawiona została na Rys.3.3. Strukturalny opis sumatora w języku VHDL realizowany jest w bloku SumLogic. Porty wejściowe SW3_1 do SW3_8 oraz osiem buforów wejściowych IBUF służą do zadania wartości dwóch 4-bitowych składników. Porty oraz bufory wyjściowe służą do wyprowadzenia wyniku.


Rys.3.3. Realizacja 4-bitowego sumatora binarnego w programie ORCAD.


Omawiany przykład zrealizowany został na układzie programowalnym firmy XILINX o numerze 4008. Etykiety nadane sygnałom za portami wejściowymi oraz przed portami wyjściowymi o postaci Pxx definiują numery wyprowadzeń układu 4008. Np. P28 definiuje wyprowadzenie układu o numerze 28.

Blok READBACK wraz z elementami wejściowymi oraz wyjściowymi dodany został w celu weryfikacji poprawności zapisu danych do układu 4008. Jego obecność jest opcjonalna.

Program w języku VHDL związany z blokiem SumLogic przedstawiono poniżej. Zawiera on deklarację trójwejściowej bramki XOR xor3, dwuwejściowej bramki NAND nand2 oraz trójwejściowej bramki NAND nand3. Na ich podstawie zdefiniowano blok sumatora jednobitowego Sum1Bit. Z kolei ten sumator jednobitowy służy do definicji sumatora czterobitowego SumLogic.

Strukturalny opis elementu rozpoczyna się od wyspecyfikowania części składowych deklarowanych za pomocą komendy COMPONENT. Po słowie kluczowym COMPONENT następuje nazwa komponentu, a po słowie kluczowym PORT specyfikacja wejść oraz wyjść komponentu wraz z podaniem ich typów. Dyrektywy postaci:

FOR ALL : xor3gate USE ENTITY xor3;

określają jaki blok ENTITY związany jest z deklarowanymi komponentami. Dyrektywy PORT MAP przypisują odpowiednie sygnały do odpowiednich wejść oraz wyjść poszczególnych komponentów. Przykładowo dla deklaracji bloku ENTITY:

ENTITY xor3 is

PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

I3 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ;

END xor3;

komendy:

COMPONENT xor3gate PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

I3 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ; END COMPONENT;

FOR ALL : xor3gate USE ENTITY xor3;

--

--

st1 : xor3gate PORT MAP ( A, B, Ci, Co ); -- Co

przyporządkowują komponent xor3gate do bloku ENTITY xor3 oraz wiążą z bramką st1 sygnały A, B, Ci oraz Co w taki sposób, że sygnały A, B oraz Ci są wejściami i odpowiadają sygnałom I1, I2 oraz I3 oraz sygnał Co jest wyjściem odpowiadającym wyjściu O bloku ENTITY o nazwie xor3.

Kompletna implementacja bloku SumLogic przedstawiona została poniżej.

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY xor3 is

PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

I3 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ;

END xor3;

ARCHITECTURE behavior_xor3 OF xor3 IS

BEGIN

O <= I1 xor I2 xor I3;

END behavior_xor3;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY nand2 is

PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ;

END nand2;

ARCHITECTURE behavior_nand2 OF nand2 IS

BEGIN

O <= not (I1 and I2);

END behavior_nand2;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY nand3 is

PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

I3 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ;

END nand3;

ARCHITECTURE behavior_nand3 OF nand3 IS

BEGIN

O <= not (I1 and I2 and I3);

END behavior_nand3;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY Sum1Bit is

PORT (

A : IN STD_LOGIC;

B : IN STD_LOGIC;

Ci : IN STD_LOGIC;

S : OUT STD_LOGIC;

Co : OUT STD_LOGIC ) ;

END Sum1Bit;

ARCHITECTURE behavior_Sum1Bit OF Sum1Bit IS

COMPONENT xor3gate PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

I3 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ; END COMPONENT;

COMPONENT nand2gate PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ; END COMPONENT;

COMPONENT nand3gate PORT (

I1 : IN STD_LOGIC;

I2 : IN STD_LOGIC;

I3 : IN STD_LOGIC;

O : OUT STD_LOGIC ) ; END COMPONENT;

FOR ALL : xor3gate USE ENTITY xor3;

FOR ALL : nand2gate USE ENTITY nand2;

FOR ALL : nand3gate USE ENTITY nand3;

SIGNAL t1, t2, t3 : STD_LOGIC;

BEGIN

st1 : xor3gate PORT MAP ( A, B, Ci, Co ); -- Co

st2 : nand2gate PORT MAP ( A, B, t1 );

st3 : nand2gate PORT MAP ( A, Ci, t2 );

st4 : nand2gate PORT MAP ( B, Ci, t3 );

st5 : nand3gate PORT MAP ( t1, t2, t3, S ); -- S

END behavior_Sum1Bit;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY SumLogic is

PORT (

S : OUT STD_LOGIC_VECTOR (4 DOWNTO 0);

A : IN STD_LOGIC_VECTOR (3 DOWNTO 0);

B : IN STD_LOGIC_VECTOR (3 DOWNTO 0));

END SumLogic;

ARCHITECTURE behavior OF SumLogic IS

COMPONENT S1B PORT (

A : IN STD_LOGIC;

B : IN STD_LOGIC;

Ci : IN STD_LOGIC;

S : OUT STD_LOGIC;

Co : OUT STD_LOGIC ) ; END COMPONENT;

FOR ALL : S1B USE ENTITY Sum1Bit;

SIGNAL C0, C1, C2, C3, C4 : STD_LOGIC;

BEGIN

C0 <= '0';

st1 : S1B PORT MAP ( A(0), B(0), C0, S(0),C1 );

st2 : S1B PORT MAP ( A(1), B(1), C1, S(1),C2 );

st3 : S1B PORT MAP ( A(2), B(2), C2, S(2),C3 );

st4 : S1B PORT MAP ( A(3), B(3), C3, S(3),C4 );

S(4) <= C4;

END behavior;

Ze względu na niską efektywność tworzenia układów logicznych opisywanych w sposób strukturalny ten sposób opisu nie będzie omawiany w dalszej części niniejszej publikacji. Niemniej należy mieć świadomość, że wobec faktu automatycznej generacji przez program ORCAD opisów w języku VHDL wprowadzonych schematów znajomość zasad strukturalnego opisu logiki może okazać się niezbędna.

    1. Generator numerów górników. Projekt w postaci schematu elektrycznego

Jednym ze sposobów zaprogramowanie programowalnych układów logicznych jest narysowanie schematu elektronicznego projektowanego układu. W sposobie tym pośrednio wykorzystywany jest strukturalny opis sprzętu bowiem program ORCAD z wprowadzonego schematu w sposób automatyczny generuje program w języku VHDL, który z kolei jest podstawą do wygenerowania ciągu bitowego programującego programowalny układ logiczny.

Poniżej omówiono aplikację, której celem była lokalizacja położenia górników w kopalni. System lokalizacji składa się z nadajników rozmieszczonych w chodnikach kopalni oraz z odbiorników przenoszonych przez każdego górnika. Każdy z nadajników nadaje kolejno wszystkie kombinacje 13-bitowych numerów w formacie przedstawionym na Rys.3.4. Każdy z odbiorników jednoznacznie związany z określonym górnikiem posiada "zaszyty" w sobie unikalny numer 13-bitowy. Jeżeli odbiornik znajdzie się w strefie pracy nadajnika wysyła potwierdzenie umożliwiające lokalizacje konkretnego górnika. Schematy ideowe układu nadajnika oraz odbiornika przedstawiono na Rys.3.5, Rys.3.6 oraz Rys.3.7.

Realizacja poprzez wprowadzenie schematu elektronicznego układu była w tym przypadku szczególnie efektywna bowiem znany był schemat elektroniczny poprawnie pracującego układu. W dalszej części opracowania przedstawiony zostanie układu nadajnika oraz odbiornika zrealizowany poprzez behawioralny opis funkcji w języku VHDL.

Rys.3.4. Format numeru.

Warto zauważyć, iż pierwotna realizacja układu wykonana w oparciu o układy TTL oraz CMOS średniej skali integracji wymagała wykorzystania po kilkanaście układów zarówno po stronie odbiornika jak i nadajnika. Wykorzystując układy programowalne firmy XILINX można do układu 4003 "wpisać" zarówno nadajnik jak i odbiornik wykorzystując tylko około 50% pojemności układu. Dodatkowymi zaletami są niezwykła elastyczność przeprogramowania układu oraz redukcja zużycia mocy szczególnie istotna w przypadku zasilania bateryjnego.

Rys.3.5. Blokowy schemat układu nadajnika oraz odbiornika.


Rys.3.6. Schemat ideowy nadajnika numerów.

Rys.3.7. Schemat ideowy odbiornika numerów umożliwiający detekcje konkretnego numeru.


  1. Behawioralny opis sprzętu

Behawioralny opis sprzętu polega na opisie w języku VHDL funkcji układu, a nie struktury połączeń elementów. Zamiast używać instrukcji definiujących komponenty oraz połączenia między nimi używa się operatorów, instrukcji podstawienia, definicji procesów, instrukcji pętli, rozgałęzień itp. Opis behawioralny przypomina zapis algorytmu w takich językach programowania jak C lub Pascal.

Poniżej przedstawiono opis behawioralny funkcji sumatora jednobitowego omawianego w poprzednim rozdziale. Warto zwrócić uwagę na znacznie bardziej zwarty i czytelny zapis wewnątrz bloku ARCHITEKTURE w porównaniu z opisem strukturalnym.

ENTITY Sum1Bit is

PORT (

A : IN STD_LOGIC;

B : IN STD_LOGIC;

Ci : IN STD_LOGIC;

S : OUT STD_LOGIC;

Co : OUT STD_LOGIC ) ;

END Sum1Bit;

ARCHITECTURE behavior_Sum1Bit OF Sum1Bit IS

BEGIN

S <= ( A and B ) or ( A and Ci ) or ( B and Ci );

Co <= A xor B xor Ci;

END behavior_Sum1Bit;

Poniżej przedstawiono wybrane techniki programowania używane podczas behawioralnego opisu zachowania układów cyfrowych.

    1. Wielokrotne wywołanie procesu

Rozważmy przykład:

EXAMPLE: process (A, B, M)

begin

Y <= A;

M <= B;

Z <= M;

end process EXAMPLE;

Występuje tutaj przypisanie do sygnału M oraz sygnał M znajduje się jednocześnie na liście aktywacyjnej procesu. W instrukcji M <= B; sygnał M nie zmienia się ponieważ zmiany następują dopiero po zakończeniu procesu. W związku z tym nie nastąpi zmiana wartości sygnału Z (ściśle mówiąc nowa wartość sygnału Z będzie równa wartości sygnału B). Gdy proces się zakończy M jest zmieniane na wartość B, ale Z zmienia wartość na poprzednią wartość M. Ale M się zmieniło i M jest na liście aktywacyjnej więc proces jest powtórnie wywoływany. To spowoduje zmianę wartości sygnału Z. Jest to więc sposób na wielokrotne wywołanie procesu.

    1. Definiowanie rejestrów

Poniższy program opisuje przerzutnik typu D wyzwalany narastającym zboczem:

entity FLOP is

port (D, CLK : in std_ulogic;

Q : out std_ulogic);

end FLOP;

architecture A of FLOP is

begin

process

begin

wait until CLK'event and CLK='1';

Q <= D;

end process;

end A;

Wyjście Q jest uaktualniane tylko wówczas gdy CLK się zmieniło i gdy jest '1' czyli w odpowiedzi na narastające zbocze. Proces jest taktowany sygnałem zegarowym CLK.

Narastającego zbocze może zostać również wykryte za pomocą następującej sekwencji instrukcji:

process (CLK)

begin

if (CLK'event and CLK='1') then

Q <= D;

end if;

end process;

lub za pomocą:

library IEEE;

use IEEE.Std_Logic_1164.all;

...

process

begin

wait until RISING_EDGE(CLK);

Q <= D;

end process;

Ostatni sposób nie zawsze jest dostępny.

Przerzutnik przezroczysty typu "latch" może zostać zaimplementowany w następujący sposób:

process (EN, D)

begin

if (EN = '1') then

Q <= D;

end if;

end process;

Gdy EN=1 to przerzutnik jest przezroczysty, gdy nie to podtrzymuje poprzednią wartość. Jest to tzw. "incomplete assigment" występujący wówczas gdy wartość jest przypisana do zmiennej w jednej części instrukcji if, a w innej nie.

Poniżej przedstawiono implementacje wybranych typów przerzutników. Są to kolejno:

-- D F/F with asynchronous Reset

-- CLK: in STD_LOGIC;

-- RESET: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

process (CLK, RESET)

begin

if RESET='1' then --asynchronous RESET active High

DOUT <= '0';

elsif (CLK'event and CLK='1') then --CLK rising edge

DOUT <= DIN;

end if;

end process;

-- D F/F with synchronous Reset

-- CLK: in STD_LOGIC;

-- RESET: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

process (CLK)

begin

if CLK'event and CLK='1' then --CLK rising edge

if RESET='1' then --synchronous RESET active High

DOUT <= '0';

else

DOUT <= DIN;

end if;

end if;

end process;

-- D Flip-Flop

-- CLK: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

process (CLK)

begin

if CLK'event and CLK='1' then --CLK rising edge

DOUT <= DIN;

end if;

end process;

-- D Latch

-- GATE: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

process (GATE, DIN)

begin

if GATE='1' then --GATE active High

DOUT <= DIN;

end if;

end process;

-- D Latch with Reset

-- GATE: in STD_LOGIC;

-- RESET: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

process (GATE, DIN, RESET)

begin

if RESET='1' then --RESET active High

DOUT <= '0';

elsif GATE='1' then --GATE active High

DOUT <= DIN;

end if;

end process;

    1. Czterobitowy rejestr z równoległym ładowaniem i asynchronicznym zerowaniem

Przykład rejestru czterobitowego z równoległym ładowaniem i asynchronicznym zerowaniem przedstawiono poniżej.

-- 4-bit parallel load register with asynchronous reset

-- CLK: in STD_LOGIC;

-- ASYNC: in STD_LOGIC;

-- LOAD: in STD_LOGIC;

-- DIN: in STD_LOGIC_VECTOR(3 downto 0);

-- DOUT: out STD_LOGIC_VECTOR(3 downto 0);

process (CLK, ASYNC)

begin

if ASYNC='1' then

DOUT <= "0000";

elsif CLK='1' and CLK'event then

if LOAD='1' then

DOUT <= DIN;

end if;

end if;

end process;

    1. Czterobitowy binarny licznik asynchroniczny

Poniżej przedstawiono implementację czterobitowego binarnego licznika asynchronicznego.

-- 4-bit asynchronous binary counter

-- CLK: in STD_LOGIC;

-- RESET: in STD_LOGIC;

-- COUNT: out STD_LOGIC_VECTOR(3 downto 0);

-- The declaration of the auxiliary signal COUNTER must be inserted in

-- the architecture declarative part.

-- The output port "COUNT" cannot appear on the right side of assignment

-- statements.

signal COUNTER: STD_LOGIC_VECTOR(3 downto 0);

process (CLK, COUNTER, RESET)

begin

if RESET='1' then

COUNTER <= "0000";

else

if CLK'event and CLK='1' then

COUNTER(0) <= not COUNTER(0);

end if;

if COUNTER(0)'event and COUNTER(0)='0' then

COUNTER(1) <= not COUNTER(1);

end if;

if COUNTER(1)'event and COUNTER(1)='0' then

COUNTER(2) <= not COUNTER(2);

end if;

if COUNTER(2)'event and COUNTER(2)='0' then

COUNTER(3) <= not COUNTER(3);

end if;

end if;

end process;

COUNT <= COUNTER;

    1. Konwerter kodu BCD na kod wyświetlacza siedmiosegmentowego

W przypadku układów wykorzystujących wyświetlacze siedmiosegmentowe LED częstym problemem jest przekodowanie liczby w zakresie od 0 do 9 zapisanej w kodzie BCD na kod wyświetlacza. Poniżej przedstawione zostało rozwiązanie tego problemu. Ciągi bitowe zwracane w zmiennej LED sterują poprawnie poszczególnymi segmentami wyświetlacza jeżeli zostały one podłączone do kolejnych bitów w sposób objaśniony w komentarzu.

--HEX-to-seven-segment decoder

-- HEX: in STD_LOGIC_VECTOR (3 downto 0);

-- LED: out STD_LOGIC_VECTOR (6 downto 0);

--

-- podlaczenie segmentow wyswietlacza

-- do kolejnych bitow zmiennej LED

-- 0

-- ---

-- 5 | | 1

-- --- <- 6

-- 4 | | 2

-- ---

-- 3

with HEX select

LED<= "1111001" when "0001", --1

"0100100" when "0010", --2

"0110000" when "0011", --3

"0011001" when "0100", --4

"0010010" when "0101", --5

"0000010" when "0110", --6

"1111000" when "0111", --7

"0000000" when "1000", --8

"0010000" when "1001", --9

"0001000" when "1010", --A

"0000011" when "1011", --b

"1000110" when "1100", --C

"0100001" when "1101", --d

"0000110" when "1110", --E

"0001110" when "1111", --F

"1000000" when others; --0

    1. Multiplekser 4 na 1

Poniższy przykład realizuje multiplekser o czterech wejściach A, B, C, D oraz jednym wyjściu MUX_OUT. Sygnał SEL pełni rolę wejścia adresowego.

-- 4 to 1 multiplexer design with case construct

-- SEL: in STD_LOGIC_VECTOR(0 to 1);

-- A, B, C, D:in STD_LOGIC;

-- MUX_OUT: out STD_LOGIC;

process (SEL, A, B, C, D)

begin

case SEL is

when "00" => MUX_OUT <= A;

when "01" => MUX_OUT <= B;

when "10" => MUX_OUT <= C;

when "11" => MUX_OUT <= D;

when others => MUX_OUT <= 'X';

end case;

end process;

    1. Czterobitowy rejest przesuwający

Poniżej przedstawiono realizację czterobitowego rejestru przesuwnego o szeregowym wejściu oraz o szeregowym wyjściu.

-- 4-bit serial-in and serial-out shift register

-- CLK: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

process (CLK)

variable REG: STD_LOGIC_VECTOR(3 downto 0);

begin

if CLK'event and CLK='1' then

REG := DIN & REG(3 downto 1);

end if;

DOUT <= REG(0);

end process;

    1. Czterobitowy licznik synchroniczny z wejściem zezwalającym na zliczanie, asynchronicznym zerowaniem i synchronicznym ładowaniem

Poniżej widoczna jest implementacja czterobitowego licznika synchronicznego z wejściem zezwalającym na zliczanie, asynchronicznym zerowaniem i synchronicznym wpisem równoległym.

-- 4-bit synchronous counter with count enable, asynchronous reset and synchronous load

-- CLK: in STD_LOGIC;

-- RESET: in STD_LOGIC;

-- CE, LOAD, DIR: in STD_LOGIC;

-- DIN: in INTEGER range 0 to 15;

-- COUNT: out INTEGER range 0 to 15;

process (CLK, RESET)

--auxiliary variable COUNTER declaration

--the output port "COUNT" cannot appear on the right side of assignment

-- statements

variable COUNTER: INTEGER range 0 to 15;

begin

if RESET='1' then

COUNTER := 0;

elsif CLK='1' and CLK'event then

if LOAD='1' then

COUNTER := DIN;

else

if CE='1' then

if DIR='1' then

if COUNTER = 15 then

COUNTER := 0;

else

COUNTER := COUNTER + 1;

end if;

else

if COUNTER = 0 then

COUNTER := 15;

else

COUNTER := COUNTER - 1;

end if;

end if;

end if;

end if;

end if;

COUNT <= COUNTER;

end process;

    1. Bufor trójstanowy

Poniżej przedstawiono przykład realizacji bufora trójstanowego.

-- Tristate Buffer

-- ENABLE: in STD_LOGIC;

-- DIN: in STD_LOGIC;

-- DOUT: out STD_LOGIC;

DOUT <= DIN when ENABLE='1' else 'Z';

    1. Funkcja konwersji typu bit_vector do typu integer

W języku VHDL ciągi bitów nie reprezentują sobą żadnej wartości. Poniżej przedstawiono przykład funkcji, która przyjmuje jako argument wektor bitowy i zwraca odpowiadającą mu w kodzie binarnym liczbę całkowitą.

function bit_vec2int(A : BIT_VECTOR) return integer is

variable RESULT: integer:=0;

variable TMP: integer:=1;

begin

if A'length = 0

then return RESULT;

end if;

for i in A'reverse_range loop

if a(i)='1'

then RESULT:=RESULT+TMP;

end if;

TMP:=TMP*2;

end loop;

return RESULT;

end;

    1. Automat o skończonej liczbie stanów

Automat o skończonej liczbie stanów( FSM - ang. Finite State Machine) składa się ze skończonej, określonej liczby stanów oraz operacji definiujących przejścia miedzy stanami. Ogólna struktura maszyny stanowej przedstawiona jest na rysunku poniżej:

Rys.4.1. Struktura automatu FSM.

Zdefiniujmy FSM posiadającą cztery stany typu definiowanego przez użytkownika:

architecture RTL of FSM is

type T_STATE is (IDLE, RW_CYCLE, INT_CYCLE, DMA_CYCLE);

signal NEXT_STATE, STATE : T_STATE;

begin

...

Zdefiniowane są trzy sygnały wejściowe: RW,INT_REQ oraz DMA_REQ. Ogólny format maszyny FSM wygląda w sposób następujący:

NS: process(STATE,RW,INT_REQ,DMA_REQ)

begin

case STATE is

when IDLE =>

-- statements

when RW_CYCLE =>

-- statements

when INT_CYCLE =>

-- statements

when DMA_CYCLE =>

-- statements

end case;

end process NS;

Szczegółowa implementacja może posiadać postać:

NS: process(STATE,RW,INT_REQ,DMA_REQ)

begin

NEXT_STATE <= STATE;

case STATE is

when IDLE =>

if (INT_REQ = '1') then

NEXT_STATE <= INT_CYCLE;

elsif (DMA_REQ = '1') then

NEXT_STATE <= DMA_CYCLE;

end if;

when RW_CYCLE =>

-- statements

...

end process NS;

Proces NS jest czuły na wejścia oraz sygnał stanu. Każda gałąź case definiuje nową wartość stanu.

Implementacja rejestru stanu może przybrać postać:

REG : process (CLK, RST)

begin

if RST = '1' then

STATE <= IDLE;

elsif CLK'event and CLK ='1' then

STATE <= NEXT_STATE;

end if;

end process REG;

    1. Implementacja układów z rodziny TTL

Poniżej przedstawiono dwa przykłady realizacji w języku VHDL układów rodziny TTL. Zwykle każda implementacja języka zawiera odpowiednią bibliotekę definiującą funkcje najbardziej popularnych układów scalonych z rodzin TTL oraz CMOS.

      1. Implementacja układu 7400

LIBRARY ieee;

USE ieee.std_logic_1164.all;

USE work.orcad_prims.all;

ENTITY \74S00\ IS PORT(

I0_A : IN std_logic;

I0_B : IN std_logic;

I0_C : IN std_logic;

I0_D : IN std_logic;

I1_A : IN std_logic;

I1_B : IN std_logic;

I1_C : IN std_logic;

I1_D : IN std_logic;

O_A : OUT std_logic;

O_B : OUT std_logic;

O_C : OUT std_logic;

O_D : OUT std_logic;

VCC : IN std_logic;

GND : IN std_logic);

END \74S00\;

ARCHITECTURE model OF \74S00\ IS

BEGIN

O_A <= NOT ( I0_A AND I1_A ) AFTER 5 ns;

O_B <= NOT ( I0_B AND I1_B ) AFTER 5 ns;

O_C <= NOT ( I1_C AND I0_C ) AFTER 5 ns;

O_D <= NOT ( I1_D AND I0_D ) AFTER 5 ns;

END model;

      1. Implementacja układu 74138

LIBRARY ieee;

USE ieee.std_logic_1164.all;

USE work.orcad_prims.all;

ENTITY \74S138\ IS PORT(

A : IN std_logic;

B : IN std_logic;

C : IN std_logic;

G1 : IN std_logic;

G2A : IN std_logic;

G2B : IN std_logic;

Y0 : OUT std_logic;

Y1 : OUT std_logic;

Y2 : OUT std_logic;

Y3 : OUT std_logic;

Y4 : OUT std_logic;

Y5 : OUT std_logic;

Y6 : OUT std_logic;

Y7 : OUT std_logic;

VCC : IN std_logic;

GND : IN std_logic);

END \74S138\;

ARCHITECTURE model OF \74S138\ IS

SIGNAL L1 : std_logic;

SIGNAL N1 : std_logic;

SIGNAL N2 : std_logic;

SIGNAL N3 : std_logic;

SIGNAL N4 : std_logic;

SIGNAL N5 : std_logic;

SIGNAL N6 : std_logic;

SIGNAL N7 : std_logic;

SIGNAL N8 : std_logic;

BEGIN

N1 <= ( A ) AFTER 7 ns;

N2 <= ( B ) AFTER 7 ns;

N3 <= ( C ) AFTER 7 ns;

N4 <= NOT ( A ) AFTER 6 ns;

N5 <= NOT ( B ) AFTER 6 ns;

N6 <= NOT ( C ) AFTER 6 ns;

N7 <= ( G1 ) AFTER 6 ns;

N8 <= NOT ( G2A OR G2B ) AFTER 6 ns;

L1 <= ( N7 AND N8 );

Y0 <= NOT ( N4 AND N5 AND N6 AND L1 ) AFTER 5 ns;

Y1 <= NOT ( N1 AND N5 AND N6 AND L1 ) AFTER 5 ns;

Y2 <= NOT ( N4 AND N2 AND N6 AND L1 ) AFTER 5 ns;

Y3 <= NOT ( N1 AND N2 AND N6 AND L1 ) AFTER 5 ns;

Y4 <= NOT ( N4 AND N5 AND N3 AND L1 ) AFTER 5 ns;

Y5 <= NOT ( N1 AND N5 AND N3 AND L1 ) AFTER 5 ns;

Y6 <= NOT ( N4 AND N2 AND N3 AND L1 ) AFTER 5 ns;

Y7 <= NOT ( N1 AND N2 AND N3 AND L1 ) AFTER 5 ns;

END model;

  1. Wykaz funkcji języka VHDL

    1. Atrybuty

Zmienne, funkcje, typy, sygnały oraz stałe posiadają związane z nimi atrybuty. Dodatkowo programista ma możliwość definiowania własnych nowych atrybutów. Typy predefiniowane przedstawiono w poniższej tabeli.

Atrybut

Typ wyniku

Wynik

Atrybuty typów

T'Base

typ basowy typu T; wówczas gdy typ T jest podtypem

Atrybuty skalarów

T'Left

jak T

najbardziej lewostronna wartość T

T'Right

jak T

najbardziej prawostronna wartość T

T'Low

jak T

najmniejsza wartość T

T'Right

jak T

największa wartość T

T'Ascending

boolean

true jeżeli T jest uporządkowane rosnąco; jeżeli nie to false

T'Image(x)

string

tekstowa reprezentacja wartości x typu T

T'Value(s)

typ bazowy T

wartość w T reprezentowana przez string s

Atrybuty dyskretnych lub fizycznych typów lub podtypów

T'Pos(s)

integer

numer pozycji występowania s w T

T'Val(x)

typ bazowy T

wartość na pozycji x w T

T'Succ(s)

typ bazowy T

wartość na pozycji o jeden większej niż s w T

T'Pred(s)

typ bazowy T

wartość na pozycji o jeden mniejszej niż s w T

T'Leftof(s)

typ bazowy T

wartość na pozycji o jeden w lewo niż s w T

T'Rightof(s)

typ bazowy T

wartość na pozycji o jeden w prawo niż s w T

Atrybuty tablic

A'Left(n)

lewy zakres indeksu o numerze n

A'Right(n)

prawy zakres indeksu o numerze n

A'Low(n)

dolna granica indeksu o numerze n

A'High(n)

górna granica indeksu o numerze n

A'Range(n)

zakres indeksu o numerze n

A'Reverse_range(n)

odwrócony zakres indeksu o numerze n

A'Length(n)

liczba wartości w indeksie o numerze n

A'Ascending(n)

boolean

thue jeżeli wartości o numerze n nawastają; false jeżeli opadają

Atrybuty sygnałów

S'Delayed(t)

równy sygnałowi S ale opóźniony o t

S'Stable(t)

boolean

true jeżeli sygnał nie zmienił się przez okres czasu t; w przeciwnym przypadku false

S'Quiet(t)

boolean

true jeżeli sygnał nie miał transakcji przez okres czasu t; w przeciwnym przypadku false

S'Transaction

boolean

wartość zmieniająca się w każdym cyklu symulacji w którym nastąpiła transakcja na sygnale S

S'Event

boolean

true jeżeli sygnał się zmienił; w przeciwnym przypadku false

S'Active

true jeżeli sygnał miał transakcję w aktualnym cyklu symulacji; w przeciwnym przypadku false

S'Last_event

czas od ostatniego zdarzenia, które wystąpiło na sygnale S

S'Last_active

czas od ostatniej transakcji, która wystąpiła na sygnale S

S'Last_value

wartość sygnału S poprzedzająca ostatnie zdarzenie

S'Driving

true jeżeli proces steruje sygnałem S; false w przeciwnym przypadku

S'Driving_value

aktualna wartość dla sterownika związanego z sygnałem S

Atrybuty nazw

E'Simple_name

string

string pojawiający się w deklaracji E

E'Path_name

string

string poprzez hierarchiczną ścieżkę opisujący E

    1. Definicja tablic

Tablica jest zmienną złożoną z elementów tej samej wartości. Do elementów tablicy dostajemy się poprzez podanie indeksu (dla tablic jednowymiarowych) lub indeksów (dla tablic wielowymiarowych).

type BYTE is array ( 0 to 7 ) of BIT; -- definicja typu tablicy

type L4Vect is array ( 1 to 8, 1 to 2 ) of L4; -- dwuwymiarowa tablica

type X is ( LOW, HIGH );

type X1 is array ( 0 to 7, X ) of BIT; -- dwuwymiarowa tablica

    1. Instrukcja case

Instrukcja case służy do wyboru do wykonania jednej z kilku sekwencji instrukcji. Ogólny format instrukcji wygląda w sposób następujący:

case wyrażenie is

when wybów1 => sekwencja 1;

when wybów2 => sekwencja 2;

......

end case;

Każda możliwa wartość wyrażenia musi posiadać odpowiedni jeden i tylko jeden wybór związany z odpowiednim wyborem.

P1:process

variable x: Integer range 1 to 3;

variable y: BIT_VECTOR (0 to 1);

begin

C1: case x is

when 1 => Out_1 <= 0;

when 2 => Out_1 <= 1;

when 3 => Out_1 <= 2;

end case C1;

C2: case y is

when "00" => Out_2 <= 0;

when "01" => Out_2 <= 1;

when "10" => Out_2 <= 2;

when "11" => Out_2 <= 3;

end case C2;

end process;

P2:process

type Codes_Of_Operation is (ADD,SUB,MULT,DIV);

variable Code_Variable: Codes_Of_Operation;

begin

C3: case Code_Variable is

when ADD | SUB => Operation := 0;

when MULT | DIV => Operation := 1;

end case C3;

end process;

P3:process

type Some_Characters is ('a','b','c','d','e');

variable Some_Characters_Variable: Some_Characters;

begin

C4: case Some_Characters_Variable is

when 'a' to 'c' => Operation := 0;

when 'd' to 'e' => Operation := 1;

end case C4;

end process;

P5:process

variable Code_of_Operation : INTEGER range 0 to 2;

constant Variable_1 : INTEGER := 0;

begin

C6: case Code_of_Operation is

when Variable_1 | Variable_1 + 1 =>

Operation := 0;

when Variable_1 + 2 =>

Operation := 1;

end case C6;

end process;

P6:process

type Some_Characters is ('a','b','c','d','e');

variable Code_of_Address : Some_Characters;

begin

C7:case Code_of_Address is

when 'a' | 'c' => Operation := 0;

when others => Operation := 1;

end case C7;

end process;

Sekwencja when others może wystąpić tylko jako ostatnia.

    1. Scalanie wartości (ang. aggregate)

Scalanie wartości oznacza łączenie kilku wartości w rekord lub tablicę. Łączone elementy specyfikuje się w nawiasach rozdzielając przecinkiem. Wyspecyfikowane elementy składają się z określenia pozycji oraz wartości wpisywanej w wyspecyfikowaną pozycję.

Słowo kluczowe other może być tylko ostatnie w specyfikowanej liście. Każda pozycja podczas określania wartości tablicy lub rekordu musi zostać wyspecyfikowana raz i tylko raz.

variable v1 : bit_vector ( 0 to 3 ) := ( '1', '1', '0', '1' ); -- "1011"

variable v2 : bit_vector ( 3 downto 0 ) := ( '1', '1', '0', '1' ); -- "1101"

variable v3 : bit_vector ( 1 to 4 ) := ( 1=>'0', 3=>'0', 4=>'0', 2=>'1' );

-- "0010"

sugnal s1 : std_logic_vector ( 7 downto 0 );

s1 <= ( 7 downto 4 => '1', 3 downto 0 => '0' ); -- "11110000"

s1 <= ( 6 downto 5 => '1', others => 'Z' ); -- "Z11ZZZZZ"

s1 <= ( others => 'Z' ); -- "ZZZZZZZZ"; niezależnie od długości s1

s1 <= ( 6 | 3 => '1', others => '0' ); -- "01001000"

type R1Def is record

f1 : integer;

f2 : std_logic_vector ( 3 downto 0 )

end record;

variable r1 : R1Def := ( f1 => 33, f2 => "0110" );

    1. Typ Bit

Typ bit jest predefiniowany w pakiecie Standard i zawiera dwie wartości: '0' oraz '1'. Typ posiada tylko dwie wartości i nie jest możliwe użycie go np. do reprezentacji stanu wysokiej impedancji. Sygnały typu bit nie są typu resolved i w związku z tym sygnał taki może posiadać tylko jeden sterownik. Wartości typu bit zapisywane są w apostrofach.

signal b1, b2 : bit;

b1 <= '1';

b2 <= not b1;

    1. Typ Bit_Vector

Typ Bit_Vector jest zdefiniowany w pakiecie Standard i reprezentuje jednowymiarową tablicę elementów typu bit. Rozmiar wektora typu Bit_Vector jest specyfikowany podczas definiowania wektora. Wartości typu Bit_Vector zapisywane są w podwójnych apostrofach w odróżnieniu od elementów wektora, które zapisywane są w pojedynczych apostrofach.

signal bv : bit_vector( 7 downto 0 );

signal b : bit;

bv( 0 ) <= '1';

bv <= '0' & "011011" & b;

    1. Typ Boolean

Typ Boolean jest zdefiniowany w pakiecie Standard i posiada dwie wartości: false oraz true. Typowo jest wynikiem wykonania operacji relacyjnych. W odróżnieniu od niektórych klasycznych języków programowania wartości typu boolean nie są identyczne z logicznymi wartościami '0' oraz '1'.

    1. Typ Character

Typ character jest typem wyliczeniowym zawierającym wszystkie akceptowalne znaki. Jego specyfikacja znajduje się w pakiecie Standard.

    1. Pakiet

Pakiet grupuje definicje stałych, typów definiowanych przez użytkownika, deklaracje komponentów (sygnałów i zmiennych) oraz definicje funkcji i procedur (wliczając definicje funkcji operatorów). Celem definicji pakietu jest zdefiniowanie zasobów współużywalnych w celu wykorzystywania ich przez różne projekty. Zwyczajowo pakiety zapisywane są w postaci bibliotek.

Pakiet składa się z deklaracji pakietu oraz z ciała pakietu. Deklaracja pakietu zawiera definicje stałych, typów oraz nagłówki funkcji i procedur. Ciało pakietu zawiera definicje deklarowanych w deklaracji pakietu procedur i funkcji. Definicja pakietu posiada następującą składnię:

-- Deklaracja pakietu

package nazwa_pakietu is

deklaracja_pakietu

end nazwa_pakietu;

-- Definicja ciała pakietu

package body nazwa_pakietu is

definicja_pakietu

end package body nazwa_pakietu;

Jednostki zdefiniowane w pakiecie są dostępne w projekcie po użyciu dyrektywy use.

Standard języka VHDL wymaga obecności co najmniej dwóch pakietów: STANDARD oraz TEXTIO. Pierwszy zawiera definicje typów, stałych i operatorów. Drugi zawiera definicje operacji współpracujących z plikami tekstowymi. Kolejnym często występującym pakietem jest STD_LOGIC_1164 zawierający rozszerzenia języka.

  1. Wykorzystanie programu OrCAD do projektowania bloków w języku VHDL

    1. Kompilacja projektu

Kompilacja projektu wymaga odpowiedniego ustawienia opcji optymalizacji, syntezy oraz formatu plików docelowych. Poniżej zawarto omówienie używanych opcji oraz zalecane ustawienia dla układów FPGA firmy XILINX.

      1. Opcje optymalizacji

Optymalizacja jest procesem manipulowania projektem w celu polepszenia parametrów implementacji. Główną opcją optymalizacji jest balansowanie między liczbą komórek użytych do implementacji, a maksymalną prędkością pracy projektu.

0x01 graphic

Opcje optymalizacji posiadają następujące pola:

Optimize For / Area - przeprowadza minimalizację liczby użytych bramek,

Optimize For / Area - celem optymalizacji jest minimalizacja czasu propagacji sygnału od wejścia do wyjścia,

Effort - określa nakład pracy procedur optymalizujących. Duży nakład oznacza dłuższy czas obliczeń, ale powoduje lepsze wyniki optymalizacji,

Retiming - włacza opcję "retiming" podczas optymalizacji. Zwykle przerzutniki "latch" umieszczane są na wejściu i wyjściu bloków logiki kombinacyjnej. Włączenie opcji próbuje zmienić liczbę i pozycję przerzutników w celu znalezienia bardziej efektywnej implementacji,

Resource sharing - włącza opcję współdzielenia zasobów. Umożliwia współdzielenie komponentów (jako przeciwieństwo dla ich duplikowania) w warunkach gdy ich użycie w czasie wzajemnie się wyklucza,

Resynthesis - specyfikuje możliwość przeprowadzenia innej syntezy dla fragmentów projektu podczas optymalizacji w celu poprawienia parametrów implementacji,

Boolean Optimization - włącza opcję redukcji Boolean w celu poprawienia implementacji,

Constant Propagetion- włącza opcję propagacji stałych przez logikę. Np. A OR '1' daje zawsze wartość '1' więc bramka OR jest eliminowana.

      1. Opcje syntezy

Opcje syntezy zarządzają sposobem implementacji maszyn stanowych (FSM).

0x01 graphic

Sposób kodowania stanów może przyjąć jedną z czterech wartości:

One Hot - dokładnie jeden bit jest aktywny dla każdego stanu. Każdy stan ma przypisany jeden przerzutnik aktywny wówczas gdy maszyna FSM jest w tym stanie. Tylko jeden z takich przerzutników może być aktywny w każdej chwili czasu. Jest to metoda najszybsza ale i najbardziej rozrzutna,

Binary - kolejne kombinacje bitów przypisane są do kolejnych stanów. Stany maszyny FSM kodowane są w kodzie binarnym,

Compact - kolejne kombinacje binarne przypisywane są do stanów ale nie kolejno lecz w sposób zapewniający optymalną pracę (w kontekście optymalizacji prędkości lub rozmiaru),

Gray - stany zmieniają się zgodnie z kodem Gray-a, a więc przejścia między stanami powodują zmianę tylko jednego bitu kodu stanu. Taka implementacja eliminuje ryzyko statycznych hazardów.

      1. Opcje formatu docelowego

Zakładka umożliwia ustawienie opcji docelowego formatu plików generowanych w trakcie kompilacji.

0x01 graphic

Dostępne są następujące opcje:

Netlist Format - specyfikuje format listy połączeń generowanej w trakcie kompilacji. Można wybrać między EDIF 2 0 0, VHDL oraz XNF. Domyślnie wybierany jest format zalecany przez dostawcę wyspecyfikowanego podczas tworzenia projektu.

Add I/Os - powoduje, że bloki I/O zostaną dodane automatycznie do sygnałów wejściowych i wyjściowych. Jeżeli bloki takie zostały dodane ręcznie podczas rysowania schematu opcja ta musi być wyłączona. Jeżeli wejścia i wyjścia nie są rysowane jako fragment schematu lecz są opisane w postaci programu VHDL wówczas opcja ta musi być włączona ponieważ w języku VHDL nie ma możliwości opisu bloków I/O,

Use Complex Macrocells -powoduje wykorzystanie wszelakich makrokomórek dostępnych w bibliotece. Przykładowo, jeżeli logika zawiera sumator wówczas włączenie opcji powoduje, że wykorzystywany jest blok sumatora dostarczony przez producenta docelowego sprzętu zamiast tworzenia sumatora z elementarnych komórek logicznych,

Fanout Limit -określa maksymalną liczbę urządzeń, które mogą być sterowane przez pojedyncze wyjście bez konieczności buforowania. Duża wartość parametru daje w efekcie mniejszy projekt, mała liczba generuje projekt szybszy.

    1. Opcje tworzenia pliku programującego układy FPGA

Implementacja języka VHDL wbudowana w program ORCAD zawiera istotne różnice w stosunku do standardu języka (IEEE Standard VHDL Language Reference Manual (IEEE Std. 1076-1993)). Poniższa tabela przedstawia zestawienie konstrukcji języka wbudowanych oraz nie wbudowanych w program ORCAD. Znak √ oznacza, iż dana cecha jest dostępna odpowiednio podczas symulacji lub kompilacji. Tylko dostępność podczas kompilacji umożliwia wykorzystanie w trakcie implementowania w programowalnych układach logicznych.

Konstrukcja VHDL

Symulacja

Kompilacja

Komentarz

Bloki entities; configuration

Deklaracje entity

Nagłówki entity

Generics

Porty

Tryby BUFFER oraz LINKAGE nie występują (traktowane są jako INOUT)

Ciała architecture

Deklaracje architecture

Deklaracje configuration

Można wybrać określony entity oraz architecture lub zaimportować cały pakiet.

Konfiguracja bloku

Nie zaimplementowane

Konfiguracja komponentu

Nie zaimplementowane

Podprogramy, pakiety

Deklaracje podprogramów

Podprogramu rekurencyjne nie są dozwolone

Parametry formalne

Stałe i zmienne jako parametry

Sygnały jako parametry

Pliki jako parametry

Nie zaimplementowane

Ciała podprogramów

Przeładowanie podprogramów

Nie zaimplementowane

Przełądowanie operatorów

Sygnatury

Nie zaimplementowane

Funkcje resolution

Nie zaimplementowane; w trakcie symulacji używana jest operacja typu WIRED-OR w celu określenia wartości sygnału

Deklaracja pakietu

Nie zaimplementowane użycie globalnych sygnałów w listach asocjacyjnych portów

Ciała pakietów

Funkcje i procedury

Nie zaimplementowane funkcje rekurencyjne

Typy

Typy skalarne

Typy wyliczeniowe

Predefiniowane typy wyliczeniowe

Typy integer

Predefiniowane typy integer

Typy jednostek fizycznych

Nie zaimplementowane

Predefiniowane typy jednostek fizycznych

Jedynie TIME zawierający fs, ps, ns, us oraz ms

Typy floating

Nie zaimplementowane

Predefiniowane typy floating

Typy złożone (rekordy)

Tablice

Tablice nie mogą posiadać więcej niż dwa wymiary. Podczas kompilacji:

tablica może być tylko jednowymiarowa,

elementy tablicy mogą być tylko typu bit, Boolean oraz std_logic

Ograniczone zakresy indeksów

Predefiniowane typy tablicowe

Typy rekordowe

Nie zaimplementowane dla kompilacji

Alokacja i zwalnianie obiektów

Nie zaimplementowane

Typ file

Nie zaimplementowane dla kompilacji

Operacje na plikach

Podczas symulacji parametr FILE_OPEN_STATUS procedury FILE_OPEN jest niedostępny; nie zaimplementowane dla kompilacji

Deklaracje

Deklaracje typów

Deklaracje typów okrojonych

Nazwy resolution function są ignorowane w deklaracjach podtypów

Obiekty

Deklaracje obiektów

Deklaracje stałych

Wartość stałej musi być lokalnym wyrażeniem statycznym

Deklaracje sygnałów

Typ sygnału musi być typem wyliczeniowym, typem integer lub jednowymiarową tablicą elementów typu wyliczeniowego; sygnały typu guarded (REGISTER lub BUS) nie są dozwolone

Deklaracje zmiennych

Deklaracje zmiennych plikowych

Deklaracje interfejsu

Listy asocjacyjne

Konwersja typów nie jest dozwolona

Deklaracje alias-ów

alias_designator nie może być literałem oraz symbolem operatora; subtype_indication nie jest dozwolony; specyfikacja sygnatury nie jest dozwolona

Aliasy obiektów

Aliasy nie-obiektów

Nie zaimplementowane

Deklaracje atrybutów

Deklaracje komponentów

Deklaracje szablomów grup

Nie zaimplementowane

Deklaracje grup

Nie zaimplementowane

Specyfikacje

Specyfikacje atrybutów

Specyfikacje konfiguracji

Częściowo zaimplementowane. Następujące przypadki nie są dozwolone:

use library.all

use library.package.object

Binding indication

Entity aspect

Generic map; port map aspects

Default binding indication

Disconnection specyfication

Nie zaimplementowane

Nazwy

Nazwy

Prefix będący nazwą funkcji nie jest dozwolony

Proste nazwy

Wybiórcze nazwy

Nazwy indeksowane

Prefiksem nazwy indeksowanej musi być inna nazwa indeksowana lub nazwa prosta; podczas kompilacji indeks tablicy MUSI BYĆ STAŁĄ

Slice names

Nazwy atrybutów

Sygnatury nie są dozwolone; sprawdzanie błędów jest limitowane; nazwy tablic nie mogą być prefiksami

Wyrażenia

Wyrażenia

Operatory

Operatory logiczne

Operatory relacyjne

Dla typów wektorowych dozwolone tylko EQ oraz NEQ

Operatory przesuwania

Podczas kompilacji licznik przesuwania musi być stały

Operatory dodawania

Ograniczona implementacja użycia wyrażeń jako operatorów konkatenacji

Operatory znaków

Operatory mnożenia

Inne operatory

Podczas kompilacji operator potengowania (**) nie jest dozwolony

Operandy

Literały

Agregaty

discrete_range jako wybów podczas stoważyszania elementów nie jest dozwolony

Agregaty rekordów

asocjacja za pomocą nazwy nie jest dozwolona; podczas kompilacji nie jest dozwolone

Agregacje tablic

Wywołanie funkcji

Rekurencja nie jest dozwolona

Qualified expressions

Konwersje typów

Konwersja integer do real nie jest dozwolona;

Allocators

Nie zaimplementowane

Wyrażenia statyczne

Locally static primaries

Globally static primaries

Uniwersalne wyrażenia

Wyrażenia sekwencyjne

Wyrażenia wait

Nie można jawnie specyfikować czasu oczekiwania; dla kompilacji czyste instrukcje wait oraz wait for nie są dozwolone

Przypisanie sygnałów

Wyrażenie reject nie jest dozwolone; przypisania do zagregowanych zmiennych nie są dozwolone; dla kompilacji opóźnienia podczas przypisania są dozwolone ale ignorowane

Przypisanie zmiennych

przypisania do zagregowanych zmiennych nie są dozwolone

Wywoływanie procedur

Rekurencja nie jest dozwolona

Wyrażenia case

Wyrażenia tablicowe nie są w pełni dozwolone

Pętle

Kompilacja: dla pętli for tylko stałe zakresy są dozwolone

Wyrażenia next oraz exit

Kompilacja: mogą występować tylko w prostych pętlach for

Wyrażenia wykonywane równolegle

Wywoływanie procedur

Rekurencja nie jest dozwolona

Warunkowe przypisanie sygnałów

Zakres widoczności zmiennych i sygnałów

Rejony deklaracji, zakres deklaracji, widoczność zmiennych i sygnałów

Wyrażenie use

Dozwolone tylko na początku bloku entity

Jednostki projektu i ich analiza

Jednostki projektu, biblioteki, wyrażenia context

Analiza projektu

Bardziej restrykcyjna; oprogramowanie wybiera poprawny sposób oraz kolejność analizy projektu

Współpraca oraz wykonanie

Współpraca hierarchii schematu

Wyrażenia blokowe nie są dozwolone

Współpraca nagłówków bloków

Wyrażenia blokowe nie są dozwolone

Wyrażenia generic oraz port w nagłówkach bloków

Nie są dozwolone

Deklaracje podprogramów oraz ich ciała

Podprogramy są włączane w ciało projektu (in-lined); rekurencja nie jest dozwolona

Deklaracje typów, podtypów, atrybutów oraz komponentów

Wyrażenia blokowe

Nie są dozwolone

Elementy leksykalne

Zbiory znaków, separatory, delimitery, identyfikatory, łańcuchy znaków, łańcuchy bitów

Środowisko języka

Atrybuty

Kompilacja: atrybuty: delayed, last_event, last_value, succ oraz pred nie są dozwolone; atrybut stable częściowo dozwolony; atrybuty quiet, transaction oraz active nie są dozwolone podczas symulacji

Pakiet STANDARD

Nie jest dostępny pełny zestaw jednostek czasu; podczas kompilacji funkcja now nie jest dostepna

Pakiet TEXTIO

Funkcja read niedostepna dla typów time oraz real; W funkcji write parametr unit niedostepny dla typu time

Biblioteki IEEE oraz VITAL

Typy standardowe

Dla kompilacji: typy "-", "U", "X" oraz "W" są zamieniane na "0"

  1. Przykłady

Poniżej przedstawiono przykłady realizacji projektów układów logicznych za pomocą programu ORCAD z przeznaczeniem do zaprojektowania układów logicznych firmy XILINX. Działanie wszystkich projektów zostało praktycznie sprawdzone na układach 4003 oraz 4008. Wykorzystano układy elektroniczne oraz peryferia znajdujące się na XILINX FPGA Demo Board. W szczególności wykorzystano znajdujące się na płytce przełączniki do zadawania wartości sygnałów, zestaw 16 diod LED oraz dwa siedmiosegmentowe wyświetlacze LED do wyświetlania informacji.

Przepływ informacji między programowalnym układem logicznym a jego zewnętrzem odbywa się za pomocą wyprowadzeń układu. To, które z wyprowadzeń jest wejściem lub wyjściem oraz jaki sygnał zewnętrzny jest z nim stowarzyszony określane jest za pomocą portów wejściowych oraz wyjściowych wraz z wejściowymi oraz wyjściowymi buforami. Sposób doprowadzania sygnału do układu przedstawia Rys.7.1. Wyprowadzanie sygnału z układu przedstawiono na Rys.7.2.

Rys.7.1. Doprowadzanie sygnału do układu.

Rys.7.2. Wyprowadzanie sygnału z układu.

Przedstawiony na Rys.7.1 sposób doprowadzenia sygnału zewnętrznego do układu programowalnego składa się z portu wejściowego SW3_8 oraz bufora wejściowego IBUF. Wyjście z bufora stanowi wejście dla logiki wewnątrz programowanego układu. Numer wyprowadzenia programowalnego układu logicznego, z którym związany jest dany sygnał określony jest poprzez atrybut linii między portem a buforem wejściowym. Atrybut ten nosi nazwę LOC i jego wartość jest w postaci Pxx, gdzie xx jest numerem wyprowadzenia układu scalonego. Oznaczenie P28 wiąże sygnał z 28 wyprowadzeniem układu.

Wyprowadzanie sygnałów z układu na zewnątrz realizowane jest w analogiczny sposób. Sygnał z wnętrza układu podawany jest na wejście bufora wyjściowego OBUF, a następnie na wejście portu wyjściowego. Wartość atrybutu LOC określa numer wyprowadzenia układu scalonego związanego z danym sygnałem wyjściowym.

Programy w języku VHDL stanowią fragmenty bloków odpowiednich hierarchicznych. Takie bloki biorą udział zarówno w symulacji pracy układu jak i podczas syntezy ciągu bitowego programującego programowalny układ logiczny.

Występujące na schematach bloki READBACK są opcjonalne i ich przeznaczeniem jest weryfikacja ciągu bitowego programującego układ.

    1. Konwerter liczby binarnej na kod BCD. Wyświetlenie liczby BCD na wyświetlaczu 7-segmentowym

Jedną z typowych aplikacji programowalnych układów logicznych jest wykorzystanie ich w roli układów sterujących miernikami cyfrowymi. W takiej roli mogą stanowić "serce" woltomierza lub częstotliwościomierza. W takich zastosowaniach użyteczną rolę spełniają konwertery kodu binarnego na kod wyświetlaczy 7-segmentowych. Pośredniczą one między wewnętrznymi funkcjami miernika, pracującymi zwykle w kodzie binarnym, a wyjściem z układu realizowanym wielokrotnie za pomocą wyświetlania informacji na wyświetlaczach 7-segmentowych LED.

Przedstawiony na Rys.7.3 schemat realizuje konwersję kodu binarnego na kod wyświetlacza 7-segmentowego. Konwersja realizowana jest przez hierarchiczny blok VHDL_Blk. Ciało programu w języku VHDL związanego z tym blokiem przedstawiono w ramce poniżej. Liczba binarna doprowadzana jest do układu za pomocą zestawu przełączników dołączonych do wejść układu o numerach 19, 20, 23, 24, 25, 26 oraz 27. Stany przełączników potwierdzane są na diodach dołączonych do wyjść układu o numerach 59, 58, 57, 66, 65, 62 oraz 61. Zdekodowana liczba wyświetlana jest na dwóch wyświetlaczach 7-segmentowych dołączonych do wyjść 39, 38, 36, 35, 29, 40 i 44 oraz 49, 48, 47, 46, 45, 50 i 51.

W celu wykonania konwersji zdefiniowano proces, na którego liście inicjalizacyjnej znajduje się wejściowa liczba binarna:

process( Bin ) -- bin-2-BCD converter

Proces ten wykonywany jest tylko wówczas gdy wejściowa zmienna Bin ulegnie zmianie. Przekodowanie liczby z kodu binarnego na kod wyświetlacza odbywa się w dwóch etapach. Wpierw liczba binarna zamieniana jest na liczbę w kodzie BCD. Zamiana wykonywana jest na zasadzie testowania kolejnych bitów liczby binarnej (poczynając od najmłodszego) i dodawaniu do wyniku odpowiednich wag. Następnie dla dwóch cyfr liczby w kodzie BCD wywoływana jest funkcja wyliczająca wzór danej cyfry, który wyświetlany jest na wyświetlaczu.

W celu wyświetlenia zdekodowanej liczby zdefiniowano oddzielny proces wzbudzany sygnałem digit:

process( digit ) -- BCD-2-7 segment LED converter

begin

LdLED <= BCD2LED( digit( 3 downto 0 ) );

UpLED <= BCD2LED( digit( 7 downto 4 ) );

end process;

Sygnał digit ustawiany jest przez proces wykonujący konwersje.


Rys.7.3. Schemat logiczny konwertera kodu binarnego na kod wyświetlacza 7-sermentowego.


Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY VHDL_Blk is

PORT (

Bin : IN STD_LOGIC_VECTOR (6 DOWNTO 0);

UpLED : OUT STD_LOGIC_VECTOR (6 DOWNTO 0);

LdLED : OUT STD_LOGIC_VECTOR (6 DOWNTO 0));

END VHDL_Blk;

ARCHITECTURE behavior OF VHDL_Blk IS

shared VARIABLE TMP : std_logic_vector ( 7 downto 0 );

shared VARIABLE TMP1 : std_logic_vector ( 3 downto 0 );

SIGNAL digit : std_logic_vector ( 7 downto 0 );

--

-- Konwersja liczby w kodzie BCD na kod wyswietlacza 7-segmentowego

-- zrealizowana w postaci funkcji

--

FUNCTION BCD2LED( BCD : std_logic_vector( 3 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 6 downto 0 );

begin

if BCD = "0000" then TMP := "1000000"; -- 0

elsif BCD = "0001" then TMP := "1111001"; -- 1

elsif BCD = "0010" then TMP := "0100100"; -- 2

elsif BCD = "0011" then TMP := "0110000"; -- 3

elsif BCD = "0100" then TMP := "0011001"; -- 4

elsif BCD = "0101" then TMP := "0010010"; -- 5

elsif BCD = "0110" then TMP := "0000010"; -- 6

elsif BCD = "0111" then TMP := "1111000"; -- 7

elsif BCD = "1000" then TMP := "0000000"; -- 8

elsif BCD = "1001" then TMP := "0010000"; -- 9

else

TMP := "0111111";

end if;

return TMP;

end BCD2LED;

BEGIN

process( Bin ) -- bin-2-BCD converter

begin

TMP := ( '0' & Bin ) and "00001111";

if TMP > "00001001" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

if Bin(4) = '1' then

TMP := std_logic_vector( unsigned(TMP) + x"16" );

if TMP(3 downto 0) > "1001" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

end if;

if Bin(5) = '1' then

TMP := std_logic_vector( unsigned(TMP) + x"32" );

if TMP(3 downto 0) > "1001" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

end if;

if Bin(6) = '1' then

TMP := std_logic_vector( unsigned(TMP) + x"64" );

if TMP(3 downto 0) > x"9" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

if TMP(7 downto 4) > x"9" then

TMP := std_logic_vector( unsigned(TMP) + x"60" );

end if;

end if;

digit <= TMP;

end process;

process( digit ) -- BCD-2-7 segment LED converter

begin

LdLED <= BCD2LED( digit( 3 downto 0 ) );

UpLED <= BCD2LED( digit( 7 downto 4 ) );

end process;

END behavior;

    1. Częstotliwościomierz

Zasada działania miernika częstotliwości polega na zliczaniu liczby impulsów w określonej jednostce czasu. W przypadku sygnałów cyfrowych zlicza się liczbę narastających lub opadających zboczy sygnału badanego. Jeżeli przykładowo czas zliczania wynosi 1 milisekundę wówczas zliczona liczba impulsów podaje częstotliwość badanego sygnału wyrażoną w [kHz].

Rysunek 7.4 przedstawia schemat częstotliwościomierza wykonany w programie ORCAD w formacie umożliwiającym za jego pomocą zaprogramowanie programowalnego układu logicznego. Mierzony sygnał dołączony jest do portu SW3_1. Wynik pracy częstotliwościomierza wyświetlany jest na dwóch 7-segmentowych wyświetlaczach LED (patrz poprzedni przykład).

Na schemacie zawarte są dwa bloki opisane w języku VHDL: VHDL_Div oraz VHDL_Blk. Wejściem dla pierwszego bloku jest wyjście wbudowanego w programowalny układ oscylatora o częstotliwości 8MHz. Blok VHDL_Div dzieli częstotliwość wejściową w celu uzyskania sygnału taktującego zliczanie liczby impulsów w bloku VHDL_Blk (patrz ciało ARCHITECTURE behavior OF VHDL_Div IS w ramce poniżej). Zależnie od częstotliwości sygnału mierzonego częstotliwość sygnału wyjściowego z bloku VHDL_Div musi ulegać zmianie tak aby w trakcie jednego zliczenia zarejestrować od 1 do maksymalnie 99 impulsów (tylko taki zakres można wyświetlić na dwóch polach wyświetlacza).

Blok VHDL_Blk zlicza liczbę narastających zboczy sygnału mierzonego. Realizowane jest to dzięki instrukcji (patrz ciało ARCHITECTURE behavior OF VHDL_Blk IS poniżej):

elsif ( InpSignal = '1' and InpSignal'Event ) then

Zliczanie odbywa się w kodzie BCD. Jednocześnie jeżeli sygnał Reset osiągnie wartość zero wówczas licznik przyjmuje wartość zerową:

if Reset ='0' then

count <= "00000000";

W celu wyświetlenia zliczonej wartości zdefiniowano proces, na którego liście inicjalizacyjnej znajduje się sygnał Reset:

process( Reset )

begin

if Reset = '0' and Reset'Event then

LdLED <= BCD2LED( count( 3 downto 0 ) );

UpLED <= BCD2LED( count( 7 downto 4 ) );

end if;

end process;

Pojawienie się opadającego zbocza sygnału Reset powoduje podstawienie za zmienne LdLED oraz UpLED wzorców obu cyfr zliczonej liczby. Stanowią one wyjście z bloku VHDL_Blk i są bezpośrednio połączone z 7-segmentowymi wyświetlaczami LED.


Rys.7.4. Schemat ideowy częstotliwościomierza.


Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY VHDL_Div is

PORT (

InpClk : IN STD_LOGIC;

OutClk : OUT STD_LOGIC);

END VHDL_Div;

ARCHITECTURE behavior OF VHDL_Div IS

SIGNAL TMPCLK : std_logic_vector ( 7 downto 0 ) := x"00";

shared VARIABLE TMP : std_logic_vector ( 7 downto 0 );

BEGIN

process

begin

wait until InpClk = '1' and InpClk'Event;

TMP := TMPCLK;

TMP := std_logic_vector( unsigned(TMP) + 1 );

if TMP > "01000110" then

OutClk <= '0';

TMP := 0;

else

OutClk <= '1';

end if;

TMPCLK <= TMP;

end process;

END behavior;

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY VHDL_Blk is

PORT (

InpSignal : IN STD_LOGIC;

Reset : IN STD_LOGIC;

UpLED : OUT STD_LOGIC_VECTOR (6 DOWNTO 0);

LdLED : OUT STD_LOGIC_VECTOR (6 DOWNTO 0));

END VHDL_Blk;

ARCHITECTURE behavior OF VHDL_Blk IS

SIGNAL TMPCLK : std_logic_vector ( 3 downto 0 ) := "1110";

SIGNAL count : std_logic_vector ( 7 downto 0 ) := x"00";

SIGNAL TMPRES : std_logic := '0';

shared VARIABLE TMP : std_logic_vector ( 3 downto 0 );

shared VARIABLE TMP8 : std_logic_vector ( 7 downto 0 );

FUNCTION BCD2LED( BCD : std_logic_vector( 3 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 6 downto 0 );

begin

if BCD = "0000" then TMP := "1000000"; -- 0

elsif BCD = "0001" then TMP := "1111001"; -- 1

elsif BCD = "0010" then TMP := "0100100"; -- 2

elsif BCD = "0011" then TMP := "0110000"; -- 3

elsif BCD = "0100" then TMP := "0011001"; -- 4

elsif BCD = "0101" then TMP := "0010010"; -- 5

elsif BCD = "0110" then TMP := "0000010"; -- 6

elsif BCD = "0111" then TMP := "1111000"; -- 7

elsif BCD = "1000" then TMP := "0000000"; -- 8

elsif BCD = "1001" then TMP := "0010000"; -- 9

else

TMP := "0111111";

end if;

return TMP;

end BCD2LED;

BEGIN

process( Reset )

begin

if Reset = '0' and Reset'Event then

LdLED <= BCD2LED( count( 3 downto 0 ) );

UpLED <= BCD2LED( count( 7 downto 4 ) );

-- TMPRES <= '1';

end if;

end process;

process( Reset, InpSignal, count ) -- ( Clk , InpSignal, count )

begin

if Reset ='0' then -- and Reset'Event then

count <= "00000000";

elsif ( InpSignal = '1' and InpSignal'Event ) then

TMP8 := count;

TMP8 := std_logic_vector( unsigned(TMP8) + 1 );

if TMP8( 3 downto 0 ) > "1001" then -- decimal adjustment of the lower digit

TMP8 := std_logic_vector( unsigned(TMP8) + 6 );

end if;

if TMP8( 7 downto 4 ) > "1001" then -- decimal adjustment of the upper digit

TMP8 := std_logic_vector( unsigned(TMP8) + x"60" );

end if;

count <= TMP8;

end if;

end process;

    1. Generator numerów górników

Szczegóły pracy systemu kontroli przemieszczania się górników w chodnikach pod ziemią przedstawiono w rozdziale 3.2. Poniżej przedstawiono projekt nadajnika oraz odbiornika (detektora) numerów, w których główne bloki funkcjonalne zostały opisane w języku VHDL w sposób behawioralny.

Na schemacie widoczne są cztery bloki hierarchiczne opisane w języku VHDL: Divider, GenNum, ReadNum oraz VHDL_Blk.

Blok Divider (patrz pierwsza ramka poniżej) odpowiada za podział częstotliwości z wbudowanego generatora w celu uzyskania częstotliwości taktującej prace bloków GenNum oraz ReadNum. Zawarto w nim dwa procesy, z których pierwszy odpowiada za podział częstotliwości wejściowej przez 16, a drugi ustala współczynnik wypełnienia sygnału wyjściowego.

Blok GenNum powoduje wygenerowanie numerów w odpowiednim formacie dla wszystkich kombinacji 13-bitowych numerów. Pobudzany jest narastającym zboczem sygnału wyjściowego z bloku Divider. Posiada wbudowany licznik tmp_clk, którego różne wartości powodują wysłanie na wyjście Numer kolejnych fragmentów generowanego sygnału. Dodatkowy 13-bitowy licznik nbr zawiera aktualnie wysyłany numer górnika.

Blok ReadNum odbiera wysyłany przez blok GenNum numer i wpisuje go ro rejestru szeregowego. Realizowane jest to przez wydzielony proces:

process ( In_clk, In_num )

begin

if ( In_clk'Event and In_clk = '0') then

dtk_nbr <= dtk_nbr( 14 downto 0 ) & In_num;

end if;

end process;

Następnie numer przypisany do sygnału dtk_nbr z sekwencją "01111110" oraz z sekwencją 7 bitów zadaną przez dołączone go układu przełączniki (wejście DIP bloku ReadNum):

process ( dtk_nbr, DIP )

begin

if dtk_nbr = "01111110" & DIP( 6 downto 0 ) & '0' then

aux <= '1';

Jeżeli numer zostanie wykryty ustawiana jest zmienna aux, która w kolejnym procesie powoduje ustawienie sygnału wyjściowego Num_OK.

Blok VHDL_Blk używany jest do wyświetlenia na dwóch polach wyświetlacza 7-segmentowego zadawanego za pomocą przełączników fragmentu numeru. Ułatwia to weryfikację poprawności pracy układu.

Warto zauważyć, że w celu ułatwienia testowania pracy układu zarówno nadajnik jak i odbiornik zaprojektowano w pojedynczym układzie FPGA. W rzeczywistości są to dwa niezależne układy, a połączenie między nimi realizowane jest na drodze radiowej.


Rys.7.5. Schemat ideowy nadajnika oraz detektora numerów.


Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY Divider is

PORT (

Inp : IN STD_LOGIC;

Outp : OUT STD_LOGIC);

END Divider;

ARCHITECTURE behavior OF Divider IS

-- signal count, new_count : std_logic_vector (3 downto 0 ) := "0000";

signal count : std_logic_vector (3 downto 0 ) := "0000";

BEGIN

process ( Inp )

begin

if (Inp'Event and Inp = '1') then

if count = "1111" then

count <= "0000";

else

count <= std_logic_vector(unsigned(count) + "1");

end if;

end if;

end process;

process ( count )

begin

if count >= "0111" then

Outp <= '1';

else

Outp <= '0';

end if;

end process;

END behavior;

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY GenNum is

PORT (

ClkInp : IN STD_LOGIC;

Numer : OUT STD_LOGIC);

END GenNum;

ARCHITECTURE behavior OF GenNum IS

signal nbr : std_logic_vector (12 downto 0) := "0000000000000";

signal tmp_clk : std_logic_vector (4 downto 0) := "00000";

BEGIN

process ( ClkInp )

begin

if ( ClkInp'Event and ClkInp = '1') then

if tmp_clk( 4 ) = '0' then

case tmp_clk(3 downto 0) is

when "0000" => Numer <= '0';

when "0001" => Numer <= nbr( 0 );

when "0010" => Numer <= nbr( 1 );

when "0011" => Numer <= nbr( 2 );

when "0100" => Numer <= nbr( 3 );

when "0101" => Numer <= nbr( 4 );

when "0110" => Numer <= nbr( 5 );

when "0111" => Numer <= '0';

when "1000" => Numer <= nbr( 6 );

when "1001" => Numer <= nbr( 7 );

when "1010" => Numer <= nbr( 8 );

when "1011" => Numer <= nbr( 9 );

when "1100" => Numer <= nbr( 10 );

when "1101" => Numer <= nbr( 11 );

when "1110" => Numer <= nbr( 12 );

when "1111" =>

Numer <= '0';

nbr <= std_logic_vector(unsigned(nbr) + "1");

when others => Numer <= '1';

end case;

else

Numer <= '1';

end if;

if tmp_clk > "11000" then

tmp_clk <= "00000";

else

tmp_clk <= std_logic_vector(unsigned(tmp_clk) + "1");

end if;

end if;

end process;

END behavior;

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY ReadNum is

PORT (

In_clk : IN STD_LOGIC;

In_num : IN STD_LOGIC;

Num_OK : OUT STD_LOGIC;

Numer : OUT STD_LOGIC_VECTOR(6 DOWNTO 0);

DIP : IN STD_LOGIC_VECTOR (7 DOWNTO 0) );

END ReadNum;

ARCHITECTURE behavior OF ReadNum IS

signal dtk_nbr : std_logic_vector (15 downto 0) := "0000000000000000";

signal aux : std_logic := '0';

-- signal aux_nbr : std_logic_vector (6 downto 0) := "0000000";

BEGIN

process ( In_clk, In_num )

begin

if ( In_clk'Event and In_clk = '0') then

dtk_nbr <= dtk_nbr( 14 downto 0 ) & In_num;

end if;

end process;

process ( dtk_nbr, DIP )

begin

if dtk_nbr = "01111110" & DIP( 6 downto 0 ) & '0' then

aux <= '1';

else

aux <= '0';

end if;

end process;

process ( aux )

begin

if aux'Event and aux = '1' then

Numer <= dtk_nbr( 7 downto 1 );

if Num_OK = '0' then

Num_OK <= '1';

else

Num_OK <= '0';

end if;

end if;

end process;

END behavior;

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY VHDL_Blk is

PORT (

Bin : IN STD_LOGIC_VECTOR (6 DOWNTO 0);

UpLED : OUT STD_LOGIC_VECTOR (6 DOWNTO 0);

LdLED : OUT STD_LOGIC_VECTOR (6 DOWNTO 0));

END VHDL_Blk;

ARCHITECTURE behavior OF VHDL_Blk IS

shared VARIABLE TMP : std_logic_vector ( 7 downto 0 );

shared VARIABLE TMP1 : std_logic_vector ( 3 downto 0 );

SIGNAL digit : std_logic_vector ( 7 downto 0 );

FUNCTION BCD2LED( BCD : std_logic_vector( 3 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 6 downto 0 );

begin

if BCD = "0000" then TMP := "1000000"; -- 0

elsif BCD = "0001" then TMP := "1111001"; -- 1

elsif BCD = "0010" then TMP := "0100100"; -- 2

elsif BCD = "0011" then TMP := "0110000"; -- 3

elsif BCD = "0100" then TMP := "0011001"; -- 4

elsif BCD = "0101" then TMP := "0010010"; -- 5

elsif BCD = "0110" then TMP := "0000010"; -- 6

elsif BCD = "0111" then TMP := "1111000"; -- 7

elsif BCD = "1000" then TMP := "0000000"; -- 8

elsif BCD = "1001" then TMP := "0010000"; -- 9

else

TMP := "0111111";

end if;

return TMP;

end BCD2LED;

BEGIN

process( Bin ) -- bin-2-BCD converter

begin

TMP := ( '0' & Bin ) and "00001111";

if TMP > "00001001" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

if Bin(4) = '1' then

TMP := std_logic_vector( unsigned(TMP) + x"16" );

if TMP(3 downto 0) > "1001" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

end if;

if Bin(5) = '1' then

TMP := std_logic_vector( unsigned(TMP) + x"32" );

if TMP(3 downto 0) > "1001" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

end if;

if Bin(6) = '1' then

TMP := std_logic_vector( unsigned(TMP) + x"64" );

if TMP(3 downto 0) > x"9" then

TMP := std_logic_vector( unsigned(TMP) + 6 );

end if;

if TMP(7 downto 4) > x"9" then

TMP := std_logic_vector( unsigned(TMP) + x"60" );

end if;

end if;

digit <= TMP;

end process;

process( digit ) -- BCD-2-7 segment LED converter

begin

LdLED <= BCD2LED( digit( 3 downto 0 ) );

UpLED <= BCD2LED( digit( 7 downto 4 ) );

end process;

END behavior;

    1. Generator sygnału wizyjnego dla monitora monochromatycznego

Programowalne układy logiczne firmy XILINX mogą zostać użyte do generacji sygnałów dla monitorów komputerowych. W tej roli pracują podobnie do generatorów obrazu wchodzących w skład kart graficznych. Poniżej przedstawiono aplikację, której celem jest generacja ramki obrazowej dla monitora monochromatycznego. Kompletny sygnał wizyjny składa się z trzech sygnałów: sygnały synchronizacji poziomej, sygnały synchronizacji pionowej oraz sygnału luminancji. W przypadku monitorów komputerowych poziomy wszystkich sygnałów mieszczą się w standardzie TTL. Wygląd sygnałów synchronizacji poziomej i pionowej przedstawia rysunek 7.6. Sygnał luminancji jest równy jedynce logicznej gdy odpowiadający mu punk na ekranie jest jasny lub jest równy zeru logicznemu gdy odpowiedni punkt jest wygaszony.

Rys.7.6. Kształt impulsów synchronizacji poziomej oraz pionowej.

Schemat generatora sygnału wizyjnego wykonany w programie ORCAD przedstawia rysunek 7.7. Zawiera on trzy bloki, których logika jest opisana za pomocą języka VHDL. Są to: Div, V2H oraz Video.

Blok Div służy do generacji impulsu synchronizacji poziomej (wyjście H_Sync). Wykonuje on podział częstotliwości z generatora (wejście M8) w celu uzyskania odpowiednich reżimów czasowych generowanego sygnału synchronizacji. Dodatkowo wyjściem z bloku jest licznik kolumn (punktów w linii) generowanego obrazu. Jest to 9-bitowy sygnał stanowiący wejście do bloku generacji sygnału wizyjnego. Szczegóły implementacyjne bloku znajdują się w ramce poniżej wewnątrz ciała ARCHITECTURE behavior OF Div IS.

Blok V2H służy do generacji impulsu synchronizacji pionowej. Wejściem taktującym jest sygnał synchronizacji poziomej, bowiem wygodnie jest podawać parametry czasowe sygnału synchronizacji pionowej w jednostkach równych okresowi synchronizacji poziomej. Wyjściem z układu jest sygnał V_Sync. Dodatkowo 9-bitowe wyjście Count umożliwia blokowi Video zliczanie linii generowanego obrazu. Szczegóły implementacyjne przedstawiono w ramce poniżej w ciele ARCHITECTURE behavior OF V2H IS.

Rys.7.7. Schemat ideowy generatora sygnału wizyjnego.

Blok Video służy do generacji sygnału luminancji (wyjście Video). Dodatkowo niektóre monitory potrafią reagować na sygnał podniesionej jasności obrazu, który może zostać wygenerowany na wyjściu Intensity. Zadaniem omawianej aplikacji będzie generacji różnego typu obrazów testowych. Będą to np.: pasy poziome, pasy pionowe, krata, szachownica, itp. Typ generowanego aktualnie obrazu wybierany jest za pomocą przełączników dołączonych do wejścia Mode bloku Video. Szczegóły implementacyjne programu w języku VHDL bloku Video przedstawiono poniżej. Zasada pracy bloku Video polega na ustawianiu w stan wysoki lub niski wyjścia luminancji Video zależnie od aktualnego stanu licznika kolumn i linii. Zależnie od stanu czterech młodszych linii sygnału Mode wywoływana jest odpowiednia funkcja ustalająca stan sygnały luminancji:

case Mode( 3 downto 0 ) is

when "0000" => AUX <= Video0( NoCol, NoRow, Mode );

when "0001" => AUX <= Video1( NoCol, NoRow, Mode );

when "0010" => AUX <= Video2( NoCol, NoRow, Mode );

when "0011" => AUX <= Video3( NoCol, NoRow, Mode );

when "0100" => AUX <= Video4( NoCol, NoRow, Mode );

when "0101" => AUX <= Video5( NoCol, NoRow, Mode );

when "0110" => AUX <= Video6( NoCol, NoRow, Mode );

when "0111" => AUX <= Video7( NoCol, NoRow, Mode );

when "1000" => AUX <= Video8( NoCol, NoRow, Mode );

when "1001" => AUX <= Video9( NoCol, NoRow, Mode );

when "1010" => AUX <= Video10( NoCol, NoRow, Mode );

when others =>

AUX <= "00";

end case;

Wywoływane w instrukcji case funkcje generują pasy pionowe, poziome lub kratę (ramka poniżej nie zawiera implementacji wszystkich funkcji). Przykładowo generacja poziomych pasów wymaga odczytywania stanu licznika linii i w zależności od jego wartości ustawienia lub gaszenia sygnału luminancji.

Warto zauważyć, że przedstawiona aplikacja w istotny sposób różni się od typowych kart graficznych mimo, iż ich zachowanie wydaje się być bardzo podobne. Wszystkie karty graficzne posiadają własną pamięć, w której przechowywana jest informacja o luminancji (lub chrominancji w przypadku obrazów kolorowych). Zawartość tej pamięci jest modyfikowana przez główny procesor systemu komputerowego. Omawiana aplikacja nie posiada lokalnej pamięci i dlatego jest w stanie generować tylko stały nie zmieniający się zestaw statycznych obrazów.

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY Div is

PORT (

M8 : IN STD_LOGIC;

H_Sync : OUT STD_LOGIC;

Count : OUT STD_LOGIC_VECTOR (8 DOWNTO 0));

END Div;

ARCHITECTURE behavior OF Div IS

SIGNAL TMPCLK : std_logic_vector ( 8 downto 0 ) := "000000000";

SHARED VARIABLE AUX : std_logic_vector ( 8 downto 0 );

BEGIN

PROCESS( M8, TMPCLK )

BEGIN

IF M8 = '1' AND M8'Event THEN

AUX := TMPCLK;

AUX := std_logic_vector( unsigned(AUX) + 1 );

IF AUX > "110010000" THEN -- 110010000 = 400 = 55.1us

AUX := "000000000";

END IF;

IF AUX < "001000000" THEN -- 1000000 = 64 = 8.4us

H_Sync <= '1';

ELSE

H_Sync <= '0';

END IF;

TMPCLK <= AUX;

END IF;

Count <= TMPCLK;

END PROCESS;

END behavior;

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY V2H is

PORT (

H_Sync : IN STD_LOGIC;

V_Sync : OUT STD_LOGIC;

Count : OUT STD_LOGIC_VECTOR (8 DOWNTO 0) );

END V2H;

ARCHITECTURE behavior OF V2H IS

SIGNAL TMPCLK : std_logic_vector ( 8 downto 0 ) := "000000000";

SHARED VARIABLE AUX : std_logic_vector ( 8 downto 0 );

BEGIN

PROCESS( H_Sync, TMPCLK )

BEGIN

IF H_Sync = '1' AND H_Sync'Event THEN

AUX := TMPCLK;

AUX := std_logic_vector( unsigned(AUX) + 1 );

IF AUX > "101010010" THEN -- = 370 = 20.4ms .

AUX := "000000000";

END IF;

IF AUX < "000010000" THEN -- = 16 = 880us

V_Sync <= '0';

ELSE

V_Sync <= '1';

END IF;

TMPCLK <= AUX;

END IF;

Count <= TMPCLK;

END PROCESS;

END behavior;

Library ieee;

Use ieee.std_logic_1164.all;

Use ieee.numeric_std.all;

ENTITY Video is

PORT (

NoCol : IN STD_LOGIC_VECTOR (8 DOWNTO 0);

NoRow : IN STD_LOGIC_VECTOR (8 DOWNTO 0);

Mode : IN STD_LOGIC_VECTOR (7 DOWNTO 0);

Clk : IN STD_LOGIC;

Video : OUT STD_LOGIC;

Intensity : OUT STD_LOGIC);

END Video;

ARCHITECTURE behavior OF Video IS

--

-- Pionowe pasy

--

FUNCTION Video1( NoCol : std_logic_vector( 8 downto 0 );

NoRow : std_logic_vector( 8 downto 0 );

Mode : std_logic_vector( 7 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 1 downto 0 );

begin

TMP( 0 ) := '0'; TMP( 1 ) := NoCol( 1 );

return TMP;

end Video1;

--

-- Poziome pasy

--

FUNCTION Video8( NoCol : std_logic_vector( 8 downto 0 );

NoRow : std_logic_vector( 8 downto 0 );

Mode : std_logic_vector( 7 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 1 downto 0 );

begin

TMP( 0 ) := '0'; TMP( 1 ) := NoRow( 4 );

return TMP;

end Video8;

--

-- Krata

--

FUNCTION Video9( NoCol : std_logic_vector( 8 downto 0 );

NoRow : std_logic_vector( 8 downto 0 );

Mode : std_logic_vector( 7 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 1 downto 0 );

VARIABLE AUXC, AUXR : std_logic_vector ( 8 downto 0 );

begin

TMP( 0 ) := '0';

AUXC := std_logic_vector( unsigned(NoCol) - 96 );

if ( AUXC < "100000000" ) AND ( AUXC(3 downto 0) = "0000" ) then

TMP( 1 ) := '1';

else

TMP( 1 ) := '0';

end if;

AUXR:= std_logic_vector( unsigned(NoRow) - 64 );

if ( AUXR < "100000000" ) AND ( AUXR(3 downto 0) = "0000" ) then

TMP( 1 ) := '1';

end if;

if NoRow < "001000000" then TMP( 1 ) := '0'; end if;

if NoCol < "001100000" then TMP( 1 ) := '0'; end if;

return TMP;

end Video9;

--

-- Szachownica

--

FUNCTION Video10( NoCol : std_logic_vector( 8 downto 0 );

NoRow : std_logic_vector( 8 downto 0 );

Mode : std_logic_vector( 7 downto 0 ) ) return std_logic_vector is

VARIABLE TMP : std_logic_vector ( 1 downto 0 );

VARIABLE AUXC, AUXR : std_logic_vector ( 8 downto 0 );

begin

TMP( 0 ) := '0';

AUXC := std_logic_vector( unsigned(NoCol) - 96 );

if ( AUXC < "100000000" ) AND ( AUXC(3 downto 0) = "0000" ) then

TMP( 1 ) := '1';

else

TMP( 1 ) := '0';

end if;

AUXR:= std_logic_vector( unsigned(NoRow) - 64 );

if ( AUXR < "100000000" ) AND ( AUXR(3 downto 0) = "0000" ) then

TMP( 1 ) := '1';

end if;

if TMP( 1 ) = '0' then

TMP( 1 ) := AUXR( 4 ) XOR AUXC( 4 );

end if;

if NoRow < "001000000" then TMP( 1 ) := '0'; end if;

if NoCol < "001100000" then TMP( 1 ) := '0'; end if;

return TMP;

end Video10;

SIGNAL AUX : std_logic_vector ( 1 downto 0 );

BEGIN

PROCESS( Clk )

BEGIN

if Clk'Event and Clk = '0' then

case Mode( 3 downto 0 ) is

when "0000" => AUX <= Video0( NoCol, NoRow, Mode );

when "0001" => AUX <= Video1( NoCol, NoRow, Mode );

when "0010" => AUX <= Video2( NoCol, NoRow, Mode );

when "0011" => AUX <= Video3( NoCol, NoRow, Mode );

when "0100" => AUX <= Video4( NoCol, NoRow, Mode );

when "0101" => AUX <= Video5( NoCol, NoRow, Mode );

when "0110" => AUX <= Video6( NoCol, NoRow, Mode );

when "0111" => AUX <= Video7( NoCol, NoRow, Mode );

when "1000" => AUX <= Video8( NoCol, NoRow, Mode );

when "1001" => AUX <= Video9( NoCol, NoRow, Mode );

when "1010" => AUX <= Video10( NoCol, NoRow, Mode );

when others =>

AUX <= "00";

end case;

Video <= AUX( 1 );

Intensity <= AUX( 0 );

end if;

END PROCESS;

END behavior;

Literatura

  1. IEEE Standard VHDL Language Reference Manual, IEEE Std. 1076-1987, Institute of Electrical and Elecronic Engineers, 1988.

  2. IEEE Standard VHDL Language Reference Manual, ANSI/IEEE Std. 1076-1993, Institute of Electrical and Elecronic Engineers, 1984.

  3. XILINX - opis programu XACT

  4. XILINX - katalogi firmowe

  5. Esperan MasterClass. The multimedia VHDL Tutorial, A Windows-based tutorial for FPGA and PLD design, OrCAD, oprogramowanie na płytce CD.

  6. Navabi Z.: VHDL. Analysis and Modeling of Digital Systems, McGraw-Hill, New York, 1998.

  7. Ashenden P.J.: The VHDL Cookbook. First Edition, Dept. Computer Science, University of Adelaide, South Australia, 1990, materiały dostępne w sieci Internet.

ZROBIĆ: