*Dieser Schritt ist in der gelieferten VM bereits abgeschlossen*
*Dieser Schritt ist in der gelieferten VM bereits abgeschlossen.*
1. Um den RISC-V Emulator zu installieren wird Python 3 und den Python Package Manger pip benötigt. Unter Linux ist beides in den meisten Paketmanagern enthalten und sollte über die bekannten Möglichkeiten installiert werden können.
2. Installiere den Emulator als pip-Paket (das Paket heißt `riscemu`): `pip install riscemu`
3. Der Emulator wird als Python-Paket installiert und kann mit `python3 -m riscemu` ausgeführt werden. Teste deine Installation mit `python3 -m riscemu --version`, es sollte mindestens Version `2.0.3` installiert sein.
1. Um den RISC-V Emulator zu installieren brauchst du Python 3 und den Python Package Manger pip. Unter Linux ist das in dem Paketmanager deiner Distribution enthalten.
2. Installiere den Emulator als pip Paket (das Paket heißt `riscemu`): `pip install riscemu`
3. Teste deine Installation mit `python3 -m riscemu --version`, es sollte mindestens Version `2.0.3` installiert sein.
## RISC-V Code Ausführen
*Das folgende Beispiel findet auf der Konsole statt, der RISC-V Emulator hat nur ein Konsoleninterface. In Ubuntu kann mithilfe des Kontextmenüs (Rechtsklick) im Dateibrowser die Eintrag "Im Terminal öffnen" ein Terminal öffnen, in dem bereits der Ordner geöffnet ist.*
*Der RISC-V Emulator hat nur ein Konsoleninterface, weswegen auch folgendes Beispiel auf der Konsole abläuft. In Ubuntu kann mithilfe des Kontextmenüs (Rechtsklick) im Dateibrowser über den Eintrag "Im Terminal öffnen" ein Terminal geöffnet werden, in dem bereits der entsprechende Ordner geöffnet ist.*
Betrachten wir das beispiel: `hello-world.asm`
Betrachten wir das Beispiel: `hello-world.asm`
```
# hello-world.asm
@ -37,7 +39,7 @@ text: .asci "Hello World\n"
ecall
```
Das Programm kann auf der konsole mit dem dem Befehl `python3 -m riscemu hello-world.asm` ausgeführt werden. Dafür solltest du in der Konsole in dem gleichen Ordner wie dein assembly program sein. Es sollte das folgende Output in deiner Konsole erscheinen:
Das Programm kann auf der Konsole mit dem dem Befehl `python3 -m riscemu hello-world.asm` ausgeführt werden. Dafür solltest du in der Konsole in dem gleichen Ordner wie dein assembly-Programm sein. Es sollte der folgende Output in deiner Konsole erscheinen:
```
> python3 -m riscemu hello-world.asm
@ -64,7 +66,7 @@ Hello World
## Debugging
Der RISC-V Standard hat einen befehl reserviert der als sogenannter "Breakpoint" agiert. Wenn der Emulator diesen befehl erkennt, wird ein interaktiver Debugger gestartet. Lass uns nun hinter den ersten `ecall` Befehl in `hello-world.asm` einen Breakpoint-Befehl einfügen:
Der RISC-V Standard hat einen Befehl reserviert, der als sogenannter "Breakpoint" agiert. Wenn der Emulator diesen Befehl ausführt, wird ein interaktiver Debugger gestartet. Lass uns nun hinter den ersten `ecall` Befehl in `hello-world.asm` einen Breakpoint-Befehl einfügen:
```
@ -82,7 +84,7 @@ Der RISC-V Standard hat einen befehl reserviert der als sogenannter "Breakpoint"
ecall
```
Wenn das Programm nun ausgeführt wird, erscheint das folgende Output generiert:
Wenn das Programm nun ausgeführt wird, wird der folgende Output generiert:
```
Hello World
@ -92,31 +94,31 @@ Debug instruction encountered at 0x00000127
>>>
````
Dies ist der interaktive Debugger. Hier kann der Zustand der Register, des CPUs und des Arbeitsspeichers betrachtet werden. Eine übersicht der wichtigsten Funktionen sind in der debugging.md Datei zu finden.
Dies ist der interaktive Debugger. Hier kann der Zustand der Register, der CPU und des Arbeitsspeichers betrachtet werden. Eine Übersicht der wichtigsten Funktionen sind in der debugging.md Datei zu finden.
## Installation der RISC-V Gnu Toolchain
*Auch dieser Schritt ist in der VM schon erledigt.*
Für die Installatio unter Linux (Ubuntu) ist ein installationsskript beigelegt, welches die RISC-V toolchain für die Nutzung im Rahmen der Vorlesung konfiguriert und kompiliert. Ich bin mir nicht sicher ob die toolchain unter Windows kompilierbar ist, nehmt dafür am besten das [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)
Für die Installation unter Linux (Ubuntu) ist ein Installationsskript beigelegt, welches die RISC-V Toolchain für die Nutzung im Rahmen der Vorlesung konfiguriert und kompiliert. Ich bin mir nicht sicher ob die toolchain unter Windows kompilierbar ist, nehmt dafür am besten das [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)
Falls das Skript für eure Platform nicht funktioniert, hier noch ein paar tips:
Falls das Skript für eure Platform nicht funktioniert, hier noch ein paar Tipps:
- Ihr braucht ca 8 Gigabyte Festplattenplatz um RISC-V GCC zu kompilieren. (Ein teil kann nach der Installation gelöscht werden).
- In der [GitHub Readme](https://github.com/riscv-collab/riscv-gnu-toolchain#readme) findet ihr die genauen pakete, die ihr auf eurer Distro installierne müsst.
- In der [GitHub Readme](https://github.com/riscv-collab/riscv-gnu-toolchain#readme) findet ihr die genauen Pakete, die ihr auf eurer Distribution installieren müsst.
- Klont das Repository mit `git clone --depth 1 https://github.com/riscv-collab/riscv-gnu-toolchain.git` um nicht die ganze git history mit herunter zu laden.
- Konfiguriert die Installation mit dem folgenden Befehl (dafür müsst ihr in die geklonte repo rein `cd`'en): `./configure --prefix=$(pwd)/../toolchain/ --with-arch=rv32ima --disable-linux --disable-gdb --disable-multilib`. Der Zielordner muss unbedingt vorher angelegt werden! Also `mkdir $(pwd)/../toolchain/` (Legt den ordner `toolchain` im Überordner der Repo an).
- Baut den compiler mit allen verfügbaren threads: `make -j <anzahl der threads eures CPU>`.
- Nach der installation sollte der `toolchain/bin` Ordner zu dem `$PATH` hinzugefügt werden, hierfür gibt es mehrere Möglichkeiten. Hier ein tutorial zur `$PATH` vairable: https://www.howtogeek.com/658904/how-to-add-a-directory-to-your-path-in-linux/
- Konfiguriert die Installation mit dem folgenden Befehl (dafür müsst ihr in die geklonte Repo rein `cd`'en): `./configure --prefix=$(pwd)/../toolchain/ --with-arch=rv32ima --disable-linux --disable-gdb --disable-multilib`. Der Zielordner muss unbedingt per Hand angelegt werden, sonst funktioniert die Kompilation nicht! Also muss `mkdir $(pwd)/../toolchain/` ausgeführt werden (Legt den ordner `toolchain` im Überordner der Repo an).<!-- Den Schritt würde ich vorher erwähnen, wer liest sich schon den nächsten Schritt durch bevor der davor ausgeführt wird^^ -->
- Baut den Compiler mit allen verfügbaren Threads: `make -j <anzahl der threads eures CPU>`. Die Anzahl der verfügbaren Threads kann mit dem Befehl `grep -c ^processor /proc/cpuinfo` bestimmt werden.
- Nach der Installation sollte der `toolchain/bin` Ordner zur `$PATH`-Variable hinzugefügt werden, hierfür gibt es mehrere Möglichkeiten. Hier ein tutorial zur `$PATH`-Variable: https://www.howtogeek.com/658904/how-to-add-a-directory-to-your-path-in-linux/
Nach der installation sollte es möglich sein C und RISC-V Assembly in RISC-V Objektdateien und executables zu kompilieren und den bytecode aus zu geben:
Jetzt kann mit Hilfe der Toolchain, C und RISC-V Assembly in RISC-V Objektdateien und Executables kompiliert werden:
```
# "riscv32-unknown-elf" ist das präfix, und "as" der befehl für den Assembler:
# "riscv32-unknown-elf" ist das Präfix, und Folgendes der Befehl für den Assembler:
Der Debugger des Emulators kann mit dem `ebreak` Breakpint-Befehl gestartet werden.
Der Debugger des Emulators kann mit dem `ebreak` Breakpoint-Befehl gestartet werden.
## Verfügbare Objekte und Funktionen
In dem interaktiven Debugger sind folgende Variablen definiert:
* `cpu` Der CPU der den `ebreak` gelesen hat
* `cpu` Die CPU, die den `ebreak` gelesen hat
* `mem` und `mmu` ermöglichen Zugriff auf den Arbeisspeicher
* `regs` representiert die Register des CPUs
* `regs` repräsentiert die Register der CPU
Des weiteren sind ein paar hilfsfunktionen verfügbar:
Des weiteren sind ein paar Hilfsfunktionen verfügbar:
* `run_ins('name', 'arg1', 'arg2', 'arg3')` führt den befehl `name arg1, arg2, arg3` an der aktuellen Stelle im CPU aus.
* `step()` führt den nächte Befehl aus
* `cont()` beendet den debugger
* `dump(address)` gibt den Inhalt des Arbeitsspeichers an der Adresse `address` aus, das Verhalten der Funktion ist durch einige Parameter anpassbar, die später weiter erklärt werden.
* `run_ins('name', 'arg1', 'arg2', 'arg3')` führt den Befehl `name arg1, arg2, arg3` an der aktuellen Stelle im Programm aus.
* `step()` führt den nächsten Befehl aus
* `cont()` beendet den Debugger
* `dump(address)` gibt den Inhalt des Arbeitsspeichers an der Adresse `address` aus. Das Verhalten der Funktion ist durch einige Parameter anpassbar, die später weiter erklärt werden.
## Die `dump()` Funktion
Mit hilfe dieser Funktion kann der Arbeitsspeicher angezeigt werden. Die Signator der Funktion ist etwas komplexer:
Mit hilfe dieser Funktion kann der Inhalt des Arbeitsspeichers angezeigt werden. Die Signatur der Funktion ist etwas komplexer:
Es ist möglich einen festgelegten bereich an zu zeigen, in dem `start` und `end` angegeben werden. Es ist auch möglich nur `start` zu verwenden, dann werden `rows` (standardmäßig 10) zeilen um die adresse `start` herum ausgegeben, und die Werte an der Adresse hervorgehoben. Eine andere Stelle zum hervorheben kann mit `highlight` bestimmt werden.
Es ist möglich einen festgelegten Bereich anzuzeigen, indem `start` und `end` angegeben werden. Es ist auch möglich nur `start` zu verwenden, dann werden `rows` (standardmäßig 10) Zeilen um die Adresse `start` herum ausgegeben, und die Werte an der Adresse hervorgehoben. Eine andere Stelle zum Hervorheben kann mit `highlight` bestimmt werden.
Mit `bytes_per_row` kann angegeben werden, wie viele bytes pro zeile ausgegeben werden, und `group` gibt an, wie viele bytes zusammen zu einem Wert gruppiert werden (z.B. praktisch um 32bit Inteegers anzuschauen).
Mit `bytes_per_row` kann angegeben werden, wie viele Bytes pro Zeile ausgegeben werden, und `group` gibt an, wie viele Bytes zusammen zu einem Wert gruppiert werden (z.B. praktisch um 32bit Integers anzuschauen).
Das Ausgabeformat kann mit dem `fmt` Argument angegeben werden, standardmäßig ist das `hex`, andere möglichkeiten sind `int`, `uint` für Ganzzahlen und `char` für Ascii-Text.
Das Ausgabeformat kann mit dem `fmt` Argument angegeben werden, standardmäßig ist das `hex`. Andere Möglichkeiten sind `int`, `uint` für positive Ganzzahlen und `char` für ASCII-Text.