Forum: Mikrocontroller und Digitale Elektronik Strings im Flash ablegen, oder nicht?


von Stringmaster Flash (Gast)


Lesenswert?

Ich beschäftige mich derzeit mit AVRs und und dem avr-gcc. Soweit 
funktioniert auch alles, ich frage mich jedoch wann es Sinn macht 
Strings im Flash abzulegen (so wie im Tutorial beschrieben).

Mal angenommen ich habe folgenden Code:
1
uart_output("Dies ist eine Test-Ausgabe aus dem RAM");

vs.
1
uart_output_P(PSTR("Dies ist eine Test-Ausgabe aus dem Flash"));

Option #2 braucht letztendlich sogar mehr Flash, wenn man es kompiliert. 
Das macht vermutlich auch Sinn, weil der String ja in beiden Fällen im 
Flash steht, im letzteren aber noch etwas Overhead entsteht, um die 
Pointer einzurichten, während im ersten Fall der Stack Frame vom 
Compiler vorbereitet werden kann.

Nun ist mir nicht ganz klar, warum man Strings überhaupt im Flash 
ablegen möchte. Sofern man es nicht mit "globalen" Strings zu tun hat 
ist es doch platztechnisch sinnvoller Strings ganz normal zu verabeiten 
und auf die _P Pendants zu verzichten, oder?

Was übersehe ich? Was sind die jeweiligen Vor- bzw. Nachteile?

von Stefan E. (sternst)


Lesenswert?

Stringmaster Flash schrieb:
> Sofern man es nicht mit "globalen" Strings zu tun hat
> ist es doch platztechnisch sinnvoller Strings ganz normal zu verabeiten
> und auf die _P Pendants zu verzichten, oder?

Alle String-Literale sind "globale" Strings, auch die, die du direkt als 
Funktionsargument benutzt.

Stringmaster Flash schrieb:
> Was übersehe ich? Was sind die jeweiligen Vor- bzw. Nachteile?

Der Vorteil ist der geringere statische RAM-Verbrauch.

von dude (Gast)


Lesenswert?

Stringmaster Flash schrieb:
> Option #2 braucht letztendlich sogar mehr Flash, wenn man es kompiliert.

Ja, aber dafür der String nicht komplett in den SRAM, und der ist 
meistens viel knapper.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Stringmaster Flash schrieb:
> Was sind die jeweiligen Vor- bzw. Nachteile?

Der Nachteil bei Verzichten auf die "_P Pendants" ist, dass Du Dir das 
RAM vollknallst. Hast Du viele Strings in Deinem Programm, die zum 
Beispiel auf einem Display ausgegeben werden sollen, kann es schnell 
passieren, dass der Speicherplatz im RAM ausgeht.

Der Vorteil: Der Zugriff auf die Strings im RAM ist einfacher und 
benötigt auch minimal weniger Platz im Flash.

von Carl D. (jcw2)


Lesenswert?

Stringmaster Flash schrieb:
> Was sind die jeweiligen Vor- bzw. Nachteile?

FLASH und RAM sind bei AVR nicht einfach nur andere Addressbereiche, 
sonder 2 völlig getrennt Addressräume, die unterschiedliche Befehle für 
den Zugriff erfordern.
Im FLASH stehen die Strings in jedem Fall, denn alles nicht-Null 
Initialisierte muß ja irgendwo stromausfallsicher gespeichert sein. Der 
Startup-Code kopiert diese Initialisierungsdaten aus dem FLASH ins RAM. 
Und dort belegen die String-Konstanten wertvollen Platz.

Daß mit "_P" mehr Platz gebraucht wird, liegt daran, daß anderer 
Zugriffs-Code gebraucht wird, der zudem nur Register R30/R31 oder kurz Z 
zugreifen kann, d.h. potentiell erst mal die nötigen Register 
freischaufeln muß.

Angenehmer Nebeneffekt von FLASH-Stringliteralen ist übrigens, daß sie 
auch wirklich konstant sind ;-)

von Jacko (Gast)


Lesenswert?

Wenn das RAM genug Platz bietet, pack sie in's RAM.
FERTIG.

Wenn du auf kleinen AVRs mit 256  512  1024 Byte RAM
nicht ganz so kleine Anwendungen programmierst, musst du
irgendwann tüfteln, wie du den Speicherplatz optimal ausnutzen
kannst. Zur Not müssen eben Strings, oder alternative
Parameter-Arrays nachgeladen werden...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stringmaster Flash schrieb:
> Ich beschäftige mich derzeit mit AVRs und und dem avr-gcc. Soweit
> funktioniert auch alles, ich frage mich jedoch wann es Sinn macht
> Strings im Flash abzulegen (so wie im Tutorial beschrieben).

Dazu wurde ja schon einiges geschrieben.  Der Grund dafür, read-only 
Daten (.rodata) im RAM abzulegen und nicht im Flash, ist, dass man bei
1
char readc (const char *c)
2
{
3
    return c;
4
}

Nicht weiß, ob ausm RAM per LD oder ausm Flash per LPM gelesen werden 
muss.  readc kann nämlich auch für veränderliche Daten aufgerufen 
werden.  Das const char* sagt ja nur, dass diese Funktion *c nicht 
ändert.

Manche neuere AVR-Derivate wie ATtiny40 (Reduced Tiny -mmcu=avrtiny) und 
ATtiny1616 etc. (-mmcu=avrxmega3) haben einen linearen Adressraum, wo 
das Flash im RAM-Adressbereich sichbar ist.  Auf diesen Devices kann man 
auf __flash und PROGMEM / PSTR etc. ohne Overhead verzichten.
1
const int i = 2;
2
3
__attribute((noinline,noclone))
4
int readi (const int *pi)
5
{
6
    return *pi;
7
}
8
9
int main (void)
10
{
11
    return readi (&i);
12
}
 
Zunächst für ein "normales" Device mit .rodata im RAM:
 
1
$ avr-gcc  foo.c -o foo.elf -mmcu=attiny25 -Os -save-temps -Wl,-Map,foo.map
2
$ avr-objdump -d foo.elf
 
1
Disassembly of section .text:
2
...
3
4
00000042 <readi>:
5
  42:  fc 01         movw  r30, r24
6
  44:  80 81         ld  r24, Z
7
  46:  91 81         ldd  r25, Z+1
8
  48:  08 95         ret
9
10
0000004a <main>:
11
  4a:  80 e6         ldi  r24, 0x60
12
  4c:  90 e0         ldi  r25, 0x00
13
  4e:  f9 cf         rjmp  .-14       ; 0x42 <readi>
 
i liegt also an Adresse 0x60, das ist die erste RAM-Adresse des 
ATtiny25.  Im Flash liegt i an Adresse 0x54 (direkt nach dem Code), und 
der Startup-Code kopiert es an RAM-Adresse 0x60.
 
1
.data           0x00800060        0x2 load address 0x00000054
2
...
3
 .rodata        0x00800060        0x2 foo.o
4
                0x00800060                i
5
...
6
                0x00000054                __data_load_start = LOADADDR (.data)
7
                0x00000056                __data_load_end = (__data_load_start + SIZEOF (.data))
 
Das gleiche Programm compilert für einen ATtiny40:
 
1
$ avr-gcc  foo.c -o foo.elf -mmcu=attiny40 -Os -save-temps -Wl,-Map,foo.map
2
$ avr-objdump -d foo.elf
 
1
Disassembly of section .text:
2
...
3
4
00000040 <main>:
5
  40:  8a e4         ldi  r24, 0x4A
6
  42:  90 e4         ldi  r25, 0x40
7
  44:  f8 cf         rjmp  .-16       ; 0x36 <readi>
 
1
.rodata         0x0000404a        0x2 load address 0x0000004a
2
 .rodata        0x0000404a        0x2 foo.o
3
                0x0000404a                i
4
...
5
.data           0x00800040        0x0 load address 0x0000004c
6
...
7
                0x0000004c                __data_load_start = LOADADDR (.data)
8
                0x0000004c                __data_load_end = (__data_load_start + SIZEOF (.data))
 
Hier liegt i an Flash-Adresse 0x4a, was per RAM-Adresse 0x404a 
zugegriffen wird.  Das RAM beginnt bei 0x40, enthält hier aber keine 
Daten.  Wenn es welche hätte, würde es vom Startup-Code mit Flash-Daten 
ab 0x4c initialisiert werden.

von Detlev T. (detlevt)


Lesenswert?

Auch wenn der String "im RAM" ist, ist er (zusätzlich) im Flash und wird 
zu Beginn des Programmes ins RAM kopiert. Wo sollte er auch sonst 
herkommen, RAM-Inhalte sind ohne Strom ja futsch.

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.