Der RISC-V-Befehlssatz hat seinen festen Platz in der Welt der Embedded-Systeme und Mikrocontroller (MCU) eingenommen. Wie wichtig ein offener Befehlssatz sein kann und welche Vorteile er bringen kann, zeigen die Handelskriege, die derzeit geführt werden. Kommerzielle Anbieter von Befehlssätzen, wie z. B. ARM, haben die Lizenzierung ihrer eigenen Befehlsarchitekturen als Reaktion auf den freien RISC-V-Befehlssatz angepasst und kundenfreundlicher gestaltet. Konkurrenz belebt bekanntlich den Markt. Daher soll dieser Artikel Ihnen helfen, die ersten Schritte mit RISC-V-basierten MCUs zu machen.
 

Was kreieren wir?

Der Canaan Kendryte K210 ist ein preiswerter Mikrocontroller mit einem RISC-V-Kern. Er findet sich auf dem Sipeed Maixduino-Board oder dem Sipeed Maix Bit (beide im Elektor-Shop verfügbar) aber auch auf anderen Boards und kann als Kit mit Kamera und LCD für ca. 30 € erworben werden.Wir werden auf dieser MCU zeigen, wie eine Toolchain und das Software Development Kit (SDK) für das Board unter Linux installiert werden können. Neben dem klassischen "Hallo Welt" werden wir Quake 1, ein 3D-Shooter-Spiel aus der Mitte der 90er Jahre, verwenden und zeigen, wie dieses für das K210 übersetzt werden kann. Wenn Sie Quake 1 nur ausprobieren und den Quellcode nicht selbst erstellen wollen, können Sie eine fertige Binärdatei aus dem GitHub-Repository von elect-gombe [1] herunterladen und direkt flashen.
 
Sipeed Maixduino
Bild 1: Sipeed Maixduino mit Display und Kamera

Hardware

Dieser Artikel nutzt das Sipeed Maixduino (Bild 1) und das Sipeed Maix Bit (Bild 2), die einen Canaan Kendryte K210 als Mikrocontroller enthalten. Es handelt sich um einen RISC-V-basierten Mikrocontroller, der nach RISC-V-Terminologie mit zwei Herzen (Prozessorkernen) ausgestattet ist.

Sipeed MAix Bit
Bild 2: Sipeed Maix Bit mit angeschlossenem Display.
 

Die genauen technischen Details sind in Tabelle 1 zu finden. In der Tabelle steht die Bezeichnung RV64IMAFDC für den Prozessorkern. Was auffällt, ist die Bezeichnung RV64 am Anfang – und ja, es handelt sich um einen 64-Bit Mikrocontroller.

Specification K210
Tabelle 1: Spezifikationen K210

Hinzu kommt der interne SRAM, der mit 8 MB (6 MB User-RAM + 2 MB AI-RAM) größer ist als der interne Flash vieler anderer MCUs. Wie der Maxim MAX78000 verfügt auch der K210 über einen Beschleuniger für neuronale Netzwerke. Auch dessen Einsatz wurde bereits in Elektor vorgestellt [2].

Auch an der sonstigen Ausstattung wurde nicht gespart. Eine Kamera und ein LCD mit 320 × 240 Pixeln sind in beiden Boards enthalten. Wer den Maixduino verwendet, sollte beachten, dass das WiFi-Modul über die gleiche Schnittstelle wie die SD-Karte angeschlossen wird. Bei den Tests gingen mindestens zwei SD-Karten in einen undefinierten Funktionszustand über. Für Maix Bit liegen noch keine Ergebnisse dazu vor. Zudem ist nicht jede SD-Karte geeignet und man muss ein bisschen herumprobieren, welche Karte am Ende mit dem K210 läuft.

Toolchain

Für die Entwicklung auf einem Kendryte K210 nutzen wir Linux (Ubuntu 20.04). Als Tipp sei angemerkt, dass sich die Installation auf einer virtuellen Maschine bewährt hat. Snapshots ermöglichen eine Wiederherstellung des Betriebssystems in einen definierten Zustand, ohne viel Zeit zu verlieren. Die Nutzung von Windows ist möglich, wird aber in diesem Artikel nicht behandelt.

Ein Compiler und ein SDK werden einmalig für den Kendyte K210 benötigt. Da der K210 ein RV64, ein 64-Bit RISC-V, ist, wäre es sinnvoll, das aktuelle GCC für die Architektur zu nutzen. Leider wurden für den K210 der RISC-V GCC-Compiler und die C-Bibliotheken vor einigen Jahren von Kendryte modifiziert und in einem separaten GitHub-Repository gepflegt.  Diese modifizierte Kombination aus Compiler und C-Bibliothek hat ein paar kleinere Anpassungen, die nicht in den Hauptzweig von RISC-V GCC und C-Bibliothek zurückgeführt wurden. Diese Anpassungen für den aktuelle GCC vorzunehmen, ist nicht unmöglich, aber momentan nicht Teil dieses Artikels. Wir konzentrieren uns darauf, mit dem K210 zu beginnen.

Kompilieren eines Compilers

Die folgenden Befehle werden in ein Terminal oder ein Terminalfenster eingegeben. Wir können den geeigneten Compiler [3] mit git clone --recursive https://github.com/kendryte/kendryte-gnu-toolchain auf unseren Computer klonen. Die Toolchain wird nicht fertig geliefert, sondern als Quellcode, den wir nun übersetzen müssen, damit sie Code für einen RV64-Prozessor erzeugt.

Dafür brauchen wir einige Pakete unter Linux. Diese können mit sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev installiert werden.

Jetzt, da alles bereit ist, wechseln wir mit cd kendryte-gnu-toolchain zu dem Ordner, die durch git clone erstellt wurde. Hier konfigurieren wir den Compiler mit ./configure --prefix=/opt/kendryte-toolchain --with-cmodel=medany --with-arch=rv64imafc --with-abi=lp64f, sodass er einen geeigneten Code für den K210 generieren kann. Ist der Befehl erfolgreich ausgeführt, können wir den Compiler sudo make -j8 erstellen, wobei 8 für die Anzahl der Prozessorkerne des eigenen Computers steht. Der Compiler ist nun kompiliert und installiert in /opt/kendryte-toolchain. Der Compiler ist nun fertig und bereit zur Anwendung. Das Terminal kann nun geschlossen werden.

SDK-Installation

Nachdem der Compiler installiert ist, kommt nun das SDK für den Kendryte K210. Dazu wird wieder ein Terminal oder Terminalfenster benötigt. Für das SDK gibt es zwei Auswahlmöglichkeiten, eine mit einem FreeRTOS, das nicht mehr gepflegt wird, und eine Variante ohne FreeRTOS. Für den ersten Schritt brauchen wir ein SDK ohne FreeRTOS. Dieses wird mit git-clone https://github.com/kendryte/kendryte-standalone-sdk.git geklont. Mit cd kendryte-standalone-sdk können wir nun in diesen Ordner wechseln.

Das SDK basiert auf CMake und wird mit Hilfe der Kommandozeile bedient. Als ersten Test versuchen wir das Hello-World-Projekt zu erstellen. Mit mkdir build && cd build wird das Verzeichnis build erzeugt und in dieses navigiert. Das mit dem SDK mitgelieferte hello_world kann nun mit cmake ... DPROJ=hello_world -DTOOLCHAIN=/opt/kendryte-toolchain/bin erstellt werden. Danach kann das Projekt für den K210 mit make kompiliert werden.

Im build Ordner sehen Sie dann zwei Dateien - einmal eine hello_world.bin und einmal eine hello_world Datei. Die hello_world.bin ist dazu gedacht, auf den K210 hochgeladen zu werden; die hello_world Dateien können zum Debuggen verwendet werden. Das Debuggen des K210 ist ein Thema für einen ausführlicheren Artikel.

Flash-Tools und serielles Terminal

Nachdem der erste Quellcode für den K210 erfolgreich übersetzt wurde, ist es nun an der Zeit, ihn auf das Board zu bringen. Dafür benötigen wir kflash und kflash_gui (Bild 3). Wie in den vorherigen Schritten wird ein Terminal oder ein Terminalfenster genutzt, um die nötige Software zu installieren. Zuerst wird Python3 benötigt und kann mit sudo apt install python3-pip installiert werden.

kflash_gui ready to upload a binary
Bild 3: kflash_gui bereit zum Hochladen einer Binärdatei

Zuerst wird kflash nun mit sudo pip3 install kflash installiert. Für kflash_gui kann eine kompilierte Version von [4]. heruntergeladen werden. Nach dem Entpacken kann ein Terminal genutzt werden, um die kflash_gui-Datei zu wechseln, und ./kflash_gui, um das Programm zu starten.

Um das kflash_gui aus dem aktuellen Quellcode zu kompilieren, wird die aktuelle Version des Quellcodes mit git clone --recursive https://github.com/sipeed/kflash_gui.git geholt. Mit sudo apt install python3 python3-pip wird die notwendige Umgebung zum Kompilieren eingerichtet. Da die kflash_gui zusätzliche Abhängigkeiten hat, werden diese mit cd kflash_gui && sudo pip3 install -r requirements.txt heruntergeladen. Die kflash_gui kann nun mit python3 kflash_gui.py gestartet werden.

Neben dem Flash-Tool ist es sehr praktisch, ein serielles Terminal zur Hand zu haben. CuteCom (Bild 4) aus den Ubuntu-Paketquellen bietet ein einfach zu nutzendes grafisches serielles Terminal. Zur Installation kann sudo apt install cutecom in das Terminal oder ein Terminalfenster eingegeben werden. Nach erfolgreicher Installation ist CuteCom als Applikation verfügbar.

CuteCom serial settings for microcontroller
Bild 4: CuteComs serielle Einstellung für den K210.

Einen wichtigen Schritt stellt nun das serielle Interface unter Linux dar. Normale User haben nicht das Recht, serielle Ports zu nutzen. Um dieses Recht zu erlangen, muss man Mitglied der Nutzergruppe werden, indem man sudo adduser $(whoami) dialout in das Terminal oder Terminalfenster eingibt. Die Änderung tritt erst nach einem Neustart ein.

Das erste Hello World

Nachdem die Vorbereitungen abgeschlossen sind, ist es an der Zeit, die hello_world.bin zu testen. Zuerst muss die kflash_gui gestartet werden. Dann muss  das Board mit dem Computer über den K210 verbunden werden. Abhängig von der Platine sollten ein oder zwei serielle Ports erscheinen (unter Linux /dev/ttyUSBx or /dev/ttyACMx). In kflash_gui wird der entsprechende serielle Port zum Flashen und dann hello_world-bin ausgewählt. Alle anderen Einstellungen müssen nicht geändert werden. Mit download sollte hello_world.bin nun auf das Board übertragen werden. Wenn der Prozess erfolgreich beendet ist, kann CuteCom gestartet werden.

In CuteCom wird das passende serielle Interface (/dev/ttyUSBx or /dev/ttyACMx) geöffnet (115200 Baud, 8N1). Nach einem Reset des K210 erscheint nun Core 0 Hello world und Core 1 Hello world. Damit ist die erste Hello-World-Demo abgeschlossen – wir wissen nun, dass beide Prozessorkerne Code ausführen können und wir können Code erfolgreich auf den K210 laden.

Kompilieren Sie Quake 1 für den K210

Quake 1 [5] ist ein 3D-Ego-Shooter aus der Mitte der 90er Jahre und hat das Genre mit seiner Grafik und dem Spieldesign geprägt. Der Quellcode für Quake 1 wurde unter der GPL2-Lizenz 1999 veröffentlicht und dient seither als Quelle für verschiedene Ports.

Der Quellcode enthält nur die Engine selbst, jedoch keine anderen Dateien wie die Grafik oder Level. Diese Daten können aus der Quake 1 Shareware-Version [6], oder aus einer installierten Vollversion entnommen werden. Quake 1 kann immer noch DRM-frei und legal auf GOG.com [7] erworben werden.

Für den K210 hat elect_gombe (Twitter @elect_gombe) vor zwei Jahren einen Port erstellt. In der Zwischenzeit wurden Anpassungen und Fehlerkorrekturen am SDK für den K210 vorgenommen, was leider dazu führt, dass der Quellcode nicht mehr direkt auf diese Weise funktioniert. Deshalb folgen nun eine Einführung und die notwendigen Anpassungen. Zuvor sollte ein Backup der kendryte-sdk-standalone-Datei erstellt werden (da die folgenden Modifikationen die Kompilierung von Quake 1 ermöglichen, aber nicht unbedingt für andere Quellcodes geeignet sind).

Um Quake 1 zu übersetzen, müssen wir den Quellcode von elect_gombes Git Repository [8] klonen. Dazu öffnen wir das Terminal oder Terminalfenster und steuern mit cd ~/kendryte-standalone-sdk/src/ in das Projektverzeichnis für das K210 SDK. Hier klonen wir den Quellcode mit dem git clone https://github.com/elect-gombe/quake-k210.git, so dass eine quake-k210 Ordner entsteht.

Nun müssen wir einige Dateien des SDKs mit denen aus dem quake-k210-Ordner überschreiben. Im geöffneten Terminal kopieren wir eine modifizierte Linker-Datei in das SDK mit cp quake-k210/additionalparts/kendryte.ld ../lds/kendryte.ld. Mit cp quake-k210/additionalparts/compile-flags.cmake ../cmake/compile-flags.cmake werden modifizierte Compiler-Flags (d.h. Parameter zum Erstellen des Quellcodes) in das SDK kopiert. Dies ist notwendig, weil der Quellcode für Quake 1 noch Teile enthält, die nicht mit den modernen Einstellungen heutiger Compiler übersetzt werden können. In der Datei ./quake-k210/additionalparts/ gibt es eine crt-S-replace-Datei. Diese benötigt einige Modifikationen, damit wir sie mit dem aktuellen SDK nutzen können. Mit nano ~/kendryte-standalone-sdk/src/quake-k210/additionalparts/crt.S-replace wird crt.S-replace geöffnet. In dieser Datei befinden sich die ersten Zeilen des Quellcodes, hier Assembler, die der K210 ausführt. Zudem befinden sich einige Interrupts hier. Da diese Datei zu einer Zeit geändert wurde, als Standalone SDK und FreeRTOS SDK noch miteinander verwoben waren, gibt es hier ein paar Einträge, die beim Kompilieren und Linken Fehler verursachen. Es ist erforderlich, die Assembler-Instruktionen im trap_entry-Bereich von Zeile 166 bis Zeile 160 zu löschen. Zwischen Zeile 133 und Zeile 140 sollte nur Folgendes stehen:

133 trap_entry:

134   addi sp, sp, -REGBYTES

135   sd t0, 0x0(sp)

136

137 .handle_other:

138   ld   t0, 0x0(sp)

139   addi sp, sp, REGBYTES

140   addi sp, sp, -64*REGBYTES

Wenn die Datei entsprechend angepasst wurde, muss diese mit STRG+O gespeichert werden. Anschließend wird der Editor mit STRG+X geschlossen. Nun wird die Datei an die entsprechende Stelle im SDK kopiert. Um dies zu tun, geben Sie cp ~/kendryte-standalone-sdk/src/quake-k210/additionalparts/crt.S-replace ~/kendryte-standalone-sdk/lib/bsp/crt.S in das Terminal ein.

Im noch geöffneten Terminal wechseln wir nun in das build Verzeichnis des SDKs mit cd ~/kendryte-standalone-sdk/build/. Um keine Überbleibsel aus früheren Projekten im build Verzeichniszu haben, müssen wir es mit rm * - r leeren. Mit cmake ... -DPROJ=quake-k210 -DTOOLCHAIN=/opt/kendryte-toolchain/bin wird das Projekt nun vorbereitet. Wenn wir jetzt make eingeben würden, würde die Kompilierung mit einem Fehler abbrechen. Dies wird durch die neuen Compiler-Flags erzeugt, und das Naheliegendste wäre, die Flags so anzupassen, dass die Kompilierung erfolgreich durchläuft.

Der aktuelle Fehler kommt von der NNCASE-Library, die für neurale Netzwerke, bspw. KI, genutzt wird. Da Quake 1 diese Library nicht nutzt und wir den KI-RAM für Quake brauchen, können wir diese Library aus der Kompilierung ausschließen. Dazu nutzen wir cd ~/kendryte-standalone-sdk/lib/. Mit nano CMakeLists.txt müssen nun Anpassungen an der Datei vorgenommen werden. CMakeLists.txt enthält in Zeile 5 ADD_SUBDIRECTORY(nncase). Dies wird mit einem # auskommentiert und daraus ergibt sich #ADD_SUBDIRECTORY(nncase). In Zeile 45 muss TARGET_LINK_LIBRARIES(kendryte PUBLIC nncase) ebenfalls auskommentiert werden, so dass #TARGET_LINK_LIBRARIES(kendryte PUBLIC nncase) in Zeile 45 steht. CTRL+O speichert die Änderungen und CTRL+X beendet den Editor.

Das könnte nun den Quellcode erstellen und etwas Speicher sparen. Leider ist das nicht ausreichend. Würden wir jetzt den Quellcode erstellen, würde Quake 1 starten und nach kurzer Zeit die Fehlermeldung „Error: Out of Memory“ (Speicherplatz voll) auf dem seriellen Interface anzeigen. Zwar sind 8MB SRAM verfügbar, aber das ist sehr knapp für Quake 1. Deshalb muss die Speicherverteilung angepasst werden.

Für die Anpassung öffnen wir ein Terminal oder Terminalfenster, falls noch keins geöffnet ist. Mit nano ~/kendryte-standalone-sdk/src/quake-k210/source/sys_ctr.c wird die Datei mit dem quake_main geöffnet. In Zeile 378 und Zeile 379 sind 5,5MB des RAM verteilt. Hier müssen wir den Wert reduzieren. Als Minimalwert können wir hier MINIMUM_MEMORY nutzen. Dieser Wert ist im Quellcode definiert und bestimmt den kleinsten Speicherplatz, mit dem Quake startet. Für Quake 1 und die Shareware-Version ist dies ausreichend. Zeile 378 lautet dann parms.memsize = MINIMUM_MEMORY. Mit CTRL+O kann die Datei gespeichert werden und mit CTRL+X wird der Editor geschlossen.

Nun kann Quake kompiliert werden. Um dies zu tun, wechseln wir mit cd ~/kendryte-standalone-sdk/build/ zum build Verzeichnis im noch offenen Terminal. Mit make clean werden die Reste der fehlgeschlagenen Kompilierung entsorgt und ein nachfolgendes make startet die neue Kompilierung. Das Ergebnis ist ein quake-k210.bin im build Verzeichnis, die nun auf den K210 übertragen werden kann. Dazu wird der kflash_gui benötigt. Aber das Spiel wird so nicht starten. Wir müssen noch eine SD-Karte vorbereiten, indem wir die id1-Datei von Quake 1 kopieren. Legen Sie die SD-Karte in den spannungsfreien K210 ein, der SD-Kartenslot ist nicht hot-plug fähig. Wenn alles erfolgreich kopiert, geflasht und zusammengebaut wurde, sollte Quake 1 starten und die Demos laufen.
 

Verbindung eines modernen Gamepads mit einem Mikrocontroller

Für viele Applikationen oder Spiele auf dem Mikrocontroller ist es hilfreich, ein Eingabegerät anzuschließen. Eine Maus oder eine Tastatur über das PS/2-Interface ist eine Möglichkeit. Eine andere, für Spiele und Experimente genauso geeignete, ist ein Gamepad. Falls Sie an etwas Einfaches, wie das Nintendo Entertainment System (NES) Gamepad (Bild 5) denken, können ein paar Knöpfe mehr nicht schaden.

NES gamepad
Bild 5: NES Gamepad.

Und das NES Gamepad ist nicht gerade ein Paradebeispiel für Ergonomie. Eine Möglichkeit ist das klassische Nintendo Wii Gamepad (Bild 6), das per I2C gesteuert werden kann. Dazu wäre ein geeignetes Adapter Board nötig.

Wii classic controller
Bild 6: klassischer Wii Controller
Eine andere Möglichkeit, die speziell für die Nutzung mit dem Evaluation Board entworfen wurde, nutzt ein Playstation 2-kompatibles Gamepad (Bild 7). Dies wird von Joy-IT hergestellt und direkt mit dem passenden Verbindungskabel zur Verbindung mit einem Mikrocontroller geliefert.
Joy-IT Wireless Gamepad.
Bild 7: Joy-IT kabelloses Gamepad.

Das Gamepad, obwohl etwas versteckt zu lesen, verwendet eine SPI-Schnittstelle. Die Angabe aus dem Datenblatt kann mit Tabelle 2 in die dazugehörigen Pins einer SPI-Schnittstelle übertragen werden. Wenn Sie einen Arduino-kompatiblen Controller verwenden, können Sie die Arduino-PS2X Library [9] nutzen. DDamit sollte es jedem möglich sein, ein Gamepad auch für eigene Projekte anzuschließen. So können ein Roboterarm, ein Auto oder eine GUI mit einem Gamepad gesteuert werden.

PS2 Gamepad pins to SPI pins
Tabelle 2: PS2 Gamepad Pins zu SPI Pins

Verdrahtung und Testlauf mit Quake 1 mit Gamepad

Das Gamepad ist mit der K210-Platine (z.B. der Sipeed Maixduino oder Maix Bit) verbunden. Wenn alles bereit ist und die SD-Karte eingelegt wurde, steht einer Runde Quake 1 nichts mehr im Wege. Wird ein Maixduino benutzt, kam es bereits vor, dass er während der Tests einige SD-Karten in einen undefinierten Status zwischen funktionierend und defekt wechselten. Da der ESP32, der auf diesem Board als Wi-Fi-Interface fungieren kann, auf dem gleichen SPI-Bus wie die SD-Karte liegt, kann davon ausgegangen werden, dass dies die Kommunikation ungewollt stört. Das Ergebnis zeigt Bild 8.

Quake on K210 microcontroller
Bild 8: Quake auf dem K210 mit Gamepad.
 
Quake 1 Demo running on K210
Kleine Demonstration von Quake 1 auf dem K210

Quake 1 auf einem Mikrocontroller

Während Quake 1 nicht jedermanns Geschmack ist, was Spiele angeht, demonstriert dieses Spiel die Leistungsfähigkeit der MCU. Mit dem eingerichteten SDK und Compiler können nun Quake 1 und das erste Hello World kompiliert werden. Damit steht eine Entwicklungsumgebung für weitere Experimente mit dem K210 und Quellcode zur Verfügung, von dem man lernen kann, wie die Peripherie des K210 angesteuert wird.

Nach dem ersten Kontakt mit einem RISC-V basierten Mikrocontroller wurde immer noch wenig über RISC-V, die Architektur und Bezeichnungen gesprochen. Ein RV64IMAFC (Bezeichnung des Prozessor-Cores im K210) klingt im ersten Moment nach etwas, das beim Scrabble viele Punkte bringt, oder es fiel nichts Besseres ein, um das Spiel zu gewinnen. Um diese Bezeichnungen zu entschlüsseln - und mehr über die Vorteile von RISC-V zu erfahren - können Sie einen Blick auf den Elektor-Artikel [10] von Stuart Cording werfen. Außerdem gibt Ihnen der Artikel „Embedded System Development with RISC-V Processors“ von Allan He (erschienen in Elektor Industry 01/2021) einen guten Überblick über das RISC-V-Ökosystem. 


Fragen oder Kommentare?

Haben Sie technische Fragen oder Kommentare zu diesem Artikel? Schreiben Sie dem Autor eine E-Mail unter mathias.claussen@elektor.com oder kontaktieren Sie Elektor unter editor@elektor.com.


Übersetzung: Louisa Kuck