mikrocontroller.net

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


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Solocan Z. (solocan)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
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
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
}

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:


  uint32_t arr_size=100000;

//  uint32_t buf[arr_size];   //Funktioniert nicht.
  uint32_t address= 0x24000000;

 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

Autor: NichtWichtig (Gast)
Datum:

Bewertung
1 lesenswert
nicht 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:
MEMORY
{
RAM      (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH    (rx)  : ORIGIN = 0x08000000, LENGTH = 124K
EEPROM_0 (rx)  : ORIGIN = 0x0801F000, LENGTH = 1K
EEPROM_1 (rx)  : ORIGIN = 0x0801F400, LENGTH = 1K
...
}
SECTIONS
{
  /* prepare 1K from the FLASH as speudo EEPOM */
  .flash_eeprom_0 :
  {
    . = ALIGN(4);
    KEEP(*(.flash_eeprom_0))
    . = ALIGN(4);
  } >EEPROM_0
  
  .flash_eeprom_1 :
  {
    . = ALIGN(4);
    KEEP(*(.flash_eeprom_1))
    . = ALIGN(4);
  } >EEPROM_1
  

...

und im c-file
// data structures placed in the reserved FLASH space which is configured in the linker file xxx.ld
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.

Autor: Nop (Gast)
Datum:

Bewertung
2 lesenswert
nicht 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".

Autor: Solocan Z. (solocan)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: ... (Gast)
Datum:

Bewertung
-3 lesenswert
nicht 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.

Autor: Solocan Z. (solocan)
Datum:

Bewertung
3 lesenswert
nicht 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?

Autor: ... (Gast)
Datum:

Bewertung
-3 lesenswert
nicht 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];

Autor: NichtWichtig (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
// data structures placed in the reserved FLASH space which is configured in the linker file xxx.ld
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

Autor: hfhd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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  )

Autor: hfhd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 !! ^^

Autor: hfhd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ...
MEMORY
{ 
  RAM4   (xr)    : ORIGIN = 0x00000000, LENGTH = 16K   
  ROM   (xr)    : ORIGIN = 0x08010000, LENGTH = 960K  
  RAM   (xrw)    : ORIGIN = 0x20010000, LENGTH = 240K   
  RAM2  (xrw)    : ORIGIN = 0x2004C000, LENGTH = 16K   
  RAM3   (xrw)    : ORIGIN = 0x20000000, LENGTH = 64K   
}

  .data : 
  {
    . = ALIGN(8);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(8);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> ROM
  
  _siitcm = LOADADDR(.itcm);  
    /* ITCM RAM for critical functions */
  .itcm :
  {
    . = ALIGN(8);    
    _sitcm = .;        /* create a global symbol at itcm start */
    *(.itcm)
    *(.itcm*)    
    . = ALIGN(8);
    _eitcm = .;        /* define a global symbol at itcm end */
  } >RAM4 AT> ROM

  .bss :
  {
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(8);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
    __RamStart = . ;
  } >RAM

  .dtcm (NOLOAD):
  {
    __dtcmstart = . ;
    . = ALIGN(8);
    *(.dtcm)
    *(.dtcm*)
    __dtcmend = . ;
    . = ALIGN(8);
    
    . = ALIGN(8);
    __stackend = . ;
    . = . + _Min_Stack_Size;
    __stackstart = . ;
    . = ALIGN(8);
  } >RAM3
    
  /* Ethernet memory*/          
  .eth (NOLOAD):
  {
    . = ALIGN(8);
    *(.eth)
    *(.eth*)
    . = ALIGN(8);
  } >RAM2  


um wariablen umzupacken eben:
static uint8_t __attribute__((__section__(".dtcm"),used)) ucHeap[ configTOTAL_HEAP_SIZE ];

//oder
__ALIGN_BEGIN uint8_t         __attribute__((__section__(".eth"),used))  Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]   __attribute__((aligned(32))); 



Autor: Nop (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Solocan Z. (solocan)
Datum:

Bewertung
0 lesenswert
nicht 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
    .bigData :
  {
    . = ALIGN(4);
    KEEP(*(.bigData))
    . = ALIGN(4);
  } >RAM_D1


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

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

Autor: Nop (Gast)
Datum:

Bewertung
1 lesenswert
nicht 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:
. = ALIGN(4);
__dtcmram_end__ = .;

Und dann ein Linker-Assert:
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.

Autor: Solocan Z. (solocan)
Datum:

Bewertung
0 lesenswert
nicht 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:
>
>
. = ALIGN(4);
> __dtcmram_end__ = .;
>
> Und dann ein Linker-Assert:
>
>
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.

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

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht 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:
 .bss :
  {
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(8);
    myLargeGlobalArray = .;
    . = . + MY_LARGE_ARRAY_SIZE;
    _ebss = .;         /* define a global symbol at bss end */
    . = ALIGN(8);
    __bss_end__ = _ebss;
    __RamStart = . ;
  } >RAM

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

Autor: Solocan Z. (solocan)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
Failure at line:9 in "Target Software Startup Scripts". Please edit the debug configuration settings.

Load Failed.

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

/* Specify the memory areas (Original)
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
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
}
*/

/* Specify the memory areas (Modified)*/
MEMORY
{
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D1 (xrw)      : ORIGIN = 0x24000000, LENGTH = 768K
RAM_D2 (xrw)      : ORIGIN = 0x35000000, LENGTH = 64K
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 32K
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
}



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

Autor: gre (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: ... (Gast)
Datum:

Bewertung
-3 lesenswert
nicht 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.

Autor: Solocan Z. (solocan)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.