Forum: Compiler & IDEs __flash und memcpy


von Pupsomat (Gast)


Lesenswert?

Hallo,

ich habe ein Array im Flash (mit const __flash), das zur Laufzeit 
umkopiert wird. Die Zeile mit memcpy bringt mir eine Warnung:

"warning: incompatible implicit declaration of built-in function 
'memcpy'"

Wenn ich statt dessen versuche, memcpy_P zu verwenden, kommt diese 
Warnung:

"warning: conversion from address space '__flash' to address space 
'generic'"

Wie macht man es richtig??

Compiler: avr-gcc

von Sina A. (sinapse)


Lesenswert?

Pupsomat schrieb:
> Wie macht man es richtig??

gar nicht würd ich sagen... __flash ist eine C Erweiterung, die memcpy 
aus der standardlib nicht kennt. du musst das also "manuell" kopieren 
oder deine eigene memcpy funktion schreiben.

lg

von Jim M. (turboj)


Lesenswert?

Pupsomat schrieb:
> "warning: incompatible implicit declaration of built-in function
> 'memcpy'"
1
#include <string.h>
fehlt. In diese Header wird memcpy() deklariert.

von Le X. (lex_91)


Lesenswert?

Ich denk mal deine Daten liegen global irgendwo rum:
1
uint8_t __flash myData[100];

Der memcpy-Prototyp meiner gcc Installation hier sieht so aus:
1
_CRTIMP void* __cdecl __MINGW_NOTHROW   memcpy (void*, const void*, size_t);
Das ist zwar vom mingw, bei der avr-libc dürfts aber auch ned recht viel 
anders aussehen.

Da der Speicherbereich (Flash, RAM, EEPROM) nicht aus dem Zeiger 
ersichtlich ist sondern über das __flash Attribute mitgeschleppt wird 
geht diese Information also beim Aufruf von memcpy verloren.
Dadurch kopierst du irgendwelche Daten, die im RAM zufällig da liegen.
Der Tipp meines Vorposters mit der <string.h> ist falsch.

Du wirst deine eigene memcpy schreiben müssen.
Die muss einen Zeiger auf __flash Daten und einen RAM Zeiger erwarten.

Ohne Gewähr denke ich der Prototyp muss so aussehen:
1
void* memcpy (void* __flash, const void*, size_t);

(oder so ähnlich)

von Pupsomat (Gast)


Lesenswert?

Das ist schlecht. Ich hatte vor, alle Deklarationen mit PROGMEM im Code 
durch welche mit __flash zu ersetzen, um einheitliche Aufrufe verwenden 
zu können (wie beworben).
Schade. Jetzt greife ich zumindest in diesem Fall nochmal auf PROGMEM 
zurück.

von Karl H. (kbuchegg)


Lesenswert?

Pupsomat schrieb:
> Das ist schlecht. Ich hatte vor, alle Deklarationen mit PROGMEM im Code
> durch welche mit __flash zu ersetzen, um einheitliche Aufrufe verwenden
> zu können (wie beworben).
> Schade. Jetzt greife ich zumindest in diesem Fall nochmal auf PROGMEM
> zurück.

Na ja. Was hindet dich jetzt, tatsächlich ein memcpy dafür zu schreiben?
Ist ja nichts anderes als eine entsprechende Schleife. Was anderes macht 
ja das normale memcpy auch nicht und auf einem AVR geht auch nichts 
anderes, was Speed-Vorteile bringen würde.

> Ohne Gewähr denke ich der Prototyp muss so aussehen:
>
> void* memcpy (void* __flash, const void*, size_t);
> (oder so ähnlich)

vor allen Dingen: so ähnlich
1
void* memcpy (void* dst, __flash void* src, size_t size);

Die Reihenfolge der Argumente bei memcpy und strcpy lehnt sich an die 
'Reihenfolge' in
1
  dst = src
an. Liest man das als einer, der mit einer westlichen Sprache 
aufgewachsen ist, dann stöesst man beim Lesen von links nach rechts 
zuerst auf die Angabe vom Ziel ('Destination') und dann auf die von der 
Quelle ('Source'). Nicht anders ist das in der Argumentliste von memcpy 
und strcpy.

: Bearbeitet durch User
von Pupsomat (Gast)


Lesenswert?

Najaaa, ursprünglich war der Einsatz von memcpy (_P in diesem Fall) eine 
Optimierungslösung, weil die Schleifenversion zu viele Bytes gefressen 
hat. Ich bin auch ganz zufrieden damit und dachte jetzt, es gibt auch 
eine __flash-Alternative.

von Karl H. (kbuchegg)


Lesenswert?

Pupsomat schrieb:
> Najaaa, ursprünglich war der Einsatz von memcpy (_P in diesem Fall) eine
> Optimierungslösung, weil die Schleifenversion zu viele Bytes gefressen
> hat.

?
Fakt ist, dass ein AVR auf den Flash nur Byteweise zugreifen kann. Egal 
wie, egal wann, egal wer optimiert. An
1
  for( i = 0; i < size; i++ )
2
    *dst++ = *src++;
führt auf einem AVR kein Weg vorbei.

Das war keine Optimierungslösung, sondern ein Ersatz für pgm_readBlock, 
welches es so in pgmspace.h nicht gibt, weil es ja bereits die 
entsprechenden memcpy Funktionen gibt und memcpy zu einer 
standardkonformen Implementierung der C-Standardfunktionen dazugehört.

> Ich bin auch ganz zufrieden damit und dachte jetzt, es gibt auch
> eine __flash-Alternative.

Du kannst dir ja eine schreiben. Deswegen musst du noch lange nicht auf 
die PROGMEM Variante zurückfallen, nur weil es keine memcpy Variante 
gibt, die einen __flash Pointer nimmt (was ich nich überprüfen kann, 
weil meine Compilerversion zu alt ist und noch kein __flash kennt. Und 
nein, ich update deswegen meinen Compiler nicht)
1
void memcpy (void* dst, __flash void* src, size_t size)
2
{
3
  uint8_t* dstBytes = (uint8_t*) dst;
4
  __flash uint8_t* srcBytes = (__flash uint8_t*) src;
5
  size_t   i;
6
7
  for( i = 0; i < size; i++ )
8
    *dstBytes++ = *srcBytes++;
9
}
(wenn sicher ist, dass nie mehr als 255 Bytes involviert sind, könnte 
man auch size_t spritzen und durch eine Variante mit einem uint8_t als 
Längenangabe gehen.

: Bearbeitet durch User
von Pupsomat (Gast)


Lesenswert?

1
void memcpy_F(void* dst, const __flash void* src, uint16_t size) {
2
  uint16_t i;
3
  for (i = 0; i < size; i++) *(uint8_t *)dst++ = *(const __flash uint8_t *)src++;
4
}

ist jetzt tatsächlich kleiner als die PROGMEM-Lösung.

von Peter II (Gast)


Lesenswert?

Pupsomat schrieb:
> void memcpy_F(void* dst, const __flash void* src, uint16_t size) {
>   uint16_t i;
>   for (i = 0; i < size; i++) *(uint8_t *)dst++ = *(const __flash uint8_t
> *)src++;
> }

wozu die extra variable?
1
void memcpy_F(void* dst, const __flash void* src, uint16_t size) {
2
   while( --size )
3
      *(uint8_t *)dst++ = *(const __flash uint8_t*)src++;

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Pupsomat schrieb:
>> void memcpy_F(void* dst, const __flash void* src, uint16_t size) {
>>   uint16_t i;
>>   for (i = 0; i < size; i++) *(uint8_t *)dst++ = *(const __flash uint8_t
>> *)src++;
>> }
>
> wozu die extra variable?
>
1
> void memcpy_F(void* dst, const __flash void* src, uint16_t size) {
2
>    while( --size )
3
>       *(uint8_t *)dst++ = *(const __flash uint8_t*)src++;
4
>

mach ein
while (size--)
draus

> wozu die extra variable?
Müsste man sich ansehen, ob die nicht sowieso vom Compiler wegoptimiert 
wird.

: Bearbeitet durch User
von Pupsomat (Gast)


Lesenswert?

Ohne i wird es 8 Bytes größer. Wahrscheinlich werden da Register hin- 
und herkopiert. Übergaberegister != Arbeitsregister oder sowas. Bin 
jetzt zu faul, mir das .lss anzugucken.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Pupsomat schrieb:
> Ohne i wird es 8 Bytes größer.

Kann ich nicht nachvollziehen. In beiden Fällen ist das Binary exakt 
gleich groß.

Variante 1:
1
void memcpy_F(void* dst, const __flash void* src, uint16_t size)
2
{
3
    uint16_t i;
4
5
    for (i = 0; i < size; i++)
6
    {
7
        *(uint8_t *) dst++ = *(const __flash uint8_t *) src++;
8
    }
9
}

Variante 2:
1
void memcpy_F(void* dst, const __flash void* src, uint16_t size)
2
{
3
    while (size--)
4
    {
5
        *(uint8_t *) dst++ = *(const __flash uint8_t *) src++;
6
    }
7
}

Compiler avr-gcc 4.7.2
Optimierung: -Os

: Bearbeitet durch Moderator
von Pupsomat (Gast)


Lesenswert?

4.8.1

Schalter u.a. -flto

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Pupsomat schrieb:
> Schalter u.a. -flto

auch bei -flto sind beide gleich groß.

Voraussetzung:

Wenn -flto als Compiler-Flag, dann -flto auch als Linker-Flag und 
zusätzlich dann auch noch -Os als Linker-Flag.

(Warum man -Os dann auch beim Linken angeben muss, wenn man FLTO nutzt, 
wurde hier bereits öfter breitgetreten.)

EDIT:

Gerade auch noch mal mit 4.8.1 getestet: Gleiche Codegröße, kein 
Unterschied. Hätte mich auch echt gewundert. Der gcc ist nicht blöd.

: Bearbeitet durch Moderator
von Pupsomat (Gast)


Lesenswert?

Das wird kontextabhängig sein. Da gcc, wie du sagst, nicht blöd ist, 
nutzt er Seiteneffekte. Ich habe mal nachgesehen. Weiter unten im Code 
brauche ich den Wert von "size" nochmal. Bei der for-Variante ist der 
Wert noch irgendwo in Registern vorhanden. Bei der while-Variante 
(welche runterzählt) nicht mehr ohne Weiteres.
Jedenfalls ist das jetzt meine Erklärung...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falls die Daten als Objekte organisiert sind, kann man einfach ohne 
memcpy bzw. mit implizitem memcpy kopieren:
1
typedef struct
2
{
3
    int a, b;
4
} ab_t;
5
6
const __flash ab_t ab[] = { {1, 2 }, { 3, 4 } };
7
8
void copy (ab_t *ram, const __flash ab_t *flash)
9
{
10
    ram[3] = *flash;
11
    *ram = ab[3];
12
}

: Bearbeitet durch User
von Pupsomat (Gast)


Lesenswert?

Mh, ja, theoretisch schon. Aber per direkter Zuweisung war der 
Speicherverbrauch auch größer.

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.