Blog
Nieco spóźniony wpis z projektem
17.12.2006 - Niedziela - 14:52
Jakiś czas temu miałem termin oddania projektu układu opisanego w VHDL-u. Ponieważ do dyspozycji miałem tylko CPLD Xilinx XC95108, który bardzo pojemnym układem nie jest, program nie mógł być zbyt skomplikowany. Napisałem więc symulator taśmociągu z dwiema programowalnymi bramkami.
Większość opisu napisałem u siebie w domu, skompilowałem i przetestowałem oglądając przebiegi czasowe. Ale już na miejscu, w pracowni, okazało się, że trochę "przedobrzyłem". Nie wziąłem pod uwagę faktu, że układ nie poradzi sobie z tak skomplikowanym programem... Musiałem się wziąć za "odchudzanie"... W moich pierwotnych założeniach układ był zupełnie niezależny od zegara znajdującego się na płytce. Odpowiednie działania miały się wykonywać w momencie pojawienia się narastających zboczy sygnałów symulujących i programujących (to jest wciśnięć przypisku symulującego paczkę i przycisków programowania bramek). Niestety, w XC95108 można wykorzystać tylko trzy sygnały, które będą sprawdzane pod kątem wystąpienia zbocza (takie sprawdzanie jest możliwe tylko w przerzutniku synchronicznym na jego wejściu zegarowym). Ja potrzebowałem niestety co najmniej czterech takich sygnałów. Musiałem więc sobie darować tę możliwość i wykorzystać uzależnienie od zegara wewnętrznego, co jest nieco gorszym rozwiązaniem. Kolejnym problemem była ilość porównać dokonywanych w układzie. Komparatory wielobitowe szybko i skutecznie zżerają zasoby układu, przez co momentalnie straciłem możliwość "wgrania" opisu do tego układu. Musiałem podjąć kolejne kroki optymalizacyjne: łączenie czterobitowych wektorów w ośmiobitowe (dzięki temu nieco lepiej mogłem rozdysponować zasoby w poszczególnych makrokomórkach), minimalizacja ilości zmiennych wewnętrznych (zwalniałem przerzutniki), usunięcie sygnałów typu buffer (kolejne wolne przerzutniki) itd. Po odchudzeniu układ dawał się zaprogramować, ale stracił kilka fajnych bajerów, które miałem ochotę do niego wrzucić. Ważne, że wyrobiłem się w czasie (jako jeden z nielicznych) i nie muszę teraz stosować dalszego odchudzania ;-)
Oczywiście zamieszczam pełny kod opisu układu. Oczywiście w VHDL ;-)
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity sterownik is
port(
WE, R, P0, P1, AKC: in std_logic;
CLK: in std_logic;
LED1, LED2: out std_logic_vector(7 downto 0));
end sterownik;
architecture urzadzenie of sterownik is
function hex2led (hex: std_logic_vector(3 downto 0)) return std_logic_vector is
variable s: std_logic_vector(7 downto 1);
begin
case hex is
when "0000" => s:= "1000000";
when "0001" => s:= "1111100";
when "0010" => s:= "0001001";
when "0011" => s:= "0011000";
when "0100" => s:= "0110100";
when "0101" => s:= "0010010";
when "0110" => s:= "0000010";
when "0111" => s:= "1111000";
when "1000" => s:= "0000000";
when "1001" => s:= "0010000";
when "1010" => s:= "0100000";
when "1011" => s:= "0000110";
when "1100" => s:= "1000011";
when "1101" => s:= "0001100";
when "1110" => s:= "0000011";
when "1111" => s:= "0100011";
when others => s:= "1010101";
end case;
return s;
end hex2led;
begin
process(CLK, WE, AKC, P0, P1, R)
variable B: std_logic_vector(7 downto 0) := "00000000";
variable BS: std_logic_vector(7 downto 0);
variable start: std_logic := '0';
begin
if R = '1' then
B := "00000000";
BS := "00000000";
LED1(0) <= '1';
LED2(0) <= '1';
start := '0';
elsif CLK'event and CLK = '1' then
if P0 = '1' then BS(7 downto 4) := BS(7 downto 4) + 1;
elsif P1 = '1' then BS(3 downto 0) := BS(3 downto 0) + 1;
elsif AKC = '1' then start := '1';
elsif WE = '1' then
if B(7 downto 4) < BS(7 downto 4) then
B(7 downto 4) := B(7 downto 4)+1;
if B(7 downto 4) = BS(7 downto 4) then
LED1(0) <= '0';
end if;
elsif B(3 downto 0) < BS(3 downto 0) then
B(3 downto 0) := B(3 downto 0)+1;
if B(3 downto 0) = BS(3 downto 0) then
LED2(0) <= '0';
end if;
elsif B = BS then
B := "00010000";
LED1(0) <= '1';
LED2(0) <= '1';
end if;
end if;
end if;
if start = '1' then
LED1(7 downto 1) <= hex2led(B(7 downto 4));
LED2(7 downto 1) <= hex2led(B(3 downto 0));
else
LED1(7 downto 1) <= hex2led(BS(7 downto 4));
LED2(7 downto 1) <= hex2led(BS(3 downto 0));
end if;
end process;
end urzadzenie;
Testbencha zamieścić? ;-)
Słowa kluczowe: vhdl programowanie komputer linux
Czemu X zamiast jedynki?
25.11.2006 - Sobota - 18:27
Spodobał mi się VHDL, więc postanowiłem nieco głębiej w niego "wejść", no ale do tego potrzebne mi jest oprogramowanie. Oczywiście, mogę sobie kupić płytkę testową z układem programowalnym, ale wydatek rzędu 150 dolarów mnie przeraża. Muszę korzystać z symulatorów. A pod Linuksem wcale nie jest tak różowo. Co prawda Altera i Xilinx (dwaj czołowi producenci układów programowalnych) udostępniają swoje oprogramowanie w wersji dla Linuksa, ale... ISE Webpack Xilinksa to nieco ponad gigabajt danych do ściągnięcia i spore wymagania jeśli chodzi o sprzęt. A na dodatek "Requires Red Hat Enterprise 3.0", przez co już wiadomo, że mogą być kłopoty z jego uruchomieniem na Ubuntu. W przypadku Altery też podobnie...
Dlatego muszę skorzystać z GHDL-a i Gtkwave'a. Niestety zintegrowanego pakietu, który zrobiłby wszystko za mnie nie znalazłem, ale może i dobrze. Lepiej poznam ten język. W pakietach typu ISE Webpack czy Quartus (Altera) po napisaniu kodu można sobie "wyklikać" przebiegi testowe i zobaczyć odpowiedź układu. W przypadku GHDL-a muszę sobie napisać samemu testbencha (program testujący). I tu się zaczynają schody...
Na początek napisałem sobie prosty licznik modulo 16:
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity licznik16 is port( CLK: in std_logic; Q: out std_logic_vector(3 downto 0); R: in std_logic); end licznik16; architecture licz of licznik16 is begin process(CLK, R) variable stan: unsigned(3 downto 0):="0000"; begin if R = '0' then stan:="0000"; elsif (CLK'event and CLK='1') then stan:=stan+1; end if; Q <= std_logic_vector(stan); end process; end licz;
Napisałem, stwierdziłem, że powinien działać, ale warto byłoby go sprawdzić. No to wziąłem się za pisanie testbencha... Na początku wszystko szło bez przeszkód. Testbench napisał się momentalnie. Nieco zaglądałem do podręczników i skakałem po przykładach z Internetu, ale jakoś napisałem. Udało się skompilować i wygenerowałem plik z przebiegami, który obejrzałem sobie w Gtkwave. I tu zaskoczenie! Wszystko gra, ale nie do końca... Ale najpierw rzućmy okiem na testbencha:
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity licztb is end licztb; architecture licztbarch of licztb is component licznik16 port(CLK: in std_logic; Q: out std_logic_vector(3 downto 0); R: in std_logic); end component; signal zegar, reset: std_logic; signal Q: std_logic_vector(3 downto 0); begin mapowanie: licznik16 port map(CLK=>zegar, R=>reset, Q=>Q); process begin Q<"0000"; wait for 10 ns; zegar <= '0'; wait for 10 ns; reset <= '1' after 1 ns, '0' after 90 ns, '1' after 150 ns; wait for 10 ns; for i in 0 to 100 loop zegar <= not(zegar); wait for 50 ns; end loop; wait; end process; end licztbarch;
Czyli jak widać, najpierw zeruję wyjście, co na przebiegach widać. Chwilę potem inicjalizuję zegar (zegar ma operować tylko na stanach 0 i 1, wszelkie pozostałe stany są wykluczone), co też widać. Następnie sprytnie generuję sygnał resetujący licznik, chociaż nie muszę tak naprawdę tego robić, bo licznik został zresetowany na początku procesu. W pętli, tworzę prostokątny przebieg zegara o okresie 100 nanosekund. Zegar ten jest sprzężony z zegarem CLK w w samym liczniku. Czyli z każdym zboczem narastającym (CLK'event and CLK='1') licznik będzie się zwiększał o jeden. I wszystko byłoby ok, gdyby nie to, że zamiast stanu 1 na przebiegach pojawia się stan X (co w logice dziewięciowartościowej oznacza stan nieznany). Mało tego! Układ traktuje go jako jedynkę, bo wcale jego pojawienie się nie zaburza działania. Ale mimo to tego stanu tam nie powinno być!
Ślęczę nad tym już którąś godzinę i nie wiem co z tym zrobić... Czyżby to było związane z konwersją zmiennej typu unsigned na sygnał std_logic_vector? Nie wiem... Ktoś pomoże?
Słowa kluczowe: vhdl programowanie komputer linux
Układy programowalne - fajne!
06.11.2006 - Poniedziałek - 20:50
Dzisiaj miałem właściwie pierwszą okazję zapoznać się w praktyce z działaniem układów programowalnych i muszę, z pełną odpowiedzialnością stwierdzić, że jest to fajne ;-)
Ale dla niewprowadzonych krótkie wyjaśnienie. Układy programowalne to takie uniwersalne układy elektroniczne, które można przystosować (zaprogramować) przez wykonywanie fizycznych połączeń pomiędzy poszczególnymi blokami funkcjonalnymi układów do wykonywania najróżniejszych zadań. To taka alternatywa dla drogich układów specjalizowanych.
A jak to wygląda w praktyce? W praktyce mamy w laboratorium płytki testowe, na których jest kilka przycisków, kilka świecących diodek i dwa wyświetlacze siedmiosegmentowe. Taka płytka z układem programowalnym jest podłączona do komputera. Na komputerze jest zainstalowane oprogramowanie producenta układu (w tym przypadku Xilinx), które jest kompilatorem języka VHDL do formatu, który jest wymagany przez ten układ. W VHDL-u piszemy sobie program, który opisuje zachowanie działającego układu. VHDL jest specyficznym językiem programowania, w którym oprócz zmiennych występują również sygnały, a instrukcje można wykonywać współbieżnie lub sekwencyjnie (tak jak to ma miejsce w popularnych, typowo komputerowych językach programowania), dlatego wymaga przestawienia się w inny tryb myślenia. Po skompilowaniu taki program jest wgrywany do pamięci układu. A zawartość pamięci steruje odpowiednimi tranzystorami polowymi, które "otwierają" drogę dla sygnałów, przez co układ zaczyna wykonywać swoje zadanie. W rzeczywistości jest to dość skomplikowane, ale to co widzimy - efekt naszego działania - jest już naprawdę fajną rzeczą ;-)
Z racji nieskomplikowanego układu testowego napisałem dziś tylko licznik modulo 256, z asynchronicznym resetowaniem oraz funkcją start/stop, który wyświetlał swój stan w formie binarnej przy pomocy diód LED oraz w formie szesnastkowej na wyświetlaczach siedmiosegmentowych. Niby nic wielkiego, ale cieszyłem się jak dziecko, choćby dlatego, że jako rasowy programista postawione mi zadanie wykonałem najszybciej i jedynym błędem był drobny błąd w dekoderze sygnału czterobitowego na sygnał ośmiobitowy wyświetlacza siedmiosegmentowego (a właściwie ośmio, bo z kropką ;-)), objawiający się dziwnym wyglądem cyfry "B" ;-) Błąd został szybko poprawiony, a radości nie było końca ;-) Poniżej zamieszczam pełny kod w VHDL-u tego licznika, dla potomności ;-)
library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.all; entity licznik is port( CLK: in std_logic; STOP: in std_logic; RESET: in std_logic; W1: out std_logic_vector(7 downto 0); W2: out std_logic_vector(7 downto 0); Q: inout std_logic_vector(7 downto 0)); end licznik; architecture alicznik of licznik is function hex2led (hex: std_logic_vector(3 downto 0)) return std_logic_vector is variable s: std_logic_vector(7 downto 0); begin case hex is when "0000" => s:= "10000001"; --kod wyswietlacza inny od standardowo przyjetego when "0001" => s:= "11111001"; when "0010" => s:= "00010011"; when "0011" => s:= "00110001"; when "0100" => s:= "01101001"; when "0101" => s:= "00100101"; when "0110" => s:= "00000101"; when "0111" => s:= "11110001"; when "1000" => s:= "00000001"; when "1001" => s:= "00100001"; when "1010" => s:= "01000001"; when "1011" => s:= "00001101"; when "1100" => s:= "10000111"; when "1101" => s:= "00011001"; when "1110" => s:= "00000111"; when "1111" => s:= "01000111"; when others => s:= "00000000"; end case; return s; end hex2led; begin process(CLK, STOP, RESET) variable wartosc: std_logic_vector(7 downto 0); begin if RESET = '1' then wartosc:="00000000"; elsif rising_edge(CLK) and STOP = '0' then wartosc:=wartosc+1; end if; W1<=hex2led(wartosc(7 downto 4)); W2<=hex2led(wartosc(3 downto 0)); Q<=not(wartosc); end process; end alicznik;
Chyba warto się jeszcze trochę podszkolić w VHDL-u ;-)
Słowa kluczowe: WAT komputer programowanie VHDL
