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
intmain(void)
2
{
3
void(*volatilefcn)(void)=&TIM6_DAC_IRQHandler;
4
while(1)fcn();
5
6
...wirdehnichterreicht.
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?
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.
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.
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?
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
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
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.
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?
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.
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.
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.