Forum: Compiler & IDEs avr-gcc stolpert über PROGMEM-Konstrukt


von Jens Fischer (Gast)


Lesenswert?

Hallo Forum!

Ich versuche gerade ein Beispielprojekt für den ATmega169 zu übersetzen, 
welches folgende Codezeile enthält:
1
const uint8_t *DATE_FORMAT_NR[] PROGMEM = {EUROPEAN_DATE_NR, AMERICAN_DATE_NR, CANADIAN_DATE_NR};

Der Compiler (avr-gcc 4.7.0) gibt diese Ausschrift:
1
$ make
2
3
-------- begin --------
4
avr-gcc (GCC) 4.7.0
5
Copyright (C) 2012 Free Software Foundation, Inc.
6
This is free software; see the source for copying conditions.  There is NO
7
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8
9
10
Compiling C: RTC.c
11
avr-gcc -c -mmcu=atmega169 -I. -gdwarf-2 -DF_CPU=1000000UL -Os 
12
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 
13
-fno-inline-small-functions -fno-split-wide-types -fno-tree-scev-cprop 
14
-ffunction-sections -fdata-sections -Wall -Wstrict-prototypes 
15
-Wa,-adhlns=./RTC.lst  -std=gnu99 -Wundef -MMD -MP -MF .dep/RTC.o.d RTC.c -o RTC.o 
16
RTC.c:89:16: error: variable 'DATE_FORMAT_NR' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
17
make: *** [RTC.o] Error 1

Eigentlich steht ja dort schon ein const.

Was muß ich an dem Code ändern, damit er compiliert wird?

Grüße
Jens

von h_ (Gast)


Lesenswert?

Nein, das ist ein Pointer, der auf einen const-Wert zeigt.
Richtig wäre:
1
const uint8_t* const ...

von h_ (Gast)


Lesenswert?

Oder, was du vielleicht eher meinst:
1
const uint8_t DATE_FORMAT_NR[] ....

von asdf (Gast)


Lesenswert?

Was ist ist EUROPEAN_DATE_NR? const <iregendwas> oder #define ?
const wird womoeglich nicht funktionieren.
Womoeglich liegt es auch an der Reihenfolge der qualifier.
const ist linksbindend, bei PROGMEM weiss ich nicht.

von Jens Fischer (Gast)


Lesenswert?

asdf schrieb:
> Was ist ist EUROPEAN_DATE_NR? const <iregendwas> oder #define ?
> const wird womoeglich nicht funktionieren.
1
const uint8_t EUROPEAN_DATE_NR[] PROGMEM =   { 4, 5, 2, 3, 0, 1 };
2
const uint8_t AMERICAN_DATE_NR[] PROGMEM =   { 4, 5, 0, 1, 2, 3 };
3
const uint8_t CANADIAN_DATE_NR[] PROGMEM =   { 0, 1, 2, 3, 4, 5 }; 
4
5
const uint8_t *DATE_FORMAT_NR[] PROGMEM = {EUROPEAN_DATE_NR, AMERICAN_DATE_NR, CANADIAN_DATE_NR};

Mit einem avr-gcc von 2009 muß der Code aber mal gelaufen sein.

h_ schrieb:
> Nein, das ist ein Pointer, der auf einen const-Wert zeigt.
> Richtig wäre:const uint8_t* const ...
Das hilft leider auch nicht. Die Fehlermeldung ist die gleiche.

h_ schrieb:
> const uint8_t DATE_FORMAT_NR[] ....
Nein, ich denke das mit dem Pointer ist schon richtig so. Allerdings 
soll der Pointer offenbar auch konstant sein und im Flash gespeichert 
werden.

Grüße
Jens

von Jens Fischer (Gast)


Lesenswert?

Ich hab gerade nochmal auf einem anderen Rechner (mit einem anderen gcc) 
probiert:
1
avr-gcc (GCC) 4.3.4
2
Copyright (C) 2008 Free Software Foundation, Inc.
3
Dies ist freie Software; die Kopierbedingungen stehen in den Quellen. Es
4
gibt KEINE Garantie; auch nicht für MARKTGÄNGIGKEIT oder FÜR SPEZIELLE ZWECKE.
Da geht der Code problemlos.

Grüße
Jens

von asdf (Gast)


Lesenswert?

Es gibt da eine page mit erklaerungen und beispielen zur C deklarations 
syntax. Vielleicht hilft das weiter.

http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

von Jens Fischer (Gast)


Lesenswert?

asdf schrieb:
> http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html
Vielen Dank.

In diesem Fall liegt es aber offenbar am avr-gcc. Die Version 4.3.4 
verarbeitet das Konstrukt aus dem Eingangspost richti, aber die Version 
4.7.0 liefert einen Fehler.

Bug or Feature?

Grüße
Jens

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jens Fischer schrieb:

> In diesem Fall liegt es aber offenbar am avr-gcc.

Wenn alles andere scheitert, die Dokumentation [1] lesen:

>> 6.36.1 AVR Variable Attributes
>>
>> progmem
>>    The progmem attribute is used on the AVR to place read-only data
>>    in the non-volatile program memory (flash). The progmem attribute
>>    accomplishes this by putting respective variables into a section
>>    whose name starts with .progmem.
>>
>>    This attribute works similar to the section attribute but adds
>>    additional checking.

> Die Version 4.3.4 verarbeitet das Konstrukt aus dem Eingangspost
> richti, aber die Version 4.7.0 liefert einen Fehler.

Die Lösung steht bereits in

Beitrag "Re: avr-gcc stolpert über PROGMEM-Konstrukt"

und das funktioniert zumindest mit:

avr-gcc (GCC) 4.6.2 20110930 (prerelease)
avr-gcc (GCC) 4.7.0 20111006 (experimental)
avr-gcc (GCC) 4.7.0 20111014 (experimental)
avr-gcc (GCC) 4.7.0 20120102 (experimental)
avr-gcc (GCC) 4.7.0 20120217 (experimental)
avr-gcc (GCC) 4.7.1 20120322 (prerelease)
avr-gcc (GCC) 4.7.1 20120606 (prerelease)

Mehr hab ich momentan nicht im Angebot ;-)

> Bug or Feature?

Nach der Doku zu urteilen tippe ich auf Feature.

[1]
http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Variable-Attributes.html#AVR-Variable-Attributes

von Jens Fischer (Gast)


Lesenswert?

Johann L. schrieb:
> Die Lösung steht bereits in
>
> Beitrag "Re: avr-gcc stolpert über PROGMEM-Konstrukt"
Ich habe es gerade noch einmal getestet,
so geht es nicht:
1
const uint8_t EUROPEAN_DATE_NR[] PROGMEM =   { 4, 5, 2, 3, 0, 1 };
2
const uint8_t AMERICAN_DATE_NR[] PROGMEM =   { 4, 5, 0, 1, 2, 3 };
3
const uint8_t CANADIAN_DATE_NR[] PROGMEM =   { 0, 1, 2, 3, 4, 5 }; 
4
5
// Zeile 89:
6
const uint8_t const *DATE_FORMAT_NR[] PROGMEM = {EUROPEAN_DATE_NR, AMERICAN_DATE_NR, CANADIAN_DATE_NR};

Aber so geht es:
1
const uint8_t * const DATE_FORMAT_NR[] PROGMEM = {EUROPEAN_DATE_NR, AMERICAN_DATE_NR, CANADIAN_DATE_NR};

Da muß man erstmal drauf kommen! ^^

Vielen Dank!

Grüße
Jens

von Karl H. (kbuchegg)


Lesenswert?

Jens Fischer schrieb:

> Da muß man erstmal drauf kommen! ^^

Nö.
Da muss man
a) entweder genau lesen (denn genau das war der erste Vorschlag
   der kam)
b) die Regeln für die Platzierung von Modifier kennen

Modifier wirken immer auf das Teil links von ihnen. Es sei denn sie 
stehen bereits ganz links, dann wirken sie auf die Deklaration rechts 
von ihnen.
c) Deklarationen lesen können.


uint8_t const  c;              c ist konstant und ist ein uint8_t
const uint8_t  c;              ist genau dasselbe, da const schon
                               ganz links steht. c ist ein uint8_t und
                               dieser uint8_t ist konstant.

uint8_t const * c;             c ist ein Pointer. Und zwar ein Pointer
                               auf etwas das konstant ist. Und dieses
                               etwas ist ein uint8_t

uint8_t * const c;             c ist konstant. c ist ein konstanter
                               Pointer. Und dieser Pointer zeigt
                               auf einen uint8_t. Dieser uint8_t
                               ist nicht konstant, sondern kann
                               über den Pointer verändert werden.

uint8_t const * const c;       c ist konstant. c ist ein konstanter
                               Pointer. Und dieser Pointer zeigt auf
                               etwas, was selbst wieder konstant ist.
                               Und dieses seinerseits konstante ist
                               ein uint8_t

const uint8_t * const c;       Ist genau dasselbe, wie die Version
                               zuvor.


Immer beim Variablennamen anfangen zu lesen und sich nach aussen 
vortasten. Dann erzählt dir die Deklaration ihre Geschichte. Genau 
genommen müsste man sich nach der Rechts/Links Regel nach aussen 
vortasten, aber das sind hier keine Fälle bei der man sie braucht.

von Oliver (Gast)


Lesenswert?

Jens Fischer schrieb:
> Da muß man erstmal drauf kommen!

Nö. Das steht in der Fehlermeldung.

Jens Fischer schrieb:
> In diesem Fall liegt es aber offenbar am avr-gcc. Die Version 4.3.4
> verarbeitet das Konstrukt aus dem Eingangspost richti, aber die Version
> 4.7.0 liefert einen Fehler.

Das ist so. Früher(tm) ging dss ohne das zusätzliche const, aktuelle 
Versionen sind da empfindlicher.

Das nennt man Fortschritt...

Oliver

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


Lesenswert?

Oliver schrieb:

> Das ist so. Früher(tm) ging dss ohne das zusätzliche const, aktuelle
> Versionen sind da empfindlicher.
>
> Das nennt man Fortschritt...

Nein, Bug.  GCC hätte schon immer darauf bestehen sollen, dass ein
im Flash liegendes Objekt als "const" zu deklarieren ist, denn nur
so hat es Sinn.  Das frühere Verhalten war folglich buggy.

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


Lesenswert?

Karl Heinz Buchegger schrieb:
> c) Deklarationen lesen können.

Machst du da 'n Wiki-Artikel draus?

von Oliver (Gast)


Lesenswert?

Jörg Wunsch schrieb:
>> Das nennt man Fortschritt...
> Nein, Bug.  GCC hätte schon immer darauf bestehen sollen,

Oder so ;)

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> Modifier
  ^^^^^^^^

"Qualifier", dann findet man es auch im Sprachstandard und in der 
GCC-Doku :-)

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:
> Karl Heinz Buchegger schrieb:
>
>> Modifier
>   ^^^^^^^^
>
> "Qualifier", dann findet man es auch im Sprachstandard und in der
> GCC-Doku :-)


Äh, ja. Logisch
Danke für die Korrektur.

von Dosmo (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Nein, Bug.  GCC hätte schon immer darauf bestehen sollen, dass ein
> im Flash liegendes Objekt als "const" zu deklarieren ist, denn nur
> so hat es Sinn.  Das frühere Verhalten war folglich buggy.

Mal eine doofe Frage:
Ich weiß vom C30-Compiler für PIC24 und vom IAR-Compiler für MSP430, daß 
diese "const"-Daten grundsätzlich ins Flash legen.
Warum hat man beim AVR diese PROGMEM-Konstrukt? Daß man beim Lesen aus 
dem Flash aufpassen muß (LPM) ist klar, aber warum legt der GCC für den 
AVR nicht auch einfach alle "const"-Daten ins Flash?

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


Lesenswert?

Dosmo schrieb:
> Warum hat man beim AVR diese PROGMEM-Konstrukt?

Weil die Flash-Daten nicht kompatibel sind mit den C-Standard-
Funktionen, insbesondere denen aus <string.h>.  Wenn du dir
beispielsweise die Funktion strlen() ansiehst:
1
size_t strlen(const char *s);

Woher soll strlen() jetzt wissen, ob es für den Zugriff der Daten
LD/LDS etc. oder LPM benutzen muss?

Man kann sowas implementieren, nennt sich generic pointer und
ist wohl bei manchen MCS51-Compilern üblich gewesen.  Geht dann
aber auf Kosten der Laufzeiteffizienz, denn der tatsächliche
Speicherbereich, in dem die Daten liegen, wurde dann dort mit im
Zeiger codiert.  Beim AVR müsste man dann also 3 Bytes für den
Zeiger benutzen, und zur Laufzeit wird erst entschieden, welche
der möglichen Implementierungen benutzt wird.  Die einzelnen
Implementierungen sind aber stets alle im Binary mit drin, egal,
ob sie später benutzt werden oder nicht.

PIC24 kenne ich nicht.  MSP430 ist meiner Meinung nach keine
Harvard-Maschine, d. h. es gibt für den Zugriff auf SRAM oder Flash
keine unterschiedlichen Befehle.

von Dosmo (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Weil die Flash-Daten nicht kompatibel sind mit den C-Standard-
> Funktionen, insbesondere denen aus <string.h>.  Wenn du dir
> beispielsweise die Funktion strlen() ansiehst:
> size_t strlen(const char *s);
> Woher soll strlen() jetzt wissen, ob es für den Zugriff der Daten
> LD/LDS etc. oder LPM benutzen muss?
Du meinst, es wäre nicht sehr sinnvoll, alle "const"-Daten ins Flash zu 
legen, und dann strlen() ins RAM zugreifen zu lassen.
Da ist die klare Trennung schon konsequenter.

> PIC24 kenne ich nicht.
Dort wird eine bestimmte Page des Flashes in den RAM-Adressbrereich 
eingeblendet. D.h. für die CPU ist das Lesen von "const"-Daten ein 
RAM-Zugriff.
Wenn man außerhalb dieser Page aus dem Flash lesen will, geht es aber 
auch nur mit einem LPM-Äquivalent.

> MSP430 ist meiner Meinung nach keine Harvard-Maschine.
Stimmt, der hat ha nur einen Adressbereich für RAM und Flash.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Oliver schrieb:

> Das ist so. Früher(tm) ging dss ohne das zusätzliche const, aktuelle
> Versionen sind da empfindlicher.
>
> Das nennt man Fortschritt...

Man kann natürlich auch auf der alten Version bleiben. Geht man auf eine 
neue Version, ist es immer eine gute Idee, das Kleingedruckte zu lesen: 
die Release Notes.

Für 4.6 stegt das unter "AVR":

http://gcc.gnu.org/gcc-4.6/changes.html
http://gcc.gnu.org/gcc-4.6/changes.html#avr

Und für 4.7 gibt's noch deutlich mehr:

http://gcc.gnu.org/gcc-4.7/changes.html

Jörg Wunsch schrieb:

> Man kann sowas implementieren, nennt sich generic pointer und
> ist wohl bei manchen MCS51-Compilern üblich gewesen.  Geht dann
> aber auf Kosten der Laufzeiteffizienz, denn der tatsächliche
> Speicherbereich, in dem die Daten liegen, wurde dann dort mit im
> Zeiger codiert.  Beim AVR müsste man dann also 3 Bytes für den
> Zeiger benutzen, und zur Laufzeit wird erst entschieden, welche
> der möglichen Implementierungen benutzt wird.

Das ist dann __memx

http://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html

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.