Forum: Mikrocontroller und Digitale Elektronik Codegröße 8051 vs Cortex M0


von Martin M. (herr_m_aus_k)


Lesenswert?

Hallo,

ich habe gerade ein bestehendes 8051er-Projekt nach CortexM0 portiert 
(STM32F051) und war doch überrascht über die Codegröße. Anders als 
häufig zu lesen erhalte ich deutlich mehr Code und das, obwohl ich den 
Funktionsumfang auf Cortex-M0-Seite reduziert habe:
8051er: 20354 Bytes
STM32F051: 33388 Bytes und mit optimize size: 26392 Bytes
Dabei habe ich schon auf dem Cortex-M0 sprintf und sscanf durch eigene 
Routinen ersetzt, davor waren es noch etliche 10k mehr.

Noch ein paar Rahmenbedingungen: Compiler sind Keil C51 und CooCox mit 
GCC, uC sind ADuC847 und STM32F051R8
Wie sind Eure Erfahrungen damit? Hat jemand einen Tipp, woran man noch 
drehen kann?

Vielen Dank

Gruß

Martin

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Da lesen:
http://www.mikrocontroller.net/articles/ARM_GCC#Code-Gr.C3.B6.C3.9Fe_optimieren
Beitrag "Re: (ARM) GCC binary size viel größer als von Realview"

Das wichtigste dabei ist das Disassemblisiseren und nachzusehen was da 
viel Platz verbraucht.

von Peter D. (peda)


Lesenswert?

Solange man hauptsächlich mit den internen 256 Byte SRAM auskommt und 
vorzugsweise 8Bit-Variablen nimmt, ist natürlich der 8051-Code deutlich 
kompakter.

Der 8051 ist ja ein CISC, d.h. erspart sich haufenweise umständliches 
Load-Store-Gedöns, wie es der ARM benötigt.

Erst wenn man ihn zum 32Bit Emulieren zwingt und die Variablen nach 
XDATA auslagert (Large-Model), geht der 8051 in die Knie.
Er ist auf kleine Anwendungen (2..16kB) optimiert.

von Martin M. (herr_m_aus_k)


Lesenswert?

Hallo,

und Danke für die Antworten.

@ Niklas:
Du denkst also, dass es an einzelnen eingebundenen Funktionen liegt und 
nicht an der Compilerleistung oder an Notwendigkeiten des Cortex-M0?

Zu deinen Links:
- ich verwende kein C++
- manches mache ich schon, wie zB vom Compiler berechnen zu lassen oder 
union, gilt aber für beide uCs
- manches verstehe ich nicht, zB das mit der leeren atexit-Funktion, ich 
versuche mal, mich schlau zu machen


@Peter
Ich habe meinen 8051er Quelltext auch schon mit Blick auf geringe Größe 
geschrieben, also Byte-Vars und data wo es geht. Mußte aber doch viel in 
xdata legen und verwende auch float. Früher habe ich häufig die 
Assemblerausgaben angesehen und hatte dort das Gefühl, dass ca 30% aus 
herumschieben in Register und Akku bestehen. Ich dachte eigentlich, dass 
das ein Punkt ist, wo die Cortex-M Codegröße sparen würden.
Allgemein verstehe ich dich so, dass du denkst, es liegt an der 
Architektur.


Vielen Dank

Gruß

Martin

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Martin M. schrieb:
> Du denkst also, dass es an einzelnen eingebundenen Funktionen liegt und
> nicht an der Compilerleistung oder an Notwendigkeiten des Cortex-M0?
So die Vermutung, hängt natürlich stark von deinem Quelltext ab. 
32bit-Code ist naturgemäß größer (wobei Thumb2 da schon stark 
"komprimiert"), aber ein Gutteil der Optimierungsmöglichkeiten kommen 
daher dass der GCC "ungefragt" große Funktionen aus der libc/für die CRT 
reinnimmt.

Martin M. schrieb:
> das mit der leeren atexit-Funktion
Ja das ist so ein Workaround gegen ungefragte Funktionalität.

Martin M. schrieb:
> also Byte-Vars und data wo es geht
Tja wenn deine Variablen alle "klein" sind kann der Cortex nicht 
punkten... Das ist dann halt praktisch für den 8051 ;)

Martin M. schrieb:
> und verwende auch float
Hättest du einen Cortex-M4F (mit FPU) genommen könntest du da vermutlich 
drastisch Programmspeicher & Laufzeit sparen ;-)

Peter Dannegger schrieb:
> erspart sich haufenweise umständliches
> Load-Store-Gedöns, wie es der ARM benötigt.
Diese strikte Vorgehensweise ist halt günstig für eine Pipeline und 
erlaubt eine höhere Taktfrequenz...

von Peter D. (peda)


Lesenswert?

Niklas Gürtler schrieb:
> Hättest du einen Cortex-M4F (mit FPU) genommen könntest du da vermutlich
> drastisch Programmspeicher & Laufzeit sparen ;-)

Eine FPU spart nur wenig Code.
Beim 8051 kostet die float-Lib einmalig ~1kB, dann kannst Du sie 
beliebig oft aufrufen.
Eine FPU spart daher haupsächlich nur Laufzeit.

von Heiko J. (heiko_j)


Lesenswert?

Dazu kommt noch das daß das Thumb instruction set 16 Bit, bzw 32. Bit 
(Thumb2) Instruktionen macht, der 8051 aber nur 8 Bit. D.h. der Code ist 
per se schon mal mindestens doppelt so groß. Die Frage ist: Ist das so 
schlimm ? Wer nur als Hobbybastler unterwegs ist bekommt mit den kleinen 
Cortexen wesentlich mehr Leistung zum Fast selben Preis wenn man mal die 
Versandkosten mit einbezieht.

Der günstigste 8051 kompatible (CIP-51) bei Mouser kostet 0,52 € und hat 
512 Byte Ram und 2 kB Programmspeicher. Der günstigste Cortex kostet 
0,80 € hat dafür aber schon 4 kB RAM und 32KB flash und ist um Welten 
besser ausgestattet. Wenn's wirklich um ¢ Beträge geht sind die PICs bei 
den 8-Bittern wesentlich günstiger als die 8051er.

Es ist und bleibt immer eine Frage des: Was brauch ich wirklich und was 
kostet's. Im Normalfall wird man sich auf 1 - 2 Architekturen 
"Einschießen" (sprich Know-How aufbauen) und damit seine Problemchen 
lösen. Für extreme Anforderungen, sei es Leistungsfähigkeit, Preis oder 
auch einfach nur die Anforderung des Kunden (Egal was Ihr verbaut, 
solange es aus unserem Konzern kommt) kann man auch mal eine exotischere 
Architektur hernehmen, allerdings wird dadurch der Entwicklungsaufwand 
durch die Einarbeitung höher und der will ja auch erstmal bezahlt 
werden.


Gruß Heiko

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Kannst mal mit armcc vergleichen?

von Lothar (Gast)


Lesenswert?

Martin M. schrieb:
> ich habe gerade ein bestehendes 8051er-Projekt nach CortexM0 portiert
> (STM32F051) und war doch überrascht über die Codegröße

Habe auch schon viel portiert, und die Codegröße lässt sich schon in 
etwa gleich halten wenn man folgendes berücksichtigt:

1) Die ARM-Compiler erzeugen eher schnellen als kompakten Code (weil die 
meisten ARMs ja genug Flash haben) und die Bibliotheken der Hersteller 
und vom gcc sind nicht auf Codegröße optimiert. Das ist bei sehr kleinen 
ARMs ein Problem, daher wurden für die minimale Bibliotheken entwickelt 
z.B.: LPC810 (nur 1k RAM, 4k Flash):

https://github.com/microbuilder/LPC810_CodeBase

2) Die ARMs sind Load/Store und brauchen daher mehr Code für 
Portzugriffe. Dafür können M3 und höher alle Befehle bedingt ausführen, 
was viele Jumps und Calls spart. Der M0 kann das nicht, daher ist M3 
Code deutlich kleiner als M0 Code. Da preislich kein Unterschied ist, 
besser zum M3 greifen, wenn es auf die Codegröße ankommt z.B. LPC1114/M0 
und LPC1313/M3 (8k RAM, 32k Flash) kosten beide 2,50

von Martin M. (herr_m_aus_k)


Lesenswert?

Niklas Gürtler schrieb:
> aber ein Gutteil der Optimierungsmöglichkeiten kommen
> daher dass der GCC "ungefragt" große Funktionen aus der libc/für die CRT
> reinnimmt.

ok, ich habe mal in der .map-Datei nachgesehen und habe gefunden, dass 
ich dort die floating point Routinen sowohl für single float als auch 
für double habe. Im 8051er-Projekt habe ich nur single float (kann mein 
Keil C51 nicht besser) und das möchte ich jetzt auch beibehalten, da ich 
viele float-Vars habe und die in ein EEPROM gespeichert werden, Doppelt 
so viele Bytes passen da nicht mehr rein.
Wie kann ich dem GCC sagen, dass er nichts in double rechnen soll? Ich 
habe nur an einer Stelle das Wort double in meinen Quelltexten, das ist 
bei der selbstgeschriebenen sprintf. Die Elipse akzeptiert in GCC keine 
float, gab eine Fehlermeldung bei compilieren.
Alle double Routinen zu entfernen würde schon einige kByte sparen.

Vielen Dank

Martin

PS Dank an Lothar für den Tipp / Vergelich M0 - M3

von (prx) A. K. (prx)


Lesenswert?

Heiko Jakob schrieb:
> Dazu kommt noch das daß das Thumb instruction set 16 Bit, bzw 32. Bit
> (Thumb2) Instruktionen macht, der 8051 aber nur 8 Bit. D.h. der Code ist
> per se schon mal mindestens doppelt so groß.

Sorry, aber das ist ein Milchmädchenrechnung der schlimmeren Sorte. Die 
Codegrösse ergibt sich aus der Grösse der Befehle und der Anzahl der 
Befehle. Willst du allen Ernstes behaupten, dass im vom Compiler 
erzeugten Code einem 8051 Befehl stets genau ein ARM Befehl entspricht?

: Bearbeitet durch User
von Random .. (thorstendb) Benutzerseite


Lesenswert?

Hi,

installier dir mal das MDK-ARM von keil.com und setze das Projekt mit 
MicroLib auf.

von Martin M. (herr_m_aus_k)


Lesenswert?

Hallo,

habe gerade mit Google einen Tipp gefunden:
Standardmäßig erzeugt gcc alle floating point-Konstanten als double und 
führt dann entsprechende Umwandlungen durch.
Abhilfen:
hinter jede single-float-Konstante ein f: 1.23f
Copmpileroption -fsingle-precision-constant verwenden
das hat meinen Code mit einem Schlag um 5kByte kleiner gemacht.
Dann habe ich hier im Link-Tab der Configurations ein Kästchen LTO = 
link time optimization, das bringt nochmal 1,5k. Weiss jemand, warum das 
nicht standardmäßig angehakt ist?

Gruß

Martin

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Martin M. schrieb:
> ok, ich habe mal in der .map-Datei nachgesehen
Schau auch mal in die Disassembly.

Martin M. schrieb:
> Wie kann ich dem GCC sagen, dass er nichts in double rechnen soll?
Indem du überall nur "float" und kein "double" verwendest, d.h. 
insbesondere keine double-Funktionen wie "sin" sondern nur 
float-Funktionen wie sinf. Das heißt auch:
1
float x = getVal ();
2
float y = 1. / x;
Hier wird in double gerechnet, da 1. ein double ist! 1.f schreiben um 
sicherzustellen dass in float gerechnet werden soll. Das ist alles 
C-Standard, hat nix speziell mit dem GCC zu tun.

Martin M. schrieb:
> Die Elipse akzeptiert in GCC keine
> float, gab eine Fehlermeldung bei compilieren.
Kann nicht, klappt wunderbar mit eclipse und GCC.

Martin M. schrieb:
> Weiss jemand, warum das
> nicht standardmäßig angehakt ist?
Weil das versehentlich Dinge wie den ISR-Vektor wegoptimieren könnte und 
eine Zeit lang etwas buggy war. Diese Option entspricht dem 
Compiler&Linker-flag -flto, das auch im o.g. Wiki-Artikel angegeben ist.

von Martin M. (herr_m_aus_k)


Lesenswert?

Niklas Gürtler schrieb:
>> Die Elipse akzeptiert in GCC keine
>> float, gab eine Fehlermeldung bei compilieren.
> Kann nicht, klappt wunderbar mit eclipse und GCC.

War unklar formuliert. Die Elipse wandelt grundsätzlich in double um und 
wenn man dann mit va_arg ein float holen will, geht es nicht. Der 
Compiler meckert da, ist evtl auch nur eine Warnung.

Ich bin inzwischen auf dem Cortex M0 auf unter 20k gekommen, das sieht 
erstmal akzeptabel aus.

Dank an alle für die Beteiligung an diesem Thread.

Gruß

Martin

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Martin M. schrieb:
> War unklar formuliert. Die Elipse wandelt grundsätzlich in double um
Um klar zu sein - eclipse wandelt gar nichts um. Eclipse ist nur eine 
grafische Oberfläche. Das macht wenn schon der Compiler. Und der wandelt 
nicht automatisch in float um, lediglich haben Literale der Form 1.234 
den Typ double, wenn man kein "f" anhängt.

Martin M. schrieb:
> und wenn man dann mit va_arg ein float holen will, geht es nicht.
Das kann auch nicht, da musst du was falsch gemacht haben. printf kann 
das seit Jahr und Tag...

von Heiko J. (heiko_j)


Lesenswert?

A. K. schrieb:
> Heiko Jakob schrieb:
>> Dazu kommt noch das daß das Thumb instruction set 16 Bit, bzw 32. Bit
>> (Thumb2) Instruktionen macht, der 8051 aber nur 8 Bit. D.h. der Code ist
>> per se schon mal mindestens doppelt so groß.
> Sorry, aber das ist ein Milchmädchenrechnung der schlimmeren Sorte. Die
> Codegrösse ergibt sich aus der Grösse der Befehle und der Anzahl der
> Befehle. Willst du allen Ernstes behaupten, dass im vom Compiler
> erzeugten Code einem 8051 Befehl stets genau ein ARM Befehl entspricht?

Da der Cortex auf der einen Seite jede Menge Load/Store Geschichten 
braucht,  auf der anderen Seite aber auch alle Befehle bedingt ausführen 
kann sollte sich das nicht viel schenken. Daher kann man das sehr wohl 
als Abschätzung annehmen.

Sorry aber was wolltest Du mit Deinem Post zum Thema beitragen ausser 
rumzustänkern ?

von Sascha (Gast)


Lesenswert?

Hallo,
seit wann kann der Cortex M0 alle Befehle bedingt ausführen?

Also der 8051 ist ungebrochen mit sehr effizientem Code in Assembler zu 
programmieren. Er hat eine höhere Codedichte als der Cortex M0. Er ist 
langsamer als ein Cortex M0 und insgesammt auch von der Peripherie nicht 
so leistungsstark. Dafür gibt es Derivate von Silabs die mit unglaublich 
wenig Strom auskommen. Trotzdem sind die neuen CM0 Derivate von 
Freescale auch sehr interessant.

Aber meine Empfehlung für Anfänger -> Cortex M0.
Da der Speicheraufbau um einiges einfacher gehalten ist und man auch 
schnell mal zu CM3....CM4.....CM4F wechseln kann.

Gruß Sascha

von W.S. (Gast)


Lesenswert?

Martin M. schrieb:
> 8051er: 20354 Bytes
> STM32F051: 33388 Bytes und mit optimize size: 26392 Bytes

Es ist schon ein paarmalweiter oben geschrieben worden, aber du hast es 
offenbar nicht gelesen:

Lade dir mal den "Keil" herunter, bis 32 K Code geht der als Demo und 
compiliere damit dein Projekt. Der Compiler beim Keil ist der originale 
von ARM und der erzeugt schlichtweg besseren Code (kleiner + schneller) 
als der GCC. Wenn du hingegen partout beim GCC bleiben willst, dann 
versuche mal alle verschiedenen Optimierungsstufen. Ich hatte da schon 
seltsame Dinge gesehen.

Ansonsten gilt die alte Regel, daß es wenig nützt, wenn der Quellcode 
mal eben portierbar ist - das damit gemachte Projekt ist in der Regel 
nicht genauso portierbar und es braucht bei einer anderen Hardware eben 
auch eine entsprechende Überarbeitung, wo die diversen Workarounds für 
die bisherige Plattform rausgeschmissen werden und Funktionalitäten 
umgestellt werden. Beispiele:
alt: 1 Counter muß für alles herhalten, neu: ein Dutzend Counter 
vorhanden, also sharing überflüssig.
Oder: alt: Datentransport per CPU-Loop, neu: selbiges per DMA.
usw.

W.S.

von m.n. (Gast)


Lesenswert?

Heiko Jakob schrieb:
> Sorry aber was wolltest Du mit Deinem Post zum Thema beitragen ausser
> rumzustänkern ?

Er wollte Dir lediglich klarmachen, wie unsinning die Annahme ist, dass 
ein 16 Bitter die doppelte Codegröße eines 8 Bitters braucht.
Wenn das eine 'belastbare' Aussage wäre, sollte man gleich zum 1 Bitter 
greifen :-)

von (prx) A. K. (prx)


Lesenswert?

Er meinte zwar die Breite der Codeworte, nicht der Daten, aber das ist 
ebenso unsinnig.

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.