Forum: Compiler & IDEs ARM, GCC, Linker-Skripts


von Vincent H. (vinci)


Angehängte Dateien:

Lesenswert?

Guten Morgen

Linker-Skripts für den GCC zu schreiben kommt mir ein wenig wie 
Russisch-Roulett vor, egal wie gut man im Umgang mit dem Werkzeug geübt 
ist, man hat stets eine 1/6 Chance sich in den Kopf zu schießen. Wenn 
man ungeübt ist (wie ich), dann schafft man nicht einmal das und schießt 
sich ins Knie... oder so ähnlich.

Heute kümmert mich folgendes Problem. Ich hab 4x Linker-Skripts für 
Cortex-M4 Prozessoren angehängt.

STM32F407VGTx_FLASH.ld          -> Original von ST
STM32F407VGTx_FLASH_modified.ld -> Alignment geändert
STM32L476VGTx_FLASH_modified.ld -> SRAM2 hinzugefügt
sections.ld                     -> Template ARM Plugins Eclipse


Soweit sogut. Auf den ersten Blick unterscheiden sich die Skripts nicht 
wirklich grob voneinander. Alle enthalten ihre Speicher Definitionen, 
ihre Sections, usw.

Was aber recht schnell auffällt ist, dass die Alignments der 
verschiedenen Sections von Skript zu Skript abweicht. Wieso etwa ist die 
._user_heap_stack Section von STs Original 8-Byte aligned, während der 
Rest 4-Byte aligned ist?
1
  /* User_heap_stack section, used to check that there is enough RAM left */
2
  ._user_heap_stack :
3
  {
4
    . = ALIGN(8);
5
    PROVIDE ( end = . );
6
    PROVIDE ( _end = . );
7
    . = . + _Min_Heap_Size;
8
    . = . + _Min_Stack_Size;
9
    . = ALIGN(8);
10
  } >RAM


Noch interessanter wirds, wenn man versucht mit dem originalen Skript 
Variablen im CCMRAM des F407 abzulegen und aus dem Flash zu 
initialisieren. Dazu hab ich folgendes probiert:
1
.section .ccmram
2
myvar0_ccmram: .word 3
3
myvar1_ccmram: .word 4
4
myvar2_ccmram: .word 5


Leider finden sich die Initialisierungswerte nirgends im Flash wieder 
und ein Blick ins Listening verrät wieso:
1
Sections:
2
Idx Name          Size      VMA       LMA       File off  Algn  Flags
3
  5 .data         000000ac  20000000  08003c5c  00020000  2**2  CONTENTS, ALLOC, LOAD, DATA
4
  6 .ccmram       0000000c  10000000  10000000  000200ac  2**0  CONTENTS, READONLY
5
  7 .bss          000000b0  200000ac  200000ac  000200ac  2**2  ALLOC
6
  8 ._user_heap_stack 00000604  2000015c  2000015c  000200ac  2**0  ALLOC

VMA und LMA Adresse der .ccmram Sections sind ident? ("READONLY" RAM 
find ich übrigens super)


Ändere ich das Alignment der Section ab
1
  .ccmram :
2
  {
3
    . = ALIGN(8);
4
    _sccmram = .;       /* create a global symbol at ccmram start */
5
    *(.ccmram)
6
    *(.ccmram*)
7
    
8
    . = ALIGN(8);
9
    _eccmram = .;       /* create a global symbol at ccmram end */
10
  } >CCMRAM AT> FLASH


schaut das Listening plötzlich so aus
1
Sections:
2
Idx Name          Size      VMA       LMA       File off  Algn  Flags
3
  5 .data         000000ac  20000000  08003c5c  00020000  2**2  CONTENTS, ALLOC, LOAD, DATA
4
  6 .ccmram       00000010  10000000  08003d08  00030000  2**0  CONTENTS, ALLOC, LOAD, READONLY, DATA
5
  7 .bss          000000b0  200000ac  200000ac  000200ac  2**2  ALLOC
6
  8 ._user_heap_stack 00000604  2000015c  2000015c  000200ac  2**0  ALLOC


Woher kommt dieser Wandel? Das Ergebnis lässt sich beliebig 
reproduzieren. Zum Beispiel auf einem STM32L476. Was ich in Bezug auf 
dessen Linker Skript dann nicht verstehe ist, wieso ST hier plötzlich so 
gut wie alle Sections 8-Byte aligned hat?
Ansonsten tritt auch hier exakt das gleiche Problem mit der 
Initialisierung auf. Zweite RAM-Section 8-Byte aligned und alles ist 
gut, 4-Byte aligned und VMA und LMA sind ident, sprich keine 
Initialisierungswerte im Flash!

Das Template, dass seitens des GNU ARM Eclipse Plugins erstellt wird, 
aligned übrigens ALLE Sections 4-Byte...

Blickt da noch wer durch?
Wie kann das 4/8-Byte Alignment dafür verantwortlich sein ob 
Initialisierungswerte im Flash landen oder nicht?

lg

von Vincent H. (vinci)


Lesenswert?

Einen Nachtrag hab ich noch...


Möchte ich eine Funktion ins RAM schmeißen, dann funktioniert das 
ebenfalls nicht.
1
.section .ccmram
2
myvar0_ccmram: .word 3
3
myvar1_ccmram: .word 4
4
myvar2_ccmram: .word 5
5
6
asmtest_add_in_sram:
7
    ldr     r3, [r0]
8
    ldr     r4, [r1]
9
    add     r2, r3, r4
10
    str     r2, [r0]
11
    bx      lr

Und plötzlich stehn im Listening wieder 2x idente Adresse. :(

von Vincent H. (vinci)


Lesenswert?

Ok. Ein eigenes File, dass ausschließlich RAM Funktionen enthält hat 
Abhilfe geschaffen... aus welchem Grund auch immer?

von Nop (Gast)


Lesenswert?

Also erstmal zum CCM, das kann man natürlich fast normal nutzen. 
Allerdings erstens nicht für DMA und zweitens nicht für Code (data 
only). Zumindest das zweite/dritte Linkerscript ist da falsch, weil es 
fürs CCM auch "execute" erlaubt. Das geht von der Hardware her aber 
nicht.

Dittens enthält der Startupcode wohl keine Initschleife fürs CCM, weder 
Nullinitialisierung noch Kopierschleife für initialisierte (non-zero) 
Variablen. Das mußt Du ggf. ergänzen.

Funktionen im RAM sind bei STM32 dann schlecht, wenn man damit 
Performance haben will. Wegen des ART einerseits und Buskollision 
andererseits wird das dann eher langsamer. Miß es mal nach. Wirklich 
angebracht sind sie nur dann, wenn Du damit im Betrieb das Flash 
beschreiben willst.

Readonly-Phänomen: Wenn man beim GCC Variablen deklariert, die man aber 
nur lesend verwendet, dann schnallt GCC das und setzt die automatisch 
als read-only, auch ohne const. Das ist lästig, wenn man sie ganz bewußt 
nicht als const markiert hat, um sie in die data-Sektion zu kriegen, 
damit sie zur Laufzeit aus dem schnellen RAM und nicht aus dem 
langsameren (scattered access) ROM gelesen werden. Abhilfe schafft 
__attribute mit der gewünschten section.

Alignment: Guck Dir im Startupcode mal die Kopier/Ausnullungsschleifen 
an. Womöglich sind die doppelt ausgerollt, um Bootzeit zu sparen. Dann 
wäre klar, wieso die 8-Byte-alignment haben müssen. Nur so ne Idee.

von Vincent H. (vinci)


Lesenswert?

Danke für die rasche Antwort!

Ja dass man den CCM nicht für Code nutzen kann, darauf führte mich der 
eintretende HardFault... :)

Den als "SRAM2" bezeichneten Speicher des STM32L476 sollte man aber 
schon für Code nutzen können und auch das Datenblatt weist extra darauf 
hin:
"Execution can be performed from SRAM2 with maximum performance without 
any remap thanks to access through ICode bus."

Buskollisionen dürfte es dann ja nur geben, wenn man aus dem SRAM2 
gleichzeitig Code ausführt und Speicherzugriffe tätigt?


/edit
Die Initialisierung hab ich natürlich händisch eingefügt. Aber bei 
"ALIGN(4)" landen die Werte tatsächlich nicht im Flash... Und ich 
versteh nicht wirklich wieso...
Ev. kann das Problem mal wer reproduzieren?

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Wirklich komisch, bei mir funktioniert es so
1
.ccm_data :
2
{
3
  . = ALIGN(4);
4
  _sdata2 = .;       /* create a global symbol at ccmram start */
5
  *(.ccmram)           /* .data sections */
6
  *(.ccmram*)          /* .data* sections */
7
8
  . = ALIGN(4);
9
  _edata2 = .;       /* create a global symbol at ccmram end */
10
} >CCMRAM AT> FLASH
seit Jahr und Tag wunderbar.

Vielleicht probierst du mal aus, die Output Section anders als die Input 
Section zu benennen (also nicht beide ".ccmram") wobei das rein logisch 
nichts bewirken sollte...

Was passiert, wenn du die Variablen nicht per Assembler sondern einfach 
in C(++) anlegst?
1
uint32_t testVar __attribute__((section(".ccmram"))) = 42;
und die auch mal im Code beschreibst?

von Vincent H. (vinci)


Lesenswert?

Sehr interessant.

ALIGN(4) ohne angelegte Variable in C++ funktioniert nicht.
ALIGN(4) inkl. angelegter Variable in C++ funktioniert.
ALIGN(8) ohne angelegte Variable in C++ funktioniert.

/edit
Kanns sein, dass sich da irgendeine Optimierung einmischt?
Sowas wie -ffunction-sections oder -fdata-sections?

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Vincent H. schrieb:
> Buskollisionen dürfte es dann ja nur geben, wenn man aus dem SRAM2
> gleichzeitig Code ausführt und Speicherzugriffe tätigt?

Ja, wobei "Speicherzugriffe" natürlich auch Stack sind, weil der auf dem 
Datenbus daherkommt. Eine Steigerung ist also nur realistisch, wenn Du 
ausschließlich Registerbefehle nutzt, und das geht nur in Assembler.

"Maximum performance" ist da etwas beschönigend: zwar hast Du, anders 
als beim Flash, keine waitstates, insofern ist die Aussage korrekt. Aber 
der ART maskiert die Waitstates beim Flash schon sehr gut. Von den 
Buskollisionen sagt ST da natürlich nichts. Hilft nur messen mit dem 
Oszi und gucken, ob es für Deine Routinen was bringt. Nur daß Du Dich 
halt nicht so stark wunderst, wenn es sogar langsamer ist.

Ich würde übrigens SRAM und SRAM2, also die 128kB, im Linkerfile nicht 
als zwei Bereiche markieren, sondern als einen kontinuierlichen. 
Zusammenhängender Speicher ist immer besser.

Zum Verschwinden der Variablen: Welche Compiler- und Linkeroptionen 
nutzt Du? Optimierungslevel, LTO, Entsorgung ungenutzter Teile? 
(gc-sections oder irgendwie sowas)

Tip: Manchmal hat man Variablen oder Funktionen, die man nicht nutzt 
(oder nicht sichtbar nutzt), die aber nicht wegoptimiert werden sollen. 
Da hilft beim gcc dann das __attribute "used".

von Vincent H. (vinci)


Lesenswert?

-O0, -g3
Auf die Variablen greif ich extra in einer Assembler Funktion zu. 
Generell würd ich den Speicherbereich gerne für Assembler Sachen nutzen, 
daher kommt mir die Trennung eigentlich recht gelegen.

gc-sections nutz ich in Verbindung mit
-ffunction-sections und
-fdata-sections

Ich bin immmer noch planlos. Sofern man eine C-Variable inkl. 
section("..") anlegt findet man die entsprechenden Initialisierungswerte 
immer im Flash. Auch die, die direkt in Assembler angelegt wurden.

Wirklich weh tut das Wegrationalisieren erst bei RAM-Funktionen. Wenn 
sich die im Flash nicht wiederfinden, dann gute Nacht...

von Nop (Gast)


Lesenswert?

OK, dann nimm die ganzen GCC-Compilerparameter mit den section-Sachen 
mal raus, sowohl beim Compiler wie auch beim Linker. Also die 
function-sections, gc-sections, alles mal raus.

Wenn Du die Funktion im RAM willst, dann wirst Du sie wohl explizit in 
die Data-Sektion legen müssen, weil GCC einem gerne auch mal Variablen, 
die nicht beschrieben werden, einfach wie const behandelt (auch ohne 
const!) und sie dann in RO-Data legt. Spart ja RAM.

Attribut "used", dann wird es nicht wegoptimiert, align(4) damit 
32bit-Thumb2-Befehle auch korrekt aligned werden und dann noch eines, 
damit es eben in der Data-Sektion landet anstatt in der Text-Sektion.

Bedenke auch, wenn Deine Funktion absolute Werte lädt, daß der ARM da 
etwas eingeschränkt ist und deswegen unerwartet doch aufs Flash 
zugreift. Direkt im Opcode gehen IIRC nur Werte, die man als geshiftete 
8-bit-Werte darstellen kann, also etwas wie 0xff00, aber nicht 0xf0f0. 
Lösung ist, solche Werte in eine lokale static-Variable zu laden und 
dann die Variable statt des absoluten Wertes zu nehmen.

Und aus 
https://www.embeddedrelated.com/showthread/lpc2000/5031-2.php#tabs1-chronological 
:

"So to summarize, to get and use a function in RAM you only need to do
three things:

(1) Have an init in the startup code for the .data section.
(2) Use the __attribute__((section(".data"))) on the functions.
(3) Define function pointer variables for the functions and make
indirect calls using those."

von Vincent H. (vinci)


Lesenswert?

-O0 oder -O3, -gc-sections oder nicht, alles irrelevant. Außer dem 
Aligment scheint nichts die Initialisierungswerte fürs Flash zu 
beeinflussen...

Ich habs mittlerweile ehrlich gesagt auch aufgegeben.

von Jim M. (turboj)


Lesenswert?

Vincent H. schrieb:
> Was ich in Bezug auf
> dessen Linker Skript dann nicht verstehe ist, wieso ST hier plötzlich so
> gut wie alle Sections 8-Byte aligned hat?

Wenigstens der Stack sollte 8-Byte aligned sein. Braucht man für 
FPU-fähige ARM Cores, auch bei Cortex-M, und ist daher AFAIK eine 
Vorgabe des ABI.

von Vincent H. (vinci)


Lesenswert?

Antwort von ST:


Dear customer,

the 8byte alignment is required due to few reasons (but not mandatory): 
The ST ART Flash accelerator reads the flash using a 64bit wide word. 
Therefore it is performance-wise recommended to use such alignment on 
code and data sections in flash.
The other reason is that the STM32L4 bootloader cannot deal with 
4byte-only alignment properly, when crossing memory pages/banks, as per 
the application note AN2606 section 45.3 "Known limitations" in the 
table below.

Best regards,
ST MCU Support Team

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.