Forum: Mikrocontroller und Digitale Elektronik ARM/GNU Struktur im FLASH an fester Adresse


von Simon G. (sigro)


Lesenswert?

Hallo,
folgendes Problem habe ich bisher nur unzufriedenstellend gelöst:

Ich habe ein Projekt, bei dem es die Möglichkeit gibt einen 
Paramtersatz, gespeichert in einer Struktur, nach der Programmierung 
mittels Bootloaders zu erstellen oder zu ändern.

Im Hauptprogramm gibt es einen Pointer auf diese Stelle (die Struktur 
wird hier nicht angelegt sondern nur gelesen) und soweit funktioniert es 
auch.

Nun möchte ich manchmal die Struktur doch anlegen lassen und habe dafür 
eine Sektion an der entsprechenden Adresse erstellt. Dies funktioniert 
auch, aber ich habe jetzt an mehreren Stellen die Adresse stehen. Im 
Linker und bei der Initialisierung des Pointers.
1
static const struct_t struct_var __attribute__ ((section( ".struct_section "))) = 
2
 {
3
   .a = 1,
4
   .b = 2,
5
 };
6
const struct_t *struct_ptr = (const struct_t *) (0x1FE00);
1
//[LINKER]
2
.struct_section=0x1FE00

Ich könnte natürlich den Pointer auf die Strukur initialisieren, aber 
falls ich die Struktur nicht anlegen möchte habe ich ein Problem.
deklariere ich die Struktur ohne sie zu initialisieren wird der Flash an 
der Stelle mit Nullen gefüllt und das Hex-File entsprechend aufgebläht.

Ich hoffe ich habe mich verständlich ausgedrückt und frage ob ihr 
vielleicht eine bessere Idee habt.

Vielen Dank
Simon

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Im Linker-Script NOLOAD verwenden:
1
.struct_section 0x1FE00 (NOLOAD) : {
2
  . = ALIGN(4);
3
  *(.struct_section)
4
  . = ALIGN(4);
5
}

Dann wird dafür einfach nur Platz freigehalten aber nicht mit irgendwas 
initialisiert.

Im Code musst du das struct nur noch in die Section packen genau wie du 
es schon gemacht hast, aber das "struct_ptr " brauchst du dann nicht 
mehr:
1
static const struct_t struct_var __attribute__ ((section( ".struct_section "))) = 
2
 {
3
   .a = 1,
4
   .b = 2,
5
 };

und dann ganz normal drauf zugreifen. So wird es dann eben nicht mehr 
initialisiert, die Werte 1 und 2 verschwinden beim Linken (d.h. beim 
ersten Programmstart wirst du Nonsense lesen), dafür brauchst du dann 
die üblichen Flash-Zugriffs-Routinen.

Wenn du das struct dann doch mal befüllen möchtest (z.B. beim Flashen in 
der Produktion) lasse das (NOLOAD) weg und die gewünschten Werte (1, 2) 
werden geflasht.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Da sich die Daten ändern können, sind sie streng genommen nicht const, 
sondern volatile const. Damit die Daten bei jedem Zugriff gelesen werden 
und nicht etwa vom Compiler in Immediate Operanden von Instruktionen 
eingebaut werden.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Johann L. schrieb:
> sondern volatile const

Das wäre aber ziemlich ineffizient, weil sie dann wirklich jedes Mal neu 
geladen werden... Wäre es da nicht sinnvoller das "static" wegzulassen 
und nach dem Schreiben ins-Flash ein memory-Barrier hinzuzufügen 
(_asm_ volatile ("" : : : "memory")  )?

Das funktioniert vermutlich nur in C, nicht in C++. In C++ könnte man 
"static" und "const" weglassen, aber die Variablen privat machen und nur 
getter hinzufügen...

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Man muss halt dafür sorgen, dass jeder Zugriff im Programm zu einem 
Zugriff im Assembly führt, und der Compiler nix rausoptimiert weil 
Initialwerte des Arrays als unveränderlich angenommen werden, was sie 
nicht sind.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Johann L. schrieb:
> dass jeder Zugriff im Programm zu einem Zugriff im Assembly führt

Muss man? Reicht nicht ein Zugriff und dann ggf. in CPU-Registern 
zwischenspeichern?

Die Werte ändern sich ja nur total selten. Da reicht die Memory Barrier 
nach dem Flashen...

von Simon G. (sigro)


Lesenswert?

Niklas G. schrieb:
> Im Linker-Script NOLOAD verwenden:

Super vielen Dank, das werde ich wahrscheinlich so umsetzen.

Aktuell erstelle ich die Sektion mittels Parameter
1
-Wl,-section-start=.struct_section=0x1FE00
kann ich dort auch NOLOAD verwenden?

Johann L. schrieb:
> Da sich die Daten ändern können, sind sie streng genommen nicht const,
> sondern volatile const. Damit die Daten bei jedem Zugriff gelesen werden
> und nicht etwa vom Compiler in Immediate Operanden von Instruktionen
> eingebaut werden.

Die Problematik verstehe ich scheinbar noch nicht ganz.
Für mich ist der Pointer konstant und die Struktur auf die dieser zeigt, 
sind für dieses Programm ebenfalls konstant, da sie sich innerhalb des 
Programms nicht ändern.
Entweder werden sie bei der Initialisierung einmalig geschrieben, oder 
über den Bootloader.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Simon G. schrieb:
> kann ich dort auch NOLOAD verwenden?

Weiß nicht, das erscheint mir eine recht hinterhältige Methode, im 
Linkerscript vermutet man so etwas eher...

Simon G. schrieb:
> Die Problematik verstehe ich scheinbar noch nicht ganz

Wenn der Compiler sieht, dass die Daten sich nicht ändern (In C: Weil 
"static const" dran steht, in C++: Weil "const" oder "constexpr" dran 
steht), kann es sein, dass der Compiler den Lesezugriff komplett 
wegoptimiert. D.h. die Initializer-Werte (bzw 0 wenn nicht angegeben) 
werden in den Maschinencode hart kodiert und es erfolgt überhaupt kein 
Lesezugriff auf die gewünschte Stelle.

von Simon G. (sigro)


Lesenswert?

Niklas G. schrieb:
> Simon G. schrieb:
>> kann ich dort auch NOLOAD verwenden?
>
> Weiß nicht, das erscheint mir eine recht hinterhältige Methode, im
> Linkerscript vermutet man so etwas eher...
>
Da ich das Microchip Studio benutze boot sich das an, in der 
Projekt-Konfiguration kann man die Sektionen einfach anlegen und sie 
werden dann entsprechend als Paramter eingefügt.
Aber ich kann natürlich, und ich werde es wahrscheinlich auch machen, 
die Sektion im Linkersript entsprechend anlegen.


Niklas G. schrieb:
> Wenn der Compiler sieht, dass die Daten sich nicht ändern (In C: Weil
> "static const" dran steht, in C++: Weil "const" oder "constexpr" dran
> steht), kann es sein, dass der Compiler den Lesezugriff komplett
> wegoptimiert. D.h. die Initializer-Werte (bzw 0 wenn nicht angegeben)
> werden in den Maschinencode hart kodiert und es erfolgt überhaupt kein
> Lesezugriff auf die gewünschte Stelle.

Das leuchtet ein, wahrscheinlich gab es noch keine Probleme, da ich 
bisher immer über den Pointer auf die Struktur zugegriffen habe.

Niklas G. schrieb:
> Wäre es da nicht sinnvoller das "static" wegzulassen
> und nach dem Schreiben ins-Flash ein memory-Barrier hinzuzufügen
> (_asm_ volatile ("" : : : "memory")  )?
Wie würde ich dies in meinem Fall nutzen?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Simon G. schrieb:
> Wie würde ich dies in meinem Fall nutzen?

Direkt nach dem Schreibzugriff ein
1
__asm__ volatile ("" : : : "memory");

machen. Das weist den Compiler an, zuvor in CPU-Registern 
zwischengespeicherte Werte neu aus dem Speicher zu laden. Allerdings nur 
bei Variablen die nicht "static const" (C) bzw. "const"/"constexpr" 
(C++) sind.

von Simon G. (sigro)


Lesenswert?

Niklas G. schrieb:
> Direkt nach dem Schreibzugriff ein
>
1
__asm__ volatile ("" : : : "memory");

Tut mir Leid dass ich noch einmal nachhaken muss.
Ich schreibe ja in dem Programm den Flash nie, oder ist damit die 
Initalisierung gemeint?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Simon G. schrieb:
> schreibe ja in dem Programm den Flash nie

Und wie kommen dann die Parameter ins Flash?

von Simon G. (sigro)


Lesenswert?

Niklas G. schrieb:
> Simon G. schrieb:
>> schreibe ja in dem Programm den Flash nie
>
> Und wie kommen dann die Parameter ins Flash?

Durch den Bootloader, der aber ein eigenständiges Programm ist, oder in 
Ausnahmefällen durch die Initialisierung der Struktur im Programm.
1
static const struct_t struct_var __attribute__ ((section( ".struct_section "))) = 
2
 {
3
   .a = 1,
4
   .b = 2,
5
 };

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Simon G. schrieb:
> Durch den Bootloader, der aber ein eigenständiges Programm ist

Dann brauchst du die Memory Barrier nicht. Aber woher bekommt der 
Bootloader die Daten wenn sie nicht im Hex/Bin File stehen?

Simon G. schrieb:
> oder in Ausnahmefällen durch die Initialisierung der Struktur im
> Programm.

Diese Initialisierung greift aber nicht wenn NOLOAD im Spiel ist. Hier 
brauchst du die Memory Barrier dann auch nicht aber du musst dennoch mit 
dem "const" aufpassen weil sonst ggf. gar keine Lesezugriffe gemacht 
werden.

von Simon G. (sigro)


Lesenswert?

Niklas G. schrieb:
> Dann brauchst du die Memory Barrier nicht. Aber woher bekommt der
> Bootloader die Daten wenn sie nicht im Hex/Bin File stehen?

Die Daten werden dem Bootloader per Serieller Schnittstelle übertragen.

Niklas G. schrieb:
> Diese Initialisierung greift aber nicht wenn NOLOAD im Spiel ist. Hier
> brauchst du die Memory Barrier dann auch nicht aber du musst dennoch mit
> dem "const" aufpassen weil sonst ggf. gar keine Lesezugriffe gemacht
> werden.
Dann werde ich zwei Linkerscripte vorhalten, eins für den Standardfall 
in welchem NOLOAD steht und bei dem keine Initialisierung gewünscht 
wird, und ein zweites ohne NOLOAD.

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.