Forum: PC-Programmierung GNU Compiler für RISC-V


von M. M. (blackcow)


Lesenswert?

Hallo,

es geht um die Programmierung einer RISC-V CPU. Diese ist in einem FPGA 
implementiert und funktioniert auch, genauso wie die Programmierung mit 
Assembler. Das ist aber auf Dauer unpraktisch. Nun möchte ich mit C 
arbeiten. Ich habe hier einen GNU Compiler, der auch RISC-V kann. Wie 
erzeuge ich nun einen Binärcode, den ich direkt auf der CPU laufen 
lassen kann? Also kein ausführbares Programm, das bspw. auf Linux läuft.

von S. R. (svenska)


Angehängte Dateien:

Lesenswert?

Der gcc weiß nicht, wie dein System aussieht, also musst du ihm das 
mitteilen. Dazu brauchst du ein Linkerscript und Startup-Code, die du zu 
deinem Code dazulinken musst.

Ich hab mal ein Projekt von mir zusammengekürzt und angehängt. Ist in 
der Form ungetestet, sollte aber grob tun. Mein System hat einen RV32I, 
den Einsprungpunkt bei 0x0000 und 32 KB RAM ab dort.

Wenn das bei dir nicht zutrifft, musst du den Compiler im Makefile 
anpassen, sowie die Adressen im Linkerscript ändern. Der Startup-Code 
leert .bss und ruft main() auf, mehr nicht.

Das reicht vermutlich weder für C++ noch für eine ordentliche libc. 
Zumindest die Konstruktoren müssen vor main() aufgerufen werden, und 
notwendige Syscalls brauchst du auch. Aber es ist ein guter nackter 
Start ohne Voraussetzungen.

von M. M. (blackcow)


Lesenswert?

Damit hast du mir einen Einstiegspunkt gegeben, vielen Dank!

Darf man fragen wo du das gelernt hast? Ich wüsste bei dem inline 
Assembler z.B. gar nicht wie das mit dem Compiler zusammenhängt und was 
der braucht. Vermutlich Doku von GCC durchackern oder?

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

M. M. schrieb:
> Darf man fragen wo du das gelernt hast?

Ich habe für mehrere Architekturen (x86, Cortex-M3, Epiphany, RV32) 
Startup-Code geschrieben, und mir wurde Assembler irgendwann zu blöd. 
:-) Außerdem habe ich viel Startup-Code gelesen.

Ein Programm besteht grundsätzlich aus Code und Daten, die man 
verschieden klassifiziert (unter anderem .text, .data, .bss, .rodata). 
Der Linker weist diesen Programmstücken ihre endgültigen Adressen zu. 
Das Linkerscript sagt dem Linker, was er wohin zu legen hat (Flash von 
xxx bis yyy, RAM von qqq bis zzz, .text soll ins Flash, .data ins RAM, 
uswusf.)

Die Aufgabe des Startup-Codes ist es, das System so weit zu 
initialisieren, dass es den vom Compiler generierten Code ausführen 
kann. Was genau notwendig ist, hängt von der Architektur und vom 
Compiler ab. Startup-Code enthält eigentlich immer Assembler (nur die 
Cortex-M kann man komplett in C starten), oder ist vollständig in 
Assembler geschrieben.

Ein x86 startet im Real Mode (16 Bit), den musst du erstmal treten, 
damit er 32-bittigen Code ausführen kann - oder er kommt aus einem 
Bootloader wie GRUB, dann gilt dessen Dokumentation. Epiphany und ARM 
starten im Interrupt-Kontext. Bei x86 und Epiphany ist die Startadresse 
festgelegt, ein ARM liest sie aus dem Flash. Bei RISC-V ist nichts 
festgelegt, es gilt die Dokumentation des spezifischen Cores bzw. der 
Plattform. Und so weiter.

Für gcc muss mindestens der Stack funktionieren und ".bss" muss leer 
sein, für eine libc solltest du noch die Konstruktoren ausführen (und 
die Destruktoren, wenn du ordentlich bist). Das ist bei gcc wieder 
architekturspezifisch (x86 ist etwas anders als die anderen Kinder).

Du musst dich an viele Dokumentationen halten, wenn du eine neue 
Architektur/Plattform aus dem Sumpf ziehen willst. Andererseits brauchst 
du natürlich nur das tun, was für deine Anwendung auch nötig ist - ich 
kann z.B. auf eine funktionierende libc verzichten (gcc braucht aber 
memcpy, memmove, memset und memcmp).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.