Forum: Projekte & Code [avr-gcc]. post-increment Version von avr/pgmspace.h


von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hi, für manche Projekte kann folgender Header praktisch sein, wenn man 
mehrere aufeinander folgende Daten mit den pgm_read_xxx-Makros aus 
avr/pgmspace.h aus dem AVR-Flash lesen will.

Weil avr-gcc keine Memory-Qualifier wie "flash" etc. kennt, müssen die 
pgm_read-Sachen als Inline-Assembler umgesetzt werden.

Nach den Aufruf weiß der Compiler daher nicht mehr, welchen Wert das 
Z-Register (über das der Flash ausgelesen wird) hat und kann es daher 
nicht wiederverwenden.

Bei platz- und zeitkritischen Sachen kann es daher sinnvoll sein, die 
Makros aus dem beigefügten Header einzusetzen.

Entsprechen die pgm_read_xxx(addr)  Makros einem *addr, so entsprechen 
die pgm_read_xxx_inc(addr) Makros einem *addr++, wobei addr als 
Byte-Zeiger angesehen wird, wenn ein Byte gelesen wird, als Word-Zeiger 
bei einem Word, etc..

addr muss natürlich ein L-Value sein und darf keine Nebeneffekte haben.
addr zeigt nach dem Aufruf um so viele Bytes weiter, wie gelesen wurden.

Beispiel:
1
#include "pgmread-inc.h"
2
3
uint32_t wert32;
4
uint16_t wert16;
5
uint8_t  wert8;
6
7
void lese_3_bytes (const void* addr)
8
{
9
    wert8  = pgm_read_byte_inc (addr);
10
    wert16 = pgm_read_word_inc (addr);
11
    wert32 = pgm_read_dword_inc (addr);
12
}
13
14
// Mit den vertrauten Makros:
15
void lese_3_bytes (const uint8_t * addr)
16
{
17
    wert8  = pgm_read_byte (addr++);
18
    wert16 = pgm_read_word (addr);
19
    addr += 2;
20
    wert32 = pgm_read_dword (addr);
21
}

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


Lesenswert?

Wäre schön, wenn du das als Patch zur avr-libc einreichen könntest,
vorzugsweise einschließlich einer Erweiterung der Dokumentation
(wobei es bei avr-libc für die ganze pgmspace.h-Geschichte außer
avr/pgmspace.h noch eine separate Doku gibt).

https://savannah.nongnu.org/patch/?func=additem&group=avr-libc

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bei mir macht die Verwendung dieser Makros den Code zwar meistens fixer 
und kleiner, das ist aber nicht immer der Fall.

Es war eher als Hack gedacht wenn jemand Zeit- und/oder Platzprobleme 
hat.

Es ist etwas heimtückisch, weil es nicht, macht was man eigentlich 
denkt:

Das pgm_read_byte_inc macht zum Beispiel nicht
1
   (*(p)++)

sondern
1
   (*((const uint8_t*) p)++)

d.h. es erhöht p nicht wie einen Zeiger um 1, sondern wie einen int um 
1. Analog für die anderen _inc Makros.

Ausserdem darf p keine Nebeneffekte haben, wie
1
 *((++p,p))++

Immerhin gibt das ne Warnung.

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Eigentlich würde mir so ne Lösung wie im Anhang ja besser gefallen...

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

...für simple Beispiele sieht das auch Klasse aus.

Aber schon für
1
void  u4 (char *u, unsigned char * q)
2
{
3
    *u++ = __builtin_pgm_read_byte (q++);
4
    *u++ = __builtin_pgm_read_byte (q++);
5
    *u++ = __builtin_pgm_read_byte (q++);
6
    *u++ = __builtin_pgm_read_byte (q++);
7
}

ist der Code echt unbrauchbar.

von Simon K. (simon) Benutzerseite


Lesenswert?

Ich versteh noch nicht so ganz wo du drauf hinaus willst.

Du kannst doch auch
1
void u4 (char *u, unsigned char *q)
2
{
3
    memcpy_P(u, q, 4);
4
}
oder
1
void u4 (char *u, unsigned char *q)
2
{
3
    *u = pgm_read_dword(q);
4
}
benutzen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Simon K. wrote:
> Ich versteh noch nicht so ganz wo du drauf hinaus willst.
>
> Du kannst doch auch
> [...]
> benutzen.

Natürlich kann ich das. Ich weiß, wie ich als Anwender guten Code aus 
gcc + Inline asm rausbekomme und kenne die DOs und DONTs.

Momentan sehe ich das Thema aber aus der Sicht des Compilers und da 
stellt sich die Frage: Wie kann man sowas effektiv umsetzen?

Aus Sicht des Compilers ist die libc ja auch "nur" ein Anwender, und die 
anvisierten Builtins würden so benutzt werden wie die Inline-asm Makros 
aus der libc. Für den Anwender würde sich also nichts ändern.

Der Vorteil eines Builtins ist, daß man dem Compiler auf algebraischer 
Ebene sagen kann, was im Code geschieht. Ein asm hingegen ist eine 
BlackBox die dem Compiler bis auf deren Wirkung auf die Welt 
verschlossen ist. Was im asm selbst passiert, kann er nicht wissen und 
von daher auch nicht optimieren oder gewinnbringend verwenden.

Aus Sicht einer Optimierung interessant ist die Möglichkeit des 
post-increment und die Weiterverwendung von Z. Das wird zwar auch durch 
meine obigen Makros erreicht, aber

-- der Code wird dadurch nicht hübscher
-- man muss sich von hand drum kümmern wann man das einsetzt oder nicht
-- gcc weiß immer noch nicht was abgeht

Bei mehreren zu lesenden Daten erfordert das pgm_read_dword die 
Einführung einer Union; krumme Anzahlen sind nicht effektiv behandelbar.

Nehmen wir mal an, du willst 4 aufeinander folgende Bytes lesen ohne die 
Sachen in ne Union (in ner Union müssen die Sachen zudem genauso stehen 
wie im Flash) zu packen:
1
x = pgm_read_byte (p++);
2
y = pgm_read_byte (p++);
3
k = pgm_read_byte (p++);
4
v = pgm_read_byte (p++);

Und freu dich an dem Code, den avr-gcc daraus macht.

Der Königsweg neuer (Target-abhängiger) Qualifier wie "flash" oder 
"pgmspace" wird gcc -- wenn das überhaupt jemals unterstützt wird -- 
nicht in absehbarer Zeit liefern.

Damit gingen dann Sachen wie
1
char foo (const pgmspace char * p)
2
{
3
    return *p; // Kein pgm_read-Zeugs mehr
4
}
5
6
const pgmspace mytype_t ding = 
7
{
8
    char c;
9
    const pgmspace char * string;
10
};
11
12
char bar (const pgmspace mytype_t * p)
13
{
14
    return p->c + p->string[4];
15
}

und gcc könnte die passenden Dereferenzierungen erzeugen. Zurzeit gibt 
es nur ein Attribut, keinen Qualifier.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johann L. wrote:
> Ausserdem darf p keine Nebeneffekte haben, wie
>
>
1
>  *((++p,p))++
2
>

Ok, das kann man fixen mit "+z" anstatt "=z" + "1"

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.