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
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.
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.
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...
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.
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...
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.
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.
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?
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.
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?
Simon G. schrieb:
> schreibe ja in dem Programm den Flash nie
Und wie kommen dann die Parameter ins Flash?
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 | };
|
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.
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.
|