Forum: Compiler & IDEs noinit Variable landet nicht im RAM


von Honk M. (honkmichi)


Lesenswert?

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_t  ulErrSave __attribute__((section(".no_init.ulErrSave"))); ///<  über ein SoftwareReset hinweg retten.
2
3
int main(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
    (*((volatile uint32_t *)(0x100))) = 0x1234567;
10
  }
11
  while(1)
12
  {
13
    // Juhu er läuft
14
  }
15
  return 0;
16
}
17
18
extern int32_t  ulErrSave; ///<  über ein SoftwareReset hinweg retten.
19
uint32_t Test;
20
void HardFault_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?

von W.S. (Gast)


Lesenswert?

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:
1
                  EXPORT     Errors
2
                  EXPORT     StartOfRam
3
                  EXPORT     EndOfRam
4
5
USER_StackSize    EQU     8192            
6
Top_Ram           EQU     0x10010000      ; interner RAM 10000000..1000FFFF
7
Errors            EQU     Top_Ram - 4     ; Fehler Merker
8
Top_Stack         EQU     Errors - 4
9
__initial_sp      EQU     Errors - 4      ; für die Vektor-Tafel
10
11
StartOfRam        EQU     0x10000000
12
EndOfRam          EQU     Top_Stack - USER_StackSize


W.S.

von foobar (Gast)


Lesenswert?

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?

von Jim M. (turboj)


Lesenswert?

Honk M. schrieb:
> ARM Cordex M4 Controller

Welcher genau?

Einige haben einen ROM-Bootloader, der dann auch ein paar Bytes vom RAM 
benutzt.

von kyrk.5 (Gast)


Lesenswert?

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.

von dummschwaetzer (Gast)


Lesenswert?

Hast du villeicht irgendwo in der Peripherie, z.B RTC ein paar Byte die 
du dafür benutzen kannst?

von S. R. (svenska)


Lesenswert?

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.

von Honk M. (honkmichi)


Lesenswert?

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.

von foobar (Gast)


Lesenswert?

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 ...

von S. R. (svenska)


Lesenswert?

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?

von Honk M. (honkmichi)


Lesenswert?

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_INLINE void __NVIC_SystemReset(void)
6
{
7
  __DSB();                                                          /* Ensure all outstanding memory accesses included
8
                                                                       buffered write are completed before reset */
9
  SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |
10
                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
11
                            SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */
12
  __DSB();                                                          /* Ensure completion of memory access */
13
14
  for(;;)                                                           /* wait until reset */
15
  {
16
    __NOP();
17
  }
18
}

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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.

: Bearbeitet durch User
von Honk M. (honkmichi)


Lesenswert?

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 ;-)

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

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!

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.