Raspberry Pi RP2040 (Teil 1): Programmierbare Hardware zwischen CPU und FPGA
Anfang des Jahres hat die Raspberry Pi Foundation ihren ersten Microcontroller vorgestellt: den RP2040. Auch wegen seiner guten Dokumentation wurde der RP2040 sehr gut von der Comunity aufgenommen. Das zugehörige Entwicklerboard, den Raspberry Pico, bekommt man für nur ca. 4 Euro. Weitere Hersteller haben eigene Boards angekündigt. Von Beginn an gibt es Unterstützung für MicroPython in der Thonny IDE und C/C++ in VisualStudio Code, Eclipse und anderen.
Was bietet der RP2040?
Der RP2040 ist ein ARM Cortex M0+ Dual Core mit bis zu 133 MHz, mit etwas mehr als 256 kB internem RAM, jedoch ohne internem Flash. Er hat die üblichen GPIO Ports mit PWM, ADC, I²C, SPI, UART, SWD und USB Full Speed Host oder Device.
Der Bootloader im internen ROM startet das Anwendungsprogramm im Flash oder RAM oder präsentiert sich über USB als Filesystem, auf das ein neues Image im ".UF2" Format kopiert werden kann. Das erleichtert das Flashen von neuen Programmen ganz enorm, denn einen USB-Stick mounten kann heute jedes Betriebssystem.
Zusätzlich bietet der RP2040 pro Core eine 32-Bit Integerdivisionseinheit (die dem Cortex M0+ fehlt), 2 "Interpolierer", die einfache wiederholte Berechnungen z.B. im Audio-Bereich beschleunigen sollen und 2 programmierbare IO Einheiten, die 'PIOs', mit jeweils 4 State Machines.
Das Namensschema des RP2040 ist der Raspberry Pi Foundation so wichtig, dass es in der Dokumentation gleich zu Anfang erklärt wird:
RP2040
^ 2 = Anzahl CPU-Kerne
^ 0 = Cortex M0+
^ 4 = 2^4 * 16kB RAM oder mehr
^ 0 = 2^0 * 16kB Flash oder mehr, in diesem Fall kein Flash.
Es ist also zu erwarten, dass es weitere Microcontroller dieser Familie geben wird. Gleichzeitig hat die Raspberry Pi Foundation eine Liefergarantie für den RP2040 bis 2028 gegeben. Das soll also keine Eintagsfliege bleiben.
Was ist neu?
Das sind ganz klar die programmierbaren IO-Einheiten, die PIOs. Diese ermöglichen es, fehlende Schnittstellen zu implementieren, die aufgrund von Echtzeitanforderungen nur direkt in Hardware implementiert werden können, wofür bisher nur ein CPLD oder FPGA in Frage kam. In vielen Fällen sind nun aber die neuen PIOs im RP2040 dafür ausreichend, billiger und vom Programm aus sogar rekonfigurierbar. Sie sind in ihren Möglichkeiten allerdings auch oft stark limitiert.
Die PIOs werden über 4 State Machines programmiert. Diese können ihr Programm mit bis zu 133MHz* abarbeiten. Es gibt nur 9 Befehle, die aber alle nur einen Takt benötigen, wobei Logik- und Arithmetikbefehle fast völlig fehlen, und jede PIO hat knapp bemessene 32 Worte Programmspeicher.
Wegen dieser starken Reduktion befürchte ich, dass es bald einen Wildwuchs an Varianten geben wird, wenn nicht von der Raspberry Pi Foundation selbst, dann von anderen Herstellern, die dem RP2040 nicht das Feld überlassen werden, sollte er sich als Erfolg erweisen.
*) 125MHz auf dem Raspberry Pi PICO Board.
Was fehlt?
- Der RP2040 kann mit 1.8 bis 3.3 Volt betrieben werden. Die Ports sind aber nicht 5V-tolerant.
- Die Cortex M0+ haben keine FPU, eine Integerdivision wurde aber separat implementiert.
- Der RP2040 hat keinerlei internes Flash, programmierbaren Speicher oder "Fuse Bits". Zusammen mit der Tatsache, dass der Programmspeicher in einem externen QSPI Flash liegt, ist ein Ausleseschutz für das gespeicherte Programm unmöglich. Das ist zwar für Hobbyprojekte unwichtig, spielt aber im industriellen Umfeld eine Rolle. Nicht nur um die Software zu schützen, sondern auch zur Authentifizierung ohne zusätzliches TPM-Modul.
Zumindest Punkt 3 wird hoffentlich durch einen Nachfolger addressiert werden. Das Namensschema ist zumindest dafür vorbereitet.
Punkt 1 ist schade, da gerade die PIOs sich anbieten, mit dem RP2040 Interfaces zu 'veralteter Hardware' zu bauen, die sehr oft nur 5 Volt verwenden. Dies dürfte aber dem Herstellungsprozess geschuldet sein.
Die PIOs
Einsatzgebiete
Die programmierbaren IO-Einheiten sind vor Allem dazu gedacht, fehlende Schnittstellen nachzubilden. Die Dokumentation listet neben weiteren I²C, SPI und UARTs, von denen der RP2040 jeweils schon 2 hat, noch I²S (Audio) und SDIO (SD Card), DPI und VGA, für die es Beispielprojekte gibt und 8080 und 6800 Bus Interfaces. Für letztere wäre eine 5V-Version des RP2040 sehr wünschenswert gewesen.
Weitere Interfaces sind WS2812 (LEDs), Manchester Encoding (Ethernet), CAN-Bus, Logic-Analyzer, XY2-100 (Laserscanner), usw.. Voraussetzung ist, dass man mit den PIO-Befehlen irgendwie hin kommt. Ob das klappt, kann man oft nur im Einzelfall sehen.
Programmierung über State Machines
Die PIOs werden über ihre State Machines programmiert: Es gibt 2 PIO-Blöcke mit je 4 State Machines. Diese teilen sich 32 Worte Programmspeicher. Die State Machines können jederzeit gestartet und gestoppt und das Programm neu geladen werden. Man kann also auf den gleichen Pins ggf. mehrere Protokolle anbieten und diese auch im laufenden Betrieb wechseln.
Der Befehlssatz ist mit nur 9 Basisbefehlen sehr reduziert. Dafür werden alle Befehle in nur einem Taktzyklus bei bis zu 133MHz abgearbeitet. Die PIO-Programme sind dabei meist sehr kurz: bereits ein einziger Befehl kann ein sinnvolles Programm darstellen.
Jede State Machine verfügt über 2 "universelle" Register, dazu je ein Shift Register und eine Fifo mit 4 Worten für IN und OUT, oder eine einzige Fifo mit 8 Worten, wenn nur eine Datenrichtung benötigt wird, was oft der Fall ist.
Die State Machines eines PIO-Blocks können über Statusbits untereinander kommunizieren und sich so synchronisieren oder Interrupts auslösen.
Die Befehle können auf alle GPIO-Pins zugreifen, diese aber zumeist nicht explizit adressieren. Statt dessen werden die State Machines bei der Initialisierung auf GPIO-Bereiche gemappt. Dadurch kann z.B. das gleiche Programm von mehreren State Machines abgearbeitet werden, die jeweils nur anderen Port-Pins zugewiesen sind. Pro State Machine gibt es 4 solche Mappings für die Befehle IN, OUT, SET, (der Befehl MOV übernimmt je nach Quelle oder Ziel das IN oder OUT Mapping) und für Side-Set. Die Befehle haben zumeist eine implizite, feste Quelle, Ziel oder Datenbreite, die anderen Angaben werden über Bitfelder ausgewählt. Die implizite Datenbreite ist entweder die beim Mapping eingestellte oder das Maximum von 32, z.B. bei IN oder beim Kopieren von Registern mit MOV.
Es gibt insgesamt 9 Befehle:
- JMP springt zu einer anderen Stelle im Programm. Der Sprung kann von einigen ausgewählten Bedingungen abhängig gemacht werden.
- WAIT wartet auf eine Bedingung. Das kann ein GPIO-Pin sein oder ein PIO-IRQ-Flag (Statusbit, s.o.).
- IN liest Bits von IO-Ports oder ein Register in's Input Shift Register ISR.
- OUT schreibt Bits zu IO-Ports, deren Richtungsbits oder ein Register, z.B. auch in den Program Counter (JMP) oder das Command Register (Exec).
- PUSH schreibt das Input Shift Register ISR in die Empfangs-Fifo.
- PULL liest liest Daten aus der Sende-Fifo in das Output Shift Register OSR.
- MOV kopiert Daten von einer Quelle zu einem Ziel. Beides können IO-Pins oder ein Register sein. Als Quelle ist auch ein konfigurierbarer Status, z.B. der Füllstand einer Fifo möglich.
- IRQ setzt oder löscht ein PIO-IRQ-Flag (Statusbit), auf das eine andere State Machine vermutlich wartet.
- SET schließlich kopiert einen 5-Bit-Wert in das X- oder Y-Register, auf die Out-Pins oder deren Richtungsbits.
Insgesamt haben die Befehle oft unerwartete Einschränkungen oder zusätzliche Möglichkeiten.
Jeder der Befehle kann 3 Sachen gleichzeitig machen:
- Den eigentlichen Befehl ausführen
- über Side-Set bis zu 5 GPIOs ändern oder deren Richtung umschalten (konfigurationsabhängig)
- und bis zu 31 Wartetakte anhängen.
Ein PIO-Assemblerbefehl sieht dann beispielsweise so aus:
mov pins, x side 0b01 [2]
- 'mov pins, x' ist der eigentliche Befehl (kopiere Register X zu den eingestellten GPIOs),
- 'side 0b01' setzt 2 GPIOs lt. Side-Set-Mapping,
- '[2]'definiert 2 Wartetakte.
Die Möglichkeit, Out-Pins mit Side-Set quasi 'nebenher' zu setzen, ermöglicht schnellere und kürzere oder auch Programme, die andernfalls schon zu lang wären.
Fifos und DMA:
Die State Machines greifen auf die Fifos über ihr In und Out Shift Register (ISR und OSR) zu. Das Nachfüllen aus der Fifo kann entweder automatisch bei einem programmierbaren Füllgrad geschehen oder durch die Befehle PUSH und PULL.
Die Daten der Shift Register werden mit IN und OUT auf die GPIOs ausgegeben oder eingelesen, wobei jede Datenbreite von 1 bis 32 Bit möglich ist.
CPU-seitig können die Fifos per DMA gefüllt oder gelesen werden. Zusammen mit einem entsprechend programmierten DMA können so Daten beliebiger Breite ein- oder ausgegeben werden; wenn keine Flusssteuerung nötig ist sogar mit vollen 133MHz.
Einschränkungen
Die PIO State Machines sind für Anwendungen bis ~32 MHz (für 4 State Machine-Befehle pro Schritt) meist ausreichend schnell, einzelne Anwendungen können auch volle 133MHz erreichen.
Auf Grund des eingeschränkten Befehlssatzes fehlen eigene Logik- und Arithmetikoperationen völlig. Im MOV-Befehl kann das zu kopierende Datenwort immerhin komplementiert und gespiegelt werden. Im JMP-Befehl kann über die Bedingung 'X--' oder 'Y--' das jeweilige Register decrementiert werden. Mit etwas Aufwand kann ein Shiftregister zum Bitshiften missbraucht werden. Das war's. Die 'pico-examples' enthalten ein nicht ganz ernst gemeintes Beispielprogramm, um mittels wiederholtem Decrementieren zwei Zahlen zu addieren. :-)
So zum Beispiel kann ein UART damit nachgebildet werden. Die Berechnung eines Parity-Bit wird schon kompliziert und eine automatische CRC-Berechnung scheidet wegen fehlender Arithmetik völlig aus.
Vorschau
In Kürze erscheint noch ein weiterer Artikel, in dem ich die Programmierung der PIOs an einem konkreten Beispiel, dem XY2-100 Interface für Laserscanner darstellen werde.
Über den Autor
Günter Woigk arbeitet als Software Developer im Embedded-Bereich der MATHEMA GmbH mit dem Schwerpunkt auf C und C++ Programmierung. Momentan interessiert er sich für die Möglichkeiten, die der neue Ansatz im RP2040 für kommerzielle Entwicklungen bietet.
Links
- https://www.raspberrypi.org/products/raspberry-pi-pico/
- rp2040 datasheet.pdf
- raspberry-pi-pico-c-sdk.pdf
Quellenangaben
- Header "Raspi Pico" CC-by-SA© Günter Woigk 2021.
- Abbildung 1 "Registerübersicht einer PIO State Machine": aus RP2040 Datasheet, https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
Weitere Artikel zu RP2040
- RP2040 - Teil 2 - PIO Programmierung am Beispiel eines Laserscanner-Interface
- RP2040 - Teil 3 - Interpolierer - Anwendung gesucht!