Forum: Compiler & IDEs -minit-stack=N


von Timo (Gast)


Lesenswert?

Hallo,

es wurde letzte Woche in einem Beitrag erwähnt, die obere Grenze des RAM 
kann durch den Befehl "-minit-stack=N" festgelegt werden.

Würde das Gerne mal ausprobieren. Doch wo leg ich diese Grenze fest? Im 
Code, Makefile? Ich benutze das AVR-Studio mit nem Atmega 88 und 
programiere in C.

Eine kleine Hilfestellung wäre echt super!

Vielen Dank

von Daniel N. (Gast)


Lesenswert?

Hi,
ich würds jetz einfach mal im Makefile zu den CFLAGS hinzufügen:

CFLAGS += -minit-stack=N

von Timo (Gast)


Lesenswert?

Gibt es dann da irgendeinen default Wert für N?

Bräuchte ca. 3 Byte die ich als eigenen Speicher nutzen kann und die 
nicht nach einem Reset durch den Watchdog gelöscht werden....

von Christian (Gast)


Lesenswert?

Ich glaub 40.

von A.K. (Gast)


Lesenswert?

N ist die obere Speichergrenze. Also ins Datasheet reinschauen wo die 
liegt, und davon 4 Bytes abziehen.

von Timo (Gast)


Angehängte Dateien:

Lesenswert?

>> Ich glaub 40.

Wie kommst du auf 40?

>> Also ins Datasheet reinschauen wo die liegt,

Was genau stellt denn die obere Grenze da. Meine 1024 Byte? Also der 
Atmega88 hat 1024 Byte RAM. Ist das gleichzusetzen mit dem N oder 
entspricht N der Hexzahl aus dem Datenblatt? Siehe Anhang?



von A.K. (Gast)


Lesenswert?

Oberste Adresse vom Stack, nicht Anzahl Bytes im SRAM. Also 0x4FF.

von Timo (Gast)


Lesenswert?

Gut,

hab jetzt im Makefile stehen:

CFLAGS += -minit-stack=0x04FA

Hab also sozusagen 4 Bytes freigemacht. Wie kann ich im Programm nun auf 
diese 4 Bytes zugreifen?

von A.K. (Gast)


Lesenswert?

1
struct DeinSpeicher {
2
    ...deine 3 Bytes...
3
} *ptrDeinSpeicher = (struct DeinSpeicher *)0x4FC;

von Timo (Gast)


Lesenswert?

Supi, Danke!

Also hab ich mir damit:

struct RAM_Speicher{

  unsigned char Byte_1;
  unsigned char Byte_2;
  unsigned char Byte_3;

  }*RAM = (struct RAM_Speicher*) 0x4FC;

einen Zeiger RAM auf die Variablen Byte_1 bis 3 erzeugt die sich im 
Speicherbereich 0x4FC bis 0x4FE befinden. Richtig?

Arbeite zum ersten mal mit Pointern....
Kann ich jetzt ganz normal über z.B. *RAM.Byte_1 = 50; auf die 
Speicherstellen zugreifen, oder gibt es da noch irgendetwas zu beachten.

von A.K. (Gast)


Lesenswert?

Fast. Bei *RAM.Byte_1 passen die Prioritäten nicht [läuft auf 
*(RAM.Byte_1) raus]. Eher schon RAM->Byte_1.

von Timo (Gast)


Lesenswert?

> Eher schon RAM->Byte_1

Wie jetzt? RAM->Byte_1 ist ein gültiger Befehl?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Timo wrote:

> Bräuchte ca. 3 Byte die ich als eigenen Speicher nutzen kann und die
> nicht nach einem Reset durch den Watchdog gelöscht werden....

Nimm die .noinit-Section dafür.

von Timo (Gast)


Lesenswert?

> Nimm die .noinit-Section dafür.

Was ist die  .noinit-Section?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Timo wrote:

>> Nimm die .noinit-Section dafür.
>
> Was ist die  .noinit-Section?

RTFM:

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html#sec_dot_noinit

von Timo (Gast)


Lesenswert?

Ließt sich ja ganz gut!

Das steht aber im dritten Punkt dass ich die "noinit section" an eine 
bestimmte Stelle ins RAM legen sollte, das wäre von Vorteil.

Wo kann ich das Definieren? Also dies Zeile:

$ avr-gcc ... -Wl,--section-start=.noinit=0x802000 ...


Im Makefile?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nein, das ist nicht irgendwie von Vorteil, du kannst es nur tun.
Wenn du nichts angibst, landet .noinit hinter dem normalen .data
und .bss.

von Timo (Gast)


Lesenswert?

Wo könnte ich das denn tun?

Bekomme nämlich immer die Fehlermeldung, dass ich etwas anderes dort 
überschreibe......

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Naja, wenn du da was anderes überschreibst, wird das wohl auch wahr
sein, oder?

Die Frage ist: warum willst du das denn explizit zuweisen?  Die
implizite Belegung ist doch sinnvoller, da sie automatisch die
dichteste Packung erzeugt.  Eine explizite Zuweisung braucht man
eigentlich nur, wenn man auf diese Weise z. B. Daten vom Bootlader
zur Applikation rüberreichen möchte.

Ansonsten musst du schon ein wenig mehr von deinem Fehlerbild
schreiben.

von Timo (Gast)


Lesenswert?

> Naja, wenn du da was anderes überschreibst, wird das wohl auch wahr
> sein, oder?

Schon richtig, daher möchte ich ja Wissen wo ich diese expliziete 
Zuweisung machen kann. Ansonsten überschreibe ich ja anscheinend einen 
Bereich.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Da der Linker die Aufteilung auf die Adressbereiche vornimmt, muss
man sowas auf der Linker-Kommandozeile machen.  Da man diese
normalerweise dem GCC überlässt, übergibt man das selbigen beim
Linken mit -Wl, .

Nein, ich denke, das hast du alles bereits korrekt gemacht, daran
liegt es nicht.  Der Fehler ist woanders, aber da du uns nur kleine
Bröckchen vorsetzt und meine Kristallkugel bei dem trüben Wetter
auch leider nicht richtig arbeitet, kann dir gerade niemand richtig
sagen, was du denn flasch gemacht hast.

von Timo (Gast)


Lesenswert?

Also diese explizite Zuweisung brauche ich, damit die Werte die an 
diesen Stellen liegen, nach einem Watchdog Reset nicht gelöscht werden.

Nun noch mal zu meinem Problem wie oben beschrieben:

> Bräuchte ca. 3 Byte die ich als eigenen Speicher nutzen kann und die
> nicht nach einem Reset durch den Watchdog gelöscht werden....

Da hab ich zuerst die obere Stackgrenze des Speichers mit

CFLAGS += -minit-stack=0x04FA

im Makefile runter gesetzt und mir ein Struktur im Code der folgenden 
Art erzeugt:

struct RAM_Speicher{

  unsigned char Byte_1;
  unsigned char Byte_2;
  unsigned char Byte_3;

  }*RAM = (struct RAM_Speicher*) 0x4FC;


Danach müssten doch die drei Byte im Bereich 0x4FC bis 0x4FE liegen und 
ich kann durch RAM->Byte_1 = bla bla; auf diese Stellen zugreifen.

Dabei tritt aber der Fehler auf, dass ich Probleme beim Compilieren 
bekomen. Sprich, das AVR-Studio bleibt hängen.


Dann hattest du von einer anderen Möglichkeit berichtet, die mit der 
.noinit-Section. Doch dabei tritt das folgende Problem auf:

Ich deklariere mir eine Variable "ram" nach .noinint:

volatile uc_8 ram _attribute_ ((section (".noinit")));

Hab aber zusätzlich im Code noch ein Array das folgendermaßen deklariert 
ist:

volatile uc_8 rf_buffer_rx[32]__attribute__((section(".buffer_rx")));

Nun bekomme ich beim compilieren diese Fehlermeldung:

C:\Programme\WinAVR\bin\..\lib\gcc\avr\3.4.5\..\..\..\..\avr\bin\ld.exe: 
section .noinit [00800108 -> 00800108] overlaps section .buffer_rx 
[00800100 -> 0080011f]


So, das ist im Moment mein Problem. Vielleicht weiß ja jemand ne 
Lösung....

Gruß

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Timo wrote:

> Also diese explizite Zuweisung brauche ich, damit die Werte die an
> diesen Stellen liegen, nach einem Watchdog Reset nicht gelöscht
> werden.

Naja, das macht .noinit ganz von allein, und ganz ohne feste Zuweisung
einer Adresse.

> Da hab ich zuerst die obere Stackgrenze des Speichers mit

> CFLAGS += -minit-stack=0x04FA

0x4fb müsste es auch noch tun, eigentlich sogar 0x4fc, denn die drei
Bytes brauchen nur 0x4fd, 0x4fe und 0x4ff.

> Dabei tritt aber der Fehler auf, dass ich Probleme beim Compilieren
> bekomen. Sprich, das AVR-Studio bleibt hängen.

Der Compiler tut's aber ordentlich, und der resultierende Code würde
(meiner Meinung nach, wenn du nicht noch was anderes vergeigt hast)
funktionieren.  Wenn AVR Studio da irgendwelchen Mist macht: sorry.
Musst du denen einen Bugreport schreiben.

> Hab aber zusätzlich im Code noch ein Array das folgendermaßen
> deklariert ist:

> volatile uc_8 rf_buffer_rx[32]__attribute__((section(".buffer_rx")));

Wofür ist das denn gut, und wie weist du dessen Adresse zu?

Ich glaube mich zu erinnern, dass man zusätzliche, nicht von
vornherein im Linkerscript bekannte sections nur im ROM anlegen kann.
Für zusätzliche sections im RAM muss man mit einem eigenen
Linkerscript arbeiten.

Ganz davon abgesehen: die section .buffer_rx wird (je nach der
konkreten Gestaltung deines Linkerscripts) ohnehin nicht von der
Initialisierung erfasst, also kannst du eigentlich auch deine drei
Statusbytes dort mit reinlegen.

Es ist aber ziemlich mühselig, dir hier jedes Detail aus der Nase
ziehen zu müssen.

von Timo (Gast)


Lesenswert?

Hi Jörg,

als ich habe gerade festgestellt, dass die Adresse des buffers im 
Makefile fest vorgeschrieben ist. Daher dann wohl auch das Problem mit 
der Überschneidung.

Werd mal die Adressen anpassen und .noinit auch einen festen Startwert 
zuweisen. wie kann ich das makefile denn ändern? Mit nem normalen Editor 
werden die Änderungen irgendwie nicht übernommen.....

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Timo wrote:

> wie kann ich das makefile denn ändern?

Genauso, wie du das initial da reinbekommen hast.

> Mit nem normalen Editor
> werden die Änderungen irgendwie nicht übernommen.....

Dann musst du deine Build-Umgebung bzw. dessen Manual befragen.  Bei
mir werden Makefiles (fast) nur mit 'nem normalen Editor bearbeitet.

(Ausnahme: für das initiale Makefile eines neuen Projekts nehme ich
auch gern mal Mfile.)

von Timo (Gast)


Lesenswert?

Haut bei mir nicht hin!

Sobald ich das Projekt mit dem neuen Makefile compiliere, werden die 
Änderungen verworfen. Also das Makefile sieht wieder genauso aus wie 
vorher.

Hängt das Makefile vielleicht noch mit ner anderen Datei zusammen, die 
bei der ersten Initialisierung erzeugt wird?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nimmst du AVR Studio?  Dann musst du dem Teil beibringen, dass du ein
externes Makefile benutzt.

Allerdings frage ich mich dann, wie du die Adresse deiner extra
section rx_buffer da reinbekommen hast...

von Oliver (Gast)


Lesenswert?

>Hängt das Makefile vielleicht noch mit ner anderen Datei zusammen, die
>bei der ersten Initialisierung erzeugt wird?

AVRStudio erzeugt das makefile immer neu. Das lässt sich nicht 
beeinflussen, und ein (editierbares) Template gibt es auch nicht.

Es gibt nur zwei Möglichkeiten:

- Du gibts deine zusäzuliche Compiler-/Linkeroptionen unter 
Project/Options an, dann werde die ins makefile geschrieben. Nur da kann 
eigentlich auch die feste Adresse für die ominöse buffer_rx-section 
herkommen. (Was bedeutet, daß du das Projekt nicht selber erstellt 
hast...)

- Du exportierst das makefile einmalig, und definierst das dann als 
externes makefile. Dann kannst (und musst) du beliebig per Texteditor 
drin rumändern. Automatische Anpassungen, z.B. bei hinzufügen einer 
weiteren Source-Dateiu, gibt es dann nicht mehr.

Nach wie vor ist mir aber nicht klar, warum du unbedingt eine feste 
Adress für die .noinit-section brauchst. Und wofür ist die 
buffer_rx-section? Hast du zusätzliches externes Ram am Prozesor?

Oliver

von Timo (Gast)


Lesenswert?

Hey Oliver,

das mit der ersten Möglichkeit funktioniert. Und wie du schon sagst ist 
dort auch diese ominöse rx_section definiert.

Wofür diese rx_section ist, kann ich dir gar nicht so genau sagen. Dort 
befinden sich auf jeden Fall die über UART empfangenen Daten. Wenn ich 
diese feste Adresse für die rx_section weglasse, funktioniert das 
Programm nicht mehr.

Nun hab ich dort auch für die noinit_section einen festen Bereich 
definiert und eine Variable erzeugt.

volatile uc_8 Test3 _attribute_ ((section (".noinit")));

Wenn ich keinen festen Bereich für noinit definiere, behagen sich noinit 
und rx. Mit nem festen Bereich  kann ich das Programm kompilieren und 
auch zum laufen bringen. Jedoch ist der Wert den die Variable Test3 im 
Programm zugewiesen bekommen hat nach einem Watchdog nicht mehr 
vorhanden.

Hab ich da vielleicht noch was vergessen?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Wenn ich
> diese feste Adresse für die rx_section weglasse, funktioniert das
> Programm nicht mehr.

Sieht so aus, als hätte da jemand die letzten drei Bytes optimiert,
die man für eine vernünftige Zeigerarithmetik gebraucht hätte.  Durch
das Binden von .rx_buffer auf eine 256er-Grenze braucht man für einen
256-Byte-Puffer dann nur das Lowbyte des Zeigers zu behandeln.

Das ist ,,trickreiche Programmierung'' im schlimmsten Sinne, wie man
sie zu besten Z80-Zeiten gemacht hat, um tatsächlich noch mit dem
letzten Byte auszukommen.  Würde ich auf einem AVR nicht machen, wenn
es nicht wirklich zwingend notwendig ist (es also auf genau die paar
Bytes Code oder die halbe Mikrosekunde Zeiteinsparung ankommt).

Dann leg' doch deine drei Statusvariablen noch in die .rx_buffer
Section mit rein.  Du musst dann nur gucken, ob sie vor oder hinter
dem rx_buffer landen.  Wenn sie davon landen, musst du den Anfang
dieser section drei Bytes vorziehen.

Alternativ kannst du wahrscheinlich auch noch eine Vorgabe für
.noinit mit unterbringen, z. B. genau vor dem rx_buffer oder genau
danach.

Aber ich würde das alles so umschreiben, dass man keine bestimmte
vorgegebene Adresse für rx_buffer braucht, diese section dann ganz
eliminieren, und mit .noinit ohne weitere Parametrierung arbeiten.

von Timo (Gast)


Angehängte Dateien:

Lesenswert?

> Sieht so aus, als hätte da jemand die letzten drei Bytes optimiert,
> die man für eine vernünftige Zeigerarithmetik gebraucht hätte. Durch
> das Binden von .rx_buffer auf eine 256er-Grenze braucht man für einen
> 256-Byte-Puffer dann nur das Lowbyte des Zeigers zu behandeln.

Versteh ich nicht ganz.

> Würde ich auf einem AVR nicht machen, wenn
> es nicht wirklich zwingend notwendig ist (es also auf genau die paar
> Bytes Code oder die halbe Mikrosekunde Zeiteinsparung ankommt).

Denke das ist der Haken. Der Code regelt die Datenübertragung per Funk 
zwischen zwei AVR's. Da ist schon alles ziemlich Zeitkritisch. Hab 
gerade auch festgestellt, dass da noch mehr Sachen sind, die feste 
Adressen im RAM zugewiesen bekommen haben.

aber wie kann denn die feste Adresse des rx_Buffers im RAM mein Programm 
beeinflussen? Das versteh ich nicht wirklich. Weil wenn ich sie ändere 
oder weglasse, funktioniert nix mehr.

Hab mal die Speichereinstellungen aus dem AVR Studio angehangen.


Aber sagen wir mal die Einstellungen müssen so sein. Ob das so gut ist 
oder nicht sei mal hinten angestellt. Dann bleibt mir ja nichts anderes 
übrig als dem noinit auch einen festen Speicherbereich zuzuweisen. Das 
hab ich einfach mal gemacht. Doch warum behält die Variable, die ich als 
noinit deklariert habe, nicht ihren Wert?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Timo wrote:

>> Sieht so aus, als hätte da jemand die letzten drei Bytes optimiert,
>> die man für eine vernünftige Zeigerarithmetik gebraucht
>> hätte. Durch das Binden von .rx_buffer auf eine 256er-Grenze
>> braucht man für einen 256-Byte-Puffer dann nur das Lowbyte des
>> Zeigers zu behandeln.

> Versteh ich nicht ganz.

Dann guck dir die Puffer-Behandlung an.  Sorry, du hast den Code,
wir nicht.  Wenn du fremder Leute Code benutzt, solltest du ihn
zumindest ansatzweise verstanden haben.

> Denke das ist der Haken. Der Code regelt die Datenübertragung per
> Funk zwischen zwei AVR's. Da ist schon alles ziemlich Zeitkritisch.

Ich mach' hier auch den ganzen lieben langen Tag lang Funkübertragung
mit AVRs. ;-)  Daher glaube ich dir das einfach mal nicht, dass es auf
diese Mikrosekunde noch ankommt.  Ich habe hier die ACK-Zeit eines
IEEE-802.15.4-Stacks mal von eingangs knapp 700 µs auf die vom
Standard geforderten 192 µs optimiert, und dabei keine Zeile in
Assembler verfasst (hätte sowieso bei der Komplexität des Codes nichts
gebracht), noch wäre ich auf die Idee gekommen, derartig schräge Hacks
zu benutzen.

> aber wie kann denn die feste Adresse des rx_Buffers im RAM mein
> Programm beeinflussen? Das versteh ich nicht wirklich.

Ja, du solltest das Projekt mitsamt seinen Haken und Ösen schon mal
vor der Benutzung verstanden haben.  Wenn du das Speicher-Layout dir
dann mal verinnerlicht hast, weißt du auch, wo noch Platz für dein
.noinit ist (vermutlich vor dem ganzen rx_buffer-Geraffel, ich denke,
dass da einige Bytes unbenutzt sind).  Den musst du dann nur noch so
legen, dass er nicht gerade von __do_clear_bss und __do_copy_data
erfasst wird (das sind Funktionen des Compilers, die siehst du
zumindest im .lss).

von Timo (Gast)


Lesenswert?

Den Platz für die Bytes hab ich wohl gefunden.

Wo kann ich denn die Adressen von __do_clear_bss und __do_copy_data 
ansehen? Meine Variable wird nämlich immer noch überschrieben.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Timo wrote:

> Wo kann ich denn die Adressen von __do_clear_bss und __do_copy_data
> ansehen? Meine Variable wird nämlich immer noch überschrieben.

Da das .lss-File ein Dissassembler-Listing ist, solltest du dort die
tatsächlichen Werte drin finden.

Kurz gesagt: __do_clear_bss nullt alles von __bss_start bis kleiner
__bss_end aus (diese beiden Symbole werden vom Linker eingesetzt).
__do_copy_data kopiert die Daten aus dem ROM ab __data_load_start
in den RAM ab __data_start bis unterhalb __data_end.  Wiederum werden
die genannten Symbole vom Linker eingetragen.  Die Werte aller Symbole
solltest du in der Symboltabelle (.sym) finden.

von Timo (Gast)


Lesenswert?

Hab mir gerade mal ein kleines Testprogramm geschrieben. Da funktioniert 
das mit der noinit Initialisierung.

Doch bei dem anderen Code haut das nicht hin. Die Variable wird nach dem 
Watchdog durch irgendetwas überschrieben.

von Timo (Gast)


Lesenswert?

Jetzt klappt es!

Super, vielen Dank Jörg! Der Wert blieb auch noch nach dem Watchdog 
erhalten, wurde aber von folgendem Code bei der Initialisierung 
gelöscht:

i = input(SPL);
i ^= 0xFF;
extern ui_16 __bss_start;
extern ui_16 __stack;
uc_8 *ptr = (uc_8*)&__bss_start;
while(ptr<(uc_8*)&__stack - i){/*sub because of returning of 
subroutine*/
    *ptr++=0;
  }

Werd jetzt erst einmal herausfinden, was diese Codestück genau macht. 
Vielleicht weißt du es ja?

Anscheinend werden die die Inhalte der bss_start- und stack-Adressen auf 
0 gesetzt.

Hab noch ne Frage, wo genau finde ich im AVR Studio diese 
Symboltabelle(.sym)?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ick, ich würde diesem Projekt nicht wirklich übern Weg trauen.
Die reimplementieren offenbar Teile dessen, was normalerweise
vom C-Startup-Code ausgeführt wird.

> Hab noch ne Frage, wo genau finde ich im AVR Studio diese
> Symboltabelle(.sym)?

Ich benutze kein AVR Studio.  Beim normalen WinAVR-Makefile-Template
wird diese Datei automatisch bei »make all« mit angelegt.  Wenn
AVR Studio's Makefile das nicht tut, kannst du dir die Symboltabelle
jederzeit mit dem Kommando
1
avr-nm <name of your ELF file>
angucken.

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.