Gibt es hier ARM Assembler Profis?

OP Persönliche Seite #5656102
Lesenswert?

Guten Abend,

ich versuche einen Profiling-Code auf einem Cortex-M1 zum laufen zu 
bekommen.

Der verwendet armv6-m und den Code, den ich gefunden habe, verwendet 
armv7-m.
1
  push {r0, r1, r2, r3, lr}     /* save registers */
2
  bic r1, lr, #1                /* R1 contains callee address, with thumb bit cleared */
3
  ldr r0, [sp, #20]             /* R0 contains caller address */
4
  bic r0, r0, #1                /* clear thumb bit */
5
  bl _mcount_internal           /* jump to internal _mcount() implementation */
6
  pop {r0, r1, r2, r3, ip, lr}  /* restore saved registers */
7
  bx ip                         /* return to caller */

Das hier bekomme ich als Fehler:
1
../profiling/profiler.S:21: Error: cannot honor width suffix -- `bic r1,lr,#1'
2
../profiling/profiler.S:23: Error: cannot honor width suffix -- `bic r0,r0,#1'
3
../profiling/profiler.S:25: Error: cannot honor width suffix -- `pop {r0,r1,r2,r3,ip,lr}'

Scheinbar kann armv6-m1 nur Thumb-Instruction und das sind 
ARM-Instructions vom armv7.

Ich hab bisserl rumgespielt, komm aber auf die schnelle auf keinen 
grünen Zweig.

Könnte das mir bitte jemand für armv6-m abändern?

Ich vermute, ich würde unendlich viele Fehler machen und würde dafür 
ewig brauchen ... Irgendwann würde es schon klappen, aber ich vermute 
einen Aufwand von mindestens einem halben Tag, bis ich mich da 
eingelesen habe.

Normalerweise bin ich nicht so faul!

Danke!
Mampf
OP Persönliche Seite #5656133
Lesenswert?

1
  push {r0, r1, r2, r3, lr}     /* save registers */
2

3
  movs r0, #1
4
  mov r1,lr
5
  bics r1, r0
6
  //bic r1, lr, #1                /* R1 contains callee address, with thumb bit cleared */
7

8
  ldr r0, [sp, #20]             /* R0 contains caller address */
9
  movs r1, #1
10
  bics r0, r0
11
  //bic r0, r0, #1                /* clear thumb bit */
12

13
  bl _mcount_internal           /* jump to internal _mcount() implementation */
14

15
  pop {r0}
16
  mov lr, r0
17
  pop {r0}
18
  mov ip, r0
19
  pop {r0, r1, r2, r3}
20
//  pop {r0, r1, r2, r3, ip, lr}  /* restore saved registers */
21
  bx ip                         /* return to caller */

Ich hab mich jetzt mal durchgekämpft ... vlt doch nicht so schlimm, wie 
gedacht :-)

Dieses Condition-Flag bei zB movs und bics ist mir noch ein Rätsel, da 
ich die Auswirkungen nicht kenne.

Aber die CPU hard-faultet zumindest nicht mehr ...

Falls jemand noch eine Idee hat oder sieht, dass ich was falsch gemacht 
habe, wäre ich über einen Hinweis dankbar.

VG
Mampf
Gast #5656190
Lesenswert?

Dennis H. schrieb:
> Das S bei MOVS steht für setze Flags.
> Beim ARMv6-M gibt für MOV R,#Konstante nur die MOVS-Version (Thumb1).

ok, Danke!

Wenn mein Cortex M1 nur thumb kann, machen die BIC keinen Sinn, bzw sind 
sogar falsch, weil die adressen alle bit0 gesetzt haben müssen ...

Bei thumb scheint es keinen pop lr zu geben sondern nur einen push lr + 
pop pc
OP Persönliche Seite #5656304
Lesenswert?

Aber eine Frage ...

Das versteh ich einfach nicht^^

Wie soll man LR vom Stack runterholen, wenn man keine Register 
manipulieren darf?

Hätte pop {r0} und mov lr,r0 gemacht, aber dabei wird r0 verändert.

Kann man LR direkt laden über den Stackpointer?

Zudem kommt noch, dass die Reihenfolge der push und pop wohl egal ist, 
da sie nicht hintereinander auf den Stack gepusht werden, sondern quasi 
in dedizierte Slots?
Beitrag #5656334 wurde von einem Moderator gelöscht.
OP Persönliche Seite #5656365
Lesenswert?

A. K. schrieb:
> Mampf F. schrieb:
>> Wie soll man LR vom Stack runterholen, wenn man keine Register
>> manipulieren darf?
>
> Du darfst R12 verändern und du kannst temporär Platz auf dem Stack
> schaffen um dort Register abzuladen.

Aaah, ich bin so dumm! Stand ja sogar in dem Bild, das ich gepostet 
hatte ...

Danke!

Jetzt sollte ich problemlos damit zurecht kommen
OP Persönliche Seite #5656559
Lesenswert?

Guten Morgen!

eine kleine Verständnisfrage habe ich noch ...

Also laut dem Bild aus einem der letzten Postings muss man ganz zum 
Schluss LR vom Stack holen und die funktion verlassen.

Die multi-Push und -Pops verwirren mich noch.

Also wenn zB push {r0-r3, lr} nichts anderes macht als

push {r0}
push {r1}
push {r2}
push {r3}
push {lr}

dann müsste pop {r0-r3, ip, lr} das hier machen?

pop {lr}
pop {ip}
pop {r3}
pop {r2}
pop {r1}
pop {r0}

Das wäre aber dann falsch, weil LR erst ganz zum Schluss gepoppt werden 
muss, beim pop aber als erstes gepoppt wird?

Dann hab ich aber noch das hier gefunden:

> I'm trying to understand the start and end of functions in ARM
> assembly:
>
> PUSH {R0-R2, LR}
> POP {R0-R2, PC}
> Looking at this piece of code in IDA here's what I understood (Lets
> assume SP is 0x100):
>
> PUSH R0 ; sp = 0xFC
> PUSH R1 ; sp = 0xF8
> PUSH R2 ; sp = 0xF4
> PUSH LR ; sp = 0xF0
> POP R0 ; sp = 0xF4
> POP R1 ; sp = 0xF8
> POP R2 ; sp = 0xFC
> POP PC ; sp = 0x100

mit der Antwort:
> When you PUSH or POP a bunch of registers, they always go into memory
> in the same relative positions, regardless of direction. The lowest-
> numberd register is stored at and loaded from the lowest address. So
> in this example everything will go back to the original register,
> except LR->PC.

Also wenn das stimmt, und ich keinen POP {LR} habe, dann kann ich aber 
auch kein

mov r12, r0
pop {r0}
mov lr, r0
mov r0,r12

machen ... Das widerspricht meinem Verständnis für einen Stack, aber vlt 
ist es bei ARM anders als bei allen anderen?^^

Sicher hab ich da nur ein ganz einfach zu lösendes Verständnisproblem 
...

Versteht man das, womit ich meine Probleme hab? :-)

Viele Grüße,
Mampf
OP Persönliche Seite #5656564
Lesenswert?

Ok, jetzt check ich es ...
> Registers are stored on the stack in numerical order, with the lowest
> numbered register at the lowest address.

Super Formulierung ARM lol ...

D.h. gepusht wird vom höchsten zum niedrigsten Register und gepoppt 
andersrum.

Dann passt der ursprüngliche Code, denn LR wird dann tatsächlich ganz 
zum schluss gepoppt, wenn der Stack schon wieder leer ist.

Hach na endlich!

Das hier werde ich dann nachher testen:
1
  push {r0, r1, r2, r3, lr}     /* save registers */
2
//  bic r1, lr, #1                /* R1 contains callee address, with thumb bit cleared */
3
  mov r1, lr
4
  ldr r0, [sp, #20]             /* R0 contains caller address */
5
//  bic r0, r0, #1                /* clear thumb bit */
6
  bl _mcount_internal           /* jump to internal _mcount() implementation */
7

8
  pop {r0-r3}
9
  mov r12, r0
10
  pop {r0}
11
  mov ip, r0
12
  pop {r0}
13
  mov lr, r0
14
  mov r0, r12
15
  
16
//  pop {r0, r1, r2, r3, ip, lr}  /* restore saved registers */
17
  bx ip
Persönliche Seite #5656610
Lesenswert?

Mampf F. schrieb:
> mov r12, r0
>   pop {r0}
>   mov ip, r0

ip ist ein Alias für r12. Das ist so also relativ sinnlos. IP steht hier 
für "Intra-Procedure-call scratch register" und nicht für "Instruction 
Pointer" wie bei x86. Der heißt bei ARM "PC" ("Program Counter") und ist 
ein Alias für r15.

Siehe dazu auch den AAPCS:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf

Hab jetzt nicht alles auseinander genommen, aber ich glaube da stimmt 
noch was nicht, insbesondere verlierst du den alten "r0" Wert. So könnte 
es gehen:
1
push {r0, r1, r2, r3, lr}
2
movs r2, #1
3
mov r1, lr
4
bic r1, r2
5

6
ldr r0, [sp, #20]             /* R0 contains caller address */
7
bic r0, r2
8

9
bl _mcount_internal
10

11
ldr r0, [sp, #20]    @ Lade vorheriges LR
12
mov lr, r0
13

14
ldr r0, [sp, #16]    @ Lade Rücksprungadresse (selbst gepusht)
15
mov r12, r0
16

17
pop {r0, r1, r2, r3}  @ Register wiederherstellen
18
add sp, #8        @ Springe über LR und r12 hinweg
19

20
bx r12

Echt knifflig, beim ARMv7-A ist das netter ;-) Der Trick ist hier, die 
beiden Register "direkt" vom Stack per LDR zu laden, bevor man die 
r0-r3 wiederherstellt und solange man die also noch nutzen kann. Nach 
der Wiederherstellung springt man über diese 8 Bytes hinweg indem man 8 
auf den SP addiert.

Das "BIC" Zeug könntest du auch in der _mcount_internal in C machen, das 
spart die Assembler-Schnipselei.
OP Persönliche Seite #5657961
Lesenswert?

Gab noch ein paar andere Sachen, die ich in dem gprof-Tutorial ändern 
musste.

Im PC-sampler wurde immer die falsche Adresse für die unterbrochene 
Funktion ermittelt (auf der Webseite gibt es beim Systick-Sampler 
irgendwo einen [14] Index, der musste bei mir [10] sein - ermitteln über 
Disassembler, Memory-Map und Register ... Ich liebe die 
Cortex-Debug-Möglichkeiten!)

https://mcuoneclipse.com/2015/08/23/tutorial-using-gnu-profiling-gprof-with-arm-cortex-m/

Dann lief es im Prinzip richtig und herausgekommen ist über gprof2dot 
sowas wie im Anhang :)

Achja, da sieht man deutlich, wo 4/5tel der CPU-Leistung verschwindet xD

Also ich denke, das Profiling ist richtig **geil** :-)
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren