Forum: Mikrocontroller und Digitale Elektronik STM32F446: Linker Script und Code im RAM


von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

es wird nach so langer Zeit Zeit, sich mit dem Startup und 
Linker-Scripten zu beschäftigen. Ziel der Sache soll es sein, einzelne 
ISRs aus dem RAM auszuführen. Ich weiss noch nicht, ob es einen 
Geschwindigkeitsgewinn bringt, aber das erfährt man ja nur durch 
Ausprobieren.

Mein Startpunkt ist das von EmBitz mitgelieferte Linker-Skript. Perfekt 
ist es definitiv nicht, schon allein weil darin ein CCMRAM ist, den der 
STM32F446 gar nicht hat.

Mein erster naiver Ansatz: Ich definiere ein Segment "(.code_in_ram*)" 
direkt hinter *(.data*) im .data-Segment. Immerhin kann ich ja davon 
ausgehen, dass das zumindest mal ins RAM kopiert wird. Der Start der 
einzigen Funktion in diesem Segment führt direkt zum Hard Fault Handler. 
Na gut, soviel habe ich am Anfang ja auch gar nicht erwartet.

Wo liegt denn die Funktion? Ich habe einfach mal in main() einen Zeiger 
darauf abgelegt.
1
int main(void)
2
{
3
     void (* volatile fcn)(void) = &TIM6_DAC_IRQHandler;
4
     while(1) fcn();
5
6
     ... wird eh nicht erreicht.
7
}
Die Funktion liegt bei 0x200000c9. Also mitten im BitBand-Bereich. Die 
Variable "fcn" liegt bei 0x2001FFD8, also auch im BitBand-Bereich. Warum 
bricht hier nicht das nackte Chaos aus, wenn normale Variablen im 
BitBand-Bereich liegen?

Die Funktion <TIM6_DAC_IRQHandler> ist auch nicht mehr im Disassembly. 
Nur noch eine <TIM6_DAC_IRQHandler_veneer>. Die ist auch an der Adresse, 
auf die der function pointer zeigt. Von der "echten" Funktion keine 
Spur.

Wie fängt man am besten an? Gibt es irgendwo gut lesbare Informationen 
zu diesem oder allgemeinen Linker Scripten und Code Segmenten für diese 
MCU-Familie?

: Bearbeitet durch User
von oooOO (Gast)


Lesenswert?

Da ist der Startupcode zu doof (oder du), die betreffenden Teile
zu kopieren. Also schau dir den mal an.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Nee, der RAM liegt schon ab 0x20000000.
Im DB steht, dass man dort per Bitbanding Bits kippen kann. "aliased By 
bit-banding"

Ja, du kannst davon ausgehen, dass das Startuopscript alles zwischen 
_data_start__ und __data_end_ kopieren wird.

Aber was sagt denn der Hardfaulthandler?
Falsche Instruktion? Speicherzugriff verboten?
Les doch mal die Instruktion aus dem RAM bei der Funktionsstartadresse 
aus (per Debugger).
Hätteste mal nochn Disassmebly?

fcn liegt auf dem Stack, ist daran zu erkennen, dass am Ende vom SRAM2 
liegt.

Noch ein Fallstrick am Rande: Der I-Bus kann nur auf SRAM1 zugreifen.

Wenn du so gerne Hardfaults erzeugst:
Binde doch mal meine Faulthandler ein, die geben per UART viele Infos 
aus.

von Walter T. (nicolas)


Lesenswert?

oooOO schrieb:
> Da ist der Startupcode zu doof (oder du), die betreffenden Teile
> zu kopieren. Also schau dir den mal an.

Den habe ich noch nicht drin. Aber im Disassembly müsste die Funktion ja 
zumindest zu sehen sein.

Mw E. schrieb:
> Hätteste mal nochn Disassmebly?

Mw E. schrieb:
> Binde doch mal meine Faulthandler ein,

Das ist ein guter Punkt. Ich erzeuge mal ein Minimalprojekt, anstelle im 
Großprojekt herumzuwerkeln.

von Walter T. (nicolas)


Lesenswert?

Mw E. schrieb:
> Nee, der RAM liegt schon ab 0x20000000.
> Im DB steht, dass man dort per Bitbanding Bits kippen kann. "aliased By
> bit-banding"

Aber: Ich kann an dem Linker Script nichts entdecken, was ihm verbietet, 
im Bereich 0x22000000 bis 0x24000000 auch Variablen abzulegen. Und wenn 
dorthinein geschrieben wird, müsste doch für die Variablen im Bereich 
0x2000000+ das nackte Chaos ausbrechen?

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Walter T. schrieb:
> Aber: Ich kann an dem Linker Script nichts entdecken, was ihm verbietet,
> im Bereich 0x22000000 bis 0x24000000 auch Variablen abzulegen.

Doch:
1
/* Memory Spaces Definitions */
2
MEMORY
3
{
4
    ROM  (rx) : ORIGIN = 0x08000000, LENGTH = 512K
5
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
6
    /* Der STM32F446RE besitzt keinen CCRAM */
7
}

RAM startet ab 0x20000000 und ist 128K lang, geht also bis 0x20020000-1

von Bauform B. (bauformb)


Lesenswert?

Walter T. schrieb:
> Mein Startpunkt ist das von EmBitz mitgelieferte Linker-Skript. Perfekt
> ist es definitiv nicht, schon allein weil darin ein CCMRAM ist, den der
> STM32F446 gar nicht hat.

Das CCMRAM ist aber genau das richtige für deine Interrupt-Routinen, mal 
abgesehen davon, dass dein F4 dafür keine spezielle Hardware hat. Also 
benutzt du einfach das SRAM1.

Was den Linker und den Start Up Code angeht, sind das nur andere 
Adressen. Der Mechanismus, der den Code aus dem Flash ins (CCM)RAM 
kopiert, ist der gleiche und die Anweisungen im Linker Script sind die 
gleichen wie beim echten CCMRAM.

https://community.arm.com/developer/ip-products/system/f/embedded-forum/4605/how-to-use-ccm-sram-for-cortex-m4

von oooOO (Gast)


Lesenswert?

Die Bitbandadressen sind Aliasadressen die ebenfalls auf den
RAM zeigen. Lies mal das (ARM-)Referenzmanual.
Mit der Methode des "unbekuemmerten Probierens" kommt man eben
nicht sehr weit.

Damit Funktionen ins RAM geschaufelt werden, brauchen sie u.U.
auch nur/noch passende Attribute. Auch da solltest du mal die
Dok lesen.

von Walter T. (nicolas)


Lesenswert?

Mw E. schrieb:
> RAM startet ab 0x20000000 und ist 128K lang, geht also bis 0x20020000-1

Oh. Das ist peinlich. Klar, diese im Diagramm so winzige 1 MB Bit band 
region ist ja mein komplettes SRAM.

Ich habe noch einmal naiv von vorn angefangen. Ein neues, sehr kleines 
Projekt. Das Linker Script ist noch unverändert. Eine ISR (den 
SysTick-Handler) stumpf in das Segment .data gelegt. Sie liegt jetzt von 
der Adresse her im SRAM.

Die ISR läuft und macht das, was sie soll. Wenn es auch nicht sehr viel 
ist.
Keine Hard Faults. Die ISR lässt sich jetzt nicht mehr mit dem ST-Link 
debuggen, aber ich will nicht jammern.

Jetzt muss ich noch einmal schauen, was vorher schief gelaufen sein mag. 
Beim "großen" Projekt läuft exakt dieses Vorgehen mit dem 
TIM6_DAC_IRQHandler() unweigerlich in den Hard Fault.

Es wird Zeit, mir einen eigenen Hard Fault Handler zu schreiben.

Edit: Es entwickelt sich in Richtung Heisenbug. Es stellt sich heraus: 
Der Hardfault entsteht nur, wenn in der ISR ein Breakpoint ist. 
Ansonsten läuft alles ganz banal und sauber durch. (Aktueller Stand: 
Altes Linker Script, keine Änderung des Startup-Codes, nur die Funktion 
ist in Segment .data eingeordnet.) Daß die Funktion wirklich aus dem 
SRAM ausgeführt wird, erkenne ich daran, dass ein Codeabschnitt, in dem 
ein Sprung ausgeführt wird, jetzt zwei Takte weniger benötigt (ermittelt 
mit DWT_CYCCNT).

Was ist eigentlich der schnellste Weg, sich den ISR Vector Table 
anzuschauen? Gibt es da schon einen vordefinierten Header?

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Im ISR-Vektor ist immer noch eine Adresse im Flash-Bereich. Eine 
"veneer"-Funktion. Also wird mich die Aktion im RAM mehr Zeit kosten als 
nutzen.

von Nop (Gast)


Lesenswert?

Walter T. schrieb:
> Im ISR-Vektor ist immer noch eine Adresse im Flash-Bereich. Eine
> "veneer"-Funktion. Also wird mich die Aktion im RAM mehr Zeit kosten als
> nutzen.

Du könntest natürlich die ganze Vektortabelle ins RAM schieben, 
Stichwort VTOR. Dann kannst Du sie sogar zur Laufzeit ändern.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Walter T. schrieb:
> Also wird mich die Aktion im RAM mehr Zeit kosten als
> nutzen.

Die Forschungsergebnisse interessieren mich trotzdem.
Eigentlich müsste es durch Flash Cache eh schnell genug sein.
Der erste ISR Befehl sorgt für ein miss und dann sind aber schon 128? 
Byte in den "ART Accel." (wie STM das nennt) geladen.
Aber wer weis?

Daher mal Nops Vorschlag durchführen, aber eben nicht vergessen die 
Vektortabelle auch ins RAM zu linken.

ABER!
Die ersten 2 Einträge müssen bleiben.
Das lässt sich händisch an den Anfang schreiben oder die ISR Tabelle 
wird von hand kopiert.
Das sind ja Stack und Programmstart.

von Walter T. (nicolas)


Lesenswert?

Mw E. schrieb:
> Die Forschungsergebnisse interessieren mich trotzdem.

Ist ja schnell gemacht. Letztendlich habe ich nur die ISR aus dem 
Segment .text in das Segment .data geschoben. Linker und Startup-Code 
habe ich nicht mehr angefasst.

Oben habe ich mit viel Mühe nach einem Fehler gesucht, der keiner war.

Die Laufzeit habe ich mit dem Taktzähler DWT_CYCCNT ermittelt (Min und 
Max-Wert).

Bei mir hat es 2 Takte gebracht (ISR im Flash 15...30 Takte, im SRAM 
ohne CCM 15...28 Takte). Aber die ISR hat auch nur einen 
Verzweigungspunkt.

Für mich ist das Fazit: Es bringt mir nichts, an dieser Stelle noch zu 
mikrooptimieren, aber ich hätte mich geärgert, es nicht probiert zu 
haben.

: Bearbeitet durch User
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.