Ich arbeite auf einem ARM Cordex M4 Controller und habe folgendes
Problem:
Ich möchte im Hardfault_Handler() eine Fehlerursache in einer Variable
speichern, dessen Inhalt nach dem Softwarereset noch erhalten sein soll.
Der SoftwareReset passiert am Ende des Hardfault.
Folgendes kleines Beispielprojekt:
1
int32_tulErrSave__attribute__((section(".no_init.ulErrSave")));///< über ein SoftwareReset hinweg retten.
2
3
intmain(void)
4
{
5
// Es soll einmal, nach dem ersten Start, ein Hardfault ausgelöst werden.
6
if(ulErrSave!=2)
7
{
8
// Hardfault auslösen
9
(*((volatileuint32_t*)(0x100)))=0x1234567;
10
}
11
while(1)
12
{
13
// Juhu er läuft
14
}
15
return0;
16
}
17
18
externint32_tulErrSave;///< über ein SoftwareReset hinweg retten.
19
uint32_tTest;
20
voidHardFault_Handler(void)
21
{
22
23
ulErrSave=2;
24
// Test = ulErrSave; //<--- Wenn ich diese Zeile einbaue funktioniert es!
25
26
if(SCB->HFSR&SCB_HFSR_DEBUGEVT_Msk)
27
{
28
// Debugging related hard fault
29
FAULT_TRAP();
30
}
31
.......
32
NVIC_SystemReset();
33
while(1);
34
}
Wenn ich das ganze jetzt mit dem Debugger teste, sehe ich das die
Variable ulErrSave beim Start erstmal nicht inisiliert ist. Wenn dann
der Hardfault_Handler() aufgerufen wird, wird sie gesetzt. Nach dem
Reset steht dann wieder der Ursprüngliche Wert drin. (nicht 0! sie wird
nicht initislisiert).
Es sieht erstmal so aus als wenn der Wert nicht ins RAM geschrieben
wird.
Wenn ich die Variable ulErrSave jetzt nach dem beschreiben nochmal lese
(siehe im Quelltext) dann funktioniert es.
Ich habe den Assemblercode zwischen beiden Varianten verglichen und kann
an der Speicherstelle kein unterschied feststellen. Beides:
1
0x0000032E 4B 14 LDR R3,[PC,#0x50]
2
0x00000330 22 02 MOV R2,#0x2
3
0x00000332 60 1A STR R2,[R3,#0x0]
Auch mit der Data Memory Barriere (DMB) habe ich schon rumgespielt. Ohne
Erfolg.
Hat jemand eine Idee was dort passiert?
Honk M. schrieb:> Hat jemand eine Idee was dort passiert?
Schau dir doch mal deinen Startup-Code an. Vermutlich wird dort bei
jedem Neustart der RAM abgelöscht, diverse Bereiche vorbelegt oder
ähnliches.
Wenn du so etwas vorhast, dann schreib dir deinen Startup in Assembler
und füge dort auch den Faulthandler und die betreffende
Fehler-Variable (an absoluter Stelle) ein. Die exportierst du und in
dein startup.h kommt lediglich ein "extern long MyLastEror" hinein.
Dann mußt du lediglich dafür sorgen, daß bei den diversen
Startup-Aktionen deine Fehlerzelle nicht überschrieben wird - alternativ
kanst du die ja auch als Argument an main() übergeben.
Man kann beispielsweise sowas auf die brutale Art etwa so erledigen:
Findet der Schreibzugriff in den beiden Varianten auch an der gleichen
Stelle statt?
Ich würd's ja mal mit nem "volatile" probieren ... wäre hier eh
angebracht.
Hat die CPU nen Cache?
Gibt es Cache? Write-trough? Wenn nein, dann Cache Kohärenz beachten!
Bus Syncronization? Eventuell wird der gar nicht geschrieben und wartet
nur irgendwo auf eine Bus geschrieben zu werden. Der Debugger kann das
ja beinflussen. Lösung wäre hier irgendwelche sync Befehle.
In einem XT-BIOS habe ich gesehen, dass der Fehlerzustand vor dem
(destruktiven) RAM-Test in ein Register gesichert und nach dem RAM-Test
wiederhergestellt wird.
Wenn du einfach was Äquivalentes in deinen Startup-Code einfügst, sollte
das Problem gegessen sein. Sollte deine Variable nämlich in .bss (bzw.
einer vergleichbaren Section) landen, dann wird dein Startup-Code die
wahrscheinlich löschen.
Volatile war auch mein erster Gedanke, hat aber nichts gebracht. Fehlt
aber auch in dem Beispiel. Ihr hab recht, das wäre angebracht.
Die Variable ulErrSave wird nicht neu initialisiert. Das mit der Section
.noinit funktioniert soweit. Das kann ich im Debugger sehen. Ich kann
sie ändern, und nach einem Reset steht immer das selbe drin.
Nur wenn sie im Hardfault_Handler() gesetzt wird, bleibt es nicht drin
stehen! Es sei den ich greife nach dem schreiben nochmal lesend zu.
Es handel sich um den NetX90. Er hat 2 Kerne und jedem Kern ist sein
eigener Bus und sein eigener RAM zugeordnet. Von einem Cache vor dem Ram
schreiben sie nichts.
-> Würde es da Befehle geben um den Cache ins RAM zu übertragen?
Ich glaube, den Startup prozess ändern würde nichts bringen. Die Lösung
mit noinit funktioniert. Das Problem muss so anders liegen. Nur eben
nicht wenn ich einfach auf die Variable schreibe.
From
http://infocenter.arm.com/help/topic/com.arm.doc.dai0321a/BIHDHCGJ.html> A DSB is required before generating self-reset to ensure all> outstanding transfers are completed. The use of the CPSID I> instruction is optional.
Also ein "_dsb(15);" vor den Reset. Und sicherstellen, dass der write
auch vor dem _dsb passiert (volatile, asm(memory), etc).
Und hoffen, dass der ASIC nicht noch irgendwelchen Quatsch macht - das
Teil sieht ziemlich komplex aus ...
Honk M. schrieb:> Nur wenn sie im Hardfault_Handler() gesetzt wird,> bleibt es nicht drin stehen!
Mal so als ganz blöde Frage:
Hast du den Hardfault_Handler() mal disassembliert und nachgeschaut,
dass der Schreibvorgang auch ganz sicher da drin ist?
S. R. schrieb:> Mal so als ganz blöde Frage:> Hast du den Hardfault_Handler() mal disassembliert und nachgeschaut,> dass der Schreibvorgang auch ganz sicher da drin ist?
Ja der STR Befehl ist in beiden fällen drin. Hab ich oben beschrieben!
foobar schrieb:> Also ein "_dsb(15);" vor den Reset. Und sicherstellen, dass der write> auch vor dem _dsb passiert (volatile, asm(memory), etc).
Ich hab jetzt einfach mal eine 2. 32Bit Variable in die selbe .noinit
Section gelegt und bei nacheinander geschrieben, und siehe da, es
funktioniert auch!
Das riecht also danach das ein Cache vorhanden ist, der vor dem Reset
nicht weiter ins RAM überträgt.
Aber wie kann ich ihn dazu bringen den Cache zwischendurch zu schreiben?
_DSB funktioniert irgendwie nicht.
ich hab auch gesehen das der Befehl schon in der Resetfunktion vorhanden
ist.
1
/**
2
\brief System Reset
3
\details Initiates a system reset request to reset the MCU.
4
*/
5
__STATIC_INLINEvoid__NVIC_SystemReset(void)
6
{
7
__DSB();/* Ensure all outstanding memory accesses included
Laut der Hilscher Webseite hat der netx90 einen Cortex-M4 und einen
xPIC.
Ein Hardfault handler deutet ja eher auf einen M4 hin und da hat ARM
keinen Cache vorgesehen (erst ab M7 gibts Caches). (manche Hersteller
bauen aber einen ICache an den Flash, aber da is ja kein RAM).
Aber da gehts um den Kern.
Man müsste genauer wissen was für ein Controller das ist um zu gucken ob
da nicht einfach ein Cache im AHB zwischen Kern und Busmatrix gebastelt
wurde.
Das musst du rausfinden und dann gibts bei Caches einen "Cache Clean"
Befehl.
Der zwingt dann eine Cache line zurück in den RAM.
Ich hatte jetzt mit Hilscher kontakt. So richtig weiterhelfen konnten
sie mir nicht. Haben mich aber auf diesen Beitrag von ARM verwiesen
(Figure 38. A dummy read ensures that the system level write buffer is
drained):
http://infocenter.arm.com/help/topic/com.arm.doc.dai0321a/BIHDHCGJ.html#BIHFJCIA
Wo im Prinzip gesagt wird, "wenn die CPU ein Write Buffer hat, musst du
noch mal zurücklesen damit es sicher im Speicher landet."
Damit funktioniert es bei mir auch. Deshalb nehme ich mal an der NetX90
hat so ein WriteBuffer, und das zurücklesen ist dann die Lösung.
Vielen Dank für die Hilfe ;-)
Durchaus interessant!
Son Minibuffer is mir noch nicht aufgefallen und dass der sich so
"cachig" verhält.
Der DSB ist ja eigentlich für sowas da, denn die Bus systeme sind auch
eher paketbasierend.
Daher wollt ich da noch etwas mehr lesen und hab das hier gefunden:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEADII.html
Das Ding kann auch in der Busmatrix und in der AHB->APB Bridge wohnen!