Hallo,
Ich hantiere derzeit mit einem STM32 Cortex M4 Eval Board von Waveshare,
und experimentiere mit jenen bereitgestellten Beispielen, wie dem
LCD-Display, SRAM etc.
Dazu hab ich bisher das bereitgestellte MDK von KEIL genommen, welches
ja mit dem built-in Realview Compiler daherkommt, der in der freien
Version bis zu 32kb binary Size generiert.
Allerdings möchte ich nicht an Windows gebunden sein, und habe mir unter
Linux mittels der Anleitung aus dem Wiki eine Eclipse Umgebung
eingerichtet.
Das Ganze funktioniert auch eigentlich super, Debugging funktioniert gut
(wenn auch ein wenig langsam) mit dem STlink 2. Dort verwende ich das
GCC Embedded Tools von Launchpad.
Ich hatte zwischenzeitlich auch die Sourcery Bench Lite Version am
laufen gehabt, aber nach dem ich die Einschränkung bezüglich FPU
Funktionalität gesehen habe, bin ich dann auf das freie GCC gewechselt.
Naja ich erwähne das eigentlich nur, um zu sagen, dass dort das gleiche
Problem war, zu dem ich jetzt komme.
Bei Realview entstehen binary sizes von ca. 8-27kb mit den Beispielen
von Waveshare. Und beim GCC gibt es Größen, von 64kb-100kb.
Ich denke GCC fügt noch viel unnötige Bibliotheken in die binary ein.
Darauf hab ich dann verschiedene Compiler Optimierungsoptionen
eingefügt, welche zwar marginal was gebracht haben(ca. 10-20kb weniger)
aber trotzdem das Ganze noch immer viel zu groß ist.
Hier die Compiler Optionen:
Realview CC:
Wie man oben sieht nutze ich für GCC "stm32_flash.ld" aus der StdPeriph
Lib.
Wenn Bedarf besteht, kann ich auch noch mal die beiden Projekte(GCC,
Realview) hochladen.
Ach ja der Grund warum ich so an Code-size "geize" ist, dass ich für den
Anfang wegen der beschränkten Schreibanzahl im Flash lieber im SRAM
arbeite, und erst wenn es wirklich notwendig wird, auf den Flash
umsteige.
Vielen Dank schon mal
Philipp
Die Größe der Binaries machst Du an deren Dateigröße fest, oder
untersuchst Du die mit dem Mapfile oder ähnlichem?
Zumindest beim gcc erzeugst Du eine .elf-Datei, das ist kein "nacktes"
Binary, also nicht 1:1 das Abbild eines Flash-ROM o.ä., sondern kann
noch diversen weiteren Kram enthalten, wie z.B. auch
Debug-Informationen.
Äpfel - Birnen
softfp - hardfp
newlib - whateverlib
Kostenlos - einige k€
Schau doch mal nach, was den vielen Platz verbraucht.
Abgesehen davon: Den Controller durch Neuprogrammieren totzuflashen muss
man auch erstmal schaffen.
Karl schrieb:> Kostenlos - einige k€>> Schau doch mal nach, was den vielen Platz verbraucht.
Falls die k€ zu viel Platz verbrauchen würde ich sie dir auch abnehmen.
Auf meinem Konto ist noch viel Platz :-)
(dort ist allerdings ein 136320 Byte großes Bild im Flash gespeichert,
um Verwirrung vorzubeugen)
@Karl
Ob es ein Äpfel-Birnen Vergleich ist, sei mal dahin gestellt.
Es werden zwei Compiler verglichen, mit dem selben Output, und
float-Zahlen sind bei vielen der getesteten Beispiele nicht vorhanden.
Aber der Rest den du meinst leuchtet mir schon ein. Ich dachte
eigentlich, dass der GCC Compiler sogar bessere Arbeit verrichtet, aber
mit den Argumenten, ist das wirtschaftlich natürlich Unsinn(wer würde
dann noch RealView kaufen)
Also ist der GCC einfach doch zu "generell", um sehr (größen)optimierten
Code zu erzeugen...
Karl schrieb:> Schau doch mal nach, was den vielen Platz verbraucht.
Jetzt muss ich mich leider doof stellen, wie geht das?
Gruß
Philipp
Philipp_M schrieb:> Aber der Rest den du meinst leuchtet mir schon ein. Ich dachte> eigentlich, dass der GCC Compiler sogar bessere Arbeit verrichtet, aber> mit den Argumenten, ist das wirtschaftlich natürlich Unsinn(wer würde> dann noch RealView kaufen)
Die Leute, die glauben, dass man für mehr Geld automatisch einen
besseren Compiler bekäme. Oder Leute, die einen Compiler mit einer
IDE verwechseln und einen bestimmten Compiler deshalb bevorzugen, weil
sie die mit ihm üblicherweise gemeinsam angebotene IDE gut finden.
> Also ist der GCC einfach doch zu "generell", um sehr (größen)optimierten> Code zu erzeugen...
Keineswegs.
Ohne eine genaue Analyse kann man aber nicht viel sagen. Am besten
wäre es natürlich, wenn du die Codegrößen für einzelne Funktionen
zwischen beiden Compilern vergleichen könntest. Bei den GNU-Tools
geht das mit “arm-none-eabi-nm --print-size” (ggf. noch --size-sort
mit angeben).
Irgendwo im Code in printf/sprintf? Falls ja, erstmal durch
iprintf/siprintf ersetzen oder auskommentieren, wenn dies eine deutliche
Änderung der Codegröße bringt, ist der "Übertäter" eingegrenzt. stdio
und insbes. stddio mit FP sind in der newlib zwar universell aber
speicherhungrig. Es existieren schlankere Implementierungen. Ansonsten:
mehr Informationen liefern. Code, Linker-Script und Makefile
zusammenpacken und einstellen.
Falls du C++ verwendest: -fno-exceptions , -fno-rtti, eine leere
Funktionen "__cxa_pure_virtual" (mit extern "C", falls in einem C++
Sourefile) einbauen. C++11 verwenden, so viele Konstruktoren wie möglich
"constexpr" machen.
Auch für C:
* Den ARM Compiler mit LTO neu compilen (oder hier
http://games.2g2s.de/zeug/gcc-arm-none-eabi-4_7-2013q1-20130320-linux.tar.bz2
von mir ein Binary für Linux loaden) und beim linken deines Programms
-flto angeben. Dies erfordert, dass du alle Compiler Flags auch beim
Linken angibst.
* Mit "-ffunction-sections -fdata-sections" compilen und linken, mit
-Wl,--gc-sections linken.
* Da du ja einen Cortex-M4F hast (mit FPU), solltest du auch das hardfp
ABI verwenden: "-mfpu=fpv4-sp-d16 -mfloat-abi=hard" beim Compilen und
linken
* Leere Funktion "atexit" einbauen.
* Mit -Os compilen und linken.
* Die Inlining-Heuristik vom GCC ist nicht so gut, bei kleinen
Funktionen überlegen ob inlinen grundsätzlich sinnvoll ist (zB weil der
Aufruf-Aufwand schon groß wäre), und dort "__attribute__( (
always_inline ) )" verwenden.
* Möglichst viele Berechnungen vom Compiler anstellen lassen durch
Verwendung von "const" und "constexpr". Dazu auch "union" statt
Pointer-Magic verwenden, damit der Compiler "sieht" was man vorhat.
* ggf. uint_fast8_t statt uint8_t verwenden etc.
* Wie oben schon genannt, printf und andere hungrige Library-Funktionen
vermeiden.
* Mehrere aufeinanderfolgende Zugriffe auf dieselbe "volatile" Variable
vermeiden, da diese natürlich nicht optimiert werden können/dürfen (Die
ST StdPeriphal Library z.B. macht das leider)
Und am wichtigsten natürlich, mit "objdump -d foo.elf" den generierten
Code disassemblen und ansehen. Dabei auf Funktionen wie
"_ZN5STM323RCC6ClocksL6SYSCLKE.5950.4626" achten, die sind ein Hinweis
auf nicht vernünftig optimierte (constexpr) C++ Konstruktoren. Auch
nachsehen welche Library Funktionen im Binary landen, von welcher in
deinem Code verwendeten Funktion diese benötigt werden, und überlegen ob
diese so zwingend erforderlich sind (wie printf oben).
Niklas Gürtler schrieb:> * Die Inlining-Heuristik vom GCC ist nicht so gut,
selbst wenn man "inline" dranschreibt ignoriert der GCC das gerne mal,
wollte ich noch schreiben, aber hier gibts ja das 15min-Limit.
Philipp_M schrieb:> Ich vergleiche denke ich schon die richtigen Größen, also die, die der> "size-analyzer" der jeweiligen Toolchains ausgibt. Also beim GCC z.B.>> arm-none-eabi-size --format=berkeley LCD-Driver.elf> text data bss dec hex filename> 176152 2180 1088 179420 2bcdc LCD-Driver.elf
Wende die Size-Tools mal nicht auf das gesamte Programm (*.elf), sondern
auf einzelne Objektmodule (*.o) an. Wenn dort die Unterschiede zwischen
den beiden Compilern geringer sind (was ich fast vermute), liegt die
Ursache des drastisch schlechteren Abschneidens des GCC (auch) in den
verwendeten Laufzeitbibliotheken. Beim Keil-Compiler hast du die Auswahl
zwischen der ARM-Standard-Library und der MicroLib. Letztere ist nicht
so sehr geschwindigkeitsopimiert und in der Funktionalität einiger
Funktionen eingeschränkt, dafür ist der mit ihr gelinkte Code deutlich
kompakter:
http://www.keil.com/arm/microlib.asphttp://www.keil.com/support/man/docs/armlib/armlib_bajjibhh.htm
Hi Philipp,
unabhängig von deinem Problem
muss ich Karl rechtgeben :
>Abgesehen davon: Den Controller durch Neuprogrammieren totzuflashen muss>man auch erstmal schaffen.
das Flash hält 10.000 Schreibzyklen aus
ich glaube du machst dir da unnötig sorgen
Gruss Uwe
Hui,
da sind ja einige (gute) Antworten zusammengekommen :)
Vielen Dank dafür, man lernt ja nie aus.
Tatsächlich hat die Funktion "sprintf" knapp 20kB Code geschluckt, also
schon mal einer der Übeltäter.
Danke für die Erklärung @Yalu X.
Ansonsten, sind die reinen Objectfiles tatsächlich in etwa dem gleichem
Größen-Rahmen wie die des Realview Compilers.
Mit ein wenig Anpassung hab ich dann auch ca. 17kB
geschafft(wahrscheinlich gibts da sogar noch mehr Möglichkeit nach oben,
aber wozu hat man 1MB Flash)
Danke für die schöne Liste @Niklas Gürtler, das hat mir viel
weitergeholfen.
Uwe B. schrieb:> das Flash hält 10.000 Schreibzyklen aus> ich glaube du machst dir da unnötig sorgen
Ich hatte noch die 1000 Zyklen vom STM32F1 in der Wiki im Kopf, aber bei
10.000 brauch mir tatsächlich keine großen Sorgen machen.
Gruß
Philipp M
Niklas Gürtler schrieb:> * Den ARM Compiler mit LTO neu compilen (oder hier> http://games.2g2s.de/zeug/gcc-arm-none-eabi-4_7-2013q1-20130320-linux.tar.bz2> von mir ein Binary für Linux loaden) und beim linken deines Programms
Also beim Geocaching und wenn ich ein Premium-Member wäre, dann würde
ich für das Bereitstellen der Crosstoolchain "einen Favoritenpunkt
vergeben"
Nur dumm, daß ich auf meinem Linux wegen Gnuradio nur 32bit-Linux
installiert habe und deine Toolchain für 64bit gebaut ist.
Lass mich dumm fragen: Wie schwer wäre es für dich hier auch eine
Version für 32Bit zu bauen?
Danke trotzdem!
Günter (dl4mea)
Günter (dl4mea) schrieb:> Lass mich dumm fragen: Wie schwer wäre es für dich hier auch eine> Version für 32Bit zu bauen?
Hm gerade nicht so ganz einfach... Probier vielleicht erstmal den
Linaro: https://launchpad.net/gcc-linaro
Hi,
danke erst mal. Hab ich geladen. Aber jetzt mal zwei Fragen:
Bei mir gehts darum Code fürs Beaglebone zu erzeugen. Bislang verwende
ich die Angstrom-Toolchain unter Eclipse.
Frage 1: Was ist der Unterschied zwischen den zwei gcc, die in den
Toolchain-Paketen enthalten sind. Zum einen ist ein plain "gcc" und dann
eins mit Präfix:
/usr/local/gcc-arm-none-eabi-4_7-2013q1$ find . -iname *gcc
./bin/arm-none-eabi-gcc
./arm-none-eabi/bin/gcc
Binär sind beide identisch, wozu also?
Frage 2: Offenbar unterscheiden sich die Pakete immer dadurch, ob sie
für "Bare Metal" gemacht sind oder ob sie Linux unterstützen. Richtig?
Fürs Beaglebone brauch ich die Includes und Libs. Kann ich die Libs dann
einfach vom Bone runterkopieren, und wenn ja, wohin am besten, denn es
gibt in den Paketen mehrere "passende" Verzeichnisse:
/usr/local/gcc-arm-none-eabi-4_7-2013q1$ find . -name include
./lib/gcc/arm-none-eabi/4.7.3/install-tools/include
./lib/gcc/arm-none-eabi/4.7.3/include
./lib/gcc/arm-none-eabi/4.7.3/plugin/include
./arm-none-eabi/include
/usr/local/gcc-arm-none-eabi-4_7-2013q1$ find . -name lib
./lib
./arm-none-eabi/lib
Oder ist es zwingend nötig die Libs auch mit der crosstoolchain zu
übersetzen? (Kann mir vorstellen das wäre so mit LTO, aber das jetzt mal
hinten angestellt)
Danke, Günter
Günter (dl4mea) schrieb:> Binär sind beide identisch, wozu also?
Tja weiß ich auch nicht, vielleicht irgendwas historisches mit
Directory-Layouts...
> Frage 2: Offenbar unterscheiden sich die Pakete immer dadurch, ob sie> für "Bare Metal" gemacht sind oder ob sie Linux unterstützen. Richtig?
Unter anderem, ja
> Fürs Beaglebone brauch ich die Includes und Libs. Kann ich die Libs dann> einfach vom Bone runterkopieren, und wenn ja, wohin am besten, denn es> gibt in den Paketen mehrere "passende" Verzeichnisse:
Du verwendest den Beaglebone mit Betriebssystem (Linux)? Dann bringt dir
der arm-gcc-embedded (und linaro vermutlich auch) nichts, du brauchst
einen GCC der für Linux ausführbare ELF's erzeugt. Ich würd die
libs/includes in ein eigenes Verzeichnis kopieren und CPATH bzw.
LIBRARY_PATH verwenden um sie für den GCC auffindbar zu machen. Wobei
die Standard-C & C++ Libraries wie <string.h> oder <vector> ja beim GCC
dabei sein sollten.
> Oder ist es zwingend nötig die Libs auch mit der crosstoolchain zu> übersetzen?
Nein, das sollte so gehen. Habe mich aber oben vertan, du musst beim
compilen jeder C-Datei "-flto" noch mit angeben, damit deren Inhalt mit
optimiert werden kann.
Niklas Gürtler schrieb:> Kleines Update: Die neue Version vom GCC-ARM-Embedded> (4.7-2013-q2-update) hat nun LTO eingebaut, und die läuft auch unter> 32bit-x86.
Die obige Version habe ich geladen und installiert (Windows). Gibt es
eine einfache Oberfläche, wie z. B. "Programmers Notepad [WinAVR]", in
der ich den Compiler integrieren kann?
GCC-Anfänger schrieb:> Die obige Version habe ich geladen und installiert (Windows). Gibt es> eine einfache Oberfläche, wie z. B. "Programmers Notepad [WinAVR]", in> der ich den Compiler integrieren kann?
Jede Oberfläche, die etwas mit dem GCC anfangen kann; eclipse, netbeans,
Dev-C++, geany, KDevelop, etc. CoIDE ist sogar speziell für die ARM µC
gemacht. Ich hab mal im Wiki
http://www.mikrocontroller.net/articles/STM32#GCC aufgelistet was man
zur direkten Benutzung vom GCC für STM32 braucht.
Martin Thomas schrieb:> stdio> und insbes. stddio mit FP sind in der newlib zwar universell aber> speicherhungrig. Es existieren schlankere Implementierungen.
Ich habe aktuell ein recht ähnliches Problem. Der Code wird mit Keil so
compiliert dass er gut in den Flsh passt bei GCC, trotz Optimierung (-Os
-flto -ffunction-sections -fdata-sections -Wl,-gc-sections) kommt es zum
Falsh Overflow (ca. 12kb). Könntest Du die konkreten Libraryalternativen
(Namen) für stdio+fp benennen. Gibt es für string auch eine reduzierte
Lib?
Erstens: Die Optimierungsstufen beim GCC sind m.E. nicht logisch. -O3
macht meist umfänglicheren und nicht schnelleren Code als -O2 und so
weiter. Da bleibt nur eines: Ausprobieren.
Zweitens: Warum zum Teufel reitest du auf sowas wie stdio herum? Mach
dir deine I/O Routinen selber, dann hast du genau DAS, was du brauchst
und bist wenigstens einigermaßen sicher vor bösen Überraschungen, wenn
du mal Compiler und/oder µC Architektur wechselst. Ich schreib mein
Zeugs auch so, daß es auf verschiedensten Zielarchitekturen
gleichermaßen läuft (bis auf die alleruntersten LowLevel-Routinen) und
das geht eben nur dann, wenn man auf Kram wie printf und Konsorten
komplett verzichtet. Sowas ist m.E. auf einem µC ohnehin deplaziert.
Schließlich weiß man als Programmierer ja wohl, was man wohin ausgeben
oder hereinholen will.
W.S.
W.S. schrieb:> Zweitens: Warum zum Teufel reitest du auf sowas wie stdio herum? Mach> dir deine I/O Routinen selber, dann hast du genau DAS, was du brauchst> und bist wenigstens einigermaßen sicher vor bösen Überraschungen, wenn> du mal Compiler und/oder µC Architektur wechselst.
Das ist genau das Problem, in dem Projekt wird "zwangsweise" fremder
Code verwendet. Der nutzt leider sscanf sprintf und auch float (in
Software).
>Ich schreib mein> Zeugs auch so, daß es auf verschiedensten Zielarchitekturen> gleichermaßen läuft (bis auf die alleruntersten LowLevel-Routinen)
Wo bekommst Du die Sys-Calls (als Lib) her?
> Sowas ist m.E. auf einem µC ohnehin deplaziert.
Für das Debbugging allerdings sehr hilfreich ohne gleich einen JTAG
Tracer einzusetzen.
MartinG schrieb:> Wo bekommst Du die Sys-Calls (als Lib) her?
Was verstehst du unter Sys-Calls? und dann noch als Lib? Meinst du
SVC's? oder was dann? Wie ICH sowas mache, kannst du hier in der
Codesammlung bei der Lernbetty anschauen. Die ist zwar noch ARM7TDMI,
aber am Prinzip ändert das nix. Standard-IO ist allenfalls auf dem PC
angesagt, aber doch nicht wirklich nutzvoll auf einem µC.
"Der nutzt leider sscanf sprintf" O je, was hast du denn da an
greulichem Fremdzeugs? Wenn ich du wäre und die Quellen hätte, würde ich
mir das Ganze umschreiben. Ich sehe hier in diesem Forum ohnehin viel
Quellcode-Müll, bei dem alles kreuz und quer voneinander abhängt, lustig
zwischen allen Moduln hin und hergehupft wird und die Lesbarkeit (und
damit auch Wartbarkeit) grottenschlecht ist. Aber das gibt's auch
anderswo, ich erinnere mich an die Quellen zu ANGEL (von ARM), wo es
ineinander geschachtelte #ifdef Blöcke gab, die sich über mehrere
Quelldateien hinzog.
Im Zweifelsfall hat man auf lange Sicht mehr davon, wenn man solch einen
Quellcode-Salat einfach mit der Keule raushaut und das Ganze komplett
selber (und besser) schreibt. Nebenbei gesagt hatte ich das auch mit der
Lernbetty so gemacht, weil der damals vorliegende "BOOP"-Code einfach
nur zu scheußlich war (aber auch weil dessen Autor sich mit seinem
Copyright seltsam krumm hatte).
W.S.
@Niklaus Gürtler:
Hmm, kann es sein dass LTO das Programm irgendwie "kaputtoptimiert" ?
Mit LTO:
1
$ arm-none-eabi-size bin/test_rom.elf
2
text data bss dec hex filename
3
48520 3340 24220 76080 12930 bin/test_rom.elf
ohne LTO:
1
$ arm-none-eabi-size bin/test_rom.elf
2
text data bss dec hex filename
3
1192 0 0 1192 4a8 bin/test_rom.elf
es ist zweimal exakt der selbe Code. Das einzige, was ich geändert
habe, war im Makefile:
CFLAGS += -lto
LDFLAGS += -lto
und dann 'make clean' und 'make'.
Tobias Plüss schrieb:> Hmm, kann es sein dass LTO das Programm irgendwie "kaputtoptimiert" ?
Funktioniert der Code denn noch?
Aber es ist sehr gut möglich... LTO löscht unbenutzte Funktionen, und
wenn es die main(), den ISR-Vector oder die ISR's selber als unbenutzt
erkennt verschwinden die und es bleibt nichts übrig. Daher den
ISR-Vector mal mit __attribute__((used)) versehen und die davon
referenzierten ISR's inklusive der indirekt referenzierten main()
sollten drin bleiben. Schau auch mal in die Disassembly was fehlt.
Ob es noch funktioniert konnte ich jetzt noch nicht verifizieren, habe
die HW gerade nicht zur Hand.
Was könnte der Grund sein, dass das selbe Programm mit LTO grösser
wird?
und zwar signifikant, siehe meinen Auszug von arm-none-eabi-size im
vorhergehenden Post.
Des Weiteren habe ich jetzt noch ein printf() eingefügt. Also in meiner
main.c einfach
#include <stdio.h>
und dann in der main():
printf("hello world\n");
mit LTO wird das ganze nicht compiliert. Der Fehler passiert anscheinen
beim Linken:
1
CC bin/test_rom.elf
2
`_sbrk_r' referenced in section `.text._malloc_trim_r' of ../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7e-m/fpu/libc.a(l
3
ib_a-freer.o): defined in discarded section `.text' of src/syscalls.c.o (symbol from plugin)
4
`_sbrk_r' referenced in section `.text._malloc_trim_r' of ../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7e-m/fpu/libc.a(l
5
ib_a-freer.o): defined in discarded section `.text' of src/syscalls.c.o (symbol from plugin)
6
`_sbrk_r' referenced in section `.text._malloc_trim_r' of ../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7e-m/fpu/libc.a(l
7
ib_a-freer.o): defined in discarded section `.text' of src/syscalls.c.o (symbol from plugin)
8
`_fstat_r' referenced in section `.text.__smakebuf_r' of ../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7e-m/fpu/libc.a(li
9
b_a-makebuf.o): defined in discarded section `.text' of src/syscalls.c.o (symbol from plugin)
10
`_isatty_r' referenced in section `.text.__smakebuf_r' of ../lib/gcc/arm-none-eabi/4.8.3/../../../../arm-none-eabi/lib/armv7e-m/fpu/libc.a(l
11
ib_a-makebuf.o): defined in discarded section `.text' of src/syscalls.c.o (symbol from plugin)
12
....
Und das füllt mir dann ca. zwei Bildschirmseiten :-)
Der selbe Code, ohne dass ein einziges Bit geändert wird, compiliert,
wenn ich beim compilieren und Linken das -flto weglasse.
Ob das beim ARM GCC wirklich richtig implementiert ist? Verdächtig ist
auf jeden Fall, dass das Binary mit LTO grösser wird und teilweise nicht
mehr compiliert.
Also, es wäre jetzt nicht so, dass ich das unbedingt dringend brauche,
ich habe kein Platzproblem bei meinem uC. Aber ich wollte LTO halt mal
ausprobieren, und dabei bin ich eben auf das eben gesagte gestossen :-)
Tobias Plüss schrieb:> Was könnte der Grund sein, dass das selbe Programm mit LTO grösser> wird?
Ohhps es wird größer, hab ich grad überlesen...
> Des Weiteren habe ich jetzt noch ein printf() eingefügt.> [...]> Der selbe Code, ohne dass ein einziges Bit geändert wird, compiliert,> wenn ich beim compilieren und Linken das -flto weglasse.
Das wundert mich jetzt etwas... Hast du denn sbrk, isatty etc.
implementiert? Wenn nein, was passiert in der Disassembly mit den
Aufrufen dieser Syscalls von malloc() etc. aus?
>> Ob das beim ARM GCC wirklich richtig implementiert ist? Verdächtig ist> auf jeden Fall, dass das Binary mit LTO grösser wird und teilweise nicht> mehr compiliert.
Ja, und es funktioniert super bei mir :-D Aber ich benutze auch keine
dynamische Speicherverwaltung mit malloc() und kein OS mit den Syscalls
sbrk() etc., vielleicht hapert es da etwas mit der libc.
Hallo,
nein, nein, ich habe weder ein OS noch irgendwelche dyn. Speicher!
Ich habe den Beispielcode
http://www.emb4fun.de/download/arm/examples/STM32F4-Discovery.zip
von hier
http://www.emb4fun.de/arm/examples/index.html
genommen. Als libc wird die vom GCC verwendet. (Da habe ich nichts dran
gedreht ;-) )
Wie sieht denn dein Beispielcode aus?
In der Disassembly kann ich leider nicht nachschauen, weil ja das ganze
nicht compiliert werden kann. Er bricht nach den 2 Seiten
Fehlermeldungen dann ab mit Error 2. (?)
Tobias Plüss schrieb:> Hmm, kann es sein dass LTO das Programm irgendwie "kaputtoptimiert" ?> CFLAGS += -lto> LDFLAGS += -lto
Du willst doch bestimmt auch optimieren, oder nicht? Ohne -O Schalter
wird nicht optimiert, d.h. es ist wie -O0!
Tobias Plüss schrieb:> Hallo,>> nein, nein, ich habe weder ein OS noch irgendwelche dyn. Speicher!> Ich habe den Beispielcode
Du verwendest aber das printf() aus der libc, und das will dynamischen
Speicher (malloc), und das will ein OS. Wenn du das nicht willst, musst
du ein alternatives printf() verwenden, das keinen dynamischen Speicher
braucht.
> Wie sieht denn dein Beispielcode aus?
Was fürn Beispielcode? Mein Code enthält jedenfalls kein printf()
> In der Disassembly kann ich leider nicht nachschauen, weil ja das ganze> nicht compiliert werden kann.
Ich dachte ohne LTO kompiliert es?
Ja, Optimierung habe ich wie folgt eingeschaltet:
-Os -O2 -falign-functions=16 -fno-inline -fomit-frame-pointer
Ohne LTO und mit printf() compiliert es.
Ohne LTO und ohne printf() compiliert es sowieso ;-)
Mit LTO und ohne printf() geht es auch.
Mit LTO und mit printf() geht es nicht.
(damit hätten wir wohl alle Varianten erschlagen ;-) )
Tobias Plüss schrieb:> -Os -O2 -falign-functions=16 -fno-inline -fomit-frame-pointer
-fno-inline, wozu das denn... Würde mich nicht wundern wenn das in
Kombination mit LTO komische Dinge bewirkt.
> Ohne LTO und mit printf() compiliert es.> Ohne LTO und ohne printf() compiliert es sowieso ;-)> Mit LTO und ohne printf() geht es auch.> Mit LTO und mit printf() geht es nicht.
Dann zeig doch mal die Disassembly von allen Varianten die
funktionieren, aber bitte bis auf ein Minimum reduziert, d.h. alles
rausnehmen bis auf das printf(), oder so.
Tobias Plüss schrieb:> Ja, Optimierung habe ich wie folgt eingeschaltet:>> -Os -O2 -falign-functions=16 -fno-inline -fomit-frame-pointer
Und die werden aucj beim Linken verwendet, ja? Denn mit LTO ist der
beim Compilieren erstellte Assembler-Code Makulatur. Der Compiler wird
mit den beim LTO-Link angegebenen Optionen erneut aufgerufen.
Hallo Niklas,
hier mal ein Beispiel.
Wenn du es compilierst mit
'make'
dann hagelt es Fehlermeldungen (wegen des -flto).
Dann habe ich aber auch noch mein 'originales' Makefile drin gelassen,
dort ist -flto nicht drin. Also:
'make -f Makefile_ohne_lto'
und es geht ;-)
und wenn du aus der src/main.c das printf() raus kommentierst, dann
siehst du auch, dass die angegebenen Grössen der Elf Datei grösser sind,
wenn mit -flto compiliert und gelinkt wird.
Gruss,Tobias
Von wegen
Tobias Plüss schrieb:> nein, nein, ich habe weder ein OS noch irgendwelche dyn. Speicher!> Ich habe den Beispielcode
In der src/syscalls.c sind Hilfs-Funktionen für dynamisches
Speichermanagement definiert, die so tun als wären sie ein OS, damit die
Standard-libc-Funktion malloc() funktioniert. LTO optimiert die
scheinbar weg, obwohl sie von der libc gebraucht werden. Einfügen von
am Anfang der Datei weist den GCC an, die Funktionen zu behalten; der
Code kann dann mit LTO compiliert werden. Allerdings wird er immer noch
258B größer dadurch. Vermutlich ist dieser Code einfach schlecht zu
optimieren...
Es sind aber noch stolze 25kB an Code da für ein leeres Programm;
Memory-Management, "Fake"-OS-Syscalls, "double"-Arithmetik, nur um
volles printf() zu haben. Sicher dass du so viel Platz dafür hergeben
willst und nicht doch auf volles printf() verzichten kannst?
Hi Niklas,
kann ich denn auf das ganze dynamische gedöns verzichten, wenn ich
printf() weglasse? dann brauche ich auch die syscalls.c nicht. Oder wie
machst du das bei dir?
Niklas Gürtler schrieb:> Vermutlich ist dieser Code einfach schlecht zu optimieren...
Das "-Os -O2" optimiert nicht auf Größe, denn -O2 steht hinter -Os und
überschreibt es daher.
Tobias Plüss schrieb:> Hi Niklas,> kann ich denn auf das ganze dynamische gedöns verzichten, wenn ich> printf() weglasse?
Ja. Oder ein alternatives sparsames printf() verwenden, das z.B. keinen
double-Support enthält (float ist ja dank FPU effizient).
>dann brauche ich auch die syscalls.c nicht. Oder wie> machst du das bei dir?
Genau. Ohne printf() brauchts kein malloc(), ohne malloc() kein sbrk
etc.
Ich hab bis jetzt keine solche String-Verarbeitung gebraucht... Aber dem
Gefühl nach würde ich Strings fix in .data allokieren und mit itoa etc.
da hineinschreiben; Code-aufwändiger aber sparsamer.
Johann L. schrieb:> Das "-Os -O2" optimiert nicht auf Größe, denn -O2 steht hinter -Os und> überschreibt es daher.
Stimmt... Im makefile stand noch "-O0". "-Os" hat ganze 300 bytes
gebracht.
Johann L. schrieb:> Wenn immer noch ohne -O gelinkt wird ists auch kein Wunder wenn -flto> nichtmehr bringt im Vergleich zu -O0.
Stimmt, mit -Os linken bringt nochmal ganze 300 bytes. Damit sinds sogar
weniger als ohne LTO. Woah :o)
@Niklas Gürtler
Du sagst, printf() und Konsorten sollte man nicht verwenden.
Nun, ich habe jetzt hier eine Anwendung, wo ich ein einfaches Terminal
userinterface realisieren will. Da mein String Output über verschiedene
Kanäle gehen kann, muss ich zuerst die Strings, die ausgegeben werden
sollen, fixfertig formatieren, und erst dann ausgeben.
Printf() habe ich daher weggelassen, und ich formatiere jetzt meine
Strings mit sprintf(). Allerdings wird auch hier ein malloc() eingebaut
:-) Wie kann ich einen String formatieren, ohne malloc() ?
Also, wie kann ich das
einigermassen schön umformulieren?
Um die eigentliche Ausgabe zu ralisiern, habe ich dann eine
Callbackfunction erstellt, die einen String aufnehmen kann, und die dann
über einen entsprechenden Kanal den String ausgibt. Der Kanal kann UART
oder Ethernet sein, durch einfaches Ändern der Callbackfunction kann ich
so versch. Medien benutzen, aber eben, mit sprintf, und dort hats ein
malloc() drin... :-(
Linda schrieb:> @Niklas Gürtler>> Du sagst, printf() und Konsorten sollte man nicht verwenden.
Naja "sollte" ist relativ; man sollte es sich jedenfalls gut überlegen.
> Also, wie kann ich das
>> einigermassen schön umformulieren?
Naja, was macht das denn? Einen Integer dezimal formatieren und Strings
zusammenfügen. Ich würde zu sowas tendieren:
1
staticBuffer[1024];// angemessene größe vorher anhand des maximums der Teilstrings berechnen
Ist wohl ziemlich hässlich. Dafür entfällt der Overhead für das Parsen
eines Format-Strings. Ich glaube ich programmiere mir mal eine schöne
C++ Template Library um die Generation solchen Codes zu
automatisieren...
Du könntest auch eine sprintf Implementation suchen welche ohne malloc()
auskommt.
schon besser, auch wenns nicht gut ist wegen malloc(). Aber ich glaub,
ich bau mir da mein sprintf() selber, sofern man nicht unheimlich
komplexe formatierungen braucht sondern nur paar Strings aneinander
hängt, geht das ja noch einfach.
Aber wozu braucht printf() oder snprintf() überhaupt malloc() ? auch
puts() braucht malloc, und das leuchtet mir wirklich nicht ein.
Linda schrieb:> Printf() habe ich daher weggelassen, und ich formatiere jetzt meine> Strings mit sprintf().
Na Klasse, den Teufel mit dem Beelzebub ausgetrieben! Gratulation!
Linda schrieb:> Da mein String Output über verschiedene> Kanäle gehen kann, muss ich zuerst die Strings, die ausgegeben werden> sollen, fixfertig formatieren, und erst dann ausgeben.> sprintf(buffer, "Command entered: %s; return code: %d.\n", my_cmd, ret_val);
Das mußt du garnicht. Niemand muß sowas. Ich würde das schlichtweg so
etwa schreiben (benutzte Funktionen siehe Lernbetty):
word wohin;
wohin = toUART;
String_Out("Command entered: ", wohin);
String_Out(my_cmd, wohin);
String_Out(" return code: ", wohin);
Dezi_Out (ret_val, 1, wohin);
String_Out(".\r\n", wohin);
Und das wär's. Ohne printf, ohne malloc, ohne strings.h, ohne elenden
Overhead. OK, String_Out und Dezi_Out beruhen auf Char_Out(char aChar,
word toWhere) und das muß selber geschrieben werden. aber nen String_Out
und nen Dezi_Out(long aDez, int mindigits, word toWhere) sollte man ja
so ziemlich aus dem Ärmel schütteln oder aus der Lernbetty abschreiben
;-)
Linda, versuche doch endlich mal die geistige Kraft aufzubringen, dich
von vorgekauten Dingen zu lösen und ein Problem aus eigener Kraft zu
erledigen.
W.S.
W.S. schrieb:> Und das wär's. Ohne printf, ohne malloc, ohne strings.h, ohne elenden> Overhead. OK, String_Out und Dezi_Out beruhen auf Char_Out(char aChar,> word toWhere) und das muß selber geschrieben werden.
Und was machen diese Funktionen? Die String-Daten direkt über den
Ausgabe-Kanal, also UART oder so verschicken? Das will sie aber nicht:
Linda schrieb:> muss ich zuerst die Strings, die ausgegeben werden> sollen, fixfertig formatieren, und erst dann ausgeben.
Und so einen String komplett zusammenbauen können deine Funktionen
nicht. Außerdem kann der Ausgabekanal (zB UART) langsam sein; deine
Funktionen müssten dann für jedes Zeichen warten bis es weg ist, sodass
man im Endeffekt viel Zeit mit Warten verschwendet während man auch die
Formatierung schnell mit einem Puffer hätte abhandeln können und dann
asynchron senden können.
Ich hab vor Jahren den "Ubergang gewagt von Standardzeugs zu einer
selbergebastelten FIFO-Struktur und mit der mach ich
- IO, ohne Race-Conditions!
- Formatierung
- Parsen
Ja, der FIFO braucht paar Felder, ich verschwende 6 Integer/Pointer =
24Bytes. Aber damit sind halt soooo viel Probleme ein f"ur allemal
gel"ost. Und sp"atestens parsen ist ein heftiges Problem f"ur viele in
dem Thread, wenn schon 'Ausgeben' schwer sein soll.
Ich bin von dem Fifo bis heute nicht mehr weggekommen, im Gegenteil. Bei
mir bekommen alle wichtigen Strukturen gleich die fifoPrintXXX und
fifoParseXXX Funktionen, so wie man das bei C++ mit den Operatoren <<
und >> tun w"urde.
Alles was C 'Standard' ist, darf man als Neuling ohne schlechtes
Gewissen ungesehen in die Tonne treten. Besser man hat so 'nen R*tz im
Leben nie angefasst.
Niklas Gürtler schrieb:> Und was machen diese Funktionen? Die String-Daten direkt über den> Ausgabe-Kanal, also UART oder so verschicken? Das will sie aber nicht:
Lerne doch erstmal verstehendes Lesen:
Linda schrieb:> Nun, ich habe jetzt hier eine Anwendung, wo ich ein einfaches Terminal> userinterface realisieren will. Da mein String Output über verschiedene> Kanäle gehen kann, muss ich zuerst die Strings, die ausgegeben werden> sollen, fixfertig formatieren, und erst dann ausgeben.
Also Linda meint, da ihr Stringoutput verschiedene Kanäle bedienen kann,
müßte man zuvor die Strings komplett aufbauen und erst dann senden. Das
ist Unsinn oder falsch dargestellt - zumal wenn es sich um ein
§einfaches Terminal-Useinterface" sprich Kommandozeile handelt.
Deine Frage danach, was die Funktionen machen ist leicht beantwortet:
Sie machen das, wofür sie da sind, nämlich Texte oder eben Zahlen als
Texte in den jeweils bezeichneten Ausgabekanal bzw. Stream zu senden.
Ein Zusammensetzen zu einem Gesamtstring ist dabei völlig unnötig, kann
aber gemacht werden (toString) falls das wirklich benötigt wird. Du
hast ungefragt unterstellt, daß es sich bei dem Ausgabekanal immer nur
um einen UART handelt und das ist falsch. Es kann sich je nach Bedarf um
einen UART, eine Datei, einen Screenbuffer, einen Drucker oder sonstwas
handeln, Hauptsache es ist kein ungepuffertes Blockdevice, auf das man
bekanntlich nur blockweise schreiben kann. Ich habe hier das "toUART"
nur als relativ übliches Beispiel genommen.
Also erst lesen, dann verstehen, dann erst ans Nörgeln denken.
W.S.
Marc P. schrieb:> Ich hab vor Jahren den "Ubergang gewagt von Standardzeugs zu einer> selbergebastelten FIFO-Struktur und mit der mach ich..
Das ist auch mit Abstand das Beste. Ich benutze bei meinen Projekten für
so ziemlich alle I/O auch gepufferte Kanäle. Damit sind die Ausgaben
ruckzuck erledigt, blockieren nur dann, wenn auf größere Datenmengen
halt gewartet werden MUSS und die eigentliche Transmission erfolgt
transparent per Interrupts o.ä.
W.S.
W.S. schrieb:> Lerne doch erstmal verstehendes Lesen:
Musst du gerade sagen, der jedem bei jeder Frage sofort dein komisches
Projekt unter die Nase hälst, das vielleicht als Anti-Beispiel (wie man
es nicht macht) taugt.
W.S. schrieb:> Das> ist Unsinn oder falsch dargestellt - zumal wenn es sich um ein> §einfaches Terminal-Useinterface" sprich Kommandozeile handelt.> Ein Zusammensetzen zu einem Gesamtstring ist dabei völlig unnötig
Und was wenn das Programm noch was anderes vorhat als Ewigkeiten auf ein
langsames UART oder so zu warten?
> kann aber gemacht werden (toString) falls das wirklich benötigt wird.
Und das braucht malloc?
W.S. schrieb:> Hauptsache es ist kein ungepuffertes Blockdevice,
Tja, schöne Einschränkung. Schön wäre ja eine allgemeingültige Lösung
Strings zu formatieren, die vom Ziel-Device unabhängig ist?!
W.S. schrieb:> Also erst lesen, dann verstehen, dann erst ans Nörgeln denken.
Bei dir ist nörgeln nie verkehrt.
W.S. schrieb:> Ich benutze bei meinen Projekten für> so ziemlich alle I/O auch gepufferte Kanäle.
Also mit Puffer so wie ich mit itoa etc. geschrieben hatte? Cool.
OK, herumnörgeln kannst du gut, aber das verstehende Lesen funktioniert
immer noch nicht.
Weißt du was? Mach doch mal ne Version von der Lernbetty nach deinen
Vorstellungen. Einfach darum, um hier in diesem Forum mal was
Produktives beizutragen. Vielleicht ist deine Variante viel grandioser
als alles zuvor dagewesene. Also mach mal - aber nicht nur mit dem
Mundwerk.
W.S.
W.S. schrieb:> Weißt du was? Mach doch mal ne Version von der Lernbetty nach deinen> Vorstellungen. Einfach darum, um hier in diesem Forum mal was> Produktives beizutragen. Vielleicht ist deine Variante viel grandioser> als alles zuvor dagewesene. Also mach mal - aber nicht nur mit dem> Mundwerk.
Sowas in der Art ist in Arbeit, und es ist (wird) hinreichend grandios.
Dann kannst du dir mal ansehen wie man "echt" wiederverwendbar & sauber
programmiert.
Dr. Sommer schrieb:>> Also mach mal - aber nicht nur mit dem Mundwerk.> Sowas in der Art ist in Arbeit, und es ist (wird) hinreichend grandios.> Dann kannst du dir mal ansehen wie man "echt" wiederverwendbar & sauber> programmiert.
Da hat man dich ganz richtig eingeschätzt: Große Klappe, nichts
dahinter.
Dr. Sommer schrieb:> Es gibt auch Leute die haben noch ein Leben außerhalb von> mikrocontroller.net. Trollen kann jeder.
Du bist erkannt worden, also nicht ablenken, es geht um deine große
Klappe. Man lese sich nur deinen Beitrag, der nur so von Überheblichkeit
strotzt.
Ludwig schrieb:> Man lese sich nur deinen Beitrag, der nur so von Überheblichkeit> strotzt.
Der ist für W.S., der hat das nötig, wie man leicht erkennt wenn man
seine penetranten fehlinformierenden predigenden Beiträge im Forum
sucht. Falls du identisch zu W.S. bist - schön dass dir deine eigene
Medizin schmeckt.
Neugieriger Leser schrieb:> Und? Ist es schon fertig? ;)
rotfl.. natürlich nicht. Wie denn auch?
Du solltest keinerlei konkrete Wirkleistung von diesem Autor erwarten.
Der Bursche besteht zu 100% aus Scheinleistung.
W.S.