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.
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.