Hallo Community!
Seit ich mich tiefer mit dem STM32F4 beschäftige und auch seine
Speicher-Architektur im Detail verstehen will, interessiert mich vor
allem eine Frage:
Wann wird der I-Bus verwendet?
Sind die vorgegebenen Linkerskripte von Keil/Atollic/etc korrekt?
Diese Frage ist hier auch schon mal aufgetaucht, und die Antwort lautete
damals:
"Das passt so. Das Programm wird an 0x08000000 gelinkt und abgelegt,
denn da ist das Flash."
Aber ist der ARM-Core intelligent genug, um zu wissen, dass er diese
Adressen über den I-Bus erreichen kann?
Ich habe soeben mal gemessen. Coremark, wie man es unter
http://www.eembc.org/coremark/download.php herunterladen kann.
Minimalste Anpassungen für die Clock, Printf und Timer. Alles ins RAM
gelinkt, um Flash-Latenzen außen vor zu lassen.
3000 Iterationen im Performance Run benötigen dann 52,741 Sekunden
Dann habe ich das Linkerskript modifiziert:
- Memory section IBUS erstellt
- .isr_vector und .text in den IBUS gelinkt, aber mit Loadaddress im RAM
das sieht dann so aus:
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K /* only accessible by CPU */
8
}
9
10
/* Define output sections */
11
SECTIONS
12
{
13
/* The startup code goes first into FLASH */
14
.isr_vector :
15
{
16
. = ALIGN(4);
17
KEEP(*(.isr_vector)) /* Startup code */
18
. = ALIGN(4);
19
} >IBUS AT> RAM
20
21
.text :
22
{
23
/* put interrupt service routines first into flash */
24
*(.*_Handler)
25
*(.*_IRQHandler)
26
27
/* all program code goes here */
28
*(.text) /* .text sections (code) */
29
*(.text*) /* .text* sections (code) */
30
*(.glue_7) /* glue arm to thumb code */
31
*(.glue_7t) /* glue thumb to arm code */
32
*(.eh_frame)
33
34
KEEP (*(.init))
35
KEEP (*(.fini))
36
37
. = ALIGN(4);
38
_etext = .; /* define a global symbols at end of code */
39
} >IBUS AT> RAM
40
{...}
Neu compiliert und gelinkt habe ich den selben Test ablaufen lassen. Und
nun braucht dieser nur noch 37,159 Sekunden.
Das sind fast *30%* weniger Zeit! Fast *40%* Leistungsgewinn!
Die Linkerskripte sind also nicht auf die Hardware optimiert.
Leider findet man zu diesem "Feature" auch absolut keine Informationen
im Internet. Selbst STM sagt nur, dass es den IBus gibt, aber nicht, wie
man den richtig verwendet.
~Lil B
Little B. schrieb:> Minimalste Anpassungen für die Clock, Printf und Timer. Alles ins RAM> gelinkt, um Flash-Latenzen außen vor zu lassen.>> 3000 Iterationen im Performance Run benötigen dann 52,741 Sekunden
Du vergleichst also Flash mit RAM. Vergleiche doch mal deine "IBUS"
section an adresse 0 mit ausführung ganz normal aus dem Flash (also
0x08000000...).
Little B. schrieb:> Nein, ich vergleiche RAM über 0x20000000 mit RAM über 0x00000000
Achso, mich hat verwirrt dass du nirgendwo erwähnst, dass du den RAM an
Adresse 0x00 umgemappt hast, denn normalerweise ist da ja auch der Flash
zu finden ;-)
Interessant wäre dann noch das gleiche nur mit Flash, also Flash an
0x08000000 vs. Flash an 0x00, denn das ist ja der "normale"
Anwendungsfall, für den auch die normalen Linkerscripte sind...
Dr. Sommer schrieb:> Interessant wäre dann noch das gleiche nur mit Flash, also Flash an> 0x08000000 vs. Flash an 0x00, denn das ist ja der "normale"> Anwendungsfall, für den auch die normalen Linkerscripte sind...
Das habe ich just getan.
Hier scheint es jedoch egal zu sein, wohin man linkt.
Ob von 0x00 oder von 0x08000000 ergibt beides (der selbe test wie oben)
37,052 Sekunden.
...jetzt bin ich verwirrt und enttäuscht...
Das ist "halbwegs" dokumentiert, wenn man sich in der Doku
zurechtgefunden hat. Im Adressbereich 0x00000000 bis 0x1FFFFFFF werden
Instruktionen über den I-Bus geholt, wohingegen im Bereich ab 0x20000000
Instruktionen über den geteilten D-Bus geholt werden.
Bei Flash ist es egal, ob man für Startadresse 0 oder 0x08000000 linkt,
da beide Adressbereiche, in denen das Flash eingeblendet werden kann, im
Codebereich liegen. Beim SRAM ist es nicht egal, denn wenn man für
Startadresse 0 linkt, ist es schneller, als wenn man für 0x20000000
linkt (spätestens nach dem ersten Sprung nach dem Resetvektor erfolgen
die Codezugriffe dann über Adressen 0x20....
Siehe Reference Manual Seite 69
http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf
Genauso ärgerlich ist, daß kaum ein Linkerscript, das durchs Netz oder
bei den diversen IDEs dabei ist, das core-coupled SRAM vernünftig nutzt.
Wenn man zwei DMAs am laufen hat (Ethernet, SD-Card), macht es gefühlte
30-50% aus, wenn der Stack im CCM und nicht im normalen SRAM liegt.
Tassilo H. schrieb:> Das ist "halbwegs" dokumentiert, wenn man sich in der Doku> zurechtgefunden hat. Im Adressbereich 0x00000000 bis 0x1FFFFFFF werden> Instruktionen über den I-Bus geholt, wohingegen im Bereich ab 0x20000000> Instruktionen über den geteilten D-Bus geholt werden.
Wieder was gelernt!
Ich bin von der Tabelle auf Seite 71 in diesem Reference Manual
ausgegangen, wo ein Adressbereich von 1MB angegeben ist. Dass der IBus
aber 512 MB adressieren kann, hab ich bisher noch nicht gelesen.
Also ist meine obige Information nur relevant, wenn man vom SRAM oder
aus dem FSMC das Programm ausführt.
Tassilo H. schrieb:> Genauso ärgerlich ist, daß kaum ein Linkerscript, das durchs Netz oder> bei den diversen IDEs dabei ist, das core-coupled SRAM vernünftig nutzt.> Wenn man zwei DMAs am laufen hat (Ethernet, SD-Card), macht es gefühlte> 30-50% aus, wenn der Stack im CCM und nicht im normalen SRAM liegt.
Was ist hierbei der Leistungsgewinn wenn der Stack im CCM liegt ?
DMA Zugriffe auf dem CCM funktionieren ja nicht wenn ich das richtig
verstanden habe.
Stefan schrieb:> Was ist hierbei der Leistungsgewinn wenn der Stack im CCM liegt ?> DMA Zugriffe auf dem CCM funktionieren ja nicht wenn ich das richtig> verstanden habe.
Na ja, man legt die DMA-Buffer (z.B. Buffer für SD-Card und Ethernet)
ins normale SRAM. Die muß der CPU-Core ja meist nicht komplett selber
verarbeiten. Wenn man Ethernet und SD-Card mit hoher Datenrate laufen
läßt, ist die Busauslastung damit schon nennenswert, d.h. die
Wahrscheinlichkeit ist hoch, daß ein Zugriff warten muß.
Alle lokalen Variablen liegen auf dem Stack, legt man den (und ggf. auch
globale Buffer, auf die DMA nicht zugreifen muß) ins CCM, muß der Core
viel seltener auf einen RAM-Zugriff warten (meist gibt es ja noch mehr
zu tun als nur SD-Karte und Ethernet anzusteuern).
Das ganze hat zugegebenermaßen Fallstricke und deshalb ist es vmtl. in
den Tutorials meist nicht drin: Wenn man z.B. Elm CHan's FatFS mit der
ST-Beispielimplementierung für den SD-Zugriff einfach ungesehen
übernimmt, geht das schief, wenn man mal 512 Bytes oder mehr auf einmal
aus einer lokalen (Stack-)Variable an die Schreibfunktion übergibt: Die
SD-Card-Library versucht, den Sektorbuffer zu umgehen, wenn möglich, und
wird versuchen, die Daten direkt von der Quelle zu schreiben. Das klappt
dann nicht. Andererseits ist das nur einer von mehreren Fallstricken,
die sowieso schon bei der DMA-Verwendung lauern (Alignment der
Datenbuffer, DMA kann ggf. keine 4k-Adressgrenze queren, irgendwas war
da).