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