Forum: Mikrocontroller und Digitale Elektronik Bitte um Erklärung für Assembler Befehl


von Falko J. (spacefrog)


Lesenswert?

Hallo zusammen,

ich versuche gerade ein hard fault auf einem Cortex M7 zu debuggen. Hab 
mir dann denn Stack Pointer angeschaut um zu schauen bei welchem Befehl 
er in den Hard Fault Handler gesprungen ist. Im Stackpointer steht 
0x20016C08. Im assembler listing finde ich an der Adresse folgenden 
Befehl:


   0x20016c08: 0x0000         MOVS      R0, R0

Das kopiert doch den Wert von Register 0 nach 0, wozu kann das gut sein?
Oder hab ich ein ganz großen Denkfefehler?

LG
Falko

von Lutz H. (luhe)


Lesenswert?

Was macht movs? setzt es irgendwelche flags?

von Einer (Gast)


Lesenswert?

Und die Befehle drumherum?

0000 sieht für mich erstmal verdächtig nach Datenbereich aus.

MfG

von Karl H. (kbuchegg)


Lesenswert?

Falko J. schrieb:
> Hallo zusammen,
>
> ich versuche gerade ein hard fault auf einem Cortex M7 zu debuggen. Hab
> mir dann denn Stack Pointer angeschaut um zu schauen bei welchem Befehl
> er in den Hard Fault Handler gesprungen ist. Im Stackpointer steht
> 0x20016C08.

Ist das der Inhalt vom Stackpointer oder der Inhalt des Speichers auf 
den der Stackpointer zeigt?

Der Stackpointer sagt dir, wo du die Adresse finden kannst, von wo aus 
der Fault Handler aufgerufen wurde.

von Amateur (Gast)


Lesenswert?

Ich kenne mich ja mit dem Cortex M7 nicht aus, aber könnte das das 
Äquivalent zu NOP sein?

von Karl H. (kbuchegg)


Lesenswert?

Amateur schrieb:
> Ich kenne mich ja mit dem Cortex M7 nicht aus, aber könnte das das
> Äquivalent zu NOP sein?

Der wird wohl kaum einen Hard Fault auslösen.

ich denke eher, dass die Adresse insofern falsch ist, dass dort gar 
nicht die Instruktion zu finden ist, die den Fault ausgelöst hat.

von Falko J. (spacefrog)


Angehängte Dateien:

Lesenswert?

Karl H. schrieb:
> Ist das der Inhalt vom Stackpointer oder der Inhalt des Speichers auf
> den der Stackpointer zeigt?

Das war der Inhalt des Stackpointers. Genauer des "Main Stack Pointers" 
es gibt auch noch einen "Process Stack Pointer", da war aber alles 0. 
(Was ist das eigentlich?)

Das Problem ist, das der hard fault nur sporadisch auftritt und dann 
auch immer andere Werte im Stackpointer stehen...

Einer schrieb:
> 0000 sieht für mich erstmal verdächtig nach Datenbereich aus.

Was genau bedeutet das 0000???

Ich hab jetzt noch mal ein Hard Fault hinbekommen, sieht in der IDE 
diesmal so wie auf dem Bild aus. Die Fehler scheinen nur bei 
eingeschalteten D-Cache und Optimierung "Speed" aufzutreten....

von Karl H. (kbuchegg)


Lesenswert?

Falko J. schrieb:

> Das war der Inhalt des Stackpointers.

Gut. Unter dieser Adresse findest du im Speicher die Returnadresse (d.h. 
solange der Hardfault Handler seinerseits noch nichts auf dem Stack 
abgelegt hat).

Im SP steht 0x20016b90
D.h. an dieser Position steht gerade die Spitze des Stacks. Auf dem 
Stack liegt offenbar der Wert 0x20013cb4

Dort ist die Adresse von wo aus der Aufruf tatsächlich ausgelöst wurde.

von Lothar (Gast)


Lesenswert?

Lutz H. schrieb:
> Was macht movs? setzt es irgendwelche flags?

Richtig, das ist zum Flags updaten. Beispiel: es wird etwas aus dem 
Speicher geladen und man will dann z.B. prüfen of es 0 ist:

LDR   R0, [R1]  ; read into R0
MOVS  R0, R0    ; dummy to update flags

Geht alternativ natürlich auch mit CMPS

Amateur schrieb:
> aber könnte das das Äquivalent zu NOP sein?

Für NOP würde man MOV ohne S nehmen.

von Sascha (Gast)


Lesenswert?

Hallo,
also der Befehl MOVS r0,r0 löst mit sicherheit nicht den Fehler aus, 
sondern der Fehler entsteht etwas füher oder danach. Das S steht für 
Flag Update, d.h. die Flags die beim copieren von R0 auf R0 entstehen 
werden im Statusregister übernommen. Du siehst ja es ist ein Hardfault 
somit lässt sich ja nachschauen, was alles einen Hardfault verursachen 
kann.
Aber Intressant ist warum dein PC (Programmcounter) auf 0x080xxxx steht, 
das dürfte der Flash_ROM Bereich sein, er müsste aber eigentlich auf der 
Spiegelung bei 0x00xxxx stehen. Da stimmt irgend eine initialisierung 
nicht, oder der Debugger macht einen Adressoffset !!!

Gruß Sascha

PS.
Cortex M7 von Atmel mit 300MHz und Floating Point 64 Bit ist echt mega 
cool für DSP,

von Klaus (Gast)


Lesenswert?

Ich schlage vor, die Doku zu Hard-Faults des M7 zu lesen. Es gibt da 
einige Unterschiede zwischen den Cores. Ganz allgemein, gibt es einige 
Register mit Flags, die den Fehler näher qualifizieren. Diese Bits 
werden unter Umständen nur gesetzt (weiß jetzt nicht mehr bei welchem 
Core ich das so gelesen habe, aber es war ein niedriger M-Core) wenn 
zuvor andere Bits gesetzt wurden und/oder ein Hard-Fault-Handler im Code 
eingefügt wurde. Vermutlich ist der Mechanismus bei verschiedenen Cores 
etwa ähnlich.

Ich mag mich irren, aber soweit ich mich erinnere, wird, soweit ein 
Hard-Fault-Handler vorhanden ist, ein ganzer Satz an Registerwerten auf 
dem Stack abgelegt. Ob der oberste gerade der letzte PC ist, weiß ich 
gerade nicht. Ich würde es nachlesen.

Beim Keil (auf einem ST) habe ich auch erlebt, dass Hard-Faults 
auftraten, für die kein Fehlerbit gesetzt wurde. Dann liess sich das 
mit wiederholtem Clean-Build und Start beheben und er trat erst wieder 
nach einigen Tagen (wieder nicht klärbar) auf. Möglicherweise ist das 
beim IAR ähnlich.

Tut mir leid, wenn ich nicht mehr sagen kann, aber ich hoffe das hilft 
ein bisschen. (Karl Heinz weiss es unter Umständen aus näherer 
Vergangenheit und auf den M7 bezogen, präziser).

von Karl H. (kbuchegg)


Lesenswert?

Ich weiss es auch nicht genauer und müsste auch nachlesen.

Aber eines weiss ich mit Sicherheit. Der Stackpointer ist nur der 
Ausgangspunkt der Kette, mit der man die problematische Instruktion 
eventuell finden kann. Im Stackpointer steht ganz sicher nicht drinnen, 
welche Instruktion das war. Das folgt schon alleine daraus, wie ein 
Stack funktioniert.
D.h. wozu die Bytewerte an der Stelle des Stackpointer-Wertes im RAM 
disassemblieren ist vollkommen uninteressant. Das sind sowieso keine 
Instruktionen sondern einfach nur Daten (ob Registerinhalte oder 
Rücksprungadressen müsste man jetzt nachlesen).

von Falko J. (spacefrog)


Lesenswert?

Karl H. schrieb:
> Falko J. schrieb:
>
>> Das war der Inhalt des Stackpointers.
>
> Gut. Unter dieser Adresse findest du im Speicher die Returnadresse (d.h.
> solange der Hardfault Handler seinerseits noch nichts auf dem Stack
> abgelegt hat).
>
> Im SP steht 0x20016b90
> D.h. an dieser Position steht gerade die Spitze des Stacks. Auf dem
> Stack liegt offenbar der Wert 0x20013cb4
>
> Dort ist die Adresse von wo aus der Aufruf tatsächlich ausgelöst wurde.

Aha! Danke... hab da irgendwie ganz falsch gedacht....aber warum ist die 
Adresse 0x20013cb4 und nicht 0x6c002001? steht der Zeiger schon auf der 
nächste freien Stelle?

Und 0x20013cb4 währe ja voll im sram ??? Von da will ich doch gar keine 
Befehle ausführen..

Linker File:
1
/*###ICF### Section handled by ICF editor, don't touch! ****/
2
/*-Editor annotation file-*/
3
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
4
/*-Specials-*/
5
define symbol __ICFEDIT_intvec_start__ = 0x08000000;
6
/*-Memory Regions-*/
7
define symbol __ICFEDIT_region_ROM_start__     = 0x08000000;
8
define symbol __ICFEDIT_region_ROM_end__       = 0x080FFFFF;
9
define symbol __ICFEDIT_region_RAM_start__     = 0x20000000;
10
define symbol __ICFEDIT_region_RAM_end__       = 0x2004C000;
11
define symbol __ICFEDIT_region_ITCMRAM_start__ = 0x00000000;
12
define symbol __ICFEDIT_region_ITCMRAM_end__   = 0x00003FFF;
13
/*-Sizes-*/
14
define symbol __ICFEDIT_size_cstack__ = 0x2500;
15
define symbol __ICFEDIT_size_heap__   = 0x10000;
16
/**** End of ICF editor section. ###ICF###*/
17
18
19
define memory mem with size = 4G;
20
define region ROM_region      = mem:[from __ICFEDIT_region_ROM_start__   to __ICFEDIT_region_ROM_end__];
21
define region RAM_region      = mem:[from __ICFEDIT_region_RAM_start__   to __ICFEDIT_region_RAM_end__];
22
define region ITCMRAM_region  = mem:[from __ICFEDIT_region_ITCMRAM_start__ to __ICFEDIT_region_ITCMRAM_end__];
23
24
define block CSTACK    with alignment = 8, size = __ICFEDIT_size_cstack__   { };
25
define block HEAP      with alignment = 8, size = __ICFEDIT_size_heap__     { };
26
27
initialize by copy { readwrite };
28
do not initialize  { section .noinit };
29
30
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
31
32
place in ROM_region   { readonly };
33
place in RAM_region   { readwrite,
34
                        block CSTACK, block HEAP };

Fragen über fragen

Vielen Dank auch für alle anderen Info's...muss ich mir alles anschauen

von Klaus (Gast)


Lesenswert?

Karl H. schrieb:
> [...]
> Im Stackpointer steht ganz sicher nicht drinnen,
> welche Instruktion das war.
> [...]

ACK. Das auf keinen Fall.

von Falko J. (spacefrog)


Lesenswert?

Sascha schrieb:
> Aber Intressant ist warum dein PC (Programmcounter) auf 0x080xxxx steht,
> das dürfte der Flash_ROM Bereich sein, er müsste aber eigentlich auf der
> Spiegelung bei 0x00xxxx stehen. Da stimmt irgend eine initialisierung
> nicht, oder der Debugger macht einen Adressoffset !!!


Hängt das nicht davon ab, welches Interface man benutzt...hatte da mal 
irgenwo was gelesen...Ich benutze das AXI Interface..(war war in CubeMx 
voreingestellt)...

von Jim M. (turboj)


Lesenswert?

Sascha schrieb:
> Aber Intressant ist warum dein PC (Programmcounter) auf 0x080xxxx steht,
> das dürfte der Flash_ROM Bereich sein, er müsste aber eigentlich auf der
> Spiegelung bei 0x00xxxx stehen. Da stimmt irgend eine initialisierung
> nicht, oder der Debugger macht einen Adressoffset !!!

Blödsinn. Im PC steht eine Flash Adresse (vom Hardfault Handler) drin, 
die wird beim Exception Entry aus der Vektortabelle ausgelesen. Dort 
stehen die korrekten 0x080xxxx Flash Addressen drin, falls das Linker 
Skript nicht völlig kaputt ist.

Der OP müsste mal nachlesen, wie der Stack bei Exception Entry 
auszusehen hat. Das steht in der Beschreibung vom Core, also im 
Zweifelsfall bei ARM (infocenter.arm.com). Cortex M Cores sichern gleich 
etliche Register auf einmal, der PC ist da nur eins von vielen.

Stack auslesen im Hardfault kann aber selbst ein Fault auslösen (z.B. SP 
außerhalb des gültigen Bereichs) -> Lockup.

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Was sagt denn das Hard Fault Status Register HFSR im SCB (0xE000ED2C)
Bit 1 wäre Hard Fault bei Vector Table Fetch
Bit 30 bei Bus-,MemoryManagment- oder Usage-Fault.

Ich empfehle da mal Abschnitt 12.4 im Buch "The Definitive Guide to ARM 
Cortex-M3 and Cortex-M4 Processors"

von Falko J. (spacefrog)


Lesenswert?

Hi Dennis,

das Bit 30 ist gesetzt. Die anderen sind nicht gesetzt.
Im CFSR ist PRECISERR(Bit 9) und BFARVALID (BIT 15) gesetzt.
Im BFAR und MMFAR stehen Werte (siehe Bild) die aber jedesmal 
unterschiedlich sind.... hilft das?

das Buch ist bestellt...

LG
Falko

von Klaus (Gast)


Lesenswert?

M3? M4? Der TO hat "M7" geschrieben. Nicht das es da Unterschiede gibt. 
Ausserdem sind die frei herunterladbaren Beschreibungen an sich 
hinreichend. Da braucht man kein Geld auszugeben. :-)

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Klaus schrieb:
> M3? M4? Der TO hat "M7" geschrieben. Nicht das es da Unterschiede gibt.
> Ausserdem sind die frei herunterladbaren Beschreibungen an sich
> hinreichend. Da braucht man kein Geld auszugeben. :-)

So groß ist der Unterschied zwischen M3/M4 und M7 auch nicht.
Ich halte das Buch ja nicht nur wegen den Zusammengetragenen Infos für 
Wertvoll.
Es gibt da ja auch noch mehr zu lernen.

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Precise bus faults ist ein Speicherzugriffsproblem von einem 
Lade-/Schreibbefehl.

SCB->BFAR beinhaltet normalerweile die Adresse die den bus-fault 
auslöst.

Du könntest den BusFault-Interrupt aktivieren .
Setze dazu Bit 17 in SCB->SHCSR
und direkt beim Eintreten das SCB->BFAR lesen.
Wenn es valide Informationen enthält ist in SCB->CFSR das BFARVALID-Bit 
gesetzt.

von Falko J. (spacefrog)


Lesenswert?

Hallo Dennis,
hab den Bus Fault Interrupt aktiviert. Beim Testen ist er dann auch in 
den BusFault Handler gesprungen. Das Bit PRECISERR Bit ist gesetzt. Also 
gültige Informationen im BFAR. Im BFAR steht 0x000FFFFC. das heiß also 
das ein Fehler beim Zugriff auf die Adresse 0x000FFFFC aufgetreten ist. 
Die Adresse ist aber (lt. Device Datasheet vom STM32F746, Seite 88) 
RESERVED. Deshalb der Fault. Soweit Richtig?

In der zwischenzeit habe ich auch rausgefunden was bei einem Fehl ins 
Stack geschrieben wird:
SP+0: Stacked R0
SP+4: Stacked R1
SP+8: Stacked R2
SP+C: Stacked R3
SP+10: Stacked R12
SP+14: Stacked LR
SP+18: Stacked PC
SP+1C: Stacked xPSR


Im SP+14 steht also der Programmcounter bei dem der Fehler aufgetreten 
ist. Der Wert ist 0x08098A04.

Hier das Disassembly für diesen Bereich:
1
    0x80989be: 0xd005         BEQ.N     0x80989cc
2
    0x80989c0: 0x6836         LDR       R6, [R6]
3
    0x80989c2: 0xb1b6         CBZ       R6, 0x80989f2
4
    0x80989c4: 0xf856 0x0c04  LDR.W     R0, [R6, #-0x4]
5
    0x80989c8: 0x4540         CMP       R0, R8
6
    0x80989ca: 0xd1e7         BNE.N     0x809899c
7
    0x80989cc: 0xb196         CBZ       R6, 0x80989f4
8
    0x80989ce: 0x6830         LDR       R0, [R6]
9
    0x80989d0: 0xb108         CBZ       R0, 0x80989d6
10
    0x80989d2: 0x6871         LDR       R1, [R6, #0x4]
11
    0x80989d4: 0x6041         STR       R1, [R0, #0x4]
12
    0x80989d6: 0x6870         LDR       R0, [R6, #0x4]
13
    0x80989d8: 0xb108         CBZ       R0, 0x80989de
14
    0x80989da: 0x6831         LDR       R1, [R6]
15
    0x80989dc: 0x6001         STR       R1, [R0]
16
    0x80989de: 0x6be8         LDR       R0, [R5, #0x3c]
17
    0x80989e0: 0x42b0         CMP       R0, R6
18
    0x80989e2: 0xf040 0x8099  BNE.W     0x8098b18
19
    0x80989e6: 0x6830         LDR       R0, [R6]
20
    0x80989e8: 0xb006         ADD       SP, SP, #0x18
21
    0x80989ea: 0x63e8         STR       R0, [R5, #0x3c]
22
    0x80989ec: 0x4630         MOV       R0, R6
23
    0x80989ee: 0xe8bd 0x8bf0  POP.W     {R4-R9, R11, PC}
24
    0x80989f2: 0x2600         MOVS      R6, #0
25
    0x80989f4: 0x6baf         LDR       R7, [R5, #0x38]
26
    0x80989f6: 0x68e8         LDR       R0, [R5, #0xc]
27
    0x80989f8: 0x19c0         ADDS      R0, R0, R7
28
    0x80989fa: 0x9001         STR       R0, [SP, #0x4]
29
    0x80989fc: 0x1f00         SUBS      R0, R0, #4
30
    0x80989fe: 0x9004         STR       R0, [SP, #0x10]
31
    0x8098a00: 0x9904         LDR       R1, [SP, #0x10]
32
    0x8098a02: 0x9801         LDR       R0, [SP, #0x4]
33
    0x8098a04: 0x6809         LDR       R1, [R1]
34
    0x8098a06: 0x1a44         SUBS      R4, R0, R1
35
    0x8098a08: 0x6820         LDR       R0, [R4]
36
    0x8098a0a: 0x2800         CMP       R0, #0

Der Fehler wurde also durch

0x8098a04: 0x6809         LDR       R1, [R1]

ausgelöst, richtig?


LG Falko

von Little B. (lil-b)


Lesenswert?

Bevor wir nun exzessiv auf assembler-ebene versuchen, zu debuggen, wäre 
doch der c-code an dieser stelle hilfreich.

Ich vermute ab addresse 0x80989f2 die c-funktion, die den Fehler 
verursacht. Das ist aber schwer zu sagen, da kein Stack angelegt wird 
und auch sonst keine referenzpunkte zu sehen sind.

Fakt ist, dass die Core genau das macht, was da steht. Es wird ein 
Pointer geladen, dieser mit einem Offset versehen und von der 
resultierenden Adresse gelesen.

Aber so ein Disassembly habe ich noch nie gesehen... Welchen compiler 
verwendest du, mit welchen optimierungen? Ich vermute den Fehler in der 
Struktur, auf die R5 zeigt (was auch immer da liegen mag)

Am besten zeigst du mal den entsprechenden Code.

von Karl H. (kbuchegg)


Lesenswert?

Little Basdard hat recht.
Spätestens jetzt wird es Zeit, aus dem Map File rauszusuchen, welche 
Funktion da in diesem Adressbereich liegt.

1
....
2
    0x8098a00: 0x9904         LDR       R1, [SP, #0x10]
3
    0x8098a02: 0x9801         LDR       R0, [SP, #0x4]
4
    0x8098a04: 0x6809         LDR       R1, [R1]

R1 wird über Stackpointer+Offset indirekt geladen. Meistens ist das ein 
Zeichen für eine funktionslokale Variable.
Das Laden von R1 über indirekt R1 sieht nach einer Dereferenzierung aus.

Alles zusammengenommen stinkt das für mich nach einer nicht 
initialisierten funktionslokalen Pointer-Variablen im C Code. Könnte 
auch ein Pointer sein, der 'in den Wald' zeigt (was im Grunde dasselbe 
ist, wenn man ihn nicht initialisiert)
Und ich möchte hinzufügen das es sich dabei wohl um so ziemlich die 
häufigste Ursache für Programmabstürze handelt.

von Karl H. (kbuchegg)


Lesenswert?

Karl H. schrieb:

> Alles zusammengenommen stinkt das für mich nach einer nicht
> initialisierten funktionslokalen Pointer-Variablen im C Code. Könnte
> auch ein Pointer sein, der 'in den Wald' zeigt (was im Grunde dasselbe
> ist, wenn man ihn nicht initialisiert)

Ah nicht ganz. Davor kommt
1
    0x80989fa: 0x9001         STR       R0, [SP, #0x4]
2
    0x80989fc: 0x1f00         SUBS      R0, R0, #4
3
    0x80989fe: 0x9004         STR       R0, [SP, #0x10]

Aber so hat das wirklich wenig Sinn. Anhand der Adresse im Map-File 
identifizieren, welche Funktion dort liegt und dann im C Code nachsehen.

von Falko J. (spacefrog)


Lesenswert?

Hi,
da liegt die Funktion "GUI_ALLOC_FreeFixedBlock". Das stammt von der 
StemWin Lib. Hab da kein quellcode zu....

von Karl H. (kbuchegg)


Lesenswert?

Falko J. schrieb:
> Hi,
> da liegt die Funktion "GUI_ALLOC_FreeFixedBlock". Das stammt von der
> StemWin Lib. Hab da kein quellcode zu....

Könnte ein Hinweis darauf sein, dass der freie Speicher zu Ende ist. 
Obowhl ich eigentlich erwarten würde, dass derartige Dinge von einer 
Systembibliothek abgefangen werden.

Untersuch mal, ob du in deinem Programm massenhaft Resourcen anlegst, 
die du nie wieder frei gibst.

Ansonsten dann eben Plan B
Im Speicher eine globale Variable anlegen.
Im Programm an strategischen Stellen unterschiedliche Werte in diese 
Variable legen. Nach dem Crash nachsehen, was als letztes in diese 
Variable geschrieben wurde. Die Umgebung dieser Stelle muss nicht 
unbedingt mit dem Crash zu tun haben aber es könnte ein Anhaltspunkt 
sein.

Ansonsten bleibt dann  nur noch Plan C
Mit einem leeren Programm anfangen und stückweise Teile aus dem 
jetztigen einbauen, bis sich die Crashes wieder einstellen. Jede 
einzelne Zwischenstufe ausgiebig testen.

von Karl H. (kbuchegg)


Lesenswert?

Möglich wäre auch noch, dass du die Speicherverwaltung mit einem 
Schreib-Zugriff out of Bounds oder einem dangling Pointer zerschossen 
hast.

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

Wann genau knallt es denn ?
Regelmäßig, daß man sich da mit einem Breakpoint davor hängen könnte, 
oder sporadisch ?

Ist es dir möglich mit GUI_ALLOC_GetMaxSize vorher zu schauen wieviel 
Speicher im GUI_Speicher noch frei ist oder das zu überwachen.
Und was passiert bei Aufruf von GUI_ALLOC_FreeFixedBlock mit wildem 
Pointer ?

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.