Forum: Compiler & IDEs Wie speichere ich große Arrays in STM32?


von Solocan Z. (solocan)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich brauche ein Array der Größe ~100.000 Floats (=400kB). Ich stelle 
aber fest, dass es nicht größer als ~30000 nicht packt (120kB).

Es geht um STM32H743 mit 1MB Ram. Die entsprechende Stelle des 
Linkerscripts sieht momentan so aus:
1
/* Highest address of the user mode stack */
2
_estack = 0x20020000;    /* end of RAM */
3
/* Generate a link error if heap and stack don't fit into RAM */
4
_Min_Heap_Size = 0x200;      /* required amount of heap  */
5
_Min_Stack_Size = 0x400; /* required amount of stack */
6
7
/* Specify the memory areas */
8
MEMORY
9
{
10
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
11
RAM_D1 (xrw)      : ORIGIN = 0x24000000, LENGTH = 512K
12
RAM_D2 (xrw)      : ORIGIN = 0x30000000, LENGTH = 288K
13
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 64K
14
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
15
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
16
}

Ich verstehe nicht so ganz, wo das Programm überhaupt versucht, das 
Array anzulegen.

Ich habe versucht, direkt auf RAM_D2 über die Addresse zuzugreifen: Ohne 
Erfolg:
1
  uint32_t arr_size=100000;
2
3
//  uint32_t buf[arr_size];   //Funktioniert nicht.
4
  uint32_t address= 0x24000000;
5
6
 uint32_t* buf = (uint32_t*)0x40000;  //Funktioniert auch nicht


Kann mir jemand bitte auf die Sprünge helfen? Wo stelle ich das ein? Wie 
lege ich mein Array dort an?

Anbei das Linkerscript

Danke und beste Grüße

von NichtWichtig (Gast)


Lesenswert?

Im Linkerfile ein eigenen Bereich zu benennen könnte schon mal gut sein.

Beim gcc (AtollicTrueSTUDIO 9.1.0) hatte ich neulich den Wunsch das 
Flash quasi als EEPROM zu mißbrauchen und fand coole Hinweise im Netz.

Linker file:
1
MEMORY
2
{
3
RAM      (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
4
FLASH    (rx)  : ORIGIN = 0x08000000, LENGTH = 124K
5
EEPROM_0 (rx)  : ORIGIN = 0x0801F000, LENGTH = 1K
6
EEPROM_1 (rx)  : ORIGIN = 0x0801F400, LENGTH = 1K
7
...
8
}
9
SECTIONS
10
{
11
  /* prepare 1K from the FLASH as speudo EEPOM */
12
  .flash_eeprom_0 :
13
  {
14
    . = ALIGN(4);
15
    KEEP(*(.flash_eeprom_0))
16
    . = ALIGN(4);
17
  } >EEPROM_0
18
  
19
  .flash_eeprom_1 :
20
  {
21
    . = ALIGN(4);
22
    KEEP(*(.flash_eeprom_1))
23
    . = ALIGN(4);
24
  } >EEPROM_1
25
  
26
27
...

und im c-file
1
// data structures placed in the reserved FLASH space which is configured in the linker file xxx.ld
2
const TDataSet __attribute__((section (".flash_eeprom_0"))) k_Data = { 0, 72, 4000, 10, 0 };

Das klappt und die Daten wurden in den gewünschten Bereich angelegt.
Wichtig ist wohl
Sowas im RAM würde vermutlich auch gehen nur eben mit 100.000 float 
Werten.
Pointer drauf ansetzen und den reservierten Speicherblock einfach dafür 
nutzen.
Könnte klappen.

von Nop (Gast)


Lesenswert?

Solocan Z. schrieb:

> ich brauche ein Array der Größe ~100.000 Floats (=400kB). Ich stelle
> aber fest, dass es nicht größer als ~30000 nicht packt (120kB).

Erste Grundregel bei so einer Frage: führ die Fehlermeldung auf, die der 
Compiler odr Linker wirft. Der wird ja wohl kaum sagen "ah geh i packs 
net".

von Solocan Z. (solocan)


Lesenswert?

Nop schrieb:
> Solocan Z. schrieb:
>
>> ich brauche ein Array der Größe ~100.000 Floats (=400kB). Ich stelle
>> aber fest, dass es nicht größer als ~30000 nicht packt (120kB).
>
> Erste Grundregel bei so einer Frage: führ die Fehlermeldung auf, die der
> Compiler odr Linker wirft. Der wird ja wohl kaum sagen "ah geh i packs
> net".

Ich muss leider sagen: Genau so ist es. Das Programm läuft nicht. Ohne 
Fehlermeldung. Ohne nichts. Compiler interessiert das nicht. Den Linker 
wohl auch nicht, wenn ich in meinem Code irgendwelche Speicherbereiche 
unzulässig inkrementiere und irgendwelche andere wichtige RAM-Bereiche 
überschreibe.

Sonst hätte ich die Fehler gepostet.

Also wie stelle ich fest, dass es "nicht läuft": Ich habe erstens paar 
Prints und LED Meldungen am Anfang. Wenn ich bestimmte Größe 
überschreite, sind diese Weg. Zweitens tritt das nur auf, wenn ich mein 
Array auch initialisiere, also beschreibe. Wenn ich diese nur definiere, 
ist das nur ein Pointer und stört sonst keine Speicherbereiche. Also das 
Ganze ist für mich ein klarer Fall für nicht passenden Speicherbereich. 
Das Linkerskript gibt auch die ersten Hinweise, warum es nicht 
funktionieren soll.

Komischerweise habe ich in einem anderen Programm bereits ein Array mit 
100000 Elementen schon am Laufen! Das kann aber auch ein Zufall sein, da 
das Programm größer ist und das Array vermutlich woanders abgelegt wird, 
wo die anderen Speicherbereiche nicht missbraucht werden.

: Bearbeitet durch User
von ... (Gast)


Lesenswert?

Die Verteilung des Speichers ist keine Lotterie.
Was wo liegt steht im Map-File.

> Es geht um STM32H743 mit 1MB Ram.
Scheinbar eine Nummer zu gross.

von Solocan Z. (solocan)


Lesenswert?

... schrieb:
> Scheinbar eine Nummer zu gross.

Genau, weil die kleineren STM32 ganz anders zu handlen sind...

Wenn du aber sonst so schlau bist, kannst du mir gerne verraten, warum 
und wie der freier RAM-Bereich RAM_D1 nicht ausreicht, obwohl in 
MAP-Datei keine Verstöße zu verzeichnen sind. Ich würde mich dafür 
bedanken. Ansonsten kannst du deine Kräfte, die du zum anonymen Dissen 
verballerst, sinnvoller einsetzen.


NichtWichtig schrieb:
> Im Linkerfile ein eigenen Bereich zu benennen könnte schon mal gut sein.

Mein Problem ist, dass dort bereits einen Speicherbereich gibt, der groß 
genug ist: RAM_D1.

Ich kann trotzdem versuchen, einen eigenen Custom-RAM-Bereich zu öffnen 
und andere zu verkleinern. Aber mal was anderes: Die Idee mit der 
Teilung vom Flash ist echt cool. Ich hatte davon schon gehört aber 
gescheut, da der Flash nicht so viele Schreibzyklen verkraftet. Besteht 
der ganze Hack nur daraus? Oder gibt es noch was dafür zu machen? 
Flashen geht danach ganz normal?

von ... (Gast)


Lesenswert?

> obwohl in MAP-Datei keine Verstöße zu verzeichnen sind

Ja wo hat er denn das/die Array(s) hingelegt?
Zu faul mal zu suchen?
Scheinbar ja wohl nicht im RAM_D1.

Im uebrigen benutze ich IAR. Dort geht das einfach mit einer
Region im Linkerfile und einem Pragma. Falls es nicht sowieso
schon eine passende Region gibt.

define region RAMD1_region   = mem:[from _ICFEDIT_region_RAMD1_start_ 
to __ICFEDIT_region_RAMD1_end__];

#define RAMD1 _Pragma("location=\"RAMD1\"") __root __no_init
RAMD1 float array[MAX];

von NichtWichtig (Gast)


Lesenswert?

Klar hast Du den Speicherbereich im .ld aber im c file hast Du es nicht 
so angegeben wie es der gcc haben möchte.
Siehe mein Beispiel:
1
// data structures placed in the reserved FLASH space which is configured in the linker file xxx.ld
2
const TDataSet __attribute__((section (".flash_eeprom_0"))) k_Data = { 0, 72, 4000, 10, 0 };


Die EEPROM Simulation ist schon eine Krücke welche die endliche 
Schreibbarkeit berücksichtigen möchte.

Man verballert mal eben 2 1K Bänke um abwechselnd seine Daten dort 
abzulegen.
Ist die eine Bank voll wird der letzte Wert/Datensatz in die 2. Bank 
geschrieben und die erste gelöscht.

Je kleiner die Datenmenge ist desto öfters läßt sie sich flashen.

Ob das jetzt gut ist muß man je nach Projekt abschätzen.
Wenn der Speicher eh frei ist kann man das so machen.

Tips von ST finden man u.a. damit:
AN2594
Application note EEPROM emulation in STM32F10x microcontrollers

von hfhd (Gast)


Lesenswert?

das klappt schon solange es in eine komplette RAM section passt


MEMORY
{
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D1 (xrw)      : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw)      : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K


demnach MUSS das array in RAM_D1

standardmößig legt Cube eine einzige section fest die dummerweise 
sollche effekte hat
du musst dein linkerfile komplett anpassen

Stack  an das ende vom DTCM RAM

heap und variablen (.bss )  auf den D3 ram ( aufpassen ob nicht die 16 
eth/USB mit drinhängen )

den D3 RAM legst du auf eine eigene section
das array  legst du dann auf diese section .. und NUR das


ITCM RAM kannst du nicht als RAM für variablen nutzen
das ist für ausführbare funktionen
( ohne waitstates ausführen -> sack schnell  )

von hfhd (Gast)


Lesenswert?

hfhd schrieb:
> den D3 RAM legst du auf eine eigene section
> das array  legst du dann auf diese section .. und NUR das

D1 ... verdammt D1 !! ^^

von hfhd (Gast)


Lesenswert?

den DTCM RAM kannst du natürlich auch für variablen nutzen
musst diese aber auch manuell zuweisen das sie dort liegen sollen

bsp von mir ... es läuft zumindest ...
1
MEMORY
2
{ 
3
  RAM4   (xr)    : ORIGIN = 0x00000000, LENGTH = 16K   
4
  ROM   (xr)    : ORIGIN = 0x08010000, LENGTH = 960K  
5
  RAM   (xrw)    : ORIGIN = 0x20010000, LENGTH = 240K   
6
  RAM2  (xrw)    : ORIGIN = 0x2004C000, LENGTH = 16K   
7
  RAM3   (xrw)    : ORIGIN = 0x20000000, LENGTH = 64K   
8
}
9
10
  .data : 
11
  {
12
    . = ALIGN(8);
13
    _sdata = .;        /* create a global symbol at data start */
14
    *(.data)           /* .data sections */
15
    *(.data*)          /* .data* sections */
16
17
    . = ALIGN(8);
18
    _edata = .;        /* define a global symbol at data end */
19
  } >RAM AT> ROM
20
  
21
  _siitcm = LOADADDR(.itcm);  
22
    /* ITCM RAM for critical functions */
23
  .itcm :
24
  {
25
    . = ALIGN(8);    
26
    _sitcm = .;        /* create a global symbol at itcm start */
27
    *(.itcm)
28
    *(.itcm*)    
29
    . = ALIGN(8);
30
    _eitcm = .;        /* define a global symbol at itcm end */
31
  } >RAM4 AT> ROM
32
33
  .bss :
34
  {
35
    _sbss = .;         /* define a global symbol at bss start */
36
    __bss_start__ = _sbss;
37
    *(.bss)
38
    *(.bss*)
39
    *(COMMON)
40
41
    . = ALIGN(8);
42
    _ebss = .;         /* define a global symbol at bss end */
43
    __bss_end__ = _ebss;
44
    __RamStart = . ;
45
  } >RAM
46
47
  .dtcm (NOLOAD):
48
  {
49
    __dtcmstart = . ;
50
    . = ALIGN(8);
51
    *(.dtcm)
52
    *(.dtcm*)
53
    __dtcmend = . ;
54
    . = ALIGN(8);
55
    
56
    . = ALIGN(8);
57
    __stackend = . ;
58
    . = . + _Min_Stack_Size;
59
    __stackstart = . ;
60
    . = ALIGN(8);
61
  } >RAM3
62
    
63
  /* Ethernet memory*/          
64
  .eth (NOLOAD):
65
  {
66
    . = ALIGN(8);
67
    *(.eth)
68
    *(.eth*)
69
    . = ALIGN(8);
70
  } >RAM2

um wariablen umzupacken eben:
1
static uint8_t __attribute__((__section__(".dtcm"),used)) ucHeap[ configTOTAL_HEAP_SIZE ];
2
3
//oder
4
__ALIGN_BEGIN uint8_t         __attribute__((__section__(".eth"),used))  Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]   __attribute__((aligned(32)));

von Nop (Gast)


Lesenswert?

Solocan Z. schrieb:

> Das Linkerskript gibt auch die ersten Hinweise, warum es nicht
> funktionieren soll.

Das Mapfile würde noch viel bessere geben.

Was allerdings auffällt: der erste Speicherbereich ist 128kB groß. 30000 
mal 4 Byte ist 117kB. Zusammen mit noch ein paar Variablen von Dir 
könnte DTCMRAM voll sein, wenn das Array wesentlich größer wird.

Also, poste doch erstmal das Mapfile, damit man das schonmal 
verifizieren kann.

Und zwar sowohl das Mapfile, wenn Du das Array 30000 groß machst und 
alles geht, als auch das Mapfile, was bei 100000 rauskommt.

von Solocan Z. (solocan)


Lesenswert?

Ok. Ich hatte mir eingebildet, dass das Array in RAM_D1 gemappt würde. 
War natürlich falsch. Es war in DTCM drin. Facepalm. Nun habe ich eine 
neue Section im Linkerskript definiert und das Array dorthin gelegt.

STM32H743ZI_FLASH.ld
1
    .bigData :
2
  {
3
    . = ALIGN(4);
4
    KEEP(*(.bigData))
5
    . = ALIGN(4);
6
  } >RAM_D1


main.c
1
uint32_t __attribute__((section (".bigData"))) mybuf[100000] ;

Vielen Dank an euch alle für die wertvollen Inputs!

von Nop (Gast)


Lesenswert?

Cool! Merkwürdig, daß Dein Linker keinen Fehler gemeldet hat.

Du könntest ansonsten am Ende jeder RAM-Sektion im Linkerfile noch ein 
Linker-Label einfügen, etwa so am Ende der DTCMRAM-Sektion:
1
. = ALIGN(4);
2
__dtcmram_end__ = .;

Und dann ein Linker-Assert:
1
ASSERT(__dtcmram_end__ < ORIGIN(DTCMRAM)+LENGTH(DTCMRAM), "region DTCMRAM overflowed!")

Dann sollte bei Overflow ein Fehler kommen, so daß Du nächstesmal nicht 
erst debuggen brauchst.

von Solocan Z. (solocan)


Lesenswert?

Nop schrieb:
> Cool! Merkwürdig, daß Dein Linker keinen Fehler gemeldet hat.
>
> Du könntest ansonsten am Ende jeder RAM-Sektion im Linkerfile noch ein
> Linker-Label einfügen, etwa so am Ende der DTCMRAM-Sektion:
>
>
1
. = ALIGN(4);
2
> __dtcmram_end__ = .;
>
> Und dann ein Linker-Assert:
>
>
1
ASSERT(__dtcmram_end__ < ORIGIN(DTCMRAM)+LENGTH(DTCMRAM), "region 
2
> DTCMRAM overflowed!")
>
> Dann sollte bei Overflow ein Fehler kommen, so daß Du nächstesmal nicht
> erst debuggen brauchst.

Ja, danke auch für diesen Tipp. Es hat mich reichlich verärgert, dass 
das Ding ohne Hinweis einfach abstürzte.

von Markus F. (mfro)


Lesenswert?

Nop schrieb:
> Das Mapfile würde noch viel bessere geben.
>
Hier würde ich voll und ganz zustimmen. Aber der TO will oder kann wohl 
nicht.


> Was allerdings auffällt: der erste Speicherbereich ist 128kB groß. 30000
> mal 4 Byte ist 117kB. Zusammen mit noch ein paar Variablen von Dir
> könnte DTCMRAM voll sein, wenn das Array wesentlich größer wird.

Man kann übrigens - wenn man will - das Array auch gleich im Linker 
Script anlegen (und damit ganz genau bestimmen, wo es liegt).

Also beispielsweise im .bss-Segment:
1
 .bss :
2
  {
3
    _sbss = .;         /* define a global symbol at bss start */
4
    __bss_start__ = _sbss;
5
    *(.bss)
6
    *(.bss*)
7
    *(COMMON)
8
9
    . = ALIGN(8);
10
    myLargeGlobalArray = .;
11
    . = . + MY_LARGE_ARRAY_SIZE;
12
    _ebss = .;         /* define a global symbol at bss end */
13
    . = ALIGN(8);
14
    __bss_end__ = _ebss;
15
    __RamStart = . ;
16
  } >RAM

Dann reicht im Code die Referenz darauf:
1
extern float myLargeGlobalArray[];

von Solocan Z. (solocan)


Angehängte Dateien:

Lesenswert?

So, jetzt habe ich bisschen weitergespielt und hänge am nächsten Limit.

Ich habe nun den RAM-Bereich RAM_D1 auf 768K vergrößert, um größere 
Arrays zu speichern. Damit sollten 190000 Floats speichern möglich sein. 
Nun habe ich allerdings das Problem, dass es ab 130000 Elementen zwar 
ohne Fehler kompiliert und verlinkt wird, aber das Loading fehlschlägt.
1
Failure at line:9 in "Target Software Startup Scripts". Please edit the debug configuration settings.
2
3
Load Failed.

Ich finde gerade leider keinen Hinweis, warum es nicht geladen werden 
kann.

1
/* Specify the memory areas (Original)
2
MEMORY
3
{
4
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
5
RAM_D1 (xrw)      : ORIGIN = 0x24000000, LENGTH = 512K
6
RAM_D2 (xrw)      : ORIGIN = 0x30000000, LENGTH = 288K
7
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 64K
8
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
9
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
10
}
11
*/
12
13
/* Specify the memory areas (Modified)*/
14
MEMORY
15
{
16
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
17
RAM_D1 (xrw)      : ORIGIN = 0x24000000, LENGTH = 768K
18
RAM_D2 (xrw)      : ORIGIN = 0x35000000, LENGTH = 64K
19
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 32K
20
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
21
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
22
}

Wenn es am Debug Skript liegt, ist es vielleicht off topic für hier aber 
ich bin mir irgendwie nicht so ganz sicher, ob ich beim Verändern des 
Speichers alles richtig gemacht habe. Habe ich da etwas übersprungen?

Viele Grüße und danke nochmals

von gre (Gast)


Lesenswert?

Solocan Z. schrieb:
> Ich habe nun den RAM-Bereich RAM_D1 auf 768K vergrößert, um größere

rechne mal nach

512k block ab 0x2400 0000

512k bytes sind 0x80000

der Ram block hört bei 24080000 also auf
der nächste block geht ab 0x3000 0000 erst los


das ist kein zusammenhängender block

du könntest also nur 2 getrennte arrays dort speichern



oder ein discovery holen wo schon 8-16MB  ram drauf sind

von ... (Gast)


Lesenswert?

Solocan Z. schrieb:
> Ich habe nun den RAM-Bereich RAM_D1 auf 768K vergrößert

Die Speicherverteilung ist doch eine Lotterie.
Nur, er hat er die Niete gezogen.

LOL.

von Solocan Z. (solocan)


Lesenswert?

gre schrieb:
> Solocan Z. schrieb:
>> Ich habe nun den RAM-Bereich RAM_D1 auf 768K vergrößert, um größere
>
> rechne mal nach
>
> 512k block ab 0x2400 0000
>
> 512k bytes sind 0x80000
>
> der Ram block hört bei 24080000 also auf
> der nächste block geht ab 0x3000 0000 erst los
>
>
> das ist kein zusammenhängender block

Ja, ich habe auch später gelesen, dass STM nicht eine einzige1056kB 
physikalische RAM mit 3 logischen konfigiurierbaren Regionen hat, 
sondern tatsächlich 3 getrennte RAMs, die auch HWmäßig fest adressiert 
sind. War ein Missverständnis von mir.

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.