Zpět na seznam článků     Číst komentáře (10)     Verze pro tisk

Úvod do jednočipů

Autor: dex/THS   
30.10.2007

Jsem si všiml, že by někdo chtěl článek o programování jednočipů. Nepochybuju o tom, že se tu nějaký takový objeví, možná, že jich bude dokonce víc - tak v předstihu přidávám jakýsi teoretický úvod, i když většina z vás asi dobře ví, o čem a proč je řeč. Pro nízkou informační hodnotu dávám článek do usertextů :)


Jednočipy, jednočipové počítače (anglicky microcontrollers, tedy ošklivé slovo "mikrokontroléry") jsou, jak český název přesně říká, počítače integrované v jednom čipu.
Jde o to, že jeden čip (integrovaný obvod, to je ta křemíková věc v keramickém pouzdře s nožičkami) v sobě obsahuje skoro všechno potřebné pro provoz, tedy jak procesorové jádro, tak paměť volatilní (její obsah nepřežije vypnutí) a nonvolatilní (jejíž obsah vypnutí počítače přežije), a dále různé časovače a řadiče pro vstupy a výstupy (sériové, případně paralelní, AD/DA převodníky pro zpracování nebo generování analogového signálu, nebo zabudovaný ethernet, IrDA kontrolér, DMA). V hodně jednoduchém případě přidáte jen časovací krystal (nebo ho počítač taky obsahuje v pouzdře), zdroj napájení, nějak počítač naprogramujete a můžete fungovat.
Vzhledem k tomu, že současná technika umožňuje velký stupeň integrace, dá se na dostatečně velký křemík naflákat i celé PC (třeba i80386 s celým chipsetem, který normálně najdete kolem procesoru roztoulaný na motherboardu). Kdysi dříve bývaly z technických důvodů jednočipové počítače jednodušší a jaksi méně výkonné než velké "opravdové počítače", ale dneska, kdy lze do jednoho pouzdra narvat výkonný RISCový procesor se všemi potřebnými periferiemi, se rozdíly trochu stírají - například procesory PowerPC najdete jak ve stolních počítačích a serverech (od Genesi nebo Apple), tak i v úloze jednočipů v embedded řešeních.
Ve většině případů ale pro jednočipové počítače stále platí, že vstupy a výstupy mají poměrně pevně dané a v limitovaném počtu, paměť je relativně malá, a to jak nonvolatilní pro samotný program (ta bývá typicky v kilobajtech), tak především volatilní (RAM) pro uložení dat a proměnných za provozu - ta se často udává spíše v bajtech.

Jak se jednočipy programují?

Tak jde to ve strojovém kódu. Protože "kompilace" rukou na papír a datlování dat do počítače v hexa číslech není nejlepší způsob, jak strávit mládí nebo aspoň pracovní dobu, je nejlepším způsobem, jak jednočipy programovat, použití jazyka symbolických adres - assembleru.
Český název napovídá, že místo přepočítávání adres skoků a čtení do/z paměti se používá pro člověka čitelnější nahrazení adres symbolem - návěštím, kterému pak překladač dosadí odpovídající adresu.
Jednoduše řečeno, program bez použití návěští by vypadal asi takhle:
345 in a,(235)
347 jz 345
Je tomu dobře rozumět? Moc ne. Vidíme jen načtení hodnoty z portu 253 a podmíněný skok na adresu 345. Díky tomu, že jsem adresy uvedl v poli návěští, tak vidíte, že se skáče zpět na čtení portu, ale způsob, jak zajistit, aby adresa v operandu instrukce odpovídala opravdu cíli, je jen pracné ruční ladění. No a jak by to vypadalo s pomocí návěští?
.org 345
port_pro_cteni_teploty:=235
opakuj_cteni_teploty: in a,(port_pro_cteni_teploty)
jz opakuj_cteni_teploty
Vypadá to ukecaně - pro lepší pochopení jsem použil hodně dlouhé názvy návěští. V praxi se obvykle používaí názvy mnohem kratší.
První pseudoinstrukcí (nejde o povel pro procesor, v přeloženém kódu se vůbec neobjeví, jde jen o informaci pro překladač) se určí nastavení ukazatele adres, tedy že se bude překládat od adresy 345.
V textu se objevují definice dvou návěští: jednak definice konstanty port_pro_cteni_teploty, kterou si jednou nadefinujeme a v dalším textu se už na ni jenom odkazujeme (a v případě, že při konstrukci počítače pověsíme teploměr na jiný port, nám v programu stačí změnit jediné místo - definici - a nemusíme pracně hledat, kde všude v programu se nám vyskytuje číslo 253 a zda je použito jako číslo portu, nebo za jiným účelem). Navíc nám už první pohled na jinak nepochopitelnou instrukci napovídá, co že to vlastně děláme. Použití předdefinovaných konstant je dobrý programátorský zvyk!
Podobně je to s návěštím opakuj_cteni_teploty, kdybychom jakkoliv měnili umístění začátku kódu (třeba změnou v pseudoinstrukci .org), počítač bude vždy skákat na opakování čtení teploty, ať už bude přeloženo na adresu 345 nebo úplně jinou.

Proč jsem napsal, že assembler je nejlepším způsobem, jak jednočipy programovat?
Rozhodně to není způsob jediný. Musíme si přiznat, že pro jednočipy se dá programovat v téměř libovolném jazyce, pokud máte kompilátor, který překládá do strojového kódu vámi použitého jednočipu.
Takže jednočipy lze programovat například v C, Basicu, Forthu, Pascalu (možná i Forthu, teď si nejsem jist, zda jsem takový překladač viděl). Interpretované nebo skriptovací kazyky se běžně nepoužívají, bylo by totiž nutné mít v jednočipu interpret daného jazyka a k němu navíc nahrávat zdrojový text vašeho programu (přesto například BasicSTAMP od Parallaxu umožňuje spouštět interpretované programy v Basicu). Dále existuje možnost programovat ve zvláštních jazycích, které výrobci vymysleli pro své systémy na míru, a mají buď umožňovat programování paralelních procesů, kde se zadané činnosti vykonávají na základě procházení podmínkových tabulek (konečný stavový automat), nebo se některé aplikace dají programovat i "vizuálně" propojováním jednotlivých funkčních bloků nebo kreslením logických schémat (Siemens Logo - http://www1.conrad.de/xl/1000_1999/1900/1980/1982/198267_BB_00_FB.EPS.jpg ).
Použití C nebo jiného vyššího jazyka s sebou přináší několik výhod, a pochopitelně nevýhod. Například, odpadá nutnost učit se instrukční soubor daného procesoru. Stačí umět daný jazyk a osvojit si knihovní funkce nebopovely specifické pro daný kompilátor a cílovou platformu. Díky vyšší úrovni jsou v programu přehledněji patrné funkční a logické celky, jako programové bloky, cykly, skoky a podmínky, než při použití asembleru. Na druhou stranu, jste závislí na tom, jak dobře ovládá daný překladač instrukční sadu daného procesoru (umí-li využít podle situace celou nabídku existujících instrukcí) jste závislí na konstrukcích, které daný přeladač vytvoří (proměnné, objekty, ...) a musíte počítat s tím, že vykonávání programu psaného ve vyšším jazyce bude obecně pomalejší a že program sám zabere více místa v cenné paměti - i když tyto nevýhody se stírají při použití jazyků bližších hardwaru, jako je C, naopak vyniknou při použití například Basicu nebo Pascalu. Dnešní hardware sice dosahuje vysokých výpočetních výkonů a i velikostí paměti máme na výběr, ale historicky je lepší počítat s tím, že jednočip zdaleka nedosahuje schopností mainframu.
Použití vyššího programovacího jazyka nebo jiného prostředku trochu připomíná situaci, kdy mám benzinový motor 50 ccm (jednočipový mokropočítač omezených možností a schopností, například s omezenou pamětí nebo limitním adresovým prostorem provstupy a výstupy), a na to, abych ho nějak využil, si pořídím podvozek a kastli Ferrari (vyšší programovací jazyk) a divím se, že to málo táhne a nevyjede kopec. Přitom při osazení do mopedu se můžu proplétat uličkami, kudy Ferrari neprojede, nebo mezi stromy - čímž oproti Ferrari, byť s pořádným dvanáctiválcem, dostávám jakousi přidanou hodnotu.
Při použití assembleru máme největší kontrolu nad tím, co jednočip dělá, a hlavně, jak to dělá (pochopitelně tak, jak jsme mu to do programu napsali). Máme přímou kontrolu nad vstupy i výstupy, nad umístěním čehokoliv v paměti, sami si zařizujeme obsluhu přerušení.

He, oč jde při přerušení? Jde o signál procesoru od periferií. Dělí se na maskovatelné (jde softwarově zakázat) a nemaskovatelné (nejde zakázat softwarově). Pokud se tento signál objeví (a pokud v případě maskovatelného přerušení není toto zakázáno), provede se programový skok na definovanou adresu (většinou je v čipu pevně zadrátováno, kde v paměti se toto místo nachází). Na toto místo (bývá více než jedno, obvykle je jiné pro maskovatelné a nemaskovatelné, nebo se liší podle priority přerušení) umisťujeme kód, který se při příchodu přerušení provede. Může to být signál od periferie, například že jsou připravena na vstupu data ke čtení, může to být periodické přerušení vyvolávané čsovačem (typicky každou padesátinu vteřiny), může to být třeba signál napojený na nouzové tlačítko (při ovládání strojů je dobré pověsit ho na nemaskovatelné přerušení a do příslušného kódu naprogramovat zastavení všech motorů stroje).

Architektura jednočipů se dělí na von Neumannovskou a harvardskou. Oč jde?

Von Neumannovské počítače mají "jednu paměť", jejíž obsah lze interpretovat jako programový kód nebo data podle kontextu. Pokud máte v paměti třeba nějaký text, a necháte program skočit "do" tohoto textu, počítači je jedno, jaký má text význam pro vás, a snaží se ho interpretovat jako instrukce (nejspíše nesmyslné), a ty provádět (a velmi pravděpodobně dojde ke zhroucení nebo zbloudění systému). Výhodou tohoto schematu je například možnost snadno vytvářet samomodifikující kód nebo ukládat data rovnou do operandů jednotlivých instrukcí, čímž se šetří místo v paměti, například u čítačů. Asi takhle (známe-li délku operačního kódu instrukce):
citac: ld a,0
inc a
ld (citac+1),a
Tím dosáhneme toho, že se přímo v programovém kódu instrukce ld a,0 změní na ld a,1, a při dalších průchodech sebude dále zvyšovat. A to nemluvím o možnosti za běhu programu měnit například instrukci inc za dec nebo nop, a tak hodnotu čítače nejenom zvyšovat, ale i snižovat či ponechávat nezměněnou. Změny programového kódu ale u jednočipů, kde program je umístěn v nezapisovatelné paměti, v praxi nevyužijeme (pokud není možné umístit kód do RAM a skočit na něj tam).

Harvardská architektura paměť rozděluje na dvě části. To, co je v jedné, počítač považuje z ainstrukce, a odmítá je interpretovat jako data, to, co je v druhé části, jsou prostě data, a nemohou být vykonávána jako program. Nevýhodou je obtížná modifikace existujícího programu, výhodou je velká bezpečnost uloženého programu - velmi obtížně se stane, že by se nějakým omylem nebo chybou přepsal nějakými nesmysly. Hardwarově to ovšem předpokládá existenci dvou oddělených sběrnic, což zvyšuje na jednu stranu složitost zařízení, na druhou stranu může být šířka obou sběrnic různá a optimalizovaná pro očekávaný adresní rozsah, dále může procesor číst z obou sběrnic zároveň instrukci i data k ní patřící.

Ještě jedno si o jednočipech řekneme - stejně jako "velké" procesory, i jednočipy si můžeme rozdělit na ty, které mají jeden společný adresový prostor, a na ty, které mají oddělený I/O a adresový prostor.

Jednoduše řečeno, procesory s jednotným adresovým prostorem prostě nerozlišuje mezi pamětí a periferními zařízeními (paralelní a sériové linky, triaky, převodníky, disky, ...). Prostě zapíše nebo přečte obsah dané adresy a to, zda jde o buňku paměti nebo vstupně/výstupní zařízení, je dáno adresovým dekodérem - tedy logikou, která je přidrátovaná mezi adresovou sběrnicí procesoru a dané periferie nebo pamětí. Výhodou je menší komplikovanost hardwaru samotného procesoru a menší počet vývodů, menší instrukční sada, nevýhodou potom může být, při zápisu na nesprávnou adresu nebo při přetečení zásobníku nebo bufferu, nebezpečí zápisu na nějakou periferii (asi byste nechtěli, aby se vám při spadnutí programu na diskový řadič vyslal nějakým omylem povel "všechno smazat").

Procesory s odděleným adresním prostorem si vyžádal historický kontext tím, že se předpokládalo, že periferie jsou pomalejší než paměť. To si vynutilo oddělení rychlých a pomalých (uměle doplněných o čekací smyčku) instrukcí pro čtení a zápis. S tím pak byly spojeny signály "navíc", které určovaly, zda se pracuje s pamětí nebo periferiemi (memrq / iorq). Na sběrnici tak mohly stejnou adresu sdílet jak paměťová buňka (aktivovaná při signálu memrq), tak periferie (aktivovaná při signálu iorq) a tak vlastně existuje paralelní adresní prostor paměťový a prostor periferií. Výhodou je jakási vyšší bezpečnost - pokud zapisuju do paměti, nedostane se informace na výstupy (výjimkou je takzvaný memory-maping periferií, kdy je periferie naschvál aktivovaná signálem memrq, aby bylo možno použít rychlejší instrukce určené pro přístup do paměti a zrychlit tak přenos dat - bylo tak řešeno například připojení disků u systému MSX nebo tak Eda Smutný zkonstruoval československý počítač SAPI-1).

Pochopitelně i u jednočipů existuje dělení na CISC procesory s "úplnou" instrukční sadou (tím se myslí, že jednou instrukcí se snažíme udělat co nejvíce věcí, čímž se má dosánout toho, že určité činnosti dosáhneme poměrně krátkým programem, délka instrukcí bývá proměnlivá podle délky operandů nebo prefixů, takže dekódování zabere určitý čas, podle délky instrukce pokaždé jiný, a určité instrukce se provádějí s dedikovanými registry - například pouze s akumulátorem, pouze na indexregistrech, se zásobníkem) a na RISC procesory s "redukovanou" instrukční sadou - která přitom ale může být rozsáhlá, stejně jako instrukční set CISCumůže být velmi chudý (RISC se pokouší činnosti provádět pomocí jednoduchých základních instrukcí, ze kterých teprve skládáme složitější děje, mívá instrukce balené s operandy do celků jednotné délky - takže instrukce zabere například 4 bajty, a to vždy bez ohledu na to, zda s sebou bere operand nebo ne - čímž se má urychlit provádění tím, že procesor nemusí bádat nad tím, kolik bajtů instrukce načíst, a typicky jsou si registry rovnocenné, takže operace lze provádět s jakýmkoliv registrem, dokonce u některých procesorů lze jakýkoliv registr použít jako zásobník).

Jaké jednočipy si můžeme vybrat?

Historicky populární intelácké jednočipy i8048 (zvykem bývá číslovat jednočipy různě podle velkosti osazených pamětí a zabudovaných periferií, takže třeba 8035 má stejné jádro jako 8048, jen jiné vybavení) s poměrně chudým instrukčním souborem a nízkým výkonem už dnes běžně nepotkáte, jejich klony ale vyráběla Tesla a tak je můžete najít v hromádkách starých součástek, ve straých modemech nebo jako řadiče klávesnic.

8051 postavené na harvardské architektuře jsou jednočipy dnes už taky zastaralé, vyráběly se ale v široké škále výbav, verze s převodníky (hlavně od Phillipsu) můžete najít v televizích. Výkonově předčily 8048, která opravdu moc neuměla, a tak se používaly dlouhá léta. Naučit se 8051 assembler není k zahození ani dnes, přežívá v některých výrobcích Atmelu.

Atmel vyrábí vlastně jednočipy několika odlišných typů. Jednak jsou to jednoduché čipy založené na 8051 architektuře.

Potom jsou to čipy AT91 založené na RISCové architektuře ARM. Historie tohoto procesoru je velice zajímavá. V době, kdy výrobci osobních počítačů přecházeli z osmibitů na šestnácti- a 32bity (například Atari, Commodore a Apple z 6502 na 68k Motorolu), britská firma Acorn se rozhodla vyvinout si procesor svůj - prostě koupili nástroje na návrh procesorů a do dvou měsíců meli hotový návrh 32 bitového procesoru, který výkonem předčil všechna očekávání. Dostal název Acorn RISC machine - ARM, a uveden byl v pracovních stanicích a školních počítačích Acorn Archimedes. Platforma (s operačním systémem s grafickým rozhraním RiscOS) existuje dodnes pod názvem RiscPC, ovšem již oddělena od Acornu. Ten založil ARM konsorcium a poskytuje licence na ARM architekturu (neustále vyvíjenou a vylepšovanou) výrobcům procesorů. Typicky se ARMy osazují pro svůj slušný výkon a malou spotřebu do přenosných herních konzolí, kapesních počítačů a mobilních telefonů. V licenci je vyrábí i Intel pod názvem XScale (kombinované s méně technicky vyspělou intelovskou architekturou Thumb). ARM má dobře propracovaný assembler, poměrně dobře čitelný i pro člověka, podmíněné vykonávání instrukcí, vzhledem k výkonu je možné programovat na něj s úspěchem i ve vyšších programovacách jazycích, ba dokonce provozovat na něm Javovské aplikace.

Pak produkuje Atmel vlastní architekturu AVR (osmibitový RICS harvardské architektury s registrovou sadou mapovanou do paměti a I/O registry rovněž mapovanými do paměti, nemá zcela rovnocenné registry) a AVR32 (poměrně nový RISC s 16 a 32bitovými instrukcemi, podávající velký výpočetní výkon, v podstatě jde o jakési základní jádro instrukční sady, k němuž se v jednotlivých implementacích přidávají další specializované instrukce podlepotřeb daného procesoru). ARM a AVR32 jsou dostatečně silné a vybavené na to, aby na nich mohl běhat například Linux.

PICy jsou klasicky harvardovské osmi- až 16bitové CISC procesory, byť instrukční sada čítá pouze 35 až 70 instrukcí podle výbavy daného procesoru (proto bývají chybně označovány jako RISC, například na české Wikipedii), s jedním pracovním registrem a náhradou zbytku registrové sady v paměti. Používají pochopitelně vlastní strojový jazyk. Některé PICy měly zabudovaný interpret tokenizovaného Basicu (to jest, příkaz, ve zdrojovém textu se skládající z několika písmen, se uloží kvůli úspoře paměti jako krátký, většinou jednobajtový kód), ale jiné se dají programovat i v C nebo Pascalu.

68HC11 je historický relikt, von Neumannovský potomek procesoru Motorola 6800 a příbuzný procesoru 6502 (6800 měl dva osmibitové akumulátory a jeden indexregistr, náhradu registrové sady v paměti, a 6502 měl jediný akumulátor, dva osmibitové indexregistry a stejnou náhradu registrové sady), 68HC11 má kromě dvou akumulátorů indexregistry dva a navíc šestnáctibitové. Jeho střeva jsou poměrně jednoduchá a k použití si ho vybere především ten, kdo je na danou platformu zvyklý z minulosti.

Podobně je to s procesory Z180 a eZ80, následovníky von Neumannovského procesoru Z80 od Zilogu (který se používal i v "opravdových" počítačích především provozujících operační systém CP/M, a v "Microsoftí" platformě MSX). Z80 vylepšuje starší osmibitový (ovšem vybavený 16bitovou aritmetikou) Intelácký i8080 a jeho instrukční sadu, čítající 256 instrukcí, a to přidáním prefixu - kód instrukce se tak může rozrůst do délky, pokud první bajt kódu instrukce odpovídá prefixu, znamená to, že vlastní tělo kódu instrukce následuje až v dalším bajtu, ale bude se interpretovat jinak, než pokud by kódu prefix nepřecházel, instrukce se tak prodlužuje o jeden (prefixový) bajt. Z80 přinesl zdvojení registrové sady, indexregistry (které nejsou zdvojeny) a bitové operace. A především eZ80 přidává další vylepšení - rozšíření šířky registrů z původních 8 a16 bitů na 24, zvětšení adresovatelného paměťového prostoru a dokonce zabudovaný TCP stack - eZ80 je totiž určen k ovládání strojů a průmyslových zařízení přes webové rozhraní nebo jiným podobným vzdáleným způsobem prostřednictvím sítě (telnet / ssh). Výhodou všech procesorů od Ziogu je poměrně jednoduchá mnemonika instrukcí - původní intelovskou instrukční sadu si značně zjednodušili a tak zlehčili práci programátorů. například instrukce plnění registru čímkoliv původní 8080 rozlišuje na mov, ldax, lda, mvi, stax, sta, lxi, lhld, shld, sphl, zatímco u Zilogu jsou všechny tyhle instrukce shrnuty pod jednotné "ld" a v operandech se jednoduše uvede, KAM se CO má strčit (takže třeba ne zrovna srozumitelná instrukce STAX D dle Intelovské mnemoniky se u Zilogu zapíše jako LD (DE),A - což značí plnění adresy pointerované dvojregistrem DE obsahem akumulátoru A.

Zilog kromě toho vyrábí Z8 Encore!, u nás sice velmi málo známé, ale velice dobré jednočipy s 8bitovým RISC jádrem a modifikovanou harvardskou architekturou. I když vůbec nejsou kompatibilní s rodinou Z80, používají podobně jednoduchou mnemoniku instrukcí. Přechod programátora se zkušenostmi ze Z80 na Z8 tak i přes rozdílnost architektur není obtížný a i pro nováčka není těžké se poměrně jednoduchý zápis assembleru naučit. Jediná věc, která může bránit použití v amatérských konstrukcích, je jejich malá velikost a tak při vekém počtu nožiček na pouzdře ne zrovna jednoduché pájení. Velikosti pamětí se pohybují od 1kB do 64 kB pro FLASH a od 256 bajtů do 4kB pro RAM, s možností připojení externí paměti. Bohatá je i výbava periferií s tříkanálovým DMA - tedy věcí, která zajišťuje rychlý přesun bloků dat z periferie do paměti a obráceně, rychleji, než by to prováděl procesor použitím jednotlivých instrukcí.

Když už byla řeč o ARMech, zmíním ještě architekturu ColdFire, vycházející z 32bitové rodiny CISCových procesorů Motorola 680x0 (použitých ve "velkých" počítačích Sun, Macisntosh, Atari ST, NeXT nebo Amiga), které jsou von Neumannovské koncepce, se společným adresovým prostorem, aumožňují běh kódu v režimu supervizorském (určeno pro běh jádra systému) a uživatelském s omezenými privilegii přístupu do paměti (určeno pro běh alikací, které pak nemají právo měnit paměťový prostor systému a jiných aplikací).

A vrchol všeho, architektura PowerPC, vyráběná ve Freescale sice jako procesor pro embedded řešení, ale jinak jde o procesory 32 až 64 bitové, RISCové, odvozené od mainframových procesorů Power od IBM modifikací pro použití ve stolních počítačích (snížením spotřeby a mírnou úpravou instrukční sady a vektorových jednotek určených pro provádění paralelních výpočtů a operací). Najdete je ve všech PowerMacintoshích a PowerMacech od Apple a serverech od firmy Genesi. Některé vektorovou jednotku nenesou, její použití ale zvyšuje výkon hlavně při multimediálních operacích, a to až 20x (na podobném principu byl založen úspěch počítačů Cray a jsou tak konstruovány i procesory grafických karet). Pochopitelně procesor běhající v blade serveru těžko označíme za jednočip - ovšem s ním příbuzný malý čip s integrovanými periferiemi, běhající v nějaké řídící jednotce, s ním sdílí společné jádro a instrukční sadu. Výkon těchto procesorů je (velmi zhruba) podobný jako u x86 taktované na dvojnásobné frekvenci - tedy deska Efika obsahující vylepšené jádro 603e na 400 MHz odpovídá zhruba výkonově x86 na 800 MHz.

Líbil se Vám článek?
Budeme potěšeni, pokud vás zaujme také reklamní nabídka

Social Bookmarking

     





Hodnocení/Hlasovalo: 1/3

1  2  3  4  5    
(známkování jako ve škole)