Forum: Compiler & IDEs Arrays können max 32768 Byte groß sein. Warum nicht 65534?


von Peek&Poke (Gast)


Lesenswert?

Hi,
Vermutung: eine Flash Konstante (also z.B. eine abgelegte Bitmap Grafik) 
darf nicht größer also 32.768 Byte sein. Ist dies korrekt?

Weil (Config: WinAVR, Atmega128):

const prog_uint16_t Grafik[16384]  ={
0x0000, 0x0000, 0x0000,..... (hier sind dann 16384 Worte abgelegt: 16384 
x 2 = 32768 Byte)

ergibt einen Fehler "error: size of variable 'Grafik' is too large"

Ein Byte weniger:

const prog_uint16_t Grafik[16383]  ={
0x0000, 0x0000, 0x0000,.....

ist aber OK und funzt auch einwandfrei.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Speicherzugriffe 
ist keine Hilfe. Mit "Static" habe ich vorsichthalber auch ausprobiert: 
keine Änderung.


Jetzt stellen sich mir 2 Fragen:
a) gibt es einen Workaround (außer die Daten auf mehrere Arrays 
aufzuteilen, was ich unbedingt vermeiden möchte)?
b) Warum ist dies so? Vielleicht weil der Pointer intern mit als signed 
integer behandelt wird. Wenn ja: was für ein Blödsinn, wofür braucht man 
negative Zahlen bei einem Pointer. Wenn nein, was sonst?

Also: b) wäre nett zu wissen, aber eine Antwort auf a) wäre mit vieeeel 
lieber.

In Hoffnung auf Hilfe sage ich schonmal Danke!!!

von Hagen R. (hagen)


Lesenswert?

Selbes Problem habe ich auch. Ich nehme 2 Arrays nacheinander und hoffe 
darauf das AVR-GCC sie mir nicht zerpflückt. Bisher hat das immer 
funktioniert.

Worans liegt ? Wird wohl schon so sein das man intern mit signed 16 Bit 
arbeitet. Und die Programmierer dachten sich wohl das es niemals 32K 
große Daten im FLASH gibt weil es niemals 32K FLASHs geben kann, also 
ohne Weitblick drauflos.

Eine Lösung/Erklärung habe ich bisher nicht gefunden.

Gruß Hagen

von Peter D. (peda)


Lesenswert?

Hagen Re wrote:

> Eine Lösung/Erklärung habe ich bisher nicht gefunden.

Ganz einfach, LPM arbeitet mit 2 Byte-Adressen und damit lassen sich nur 
65536 Byte (= 32768 Worte) adressieren.

Darüber braucht man 3 Byte Pointer und ELPM.


Ups sehe gerade, es sind ja nur 16384 Worte, dann sollte es eigentlich 
passen.


Peter

von A.K. (Gast)


Lesenswert?

Das erklärt aber nicht, warum es bereits ab 32768 Bytes nicht klappt.

Eine Grenze von 65536 wäre klar. Zwar nicht wegen LPM (damit wären erst 
65537 Bytes ein Problem), sondern weil bei einem Array der Grösse N die 
Adresse &a[N] noch definiert und grösser als &a[N-1] sein muss.

von Ingo E. (ogni42)


Lesenswert?

Aus der InstructionSet Manual:


Loads one byte pointed to by the Z-register into the destination 
register Rd. This instruction features a 100% space effective
constant initialization or constant data fetch. The Program memory is 
organized in 16-bit words while the Z-pointer is a
byte address. Thus, the least significant bit of the Z-pointer selects 
either low byte (ZLSB = 0) or high byte (ZLSB = 1). This
instruction can address the first 64K bytes (32K words) of Program 
memory.

von A.K. (Gast)


Lesenswert?

@Ingo: und was sagt uns das nun bei einer Variablen von 32768 Bytes?

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Meine Vermutung:

Der Array-Index darf maximal INT_MAX (== 32767) betragen; das durch 2 
geteilt gibt 16383,5 (als Integer also 16383)...

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


Lesenswert?

Ich glaube, es hängt damit zusammen, dass die Indizierung eines Arrays
entsprechend dem C-Standard implizit vom Typ "int" ist und damit
vorzeichenbehaftet.  Ich müsste aber nachlesen, wo das genau im
Standard steht.

von A.K. (Gast)


Lesenswert?

Ich denke auch dass es dort irgendwo zu suchen ist. Nur betrifft das 
Problem ja alle 16bit Maschinen, und dass PCs anno 80er und die PDP-11 
auf der C entstand auch dahingehend beschrnäkt gewesen seien, daran kann 
ich mich nicht erinnern.

von Stefan (Gast)


Lesenswert?

Ich kann's mir nur so erklären:

1/ Wie Peter schreibt geht der ROM-Zugriff über LPM. Damit kann man über 
Z grundsätzlich 64 KB Bytes adressieren (0 - 0xFFFF).

2/ Bei der Definition von Arrays gibt man eine nichtleere Anzahl von 
Elementen N an (C-Standard), d.h. unsigned.

3/ Beim Zugriff auf ein Array kann der Index allerdings signed sein. 
a[10] ist ebenso erlaubt wie a[-10].

Bei der Codeerzeugung müssen alle drei Constraints gleichzeitig 
eingehalten werden.

Ich vermute:

Wegen 3/ wird &a[0] nicht auf Z=0 gelegt sondern auf Z=0xFFFF/2. Dann 
ist nach 2/ N max. bei Bytezugriff 0xFFFF/2 und bei Wordzugriff 
0xFFFF/2/2

von Peek&Poke (Gast)


Lesenswert?

@Hagen:
Du sagst :"ich nehme 2 Arrays nacheinander und hoffe
darauf das AVR-GCC sie mir nicht zerpflückt."

Warum sollte GCC die nicht zerpflücken? Sie werden doch nicht 
zwangsläufig hintereinandergelegt.
Ich habe es gerade mal ausprobiert:

Wenn ich zwei Blöcke anlege (direkt hintereinander)z.B.
const prog_uint16_t Test1[12000]  = {
0x0000, 0x0000 .........
}
const prog_uint16_t Test2[12000]  = {
0x0000, 0x0000 .........
}
anlege, und dann Test1 (mit pgm_read_word) auslese, dann sind bis zu 
Wort 12000 alle Daten OK. Lese ich weiter, kommt lange nur Unsinn und 
dann, viel später erst, fangen die Daten von Test2 an - dazwischen 
irgendwas anderes. Legst du die Daten vielleicht anders ab? Oder gibt es 
eine spezielle Option?

sonst irgendein Tipp von jemanden, wie man mehr als 32768 Byte am Stück 
ablegen (meinetwegen auch 2 kleinere Arrays) und dann alles auf einmal 
am Stück auslesen kann?

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

Reserviere einfach einen Speicherbereich deiner Wahl und definiere 
eigene Pointer bastele ne Struktur und verwalte sie unabängig von der 
stdlib.lib. Schon bist die Sorgen los. Irgend wer schrieb Pointer vom 
Typ Int. Das dürfte es treffen. Und auf DOS-PCs war es genauso. 
Quickbasic hatte die gleiche Schranke und auch PDS Basic ich meine auch 
MS C.

von A.K. (Gast)


Lesenswert?

Der Compiler liegt richtig.

Die Zielmaschine muss bei einem Array mit N Elementen sowohl a[N]-a[0] 
als auch a[0]-a[N] berechnen können. Und zwar mit dem Typ "ptrdiff_t", 
d.h. mit "int". Da sie zuerst die Differenz ausrechnet, dann durch die 
Grösse eines Elementes dividiert, muss das Zwischenergebnis immer im 
Bereich -32768..+32767 liegen. Folge: Arrays können maximal 32767 Bytes 
lang sein, unabhängig vom Datentyp.

Abhilfe:

uint16_t a1[4096];
uint16_t a2[4096];
uint16_t a3[4096];
uint16_t a4[4096];
uint16_t *a[4] = { a1, a2, a3, a4 };

#define A(n) a[(n)/4096U][(n)%4096U];

Nicht schön, sollte aber funktionieren.

von A.K. (Gast)


Lesenswert?

> ... a[N]-a[0] als auch a[0]-a[N] ...

... &a[N]-&a[0] als auch &a[0]-&a[N] ...

von Ingo E. (ogni42)


Lesenswert?

Das geht schief:
1
#include <inttypes.h>
2
#include <avr/pgmspace.h>
3
4
const prog_uint16_t Grafik[16384] = {[16383] = 0};
5
6
int main( void )
7
{
8
  return 1;
9
}

Das funktioniert:
1
#include <inttypes.h>
2
#include <avr/pgmspace.h>
3
4
const prog_uint8_t Grafik[16384] = {[16383] = 0};
5
6
int main( void )
7
{
8
  return 1;
9
}

Es liegt also nicht an der puren Größe des Feldes, sondern auch am 
Datentyp, da die Adressrechnung ja &a[0]-&a[N] intern als &a - 
&a+N*sizeof(T) durchgeführt wird. Eigentlich sollte das beim AVR Flash 
speicher auch mit uint16_t funktionieren, da der ja 16 Bit breit ist, 
tut es aber nicht. Warum? Da bin ich überfragt.

BTW, das zweite Codesegment scheitert bei 32768.

von A.K. (Gast)


Lesenswert?

> da die Adressrechnung ja intern als &a - &a+N*sizeof(T)
> durchgeführt wird

Kaum. Jedenfalls wenn die Adressen nicht konstant sind - das war nicht 
ganz so wörtlich gemein. Ausführlicher:
   uint16_t *p1 = ?; // Wert: &a[0], dem Compiler nicht bekannt
   uint16_t *p2 = ?; // Wert: &a[N], dem Compiler nicht bekannt
Wenn nun
   p2 - p1
berechnet wird, dann wird erst subtrahiert, dann dividiert. Das 
Zwischenergebnis muss in ptrdiff_t passen.

Im übrigen ist in deinem Beispiel
   prog_uint8_t Grafik[16384]
die Gesamtgrösse trivialerweise < 32768 und somit problemlos. Wie ich 
oben schon schrieb, geht es um die Anzahl Bytes, nicht die Anzahl 
Elemente.

von Ingo E. (ogni42)


Lesenswert?

Hmm. Dass das im zweiten Beispiel problemlos ist, war ja gerade mein 
Punkt: Die Größe des Feldelementes spielt eben bei der Adressierung von 
Feldern eine Rolle. für a[n] muss eben erst einmal der Pointerwert 
(signed, da n auch <0 sein kann) bestimmt werden.

Für Dein Beispiel p2-p1 ist die Größe des Datenelementes ja irrelevant.

Aber ich fürchte wir kommen um das Lesen des Standards, wie Jörg 
vorgeschlagen hat, nicht herum.

von Ingo E. (ogni42)


Lesenswert?

Abschnitt 6.5.2.1 und 6.5.6 im Standard. Es wird mit der Größe des Typs 
multipliziert, wobei das Ergebnis in einen signed integer passen muss

von Ingo E. (ogni42)


Lesenswert?

Nach lesen und verstehen von A.K.s posting habe ich (=Dummbatz) 
festgestellt, dass wir dasselbe gemeint haben, nur mit anderen Worten.

von pumpkin (Gast)


Lesenswert?

hallo jungs,

ich verstehe den zugriff von A.K. nicht. ich habe es mir so gedacht (in 
anlehnung an A.K.'s beispiel):



const unsigned char sinus_1[20480] PROGMEM = {blabla};
const unsigned char sinus_2[20480] PROGMEM = {blublu};

const unsigned char *sinus[2] = {sinus_1, sinus_2};

#define SINUS(n) sinus[(n)/20480U][(n)%20480U]

zugriff:

PORTA = pgm_read_byte(SINUS(ptr_sinus));

ich weiss nicht ob es überhaupt so funktionieren kann. zudem ist mir das 
#define etwas schleierhaft, wie funktioniert das (makro-mäßig?)? 
gänzlich unklar ist mir der zugriff über pgm_read_byte (wegen dem 
'makro') - der compiler schluckt es, aber rauskommen tut nur unsinn 
(bzw. garnix).

kann mir das bitte jemand erklären oder korrigieren?

grüße | pumpkin

von pumpkin (Gast)


Lesenswert?

nachtrag:

ptr_sinus ist ein unsigned integer.

pumpkin

von A.K. (Gast)


Lesenswert?

pgm_read_byte will die Adresse:
  PORTA = pgm_read_byte(&SINUS(ptr_sinus));

Besser wär's aber, du verwendest eine Zweierpotenz statt 20480. 
Dividiert sich entschieden leichter.

von pumpkin (Gast)


Lesenswert?

moin A.K.,

hab ich versucht, hat gerade auch funktioniert. dann habe ich deinen tip 
beherzigt und meine arrays in 2er-potenzen zerteilt (10 mal 4096). nun 
nölt der compiler rum:

...
Error: value of 73728 too large for field of 2 bytes at 1736
...
...

...
Error: Object file not found on exp loction...
...


is schon klar, nebenan ( Beitrag "pgm_read_byte(); --> Nur für Lower 64k Flash (mega128)???" 
) geht es ja um genau dieses problem (adresspointer reicht wohl nicht 
aus). also habe ich pgm_read_byte_far() bemüht. ohne ergebnis, er macht 
es nicht (da gibt er noch die warnung eines bedenklichen cast's aus).

ich fummle schon den ganzen tag daran, so langsam hab ich keine lust 
mehr
% )  . freue mich über jeden konstruktiven vorschlag.

pumpkin

von pumpkin (Gast)


Lesenswert?

ok, komando zurück, er macht es, aber nur wenn ich ausschließlich diesen 
40960er (10 * 4096) table reinlade. kredenze ich das ganze noch mir 
einem zweiten 40960er table blökt er rum (was mich doch sehr stutzig 
macht, ich denke ich kann so oder so nur bis 2^15 adressieren - da bin 
ich in beiden fällen drüber oder sehe ich hier was falsch?).

woran liegt's?

entnervter pumpkin

von A.K. (Gast)


Lesenswert?

Mit 16 Bits lassen sich >64KB nun einmal nicht adressieren. 32 Bits 
nötig. Dazu gibt es die _far Versionen. Aber du bewegst dich in einem 
Bereich, für den AVR und insbesondere WinAVR nicht einfach gebaut sind, 
das wird daher hässlich.

von pumpkin (Gast)


Lesenswert?

okäse, ich dachte schon ich bin zu doof für den kram. das macht mir mut 
; )

was würde abhilfe schaffen? den zweiten table komplett im upper 64k 
ablegen und dann mit _far drauf zugreifen? kann ich mit _far auch in den 
lower zugreifen (so wie ich es sehe ja, aber ich werd nicht so recht 
schlau aus der doku ( 
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html )).

hat jemand ein paar gute links?

pumpkin

von A.K. (Gast)


Lesenswert?

Wie deutlich muss ich eigentlich noch werden? Das Naheliegende wären 
32-Bit Variablen für den Index.

von pumpkin (Gast)


Lesenswert?

moin,

sei mir nicht böse, aber scheinbar musst du noch deutlicher werden. das 
problem ist ja, dass er es nicht in den progmem legen will wenn ich über 
64k komme. das sollte er doch erstmal machen, um den zugriff kann ich 
mich noch später kümmern. was hat ein 32bit index mit dem ablegen zutun?

pumpkin

von A.K. (Gast)


Lesenswert?

Ich habs mal kurz getestet und konnte problemlos 4x20KB ablegen. Zugriff 
über pgm_read_byte_far und 32bit-Rechnung sah vom erzeugten Code her gut 
aus. Gelinkt hat er es auch ohne Meldung.

von A.K. (Gast)


Lesenswert?

PS: WinAVR Jan/2007.

von pumpkin (Gast)


Lesenswert?

habe das update auf die neue WinAVR version gemacht. er meckert jetzt 
nicht mehr wegen dem fehlenden .elf rum. aber ablegen tut er es dennoch 
nicht. wiederum der gleiche fehler. seh ich den wald vor lauter bäumen 
nicht mehr? folgendes:

- ich deklariere die tables im progmem aus externen textdateien (2 mal 
10*4096 unsigned chars, also table1[40960] und table2[40960])

- danach füge ich diese zusammen nach deiner vorgabe:

const unsigned char *sinus[10] = {
  sinus_40960_1,
  ...
  sinus_40960_10 };

const unsigned char *dreieck[10] = {
  dreieck_40960_1,
  ...
  dreieck_40960_10 };

- auf beide tables zugriff per pgm_read_byte_far( &DREIECK(ptr_ch_1) 
); inklusive deines makros

nun nölt der compiler rum dass im der cast im pgm__far nicht schmeckt. 
mir ist klar dass er eine long-adresse sehen will. ist mir doch aber 
relativ egal, weil er sich die adressen selber hinlegt wie er sie 
braucht - ich habe schließlich keinen einfluss auf den speicherbereich 
in welchen er die elemente legt?!? was &DREIECK(ptr_ch_1) auswirft kann 
ich imho nicht weissagen...oder doch?

kann es sein dass er sich daran stört, dass ein_teil von dreieck[] 
oberhalb von 64k liegt und ein teil darunter?

dümmlicher pumpkin

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.