Ich wollte ein Stück Software, das in Assembler vorliegt, tracen,
möglichst über SWO, also Nachrichten im SWV-View des STM32CubeIDE.
Aus einem C-Beispielprogramm heraus lief es ja zuletzt. Die
Tracefunktion ITM_SendChar(char); ist aber ein Stück aus einer
C-Bibliothek, die dazugelinkt wird.
Die habe ich ja in Assembler nicht. Ich kann vielleicht ein bißchen
Assemblercode noch dazupacken, der vielleicht das SWV-Protokoll
implementiert, aber wo und wie?
Also wirklich, das
__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch)
{
if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */
((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0
enabled */
{
while (ITM->PORT[0U].u32 == 0UL)
{
__NOP();
}
ITM->PORT[0U].u8 = (uint8_t)ch;
}
return (ch);
}
in Assembler zu schreiben, kann doch wohl kein Problem sein, oder? Im
Prinzip ist das doch nur ein UART (zumindest die übliche Einstellung),
man schreibt das Byte ins Sende-FIFO, wenn Platz ist. Die Abfrage, ob
"ITM enabled", kann man sich ja auch ggf. noch sparen.
Für z. B. STM32H750 im RM0433, 60.6.3, ITM stimulus register 0, da ist
die FIFO-Behandlung erklärt.
Der kompliziertere Teil sind die ganzen Einstellungen davor.
Christoph K. schrieb:> ITM_SendChar(char); ist aber ein Stück aus einer C-Bibliothek,> die dazugelinkt wird. Die habe ich ja in Assembler nicht.
Hast du ein Programm, in dem ITM_SendChar() in C funktioniert und in
dem dein Stück Assembler auch drin ist? Dann könnte es mit 3 Zeilen
Assembler funktionieren:
Bauform B. schrieb:> Christoph K. schrieb:>> ITM_SendChar(char); ist aber ein Stück aus einer C-Bibliothek,>> die dazugelinkt wird. Die habe ich ja in Assembler nicht.>> Hast du ein Programm, in dem ITM_SendChar() in C funktioniert und in> dem dein Stück Assembler auch drin ist? Dann könnte es mit 3 Zeilen> Assembler funktionieren:>
1
.global ITM_SendChar
2
> movs r0, #72
3
> bl ITM_SendChar
> 72 ist der Anfang von "Hallo, Welt" ;)
Nein, die Konstellation liegt so nicht vor. Ich muß die
C-Implementierung in Assemblercode einbinden.
Aber ich werde mich mal an das von A.B.(gast) Vorgeschlagene begeben.
Konnte jetzt ITM_SendString und ITM_SendChar in Assembler
implementieren:
1
.equ ITM , 0xE0000000
2
main:
3
ldr r0,=hello
4
bl ITM_SendString
5
pop {r0,r1}
6
b .
7
hello: .asciz "hi!"
8
.global ITM_SendChar,ITM_SendString
9
10
ITM_SendString: @ r0 - address of null-terminated String
11
push {r0-r1}
12
mov r1,r0
13
1: mov r0,#0
14
ldrb r0,[r1]
15
cbz r0,2f
16
bl ITM_SendChar
17
add r1,#1
18
b 1b
19
2: pop {r0-r1}
20
bx lr
21
putchar:
22
ITM_SendChar:
23
push {r0-r1}
24
1: ldr r1, = ITM
25
ldr r1,[r1]
26
cmp r1,#0
27
beq 1b
28
ldr r1, = ITM
29
str r0,[r1]
30
pop {r0-r1}
31
bx lr
Aber der Debugger (STM32CubeIDE) verhält sich merkwürdig.
Es wird "hi!" in der SWV ITM Console ausgegeben, aber wenn ich durch
ITM_SendSTring durchsteppe, findet die loop auch die terminierende 0,
fällt durch auf Label 2: und steppe ich bis zum bx lr springt der
Debugger zurück auf `add r1,#1` (!?).
Auch seltsam sind die vielen (sehr vielen) blauen trace-messages Zeile
590 in simple.s im Debug Window.
Modifiziere ich den Code etwas, so springt der Debugger auf die
branch-Instruction zurück.
1
.equ ITM , 0xE0000000
2
main:
3
ldr r0,=hello
4
bl ITM_SendString
5
pop {r0,r1}
6
b .
7
hello: .asciz "hi!"
8
.global ITM_SendChar,ITM_SendString
9
10
ITM_SendString: @ r0 - address of null-terminated String
11
push {r0-r1}
12
mov r1,r0
13
1: mov r0,#0
14
ldrb r0,[r1],#1
15
cbz r0,2f
16
bl ITM_SendChar
17
b 1b @ <<<<<<<<<\
18
2: pop {r0-r1} @ | erratischer Rücksprung des Debuggers nach dem bx lr
Kann es sein, dass du gerade die falschen Register rettest? Bei r0
sollte klar sein, dass du das nicht retten musst. r1 bis r3 darfst du
bei Bedarf auch "zerstören". Umgekehrt benutzt du das lr um
ITM_SendChar() aufzurufen, aber das wird noch für die Rückkehr nach main
gebraucht, sollte also gerettet werden.
Ganz nebenbei finde ich den Namen putchar schöner als ITM_SendChar. Bei
letzterem denke ich, dass das eine CubeMX-Funktion ist.
Bauform B. schrieb:> Kann es sein, dass du gerade die falschen Register rettest? Bei r0> sollte klar sein, dass du das nicht retten musst. r1 bis r3 darfst du> bei Bedarf auch "zerstören". Umgekehrt benutzt du das lr um> ITM_SendChar() aufzurufen, aber das wird noch für die Rückkehr nach main> gebraucht, sollte also gerettet werden.>> Ganz nebenbei finde ich den Namen putchar schöner als ITM_SendChar. Bei> letzterem denke ich, dass das eine CubeMX-Funktion ist.
Danke. Wertvoller Tipp. Ja klar, bei geschachtelten Subroutine Aufrufen
muß man eingangs in der SR push {...,rl} und den return mit pop {...,pc}
machen. ARM hat ja keine "RET" Instruction, mit der die Returnadresse
vom Stack geholt wird. Bereinige das gerade, aber Auprobieren kann ich
es erst nach dem Fußballspiel.
Und ITM_SendChar wird durch putchar ersetzt. Hatte ich ja schon
vorgesehen.