Forum: Compiler & IDEs Riesiges Array im Flash


von Stefan H. (schnufff)


Lesenswert?

Hi,

für ein Verschlüsselungsverfahren muss ich riesige Matrizen speichern.
Wenn ich versuche sie per:
1
const uint8_t matrix[] PROGMEM={0x11,0x22,...,0x11};
in den Flash legenzu lassen, bekomme ich immer den Fehler:
"Größe der Variablen "matrix" ist zu hoch."
Ich benutze einen atmega128 und die matrix ist 81Kbyte( ja 81*1024 
bytes)
gross, sollte also in den Flash passen.

G++gle und Forumssuche hat leider nicht erbracht.
Was läuft da schief?

Danke
Stefan

von ich (Gast)


Lesenswert?

wahrscheinlich is das einfach zu groß;)

versuch doch mal die große matrix in 4 kleine aufzuteilen etc?!

von Oliver (Gast)


Lesenswert?

Der avr-gcc adressiert Arrays über 16-bit-Pointer, damit ist die Größe 
auf 32kB begrenzt. Warum es nicht 64kB sind, wurde hier mal diskutiert, 
wenn du das genauer wissen willst, musst du mal danach suchen. 81kB 
funktiert auf keinen Fall.

Oliver

von Stefan H. (schnufff)


Lesenswert?

Ah, das erklärt es natürlich.
Aber wenn ich die Matrix in ein 2-dimensionales Array mit 81 Zeilen und 
1024 Spalten zerlege, dann sollte das funktionieren, oder? Jeder Pointer 
für sich muss dann ja weniger als 32kbyte "überstreichen" können.

Danke für die schnelle Antwort
Stefan

von Falk B. (falk)


Lesenswert?

@ Stefan H. (Firma privat) (schnufff)

>Aber wenn ich die Matrix in ein 2-dimensionales Array mit 81 Zeilen und
>1024 Spalten zerlege, dann sollte das funktionieren, oder?

NEIN! Weil das auch als zusammenhängendes Objekt verwaltet wird. Du 
braucht getrennte Arrays kleiner gleich 32kB.

MfG
Falk

von Stefan H. (schnufff)


Lesenswert?

Ok, die einzelnen Arrays müssen kleiner 32Kbyte sein.
Wenn ich die Zeilen der Matrix als 1024 Arrays speichere:
1
const uint8_t Matrix0[] PROGMEM={0x01,...};
2
...
3
const uint8_t Matrix1023[] PROGMEM={0x01,...};

wobei jede Zeile 81 Bytes enthält und dann die Zeilen in einem
Array von Pointern auf Arrays verwalte:
1
const uint16_t* Matrix[] PROGMEM={Matrix0,..Matrix1023[]};

Komme ich dann mit:
1
myByte=pgm_read_byte_far((pgm_read_byte_far(&Matrix[Zeile])[Spalte]);
an das Element in Zeile,Spalte?

Nur zur Info: Ich habe mehrere Matrizen die in Summe knapp 200Kbyte 
gross sind und 3 davon sind grösser als 64K.Alle Matrizen sind statisch 
und werden nur gelesen. Ich weiss das ich besser auf einen kleinen ARM7 
umsteigen kann, aber das ist nunmal nicht die Zielplattform.

danke
Stefan

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


Lesenswert?

Du kannst das Array ja auch mit Assembler füllen und in C nur einen
Zeiger darauf verwalten.  Arrayzugriffe erfolgen in C per definitionem
(gemäß dem Standard) immer mit dem Typ "signed int", daher sind
diese hier ungeeignet.

von Stefan H. (schnufff)


Lesenswert?

Danke Jörg, aber die Matrizen werden auf einem PC mit einem 
CoputeralgebraProgramm vorberechnet und dann etwas formatiert und in 
meiner config.c eingebunden. Sie sind also zur kompletten Laufzeit des 
Programms fest.

Ich hatte mittlerweile versucht das Beispiel aus dem libc manual 
nachzubauen:
1
char string_1[] PROGMEM = "String 1";
2
char string_2[] PROGMEM = "String 2";
3
char string_3[] PROGMEM = "String 3";
4
char string_4[] PROGMEM = "String 4";
5
char string_5[] PROGMEM = "String 5";
Then use the new symbols in your table, like so:
1
PGM_P string_table[] PROGMEM = 
2
{
3
    string_1,
4
    string_2,
5
    string_3,
6
    string_4,
7
    string_5
8
};
Auslesen wollte ich dann per:
1
myChar=pgm_read_byte_far((PGM_P)pgm_read_word(&(string_table[i]))[j]);

Allerdings meckert er jetzt" pointer targets in init differ in 
signedness".
Selbst wenn der pointer "signed int" ist, muss er doch immer noch 
+32Kbyte adressieren können.Also sollte das für eine Zeile der MAtrix 
reichen, oder hab ich was flasch verstanden?

Gruss
Stefan

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


Lesenswert?

Stefan H. wrote:

> Danke Jörg, aber die Matrizen werden auf einem PC mit einem
> CoputeralgebraProgramm vorberechnet und dann etwas formatiert und in
> meiner config.c eingebunden. Sie sind also zur kompletten Laufzeit des
> Programms fest.

Ja, eben, daher kannst du sie ja genauso gut durch eine Folge von
.byte-Anweisungen in einer Assemblerdatei anlegen lassen.

von Stefan H. (schnufff)


Lesenswert?

Ok, asm für avr hab ich schon programiert, aber noch nie c und asm 
gemischt.
Ich hab grad 2 probleme. wie binde ich die *.asm datei ein und wie 
greife ich dann über C auf die bytes/words zu?

von Stefan H. (schnufff)


Lesenswert?

So, ich hab jetzt alle Array in Stücke kleiner 32K aufgeteilt und will 
mit pgm_read_byte/word_far drauf zugreifen.
Definiert sind die Array wie gehabt per
1
const uint8_t Matrix0[] PROGMEM={0x01,...};

aber keine der heir gefunden Möglichkeiten auf die hexwerte in 
Matrix[i]zuzugreifen scheint richtig zu funktionierten:
1
myByte=pgm_read_byte_far(FAR(Matrix[i]);
2
myByte=pgm_read_byte_far(FAR(Matrix+i));
3
myByte=pgm_read_byte_far(FAR(Matrix)+i));
liefern entweder nur 00 oder FF.

Bin ich denn so blöd?
Ich will einfach nur konsistent auf den kompletten flash eines Mega256 
zugreifen können und mir auf ewig konstante bytes/words auslesen können.

Gruss
Stefan

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


Lesenswert?

Stefan H. wrote:

> Ich hab grad 2 probleme. wie binde ich die *.asm datei ein und wie
> greife ich dann über C auf die bytes/words zu?

Die Assemblerdatei benennst du mit einer auf .S (großes S) versehenen
Endung und gibst sie einfach dem Compiler mit.  Der kann damit dann
was anfangen.

In der Assemblerdatei musst du die extern bekannt zu machenden Symbole
mit .global deklarieren:
1
.section .progmem.data,"a",@progbits
2
.global bigarray
3
bigarray:
4
    .byte 42, 23, 1, 5, 18

In der C-Datei deklarierst du dies einfach als externes Array:
1
extern uint8_t bigarray[];

*Aber*:

Du kannst nicht mit normalen Array- oder Zeigerzugriffen darauf
zugreifen, denn dann stolperst du ja sofort wieder darüber, dass
ein Zeiger im AVR-GCC nur 16 bits groß ist und dass der Arrayindex
per definitionem vom Typ signed int ist.  Du musst stattdessen per
Typecast vom Arraynamen einen uint32_t-Wert bilden und den an Stelle
eines Zeigers benutzen.  Die ganze Adressrechnung, die dir sonst
der Compiler abnimmt, musst du also zu Fuß erledigen.  Die sich
ergebende uint32_t-Zahl kannst du dann als Adresse an ein
pgm_read_byte_far() übergeben.

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.