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
Und die Befehle drumherum? 0000 sieht für mich erstmal verdächtig nach Datenbereich aus. MfG
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.
Ich kenne mich ja mit dem Cortex M7 nicht aus, aber könnte das das Äquivalent zu NOP sein?
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.
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....
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.
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.
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,
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).
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).
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
Karl H. schrieb: > [...] > Im Stackpointer steht ganz sicher nicht drinnen, > welche Instruktion das war. > [...] ACK. Das auf keinen Fall.
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)...
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.
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"
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
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. :-)
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.
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.
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
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.
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.
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.
Hi, da liegt die Funktion "GUI_ALLOC_FreeFixedBlock". Das stammt von der StemWin Lib. Hab da kein quellcode zu....
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.
Möglich wäre auch noch, dass du die Speicherverwaltung mit einem Schreib-Zugriff out of Bounds oder einem dangling Pointer zerschossen hast.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.