Forum: Compiler & IDEs AVR-GCC: externer SRAM, wie?


von Peter D. (peda)


Lesenswert?

Nun habe ich 32kB ab 0x8000 an den ATmega2560 rangepappt und steh auf
dem Schlauch, wie die beim GCC anmelden.

Wie kann ich dem GCC für bestimmte Variablen (große Arrays) sagen, daß
er sie dorthin legen soll?

Schön wärs, wenn der GCC ihn auch nullen bzw. initialisieren kann.

Die Lücke 0x2200-0x7FFF muß reserviert bleiben!
Da ist nämlich kein SRAM, sondern memory mapped IO.


Peter

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


Lesenswert?

Wie wär's, einfach einen Zeiger zu benutzen?

Ansonsten kannst du das irgendwie mit sections jonglieren, wenn du
unbedingt willst.

von Simon K. (simon) Benutzerseite


Lesenswert?

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ext_ram
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reloc_code

Wäre jetzt mein erster Ansatz. Also eine neue Linker Section anlegen an 
die richtige Adresse und per attribute das große Array dort hinein 
legen.

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Wie wär's, einfach einen Zeiger zu benutzen?

Wenns garnicht anders geht, wäre das die Notvariante.
Bloß dann muß ich mir die ganze Plazierung umständlich selber auszählen, 
das wollte ich vermeiden.


> Ansonsten kannst du das irgendwie mit sections jonglieren, wenn du
> unbedingt willst.

Ja, so hatte ich es gedacht.
Bin aber aus den Beispielen nicht schlau geworden. Die wollen immer 
gleich ab 0x2200 anfangen und dann auch nur für malloc-Variablen.
Malloc will ich aber garnicht verwenden, da alle Arrays schon bekannt 
sind.


Peter

von Stefan E. (sternst)


Lesenswert?

Die Verwendung per separater Section ist doch recht einfach:
1
#define EXTMEM __attribute__((section(".extmem")))
2
3
uint8_t data[100] EXTMEM;
1
-Wl,--section-start=.extmem=0x88000

Aber Achtung: keine automatische Nullung!

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


Lesenswert?

Peter Dannegger schrieb:
> Bloß dann muß ich mir die ganze Plazierung umständlich selber auszählen,
> das wollte ich vermeiden.

Hmm, verstehe ich nicht ganz.  Wenn das MMIO ist, dann hat man doch
normalerweise alles auf vorgegebenen Adressen?  Vielleicht erzählst
du uns ja doch noch drei Sätze mehr dazu, was du da genau hast.

Bliebe noch die Variante, alles in einer struct aufzuführen, und
dann einen Zeiger auf diese zu benutzen.

von Eduard S. (schneehase)


Lesenswert?

Hi

ein gutes Beispiel was du machen musst ist auf 
http://www.nongnu.org/avr-libc/user-manual/malloc.html beschrieben ab 
"Tunables for malloc()".

Du musst im Makefile für den Linker die Startadressen von .data und .bss 
ändern ( --section-start,.data=0x808000, --defsym=__heap_end=0x80ffff ). 
Würde dann in etwa so aussehen 
http://www.nongnu.org/avr-libc/user-manual/malloc-x1.png. Und du must 
vor dem Start von main() im µC den Speicher ummappen ( MCUCR, WDTCR ). 
Muss in die Section ".init3" oder ".init4". Ist sehr gut auf 
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=90099&start=0 
beschrieben

von Lazy Leo (Gast)


Lesenswert?

Hab ich schonmal was zu geschrieben. Die Suchen-Funktion ist Dein 
Freund.

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Hmm, verstehe ich nicht ganz.  Wenn das MMIO ist, dann hat man doch
> normalerweise alles auf vorgegebenen Adressen?  Vielleicht erzählst
> du uns ja doch noch drei Sätze mehr dazu, was du da genau hast.

Also:
0x0000 - 0x1FFF ist interner RAM für Stack und Variablen.

0x2000 - 0x7FFF sind IO-Chips dran, die werden per Pointer auf Structs 
adressiert.

0x8000 - 0xFFFF ist externer SRAM und da möchte ich große Arrays 
(Menü-Listen) ablegen. Die sollen wenn möglich vorbelegt oder genullt 
werden.



Peter

von Stefan E. (sternst)


Lesenswert?

Eduard Scheske schrieb:

> ein gutes Beispiel was du machen musst ist auf
> ...
> Du musst im Makefile für den Linker die Startadressen von .data und .bss
> ändern ( --section-start,.data=0x808000, --defsym=__heap_end=0x80ffff ).

Unsinn, da steht was er machen kann, nicht was er machen muss. Wie 
ich Peter kenne, will er sicher nicht alle statischen Variablen in das 
externe RAM legen und somit den Zugriff auf diese verlangsamen.

von Stefan E. (sternst)


Lesenswert?

Peter Dannegger schrieb:
> Die sollen wenn möglich vorbelegt oder genullt werden.

Diese Möglichkeit sehe ich nur, wenn du dann doch tatsächlich alles (bis 
auf den Stack) in das externe RAM legst. Oder einfach am Anfang des 
Programms selber initialisieren, z.B. von PROGMEM-Arrays kopieren.

von Peter D. (peda)


Lesenswert?

Eduard Scheske schrieb:
> ein gutes Beispiel was du machen musst ist auf
> http://www.nongnu.org/avr-libc/user-manual/malloc.html beschrieben ab
> "Tunables for malloc()".

Um mal die Bilder in dem Link zu nehmen, will ich folgendes:

Interner RAM bis 0x1FFF:
.data_1
.bss_1
.stack

Externer RAM ab 0x8000:
.data_2 (mit nem Attribut angelegt)
.bss_2 (mit nem Attribut angelegt)

Einen .heap habe ich nicht.


Peter

von Peter D. (peda)


Lesenswert?

Lazy Leo schrieb:
> Hab ich schonmal was zu geschrieben. Die Suchen-Funktion ist Dein
> Freund.

Die Suche nach "Lazy Leo" ergibt 0 Treffer.


Peter

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


Lesenswert?

Stefan Ernst schrieb:

>> Die sollen wenn möglich vorbelegt oder genullt werden.
>
> Diese Möglichkeit sehe ich nur, wenn du dann doch tatsächlich alles (bis
> auf den Stack) in das externe RAM legst.

Zustimmung.  gcrt1.S kann nur mit einem zusammenhängenden Block
.data und einem Block .bss umgehen, den Rest muss man ,,zu Fuß''
machen.

Falls genügend externer SRAM vorhanden ist, kann man natürlich wirklich
alles dahin legen und den internen RAM nur noch für den Stack benutzen.
Leider braucht externer SRAM aber einen Takt mehr im Zugriff.

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Zustimmung.  gcrt1.S kann nur mit einem zusammenhängenden Block
> .data und einem Block .bss umgehen, den Rest muss man ,,zu Fuß''
> machen.

.bss ist ja der genullte data-Bereich, kann ich nur den nach intern 
legen?


> Falls genügend externer SRAM vorhanden ist, kann man natürlich wirklich
> alles dahin legen und den internen RAM nur noch für den Stack benutzen.
> Leider braucht externer SRAM aber einen Takt mehr im Zugriff.

Werd ich dann wohl so machen müssen.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Schreib doch ein eigenes Nullungsscript in eine init Section?!

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


Lesenswert?

Peter Dannegger schrieb:
> .bss ist ja der genullte data-Bereich, kann ich nur den nach intern
> legen?

Nö, das Standard-Linkerscript verkettet .data und .bss zu einer
gemeinsamen Ausgabe-Section namens .data.

Ohne jetzt nachzugucken im gcrt1.S (das kannst du ggf. selbst tun)
würde ich sagen, dass dieses sich auf die Symbole für den Anfang
und das Ende von .bss verlässt, von daher sollte es mit einem
geänderten Linkerscript gehen.

von Stefan E. (sternst)


Lesenswert?

Peter Dannegger schrieb:

> .bss ist ja der genullte data-Bereich, kann ich nur den nach intern
> legen?

Also geht es in erster Linie um die Nullung? Dann würde ich an deiner 
Stelle doch noch mal die Heap-Lösung in Betracht ziehen. Wenn es eher 
wenige große Arrays sind (und nicht viele kleine) ist der zusätzliche 
Speicherverbrauch minimal. Und mit calloc (statt malloc) kannst du die 
Arrays dann auch gleich Nullen.

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


Lesenswert?

Ist aber auch nichts anderes, als wenn er an den Beginn seines
main() schreiben würde:
1
memset((void *)0x8000, 0, 0x8000);

von Stefan E. (sternst)


Lesenswert?

Ja, die Nullung wäre halt nur etwas selektiver.

Ich persönlich würde ja auch einfach die Lösung mit der separaten 
Section nehmen, und dann einmal zu Beginn selber Nullen. Aber ich habe 
so den Eindruck, dass ihm das irgendwie nicht zusagt.

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Falls genügend externer SRAM vorhanden ist, kann man natürlich wirklich
> alles dahin legen und den internen RAM nur noch für den Stack benutzen.

So habe ich es jetzt gemacht und es funktioniert.
Damit kann man die Menüs vorbelegen. Ich hätte ja die Menüs in den Flash 
gelegt und wäre ohne extra SRAM ausgekommen, aber die Programmierer 
wollen es halt schön bequem haben.


Ne Fallgrube war noch die init3-Routine, die muß used sein, sonst wird 
sie wegoptimiert:
1
void init_extSRAM(void) __attribute__((used,naked,section(".init3")));
2
void init_extSRAM(void)
3
{
4
  XMCRA = 1<<SRE | 1<<SRL2 | 1<<SRW00;          // 0x2200 - 0x7FFF: 1 Wait
5
                                                // 0x8000 - 0xFFFF: 0 Wait
6
  XMCRB = 1<<XMBK;                              // A15 .. A8
7
}

Und der Linkerschalter:
1
-Wl,--section-start,.data=0x808000


Danke an alle für die Hilfe.

Peter

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


Lesenswert?

> die muß used sein, sonst wird
> sie wegoptimiert:

Nur, wenn man den "ich bin zu faul, mir beim Programmieren zu
überlegen, welche Funktionen ich wirklich mal benötigen werde"-Knopf
benutzt und daher -gc-sections beim Linken angibst.  Dann mach der
Linker natürlich genau das, was du ihm sagst: du hast die Verantwortung
herauszufinden, was denn wirklich gebraucht wird, ja vom Progammierer
an den Linker übertragen, und der schmeißt dann radikal alles weg,
was unbenutzt aussieht.

Ich verstehe ja, dass C++-Programmierer darunter leiden, dass ihnen
der Compiler aus formalen Gründen zuweilen Dinge generiert, die sie
dann eigentlich gar nicht brauchen, und dass man genau diese mittels
-gc-sections wieder aufgeräumt bekommt, aber warum Programmierer für
Microcontroller nicht in der Lage sein wollen, Funktionen, die sie
gar nicht benötigen, auch gar nicht erst im Sourcecode stehen zu
lassen, das leuchtet mir nach wie vor nicht ein.

YMMV. ;-)

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.