Forum: Compiler & IDEs RISCV (CH32V003): GNU Assembler übersetzt "CALL" nicht korrekt (oder bin ich nur zu doof?)


von Thomas T. (knibbel)


Lesenswert?

Hallo zusammen,

ich versuche mir seit ein paar Tagen ein kleines 
HelloWorld-Blink-Programm direkt in RISCV-Assembler für den CH32V003 zu 
bauen und bekam es erst zum Laufen, als ich Verzögerungsschleifen, die 
mit dem Pseudo-Befehl "CALL" aufgerufen wurden, direkt ins Hauptprogramm 
eingebaut hatte und somit Unterprogramme vermieden hatte.

Ich habe inzwischen das Problem auf den folgenden "Zwei 
Zeilen-Quelltext" reduzieren können, welcher bei mir falsch übersetzt 
wird:
1
Start:        CALL Subroutine
2
Subroutine:   RET

Vorweg: Zum Übersetzen benutze ich das Paket 
"xpack-riscv-none-elf-gcc-14.2.0-3", die Version des im Paket 
enthaltenen Assemblers ist "GNU assembler (xPack GNU RISC-V Embedded GCC 
x86_64) 2.43.1".

Wenn man die beiden Zeilen mittels "riscv-none-elf-as.exe myfile.s" 
übersetzt und sich danach das Ergebnis mittels 
"riscv-none-elf-objdump.exe -d a.out" anschaut, dann sieht man 
folgendes:
1
a.out:     file format elf32-littleriscv
2
3
Disassembly of section .text:
4
5
00000000 <Start>:
6
   0:   00000097                auipc   ra,0x0
7
   4:   000080e7                jalr    ra # 0 <Start>
8
9
00000008 <Subroutine>:
10
   8:   00008067                ret

Der CALL-Befehl wird hier in zwei Befehle aufgeteilt (das ist auch noch 
in Ordnung), es fehlt jedoch im zweiten Befehl (jalr) der notwendige 
Offset, damit auch zum Unterprogramm gesprungen wird.

Richtigerweise sollte meiner Ansicht nach diese Zeile so aussehen:
1
   4:   008080e7                jalr    8(ra) # 8 <Subroutine>

Wenn ich in meinem HelloWorld-Blink-Programm die übersetzten 
CALL-Befehle derart patche (also die jalr-Befehle mit dem notwendigen 
Offset versehe), dass sie auf das Unterprogramm mit der 
Verzögerungsschleife zeigen, dann funktioniert auch alles, so wie es 
soll...

Es ist also offensichtlich der CALL-Befehl, der nicht korrekt übersetzt 
wird. Aber kann das sein? Ich habe auch ältere Versionen des Assemblers 
getestet, es ist überall dasselbe Verhalten.

Fehlt vielleicht hier nur eine Option, die ich dem Assembler mitgeben 
muss? Oder habe ich hier wirklich einen Fehler entdeckt? Das kann ich 
mir aber bei so einem grundlegenden Befehl nicht vorstellen...

Habt ihr irgendwelche Ideen?

Gruß
Thomas

: Bearbeitet durch User
von Manuel H. (Firma: Universität Tartu) (xenos1984)


Lesenswert?

Probier mal den Befehl:
1
riscv-none-elf-objdump.exe -d -r a.out

Dann siehst du, dass die erzeugte Objektdatei noch eine Relokation 
enthält, die auf das Unterprogramm verweist. Die richtige Adresse setzt 
dann der Linker ein.

von Cartman E. (cartmaneric)


Lesenswert?

Ausserdem immer an die Effekte der Pipeline denken!
Wer schon mit MIPS zu tun hatte, ist klar im Vorteil.

von Thomas T. (knibbel)


Lesenswert?

Manuel H. schrieb:
> Probier mal den Befehl:
>
>
1
riscv-none-elf-objdump.exe -d -r a.out
>
> Dann siehst du, dass die erzeugte Objektdatei noch eine Relokation
> enthält, die auf das Unterprogramm verweist. Die richtige Adresse setzt
> dann der Linker ein.

DANKE! Das ist ein wertvoller Hinweis! Ich hatte gedacht, ich komme ohne 
einen Linker klar und der Assembler macht schon alles für mich. Da lag 
also mein Denkfehler.

Vielleicht sind folgende Befehle ja eine Hilfe für Gleichgesinnte, die 
ohne eine große IDE auf der Kommandozeile RISCV-Programme für den 
CH32V003 assemblieren wollen:

Assemblieren mittels:
1
riscv-none-elf-as.exe -agl blinken.s

Dann Linken mittels:
1
riscv-none-elf-ld.exe -o a.o -Ttext 0 -e Coldstart a.out

Danach die Binärdatei bauen mittels:
1
riscv-none-elf-objcopy.exe -O binary a.o a.bin

Und Anschauen, was der Assembler gebaut hat, geht mittels:
1
riscv-none-elf-objdump.exe -d -r a.o

Gegebenenfalls müssen natürlich die Dateinamen angepasst werden.

Gruß
Thomas

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

as erzeugt aus der Assembly-Quelle eine Object Datei, was aber kein 
ausführbarer Code ist. Dazu muss mindestens Gelinkt werden, und evtl. 
braucht's noch zusätzlichen Code (Startup Code, CRT, Device 
Initialisierung, Vektor Tabellen, Linker Script, etc).

Am einfachsten bekommt man das, wenn man gcc als Treiber verwendet 
(für.S oder.sx oder mit -x assembler-with-cpp).

Weiterer Vorteil ist, dass man dann auch den C-Präprozessor im asm 
verwenden kann.

von Manuel H. (Firma: Universität Tartu) (xenos1984)


Lesenswert?

Wenn man ohne IDE arbeiten und den Kompiliervorgang etwas vereinfachen 
möchte, kann man auch PlatformIO Core benutzen. Da kann man über die 
Kommandozeile das Projekt erstellen, und statt einzeln die einzelnen 
Programme aufzurufen, tut es ein "pio run".

https://github.com/Community-PIO-CH32V/platform-ch32v

von Harald K. (kirnbichler)


Lesenswert?

Manuel H. schrieb:
> Da kann man über die
> Kommandozeile das Projekt erstellen, und statt einzeln die einzelnen
> Programme aufzurufen, tut es ein "pio run".

Naja, ein simples Makefile würde genügen, da muss man nicht mit so einer 
fetten Keule herumwedeln.

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.