Guten Morgen,
ich habe im SRAM mehrere structs, die nichts gemeinsam haben, außer,
dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein
müssen und dass sie niemals gleichzeitig benötigt werden, also sehr
wenig.
Der Grund für ersteres ist, dass Zeiger darauf im Flash abgelegt werden.
Beispiel:
Ein System:
Denkt euch die Beispiele größer, die Formatierung im Forum ist nicht für
große Tabellen gemacht.
Da die großen Structs TempSystem1 und TempSystemB ohnehin nie
gleichzeitig gebraucht werden, will ich hier den Speicherverbrauch
verringern. Es ist extrem einfach, sicherzustellen, dass sich die beiden
Systeme nicht in die Quere kommen.
Was funktionieren würde, wäre ein union mit beiden Structs als Elemente.
Damit hätte ich allerdings zwei ansonsten völlig unabhängigen
Komponenten deutlich enger gekoppelt, weil alle Temp-Structs von allen
Komponenten plötzlich in einem gemeinsamen Header definiert sein
müssten.
Das Struct als Zeiger auf einen Alias als char array zu definieren
klappt nicht, weil der ARM-GCC dann moniert: "Initializer element is not
constant".
Die Zeiger direkt in jedes Feld als Cast legen funktioniert, also so:
Was stört Dich genau an dem union-Ansatz. Der ist eigentlich genau dafür
geeignet (wenn ich Dein etwas wirres Problem verstanden haben sollte).
Die Koppelung über das Bekanntmachen der Typen findet ja erst in dem
Header statt, wo Du die union brauchst. Kommt also Komposition gleich.
Wilhelm M. schrieb:> Was stört Dich genau an dem union-Ansatz [?]
Daran stört mich, dass es dann einen Header gibt, z.B. temp.h, der zig
Deklarationen von Variablentypen enthält, die jeweils nur von einem
einzigen der diesen Header inkludierenden Quellcodedatei benötigt
werden.
Das ist für mich so ziemlich das Gegenteil von modular.
Leider nein - vor allem dann, wenn in den structs nicht nur primitive
Datentypen stehen, sondern andere Datentypen, die von anderen Headern
bereitgestellt werden.
Dann wird der Header mit dem großen Union plötzlich der, der unterm
Strich fast alle anderen Header mitzieht.
Walter T. schrieb:> Dann wird der Header mit dem großen Union plötzlich der, der unterm> Strich fast alle anderen Header mitzieht.
Na klar. Aber das macht doch nichts. Das hättest Du bei jeder anderen
Lösung auch. Denn die Typen müssen ja bekannt sein.
Die Typen sollten nur dort bekannt sein müssen, wo die Structs
letztendlich benutzt werden. An allen anderen Stellen reicht es aus,
wenn der Speicherplatz groß genug ist.
Ich verstehe Dein Problem nicht:
- Du hast eine Komponente A in A.h
- Du hast eine Komponente B in B.h
- A und B sind unabhängig
- Du hast Dein Projekt mit P.h
- In Deinem Projekt P.h führst Du A.h und B.h zusammen
- In P.h kannst Du Deine union deklarieren
- Damit bleiben A und B unabhängig.
Walter T. schrieb:> Daran stört mich, dass es dann einen Header gibt, z.B. temp.h, der zig> Deklarationen von Variablentypen enthält, die jeweils nur von einem> einzigen der diesen Header inkludierenden Quellcodedatei benötigt> werden.
Niemand zwingt dich dazu, alle Typen in einer header Datei zu haben.
Verschiebe die betroffenen Typen in neue separate Header Dateien, dann
musst du nur das inkludieren, was du wirklich brauchst.
Walter T. schrieb:> Jetzt kommt das Union:/* temp.h */> union> {> struct TempA_s A;> struct TempB_s B;> }> Temp;> dann wird temp.h indirekt alle Header includieren und alles ist> vermischt.
Na klar. Aber das passiert nur in Deinem Projekt. A und B bleiben
deswegen unabhängig.
(Andernfalls hast Du Deinen Code falsch strukturiert)
Walter T. schrieb:> und alles ist vermischt.
Da mischt sich nix....
Und wenn, Typedefinitionen fressen kein Brot.
Und ja, das geht kaum anders, da ja der Compiler die Größe beider
Strukturen braucht um die größe der Union bestimmen zu können.
Was du da planst, dir wünscht, hört sich an, wie:
"Wasch mich, aber mach mich nicht nass!"
Walter T. schrieb:> Jetzt kommt das Union:/* temp.h */> union> {> struct TempA_s A;> struct TempB_s B;> }> Temp;> dann wird temp.h indirekt alle Header includieren und alles ist> vermischt
Damit hast Du eine Definition (keine Deklaration) in einer Header-Datei,
was Du wohl nicht willst. Besser in der Header-Datei und den union-Typ
deklarieren und die (globale) Variable in einer Implementierungsdatei.
EAF schrieb:> Und ja, das geht kaum anders, da ja der Compiler die Größe beider> Strukturen braucht um die größe der Union bestimmen zu können.
Und deswegen gefällt mir die Union nicht, und wir sind wieder am Anfang.
Typdefinitionen fressen kein Brot, aber Zeit. Wenn ich aus einem uint8_t
in Aa1.h ein int8_t mache, wird auch alles im B-System neu gebaut.
Walter T. schrieb:> EAF schrieb:>> Und ja, das geht kaum anders, da ja der Compiler die Größe beider>> Strukturen braucht um die größe der Union bestimmen zu können.>> Und deswegen gefällt mir die Union nicht, und wir sind wieder am Anfang.
Das brauchst Du aber bei jeder Lösung, auch wenn Du die Union als
poor-mans-union baust.
> Typdefinitionen fressen kein Brot, aber Zeit. Wenn ich aus einem uint8_t> in aa.h ein int8_t mache, wird auch alles im B-System neu gebaut.
Nein. Dann wären A und B nicht unabhängig.
Da hast Du ein Verständnisproblem oder Dein Code ist falsch
strukturiert.
Das beschriebene Problem ist direkt mit der statischen Allokation
verknüpft. Da muss der Compiler alles kennen, um die Grösse zu
bestimmen.
Ist das Problem nicht riesig gross, sehe ich in den gesammelten Includes
kein Problem. Ist es riesig gross, sind Pointer wohl vertretbar. Hat man
dann noch im Auge, dass Unions und Structs unvollständig bleiben können,
solange sie nur als Pointer darauf vorkommen und nicht dereferenziert
werden, hat man auch die Casts vom Hals.
Walter T. schrieb:> wird auch alles im B-System neu gebaut.
Ja und?
Wenn ich das richtig verstehe, möchtest du die Zeit, welche deine
Toolchain fürs Bauen braucht verringern, in dem du in den Code dirty
Hacks einbaust?
Naja....
Kommt mir sehr seltsam vor.
Ich habe eine Lösung gefunden: Ich setze in Assembler einfach zwei Label
auf die gleiche Speicherstelle. Kein Union, keine fremden Header.
[asm]
#include "temp.h"
.align 4
.bss
.global Temp1
.global Temp2
Temp1:
Temp2:
.space sizeof(Temp_t)
[/asm]
(prx) A. K. schrieb:> dass Unions und Structs unvollständig bleiben können
Falls das nicht bekannt sein sollte:
struct S *p;
ist ohne jede Information über S zulässig. Dort, wo p dereferenziert
wird, muss S natürlich deklariert sein. Andernorts nicht unbedingt.
Damit sollte sich das ohne Casts lösen lassen.
(prx) A. K. schrieb:> struct S *p;> ist ohne jede Information über S zulässig.
Ja, aber dann muss ich den Platz für den Inhalt von S wieder irgendwo
vorsehen und die Geschichte fängt auch wieder von vorn an.
Walter T. schrieb:> (prx) A. K. schrieb:>> struct S *p;>> ist ohne jede Information über S zulässig.>> Ja, aber dann muss ich den Platz für den Inhalt von S wieder irgendwo> vorsehen und die Geschichte fängt auch wieder von vorn an.
Du hast ein wirres Problem mit der Strukturierung Deines Codes.
Anscheinend sind Deine Komponenten A und B doch nicht unabhängig.
Walter T. schrieb:> Ja, aber dann muss ich den Platz für den Inhalt von S wieder irgendwo> vorsehen und die Geschichte fängt auch wieder von vorn an.
Ich dachte, es geht um Modularisierung und die Trennung des Quellcodes
in Module, die nicht jeweils alles includen müssen. Das wäre darüber zu
machen, ausser dort, wo eine maximale Grösse der Elemente bekannt sein
muss. Dein sizeof(Temp_t) fällt aber auch nicht vom Himmel.
Wilhelm M. schrieb:> Du hast ein wirres Problem mit der Strukturierung Deines Codes.
Mindestens die Beschreibung seines Problems wirkt etwas wirr. Es ist
schwierig, im Rahmen des Threads perfekte Lösungen für nicht wirklich
verstandene Probleme zu liefern.
Ist beispielsweise dies
Walter T. schrieb:> dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein> müssen
eine zwingende Forderung aus unbekanntem Grund, oder bereits eine
vorweggenommene Lösung? Also dass die Adresse feststehen muss. Oder muss
sie das eigentlich nicht, sondern es soll nur Platz gespart werden?
(prx) A. K. schrieb:> eine zwingende Forderung aus unbekanntem Grund, oder bereits eine> vorweggenommene Lösung?
Es ist eine zwingende Forderung aus einem im Eröffnungspost behandelten
Grund, nämlich dass darauf aus in großen im Flash liegenden Structs
gezeigt wird.
Diese großen structs könnten auf einem im SRAM und einem im Flash
liegenden Teil aufgeteilt werden, aber es ist so schon unübersichtlich
genug. Nicht, weil sie wahnsinning unübersichtlich strukturiert wären,
sondern weil viel drin steht.
(prx) A. K. schrieb:> Dein sizeof(Temp_t) fällt aber auch nicht vom Himmel.
Es fällt nicht vom Himmel, aber es lässt sich mit static assertions
recht gut prüfen, dass die Größe zumindest ausreichend ist.
Walter T. schrieb:> Jetzt kommt das Union:/* temp.h */> union> {> struct TempA_s A;> struct TempB_s B;> }> Temp;> dann wird temp.h indirekt alle Header includieren und alles ist> vermischt.
Richtig, genau das ist die saubere Lösung.
Ob du nun eine Deklaration oder Definition für das Union ins Headerfile
machst, das bleibt dir überlassen.
Walter T. schrieb:> [asm]> #include "temp.h">> .align 4> .bss> .global Temp1> .global Temp2> Temp1:> Temp2:> .space sizeof(Temp_t)> [/asm]
Soll den Murks ausser dir sonst noch jemand nachvollziehen können? Du
baust potentielle Problemstellen und Eigenkreationen, um dir ein paar
verschachtelte Header Includes zu sparen?
So sieht es aus. Aus einem include-Spaghettiteller ist ein sauberer Baum
geworden, und wenige Zeilen Assembler haben gerade etliche zig Zeilen
C-Quelltext überflüssig gemacht.
Zumindest das letztere bin ich eher umgekehrt gewohnt.
Walter T. schrieb:> ich habe im SRAM mehrere structs, die nichts gemeinsam haben, außer,> dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein> müssen und dass sie niemals gleichzeitig benötigt werden, also sehr> wenig.>> Der Grund für ersteres ist, dass Zeiger darauf im Flash abgelegt werden.
Die meisten CPUs beherrschen indirekte Adressierung. Es reicht also
völlig, nur einen Pointer auf die Struct zu reservieren und alle
Zugriffe über den Offset in der Struct machen zu lassen.
Der Pointer wird einmalig auf einen entsprechend großen RAM-Bereich
gesetzt.
Ob im Flash nun die Absolutadresse oder der Offset in der Struct steht,
ist nur ein kleiner Unterschied in der Syntax:
1
xx.member// absolut
2
pxx->member// indirekt
Beim AVR können Pointerzugriffe sogar Flash sparen, da ein absoluter
LDS/STS 4 Byte Code braucht, ein LD/ST auf Pointer mit Displacement aber
nur 2 Byte.
(prx) A. K. schrieb:> Siehe "alias"
Kenne ich, aber:
"Except for top-level qualifiers the alias target must have the same
type as the alias."
Peter D. schrieb:> Es reicht also völlig, nur einen Pointer auf die Struct zu reservieren
Ich überlege mal "laut":
Das könnte dann gut klappen, wenn in jeder Struct im Flash nur auf
jeweils Felder in einem einzigen anderen Struct verwiesen wird. Dann
müssten die relativen Adressen mit offsetof() hinterlegt werden, und die
Adresse des Temp-Structs auf anderem Weg weitergegeben werden.
Wenn ich auf Elemente in mehreren Structs verweise (daß ich das tue,
habe ich in der Eröffnung unterschlagen, weil es hier nur um die
Temporäten Daten ging), müsste ich die Adresse wieder für das
Eltern-Struct jedes Elements einzeln übergeben. Dann könnte ich aber
auch direkt wieder die direkte Adresse hinterlegen, wäre also auch
wieder am Anfang, dass ich die Daten für die Feldbeschreibungen und das
Ziel (Feldinhalt) separat behandeln müsste.
Irgendwie dreht sich das im Kreis.
Wenn ich den Aufwand, für jedes Element zwei Structs zu pflegen, dem
insgesamt projektweit vier Zeilen Assembler zu pflegen
gegenüberstelle...
Ich bin mit der Assembler-Lösung nicht glücklich. Aber das andere ist
noch schlimmer.
So ähnliches vermute ich auch!
Der eigentliche Fehler wird 1 bis 3 Schichten vorher stecken.
Und jetzt wird an den Symptomen gebastelt.
Vergleichbar mit:
> Wer in die falsche Richtung läuft, braucht sich nicht zu beeilen.
Walter T. schrieb:> Ich habe eine Lösung gefunden: Ich setze in Assembler einfach zwei Label> auf die gleiche Speicherstelle. Kein Union, keine fremden Header.> [asm]> #include "temp.h">> .align 4> .bss> .global Temp1> .global Temp2> Temp1:> Temp2:> .space sizeof(Temp_t)> [/asm]
... und was steht in temp.h?
/* Alle Globalen "Temp"-Variablen" liegen an der gleichen Speicherstelle */
5
6
.align 4
7
.bss
8
.global GlTemp
9
.global MrtmTemp
10
.global MenuTemp
11
.global MainScreenTemp
12
.global DebgugScreenTemp
13
.global AgcSettingsTemp
14
.global AhcSettingsTemp
15
.global GtCtrlTemp
16
17
GlTemp:
18
MrtmTemp:
19
MenuTemp:
20
MainScreenTemp:
21
DebgugScreenTemp:
22
AgcSettingsTemp:
23
AhcSettingsTemp:
24
GtCtrlTemp:
25
.space TEMP_BYTES
1
/* temp.c */
2
#include"temp.h"
3
4
/** Globale Temp-Variable für Menüstrukturen u.Ä. aller Art. In der Assembler-Variante liegen alle an der gleichen Speicherstelle. */
5
charGlTemp[TEMP_BYTES];
6
7
charMrtmTemp[TEMP_BYTES];
8
charMenuTemp[TEMP_BYTES];
9
charMainScreenTemp[TEMP_BYTES];
10
charDebgugScreenTemp[TEMP_BYTES];
11
charAgcSettingsTemp[TEMP_BYTES];
12
charAhcSettingsTemp[TEMP_BYTES];
13
charGtCtrlTemp[TEMP_BYTES];
Und in einer der verwendenten Dateien:
1
/* mrtm.c */
2
3
#include"mrtm.h"
4
#include"temp.h"
5
6
static_assert(sizeof(MrtmTemp_t)<=sizeof(GlTemp),"Temp-Variable zu klein");
7
externMrtmTemp_tMrtmTemp;
Insgesamt spart die Aktion nicht ganz 400 Bytes gegenüber dem vorherigen
Stand. Ob ich die jetzt als Sicherheitsmarge behalte oder gewinnbringend
bei Ebay verkaufe, weiß ich noch nicht.
Walter T. schrieb:> char MrtmTemp[TEMP_BYTES];>> extern MrtmTemp_t MrtmTemp;
Na, das ist ja der Knaller ;-)
Warum so ein Sch..., statt union oder wenigstens eine poor-mans-union zu
benutzen.
Hng. Wie ich schon im Eröffnungsbeitrag schrieb, können solche
"konstanten" structs nicht aus einem konstanten Zeiger im Flash
adressiert werden. "Initializer element is not constant".
Walter T. schrieb:> Hng. Wie ich schon im Eröffnungsbeitrag schrieb, können solche> "konstanten" structs nicht aus einem konstanten Zeiger im Flash> adressiert werden. "Initializer element is not constant".
Warum willst Du im flash überhaupt eine Adresse ablegen, die immer
dieselbe ist bzw. die berechenbar ist. Wenn Du auf die Komponenten
zugreifen willst, benutzt Du Accessor-Funktionen.
Walter T. schrieb:> Perfekt. Dann kann ich für jede Variable auch noch eine> Accessor-Funktion mitpflegen.
Macht sich manchmal nicht schlecht, wenn man Invarianten der Klasse
sicherstellen will / muss (ja, auch in C).
> Und ich dachte schon, ich hätte schlechte> Ideen.
Die mit großem Abstand schlechteste Idee hast Du ja schon selbst
gebracht und willst sie einsetzen. Dazu erstmal viel Spaß bei Deinem
Heldenprojekt.
Die besten Idee (Union) hast Du aus unerklärlichen Gründen abgelehnt.
Wilhelm M. schrieb:> Die besten Idee (Union) hast Du aus unerklärlichen Gründen abgelehnt.
Der Beitrag des TO diente doch mal wieder nur dazu zu erklären, wie toll
der TO ein nicht vorhandenes Problem gelöst hat.
Walter T. schrieb:> dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein> müssen und dass sie niemals gleichzeitig benötigt werden, also sehr> wenig.>> Der Grund für ersteres ist, dass Zeiger darauf im Flash abgelegt werden.
Versteh ich jetzt nicht. Um eine Variable zur Load-Time mit einer
Adresse zu initialisieren, genügt doch, dass die Adresse zur Link-Time
bekannt ist:
Hier eine Variable var im RAM, deren Adresse in einer Variablen pvar im
Flash steht (hier mit avr-gcc):
1
int var;
2
3
int * const __flash pvar = &var;
4
5
int get_var (void)
6
{
7
return *pvar;
8
}
Auch var in __flash (oder pvar im RAM) geht ebenso.
Wenn ich dein Problem richtig verstehe?
Dein Programm kann in verschiedenen Teilprogrammen A, B, ... sich
befinden, die aber nie gleichzeitig auftreten. Beispielsweise in einer
Spieleconsole mit verschedenen Spielen, wo man aber jeweils nur eines
der Spiele auswählen und spielen kann. Um Spiel B zu spielen muss man
Spiel A verlassen. Entsprechend dürfen die RAM-Resourcen von A und B an
der gleichen Adresse liegen.
Mit normaler Allocation hätte man Speicherbedarf
RAM(A) + RAM(B) + RAM(C) + ...
aber im besten Falle braucht man nur
max (RAM(A), RAM(B), ...)
Johann L. schrieb:> Wenn ich dein Problem richtig verstehe?
Genau, das trifft die Sache.
Johann L. schrieb:> Versteh ich jetzt nicht. Um eine Variable zur Load-Time mit einer> Adresse zu initialisieren, genügt doch, dass die Adresse zur Link-Time> bekannt ist
Auch das stimmt.
Dein Beispiel (Funktion) funktioniert, aber wenn es keine Funktion
sondern ein struct im Flash ist, funktioniert die Zuweisung nicht
("Initializer element is not constant").
Das ist zumindest beim ARM-GCC der Fall. MinGW (GCC für Windows) stört
das nicht. Wie es beim AVR-GCC aussieht, weiß ich nicht, da momentan auf
meinem aktuellen Rechner nicht installiert, da momentan*) kein offenes
AVR-Projekt.
*) Vielleicht zwischen den Jahren wieder.
Walter T. schrieb:> Dein Beispiel (Funktion) funktioniert, aber wenn es keine Funktion> sondern ein struct im Flash ist, funktioniert die Zuweisung nicht> ("Initializer element is not constant").
Da das Zielelement im Flash ist, ist es ja zwangsweise konstant (mal von
der Möglichkeit abgesehen, den Flash zur Laufzeit zu ändern). Du
solltest das also eh entsprechend deklarieren als Konstante, dann dürfte
das trotzdem klappen. (Gut, C ist da manchmal eigenwillig, da es keine
echten Konstanten gibt, C++ ist da besser dran.)
Ansonsten könntest du natürlich auch eine eigene section im Linkerscript
für deine Konstanten deklarieren, die einfach nur groß genug ist. Da
musst du nur sehen, wie du die im Flash anordnest, damit sie dir den
Flashbereich für den Rest nicht zerstückelt. Wäre allemal besser als ein
Assembler-Hack.
Jörg W. schrieb:> Gut, C ist da manchmal eigenwillig, da es keine> echten Konstanten gibt, C++ ist da besser dran.
Ja, das Problem ist mir schonmal auf die Füße gefallen, sonst ich die
Struct-artigen Zugriffe einfach als Zeiger auf ein char array erzeugt,
siehe hier:
Beitrag "Initializer element is not constant"
Die strict-alias-Regeln hätten das erlaubt, und es wären keinerlei
Klimmzüge nötig.
Jörg W. schrieb:> [sinngemäß: besser im Linkerscript als in Assembler herumhacken]
Vielleicht. Ich bin bei beidem nicht glücklich. Bei der
Assembler-Variante sehe ich zumindest direkt im Projektbaum, dass hier
für ARM und PC unterschiedlich gebaut wird.
Das Problem kann man - wie oben schon beschrieben - ganz einfach über
union lösen. Genau dafür ist union gemacht worden: um Speicher zu sparen
(nicht für type punning). Alles andere ist unwartbarer Murks. Und ja:
Du kannst Deinen Code so strukturieren, dass das ohne versteckte
Abhängigkeiten machbar ist.
BMC schrieb:> Das Problem kann man - wie oben schon beschrieben - ganz einfach über> union lösen. Genau dafür ist union gemacht worden: um Speicher zu sparen> (nicht für type punning). Alles andere ist unwartbarer Murks. Und ja:> Du kannst Deinen Code so strukturieren, dass das ohne versteckte> Abhängigkeiten machbar ist.
Und der weitere Punkt ist: die Adressen der Element-Variablen haben im
flash nichts zu suchen. Den Zugriff kapselt man in Accessoren (In C als
freie Funktionen leider ohne Überladung, in C++ als freie Funktion mir
Überladung oder als Elementfunktionen der Union).
Natürlich kann es sinnvoll sein, Adressen von Variablen im Flash (oder
in Strukturen, die im Flash leben) zu haben. Einfaches Beispiel sind
Menus, die Unter-Menus enthalten.
Walter T. schrieb:> Dein Beispiel (Funktion) funktioniert, aber wenn es keine Funktion> sondern ein struct im Flash ist, funktioniert die Zuweisung nicht> ("Initializer element is not constant").
Da hast du mein Beispiel nicht richtig gelesen. Es verwendet keine
Funktion, sondern:
1
int var;
2
int * const __flash pvar = &var;
Die Funktion zeigt lediglich einen Zugriff auf pvar zur Laufzeit. Und
ob da die Adresse eines Skalars oder eines Komposits genommen wird, ist
wurscht.
----------------------------------
In einem meiner Projekte hatte ich auch das Problem, das Teilprogramme
einen (großen) Puffer brauchen, aber nie gleichzeitig. Die Puffer waren
lokal im jeweiligen Modul, und um eine Union für alle Buffer zu haben,
hätte ich fast alles in den Modules global machen müssen. Alles in den
> 10 Teilprogramm zu globalisieren, nur um eine Union für eine lokale
Variable machen zu können, hatte ich fix wieder aufgegeben, da zu
hässlich. Und "Puffer" waren jeweils ausgewachsene Structs, also nicht
simple uint8_t-Arrays.
Ich hab dann ein Overlay als Lösung genommen: In jedem Modul, das so
einen Buffer braucht, wird der Buffer als global extern deklariert:
1
// In modulA.c
2
extern bufferA_t bufferA;
3
// Die Adresse der Puffer ist zur Link-Time bekannt, kann also in
4
// einem Initializer verwendet werden:
5
[static] bufferA_t *pbuf = &bufferA;
Instanziierung gibt es auf C-Ebene nicht, stattdessen werden Symbole zur
Linkzeit in einem opt-File buffer.opt definiert, das beim Linken per
@buffer.opt angegeben wird:
1
-Wl,--defsym,bufferA=__heap_start
2
-Wl,--defsym,bufferB=__heap_start
Das Projekt war mit avr-gcc, und dort ist __heap_start ein Symbol, das
im Linker-Script definiert wird. Es folgt auf den Bereich für Static
Storage (.data, .bss, .noinit). Dementsprechend hat man folgende
Einschränkungen:
1) Mit avr-gcc oder mit Toolchain, die ein vergleichbares Speicherlayout
hat (also malloc nicht per sbrk).
2) Wenn kein malloc verwendet wird (oder man müsste __malloc_heap_start
vor dem ersten Aufruf von malloc anpassen).
3) Man kann nur ein solches Overlay anlegen.
4) Das Overlay wird vom Startup-Code nicht initialisiert.
Wer sich daran stört, dass bufferA etc. global sind: Bei einer Lösung
mit Union müsste man noch viel mehr Zeug global machen, nämlich alles,
was zur Definition von bufferA_t gebraucht wird.
Das opt-File kann man selbst schreiben, oder nach Gusto von der
Build-Umgebung erzeugen lassen. Anstatt eines opt-Files (bzw.
Definition per Optionen) könnte man auch den Assembly-Name setzen:
Walter T. schrieb:> Geht es besser?
Union ist doch gut für sowas, wurde oben ja schon zur Genüge
abgehandelt.
Ich würde es nicht machen, aber als Alternative kannst Du den benötigten
Speicher auch erst zur Laufzeit jeweils mit malloc() holen und mit
free() wieder freigeben.
Johann L. schrieb:> Ich hab dann ein Overlay als Lösung genommen: In jedem Modul, das so> einen Buffer braucht, wird der Buffer als global extern deklariert
Ganz und gar automatisch könnte man das mit Rückgriff auf die
FORTRAN-Zeit haben: mit einem COMMON-Block. Dessen Natur bestand ja auch
darin, den gleichnamigen Speicher verschiedener Objektmodule zu
überlagern.
Wer sich erinnert: bis vor noch gar nicht so langer Zeit war die
Allokation globaler nicht initialisierter Daten (.bss) in einem
COMMON-Block auch bei GCC & Co. noch die Voreinstellung. Das wurde erst
in letzter Zeit geändert, jetzt muss man diese mit -fcommon explizit
anfordern. Aber sowas funktioniert damit:
1
$ cd /tmp
2
$ cat foo.c
3
int something;
4
$ cat bar.c
5
double something;
6
$ cat mumble.c
7
int main(void) { return 0; }
8
$ echo Geht nicht
9
Geht nicht
10
$ cc -O -o foo -fno-common foo.c bar.c mumble.c
11
/usr/bin/ld: /tmp/ccIqUgYZ.o:(.bss+0x0): multiple definition of `something'; /tmp/cczAQ7uZ.o:(.bss+0x0): first defined here
12
collect2: error: ld returned 1 exit status
13
$ echo Geht
14
Geht
15
$ cc -O -o foo -fcommon foo.c bar.c mumble.c
16
$ nm --print-size foo | fgrep something
17
0000000000004018 0000000000000008 B something
Wenn man das nicht für alle .bss-Variablen haben möchte, sollte sich das
auch auf eine einzelne .section limitieren lassen.
Jörg W. schrieb:> Johann L. schrieb:>> Ich hab dann ein Overlay als Lösung genommen: In jedem Modul, das so>> einen Buffer braucht, wird der Buffer als global extern deklariert>> Ganz und gar automatisch könnte man das mit Rückgriff auf die> FORTRAN-Zeit haben: mit einem COMMON-Block. Dessen Natur bestand ja auch> darin, den gleichnamigen Speicher verschiedener Objektmodule zu> überlagern.
Diese Lösung hatte ich tatsächlich ne Zeit lang, bin aber dann wieder
davon ab. Ist schon ne Zeit her, ich weiß jetzt nicht mehr, was mich
daran gestört hat :-( Möglicherweise, dass alle Objekte den gleichen
Namen haben müssen.
Johann L. schrieb:> Möglicherweise, dass alle Objekte den gleichen Namen haben müssen.
Das wieder sollte sich doch aber mit einem Alias beheben lassen, oder?
Man benennt sie erstmal gleich (wie "common_block"), aber macht dann
jeweils einen aussagekräftigen Alias in jedem Modul dafür, den man im
Code benutzt.
Endlich mal eine sinnvolle Diskussion. Ich hatte schon befürchtet, alle
vernünftigen Menschen wären schon lange abgehauen und nur wir Deppen
hätten das Memo nicht bekommen wohin.
Johann L. schrieb:> Da hast du mein Beispiel nicht richtig gelesen. Es verwendet keine> Funktion, sondern:> int var;> int * const __flash pvar = &var;> Die Funktion zeigt lediglich einen Zugriff auf pvar zur Laufzeit. Und> ob da die Adresse eines Skalars oder eines Komposits genommen wird, ist> wurscht.
Komposit oder Skalar ist wurscht, aber
1
#define __flash /* ARM-GCC kennt das nicht */
2
intvar;
3
int*const__flashpvar=&var;/* geht */
4
int*const__flashpp=pvar;/* geht nicht mehr */
Beim Skalar ist die doppelte Verzeigerung natürlich komplett Mumpitz.
Beim Struct würde var den Speicherplatz bereitstellen, pvar die
Offsets-Struktur und pp ist letztendlich der Zeiger auf die Nutzdaten
innerhalb der Zielstruktur.
Ich weiß nicht, ob das beim AVR-GCC anders aussieht. Ich installiere das
gerade, um einen Vergleich ziehen zu können. (Nachtrag: Es sieht genauso
aus.)
Jörg W. schrieb:> Ganz und gar automatisch könnte man das mit Rückgriff auf die> FORTRAN-Zeit haben: mit einem COMMON-Block.
Genau das Konstrukt ist es, was ich vermisse.
Jörg W. schrieb:> Das wieder sollte sich doch aber mit einem Alias beheben lassen, oder?
Also ein char array, ein Alias auf das char array als char array und
dann in einen Struct-Zeiger casten? Das habe ich tatsächlich noch nicht
probiert.
Walter T. schrieb:> Genau das Konstrukt ist es, was ich vermisse.
Ist ja prinzipiell da.
Ist nur so, dass .bss mittlerweile nicht mehr automatisch als
COMMON-Block registriert wird. Aber wie gezeigt, mit -fcommon kannst du
das sofort wieder erreichen.
Alternative wäre halt eine eigene section nur mit diesem Attribut zu
versehen, aber dann musst du dich vermutlich im Linkerscript noch drum
kümmern.
Die Aliase musst du nicht unbedingt benutzen. Wenn es dich nicht stört,
dass die Variable halt in allen Übersetzungseinheiten gleich benannt
werden muss, obwohl sie völlig verschiedene Inhalte hat, kannst du das
auch direkt dort deklarieren. Dann kann die auch in jeder
Übersetzungseinheit einen anderen Typ haben, du musst nur wissen, was du
tust. Der Linker wählt dann automatisch die längste davon für die
Reservierung.
Wie geschrieben, mit einem COMMON-Block sparst du dir sogar noch den
Stress, genügend viele Bytes im Array anzulegen, da der Linker
automatisch Platz für den längsten reserviert. Allerdings bist du damit
auf einen Datentyp pro Übersetzungseinheit limitiert, mit den Aliasen
kannst du natürlich x-beliebig viele produzieren.
Jörg W. schrieb:> mit einem COMMON-Block sparst du dir sogar noch den> Stress, genügend viele Bytes im Array anzulegen
Was für ein Sprung - von "alles sehr mühsam" zu "zwei sehr brachbare
Lösungen".
Vielen Dank an GJ und Dich für euren hilfreichen Beiträge!
Walter T. schrieb:> Jörg W. schrieb:>> mit einem COMMON-Block sparst du dir sogar noch den>> Stress, genügend viele Bytes im Array anzulegen>> Was für ein Sprung - von "alles sehr mühsam" zu "zwei sehr brachbare> Lösungen".
Die einfache und nachvollziehbare Lösung wäre die union gewesen. Dies
ist einfach nur ein Hack.
Wilhelm M. schrieb:> Die einfache und nachvollziehbare Lösung wäre die union gewesen.
Wenn diese allerdings einen elendigen Rattenschwanz an anderen includes
nach sich zieht, nur damit man die notwendigen Typdefinitionen beisammen
hat (die ja für die komplette Union vorliegen müssen, egal, welchen Teil
davon man gerade braucht), kann ich das Anliegen durchaus
nachvollziehen.
Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,
auch wenn das bei vielen verpönt ist. Das hätte das Problem natürlich
genauso gelöst.
Jörg W. schrieb:> Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,
Ich auch, aber die Zeiger auf den Speicherblock von malloc() lassen sich
halt nicht im Flash hinterlegen, und wenn ich die Wahl habe zwischen
einer oder zwei großen Tabellensammlungen zu pflegen, weiß ich, was mir
wichtiger ist.
Jörg W. schrieb:> Wenn diese allerdings einen elendigen Rattenschwanz an anderen includes> nach sich zieht, nur damit man die notwendigen Typdefinitionen beisammen> hat (die ja für die komplette Union vorliegen müssen, egal, welchen Teil> davon man gerade braucht), kann ich das Anliegen durchaus> nachvollziehen.
Und genau das kann bei sauberer Trennung, von der der TO sprach, nicht
passieren.
Der TO sprach von "völlig unabhängigen Komponenten"!
(s.a.
Beitrag "Re: Struct alias").
Jörg W. schrieb:> Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,> auch wenn das bei vielen verpönt ist.
Allerdings klappt das nicht so gut, wenn die Adresse des allozierten
Speichers im Flash liegen soll. Und so dreht sich das im Kreis. Sobald
das Flash nicht mehr direkt auf das RAM zeigen muss, eröffnen sich
allerlei Möglichkeiten.
Walter T. schrieb:>> Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,>> Ich auch, aber die Zeiger auf den Speicherblock von malloc() lassen sich> halt nicht im Flash hinterlegen
Stimmt auch wieder.
BMC schrieb im Beitrag #7275782:
> Wow: die Renaissance des Common-Block: das ist mal innovativ! Was für> ein Sprung in die Steinzeit: vollkommener Mist.
Leider konnte uns der TO nicht erklären, warum er seine "total
unabhängigen" Komponenten nicht in einer union zusammenführen konnte.
Scheint am TO zu liegen ...
Wilhelm M. schrieb:> Leider konnte uns der TO nicht erklären, warum er seine "total> unabhängigen" Komponenten nicht in einer union zusammenführen konnte.
Du willst die Antwort nur nicht akzeptieren. Die Erklärung dafür stand
schließlich schon im Eröffnungsposting.
Jörg W. schrieb:> Du willst die Antwort nur nicht akzeptieren. Die Erklärung dafür stand> schließlich schon im Eröffnungsposting.
Mmh ...
Walter T. schrieb:> Was funktionieren würde, wäre ein union mit beiden Structs als Elemente.> Damit hätte ich allerdings zwei ansonsten völlig unabhängigen> Komponenten deutlich enger gekoppelt, weil alle Temp-Structs von allen> Komponenten plötzlich in einem gemeinsamen Header definiert sein> müssten.
Und das beutet keine enge Koppelung der Komponenten, denn die bleiben
unabhängig.
Wilhelm M. schrieb:> Und das beutet keine enge Koppelung der Komponenten, denn die bleiben> unabhängig.
Wenn du eine ideale Welt hast, in der es Headerfiles nur für Typen gibt
und die auch nicht kreuz und quer von anderem abhängen, mag das
funktionieren.
Ich habe leider selbst schon ausreichend "historisch gewachsenes"
Headerfile-Spaghetti mit so vielen gegenseitigen Abhängigkeiten erlebt,
dass ich den Wunsch des TE durchaus nachvollziehen kann, möglichst eben
nicht alles irgendwo reinziehen zu müssen.
Jörg W. schrieb:> Wilhelm M. schrieb:>> Und das beutet keine enge Koppelung der Komponenten, denn die bleiben>> unabhängig.>> Wenn du eine ideale Welt hast, in der es Headerfiles nur für Typen gibt> und die auch nicht kreuz und quer von anderem abhängen, mag das> funktionieren.
Also was denn nun: total unabhängig, wie der TO es von seinen
Komponenten sagt, oder kreuz und quer?