Programovatelné IO na mikrořadičích RP2040 v Raspberry Pi Pico

6. 2. 2025
Doba čtení: 23 minut

Sdílet

Dnes se seznámíme s unikátní vlastností Raspberry Pi Pico. Tyto jednodeskové mikropočítače obsahují PIO (Programmable IO), což jsou bloky ovládající vstupně-výstupní piny (GPIO). Ty se chovají jako samostatně programovatelné procesory.

Obsah

1. Programovatelné IO na mikrořadičích RP2040

2. Jednodeskové mikropočítače Raspberry Pi Pico

3. Mikrořadiče RP2040 a RP2350

4. Důvody pro existenci PIO na Raspberry Pi Pico

5. Realizace PIO na čipu RP2040

6. Interní struktura stavových strojů tvořících PIO

7. Výstupní FIFO (fronta) a posuvný registr OSR

8. Vstupní FIFO (fronta) a posuvný registr ISR

9. Čítače počtu posunů registrů ISR a OSR

10. Pracovní registry X a Y

11. Dvojice front (FIFO)

12. Instrukční sada PIO

13. Instrukce skoku (JMP)

14. Instrukce čekání na událost (WAIT)

15. Instrukce IN

16. Instrukce OUT

17. Instrukce PUSH a PULL

18. Instrukce MOV

19. Instrukce SET

20. Odkazy na Internetu

1. Programovatelné IO na mikrořadičích RP2040

Společnost Raspberry Pi Ltd. pravděpodobně není nutné čtenářům Roota podrobně představovat, protože se s jejími jednodeskovými mikropočítači setkal prakticky každý. Po hlavičkou této společnosti vzniklo poměrně velké množství různých desek s mikrořadiči nebo mikroprocesory, z nichž nejznámější je řada Raspberry Pi (od Modelu-A z roku 2012 až k Raspberry Pi 5 vydané předloni). Neméně zajímavá je i řada Raspberry Pi Zero a taktéž Raspberry Pi Pico.

V dnešním článku nás bude zajímat především poslední zmíněná řada, tedy Raspberry Pi Pico, která je založená na mikrořadičích RP2040, resp. RP2350. Interně se sice tyto mikrořadiče od sebe odlišují (mají například odlišná procesorová jádra), ovšem jednu vlastnost mají společnou – obsahují totiž takzvané PIO (Programmable IO), což je označení popisující bloky ovládající vstupně-výstupní piny, na které se můžeme dívat jako na samostatně programovatelné (i když velmi jednoduché) procesory s vlastní instrukční sadou a během nezávislým na hlavním jádru.

V dnešním článku se nejdříve ve stručnosti seznámíme s celou řadou Pi Pico a následně si popíšeme jak funkcionalitu PIO, tak i způsob jejich programování. Přitom použijeme programovací jazyk Python, resp. přesněji řečeno jeho variantu nazvanou MicroPython, kterou lze na Pi Pico bez problémů provozovat. I s MicroPythonem jsme se na stránkách Roota již ve stručnosti seznámili a to konkrétně v článcích [1] [2] [3] [4] a (i když nutno připomenout, že ve zcela odlišném kontextu) i v [5].

Poznámka: může se to sice zdát divné, ale právě spojení MicroPythonu s PIO je poměrně dobrá kombinace. Umožňuje nám využít vysokoúrovňový jazyk a současně programovat i časově kritické operace, a to bez nutnosti použití nativního assembleru (ale i to MicroPython dokáže).

2. Jednodeskové mikropočítače Raspberry Pi Pico

Původní jednodeskový mikropočítač řady Pico se jmenoval jednoduše Raspberry Pi Pico. Jeho oficiální cena byla čtyři dolary a založen byl na mikrořadiči RP2040 s 264kB RAM (SRAM) a 2MB paměti Flash. Mikrořadič RP2040 má jádro ARM Cortex-M0+ s taktovací frekvencí 133MHz, ovšem na Pico je frekvence snížena na 125MHz. Jak kapacita RAM, tak i Flash paměti společně s poměrně vysokým výpočetním výkonem umožňují běh MicroPythonu, i když mnoho projektů se píše v céčku, Rustu nebo dokonce i ve FreePascalu. Dnes nás bude zajímat i to, že původní Pi Pico má 26 GPIO pinů (varianta Pico H je prakticky totožná, až na odlišné piny pro debugging).

O rok později vznikla varianta „W“, přičemž toto písmeno naznačuje, že byl přidán modul pro Wifi. Konkrétně se jedná o čip Infineon CYW43439, díky němuž bylo možné Pico W použít v těch aplikacích, v nichž se nasazovaly konkurenční produkty ESP8266 a ESP32 (ale i mnoho dalších).

Naproti tomu modely Pico 2 a Pico 2 W jsou poněkud odlišné. Především jsou založeny na odlišném čipu, protože RP2040 nahradil výkonnější čip RP2350 a dostupná je i větší kapacita RAM i Flash (u obou se dosahují prakticky dvojnásobné hodnoty kapacity). Ovšem po SW stránce zůstala zachována kompatibilita na úrovni zdrojových kódů s původní řadou Pico, která se navíc stále používá (Pico 2 je dražší a výkon původního Pico pro mnoho aplikací více než dostačuje).

V následující tabulce jsou jednotlivé jednodeskové mikropočítače (mikrořadiče) Pico porovnány:

Označení Vydáno Mikrořadič Wifi RAM Flash
Raspberry Pi Pico 2021 RP2040 ne 264kB 2MB
Raspberry Pi Pico W 2022 RP2040 ano 264kB 2MB
Raspberry Pi Pico 2 2024 RP2350 ne 520kB 4MB
Raspberry Pi Pico 2 W 2024 RP2350 ano 520kB 4MB
Poznámka: původní Pico není s Pico 2 binárně kompatibilní, takže není možné přenášet přeložené binární bloky mezi těmito dvěma sice podobnými, ale nikoli totožnými systémy.

3. Mikrořadiče RP2040 a RP2350

Již z tabulky uvedené ve druhé kapitole je zřejmé, že modely Pi Pico (bez číslovky) a Pi Pico 2 se od sebe v několika ohledech odlišují, což je do značné míry způsobeno použitím odlišných mikrořadičů. Rozdílů je však ještě větší množství a vzhledem k tomu, že se dotýkají i problematiky probírané v dnešním článku (PIO), si tyto rozdíly taktéž vypíšeme. Zajímat nás budou především údaje o PIO:

Čip RP2040 RP2350 Poznámka
Jádra 2×ARM Cortex-M0+ 2×ARM Cortex-M33 + 2×Hazard3 RISC-V volba jader při bootu
FPU ne ano (ARM) pouze jednoduchá přesnost
Frekvence 133 MHz 150 MHz  
(S)RAM 264 kB 510 kB  
OTP paměť × 8 kB používáno například pro klíče při bootu atd.
DMA 12 kanálů 16 kanálů přímý přístup do paměti
IRQ pro DMA 2 4 HW přerušení používaná při DMA
PIO 2 (8 stavových strojů) 3 (12 stavových strojů) bude popsáno dále
PWM 16 24 pulsní šířková modulace
ADC 4 kanály, 12bitů 4 kanály, 12 bitů (popř. 8 kanálů) analogově-digiální převodník
DAC × × digitálně-analogový převodník

4. Důvody pro existenci PIO na Raspberry Pi Pico

Samotné mikrořadiče RP2040 a RP2350 jsou poměrně rychlé, takže mj. umožňují provozovat i interpretované programovací jazyky typu MicroPython (i když i zde lze zařídit překlad vybraných funkcí). Ovšem i přes samotnou rychlost mikrořadičových jader je mnohdy nutné v reálném čase pracovat se vstupně-výstupními piny. Příkladem může být například implementace vlastního sériového přenosu dat bez použití k tomu dedikovaných bloků mikrořadiče.

Samozřejmě je možné napsat subrutiny s realizací tohoto sériového přenosu. Používá se k tomu technika zvaná bit banging, tedy programové ovládání obecných vstupně-výstupních pinů (GPIO). Nicméně se mnohdy jedná o operace, které musí být přesně načasovány a v takových případech nám nemusí hrubá rychlost jader postačovat – záviset budeme (možná) na systému přerušení, na tom, jaké další činnosti jádra provádí, zda se činnost jader nepřerušuje kvůli DMA atd. A už vůbec není možné v těchto případech použít zmíněný MicroPython; vše by muselo být naprogramováno v jazyce C nebo přímo v assembleru.

Řešení spočívá právě ve využití programovatelných IO, kdy se samotná logika ovládání pinů (tj. v tom nejjednodušším případě jejich čtení či zápis) buď kompletně nebo alespoň částečně naprogramuje v PIO tak, že poběží nezávisle na jádrech mikrořadiče. Díky tomu, že v tomto případě přesně známe délku trvání hodinových cyklů, může být časování provedeno tím nejtriviálnějším způsobem – použitím instrukcí nop. V extrémním případě tak jádra nebudou moci vykonávat prakticky žádnou činnost, protože vstupně-výstupní operace poběží přes PIO.

Poznámka: jeden z příkladů sériového přenosu realizovaného přes PIP je dostupný zde.

5. Realizace PIO na čipu RP2040

Podívejme se nyní, jakým způsobem jsou PIO realizovány na čipu RP2040. Tento mikrořadič obsahuje dvojici bloků PIO (RP2350 má tyto bloky tři). Každý blok je tvořen čtveřicí stavových strojů (state machine), které dokážou, nezávisle na ostatních subsystémech, vykonávat krátké specializované programy. Ty jsou uloženy v samostatné paměti (Instruction Memory), která má kapacitu pro až 32 instrukcí. Do stavových strojů se přivádí hodinový signál, který synchronizuje prováděné operace: přečtení, dekódování a vykonání instrukcí. Samotný repertoár dostupných instrukcí je ve skutečnosti velmi krátký, protože obsahuje jen devět instrukcí. Konkrétně se jedná o instrukce IN, OUT, PUSH, PULL, MOV, JMP, WAIT, SET a IRQ. Vstupně-výstupní instrukce mohou pracovat s jakýmkoli GPIO pinem, kterých je na RP2040 celkem 30 (jsou označeny symboly GP0GP29, teoreticky je ovšem k dispozici celkem 32 GPIO). Stavové stroje musí komunikovat i se zbytkem mikrořadiče, což je realizováno přes FIFO (fronty) a systém přerušení.

Interně vypadá propojení stavových strojů s GPIO na straně jedné a s jádry mikrořadiče na straně druhé následovně:

Obrázek 1: Jeden z bloků PIO (druhý má totožnou strukturu)
Zdroj: datasheet k čipu RP2040, dostupné na https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf.

Poznámka: fronty jsou konfigurovatelné, takže je možné například zajistit, aby byly obě použity pro vstup nebo naopak pro výstup (s dvojnásobnou délkou).

6. Interní struktura stavových strojů tvořících PIO

Z prvního obrázku je patrné, že každý blok PIO interně obsahuje čtveřici stavových strojů, které jsou přes celkem osm FIFO připojeny k mikrořadiči a přes mapovací obvod k vybraným GPIO (tedy obecným vstupně-výstupním pinům). Na PIO se můžeme dívat jako na samostatnou výpočetní jednotku (CPU), která je zvláštní tím, že neobsahuje plnohodnotnou aritmeticko-logickou jednotku (a nelze tedy jednoduše realizovat například součet atd.). Interně stavový stroj obsahuje následující bloky:

  1. dva 32bitové posuvné registry ISR a OSR (shift registers) s volitelným směrem posunu
  2. dva 32bitové registry X a Y nazývané scratch registers, ovšem ve skutečnosti mají význam univerzálních registrů
  3. registr PC obsahující index instrukce uložené v paměti nazývané instruction memory
  4. 32bitové fronty (FIFO) pro čtení i zápis dat
  5. dělič hodinového signálu (16+8 bitů)
  6. mapování na GPIO
  7. rozhraní pro DMA
  8. rozhraní pro IRQ (přerušení)
Poznámka: zajímavé je, že celý blok PIO obsazuje plochu čipu, která zhruba odpovídá ploše zabrané řadičem SPI nebo I2C. Ovšem PIO je mnohem flexibilnější než tyto řadiče, navíc může být kdykoli snadno přeprogramován a v určité okamžiky může vykonávat odlišnou činnost.

7. Výstupní FIFO (fronta) a posuvný registr OSRISR

Pro komunikaci mezi mikrořadičem a stavovými stroji se používá dvojice posuvných registrů označovaná zkratkami OSR a ISR, jejichž způsob zapojení i ovládání přes instrukce PIO je nutné znát.

OSR neboli Output Shift Register slouží pro čtení dat posílaných z mikrořadiče do stavového stroje. Pro manipulaci s OSR slouží dvojice PIO instrukcí s mnemotechnickými zkratkami PULL a OUT. Instrukce PULL přečte 32bitové slovo z výstupní FIFO (fronty) a zapíše je do OSR. A instrukce OUT dokáže provést posun bitů v posuvném registru s jejich vysunutím (zápisem) na GPIO, do scratch registrů atd. Funkci si tedy můžeme graficky znázornit následovně:

Obrázek 2: Výstupní FIFO a registr OSR
Zdroj: datasheet k čipu RP2040, dostupné na https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf.

Poznámka: název tohoto registr tedy vznikl na základě toho, jak se chová z pohledu mikrořadiče (jde o výstup). Z hlediska stavového stroje je to naopak vstupní registr.

8. Vstupní FIFO (fronta) a posuvný registr ISR

Posuvný registr ISR neboli Input Shift Register naopak slouží pro (postupné) čtení dat dostupných ve stavovém stroji (GPIO atd.) s jejich posíláním do vstupní FIFO (fronty). Pro ovládání této části se používají PIO instrukce s mnemotechnickými zkratkami IN a PUSH. Instrukce IN do posuvného registru zapíše 1 až 32 bitů (s posunem zbytku) a instrukce PUSH pošle celý obsah ISR do FIFO. Opět platí, že pojmenování jak registru, tak i fronty je provedeno na základě toho, jak se jejich činnost jeví z pohledu mikrořadiče (a nikoli stavového stroje):

Obrázek 3: Vstupní FIFO a registr ISR
Zdroj: datasheet k čipu RP2040, dostupné na https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf.

9. Čítače počtu posunů registrů ISR a OSR

Při posílání dat do nebo z posuvných registrů ISR a OSR instrukcemi IN a OUT se obnovují i takzvané čítače posunů. Jedná se o další pomocné registry, které mohou obsahovat celočíselné hodnoty v rozsahu 0..32 (tedy v rozsahu „žádný posun“ až „posun o celých 32 bitů“). Po každé operaci s posuvným registrem se změní i příslušný čítač. Navíc se může provést jedna z následujících operací:

  1. Registr OSR je automaticky naplněn novou hodnotou (32bitovým slovem) ve chvíli, kdy jeho čítač dosáhne hodnoty 32.
  2. Registr ISR je automaticky vyprázdněn ve chvíli, kdy jeho čítač (odlišný od čítače předchozího) taktéž dosáhne hodnoty 32.

Hodnoty čítačů posunů se nulují operacemi PUSH, resp. PULL, ovšem taktéž instrukcemi MOV OSR, …, resp. MOV ISR, … – to je logické a očekávané chování.

Poznámka: díky těmto čítačům lze relativně snadno realizovat různé formy sériových přenosů; například implementovat SW variantu I2C, klasického UARTu nebo například výstupu pixelů na displej připojený přes vysokorychlostní sériové rozhraní.

10. Pracovní registry X a Y

V každém stavovém stroji (což je, jak již víme, vlastně malý specializovaný CPU), lze manipulovat s dvojicí 32bitových registrů pojmenovaných X a Y. V dokumentaci se tyto registry označují termínem scratch register, což by mohlo naznačovat, že je jejich obsah jen dočasný. Ovšem tyto registry jsou z pohledu stavového stroje plnohodnotnými registry, i když repertoár operací, které s nimi lze provádět, je omezený. Je tomu tak mj. i z toho důvodu, že stavové stroje neobsahují plnohodnotnou ALU, takže například není možné registry X a Y sečíst atd. (na druhou stranu je možné například snížit obsah registru o jedničku, takže lze realizovat počítané programové smyčky).

Tyto registry mohou sloužit jako zdroj nebo naopak cíl dat pro instrukce IN, OUT, SET a MOV. Taktéž mohou sloužit pro vyhodnocení podmínky u skoků, tedy u instrukce JMP.

Poznámka: registr Y je použit i u pseudoinstrukce NOP. Tato instrukce sice v instrukčním souboru PIO neexistuje, ovšem assemblery namísto ní použijí instrukci MOV Y, Y. S touto pseudoinstrukcí se poměrně často setkáme, protože slouží pro časování operací, resp. přesněji řečeno pro vkládání čekacích cyklů mezi jednotlivé operace.

11. Dvojice front (FIFO)

S frontami (FIFO) jsme se již ve stručnosti setkali v předchozím textu. Připomeňme si tedy, že každý stavový stroj obsahuje dvojici front, přičemž jedna fronta je typicky určena pro posílání dat z mikrořadiče do stavového stroje (TX fronta – transmit) a druhá fronta naopak slouží pro posílání dat ze stavového stroje do mikrořadiče (RX fronta – receive). Na straně mikrořadiče je zápis do fronty prováděn například přes DMA. Díky použití front bylo možné činnost mikrořadiče prakticky zcela oddělit od činnosti jednotlivých stavových strojů a do značné míry zajistit jejich nezávislou činnost (souběh či v tomto případě prakticky plný paralelismus). FIFO navíc dokážou generovat signál DREQ, který společně s DMA může zajistit přenosy dat bez toho, aby se musela na straně mikrořadiče spouštět přerušovací subrutina.

Každá z front má kapacitu na čtyři 32bitová slova, což umožňuje nezávislou a nepřerušovanou činnost I/O subsystému i v případě, že mikrořadič provádí jiné činnosti. Ovšem může se stát, že je zapotřebí přes PIO implementovat rychlé přenosy (displej atd.). V takovém případě lze obě fronty spojit a vytvořit jedinou FIFO s kapacitou osmi slov. Taková FIFO ovšem bude jednosměrná – tedy výstupní pro ovládání displeje a naopak vstupní, pokud se například bude přijímat video signál.

12. Instrukční sada PIO

Instrukční sada PIO obsahuje pouze devět instrukcí, ovšem již nyní je nutné upřesnit, že některé z těchto instrukcí jsou poměrně univerzální. Například instrukce JMP díky možnosti zakódování podmínky vlastně odpovídá hned několika instrukcím z jiných instrukčních sad. Totéž platí například i o instrukci MOV, která taktéž dokáže během přenosu dat provádět jednoduché operace. Všechny instrukce mají jednotnou šířku šestnácti bitů, přičemž první tři bity obsahují operační kód instrukce a dalších pět bitů pak počet zpožďovacích cyklů. Do zbylých osmi bitů jsou zakódovány operandy instrukcí, řídicí bity atd.:

# Instrukce Parametry Stručný popis
1 JMP podmínka, adresa skok, skok s podmínkou
2 WAIT polarita, zdroj čekání na splnění podmínky (stav pinu, IRQ)
3 IN zdroj, počet bitů zápis bitů ze zdroje do posuvného registru ISR
4 OUT cíl, počet bitů vysunutí bitů z posuvného registru ISR do cíle
5 PUSH blokující zápis uložení hodnoty z posuvného registru ISR do RX fronty (FIFO)
6 PULL blokující zápis přečtení hodnoty z TX fronty (FIFO) se zápisem do posuvného registru OSR
7 MOV cíl, operace, zdroj přenos dat s provedením zvolené operace (negace či otočení bitů)
8 IRQ nastavit/smazat, čekání nastavení nebo vymazání příznaku přerušení
9 SET cíl, data (pět bitů) zápis dat do zvoleného cíle
Poznámka: v operačním kódu každé instrukce je pět bitů rezervováno pro specifikaci počtu cyklů, které budou vloženy mezi tuto instrukci a instrukci následující. Toho se velmi často využívá a ostatně i v našich demonstračních příkladech se s tímto konceptem setkáme.

13. Instrukce skoku (JMP)

Podrobnější popis instrukčního souboru začneme instrukcí nazvanou JMP, kterou je možné realizovat nepodmíněný nebo i podmíněný skok. Instrukční slovo této instrukce má délku dva bajty (16 bitů) a je rozděleno do čtyř bitových polí:

+-15-14-13-12-11-10--9--8--7--6--5--4--3--2--1--0--+
| 0  0  0 |     delay    |condition|    address    |
+---------+--------------+---------+---------------+

O bitovém poli delay si řekneme podrobnosti příště, takže nás nyní budou zajímat pole nazvaná address a condition. Bitové pole address obsahuje adresu cíle skoku. Vzhledem k omezené velikosti paměti pro PIO instrukce je pět bitů dostačujících. Mnohem zajímavější je bitové pole condition, ve kterém je zakódována podmínka, která se vyhodnocuje. Toto pole má šířku tří bitů, což znamená, že lze rozlišit osm podmínek:

# Condition bity Podmínka, která se testuje
1 000 bez podmínky (skok se vždy provede)
2 001 X==0
3 010 X!=0 před snížením X o jedničku
4 011 Y==0
5 100 Y!=0 před snížením Y o jedničku
6 101 X!=Y
7 110 PIN==1 (libovolný dopředu zvolený GPIO)
8 111 registr OSR je posunut o nastavený počet bitů
Poznámka: povšimněte si, že se v rámci podmínky (po jejím vyhodnocení) může snížit obsah pracovního registru X nebo Y. Jedná se o jednu z mála aritmetických operací, kterou lze s těmito registry provádět. Ovšem snížení hodnoty vybraného registru znamená možnost implementace počítaných programových smyček, což je v praxi poměrně často používáno.

14. Instrukce čekání na událost (WAIT)

Pro subsystém PIO zcela typická je instrukce nazvaná WAIT. Tato instrukce umožňuje čekat na určitou událost, kterou může být změna stavu sledovaného pinu nebo vznik přerušení (jeho zdrojem je typicky externí událost). Vzhledem k tomu, že u pinů můžeme chtít čekat buď na změnu stavu z nuly na jedničku či naopak, obsahuje instrukční slovo i bit nazvaný pol, resp. celým jménem polarity. V případě, že je tento bit nastavený na nulu, čeká se na událost, která vynuluje příslušný pin (nebo signál přerušení) a pokud je nastavený na jedničku, čeká se (logicky) na přechod pinu do stavu 1 nebo na příchod přerušení. Dále instrukční slovo obsahuje dva bity se specifikací sledovaného zdroje a taktéž pětibitový index:

+-15-14-13-12-11-10--9--8--7--6--5--4--3--2--1--0--+
| 0  0  1 |     delay    |pol|source|  index       |
+---------+--------------+---+-----+---------------+
# Source (bity) Použitý zdroj
1 00 libovolný GPIO pin určený bitovým polem index
2 01 PIN získaný mapováním a poté určený bitovým polem index
3 10 IRQ určené bitovým polem index
4 11 rezervováno, na RP2040 nepoužito
Poznámka: rozdíly mezi GPIO a PIO (což jsou fyzicky totožné piny) se budeme zabývat v navazující části tohoto seriálu.

15. Instrukce IN

Další PIO instrukce, s níž se v dnešním článku setkáme, se jmenuje IN. Tato instrukce přečte zadaný počet bitů z vybraného zdroje (source) a pošle je do posuvného registru ISR (jen pro připomenutí – tento registr je připojen na RX FIFO a tedy slouží pro čtení dat z PIO do mikrořadiče). Kromě počtu bitů 0..31, které se mají nasunout do registru ISR se ještě ve třech bitech specifikuje zdroj (source), takže instrukční slovo vypadá následovně:

+-15-14-13-12-11-10--9--8--7--6--5--4--3--2--1--0--+
| 0  1  0 |     delay    | source  |   bit count   |
+---------+--------------+---------+---------------+
# Source (bity) Použitý zdroj data
1 000 GPIO piny
2 001 registr X
3 010 registr Y
4 011 budou se nasouvat nulové bity
5 100 rezervováno, na RP2040 nepoužito
6 101 rezervováno, na RP2040 nepoužito
7 110 registr ISR (což znamená formu rotace bitů)
8 111 registr OSR
Poznámka: opět si povšimněte, že tato zdánlivě jednoduchá instrukce může být ve skutečnosti použita v mnoha situacích.

16. Instrukce OUT

Logickým opakem výše popsané instrukce IN je pochopitelně instrukce OUT. Tato instrukce vysune specifikovaný počet bitů z posuvného registru OSR a uloží je do zvoleného cíle (destination). Instrukční slovo má velmi podobný formát, jako je tomu u instrukce IN:

+-15-14-13-12-11-10--9--8--7--6--5--4--3--2--1--0--+
| 0  1  1 |     delay    |   dest  |   bit count   |
+---------+--------------+---------+---------------+

Opět si uveďme, jaké cíle pro vysunovaná data je možné zvolit:

# Destination (bity) Použitý cíl pro data
1 000 GPIO piny
2 001 registr X
3 010 registr Y
4 011 bity se jen vysunou a zahodí
5 100 GPIO piny (odlišné mapování)
6 101 registr PC (čítač instrukcí)
7 110 registr ISR
8 111 EXEC (viz poznámka níže)
Poznámka: za zmínku stojí poslední možnost nazvaná EXEC. Získaná data jsou v tomto případě „spuštěna“, jakoby se jednalo o běžnou PIO instrukci. To programátorům umožňuje spouštět jakýkoli kód, aniž by byl uložen v instrukční paměti. Zajímavá je i možnost uložit data z registru OSR přímo do čítače instrukcí, čímž můžeme stavovému stroji „vnutit“ nepodmíněný skok.

17. Instrukce PUSH a PULL

Další dvojice instrukcí, s nimiž je možné v PIO pracovat, jsou instrukce nazvané PUSH a PULL. Tyto instrukce slouží pro přenos dat mezi posuvným registrem ISR a RX FIFO (frontou), resp. naopak mezi TX FIFO (frontou) a posuvným registrem OSR (opět si připomeňme, že jména front RX a TX i jména posuvných registrů ISR a OSR jsou odvozena z pohledu mikrořadiče, nikoli z pohledu PIO).

Konkrétně to znamená, že instrukce PUSH přesune obsah registru ISR do RX fronty. Přitom se bude pracovat se všemi 32 bity registru a po provedení této operace bude registr ISR vynulován. Instrukce PULL naopak pošle 32bitové slovo z TX fronty do registru OSR.

Instrukční slova obou těchto instrukcí vypadají podobně:

+-15-14-13-12-11-10--9--8--7---6---5--4--3--2--1--0--+
| 1  0  0 |     delay    | 0 |IfF|Blk| 0  0  0  0  0 |
+---------+--------------+---+---+---+---------------+
 
 
 
+-15-14-13-12-11-10--9--8--7---6---5--4--3--2--1--0--+
| 1  0  0 |     delay    | 1 |IfF|Blk| 0  0  0  0  0 |
+---------+--------------+---+---+---+---------------+
Poznámka: jediný rozdíl tedy spočívá v bitu číslo 7.

Musíme se však zmínit o významu bitů IfF a Blk. První z těchto bitů může přenos dat přeskočit tehdy, dokud čítač posunů nedosáhl nastaveného limitu. A druhý bit řídí způsob čekání na uvolnění fronty. Konkrétně to znamená, že tyto bity ovlivňují instrukce PUSH a PULL následovně:

Instrukce IfF Blk
PUSH přeskok, dokud čítač nedosáhl limitu SHIFTCTRL_PUSH_THRESH čekání, dokud se neuvolní RX FIFO (fronta)
PULL přeskok, dokud čítač nedosáhl limitu SHIFTCTRL_PULL_THRESH čekání, pokud je TX FIFO (fronta) prázdná

18. Instrukce MOV

Předposlední PIO instrukcí, se kterou se dnes setkáme, je instrukce určená pro přenos dat. Tato instrukce se jmenuje, jak je ve světě assemblerů zvykem, MOV. Ovšem v případě PIO se nemusí jednat o pouhý přenos dat mezi zdrojem (source) a cílem (destination), protože s daty lze provést i jednoduchou operaci specifikovanou v instrukci. Instrukční slovo vypadá následovně:

+-15-14-13-12-11-10--9--8--7--6--5--4--3--2--1--0--+
| 1  0  1 |     delay    |   dest  | op |  source  |
+---------+--------------+---------+----+----------+

Popsat si v tomto případě musíme všechna tři bitová pole, tedy source, destinationop. Začneme zdrojem dat:

# Source (bity) Použitý zdroj data
1 000 GPIO piny
2 001 registr X
3 010 registr Y
4 011 přesunou se nulové bity (vynulování cíle)
5 100 rezervováno, na RP2040 nepoužito
6 101 registr STATUS (ještě nepopsán)
7 110 registr ISR
8 111 registr OSR

Cíle dat mohou být následující:

# Destination (bity) Použitý cíl pro data
1 000 GPIO piny
2 001 registr X
3 010 registr Y
4 011 rezervováno, na RP2040 nepoužito
5 100 EXEC (data se spustí)
6 101 registr PC (čítač instrukcí)
7 110 registr ISR (příslušný čítač se vynuluje)
8 111 registr OSR (příslušný čítač se vynuluje)

Zbývá nám popis operací, která lze s daty provádět v průběhu jejich přesunu:

# Operation (bity) Vykonaná operace
1 00 přesun dat bez jejich modifikace
2 01 inverze všech bitů
3 10 otočení všech bitů v 32bitovém slovu
4 11 rezervováno, na RP2040 nepoužito
Poznámka: opět je zřejmé, že zdánlivě triviální přenos dat ve skutečnosti může sloužit k mnoha účelům, včetně spuštění kódu (instrukce) nebo nepodmíněného skoku.

19. Instrukce SET

Poslední dnes popsanou instrukcí je instrukce SET. Tato instrukce slouží pro uložení dat do zvoleného cíle, kterým může být buď pracovní registr X či Y nebo, což je taktéž velmi užitečné, zvolený GPIO pin nebo piny (musí být dopředu namapovány). Díky této instrukci tedy můžeme přímo ovládat výstupní piny. Šířka ukládaných dat je pět bitů, což znamená, že do pracovních registrů lze uložit jen hodnoty 0..31, což však pro mnoho účelů může dostačovat. Pokud je nutné registry inicializovat většími hodnotami, musí se tyto hodnoty přenést přes FIFO (což už vlastně známe).

prace_s_linuxem_tip

Formát instrukčního slova vypadá takto:

+-15-14-13-12-11-10--9--8--7--6--5--4--3--2--1--0--+
| 1  1  1 |     delay    |   dest  | data (5 bitů) |
+---------+--------------+---------+----+----------+

Cíle dat mohou být následující:

# Destination (bity) Použitý cíl pro data
1 000 GPIO piny
2 001 registr X (nejnižších pět bitů, ostatní bity jsou vynulovány)
3 010 registr Y (nejnižších pět bitů, ostatní bity jsou vynulovány)
4 011 rezervováno, na RP2040 nepoužito
5 100 GPIO piny (bude popsáno příště)
6 101 rezervováno, na RP2040 nepoužito
7 110 rezervováno, na RP2040 nepoužito
8 111 rezervováno, na RP2040 nepoužito

20. Odkazy na Internetu

  1. Zápis funkcí obsahujících instrukce Thumb a Thumb-2 v MicroPythonu
    https://www.root.cz/clanky/zapis-funkci-obsahujicich-instrukce-thumb-a-thumb-2-v-micropythonu/
  2. Zápis funkcí obsahujících instrukce Thumb a Thumb-2 v MicroPythonu (2)
    https://www.root.cz/clanky/zapis-funkci-obsahujicich-instrukce-thumb-a-thumb-2-v-micropythonu-2/
  3. Zápis funkcí obsahujících instrukce Thumb a Thumb-2 v MicroPythonu (dokončení)
    https://www.root.cz/clanky/zapis-funkci-obsahujicich-instrukce-thumb-a-thumb-2-v-micropythonu-dokonceni/
  4. Překlad funkcí přímo do nativního kódu MicroPythonem
    https://www.root.cz/clanky/preklad-funkci-primo-do-nativniho-kodu-micropythonem/
  5. MicroPython ve webovém prohlížeči: lehkotonážní varianta k Pyodide
    https://www.root.cz/clanky/micropython-ve-webovem-prohlizeci-lehkotonazni-varianta-k-pyodide/
  6. Programmable IO
    https://docs.micropython.or­g/en/latest/rp2/tutorial/pi­o.html
  7. Introduction to the PIO (Programmable Input Output) of the RP2040
    https://tutoduino.fr/en/pio-rp2040-en/
  8. MicroPython examples: PIO
    https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio
  9. PIO: Wikipedia CZ (pozor: jedná se o něco jiného!)
    https://cs.wikipedia.org/wiki/PIO
  10. RP2040 (Wikipedia)
    https://en.wikipedia.org/wiki/RP2040
  11. Maximising MicroPython speed
    https://docs.micropython.or­g/en/latest/reference/spe­ed_python.html
  12. Online ARM converter
    https://armconverter.com/?disasm
  13. The 3 different code emitters
    https://www.kickstarter.com/pro­jects/214379695/micro-python-python-for-microcontrollers/posts/664832
  14. The 3 different code emitters, part 2
    https://www.kickstarter.com/pro­jects/214379695/micro-python-python-for-microcontrollers/posts/665145
  15. Fast Filters for the Pyboard
    https://github.com/peterhin­ch/micropython-filters
  16. How to load 32 bit constant from assembler with @micropython.asm_thumb
    https://forum.micropython­.org/viewtopic.php?f=21&t=12931&sid=25­de8871fa9cfcf8cafb6318f9d8ba3a
  17. Pi pico, micropython.asm_thumb: ADR Rd, <label> and LDR Rd, <label> not implemented?
    https://github.com/orgs/mi­cropython/discussions/12257
  18. MicroPython documentation
    https://docs.micropython.or­g/en/latest/index.html
  19. Inline assembler for Thumb2 architectures
    https://docs.micropython.or­g/en/latest/reference/asm_thum­b2_index.html
  20. Inline assembler in MicroPython
    https://docs.micropython.or­g/en/latest/pyboard/tutori­al/assembler.html#pyboard-tutorial-assembler
  21. MCU market turns to 32-bits and ARM
    http://www.eetimes.com/do­cument.asp?doc_id=1280803
  22. Cortex-M0 Processor (ARM Holdings)
    http://www.arm.com/produc­ts/processors/cortex-m/cortex-m0.php
  23. Cortex-M0+ Processor (ARM Holdings)
    http://www.arm.com/produc­ts/processors/cortex-m/cortex-m0plus.php
  24. ARM Processors in a Mixed Signal World
    http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world
  25. RISCové mikroprocesory s komprimovanými instrukčními sadami
    https://www.root.cz/clanky/riscove-mikroprocesory-s-komprimovanymi-instrukcnimi-sadami/
  26. RISCové mikroprocesory s komprimovanými instrukčními sadami (2)
    https://www.root.cz/clanky/riscove-mikroprocesory-s-komprimovanymi-instrukcnimi-sadami-2/
  27. ARM Architecture (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_architecture
  28. Cortex-M0 (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_Cortex-M0
  29. Cortex-M0+ (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_Cortex-M#Cortex-M0.2B
  30. Improving ARM Code Density and Performance
    New Thumb Extensions to the ARM Architecture Richard Phelan
  31. The ARM Processor Architecture
    http://www.arm.com/produc­ts/processors/technologies/in­struction-set-architectures.php
  32. Thumb-2 instruction set
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.ddi0344c/Beiiegaf.html
  33. Introduction to ARM thumb
    http://www.eetimes.com/dis­cussion/other/4024632/Intro­duction-to-ARM-thumb
  34. ARM, Thumb, and ThumbEE instruction sets
    http://www.keil.com/suppor­t/man/docs/armasm/armasm_CEG­BEIJB.htm
  35. An Introduction to ARM Assembly Language
    http://dev.emcelettronica­.com/introduction-to-arm-assembly-language
  36. Processors – ARM
    http://www.arm.com/produc­ts/processors/index.php
  37. The ARM Instruction Set
    http://simplemachines.it/doc/ar­m_inst.pdf
  38. The Thumb instruction set
    http://apt.cs.manchester.ac­.uk/ftp/pub/apt/peve/PEVE05/Sli­des/05_Thumb.pdf
  39. Why Learn Assembly Language?
    http://www.codeproject.com/Ar­ticles/89460/Why-Learn-Assembly-Language
  40. Is Assembly still relevant?
    http://programmers.stackex­change.com/questions/95836/is-assembly-still-relevant
  41. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/on­lamp/2004/05/06/writegreat­code.html
  42. Assembly language today
    http://beust.com/weblog/2004/06/23/as­sembly-language-today/
  43. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubri­ky/assembler/vyznam-assembleru-dnes-155960cz
  44. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/li­nux/prog/asm.html
  45. AT&T Syntax versus Intel Syntax
    https://www.sourceware.or­g/binutils/docs-2.12/as.info/i386-Syntax.html
  46. Linux Assembly website
    http://asm.sourceforge.net/
  47. Raspberry Pi Pico Variants – A Detailed Comparison
    https://circuitdigest.com/ar­ticle/raspberry-pi-pico-variants-comparison
  48. Raspberry Pi Pico 2 vs Original Pico: What’s New?
    https://www.digikey.cz/en/ma­ker/blogs/2024/raspberry-pi-pico-2-vs-original-pico-whats-new
  49. pio-uart
    https://github.com/Sympatron/pio-uart
  50. Datasheet čipu RP2040
    https://datasheets.raspbe­rrypi.com/rp2040/rp2040-datasheet.pdf
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.