Forum: Compiler & IDEs STM32 Kompilat größer als erwartet


von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Ich würde mich sehr freuen, wenn mir jemand dabei helfen kann, 
herausfinden, warum das angehängte Projekt ein so großes Kompilat 
erzeugt.

Ich habe das Projekt mit der "System Workbench for STM32" erstellt. Beim 
Compilieren erhalte ich folgende Ausgabe:
1
make -j8 all 
2
Building file: ../startup/startup_stm32.s
3
Building file: ../startup/sysmem.c
4
Building file: ../src/main.c
5
Invoking: MCU GCC Assembler
6
Invoking: MCU GCC Compiler
7
Invoking: MCU GCC Compiler
8
/home/stefan/Programmierung/STM32_Workbench/Blinker/Release
9
/home/stefan/Programmierung/STM32_Workbench/Blinker/Release
10
arm-none-eabi-as -mcpu=cortex-m3 -mthumb -mfloat-abi=soft -o "startup/startup_stm32.o" "../startup/startup_stm32.s"
11
/home/stefan/Programmierung/STM32_Workbench/Blinker/Release
12
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -mfloat-abi=soft -DSTM32 -DSTM32F1 -DSTM32F103C8Tx -I"/home/stefan/Programmierung/STM32_Workbench/Blinker/CMSIS/core" -I"/home/stefan/Programmierung/STM32_Workbench/Blinker/CMSIS/device" -O2 -Wall -fmessage-length=0 -ffunction-sections -c -MMD -MP -MF"startup/sysmem.d" -MT"startup/sysmem.o" -o "startup/sysmem.o" "../startup/sysmem.c"
13
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -mfloat-abi=soft -DSTM32 -DSTM32F1 -DSTM32F103C8Tx -I"/home/stefan/Programmierung/STM32_Workbench/Blinker/CMSIS/core" -I"/home/stefan/Programmierung/STM32_Workbench/Blinker/CMSIS/device" -O2 -Wall -fmessage-length=0 -ffunction-sections -c -MMD -MP -MF"src/main.d" -MT"src/main.o" -o "src/main.o" "../src/main.c"
14
Finished building: ../startup/startup_stm32.s
15
 
16
Finished building: ../startup/sysmem.c
17
 
18
Finished building: ../src/main.c
19
20
Building target: Blinker.elf
21
Invoking: MCU GCC Linker
22
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -mfloat-abi=soft -specs=nano.specs -specs=nosys.specs -T"/home/stefan/Programmierung/STM32_Workbench/Blinker/LinkerScript.ld" -Wl,-Map=output.map -Wl,--gc-sections -o "Blinker.elf" @"objects.list"   -lm
23
Finished building target: Blinker.elf
24
 
25
make --no-print-directory post-build
26
Generating binary and Printing size information:
27
arm-none-eabi-objcopy -O binary "Blinker.elf" "Blinker.bin"
28
arm-none-eabi-size -B "Blinker.elf"
29
   text     data      bss      dec      hex  filename
30
   3460      112     1072     4644     1224  Blinker.elf

Vermutlich fehlen mir irgendwelche Compiler oder Linker Optionen, aber 
welche? Dazu habe ich wohl noch zu wenig Erfahrung, denn alle meine 
Versuche haben die Größe kaum verändert.

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe mal das map File einzeln angehängt, vielleicht hilft das.

von Stefan F. (Gast)


Lesenswert?

Ich bin so ein Esel, ich habe ganz vergessen zu schreiben, wieso ich 
glaube dass der Code zu groß ist. Also:

Wenn ich die Zeile
> puts("Hello World!")
auskommentiere, verringert sich .text von etwa 3,5kB auf weniger als 1kB 
und .data verringert sich von 112 auf 12.

Der Text ist aber viel kleiner als 100 Bytes und ich kann mir kaum 
vorstellen, dass puts() hier satte 2,5KB Code benötigt.m Ich hätte hier 
höchstens 200 Bytes Code erwartet.

Ist meine Erwartung falsch oder fehlt irgendein Setting?

von Markus F. (mfro)


Lesenswert?

Stefan U. schrieb:
> Ist meine Erwartung falsch oder fehlt irgendein Setting?

mit dem Einbinden von puts() kommen nicht nur ein paar Bytes, sondern 
gleich ein Großteil der C-Library mit.

Wenn Du die brauchst, ist das durchaus in Ordnung (bei zunehmender 
Nutzung von Funktionen aus der Library kommt von dort nur noch 
unproportional Zuwachs).

Wenn Du sie nicht brauchst (weil Du tatsächlich nur ein paar Strings 
ausgeben möchtest), schreib' dir die Routine einfach (ohne Overhead) 
selbst.

von Stefan F. (Gast)


Lesenswert?

Danke für Deine Hilfe. Das beantwortet meine Frage.

Falls der große Anhang im Eröffnungsbeitrag zu viel Platz braucht möge 
ein Moderator ihn bitte löschen. Es scheint, dass er nicht benötigt 
wurde.

von 900ss (900ss)


Lesenswert?

Markus F. schrieb:
> mit dem Einbinden von puts() kommen nicht nur ein paar Bytes, sondern
> gleich ein Großteil der C-Library mit

EIn Großteil ist wohl etwas übertrieben, aber es kommt schon daher. Es 
wird nicht nur der Code für pus() eingebunden.

Du kannst das herausfinden, indem du den Mapfile ohne puts() mit dem 
Mapfile mit eingebundendem puts() vergleichst. Da kannst du erkennen, 
was alles eingebunden wird, wenn du puts() benutzt.

von Nachtrag (Gast)


Lesenswert?

Markus F. schrieb:
> mit dem Einbinden von puts() kommen nicht nur ein paar Bytes, sondern
> gleich ein Großteil der C-Library mit

Und das ist bei jedem so, nicht nur bei ARM, sondern auch beim Spielzeug 
AVR.

von A. B. (Gast)


Lesenswert?

Wie wär's mit "-O2", "-flto" statt "-O2" ?

von A. B. (Gast)


Lesenswert?

Ach ja, und "-Os", "-flto" könnte auch noch einen Versuch Wert sein ...

Beitrag #5354779 wurde von einem Moderator gelöscht.
von Stefan F. (Gast)


Lesenswert?

-flto fürt zur Fehlermeldung: undefined reference to `_write'

-Os anstelle von -O2 macht das Programm nur wenige Bytes kleiner.

fno-rtti -fno-exceptions bringt auch fast nichts.

Im map File sehe ich eine Menge Funktionen, die ich normalerweise in 
Kombination mit Files verwende (lseek, flush, errno) und ein par Sachen 
die wohl mit dem Puffer zusammenhängen (den ich am liebsten weglassen 
würde). Ich denke das stimmt schon so, dass das alles Abhängigkeiten 
sind, die puts() mit sich bringt.

Die 2kB bringen mich jetzt nicht um, aber ich werde das im Hinterkopf 
behalten. Sicher ist das auch für die Performance relevant. Das Ausgeben 
von Trace Meldungen mit Hilfe von puts() scheint mir jetzt jedenfalls 
nicht mehr so attraktiv, wie zuvor. Das programmiere ich doch lieber 
selbst eine kleine for-Schleife.

von Pete K. (pete77)


Lesenswert?

Stefan U. schrieb:
> Sicher ist das auch für die Performance relevant.

Was hat die Größe des Codes mit der Performance zu tun?

von 900ss (900ss)


Lesenswert?

Stefan U. schrieb:
> Das programmiere ich doch lieber selbst eine kleine for-Schleife.

Hmmmm..... Ist dein Flash zu 99% voll?
Das lohnt doch sonst garnicht. Ein STM32 hat doch genug Flash.

von Och nöö (Gast)


Lesenswert?

Stefan U. schrieb:
> Sicher ist das auch für die Performance relevant.

Ja .... das ist wahrhaftig schwarze Magie!

Ich würde mich von solch abstrusen Esoterik-Code nicht
ausbremsen lassen.

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


Lesenswert?

Man kann sich auch ein kleines mini printf für debugzwecke selberhäkeln.
Das kann dann eben keine Formatierung und nur string, char, pointer, hex 
und int Ausgabe.
Wie das geht, siehe hier:
https://linux.die.net/man/3/stdarg

Dieses printf bekommt dann entweder ein UART reingeworfen oder nutzt die 
Tracecell per SWO Pin.
Codebeispiel:
https://www.segger.com/products/debug-probes/j-link/tools/j-link-swo-viewer/

von A. B. (Gast)


Lesenswert?

Stefan U. schrieb:
> -flto fürt zur Fehlermeldung: undefined reference to `_write'

Da wird _write wahrscheinlich nie explizit verwendet, sondern immer nur 
über einen Funktionspointer in der Tabelle mit den File-Operationen. 
Dann denkt sich der Compiler, ach eh' nie verwendet, also weg damit ...
Einfach das _write einmal explizit verwenden sollte helfen.

Das kommt gelegentlich vor, bei ST findet sich in den Demos mal so eine 
Mini-Datei:

#include "FreeRTOS.h"
#include "task.h"

void dummyFunction(void) __attribute__((used));

// Never called.
void dummyFunction(void) {
    vTaskSwitchContext();
}

Da war's beim vTaskSwitchContext() das gleich Problem wie beim _write.

von W.S. (Gast)


Lesenswert?

Stefan U. schrieb:
> Die 2kB bringen mich jetzt nicht um, aber ich werde das im Hinterkopf
> behalten.

Ich nehme mal an, daß du jetzt eines gelernt hast: Wer sich auf einem 
kleinen µC programmiertechnisch genauso benimmt wie auf dem PC, der muß 
eben auch damit rechnen, daß entsprechende Laufzeit-Bibliotheken und 
Ersatz-Routinen für das, was auf dem PC das Betriebssystem bietet, mit 
eingebunden werden müssen.

Das kostet dann eben Speicherplatz und es kostet Performance. Und (nicht 
zu vergessen) man muß in vielen Fällen den eigentlichen Lowlevel-Treiber 
selber dazuliefern, der sich natürlich in das Interface der 
eingebundenen Bibliotheken einpassen muß.

Mir wäre das alles viel zu stressig, weswegen ich für meine Bedürfnisse 
auf sowas wie puts oder printf generell pfeife, und mir meine eigenen, 
an die Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben 
habe.

Mittlerweile hab ich davon ja hier genug gepostet. Allerdings muß man 
dann eben nicht puts hinschreiben, sondern z.B. String_Out(...). Ob das 
dem einen oder anderen unerträglich ist, muß jeder für sich selber 
entscheiden.

W.S.

Beitrag #5355315 wurde von einem Moderator gelöscht.
von Stefan F. (Gast)


Lesenswert?

> Was hat die Größe des Codes mit der Performance zu tun?

Viel Code benötigt viele CPU Takte. Die ungenutzten Funktionen sind ja 
schon weg optimiert.

> Ich nehme mal an, dass du jetzt eines gelernt hast...

Im Prinzip hast du Recht. Ich habe hier allerdings gestaunt, weil die 
puts() Funktion bei AVR samt aller Abhängigkeiten weniger als 500 Bytes 
umfasst.

von Bauform B. (bauformb)


Lesenswert?

W.S. schrieb:
> Mir wäre das alles viel zu stressig, weswegen ich für meine Bedürfnisse
> auf sowas wie puts oder printf generell pfeife, und mir meine eigenen,
> an die Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben
> habe.
>
> Mittlerweile hab ich davon ja hier genug gepostet. Allerdings muß man
> dann eben nicht puts hinschreiben, sondern z.B. String_Out(...). Ob das
> dem einen oder anderen unerträglich ist, muß jeder für sich selber
> entscheiden.

Es ist unerträglich. Deshalb habe ich "mir meine eigenen, an die 
Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben". Nur 
heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht 
dagegen?

von Dr. Sommer (Gast)


Lesenswert?

Bauform B. schrieb:
> Nur
> heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht
> dagegen?
Dass man im Anwendungs-Code keine Bezeichner verwenden sollte, die für 
die C-Standardbibliothek reserviert sind. Das gibt ein Chaos wenn jemand 
zusätzlich den Standard-Header <stdio.h> inkludiert. Das kann man nur 
machen wenn die C Standard Library garantiert nie mit gelinkt und 
inkludiert wird. Deutlich einfacher (und standardkonform!) ist es, die 
Funktion einfach anders zu benennen (my_printf o.ä.). Das tut auch nicht 
weh.

Stefan U. schrieb:
> Im Prinzip hast du Recht. Ich habe hier allerdings gestaunt, weil die
> puts() Funktion bei AVR samt aller Abhängigkeiten weniger als 500 Bytes
> umfasst.
Die AVR-Libc ist nunmal genau auf die Nutzung ohne OS auf 
Mini-Controllern abgestimmt. Die vom ARM-GCC genutzte newlib ist mehr 
für Unixoide Systeme gemacht und daher nicht immer optimal für kleine 
Controller. Alles was mit FILE* arbeitet (fwrite, fprintf, fputs) 
braucht Puffer und dafür malloc().

von Ralph S. (jjflash)


Lesenswert?

Bauform B. schrieb:
> Es ist unerträglich. Deshalb habe ich "mir meine eigenen, an die
> Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben". Nur
> heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht
> dagegen?

... bei mir heißen die auch so und deswegen ich sehr häufig um stdio.h 
einen großen Bogen und um string.h einen kleineren...

von Ralph S. (jjflash)


Lesenswert?

Dr. Sommer schrieb:
> Deutlich einfacher (und standardkonform!) ist es, die
> Funktion einfach anders zu benennen (my_printf o.ä.). Das tut auch nicht
> weh.

: - )

Ich muß jetzt schmunzeln, weil es bei mir genau

my_printf

heißt und ich hierfür unterschiedlich abgespeckte printf Funktionen 
habe, die alle in der Main-Datei eine Funktion

my_putchar

benötigen (die von my_printf aufgerufen wird).

Je nach verwendetem Controller gibts dann von printf Versionen ohne 
Kommaausgabe, eine mit Festkomma und eine mit Fließkomma.

Somit kann man dann sogar noch relativ komfortabel Ausgaben selbst auf 
einem STM32F030 mit nur 16 kByte vornehmen.

von Bauform B. (bauformb)


Lesenswert?

Dr. Sommer schrieb:
> Bauform B. schrieb:
>> Nur
>> heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht
>> dagegen?
> Dass man im Anwendungs-Code keine Bezeichner verwenden sollte, die für
> die C-Standardbibliothek reserviert sind. Das gibt ein Chaos wenn jemand
> zusätzlich den Standard-Header <stdio.h> inkludiert.

Wie reserviert sind die wirklich? Warum weiß ich das nicht? Aber davon 
abgesehen dürfte es kein Chaos geben, wenn man dabei meine Header-Datei 
nicht inkludiert. So kompatibel sollten die eigenen Funktionen 
gefälligst sein.

Selbst wenn man die eigenen Funktionen und die newlib dazu linkt, 
müsste es eigentlich noch funktionieren. printf ist dann schon definiert 
und dürfte nicht aus der newlib geholt werden.

Aber eigentlich ist es viel einfacher: die C-Standardbibliothek kann 
ja glibc, dietlibc oder newlib heißen. Genauso gut kann man eine eigene 
verwenden.

> Alles was mit FILE* arbeitet (fwrite, fprintf, fputs)
> braucht Puffer und dafür malloc().

Eine eigene libc könnte statische Puffer pro physikalisch vorhandener 
Schnittstelle haben. Andere Kanäle zu öffnen gibt so oder so einen 
Fehler.

von Dr. Sommer (Gast)


Lesenswert?

Bauform B. schrieb:
> Wie reserviert sind die wirklich?
Aus dem C-Standard:
1
All identifiers with external linkage in any of the following subclauses (including the future library directions) and errno are always  reserved for use as identifiers with external linkage.
2
3
If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.
D.h. ohne "static" darf man keine eigenen Bezeichner namens "printf" 
o.ä. definieren, sonst passiert irgendwas. Selbst mit "static" ists 
problematisch, falls man mal versehentlich/indirekt stdio.h inkludiert.

Bauform B. schrieb:
> Warum weiß ich das nicht?
Standard nicht gelesen?

Bauform B. schrieb:
> Selbst wenn man die eigenen Funktionen und die newlib dazu linkt,
> müsste es eigentlich noch funktionieren. printf ist dann schon definiert
> und dürfte nicht aus der newlib geholt werden.
Obiges Zitat verbietet dies.

Bauform B. schrieb:
> Genauso gut kann man eine eigene
> verwenden.
Ja, da darf man dann alles machen. Ist dann halt Standard-Library-Code 
und nicht Anwendungs-Code.

Ich verstehe auch den Nutzen nicht, warum man unbedingt eine Funktion 
namens printf definieren muss - man nenne sie einfach my_printf oder so, 
und alles ist gut. Warum lange überlegen ob und wann "printf" erlaubt 
ist, wenn "my_printf" definitiv erlaubt ist und funktioniert...

von Bauform B. (bauformb)


Lesenswert?

Dr. Sommer schrieb:
> Ich verstehe auch den Nutzen nicht, warum man unbedingt eine Funktion
> namens printf definieren muss - man nenne sie einfach my_printf oder so,
> und alles ist gut.

Wenn man das Programm liest, weiß man sofort was printf macht. Bei 
my_printf wird man evt. nachschauen. Das finde ich schon nützlich.

von Dr. Sommer (Gast)


Lesenswert?

Na, aber du willst ja gar nicht das was printf macht, sonst hättest du 
ja einfach das genommen!

von Bauform B. (bauformb)


Lesenswert?

???
1
PRINTF(3)
2
3
printf,   fprintf,   dprintf,  sprintf,  snprintf,  vprintf,  vfprintf,
4
vdprintf, vsprintf, vsnprintf - formatted output conversion
1
The functions printf() and vprintf() write
2
output to stdout, the standard output stream;

Und genau das möchte ich mit meinem printf auch machen. Wo stdout 
letztlich endet ist ja ein ganz anderes Thema.

von Dr. Sommer (Gast)


Lesenswert?

Bauform B. schrieb:
> Wo stdout
> letztlich endet ist ja ein ganz anderes Thema.
Dann überschreibe doch den _write "Syscall" Stub der libc, um stdout 
umzuleiten:
https://www.mikrocontroller.net/articles/STM32_Eclipse_JLink_Linux/Windows#Optional:_Syscalls_implementieren
Dann kannst du ganz normales printf nutzen und die Ausgabe erscheint auf 
SWO, UART, oder was auch immer.

von Stefan F. (Gast)


Lesenswert?

> Dann überschreibe doch den _write "Syscall" Stub der libc, um stdout
> umzuleiten

Und schon drehen wir uns im Kreis, denn genau damit hat derb Thread 
angefangen.

Dann kommt man auf über 2,5kB Code-Größe für puts() (+dependencies), 
während es beim avr-gcc nur etwa 500 bytes kostet.

von Stefan F. (Gast)


Lesenswert?

Ich habe gemessen, wie viel Speicher die Funktionen puts() und printf() 
bei der newlib-nano auf einem STM32F103 ohne Fließkomma-Unterstützung 
benötigen:
1
Funktion            Code (Flash)   Statische Daten   Heap (malloc)   Stack
2
===========================================================================
3
puts(char*)         2184           0                 1468 oder 436   112
4
5
printf(char*)       2204           0                 1468 oder 436   112
6
7
printf(char*,args)  3572           0                 1468 oder 436   304
8
9
puts(char*) + 
10
printf(char*,args)  3572           0                 1468 oder 436   304
Der Speicherbedarf von printf() ist unabhängig von der Anzahl der 
Argumente und Formatier-Optionen. Wenn weniger als 1468 Bytes Heap zur 
Verfügung stehen, belegt die Library stattdessen nur 436 Bytes und gibt 
dann jedes Zeichen einzeln mit _write() aus. Wenn weniger als 436 Bytes 
Heap zur Verfügung stehen brechen die Funktionen mit einer HardFault 
Exception ab.

Verglichen mit der avr-libc ist das hammerhart viel, ich bin echt 
geschockt! Dabei ist die newlib-nano doch schon speziell für µC 
optimiert. Ichtraue mich gar nicht, die selbe Messung nochmal mit der 
"großen" newlib zu wiederholen.

von Johannes S. (Gast)


Lesenswert?

Ich hatte auf einem M0 das printf mit Integer durch ein vereinfachtes 
itoa ersetzt, das hat immer noch ca. 2 kB statt 3,etwas gebraucht. Die 
Integer Division scheint viel zu kosten, wobei der M3 das ja schon in HW 
kann.
Wäre interessant zu sehen was der Keil daraus macht, der soll ja besser 
optimieren.

Die Newlib haut richtig rein, ist dafür Thread safe wenn es in Richtung 
RTOS geht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Stefan U. schrieb:
> ich bin echt geschockt!

So ein STM32F103 hat 20 KB RAM. Wenn Du nicht bereit bist, 1,5KB davon 
abzuzweigen, hast Du 2 Möglichkeiten:

1. Auf puts/printf verzichten

2. Den offensichtlich satten Speicherverbrauch Deiner Applikation
   der verbliebenen 18 KB zu optimieren.

Dein Programm braucht keine 18 KB? Dann ist's auch kein Problem.

: Bearbeitet durch Moderator
von 900ss (900ss)


Lesenswert?

Frank M. schrieb:
> Dein Programm braucht keine 18 KB? Dann ist's auch kein Problem.

Sehe ich auch so. Für nicht verbrauchten Speicher gibt's kein Geld 
zurück ;)

Man muss nicht mit Speicher rumasen aber wenn er eh da ist, fange ich an 
mir Gedanken zu machen wenn es kneift und nicht sehr viel früher.

von Markus F. (mfro)


Lesenswert?

Stefan U. schrieb:
> hammerhart viel, ich bin echt
> geschockt!

wem das nicht passt, der muss eben sein xprintf() (oder was auch immer) 
selber schreiben. Wenn man auf ein paar exotische Formatierungen (wie %n 
oder %a, z.B.), die sowieso keiner braucht, verzichtet, geht das in 
weniger als 1500 Bytes.

von Johannes S. (Gast)


Lesenswert?

900ss D. schrieb:
> Man muss nicht mit Speicher rumasen aber wenn er eh da ist, fange ich an
> mir Gedanken zu machen wenn es kneift und nicht sehr viel früher.

Ack, trotzdem ist es gut zu wissen wieviel etwas kostet. Beim Vergleich 
mit AVR sollte man sehen das der AVR viel in einem Byte erledigen kann 
und der ARM da zwei braucht. Aber dafür gibts dann auch devices mit bis 
zu 2 MByte Flash.

von Matthias (Gast)


Lesenswert?

>"-Os", "-flto"
Das sind nur Compiler-Optionen und werden bei dem betreffenden Problem 
nicht helfen, da die Lib durch den Linker eingebunden wird. Also nach 
dem kompillieren des Codes.

Allerdings gibt es für den Linker auch Optimierungs-Optionen. Eine davon 
nennt sich glauch "garbage collect unused ....", die entfernt alle nicht 
verwendeten Symbole aus dem Object Code. Damit sollten alle nicht 
verwendeten Funktionen der Lib nicht in den Programmcode übernommen 
werden.

Die obige Info gilt nur für GCC. Andere Compiler/Linker dürften aber 
ähnliche Optionen haben.

Wobei 2K für ein puts() schon ordentlich ist. Evtl. kann ein Blick in 
die Lib-Sourcen (falls verfügbar) nicht schaden. Ich vermute mal, dass 
die puts()auch nur putchar() in einer Schleife aufruft. Da sollten 
eigentlich keine 100 Funktionen verschachtelt sein ;-)

Beim printf() sieht das schon etwas anders aus, wegen der versch. 
Subroutinen für die Zahlenkonvertierung.

von Dr. Sommer (Gast)


Lesenswert?

Matthias schrieb:
> Wobei 2K für ein puts() schon ordentlich ist. Evtl. kann ein Blick in
> die Lib-Sourcen (falls verfügbar) nicht schaden. Ich vermute mal, dass
> die puts()auch nur putchar() in einer Schleife aufruft.
Es ist wie gesagt das ganze Puffer-Handling mit dabei, und deswegen halt 
auch malloc(). Die newlib ist nunmal nicht für kleinste Controller 
optimiert, auhc nicht die newlib-nano. Wenn man unbedingt aufs letzte 
Byte optimieren muss, sucht man sich eine andere libc. Oder verzichtet 
auf printf - wozu braucht man das nochmal unbedingt bei super 
performance-kritischen Anwendungen?

von Stefan F. (Gast)


Lesenswert?

>> hammerhart viel, ich bin echt geschockt!

> wem das nicht passt, der muss eben sein xprintf()
> (oder was auch immer) selber schreiben.

Ich spiele mit dem Gedanken, etwas aus der avr-libc zu kopieren. Die 
dortige Implementierung kommt mit etwa 100 Bytes RAM aus.

Was mich bei dem Speicherverbrauch etwas stört ist:
Hier im Forum wird von bestimmten Leuten immer wieder gepredigt "Nimm 
besser einen ARM", da bekommst du auch mehr Speicher zum selben Preis.

Nun stelle ich aber fest, dass sowohl der Flash Verbrauch als auch der 
RAM Verbrauch erheblich höher sind, als bei AVR. Und das gilt nicht nur 
für printf, sondern zieht sich durch die gesamte Programmierung.

Zuerst hieß es: "Ja aber, die Interrupt-Vektor Tabelle darfst du nicht 
mitzählen". Dann hieß es: "Die Initialierung machst du ja nur einmal, 
die darfst du nicht mit zählen". Dann wiederum "Zugriff auf USB und RTC 
ist doof aber macht man ja nicht ständig" (sicher?). Und jetzt setzt 
printf() dem noch eine große Krone obendrauf.

Und dort endet es nicht. Auch mein eigener Code belegt auf ARM sehr viel 
mehr Speicher, als auf AVR. Und zwar ungefähr das doppelte.

Das viel gesagte Argument: "Nimm ARM, dann hast du mehr Speicher" gilt 
für mich nur noch für die großen Modelle. Einen 128kB AVR durch einen 
256kB ARM auszutauschen wäre in dieser Hinsicht nutzlos.

Jetzt mal meine ganz ehrliche Meinung, nach 1 Jahr Evaluierung: Die AVR 
sind mir immer noch sympathischer.

von kann s nicht glauben (Gast)


Lesenswert?

Dann hast du keine wirklichen uC Projekte. Einmal ARM dann will man kein 
Spielzeug a la AVR mehr. ;-)

von Markus F. (mfro)


Lesenswert?

Stefan U. schrieb:
> Das viel gesagte Argument: "Nimm ARM, dann hast du mehr Speicher" gilt
> für mich nur noch für die großen Modelle. Einen 128kB AVR durch einen
> 256kB ARM auszutauschen wäre in dieser Hinsicht nutzlos.
>
> Jetzt mal meine ganz ehrliche Meinung, nach 1 Jahr Evaluierung: Die AVR
> sind mir immer noch sympathischer.

Hast Du ein konkretes ARM-Projekt, bei dem Du wirklich an die Grenzen 
des Speichers gerätst?
Ansonsten ist das nur Rumgeheule.

von m.n. (Gast)


Lesenswert?

Markus F. schrieb:
> Ansonsten ist das nur Rumgeheule.

... und Erbsenzählerei. Nach einem Jahr Beschäftigung mit ARM (hier geht 
es wohl konkret um STM32Fxxx) sollte man die unterschiedliche 
Leistungsklasse der beiden Familien eigentlich begriffen haben.

Stefan U. schrieb:
> Die AVR
> sind mir immer noch sympathischer.

Dann nimm sie doch einfach. Bleibe bei 8 Bit Variablen, float/double ist 
böse und langsam und printf() für alle Zeit tabu.
So möchte ich allerdings nicht mehr programmieren wollen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Stefan U. schrieb:
> Ich spiele mit dem Gedanken, etwas aus der avr-libc zu kopieren. Die
> dortige Implementierung kommt mit etwa 100 Bytes RAM aus.

Dein Irrtum ist einfach, unbewusst eine Bibliothek mit in Dein Programm 
zu ziehen, die Du gar nicht brauchst. Nein, ich rede nicht von printf(), 
sondern von stdio.

printf(...) ist nichts anderes als fprintf (stdout, ...), puts(s) ist 
nichts anderes als fputs (s, stdout).

Damit ziehst Du eine komplette, komplexe, aber auch geniale 
Abstraktionsschicht in Dein Programm, welche IO für beliebige Geräte - 
egal ob zeichen- oder blockorientiert - auf gepufferte Ein- und 
Ausgabeströme transferiert. Dazu gehört auch das Interface mit 
Filepointern, welche intern auf Fildeskriptoren gemappt werden und 
letztendlich in die oben bereits erwähnten "Syscall" Stubs der libc 
führen.

Kein Wunder, so eine komfortable Lib kostet auch was!

Denn hier kannst Du mehrere Ein- und Ausgabeströme über verschiedene 
Filepointer gleichzeitig handeln. Das ist wesentlich mehr, als die 
avr-libc kann.

Warum machst Du das, wenn Du stdio gar nicht brauchst? Nimm nicht 
printf(), sondern sprintf(). Nimm nicht puts(), sondern Dein eigenes 
uart_puts()! Und schon lösen sich Deine "Platzprobleme" von selbst.

Nochmal:

> Ich spiele mit dem Gedanken, etwas aus der avr-libc zu kopieren. Die
> dortige Implementierung kommt mit etwa 100 Bytes RAM aus.

Das, was Du da kopieren willst, ist kein "echtes" stdio. Die avr-libc 
kennt genau einen Ausgabestrom, die STM32-libc kennt wesentlich mehr.

Also: Wenn Du kein stdio brauchst, dann lass es auch. sprintf() 
existiert.

EDIT:

Hier ein kleines Modul, mit dem Du ein eigenes printf() ohne stdio 
realisieren kannst:
1
#define STRBUF_SIZE 256
2
3
/*------------------------------------------------------------
4
 * log a formatted message (by va_list)
5
 *------------------------------------------------------------
6
 */
7
void
8
log_vprintf (const char * fmt, va_list ap)
9
{
10
    static char str_buf[STRBUF_SIZE];
11
12
    (void) vsnprintf ((char *) str_buf, STRBUF_SIZE, fmt, ap);
13
    (void) log_uart_puts (str_buf);
14
}
15
16
/*-------------------------------------------------------------
17
 * log a formatted message (varargs)
18
 *-------------------------------------------------------------
19
 */
20
void
21
log_printf (const char * fmt, ...)
22
{
23
    va_list ap;
24
25
    va_start (ap, fmt);
26
    log_vprintf (fmt, ap);
27
    va_end (ap);
28
}

Das benutzt sprintf(), bzw. vsnprintf() und verzichtet auf stdio. Und 
damit ist Dein Problem aus der Welt.

P.S.

Auszug aus Deinem Blinker-Programm:
1
// Write standard output to the serial port 1
2
int _write(int file, char *ptr, int len)
3
{
4
    for (int i=0; i<len; i++)
5
    {
6
        while(!(USART1->SR & USART_SR_TXE));
7
        USART1->DR = *ptr++;
8
    }
9
    return len;
10
}

Die Tatsache, dass Du hier den Parameter "int file" komplett ignorierst, 
zeigt genau das, was ich sage: Du brauchst kein stdio.

: Bearbeitet durch Moderator
von Dr. Sommer (Gast)


Lesenswert?

Stefan U. schrieb:
> Und jetzt setzt printf() dem noch eine große Krone obendrauf.

Und die ARM Architektur ist schuld daran, dass du eine suboptimale libc 
verwendest? Kaufe eine optimierte libc, z.B. die von Keil, und du kannst 
die paar Bytes sparen. Auf ARM.com gibt es irgendwo einen Vergleich, wie 
viel weniger Programm Code ARM Thumb2 im Vergleich zu 8051 braucht.
Und

Stefan U. schrieb:
> Dann wiederum "Zugriff auf USB und RTC ist doof aber macht man ja nicht
> ständig"
Wie "doof"? Ist die USB Peripherie von den AVRs so viel besser? Und 
welche von denen haben eine RTC?

Stefan U. schrieb:
> Und dort endet es nicht. Auch mein eigener Code belegt auf ARM sehr viel
> mehr Speicher, als auf AVR. Und zwar ungefähr das doppelte.

Dann optimiere deinen ARM Code auch bitte genauso aufwendig wie deinen 
AVR Code, also jeder Register Zugriff einzeln usw. Allein schon dass man 
beim AVR jede String Funktion doppelt braucht (printf und printf_P) 
treibt den Flash Verbrauch in die Höhe.

von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Stefan U. schrieb:
> Und dort endet es nicht. Auch mein eigener Code belegt auf ARM sehr viel
> mehr Speicher, als auf AVR. Und zwar ungefähr das doppelte.

Andere machen andere Erfahrungen. Bei Chans FatFs kommt ehrer das 
Gegenteil raus.
http://elm-chan.org/fsw/ff/doc/appnote.html

von Stefan F. (Gast)


Lesenswert?

> Die avr-libc kennt genau einen Ausgabestrom

Das stimmt nicht. Ich kann viele File-Handles für viele Ausgabeströme 
mit fprintf() nutzen.

von 900ss (900ss)


Lesenswert?

Leo C. schrieb:
> Andere machen andere Erfahrungen

Und wieder andere vergleichen Äpfel mit Birnen.

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.