Hallo Leute.
Mir fällt einfach keine zufriedenstellende Lösung ein. Da brauch ich mal
eure Ideen/Rat:
Ich habe einen ATmega32U4. In meinem Programm (C) möchte ich eine
Look-Up-Tabelle anlegen. Der Controller bekommt vom Benutzer einen Wert
übergeben, der die Anzahl der Einträge in der LUT darstellt. Im
Initialisierungsprozess sollen dann die Werte für die LUT berechnet
werden. Das so erhaltene Array soll dann allen Funktionen zur Verfügung
stehen (soll also global sein).
Meine Ideen:
1) Das Array wird erst in der Init-Funktion erzeugt; da ist die Anzahl
der Elemente schon bekannt. Aber wie bekomme ich es hin, dass das Array
beim Beenden der Funktion nicht verschwindet? static.... klappt nicht.
2) Das Array außerhalb der Funktionen erzeugen. Da kenn ich aber die
Länge noch nicht, und muss es immer mit der maximalen Länge anlegen.
Fällt euch was gutes ein?
Danke!!!!
Sebastian
Sebastian R. schrieb:> Aber wie bekomme ich es hin, dass das Array beim Beenden der Funktion> nicht verschwindet? static.... klappt nicht.
Als globale Variable anlegen, und in der Funktion nur den benötigten
Teil füllen.
Sebastian R. schrieb:> Der Controller bekommt vom Benutzer einen Wert übergeben, der die Anzahl> der Einträge in der LUT darstellt
Warum kannst du die LUT nicht fix im Flash anlegen für die maximale
Anzahl an Einträgen?
Sebastian R. schrieb:> Da kenn ich aber die Länge noch nicht, und muss es immer mit der> maximalen Länge anlegen
Na und? Würdest du, wenn die LUT kleiner ist, den Speicher für
irgendetwas anderes benutzen, oder wäre er dann leer? Für unbenutzten
Speicher gibt es kein Geld zurück...
Sebastian R. schrieb:> Aber wie bekomme ich es hin, dass das Array> beim Beenden der Funktion nicht verschwindet?
Globalen Pointer auf Array und global die Anzahl der Einträge speichern
?
Das ist das Prinzip globaler Variablen: Sie überleben das Ende der
Funktion und stehen allen Funktionen zur Verfügung.
Wenn Du in main() alloca() aufrufst, bleibt der Speicher erhalten, bis
main() verlassen wird (was ja üblicherweise nicht passiert) und Du
kannst ihn genauso groß machen, wie Du ihn haben willst.
Disclaimer: bevor ich jetzt wieder Haue bekomme, weise ich darauf hin,
daß ich keine Ahnung habe, wie Stack-Allocation beim AVR funktioniert.
Dr. Sommer schrieb:> Warum kannst du die LUT nicht fix im Flash anlegen für die maximale> Anzahl an Einträgen?
Oh, da fehlt noch eine Erklärung meinersteits:
Ich würde es gerne so halten, dass ich im Array immer zum nächsten Feld
gehen kann, und nicht in Intervallen springen muss. Das kommt daher,
dass das Array immer durchlaufen werden muss. Im Grunde soll eine
vorgegebene Spannungskurve abgefahren werden; und diese Kurve ist eben
in der LUT abgelegt.
> Sebastian R. schrieb:>> Da kenn ich aber die Länge noch nicht, und muss es immer mit der>> maximalen Länge anlegen>> Na und? Würdest du, wenn die LUT kleiner ist, den Speicher für> irgendetwas anderes benutzen, oder wäre er dann leer? Für unbenutzten> Speicher gibt es kein Geld zurück...
Gutes Argument!! Ich mach´s so, dass ich immer den maximalen Platz
reserviere, und nur den benötigten fülle, um obigen Punkt zu erreichen.
DANKE!!!
Sebastian
Sebastian R. schrieb:> Ich würde es gerne so halten, dass ich im Array immer zum nächsten Feld> gehen kann, und nicht in Intervallen springen muss
Das fände ich jetzt aber nicht ist schlimm. So ein Sprung (d.h.
Addition) ist auch nicht so teuer. Da auf dem AVR sowieso keine
DMA-Angelegenheiten mit rein spielen...
Markus F. schrieb:> Wenn Du in main() alloca() aufrufst, bleibt der Speicher erhalten, bis> main() verlassen wird (was ja üblicherweise nicht passiert) und Du> kannst ihn genauso groß machen, wie Du ihn haben wi
Aber wozu? Auf diese Art nicht reservierter Speicher ist kaum
anderweitig nutzbar, und somit auch praktisch "belegt". Daher ist das
i.A. eher nutzlos.
Falls
1) Nur ein solches Array bzw. Objekt benötigt wird,
2) Kein malloc o.ä. verwendet wird (alloca() ist kein Problem),
dann kann das so gemacht werden:
1
// values.h
2
3
typedefstruct
4
{
5
intvalue;
6
}value_t;
7
8
externvalue_tvalues[]__asm("__heap_start");
9
10
// values .c
11
12
voidcompute_values(intn_values)
13
{
14
for(inti=0;i<n_values;i++)
15
{
16
values[i].value=i;
17
}
18
}
Erklärung: Das Speicherlayout bei den avr-Tools ist folgendes: Ganz
unten wird Static Storage lokatiert (.data, .bss, .rodata). Darauf
folgt der Heap (verwendet von malloc() etc.), der startend ab
__heap_start nach oben hin dem Stack entgegenwächst. Darauf folgt Platz
für den Stack, der startend von RAMEND nach unten wächst.
__heap_start ist ein Symbol, das im Linker-Skript definiert wird. Es
markiert das untere Ende des Heaps. Die obige Deklaration nutzt diesen
Speicherbereich für ein Array values[], dessen Größe zur Designzeit
nicht bekannt sein muss.
Wie bereits erwähnt, muss immer ein Array der Maximalgröße in den
Speicher passen, ohne dass der Stack zu weit nach unter wächst und so
Werte im Heap oder im Static Storage überschreibt (oder umgekehrt).
Auf den Speicherbereich könnte man auch zugreifen per
1
externvalues_t__heap_start[];// Defined in Linker Skript.
Die Deklaration über __asm hat den Vorteil, dass dann bequem über die
Arrayvariable values[] zugegriffen werden kann.
__asm legt den Assembler-Name eines Objekts / einer Funktion fest, d.h.
den Symbolnamen, den der Compiler im Assemblercode für das Objekt / die
Funktion verwendet. Für den Code von oben:
1
compute_values:
2
ldi r30,lo8(__heap_start)
3
ldi r31,hi8(__heap_start)
4
ldi r19,0
5
ldi r18,0
6
rjmp .L2
7
.L3:
8
st Z+,r18
9
st Z+,r19
10
subi r18,-1
11
sbci r19,-1
12
.L2:
13
cp r18,r24
14
cpc r19,r25
15
brlt .L3
16
ret
Weil das Array außerhalb der Funktion gültig bleiben soll, ist alloca()
keine Option. malloc() hat den einzigen Vorzeil, dass es (im Gegensatz
zu __asm) standardkonform ist, aber das Problem mit der Maximalgröße
löst malloc() natürlich auch nicht sondern verschärft es eher.
Static Storage löst das Problem nur insofern, als man sich bei
Festlegung der (Maximal-)Größe zur Designzeit (scheinbar) keinen
Gedanken über den sonstigen Speicherverbrauch — insbesondere Stack — zu
machen braucht. Aber natürlich brauch man dieses Wissen bei allen
Ansätzen, um zu einer soliden Maximalgröße zu kommen.
Dafür muß du aber keine frühere GCC haben, die unbedingt PROGMEM braucht
und __flash nicht versteht. Dann geht es natürlich auch mit PROGMEM, nur
ein bißchen aufwendiger.
Johann L. schrieb:> Weil das Array außerhalb der Funktion gültig bleiben soll, ist alloca()> keine Option.
alloca() muß nur innerhalb einer Funktion aufgerufen werden, die nie
verlassen wird. main() erfüllt üblicherweise diese Anforderung.
Ups... ein Fehler:
statt #include "max7219_zif.h" sollte natürlich #include "deinding.h"
stehen.
Vorteil: die Tabelle wird von keinem anderen Modul gesehen und Zugang
ist nur über Funktion uint8_t hole_deinding(uint16_t argument) möglich.
Das macht mehr Ordnung.
Sebastian R. schrieb:> Ich würde es gerne so halten, dass ich im Array immer zum nächsten Feld> gehen kann, und nicht in Intervallen springen muss.
Was sagt hier aber gegen einer Tabelle in Flash?
Es soll eine von außen vorgebbare Spannungskurve abgefahren werden.
Mit __flash braucht mal also einen Bootloader-Mechanismus, um die Daten
ins Flash zu bekommen. Und das Array müsste dann const volatile sein.
> Noch besser: in einem Modul:
1
>// deinding.h
2
>
3
>uint8_thole_deinding(uint16_targument);
Ob das wirklich besser ist sei dahingestellt. Es gibt Call-Overhead für
eine triviale Funktion, und hold_deinding(0) o.ä. können nicht mehr zu
Konstanten gefaltet werden (ok, bei volatile eh kein Thema mehr).
> Dafür muß du aber keine frühere GCC haben, die unbedingt PROGMEM braucht> und __flash nicht versteht.
Mit progmem kann man das gleiche Interface implementieren wenn man denn
will. __flash gibt's seit avr-gcc v4.7. Ohne __flash-Support ist der
Compiler also schon mindestens 8 Jahre alt...
Warum überhaupt ein Array im Flash? Reicht das nicht im RAM? Oder, wenn
die Funktion zur Arrayberechnung einfach und die Zeitanforderungen
unkritisch sind, warum nicht einfach eine Formel mit Parameter? Der kann
dann vielleicht im EEPROM gespeichert werden.
2 zillionen abstruse Lösungsvorschläge, nur weil der TO malloc nicht
kennt.
Wenn die Tabellen so groß sind, daß sie nicht mehr ins RAM passen, dann
ist der ganze Ansatz sowieso Murks, egal, wie man das Array ins RAM
packt.
Oliver
Falk B. schrieb:> Warum überhaupt ein Array im Flash? Reicht das nicht im RAM?
Flash ist doch meist in größeren Mengen verfügbar als RAM.
Oliver S. schrieb:> nur weil der TO malloc nicht> kennt.
malloc() (und alloca) ist nur unter einer Bedingung überhaupt sinnvoll:
Wenn man den Speicher, wenn man nur wenig allokiert, anderweitig
verwendet! Das Programm muss sowieso so konzipiert sein, dass der RAM
ausreicht, wenn man die maximale Array-Größe allokiert. Wenn man weniger
allokiert, kann man den Speicher dann für einen anderen Zweck
gebrauchen? Oder liegt er brach? Da die Aufgaben der meisten Controller
ziemlich fix sind (ala 7 Eingänge überwachen, 3 Ausgänge schalten, 1
UART bedienen) wird man Speicher selten um-widmen wollen; es sei denn
man baut etwas sehr flexibles (ala N Eingänge, M Ausgänge mit N+M <=
10). Selbst in diesem Fall ist es manchmal besser/einfacher den Speicher
via "union" umzuwidmen.
Ich seh schon, beim Thema Speicher sollte man sich Gedanken machen....
Ich begrenze jetzt die Länge der LUT auf 256 Elemente. Als Schrittgrößen
beim Abfahren lasse ich nur 2^x zu. Dadurch lässt sich das Springen von
Feld zu Feld schön umsetzen.
Ein float Array würde somit 1kB belegen. Das passt noch gut in den RAM
(2kB).
Wie merke ich, wenn es im RAM Konflikte gibt? Also wenn ich z.B. 95% des
RAM belegen würde, und dann im Programmablauf der RAM nicht mehr reicht?
Absturz?
Sebastian
Sebastian R. schrieb:> 2) Das Array außerhalb der Funktionen erzeugen. Da kenn ich aber die> Länge noch nicht, und muss es immer mit der maximalen Länge anlegen.
Macht doch nichts. Um mal einen alten Spruch zu bringen, für gesparten
Speicher im Controller gibt es keine Rückerstattung.
Sebastian R. schrieb:> Wie merke ich, wenn es im RAM Konflikte gibt? Also wenn ich z.B. 95% des> RAM belegen würde, und dann im Programmablauf der RAM nicht mehr reicht?> Absturz?
Genau das bei einfachen Mikrocontrollern.
Wenn man sehr sorgfältig arbeiten möchte oder muss überlegt und
berechnet man vorher den Stack-Bedarf, verzichtet auf malloc() und
Konsorten und prüft den Stack-Bedarf zur Laufzeit. Sei es wenigstens
einmalig mit dem Debugger, oder im Programm zur Laufzeit.
Die Technik zur Überprüfung zur Laufzeit bei einfachen Mikrocontrollern
ist leider etwas primitiv. Man verwendet eine Pufferzone, in die man ein
Datenmuster schreibt. Das Datenmuster sollte so gewählt werden, dass es
nicht typischen Stack-Daten entspricht. Was schwer ist, aber zumindest
sollte es nicht nur 0x00, 0x01 oder 0xFF enthalten.
Das Vorhandensein des Datenmusters überprüft man regelmäßig. Ist es
zerstört ist der Stack zu groß geworden oder irgendwas hat wild im RAM
rumgeschrieben. Je nach Anwendung stoppt man dann den Mikrocontroller
oder startet ihn neu.
Um das Risiko zu minimieren sollte man auf Funktionen, die viel Stack
oder dynamischen Speicher benötigen, verzichten. Zum Beispiel kein
printf() und keine Rekursion. Daten per Pointer, nicht per Kopie
übergeben. Funktionen mit wenigen Argumenten verwenden. Wenige lokale
Variablen verwenden.
Eine brutale Variante ist es, für Daten grundsätzlich nur statische und
globale Variablen zu verwenden. Der Preis dafür ist, dass jede
vernünftige Programmstruktur zum Teufel geht. Richtig wild wird es, wann
man die statischen und globalen Variablen mehrfach verwendet.
Sebastian R. schrieb:> Wie merke ich, wenn es im RAM Konflikte gibt? Also wenn ich z.B. 95% des> RAM belegen würde, und dann im Programmablauf der RAM nicht mehr reicht?> Absturz?
Da: Beitrag "RAM Verbrauch auch von lokalen variablen ermitteln"
wurde das gerade diskutiert. Die richtig elegante Lösung, eingebettet in
die Atmel-Entwicklungsumgebung, fehlt noch. Schade, ich hätte sie (die
Lösung) auch gerne.
Johann L. schrieb:> Ob das wirklich besser ist sei dahingestellt. Es gibt Call-Overhead für> eine triviale Funktion, und hold_deinding(0) o.ä. können nicht mehr zu> Konstanten gefaltet werden (ok, bei volatile eh kein Thema mehr).
Wenn man die Tabelle in RAM hält, dann besetzt die Tabelle zweimal
Speicherplatz: in RAM und in Flash, woher sie beim Starten in RAM
geladen wird. Call-Overhead kann man vermeiden:
Dann sollte die Funktion aber in *.h stehen.
Notfalls gibt es noch EEPROM
Johann L. schrieb:> Mit progmem kann man das gleiche Interface implementieren wenn man denn> will.
Ja, Assembler-Code bekommt man identisch. Nur kann man bei PROGMEM nicht
die gleichen Funktionen wie für RAM benutzen, man sollte immer über eine
besondere Funktion gehen. Das ist weniger bequem.
Maxim B. schrieb:> Wenn man die Tabelle in RAM hält, dann besetzt die Tabelle zweimal> Speicherplatz: in RAM und in Flash
Das habe ich auch gerade festgestellt. Ich lager die Berechnung jetzt
doch aus (auf PC) und leg nur eine konstante LUT ab. Das kann dann ja
ruhig in den Flash. Da ist mir aber noch was unklar:
Die Tabelle zeigt den Speicherbedarf (gerundet) bei den
unterschiedlichen Arten, die Variable anzulegen (immer außerhalb jeder
Funkntion!). Die Werte zeigt mit Atmel Studio an.
Flash [byte] RAM [byte]
nur Programm 400 0
int Array[256] 900 500
const int Array[256] 400 0
const __flash int Array[256] 400 0
volatile const int Array[256] 900 500
volatile const __flash int Array[256] 900 0
Ok, volatile brauch ich nicht, weil die Variable ja nicht mehr geändert
werden soll, aber ich versteh den Speicherbedarf nicht.
Die ersten und die letzten beiden Varianten sind klar. Aber wo wird die
Variable bei den mittleren Varianten gespeichert???? In main hab ich
immer Zugriff drauf....
Sebastian
Sebastian R. schrieb:> Aber wo wird die> Variable bei den mittleren Varianten gespeichert????
Sie wird erst gespeichert, wenn in Programm benutzt.
Du hast sicher die Tabelle gelegt, aber noch keine Funktion, die die
Tabelle liest.
Der Compiler ist gar nicht dumm!
Was mir aber komisch erscheint: warum für int Array[256] nur 500 bytes
verbraucht wird und nicht 512 ?
Maxim B. schrieb:> Was mir aber komisch erscheint: warum für int Array[256] nur 500 bytes> verbraucht wird und nicht 512 ?
Das liegt dadran, dass ich die Werte gerundet hab! Es sind eigentlich
schon 512 bytes!!!
Maxim B. schrieb:> Sie wird erst gespeichert, wenn in Programm benutzt.> Du hast sicher die Tabelle gelegt, aber noch keine Funktion, die die> Tabelle liest.>> Der Compiler ist gar nicht dumm!
Das ist mir auch aufgefallen, darum hab ich in
1
main()
2
{
3
....
4
wert=Array[1];
5
wert=Array[2];
6
}
stehen. Und wenn ich das Programm simuliere, ändert sich der Wert von
wert auch passend. Die Variable muss also bekannt sein. Aber wo liegt
sie????
Sebastian R. schrieb:> Das ist mir auch aufgefallen, darum hab ich inmain()> {> ....> wert=Array[1];> wert=Array[2];> }> stehen. Und wenn ich das Programm simuliere, ändert sich der Wert von> wert auch passend. Die Variable muss also bekannt sein. Aber wo liegt> sie????
Der Compiler macht hier höchstwahrscheinlich keine Arrays, sondern
assembliert etwa so:
ldi r16, 5
ldi r17, 51
Wenn alle Werte schon vor dem Ablauf bekannt sind, kuckt Compieler, was
ihm besser ist: wirkliche Tabelle zu machen oder direkt auf notwendigen
Stellen direkte Zahlen zu laden.
Anders wird es, wenn an diese Tabelle gerufen wird mit vor dem
Programmablauf unbekannten Index.
Z.B.:
wert=Array[i];
wert=Array[x];
dabei sollte i und x erst im Programm berechnet werden.
Ja, außer Zauberwort "volatile" gibt es in GCC noch fantastische Mittel:
Inline-Assembler. Man kann Stückchen von Code genau so gestalten wie man
braucht.
Sebastian R. schrieb:> Ich seh schon, beim Thema Speicher sollte man sich Gedanken machen....>> Ich begrenze jetzt die Länge der LUT auf 256 Elemente. Als Schrittgrößen> beim Abfahren lasse ich nur 2^x zu. Dadurch lässt sich das Springen von> Feld zu Feld schön umsetzen.
Wenn es keine 2er-Potenz ist, ist das auch kein Problem:
Hat das Array N Elemente, ist der letzte Zugriff auf Element Index
gewesen und soll auf (Index + Step) mod N zugegriffen werden, dann
bekommt man den neuen Index einfach durch
1
Index := Index + Step
2
if Index >= N
3
Index := Index - N
Falls 0 <= Step < N.
Bei den allermeisten Anwendungen kommt es nicht auf die handvoll Ticks
mehr an. Damit gibt es dann weder Einschränkung bezüglich Schrittweite
noch bezüglich Anzahl Elemente.
Ist die Elementanzahl eine Zweierpotenz, kann man den Wrap-Around
erreichen durch
1
Index := (Index + Step) & (N - 1)
2er-Potenzen als Schrittweite bringen hier keinerlei Vorteil.
> Wie merke ich, wenn es im RAM Konflikte gibt?
Auf den Thread neulich wurde ja schon verwiesen. Konkret für avr-gcc
lässt sich get_mem_unused() von hier nutzen:
https://rn-wissen.de/wiki/index.php?title=Speicherverbrauch_bestimmen_mit_avr-gcc#Dynamischer_RAM-Verbrauch
Ist zwar hausbacken und nicht für kritische Anwendungen geeignet, aber
immerhin gibt es eine gute Vorstellung vom Stackverbrauch und belegt nur
ein paar Bytes Code (42 wenn ich mich recht erinnere). Ist allerdings
nicht kompatibel mit malloc().
Sebastian R. schrieb:> Die Tabelle zeigt den Speicherbedarf (gerundet) bei den> unterschiedlichen Arten, die Variable anzulegen (immer außerhalb jeder> Funkntion!). Die Werte zeigt mit Atmel Studio an.
1
> Flash [byte] RAM [byte]
2
> nur Programm 400 0
3
> int Array[256] 900 500
4
>
5
> const int Array[256] 400 0
6
> const __flash int Array[256] 400 0
7
>
8
> volatile const int Array[256] 900 500
9
> volatile const __flash int Array[256] 900 0
> Ok, volatile brauch ich nicht, weil die Variable ja nicht mehr geändert> werden soll, aber ich versteh den Speicherbedarf nicht.>> Die ersten und die letzten beiden Varianten sind klar. Aber wo wird die> Variable bei den mittleren Varianten gespeichert???? In main hab ich> immer Zugriff drauf....
Bei Variante 2 und 3 wurden die Arrays wegoptimiert, weil sie (effektiv)
nicht referenziert werden. Ob bereits der Compiler das Objekt rauswirft
oder der Linker mit --gc-sections hängt vom konkreten Fall ab. Der
Linker listet rausgekickte Objekte im Map-File auf.
GCC lokatiert volatile const in die .data Section, also ins RAM. Wenn
das Objekt ins Flash soll brauch's ein extra Section-Attribut nach
.rodata (bei Devices mit linearem Speichermodell wie ATtiny40 oder
ATmega4808) oder nach .progmem.data o.ä. (bei nicht-linearem
Speichermodell wie bei deinem ATmega32U4) wenn die Daten kein RAM
belegen sollen.
Sebastian R. schrieb:> Das ist mir auch aufgefallen, darum hab ich in
1
>main()
2
>{
3
>....
4
>wert=Array[1];
5
>wert=Array[2];
6
>}
> stehen. Und wenn ich das Programm simuliere, ändert sich der Wert von> wert auch passend. Die Variable muss also bekannt sein. Aber wo liegt> sie????
Kommt drauf an; hier kennt der Compiler die Werte und braucht keine
Zugriffe ausführen (das ist ein Vorteil von __flash gegenüber PROGMEM:
Im Gegensatz zu __flash ist pgm_read_xxx() nicht transparent).
Wenn Array[] in main() bekannt ist, dann passiert das:
Der Compiler faltet die Zugriffe, und aus Array[1] wird z.B. 123 wenn
Array[1] den Wert 123 hat. Der Compiler referenziert das Array im
Assembler-Code also nicht.
Ist das Array lokal, kann GCC nachweisen, dass es nicht gebraucht wird
und legt es erst garnicht an.
Ist das Array global, wird es angelegt aber nirgends referenziert. Der
Linker wirft was Objekt bei -data-sections -Wl,--gc-sections raus. (Bei
so einem kleinen Beispiel braucht's noch nichma -data-sections, weil
kein Objekt in .rodata referenziert wird: .rodata enthält dann nämlich
nur das eine Objekt Array[].)
Damit das Array sicher referenziert wird, geht folgender Code:
1
volatileintwert;
2
3
intmain(void)
4
{
5
wert=Array[wert];
6
}
Der Zugriff auf Array[] kann nicht gefaltet werden, und der (auf
C-Ebene) gelesene Wert wird verwendet, so dass der Zugriff stattfinden
muss. Auf Compiler-Ebene bedeutet dies, dass main() auf Array[]
zugreift und der Compiler Array[] anlegen muss. Auf Linker-Ebene
bedeutet es, dass die Section, die Symbol Array definiert, von der
Section, die Symbol main definiert, referenziert wird. Und die Section,
die Symbol main definiert, wird im Endeffekt vom Entry-Symbol
referenziert und kann damit nicht vom Linker eliminiert werden. (Bei den
GNU-Tools für AVR etwas anders: Symbol main wird im Endeffekt von
Section .vectors referenziert, also von der Vektor-Tabelle; und diese
ist KEEP im Linker-Skript.)
Dieter R. schrieb:> Ich hab das eben überflogen und verstehe nicht, von wo und wie> init_mem() aufgerufen wird. Kannst du das kurz erläutern? Danke!
Das wird automatisch vor dem start von main() aufgerufen, da kümmert
sich der Compiler drum.
Falk B. schrieb:> Dieter R. schrieb:>> Ich hab das eben überflogen und verstehe nicht, von wo und wie>> init_mem() aufgerufen wird. Kannst du das kurz erläutern? Danke!>> Das wird automatisch vor dem start von main() aufgerufen, da kümmert> sich der Compiler drum.
Das heißt, die Funktion init_mem() ist ursprünglich nicht definiert, der
Compiler lauert aber darauf, und wenn er sie irgendwo findet, dann
ruft er sie auf? Oder müssen dabei spezielle Voraussetzungen/Mechanismen
beachtet werden?
Dieter R. schrieb:> Das heißt, die Funktion init_mem() ist ursprünglich nicht definiert, der> Compiler lauert aber darauf, und wenn er sie irgendwo findet, dann> ruft er sie auf?
Quasi.
> Oder müssen dabei spezielle Voraussetzungen/Mechanismen> beachtet werden?
Naja, der "Trick" liegt in den Attributen, das ist geheimes
Compiler-Druiden Wissen ;-)
Dieter R. schrieb:> Falk B. schrieb:>> Dieter R. schrieb:>>> Ich hab das eben überflogen und verstehe nicht, von wo und wie>>> init_mem() aufgerufen wird. Kannst du das kurz erläutern? Danke!>>>> Das wird automatisch vor dem start von main() aufgerufen, da kümmert>> sich der Compiler drum.>> Das heißt, die Funktion init_mem() ist ursprünglich nicht definiert, der> Compiler lauert aber darauf, und wenn er sie irgendwo findet, dann> ruft er sie auf? Oder müssen dabei spezielle Voraussetzungen/Mechanismen> beachtet werden?Falk B. schrieb:> Dieter R. schrieb:>> Das heißt, die Funktion init_mem() ist ursprünglich nicht definiert, der>> Compiler lauert aber darauf, und wenn er sie irgendwo findet, dann>> ruft er sie auf?
Nicht wirklich.
Es ist Tool-abhängiges Zusammenspiel von Compiler und Linkerskript der
Binutils:
> Naja, der "Trick" liegt in den Attributen, das ist geheimes> Compiler-Druiden Wissen ;-)
und init_mem() könnte eine normale Funktion mit Prolog und Epilog sein.
Allerdings dürfte das Muster, das ins RAM geschrieben wird, dann nicht
bis RAMEND reichen, weil dadurch die Rücksprungadresse von init_mem()
auf dem Stack überschrieben werden würde. Dito für Variablen von
init_mem(), die im Frame der Funktion leben, was etwa passiert wenn ohne
Optimierung compiliert (und die Funktion lokale auto-Variablen hätte).
Maxim B. schrieb:> Der Compiler macht hier höchstwahrscheinlich keine Arrays, sondern> assembliert etwa so:> ldi r16, 5> ldi r17, 51
Danke Maxim und Johann. Jetzt versteh ich das. Jaja, da denkt der
Compiler o. Ä. ordentlich mit; das muss man allerdings schon wissen, wie
das geht.
Wenn ich jetzt immer auf das gesamte Array zugreife, sind auch alle
Speicherangaben logisch. Nur eine Sache ist mir noch aufgefallen: wenn
ich das Array int Array[256] anlege und dann nur wie im Beispiel auf
einzelen Arraywerte zugreife, müsste der Compiler o. Ä. doch auch
merken, dass der Rest vom Array weder gelesen, noch irgendwo verändert
wird, und damit auch alle ungenutzten Arrayelemente rausschmeißen.
Oder macht er das nicht, weil die Variable global ist, und er daher
nicht überblicken kann, ob irgendwo eine Änderung stattfindet. Aber dann
bräuchte man doch kein volatile, oder?
Danke für eure Erklärungen!!!!
Sebastian
Sebastian R. schrieb:> Wenn ich jetzt immer auf das gesamte Array zugreife, sind auch alle> Speicherangaben logisch. Nur eine Sache ist mir noch aufgefallen: wenn> ich das Array int Array[256] anlege und dann nur wie im Beispiel auf> einzelen Arraywerte zugreife, müsste der Compiler o. Ä. doch auch> merken, dass der Rest vom Array weder gelesen, noch irgendwo verändert> wird, und damit auch alle ungenutzten Arrayelemente rausschmeißen.
Nur in SEHR speziellen Situationen.
> Oder macht er das nicht, weil die Variable global ist, und er daher> nicht überblicken kann, ob irgendwo eine Änderung stattfindet.
Richtig.
> Aber dann> bräuchte man doch kein volatile, oder?
Braucht man auch nicht. Nur wenn es um das Thema Interrupt oder
Hardwareregister geht.