Forum: Compiler & IDEs noinit Variable landet nicht im RAM


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Honk M. (honkmichi)


Bewertung
1 lesenswert
nicht 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:
int32_t  ulErrSave __attribute__((section(".no_init.ulErrSave"))); ///<  über ein SoftwareReset hinweg retten.

int main(void)
{
  // Es soll einmal, nach dem ersten Start, ein Hardfault ausgelöst werden.
  if(ulErrSave != 2)
  {
    // Hardfault auslösen
    (*((volatile uint32_t *)(0x100))) = 0x1234567;
  }
  while(1)
  {
    // Juhu er läuft
  }
  return 0;
}

extern int32_t  ulErrSave; ///<  über ein SoftwareReset hinweg retten.
uint32_t Test;
void HardFault_Handler(void)
{

  ulErrSave = 2;
 // Test = ulErrSave;  //<--- Wenn ich diese Zeile einbaue funktioniert es!

  if(SCB->HFSR & SCB_HFSR_DEBUGEVT_Msk)
  {
    // Debugging related hard fault
    FAULT_TRAP();
  }
.......
  NVIC_SystemReset();
  while(1);
}

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:
  0x0000032E 4B 14        LDR       R3,[PC,#0x50]
  0x00000330 22 02        MOV       R2,#0x2
  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)


Bewertung
-2 lesenswert
nicht 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:
                  EXPORT     Errors
                  EXPORT     StartOfRam
                  EXPORT     EndOfRam

USER_StackSize    EQU     8192            
Top_Ram           EQU     0x10010000      ; interner RAM 10000000..1000FFFF
Errors            EQU     Top_Ram - 4     ; Fehler Merker
Top_Stack         EQU     Errors - 4
__initial_sp      EQU     Errors - 4      ; für die Vektor-Tafel

StartOfRam        EQU     0x10000000
EndOfRam          EQU     Top_Stack - USER_StackSize


W.S.

von foobar (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


Bewertung
2 lesenswert
nicht 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)


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

von S. R. (svenska)


Bewertung
1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
3 lesenswert
nicht 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)


Bewertung
2 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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.

/**
  \brief   System Reset
  \details Initiates a system reset request to reset the MCU.
 */
__STATIC_INLINE void __NVIC_SystemReset(void)
{
  __DSB();                                                          /* Ensure all outstanding memory accesses included
                                                                       buffered write are completed before reset */
  SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |
                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                            SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */
  __DSB();                                                          /* Ensure completion of memory access */

  for(;;)                                                           /* wait until reset */
  {
    __NOP();
  }
}

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


Bewertung
1 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.