System-on-Chip - Elektronika Praktyczna
Transkrypt
System-on-Chip - Elektronika Praktyczna
S yPs tRe m p O -Jo n E -KC hT i Y Zbuduj własny System-on-Chip 8051 w VHDL, część 2 W drugiej części artykułu przedstawiamy sposób implementacji rdzenia mikrokontrolera 8051 w układach pochodzących z rodzin: Cyclone/ Cyclone II firmy Altera i Spartan-3 firmy Xilinx za pomocą standardowych, bezpłatnych narzędzi projektowych: Quartusa II oraz WebPacka. Rekomendacje: projekt polecamy wszystkim Czytelnikom, którzy interesują się nowoczesnymi sposobami konstruowania sprzętu, a także tym, którzy chcą poznać w praktyce nowoczesną technologię System-on-Chip. Elektronika Praktyczna 3/2005 Implementacja - uwagi wstępne rdzenia (z towarzyszącymi timerami i UART-ami) z pamięciami: programu i danych (RAM i XRAM), jak pokazano na rys. 1 w poprzedniej części artykułu (EP2/2005, str. 35). Taki sposób implementacji, jakkolwiek niezwykle wygodny i efektowny, jest kosztowny, bowiem integracja pamięci o tak dużych pojemnościach w układzie FPGA jest niezbyt efektywna, nawet w przypadku, gdy korzystamy z układu wyposażonego w dedykowane obszary konfigurowalnej pamięci RAM. - Integracja w układzie FPGA rdzenia z wyprowadzonymi na zewnątrz obudowy magistralami: adresową, danych pamięci programu i pamięci danych oraz liniami I/O. W listach, jakie otrzymałem po opublikowaniu pierwszej części artykułu, była wyczuwalna nadzieja na to, że zaimplementowanie w układzie FPGA rdzenia procesora 8051 umożliwi zbudowanie sobie szybkiego, a przy tym taniego mikrokontrolera. O ile prawdą jest, że możliwa do uzyskania częstotliwość taktowania rdzenia jest rzeczywiście wysoka (warto ponadto pamiętać, że rdzeń Oregano MC8051 jest 1…4-taktowy), to koszt docelowego układu FPGA jest dość wysoki. Względnie wysoka, sięgająca 15…25 EUR, cena docelowego układu FPGA wynika z wymaganych bardzo dużych zasobów logicznych niezbędnych do zaimplementowania kompletnego mikrokontrolera. Mikrokontroler Oregano MC8051 można zaimplementować na wiele sposobów, spośród których największym powodzeniem będą się najprawdopo- Rys. 3. Zawartość pamięci konfiguracji i pamięci dobniej cieszyć dwa: programu mikrokontrolera (zaimplementowana w - Integracja w ukła- blokach SRAM) są każdorazowo odtwarzane po dzie FPGA kompletnego włączeniu zasilania 29 System-on-Chip Rys. 4. Widok okna systemu WebPack ISE po pomyślnym zakończeniu kompilacji Ten sposób jest zdecydowanie tańszy, ale - niestety - mniej komfortowy, bowiem wymaga dołączenia zewnętrznych pamięci w sposób znany z klasycznych systemów mikroprocesorowych… Ten sposób zdecydowanie zmniejsza wymagania co do zasobów logicznych układu FPGA, ale z kolei wymusza zastosowanie układu o dużej liczbie wyprowadzeń (ponad 150!). Żaden z przedstawionych sposobów nie sprawdził się niestety na płytce uruchomieniowej dla układu Cyclone EP1C3, o której pisałem w pierwszej części artykułu, ponieważ układ ten jest zbyt „mały”. W tab. 1 zestawiono podstawowe parametry układów zastosowanych w zestawach uruchomieniowych i najmniejszych układach z rodzin Spartan 3, Cyclone i Cyclone II, w których mikrokontroler Oregano MC8051 (bez pamięci programu i danych) można zaimplementować bez konieczności stosowania dodatkowych zabiegów. W tab. 2 znajduje się natomiast zestawienie wykorzystania zasobów logicznych układów, w których prezentowany mikrokontroler się „zmieścił”. Łatwo zauważyć, że układ EP2C8 jest mocno przewymiarowany, co wynika z niezrozumiałego posunięcia firmy Altera, która nie oferuje lepiej dopasowanych do wymogów tej aplikacji układu EP2C5 w obudowie Na 8051 świat się nie kończy Zasoby układu FPGA niezbędne do implementacji mikrokontrolera 8051 są dość duże, co wiąże się z ceną układu. Z tego powodu w majowej EP przedstawimy alternatywne rozwiązanie: mikrokontroler RISC, którego cena (po zaimplementowaniu w układzie FPGA) nie przekroczy 0,6 USD, do tego użytkownik będzie mógł wykorzystać w dowolny sposób pozostałe 95% zasobów układu FPGA… 30 Rys. 5. Struktura projektu Oregano MC8051 Elektronika Praktyczna 3/2005 System-on-Chip Tab. 1. Podstawowe parametry „najmniejszych” układów PLD, w których można zaimplementować mikrokontroler Oregano MC8051 Typ układu XC3S200 EP1C4 EP2C8 Rodzina Liczba LE/SLICE Spartan 3 Cyclone Cyclone II -/1920 4000/8256/- Liczba I/O zastosowanego układu 173 249 182 Wbudowany RAM [kb] 216 76,5 162 (jak np. addsubcore_ lub comb_ divider_), - zawierającego opis struktury logicznej lub funkcjonalny bloku - nazwa pliku jest rozszerzona o sufiks rtl lub struct (jak np. addsubcore_struct lub comb_divider_rtl), - zawierającego opis konfiguracji bloku opisanego w pliku nazwa_struct lub nazwa_rtl - nazwa tego pliku jest rozszerzona o sufiks cfg (jak np. addsubcore_struct_cfg lub comb_divider_ rtl_cfg). Wszystkie pliki zawierają opis VHDL-owy, co znajduje odzwierciedlenie w rozszerzeniu ich nazw - vhd. Projekt przygotowany przez firmę Oregano umożliwia implementację w PLD wszystkich pamięci wykorzystywanych przez mikrokontroler Oregano MC8051. Ze względu na trudności implementacyjne, obydwa projekty umieszczone na CD-EP3/2005B są ulokowane w hierarchii projektu o jeden poziom niżej, dlatego pliki mc8051_top_, mc8051_top_struc i mc8051_top_ struc (zawierające przypisania do poszczególnych obszarów pamięci) nie są wykorzystywane. W skład projektu wchodzi także biblioteka mc8051_p, w której znajdują się między innymi definicje stałych wykorzystywanych w projekcie (m.in. tych, które określają liczbę zaimplementowanych UART-ów, obsługę lub jej brak poleceń DA, MUL, DIV itp.), a także bi- Rys. 6. Hierarchia plików w projekcie z większą liczbą wyprowadzeń niż PQFP208. W tab. 2 przedstawiono także wyniki ilustrujące maksymalne częstotliwości taktowania rdzenia po implementacji, uzyskane za pomocą symulatorów czasowych wbudowanych w narzędzia projektowe. Warto zauważyć, że wszystkie układy PLD wybrane jako docelowe są wyposażone we wbudowane bloki konfigurowalnej pamięci SRAM (tab. 1), którą można wykorzystać do implementacji którejś z pamięci wykorzystywanej przez mikrokontroler. Można w niej zaimplementować także pamięć programu ROM, której zawartość jest każdorazowo po włączeniu zasilania ładowana z zewnętrznej pamięci konfigurującej Flash, jak to pokazano na rys. 3. Opis HDL poszczególnych bloków mikrokontrolera przygotowany przez autorów MC8051 składa się z trzech części, ulokowanych w oddzielnych plikach: - zawierającego opis jednostki projektowej (entity) - jego nazwa zawsze zawiera opis spełnianej funkcji i podkreślnik na końcu Tab. 2. Wykorzystanie zasobów logicznych układów PLD, w których zaimplementowano rdzeń Oregano MC8051 Rodzina Liczba wykorzystanych LE/SLICE Wykorzystane zasoby [%] Liczba wykorzystanych I/O Spartan 3 -/1920 100 156 EP1C4 Cyclone 3712/- 92 156 EP2C8 Cyclone II 3584/- 43 156 Typ układu XC3S200 Elektronika Praktyczna 3/2005 Maksymalna Wykorzystaczęstotliwość ne linie I/O taktowania [%] [MHz] 21,97 (układ 87 z sufiksem -5) 25,33 (układ 62 z sufiksem -6) 22,76 (układ 85 z sufiksem -7) narne kody poleceń obsługiwanych przez CPU - list. 1. Próby prowadzone przez autora wykazały, że duży wpływ na wyniki implementacji (oprócz sposobu opisu, który jest w źródłach Oregano MC8051 bliski perfekcji) mają narzędzia odpowiadające ze syntezę logiczną. Poważne różnice (ok. 17% zasobów) występują na przykład pomiędzy Quartusem II 4.1 SP2 i Quartusem 4.2, nieco mniejszą różnicę (ok. 6%) można zauważyć pomiędzy implementacjami prowadzonymi za pomocą WebPacka 6.1 i 6.3i. Wiele wskazuje na to, że warto pokusić się o stosowanie najnowszych programów narzędziowych, w których syntezery logiczne są zazwyczaj coraz lepsze, choć nie zawsze pozbawione błędów. Jednym z mechanizmów syntezerów logicznych mających wyraźny wpływ na jakość implementacji jest wnioskowanie spełnianej funkcji na podstawie opisu HDL (tzw. inferred logic/inferred module). Przykłady pokazują, że podstawiane przez programy narzędziowe predefiniowane makrofunkcje (funkcjonalne odpowiedniki fragmentów opisu HDL) nie zawsze są kompilowane wydajniej (w sensie zajmowanych zasobów lub uzyskiwanych parametrów czasowych) od oryginalnego opisu przygotowanego przez projektanta. Złożoność tych zagadnień jest zbyt duża, aby można je było dokładnie przedstawić w artykule, ale zachęcam Czytelników do samodzielnych eksperymentów - ich wyniki są zazwyczaj bardzo interesujące i ułatwiają samodzielne „wyczucie” zależności występujących podczas tworzenia opisów dla PLD. System-on-Chip na biurku Z dotychczasowego opisu wynikają następujące spostrzeżenia: - implementacja skromnie wyposażonego (choć dość szybkiego) rdzenia mikrokontrolera 8051 jest trudna i kosztowna (wymaga bowiem układu FPGA o sporych zasobach logicznych), Dla początkujących Czytelników zamierzających rozpocząć przygodę z układami PLD zachęcamy do zapoznania się z książką „Układy programowalne - pierwsze kroki”, dostępną w ofercie handlowej Wydawnictwa AVT. Szczegółowe informacje na jej temat można znaleźć pod adresem: http://www.btc.pl/index. php?id=uppk. 31 System-on-Chip List. 1. Wybrane fragmenty pliku bibliotecznego mc8051_p.vhd package mc8051_p is ----------------------------------------------------------------------------- Set data width of mc8051_alu (no other than 8 supported at the moment!) -- Default: 8 constant C_DWIDTH : integer := 8; -------------------------------------------------------------------------------------------------------------------------------------------------------- Select whether to implement (1) or skip (0) the multiplier -- Default: 1 constant C_IMPL_MUL : integer := 0; -------------------------------------------------------------------------------------------------------------------------------------------------------- Select whether to implement (1) or skip (0) the divider -- Default: 1 constant C_IMPL_DIV : integer := 0; -------------------------------------------------------------------------------------------------------------------------------------------------------- Select whether to implement (1) or skip (0) the decimal adjustment command -- Default: 1 constant C_IMPL_DA : integer := 1; -------------------------------------------------------------------------------------------------------------------------------------------------------- Select how many timer/counter units should be implemented -- Default: 1 constant C_IMPL_N_TMR : integer := 1; -------------------------------------------------------------------------------------------------------------------------------------------------------- Select how many serial interface units should be implemented -- Default: C_IMPL_N_TMR ---(DO NOT CHANGE!)--constant C_IMPL_N_SIU : integer := C_IMPL_N_ TMR; -------------------------------------------------------------------------------------------------------------------------------------------------------- Select how many external interrupt-inputs should be implemented -- Default: C_IMPL_N_TMR ---(DO NOT CHANGE!)--constant C_IMPL_N_EXT : integer := C_IMPL_N_ TMR; ---------------------------------------------------------------------------constant OFF : std_logic_vector(5 downto 0) := „000000”; constant DA : std_logic_vector(5 downto 0) := „100000”; constant ADD_ACC_RAM : std_logic_vector(5 downto 0) := „100001”; constant ADD_ACC_ROM : std_logic_vector(5 downto 0) := „100010”; constant ADDC_ACC_RAM : std_logic_vector(5 downto 0) := „100011”; constant ADDC_ACC_ROM : std_logic_vector(5 downto 0) := „100100”; constant AND_ACC_RAM : std_logic_vector(5 downto 0) := „100101”; constant AND_ACC_ROM : std_logic_vector(5 downto 0) := „100110”; constant AND_RAM_ROM : std_logic_vector(5 downto 0) := „100111”; constant SUB_ACC_RAM : std_logic_vector(5 downto 0) := „101000”; constant SUB_ACC_ROM : std_logic_vector(5 downto 0) := „101001”; constant MUL_ACC_RAM : std_logic_vector(5 downto 0) := „101010”; constant DIV_ACC_RAM : std_logic_vector(5 downto 0) := „101011”; constant OR_RAM_ACC : std_logic_vector(5 downto 0) := „101100”; constant OR_ROM_ACC : std_logic_vector(5 downto 0) := „101101”; constant OR_ROM_RAM : std_logic_vector(5 downto 0) := „101110”; constant XOR_RAM_ACC : std_logic_vector(5 downto 0) := „101111”; constant XOR_ROM_ACC : std_logic_vector(5 downto 0) := „110000”; constant XOR_ROM_RAM : std_logic_vector(5 downto 0) := „110001”; constant RL_ACC : std_logic_vector(5 downto 0) := „110010”; constant RLC_ACC : std_logic_vector(5 downto 0) := „110011”; constant RR_ACC : std_logic_vector(5 downto 0) := „110100”; constant RRC_ACC : std_logic_vector(5 downto 0) := „110101”; constant INV_ACC : std_logic_vector(5 downto 0) := „110110”; constant INV_RAM : std_logic_vector(5 downto 0) := „110111”; constant DEC_ACC : std_logic_vector(5 downto 0) := „111000”; constant DEC_RAM : std_logic_vector(5 32 List. 1. cd downto 0) := „111001”; constant COMP_RAM_ACC : std_logic_vector(5 downto 0) := „111010”; constant COMP_ROM_ACC : std_logic_vector(5 downto 0) := „111011”; constant COMP_ROM_RAM : std_logic_vector(5 downto 0) := „111100”; constant INC_ACC : std_logic_vector(5 downto 0) := „111110”; constant INC_RAM : std_logic_vector(5 downto 0) := „111111”; constant ACALL : std_logic_vector(4 downto 0) := „10001”; constant ADD_A_RR : std_logic_vector(4 downto 0) := „00101”; constant ADD_A_D : std_logic_vector(7 downto 0) := „00100101”; constant ADD_A_ATRI : std_logic_vector(6 downto 0) := „0010011”; constant ADD_A_DATA : std_logic_vector(7 downto 0) := „00100100”; constant ADDC_A_RR : std_logic_vector(4 downto 0) := „00111”; constant ADDC_A_D : std_logic_vector(7 downto 0) := „00110101”; constant ADDC_A_ATRI : std_logic_vector(6 downto 0) := „0011011”; constant ADDC_A_DATA : std_logic_vector(7 downto 0) := „00110100”; constant AJMP : std_logic_vector(4 downto 0) := „00001”; constant ANL_A_RR : std_logic_vector(4 downto 0) := „01011”; constant ANL_A_D : std_logic_vector(7 downto 0) := „01010101”; constant ANL_A_ATRI : std_logic_vector(6 downto 0) := „0101011”; constant ANL_A_DATA : std_logic_vector(7 downto 0) := „01010100”; constant ANL_D_A : std_logic_vector(7 downto 0) := „01010010”; constant ANL_D_DATA : std_logic_vector(7 downto 0) := „01010011”; constant ANL_C_BIT : std_logic_vector(7 downto 0) := „10000010”; constant ANL_C_NBIT : std_logic_vector(7 downto 0) := „10110000”; constant CJNE_A_D : std_logic_vector(7 downto 0) := „10110101”; constant CJNE_A_DATA : std_logic_vector(7 downto 0) := „10110100”; constant CJNE_RR_DATA : std_logic_vector(4 downto 0) := „10111”; constant CJNE_ATRI_DATA: std_logic_vector(6 downto 0) := „1011011” ; constant CLR_A : std_logic_vector(7 downto 0) := „11100100”; constant CLR_C : std_logic_vector(7 downto 0) := „11000011”; constant CLR_BIT : std_logic_vector(7 downto 0) := „11000010”; constant CPL_A : std_logic_vector(7 downto 0) := „11110100”; constant CPL_C : std_logic_vector(7 downto 0) := „10110011”; constant CPL_BIT : std_logic_vector(7 downto 0) := „10110010”; constant DA_A : std_logic_vector(7 downto 0) := „11010100”; constant DEC_A : std_logic_vector(7 downto 0) := „00010100”; constant DEC_RR : std_logic_vector(4 downto 0) := „00011”; constant DEC_D : std_logic_vector(7 downto 0) := „00010101”; constant DEC_ATRI : std_logic_vector(6 downto 0) := „0001011”; constant DIV_AB : std_logic_vector(7 downto 0) := „10000100”; constant DJNZ_RR : std_logic_vector(4 downto 0) := „11011”; constant DJNZ_D : std_logic_vector(7 downto 0) := „11010101”; constant INC_A : std_logic_vector(7 downto 0) := „00000100”; constant INC_RR : std_logic_vector(4 downto 0) := „00001”; constant INC_D : std_logic_vector(7 downto 0) := „00000101”; constant INC_ATRI : std_logic_vector(6 downto 0) := „0000011”; constant INC_DPTR : std_logic_vector(7 downto 0) := „10100011”; constant JB : std_logic_vector(7 downto 0) := „00100000”; constant JBC : std_logic_vector(7 downto 0) := „00010000”; constant JC : std_logic_vector(7 downto 0) := „01000000”; constant JMP_A_DPTR : std_logic_vector(7 downto 0) := „01110011”; constant JNB : std_logic_vector(7 downto 0) := „00110000”; constant JNC : std_logic_vector(7 downto 0) := „01010000”; constant JNZ : std_logic_vector(7 downto 0) := „01110000”; constant JZ : std_logic_vector(7 downto 0) := „01100000”; constant LCALL : std_logic_vector(7 downto 0) := „00010010”; constant LJMP : std_logic_vector(7 downto 0) := „00000010”; constant MOV_A_RR : std_logic_vector(4 downto 0) := „11101”; List. 1. cd constant MOV_A_D : downto 0) := „11100101”; constant MOV_A_ATRI : downto 0) := „1110011”; constant MOV_A_DATA : downto 0) := „01110100”; constant MOV_RR_A : downto 0) := „11111”; constant MOV_RR_D : downto 0) := „10101”; constant MOV_RR_DATA : downto 0) := „01111”; constant MOV_D_A : downto 0) := „11110101”; constant MOV_D_RR : downto 0) := „10001”; constant MOV_D_D : downto 0) := „10000101”; constant MOV_D_ATRI : downto 0) := „1000011”; constant MOV_D_DATA : downto 0) := „01110101”; constant MOV_ATRI_A : downto 0) := „1111011”; constant MOV_ATRI_D : downto 0) := „1010011”; constant MOV_ATRI_DATA : downto 0) := „0111011”; constant MOVC_A_ATDPTR : downto 0) := „10010011”; constant MOVC_A_ATPC : downto 0) := „10000011”; constant MOVX_A_ATRI : downto 0) := „1110001”; constant MOVX_A_ATDPTR : downto 0) := „11100000”; constant MOVX_ATRI_A : downto 0) := „1111001”; constant MOVX_ATDPTR_A : downto 0) := „11110000”; constant MOV_C_BIT : downto 0) := „10100010”; constant MOV_BIT_C : downto 0) := „10010010”; constant MOV_DPTR_DATA : downto 0) := „10010000”; constant MUL_AB : downto 0) := „10100100”; constant NOP : downto 0) := „00000000”; constant ORL_A_RR : downto 0) := „01001”; constant ORL_A_D : downto 0) := „01000101”; constant ORL_A_ATRI : downto 0) := „0100011”; constant ORL_A_DATA : downto 0) := „01000100”; constant ORL_D_A : downto 0) := „01000010”; constant ORL_D_DATA : downto 0) := „01000011”; constant ORL_C_BIT : downto 0) := „01110010”; constant ORL_C_NBIT : downto 0) := „10100000”; constant POP : downto 0) := „11010000”; constant PUSH : downto 0) := „11000000”; constant RET : downto 0) := „00100010”; constant RETI : downto 0) := „00110010”; constant RL_A : downto 0) := „00100011”; constant RLC_A : downto 0) := „00110011”; constant RR_A : downto 0) := „00000011”; constant RRC_A : downto 0) := „00010011”; constant SETB_C : downto 0) := „11010011”; constant SETB_BIT : downto 0) := „11010010”; constant SJMP : downto 0) := „10000000”; constant SUBB_A_RR : downto 0) := „10011”; constant SUBB_A_D : downto 0) := „10010101”; constant SUBB_A_ATRI : downto 0) := „1001011”; constant SUBB_A_DATA : downto 0) := „10010100”; constant SWAP_A : downto 0) := „11000100”; constant XCH_A_RR : downto 0) := „11001”; constant XCH_A_D : downto 0) := „11000101”; constant XCH_A_ATRI : downto 0) := „1100011”; constant XCHD_A_ATRI : downto 0) := „1101011”; constant XRL_A_RR : downto 0) := „01101”; constant XRL_A_D : downto 0) := „01100101”; constant XRL_A_ATRI : downto 0) := „0110011”; constant XRL_A_DATA : downto 0) := „01100100”; constant XRL_D_A : downto 0) := „01100010”; constant XRL_D_DATA : downto 0) := „01100011”; std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(4 std_logic_vector(4 std_logic_vector(4 std_logic_vector(7 std_logic_vector(4 std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(6 std_logic_vector(6 std_logic_vector(6 std_logic_vector(7 std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(4 std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 std_logic_vector(4 std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(7 std_logic_vector(4 std_logic_vector(7 std_logic_vector(6 std_logic_vector(6 std_logic_vector(4 std_logic_vector(7 std_logic_vector(6 std_logic_vector(7 std_logic_vector(7 std_logic_vector(7 Elektronika Praktyczna 3/2005 System-on-Chip Rys. 7. Jako zewnętrzną pamięć programu można wykorzystać pamięć PSM (Flash z JTAG-iem) produkowaną przez firmę STMicroelectronics - uzyskane częstotliwości sygnałów zegarowych nie są oszałamiające, - zaimplementowany mikrokontroler wymaga zaskakująco wielu wyprowadzeń. Czyli same wady? Otóż nie: w odróżnieniu od standardowych wersji mikrokontrolerów, mikrokontroler implementowany w układzie PLD oferuje niespotykane możliwości, które bez cienia przesady można porównać z możliwościami jakimi dysponują twórcy ASIC-ów (czyli układów projektowanych i budowanych pod kątem wymagań określonej aplikacji). Co więcej, elastyczność projektów realizowanych w układach PLD jest tak duża, że samodzielne zbudowanie „prawdziwego” układu System-on-Chip (czyli integrującego nie tylko procesor ze standardowymi peryferiami, lecz także wszelkie inne peryferia cyfrowe niezbędne do obsługi aplikacji) jest bez żadnych ograniczeń możliwa. Konieczna jest, co prawda, umiejętność posługiwania się którymś z języków HDL i programami narzędziowymi służącymi do implementacji projektów w układach PLD (jak na przykład WebPack ISE lub Quartus II), ale możliwość zintegrowania kompletnego „urządzenia” w pojedynczym układzie scalonym jest więcej niż atrakcyjna… Implementacja mikrokontrolera Oregano MC8051 w układach Spartan 3 firmy Xilinx Pierwszy praktyczny test przeprowadzono na układzie XC3S200 z rodziny Spartan 3 firmy Xilinx. Do implementacji rdzenia zastosowano bezpłatny program narzędziowy - WebPack ISE (rys. 4) w wersji 6.3i. Strukturę projektu uzyskaną w wyniku syntezy opisów Elektronika Praktyczna 3/2005 HDL przedstawiono na rys. 5, a widok zależności pomiędzy plikami w hierarchii projektu na rys. 6. Ponieważ zbudowanie projektu na bazie opisu dostarczonego przez firmę Oregano wymaga zabiegów nieco wykraczających poza standardową obsługę pakietu WebPack ISE, kompletny projekt ze wszystkimi źródłami i prostymi programami testowymi (optymalizowanymi dla symulatora ModelSIM firmy Mentor Graphics) publikujemy na CD-EP3/2005B. Testy funkcjonalne mikrokontrolera przeprowadzono przy częstotliwości sygnału zegarowego 20 MHz, na płytce Spartan 3 Starter Kit. Zasoby układu XC3S200 umożliwiły implementację mikrokontrolera bez pamięci programu i danych, ale zadania te doskonale spełniły zewnętrzne pamięci SRAM zainstalowane na płytce zestawu. Alternatywnym rozwiązaniem jest zastosowanie własnych pamięci Flash/EPROM/EEPROM i SRAM, które można dołączyć do złącz w jakie wyposażono płytkę zestawu Spartan 3 Starter Kit. Ze względu na łatwość programowania w systemie (ISP), w testowanym projekcie zastosowano pamięci Flash z interfejsem JTAG firmy STMicroelectronics (rodzina PSM). Schemat ilustrujący sposób zastosowania pamięci PSD835G2V (w jednej strukturze zintegrowane: pamięć Flash o pojemności 32 kB, pamięć Flash o pojemności 512 kB, pamięć SRAM o pojemności 8 kB i uniwersalna struktura CPLD) pokazano na rys. 7. Na rys. 8 pokazano schemat blokowy ilustrujący budowę i sposób zastosowania pamięci PSD. Rys. 8. Schemat blokowy pamięci PSM Tab. 3. Opis wyprowadzeń mikrokontrolera Oregano MC8051 Nazwa sygnałów clk reset p3_o…p0_o Opis Sygnał zegarowy Sygnał zerujący mikrokontroler Wyjścia portów P3…P0 p3_i…p0_i Wejścia portów P3…P0 all_rxdwr_o all_txd_o all_rxd_o all_rxd_i all_t0_i, all_t1_i int0_i, int1_i rom_data_i rom_adr_o ram_data_o ram_data_i ram_adr_o datax_i datax_o adrx_o ram_en_o ram_wr_o wrx_o Wyjście sygnalizujące kierunek transmisji przez dwukierunkową linię rxd (wyjście = 1) Szeregowe wyjście danych UART-a Wyjście danych via linię rxd w trybie 0 UART Szeregowe wejście danych UART-a Wejścia sterujące liczników-timerów Wejścia przerwań 8-bitowe wejście danych z pamięci ROM 16-bitowy adres pamięci ROM 8-bitowe wyjście danych do pamięci RAM 8-bitowe wejście danych z pamięci RAM 7-bitowy adres pamięci RAM 8-bitowe wejście danych z pamięci XRAM 8-bitowe wyjście danych do pamięci XRAM 16-bitowy adres pamięci XRAM Sygnał uaktywniający pamięć RAM Sygnał zapisu do pamięci RAM Sygnał zapisu do pamięci XRAM 33 System-on-Chip Rys. 9. Widok okna systemu Quartus II nym zakończeniu kompilacji Implementacja mikrokontrolera Oregano MC8051 w układach Cyclone firmy Altera Ze względu na zbyt małe zasoby logiczne układu EP1C3 zastosowanego na płytce ZL2PLD, zaimplementowanie rdzenia Oregano MC8051 okazało się trudniejsze niż we wcześniej opisanym przypadku układu XC3S200. Z tego powodu konieczne było ograniczenie implementowanej części mikrokontrolera do samego rdzenia (bez timerów i UART-ów), co pozwoliło częściowo zweryfikować poprawność jego działania. Ponieważ w praktycznych aplikacjach taki zabieg nie ma większego sensu, na CD-EP3/2005B publikujemy projekt przygotowany dla układów PLD o większych zasobach logicznych niż EP1C3. Do implementacji rdzenia mikrokontrolera w układzie PLD zastosowano system projektowy (dostępny bezpłatnie) Quartus 4.1 SP2 (rys. 9). Ze względu na fakt, że Quartus ma wbudowany niezłej klasy symulator, nie ma konieczności korzystania z umieszczonych na płycie CD-EP3/ 2005B plików symulacyjnych zapisanych w języku VHDL. Konfiguracja rdzenia Jak wspomniano w poprzedniej części artykułu, projektant może samodzielnie określić liczbę zaimplementowanych timerów, UART-ów oraz przerwań zewnętrznych. Do wersji rdzenia 1.4 nie ma możliwości niezależnego modyfikowania liczby tych bloków, można zmieniać liczbę implementowanych grup peryferiów składająca się z : dwóch timerów, jednego UART-a i dwóch wejść przerwań zewnętrznych. Za 34 ustalenie liczby tych elementów odpowiada predefiniowana stała C_IMPL_N_TMR, której wartość jest określana w pliku mc8051_p.vhd. W interesujący sposób twórcy rdzenia Oregano MC8051 rozwiązali problem adresowania rejestrów dodatkowych timerów i UART-ów. W obszarze SFR (Special Function Register) wykorzystano dwa adresy: - 0x8E (rejestr o napo pomyślzwie TSEL) do adresowania rejestrów danych TCON timerów - rys. 10, - 0x9A (rejestr o nazwie SSEL) do adresowania rejestrów danych UART-ów. W podobny sposób można wpłynąć na implementację niektórych rzadziej stosowanych rozkazów. Jeżeli rozkazy DIV, MUL i DA nie muszą być implementowane, to można przypisać stałym C_IMPL_DIV, C_IMPL_MUL i C_IMPL_DA (odpowiednio) wartości 0. W przeciwnym przypadku odpowiedniej zmiennej należy przypisać wartość 1. Wyprowadzenia mikrokontrolera Podsumowanie Samodzielna implementacja mikrokontrolera w układzie FPGA jest zadaniem dość skomplikowanym i wymagającym dobrej znajomości zarówno narzędzi projektowych, jak i samych układów FPGA. Wszystkich Czytelników, którzy takich doświadczeń nie mają, zachęcam najpierw do samodzielnych ćwiczeń z projektami o mniejszym stopniu skomplikowania, co pozwoli zminimalizować ryzyko kosztownego rozczarowania. Proste testy funkcjonalne przeprowadzone przez autora wykazały (programy pisane w Basicu, kompilowane za pomocą ewaluacyjnej wersji Bascoma), że mikrokontroler Oregano MC8051 pracuje poprawnie, ale należy liczyć się z możliwością wystąpienia błędów - twórcy rdzenia wyraźnie zastrzegają to sobie w licencji. Z tego powodu nie zalecamy stosowania tego rdzenia w produktach, których błędne działanie może mieć kardynalne skutki. Czytelników chcących zaryzykować poznanie nowej architektury mikrokontrolerów RISC, które można zastosować w znacznie mniejszych i przez to łatwiej dostępnych układach FPGA zapraszam do majowego wydania EP - na fanów tanich SoC-ów będzie czekać interesująca niespodzianka. Piotr Zbysiński, EP [email protected] Budowa portów I/O mikrokontrolera Oregano MC8051 jest nieco inna niż w wersjach klasycznych co powoduje, że na wyprowadzeniach układu FPGA są niezależnie dostępne linie wejściowe i wyjściowe. Schemat blokowy linii I/O pokazano w p ierw szej części artykułu. Tak przygotowany opis umożliwia łatwą implementację rdzenia w dowolnym (w sensie architektry) układzie FPGA, ale nic nie stoi na przeszkodzie aby linie wyjściowe i wejściowe połączyć ze sobą poprzez łączenie par wyprowadzeń układu lub - jeżeli umożliwia to budowa komórek I/O układu FPGA łączenie ich wewnątrz układu, co wydatnie zmniejszy liczbę niezbędnych wyprowadzeń układu docelowego. W tab. 3 zestawiono sygnały mikrokontrolera Oregano Rys. 10. Ilustracja sposobu dostępu do doMC8051 oraz skrócone opisy datkowych timerów zaimplementowanych ich funkcji. wraz z rdzeniem mikrokontrolera Elektronika Praktyczna 3/2005