====== Rust auf Pi Pico ======
[[dtlab_start|Zurück zur Themenübersicht]]
===== Aufgabenstellung =====
Bei diesem Thema untersuchen Sie wie schnell der ARM Cortex M0 auf dem Raspberry Pi Pico arbeitet. Dazu wird der Ausgang an einem GPIO geändert und die Änderung wird mit dem Oszilloskop gemessen. Dazu kommt dann die Messung der Ausführungszeit einer Primzahlensuche auf dem RP2040 und der Vergleich mit einer Ausführung auf dem Hostsystem. Der Code ist in Rust geschrieben.
===== Vorbereitung =====
==== Toolchain ====
Die Toolchain ist die gleiche wie im [[https://gitlab.elektrotechnik.hs-augsburg.de/meiti/tha_microrust_project-template|91Âþ» MicroRust Projekt]] von Claudia Meitinger.
* [[https://www.rust-lang.org/tools/install|Installieren Sie Rust]] auf ihrem Rechner
* Installieren Sie [[https://vscodium.com|VSCodium]] oder Microsoft Codium
* In Codium: [[https://rust-analyzer.github.io|rust-analyzer]]
Für Rust noch
rustup target add thumbv6m-none-eabi
cargo install elf2uf2-rs
==== Projekt ====
Im [[https://caeis.etech.fh-augsburg.de/beckmanf/picofirst.git/|picofirst Projekt]] ist ein git Repository, das die zwei Projekte
* blinky - Für den Pi Pico
* prime - Für den Hostrechner
enthält. Sie können das Repository auf ihrem Rechner mit
git clone https://caeis.etech.fh-augsburg.de/beckmanf/picofirst.git
installieren.
==== blinky ====
Das Rustprojekt Blinky enthält ein kleines Programm mit dem auf dem Pi Pico Board ohne Wifi die Onboard LED ein- und ausgeschaltet wird. Wechseln Sie in das Verzeichnis "picofirst/blinky". Sie können das Programm mit
cargo run
kompilieren und auf den Raspberry Pi Pico laden. Das funktioniert nur wenn der Pi Pico im Mass Storage Mode ist. Dazu muss der "BOOTSEL" Taster auf dem Board gedrückt und gehalten werden während man das USB Kabel mit dem Rechner verbindet. Man kann sehen, dass der Pi Pico im Mass Storage Mode ist, wenn ein neues Laufwerk im Betriebssystem angezeigt wird. Mit "cargo run" wird das Programm kompiliert, auf den Pi Pico geladen. Damit wird der Mass Storage Mode verlassen und der Code ausgeführt.
Auf dem Pi Pico sollte jetzt die LED blinken.
Die blinkende LED dient zur Überprüfung, dass die Toolchain und der Pi Pico richtig funktioniert. Auf dem Pi Pico W mit Wifi ist die LED nicht direkt am RP2040 Prozessor angeschlossen, sondern am Wifichip CYW43439. Um dort die LED anzusteuern muss der Wifichip in Betrieb genommen werden. Das habe ich deshalb hier nicht gemacht.
==== prime ====
Das Projekt "prime" enthält ein Programm zur Suche nach Primzahlen. Das Programm soll nicht auf dem Pi Pico, sondern auf dem Host - also ihrem Rechner oder dem Laborrechner - ausgeführt werden. Wechseln Sie in das Verzeichnis "picofirst/prime". Sie können das Programm direkt mit
cargo run
starten. Das Programm in [[https://caeis.etech.fh-augsburg.de/beckmanf/picofirst.git/tree/prime/src/main.rs|main.rs]] sucht die nächstkleinere Primzahl ausgehend von einer Zahl, die im Programm festgelegt ist. Die Zahl n ist im Code im Repository auf "1<<56" = 2^56 = 72057594037927936 festgelegt. Sie sollten auf dem Terminal eine ähnliche Ausgabe wie diese sehen:
Mac:prime fritz$ cargo run
Compiling prime v0.1.0 (/Users/fritz/pico/picofirst/prime)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/prime`
What is the next prime number smaller than 72057594037927936?
Found: 72057594037927931. This took 2.328603083s.
Mac:prime fritz$
Das Programm dient dazu eine Berechnung durchzuführen, die etwas länger dauert. Starten Sie das Programm einige Male und notieren Sie die Ausführungszeiten. In "blinky" ist diese Primzahlensuchfunktion auch schon integriert, aber noch nicht aktiv.
Kompilieren Sie das Programm jetzt im "Release" Modus. Damit wird der Rustcompiler eine höhere Codeoptimierung durchführen.
cargo run --release
Sie können das Programm nach dem Kompilieren auch direkt starten mit
./target/release/prime
./target/debug/prime
Vergleichen Sie die Ausführungszeiten.
* Schätzen Sie anhand des Rustcodes für die Primzahlensuche ab, wie sich die Ausführungszeit mit der Zahl n verhält. Es geht nicht um die absolute Zeit, sondern ob der Zusammenhang zwischen Ausführungszeit und Zahl konstant, linear oder sonstwie ist.
===== Aufgaben =====
==== Messung der maximal möglichen Frequenz an Pin GP15 ====
In diesem Versuch soll jetzt nicht mehr die LED wechseln, sondern der Pin GP15 in Abbildung 1 links unten.
{{ https://www.raspberrypi.com/documentation/microcontrollers/images/pico-2-r4-pinout.svg }}
**Abb. 1: Rasperry Pi Pinout**
Dazu muss der Code in [[https://caeis.etech.fh-augsburg.de/beckmanf/picofirst.git/tree/blinky/src/main.rs|blinky/src/main.rs]] geändert werden.
* Kommentieren Sie die delays in der loop aus. In der loop soll nur der Pin aus- und eingeschaltet werden.
* Ändern Sie den Pin von GP25 für die LED auf GP15 für den Pin links unten.
* Messen Sie mit dem Oszilloskop die Frequenz und das an/aus Verhältnis des Signals am Pin 15.
* Testen Sie "cargo run" und "cargo run --release"
==== Analyse des Assemblercodes ====
Man kann sich mit
cargo rustc --release -- --emit asm
den Assemblercode generieren lassen. Der Assemblercode ist im Verzeichnis target/thumbv6m-none-eabi/release/deps und fängt mit dem Projektnamen "blinky" and und endet mit der Endung ".s". Ein Pfad kann z.B. so aussehen: "target/thumbv6m-none-eabi/release/deps/blinky-cbc27bc4200d17af.s".
Öffnen Sie die Datei mit Codium. Die Datei enthält sehr viel Code... Finden Sie die Schleife. Fügen Sie dazu die delay Funktionen nochmal in den Code ein und kompilieren Sie nochmal. Dann sollten Sie etwas ähnliches wie das hier:
ldr r6, .LCPI2_8
.LBB2_6:
ldr r0, .LCPI2_5
str r4, [r0]
add r5, sp, #8
mov r0, r5
mov r1, r6
bl _ZN8cortex_m5delay5Delay8delay_us17hb904cf74fb062a91E
ldr r0, .LCPI2_5
str r4, [r0, #4]
mov r0, r5
mov r1, r6
bl _ZN8cortex_m5delay5Delay8delay_us17hb904cf74fb062a91E
b .LBB2_6
.LBB2_7:
strb r5, [r0]
finden. Sie sehen die "cortex_m5delay" Funktionen. Das sind die Aufrufe für die delay Funktion. Nehmen Sie dann die Delayaufrufe wieder aus dem Code. Die Loop sollte jetzt kürzer sein. Erläutern Sie den Code, der den Pin 15 an- und ausschaltet.
==== Vergleich RP2040 Datenblatt ====
Im [[https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf|RP2040 Datenblatt]] finden Sie im Kapitel 2.3.1.7 die Register
* SIO: GPIO_OUT_SET
* SIO: GPIO_OUT_CLR
mit denen der Pegel an einem Pin eingestellt werden kann. Wenn man beispielsweise 1<<3 = 0x00000008 in das Register GPIO_OUT_SET schreibt, dann wird der Ausgangspegel von GP03 auf High gesetzt. Damit das funktioniert muss vorher die Konfiguration des Pins gemacht werden - also beispielsweise, dass das ein Ausgang ist.
* Erläutern Sie im Code wie auf die beiden Register geschrieben wird.
==== Ausführung Primzahlensuche ====
Die Primzahlensuche dauert im Vergleich zu dem Ein- und Ausschalten der Pins relativ lang. Jetzt geht es darum die Ausführungszeiten auf dem Pi Pico mit den Ausführungszeiten auf dem Hostrechner zu vergleichen. Dazu sollen die Ausführungszeiten auf dem Pi Pico mit dem Oszilloskop gemessen werden.
* Fügen Sie die Primzahlensuchfunktion in die Loop ein.
* Aktivieren Sie das Delay nach dem Ausschalten der LED/Pins.
* Die Zeitdauer wie lange das Signal am Pin auf High ist, soll durch die Laufzeit der Primzahlensuche bestimmt sein. Die Zeitdauer wie lange das Signal auf low ist, soll konstant durch die Delayfunktion bestimmt sein.
* Verändern Sie die Werte für die Zahl n, messen Sie die Highphasen am Oszilloskop und notieren Sie die Zeiten
* Erstellen Sie einen Graphen, der die Ausführungszeit gegen die Zahl n darstellt.
* Vergleichen Sie den Graphen mit den Überlegungen aus Vorbereitung für "prime"
* Vergleichen Sie die Ausführungszeit auf dem Pi Pico mit der Ausführungszeit auf dem Host.
==== Direkter Registerzugriff ====
Um die Pins ein- und auszuschalten haben wir im Code die Funktionen wie "led_pin.set_low().unwrap()" aus dem "Hardware-Abstraction-Layer" HAL verwendet. Jetzt soll direkt auf die Register geschrieben werden. Das kann mit der Funktion "write_volatile(REG, 1<<25)" gemacht werden. Die Funktion muss als "unsafe" markiert werden. Kommentieren Sie den Code für die Definition des REG Wertes und das "write_volatile" aus und ersetzten so den "led_pin.set_low()" Code.
* Ersetzten Sie led_pin.set_low() durch einen direkten Registerzugriff
* Prüfen Sie ob der Code noch funktioniert
* Schreiben Sie nun Code, der gleichzeitig GP15 und GP25 in der Loop ändert.
* Schreiben Sie eine Variante mit direktem Registerzugriff und eine Variante mit HAL.
* Gibt es einen Unterschied in der Ausführungszeit?