Forum: Compiler & IDEs CMSIS und GNU Support


von Random .. (thorstendb) Benutzerseite


Lesenswert?

Hi Leutz,

ausgehend von diesem Thread:
Beitrag "Re: Welcher Cortex M3?"

wo es über die CMSIS heiss her ging, möchte ich "euch" in diesem Thread 
die Gelegenheit geben, über den GNU Support der CMSIS zu diskutieren.

Sicherlich ist die CMSIS auch ein politisches Projekt (sind Standards 
immer), aber sofern es sinnvoll ist, können wir diese sehr flexibel 
Erweitern und updaten.

Während der Entwicklung der Core Funktionalität gab es recht wenig 
Support seitens der GNU Gemeinde, von daher war die Implementierung 
nicht so leicht.

Daher würde ich mich über Anregungen und Kritik (bitte keine 
Beleidigungen!) freuen, und diese ggf. umsetzen.

Die CMSIS gibtz zum Download bei www.onARM.com

---
Der Ansatz des CMSIS-Core (mein Aufgabengebiet) war es, eine 
superschmale HAL zu schaffen, mit der sich die Registerprogrammierer 
anfreunden können.

So ist z.B. der NVIC des CM3 ein sehr komplexes Peripheral, wer es 
verstanden hat, darf sich glücklich schätzen (wohl die wenigsten ^^)

Die inline Funktionen in der core_cm3.h bilden exakt das ab, was man in 
seinen Source schreiben müsste, um einen Interrupt zu behandeln. Mit 
Ausnahme sehr weniger Funktionen (NVIC_SetPriorityGrouping) ergibt sich 
somit also kein Overhead.

Was sich damals aus compilertechnischen Gründen (z.B. armcc V3, gcc) 
nicht in static inline verpacken liess, wurde dann - nach langen 
recherchen - grummelnd zu einer core_cm3.c verpackt, ein Konstrukt, was 
notwendig war, aber das asbald möglich verschwinden soll.

---
Die Sache bleibt überschaubar, und grenzt sich somit massivst ab von den 
grossen DriverLibs, die mit massig parametern die HW versuchen zu 
erschlagen.

Das Problem, was ich damals dabei sah, ist, dass DLs meist nicht die 
komplette Funktionsweise eines Peripherals abbilden können, und immer 
wieder Handarbeit notwendig ist. Ausserdem blähen sie idR. den Code auf.

---
Ohne jetzt eine Politische Diskussion vom Stapel reissen zu wollen; wenn 
ihr Anregungen habe oder Fehler findet, dann bitte her damit. 
Schliesslich profitiert auch die GNU Gemeinde davon, denn CMSIS ist frei 
und Tool-unabhängig.

Mit der CMSIS hat auch der absolute Neuling, egal in welcher Umgebung, 
eine reelle Chance, ohne viel Recherche seinen Cortex-M3 zum Laufen zu 
bekommen (z.B. Umsteiger von AVR -> ARM).



VG,
/th.

von (prx) A. K. (prx)


Lesenswert?

>Details?

>z.B. hier, da ist nix drin von wegen parameter und return, da ich vom
>AAPCS ausgehen kann:__ASM uint32_t __get_PRIMASK(void)
>{
>  mrs r0, primask
>  bx lr
>}

Das funktioniert aber nur wenn eine echte Funktion daraus wird, als 
inline wird's eine Bombe. Wenn man das in der GCC-typischen Art 
realisiert, dann kann es zwar vorkommen, dass im ASM Statement
  mrs r1, primask
generiert wird, und der Compiler die Funktion (non-inlined) danach mit
  mov r0, r1
  bx  lr
beendet, aber das ist ja auch nicht falsch.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

stimmt ^^

von J. V. (janvi)


Lesenswert?

Ok - hier in diesem Thread über CMSIS weiter. Ich halte das CMSIS 
Projekt für eine sehr gute Sache, die sich wahrscheinlich durchsetzen 
wird. Als Vergleich die alten DOS Programme wo jeder einen eigenen 
Dialog zum Öffnen einer Datei gebraucht hat und jeder für jeden Drucker 
einen eigenen Treiber usw. Dann kam Windows und plötzlich konnte man die 
passende Klasse auswählen und auch für den Anwender sahen (fast) alle 
Datei Öffnen Dialoge gleich aus. Aus diesem Grund hat sich das 
durchgesetzt und zwischenzeitlich ist mein Projekt auch so weit 
fortgeschritten, daß ich meine daß es keine Fehlentscheidung war sich 
auf CMSIS einzulassen. Aber:

Die Driver Lib hat sich in den letzten 3 Versionen jährlich grundlegend 
geändert. Zuletzt mit der Einführung von CMSIS und das ist der 
Verbreitung nicht gerade zuträglich. Bleibt trotzdem zu hoffen, dass 
sich CMSIS durchsetzt und auch damit irgendwann TCP/IP Stacks und 
Filesysteme zu einer einfachen Angelegenheit bei uC werden.

Zum ersten Problem:
Alle Startup Assemblerfiles für den GCC haben bei mir eine falsche 
Adresse im Reset Vektor gelinkt welche das T-Bit nicht gesetzt hat. 
Dies führt zu einem Hard Fault beim ersten Befehl und ohne Debugger gibt 
es für Anfänger kaum eine Chance da drauf zu kommen. Mein erster 
Lösungsansatz war, Symbol+1 zu schreiben damit das geht. Sodann habe ich 
gesehen, dass eine Pseudo Anweisung .thumb_func das gleiche macht. 
Möglicherweise gibt es auch irgendwo einen Kommandozeilenschalter wo das 
Gleiche macht, der dann aber nicht Default ist und den ich auch nicht 
kenne.

Zum zweiten Problem:
Das assert_param Makro hat bei mir zu einem Linkerfehler geführt, weil 
es der Compiler als Implicit Declaration ohne Prototyp (=Warnung) 
gesehen hat. In der CMSIS Doku von STM gibt es eine farbige Grafik 
welche im übrigen Schreibfehler bei den Dateinamen hat. Alle 
stm32f10x_ppp.c Dateien benutzen assert_param welches als Makro in 
conf.h definiert ist. Alle ppp.c haben aber als Include nur ppp.h und 
dortselbst ist conf.h auch nicht drinnen. Dafür aber stm32f10x.h und 
deshalb habe ich das assert_param von conf.h in stm32f10x.h verschoben 
um übersetzen zu können. Die Pfeilrichtungen im CMSIS Diagramm mit der 
Header Hierarchie sind mir übrigens auch noch ein Rätsel.

Zum dritten Problem:
was eigentlich nur ein Schönheitsfehler mit einer unnötigen 
Neudefinition von Registeradressen ist, siehe hier:
http://www.st.com/mcu/forums-cat-9101-23.html
Der tiefere Grund für solchen Frevel hat sich mir jedenfalls noch nicht 
erschlossen.

Das vierte Problem
ist natürlich der Speicherbedarf. Da wäre mit einem Übersetzen als 
Archiv wahrscheinlich schon viel gespart. Ganz toll wäre es natürlich, 
wenn man die Symbole aus den Headern auch im Assembler benutzen könnte.

von (prx) A. K. (prx)


Lesenswert?

CMSIS ist sinnvoll, keine Frage.

Vor allem wenn darin auch die Periphieriedefinitionen des jeweiligen 
Herstellers drin sind, denn das war nicht grad selbstverständlich. Auf 
die Support-Funktionen kann ich verzichten, aber die Strukturen und Bits 
sollten drin sein.

Was mich an den Periphieriedefinitionen beispielsweise vom STM32 
allerdings durchaus nervt, das ist die Deklaration der Registerbits als 
Maske. Zwar kann man dann elegenat
  UNIT.REGISTER = ENABLE | DISABLE | EXPLODE;
schreiben, aber der elegante Weg ins doch ziemlich praktische Bitbanding 
ist damit erst einmal verbaut. Lieber wäre mir da
  UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;

So musste ich eben in meine Bitbanding-Makros eine Hilfsfunktion 
einbauen, die aus einer Maske eine Bitnummer macht.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> ist natürlich der Speicherbedarf. Da wäre mit einem Übersetzen als
> Archiv wahrscheinlich schon viel gespart.

Geht bei GCC auch einfacher:
  --function-sections
  --data-sections
im Compiler, und beim Linken dann
  -gc-sections
(oder so ähnlich). Da fliegen alle Funktionen und Daten raus, die 
nirgends referenziert werden. Sämtliche Interrupt-Handler inklusive ;-), 
weshalb man diese mit
  __attribute__((used))
verzieren sollte.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:

>   UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;

PS: Das hat auch den Vorteil, dass man zur besseren Dokumentation der 
Einstellungen
    UNIT.REGISTER = 1<<ENABLE | 0<<DISABLE | 0<<EXPLODE;
schreiben kann. Also auch Bits explizit reinschreiben kann, die man 
gezielt nicht setzen will.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

> UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;

Ist auch mein genereller Ansatz, aber so:
UNIT->REGISTER = (1<<ENABLE) | (1<<DISABLE) | (1<<EXPLODE);

oder mit
bit_on(UNIT->REGISTER, ENABLE);

was meine bevorzugte Variante ist. Eventuell etwas länger, dafür 
supereinfach pflegbar!.

Findet sich auch im core wieder (ersteres).
Viele Hersteller nehmen aber gerne - warum auch immer - defines, in 
denen das '1<<' schon mit drinsteckt, ka, why.

Warscheinlich ist das der Unterschied zwischen Register-Usern, die in 
Bits denken, und DriverLib-Usern, die in Values denken.


VG,
/th.

von J. V. (janvi)


Lesenswert?

>Zwar kann man dann elegenat
>  UNIT.REGISTER = ENABLE | DISABLE | EXPLODE;
>schreiben

genau das geht eben nicht immmer und ob es geht, kriegt man eigentlich 
nur darüber raus, indem man im Quellcode nachschaut, ob es sich um zwei 
Bits handelt die im gleichen Register mit gemeinsamem Zugriff liegen.
So hatte ich zum Beispiel bereits ziemlich Pech mit

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn | 
DMA1_Channel1_IRQn;
NVIC_Init(&NVIC_InitStructure);

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

A. K. schrieb:
> schreiben, aber der elegante Weg ins doch ziemlich praktische Bitbanding
> ist damit erst einmal verbaut. Lieber wäre mir da
>   UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;
>
> So musste ich eben in meine Bitbanding-Makros eine Hilfsfunktion
> einbauen, die aus einer Maske eine Bitnummer macht.

Wo ist denn hier der Vorteil des Bitbanding? Statt einem Speicherzugriff 
hast Du nun sechs.

Bitbanding ist insgesamt ziemlich unnütz zum Zugriff auf Peripherie 
Register. Die µC Hersteller haben die ursprünglichen Probleme mit 
atomaren Zugriffen seit Jahren gelöst.

Bitbanding hat aber durchaus andere Anwendungsgebiete.

Gruß
Marcus
http://www.doulos.com/arm/

von (prx) A. K. (prx)


Lesenswert?

Diese Funktionen verwende ich schon der darin enthaltenen Verschleierung 
der realen Vorgänge wegen nicht selbst, allenfalls als Demo wie andere 
das gemacht haben (NB: die diesbezügliche Baudratenrechnung bei der UART 
vom STM32 ist eher ein Beispiel dafür, wie man es nicht machen 
sollte).

Ausserdem ist mir der Weg über Speicherstrukturen zu aufwendig.

von (prx) A. K. (prx)


Lesenswert?

Marcus Harnisch schrieb:

> Wo ist denn hier der Vorteil des Bitbanding? Statt einem Speicherzugriff
> hast Du nun sechs.

Für 3 Bits ist das meist Unsinn, klar. Bei einem einzelnen Bit nicht.

> Bitbanding ist insgesamt ziemlich unnütz zum Zugriff auf Peripherie
> Register.

Ganz im Gegenteil. Wenn Interrupts oder ein RTOS im Spiel sind, dann 
können die atomaren Zugriffe des Bitbandings von entscheidender 
Bedeutung sein. Wenn also ein Register sowohl im Hauptprogramm als auch 
im Interrupt-Handler modifiziert wird, was durchaus nicht selten ist und 
nicht nur GPIO-Ports betrifft.

> Die µC Hersteller haben die ursprünglichen Probleme mit
> atomaren Zugriffen seit Jahren gelöst.

Nicht wirklich, d.h. oft nicht vollständig.

Bei den GPIO Ports beispielsweise nur, wenn der Hersteller der Port-Unit 
intelligent genug war, bidirektionale Open Drain Ports zuzulassen, oder 
auch für die Richtungsregister bitweise Operationen vorgesehen hat. Was 
auf STM32 und SAM7 zutrifft, auf STR7/STR9/LM3 (die Primecell) und 
LPC2000 jedoch nicht. Bei diesen muss man dafür über das nicht 
bitweise ansprechbaren Richtungsregister arbeiten und kriegt allzu 
leicht ein Problem mit nicht atomaren Zugriffen.

Es gibt sehr gute Gründe, weshalb ARM das Bitbanding eingebaut hat.

von J. V. (janvi)


Lesenswert?

> das gemacht haben (NB: die diesbezügliche Baudratenrechnung bei der UART
> vom STM32 ist eher ein Beispiel dafür, wie man es nicht machen
> sollte).

Immerhin tut sie solange man draufkommt wo man was definieren muß wenn 
man was anderes wie einen 8 Mhz Quarz verwendet. Dass dabei zur Laufzeit 
ein Haufen unnützer Code entsteht liegt klar an der Implementierung. 
Möglicherweise könnte man das auch mit einem Preprozessor Makro 
erschlagen.

> Ausserdem ist mir der Weg über Speicherstrukturen zu aufwendig.

Aus diesem Grund wurde das beim STM8 so nicht realisiert sondern mit 
direkter Parameterübergabe vereinfacht. Für STM8 gibts übrigens Compiler 
von Cosmic und die können im Assembler auch C und Bitsymbole nutzen.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> Immerhin tut sie solange man draufkommt wo man was definieren muß wenn
> man was anderes wie einen 8 Mhz Quarz verwendet. Dass dabei zur Laufzeit
> ein Haufen unnützer Code entsteht liegt klar an der Implementierung.
> Möglicherweise könnte man das auch mit einem Preprozessor Makro
> erschlagen.

Wenn's denn sein muss. Aber m.E. hatte der Programmierer nicht viel von 
dem verstanden was er da realisiert hat, auch die Hex-Konstanten 
dezimaler Zahlen sprechen eher für Blindflug bei Übernahme eines 
disassemblierten Vorbilds.

Diese ganze höchst umständliche Rechnung darin kondensiert nämlich zu 
sowas Ähnlichem wie
   usart.baudratenregister = clock / rate;
wenn man (a) sie verstanden hat und (b) einen Fehler von maximal einem 
halben letzten Bit des Nachkommaanteils zulässt, was wohl nie ein 
Problem für die Baudrate sein wird. Aber auch mit Rundung wird das kaum 
komplizierter.

> Aus diesem Grund wurde das beim STM8 so nicht realisiert sondern mit
> direkter Parameterübergabe vereinfacht.

Na also.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

A. K. schrieb:
> Marcus Harnisch schrieb:
>> Bitbanding ist insgesamt ziemlich unnütz zum Zugriff auf Peripherie
>> Register.
>
> Wenn Interrupts oder ein RTOS im Spiel sind, dann können die
> atomaren Zugriffe des Bitbandings von entscheidender Bedeutung sein.

Grundsätzlich habe ich auch nichts gegen das Bit Banding an sich.
Gerade für Peripheriezugriffe ist es aber nicht das ideale Mittel, um
Designprobleme der Hardware zu lösen (s.u.).

> Wenn also ein Register sowohl im Hauptprogramm als auch im
> Interrupt-Handler modifiziert wird, was durchaus nicht selten ist
> und nicht nur GPIO-Ports betrifft.

...und sich die Modifikation auf wenige Bits beschränkt, und der
Zugriff auf die Speicherzelle frei von Nebeneffekten ist
(clear-on-access, etc.)

In anderen Fällen ist es dann meist besser, den
Register-/Speicherzugriff mit einer Semaphore zu schützen. Die
wiederum kann man übrigens hervorragend mittels Bit Banding
implementieren (und natürlich auch mit LDREX/STREX).

> Nicht wirklich, d.h. oft nicht vollständig.

In diesen Fällen bietet Bit Banding dann aber auch keine
Universallösung.

> Bei den GPIO Ports beispielsweise nur, wenn der Hersteller der Port-Unit
> intelligent genug war, bidirektionale Open Drain Ports zuzulassen, oder
> auch für die Richtungsregister bitweise Operationen vorgesehen hat.
> [...]
> Bei diesen muss man dafür über das nicht bitweise ansprechbaren
> Richtungsregister arbeiten und kriegt allzu leicht ein Problem mit
> nicht atomaren Zugriffen.

Und mit Bit Banding hast Du das potentielle Problem, dass die
Richtungsänderung nicht gleichzeitig erfolgt. Du wirst immer jemanden
finden, der in seiner Anwendung was zu meckern hat.

Diese Probleme können nur auf Peripherieebene gelöst werden. Ob die
Hersteller das machen, steht auf einem anderen Blatt.

> Es gibt sehr gute Gründe, weshalb ARM das Bitbanding eingebaut hat.

Hauptsächlich Marketing fürchte ich. Andere µC Architekturen
unterstützen Bitzugriffe (wenn auch in anderer Form) nativ. ARM musste
in der feature list dagegenhalten, um dem 8-bit Markt etwas bieten
zu können. Jemand der es wissen sollte, hat meinen diesbezüglichen
Kommentar jedenfalls nicht dementiert :-) Außerdem ist die
Implementierung nicht besonders aufwändig.

Bit Banding ist zweifellos eine gute Sache aber keineswegs das
silver bullet, als das es uns präsentiert wird. Der tatsächliche
Nutzen für Peripheriezugriffe ist auf weniger Anwendungsfälle
beschränkt als man uns glauben machen möchte.

Gruß
Marcus
http://www.doulos.com/arm/

von (prx) A. K. (prx)


Lesenswert?

Marcus Harnisch schrieb:

> Und mit Bit Banding hast Du das potentielle Problem, dass die
> Richtungsänderung nicht gleichzeitig erfolgt.

Bei parallen Bussen geht das nicht, klar. Mein "Benchmark" dafür ist 
allerdings sowas wie 1-Wire oder I2C, d.h. wie aufwändig lässt sich das 
in Software mit Portpins implementieren. Gleichzeitigkeit ist da kein 
Problem, atomarer Zugriff hingegen schon.

Ob das nun über dne Portkonstruktion realisiert wird, oder indem 
dortigen Löcher über Bitbanding gestopft werden, ist mir letztlich egal. 
Mit der ARM Primecell für GPIO ist aber ohnehin das Kind im Brunnen.

Ansonsten betrifft das vor allem allerlei Interrupt-Flags/Enables etc, 
die gerne im Hauptprogramm und im Handler verwendet werden. Das sind 
praktisch immer einzelne Bits.

Das dies kein silver bullet ist, das ist mir klar. Aber Werbeschmarrn 
interessiert mich ohnehin nicht, ich beziehe meine Information nicht aus 
PR-Veranstaltungen sondern aus Handbüchern.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Nur noch mal zum Abschluss, bevor wir uns bestätigen dass wir mehr
oder weniger der gleichen Meinung sind:

A. K. schrieb:
> Mit der ARM Primecell für GPIO ist aber ohnehin das Kind im Brunnen.

Die Primecells sind nicht für Mikrocontroller entworfen worden,
sondern für SoC (ASSP). Wenn da jemand I²C haben möchte, dann baut er
sich entsprechendes IP ein und bastelt da nichts in Software. Das
Luminary seinerzeit die Primecells eingesetzt hat lag wohl eher an der
speziellen Beziehung zu ARM.

Ein großer Aufwand bei der Entwicklung eines µC ist es, herauszufinden
wie man es möglichst vielen Anwendern recht machen kann. Das überlässt
man bei ARM dann doch lieber den µC Herstellern. Die Primecells haben
nicht den Anspruch möglichst universell zu sein. Dafür sind sie meist
extrem einfach zu programmieren. Ein PL081 Timer z.B. kann so gut wie
nichts außer zählen und einen Interrupt generieren -- und das ist auch
gut so. Wenn ich in meinem SoC irgendwelche bizarren PWM Modi brauche,
dann baue ich mir spezielle Hardware dafür, die für diesen Zweck
dann ähnlich einfach anzusprechen ist. Niemand, der ein ASSP
entwickelt würde auf die Idee kommen, einen dieser funktional völlig
überladenen µC Timer zu verwenden. Ähnlich ist das mit GPIO, UART,
etc. Um ein paar LEDs am Handy anzusteuern, ist der PL061 GPIO prima.

Gruß
Marcus
http://www.doulos.com/arm/

von (prx) A. K. (prx)


Lesenswert?

Einverstanden. Leider verwenden mindestens STR9 und LM3 diese Primecell.

von janvi (Gast)


Lesenswert?

Warum wurden viele Defines in die C Files anstelle der Header gemacht ?

Bsp. Timer: Hier sind die defines für alle Register Bitmasken im C File.
Möchte eine Anwendung den Ausgang von TIM3 abschalten, so ist hier 
offensichtlich der Aufruf von
TIM_CCxCmd(TIM3, TIM_Channel3, TIM_CCxDisable);
vorgesehen.

Wie bei fast allen Lib Funktionen hat die Portabilität dabei ein Code 
und Lauzeit Overhead von wenigstens 100% Deshalb könnte eine Anwendung 
draufkommen, den Ausgang selbst zu schalten.

TIM3->CCER &= CCER_CC3E_RESET

Das geht aber nicht, weil die defines nicht im Timer Header 
drinnenstehen sondern im C Quellfile (wo sie nicht hingehören?) Es 
bleibt also nichts anderes übrig, als ein Define aus der Bibliothek noch 
mal zu definieren weil es nicht gefunden wird ...

von (prx) A. K. (prx)


Lesenswert?

janvi schrieb:

> Das geht aber nicht, weil die defines nicht im Timer Header
> drinnenstehen sondern im C Quellfile (wo sie nicht hingehören?)

In stm32f10x.h stehen m.W. alle Bits drin. Also:
  TIM3->CCER &= TIM_CCER_CC3E;

Mich stört eher, dass Felder aus mehreren Bits nur einzelbitweise 
enthalten sind, nicht in verständlich übersetzter Form. So wünschte ich 
mir TIM_CR2_MMS_PULSE statt des unverständlichen Ungetüms 
(TIM_CR2_MMS_1|TIM_CR2_MMS_0).

von (prx) A. K. (prx)


Angehängte Dateien:

Lesenswert?

Und so entstehen eben inkrementell meine privaten Zusatz-Includes für 
die Units. Beispiel anbei.

von (prx) A. K. (prx)


Lesenswert?

So war's natürlich gemeint:
  TIM3->CCER &= ~TIM_CCER_CC3E;
Wobei ich da auch ganz gerne schreibe:
  BBPeriphMask(TIM3->CCER, TIM_CCER_CC3E) = 0;

von janvi (Gast)


Lesenswert?

Besten Dank für den Tipp. Für ein Bit sind also definiert:

TIM_CCER_CC3E
CCER_CC3E_RESET
CCER_CC3E_SET

und das verteilt über mehrere Files. Die Applikation soll Defines ohne 
Set/Reset und Timer Nr. nutzen und Set/Reset mit Timer Nr. ist CMSIS 
Angelegenheit weil die Nutzung der Inverter Tilde dem Anwender versteckt 
werden soll.

von Frischling (Gast)


Lesenswert?

@janvi:
>Zum ersten Problem:
>Alle Startup Assemblerfiles für den GCC haben bei mir eine falsche
>Adresse im Reset Vektor gelinkt welche das T-Bit nicht gesetzt hat.
>Dies führt zu einem Hard Fault beim ersten Befehl und ohne Debugger gibt
>es für Anfänger kaum eine Chance da drauf zu kommen. Mein erster
>Lösungsansatz war, Symbol+1 zu schreiben damit das geht. Sodann habe ich
>gesehen, dass eine Pseudo Anweisung .thumb_func das gleiche macht.
>Möglicherweise gibt es auch irgendwo einen Kommandozeilenschalter wo das
>Gleiche macht, der dann aber nicht Default ist und den ich auch nicht
>kenne.

Kannst du das bitte genauer erläutern? Ich stehe nämlich vor genau dem 
Problem. Allerdings komm ich nicht auf die Lösung.

von (prx) A. K. (prx)


Lesenswert?

janvi schrieb:

> CCER_CC3E_RESET
> CCER_CC3E_SET

Diese beiden sollten schon deshalb nicht in dieser Form global definiert 
werden, weil sie nicht eindeutig dem Timer zuordnet sind.

Gegen explizite Benennung beider Varianten habe ich bei der Definition 
als Maske nichts einzuwenden, wie du auch in meiner Version erkennen 
kannst. Allerdings nicht um die Tilde einzusparen, sondern um explizit 
in die Zeile reinschreiben zu können, dass ich das Bit nicht setzen 
will. Erspart die sattsam bekannte Code/Kommentar-Schere.

Denkbar wäre, dass die Peripherie-Lib völlig unabhängig vom CMSIS 
stm32f10x.h entstand. Jedenfalls sehe ich in diesen beiden Ansätzen 
ohnehin Antagonisten. Entweder man verwendet die Lib in aller Zeilen 
fressenden Umständlichkeit, oder man geht ans Eingemachte, an die 
Register.

> Die Applikation soll Defines ohne
> Set/Reset und Timer Nr. nutzen und Set/Reset mit Timer Nr. ist CMSIS
> Angelegenheit weil die Nutzung der Inverter Tilde dem Anwender versteckt
> werden soll.

Bringt das was? Immerhin muss der Anwender auch zwischen &= und |= 
unterscheiden und wenn er vor oder nach dem STM32 auch mal was anderes 
programmiert, dass wird ihm die Notation x &= ~y; durchaus vertraut 
sein.

Denn wenn nicht, dann sind bit_set(register,mask) und 
bit_reset(register,mask) weitaus sinnvoller.

von J. V. (janvi)


Lesenswert?

>> CCER_CC3E_RESET
>> CCER_CC3E_SET

>Diese beiden sollten schon deshalb nicht in dieser Form global definiert
>werden, weil sie nicht eindeutig dem Timer zuordnet sind.

eigentlich sind sie sogar Timer Nr. 3 zuzuordnen und CCER gibts nur für 
Timer. Dagegen ist der globale Define nur irgendeinem Timer zuzuordnen. 
Man hat in diesem Spezialfall halt Glück hat, daß alle 8 möglichen Timer 
die gleiche Registerbit Anordnung haben.

>Denkbar wäre, dass die Peripherie-Lib völlig unabhängig vom CMSIS
>stm32f10x.h entstand.

Das ist mit Sicherheit so, denn es gab auch schon V1 und V2 der Lib und 
das vor CMSIS. Siehe noch die alten ST Datentypen "vu" u.a. welche noch 
aus Kompabilitätsgründen mitgeschleppt werden.

>Jedenfalls sehe ich in diesen beiden Ansätzen ohnehin Antagonisten.

Darüber kann man streiten. Ich habe schon mehrfach CanOpen und USB 
Stacks in verschiedenen Assembler und C geschrieben aber an ein 
Filesystem oder Ethernet habe ich mich bislang nicht getraut. Da hoffe 
ich, daß CMSIS künftig etwas hilfreich sein könnte. Wahrscheinlich ist 
das auch selbst gut machbar und normal bin ich auch Fan von kompaktem 
Code. Es gibt verdammt gut optimierende Compiler - GCC gehört leider 
nicht dazu - aber beim Codeoverhead kommts halt auch drauf an wie der 
Programmierer eine Sprache einsetzt. Leute die vom PC kommen sehen das 
leider kaum ein. Bei Ethernet und Multimedia wirds sowieso anders. Ein 
http Bildchen mit 256x256 Pixel bei 8 bit Farbtiefe belegt schon mal 64k 
und was soll man dagegen noch an Code mit paar lächerlichen Bytes 
wegoptimieren? Summa Summarum hat die Portabilität ganz klar seinen 
Preis und deshalb sollte der Overhead hier kein Argument gegen CMSIS 
sein. Nichtsdestotrotz könnte man viel Code bei konsequenter 
Implementierung über Makros einsparen.

>Entweder man verwendet die Lib in aller Zeilen
>fressenden Umständlichkeit, oder man geht ans Eingemachte, an die
>Register.

Momentan mache ich das Erste und mein Code der eigentlich noch kaum was 
macht, belegt (ohne Optimierung) bereits über 50kbyte. Später wenn alles 
tut, werde ich das mal von Hand optimieren, vielleicht auch in 
Assembler.
Beim Abschalten des Timers war ich jetzt halt drauf angewiesen, im 
passenden Moment zwischen den Flanken abzuschalten und nicht irgendwann.
Die gesamte Lib lässt sich sowieso allermeist auf Register=Konstante 
eindampfen weil die ganze Rechnungen und Bitschieberei zur Laufzeit gar 
nicht gefragt sind. Die graphischen Beans (Motorola Codewarrier) und 
Infineons Dave sind im Overhead übrigens auch nicht viel besser. Darüber 
hinaus hat es noch nie geschadet, wenn man das Datenblatt gelesen hat.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> eigentlich sind sie sogar Timer Nr. 3 zuzuordnen,

Nicht dem Timer 3 sondern dem Compare Channel 3.

> Code. Es gibt verdammt gut optimierende Compiler - GCC gehört leider
> nicht dazu

So schlecht ist er nicht.

> Momentan mache ich das Erste und mein Code der eigentlich noch kaum was
> macht, belegt (ohne Optimierung) bereits über 50kbyte.

Du hattest meine Notiz mit -ffunction-sections gelesen?

> Overhead übrigens auch nicht viel besser und es hat noch nie geschadet,
> wenn man das Datenblatt gelesen hat.

Ist exakt mein Ansatz. Verstehen erspart Blindflug.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> aber an ein Filesystem oder Ethernet habe ich mich bislang nicht getraut.

Hehe, bei mir ist es eher umgekehrt.

CanOpen und Ethernet Stacks wohlmöglich mit TCP/IP sind aber auch eine 
ganz andere Liga als die real existierende Peripherielib. So puristisch 
bin ich nun auch wieder nicht. Auch Codegeneratoren sind ok, wenn der 
Kram funktioniert und ich in dem entstehenden Codesalat nicht rumferkeln 
muss.

Durchdachte Libs sind schon ok, wenngleich ich da vielleicht etwas eigen 
bin. Nur sehe ich nicht ein, weshalb ich für die Definition eines 
GPIO-Pins 8 Zeilen struct Gefummel schreiben muss, wenn das auch in 
einer einzigen geht:
  gpio_config(GPIOA, 1<<9, GPIO_Mode_Output2MHz | GPIO_AltOut_PushPull);

von J. V. (janvi)


Lesenswert?

Frischling schrieb
>Kannst du das bitte genauer erläutern?

Beim Cortex M3 gibt es ein T-Bit. Dieses ist offensichtlich eine ARM 
Altlast, welches zur Umschaltung beim Interwork Code benutzt wird. D.h. 
Wechsel von 32 bit Arm Opcodes auf 16 bit Thumb(2) Opcode werden vom 
T-Bit angezeigt. Siehe Arm Architecture Manuals beim T-Bit. Dies wird 
dadurch gemacht, daß alle 16 bit Befehle (32 auch) immer auf geraden 
Adressen ausgerichtet sind. Das T-Bit wird nun gesetzt, indem ein Sprung 
nicht auf die genaue Zieladresse zeigt, sondern auf die ungerade 
Zieladresse+1. Der Arm holt sowieso das ganze Wort ab abgerundeter 
gerader Adrese und der Opcode Decoder weis dann, daß es sich um einen 16 
bit breiten Thumb Befehl handelt. Das T-bit ist beim STM32 etwas 
bescheiden beschrieben, weil man es sowieso nicht braucht. Wenn dem 
anders sei, mögen mich die ARM Spezialisten bitte korrigieren.

Im allen startup.s steht nun ein Reset Vektor:

g_pfnVectors:
  .word  _estack
  .word  Reset_Handler .... usw.

Der Reset_Handler zeigt nun auf den Programmanfang zum Umkopieren von 
Initialsierten Daten die in der Lib so aussehen:

    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
  .thumb_func

Reset_Handler:

/* Copy the data segment initializers from flash to SRAM */
  movs  r1, #0
  b  LoopCopyDataInit

Der GCC Linker setzt nun die Adresse von Reset_Handler artig und genau 
passend bei .word Reset_Handler ein. Der Cortex M3 holt den Reset Vektor 
und versucht den dort stehenden Opcode zu dekodieren. Weil das eine 
gerade Adresse ist, wird das T-Bit nach Reset nicht gesetzt und die 
Dekodierung endet mit einem Hard Fault wegen unbekanntem Opcode. Es 
wirkt hier Wunder, indem man die "falsche" (richtige) Adresse  mit .word 
Reset_Handler+1 einträgt. Der Vektor zeigt auf die ungerade Adresse, das 
T-Bit wird gesetzt und alles ist ok.

Eine andere Möglichkeit den GNU Linker automatisch dazu zu bringen die 
Adresse passend einzutragen ist diejenige, daß der Reset_Handler mit 
.thumb_func verziert wird. Warum die GNU Toolchain das nicht bereits bei 
.thumb oder .cortex-m3 richtig machen kann bleibt mir auch unbekannt.

Die Compilerhersteller wissen das und drücken den .weak Reset_Handler 
mit einem eigenen korrekten Reset_Handler beiseite. Ein nackter GCC 
macht das nicht und deshalb grinsen sich die Damen und Herren 
Lizenzverkäufer einen ab, wenn andere damit auf die Fresse fliegen, mit 
GCC keinen Support haben bzw. selber nicht schnallen was fehlt. Hoffe 
das war ausführlich genug ...

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> Beim Cortex M3 gibt es ein T-Bit. Dieses ist offensichtlich eine ARM
> Altlast, welches zur Umschaltung beim Interwork Code benutzt wird.

Die grösseren Grosshirne (A8 und so) haben den ARM instruction set mit 
an Bord, also nix Altlast.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> .thumb_func verziert wird. Warum die GNU Toolchain das nicht bereits bei
> .thumb oder .cortex-m3 richtig machen kann bleibt mir auch unbekannt.

Vielleicht weil nicht alles was in der text section steht auch Code ist, 
und bei Datenadressen wärs mit gesetzten Bit 0 etwas peinlich. Und dass 
die Tools für ARVv7-M gegenüber ARMv7 keine Extrawurst braten ist 
nachvollziehbar, die müssen immer noch beide Varianten im Auge haben.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

A. K. schrieb:
> Und dass die Tools für ARVv7-M gegenüber ARMv7 keine Extrawurst braten ist
> nachvollziehbar, die müssen immer noch beide Varianten im Auge haben.

Die RealView tools haben damit aber keine Probleme. Wenn ich sage, dass 
mein Code für CM3 übersetzt werden soll, dann ist Thumb implizit aktiv 
und das LSB ist bei Funktionsadressen immer gesetzt.

Gruß
Marcus
http://www.doulos.com/arm/

von J. V. (janvi)


Lesenswert?

Cortex soll ja eine CPU sein, bei welcher man keinen Assembler mehr 
braucht. Faktor 2 traue ich mir gegenüber dem GCC aber allemal in vielen 
Fällen zu. Ein ausserordentlicher Spaß ist natürlich den in ASM 
vorliegenden Startup in C umzuschreiben um zu sehen ob GCC alles richtig 
macht. Tut tatsächlich:

void (* const Interrupts[])(void) _attribute_ ((section("vector"))) =
{
(void (*)(void))((unsigned long)RAMStack + sizeof(RAMStack)),
Reset, NMI, HardFault, usw...

void Reset(void)
{unsigned long *Src, *Dest;
   Src = &_etext;
   for(Dest = &_data; Dest < &_edata; ) *Dest++ = *Src++;
   for(Dest = &_bss; Dest < &_ebss; )   *Dest++ = 0;
   main();
}

> Die RealView tools haben damit aber keine Probleme

deshalb leben die Hersteller ja auch trotz GCC noch ganz gut. Wenn du 
das ReaView Teil am Start hast, wäre übrigens interessant ob es mit 
solchen Sitationen klarkommt: 
Beitrag "Computed Jump im DWARF2 Debugger Format"

von J. V. (janvi)


Lesenswert?

>Nur sehe ich nicht ein, weshalb ich für die Definition eines
>GPIO-Pins 8 Zeilen struct Gefummel schreiben muss,

das Struct Gefummel kann portabel bleiben wenn ein anderer Cortex ganz 
andere IO Ports implementiert hat die vielleicht sogar nur ein Subset 
sind.

> wenn das auch in einer einzigen geht:
> gpio_config(GPIOA, 1<<9, GPIO_Mode_Output2MHz | GPIO_AltOut_PushPull);

In deiner Anwendung mag das besser sein, nicht jedoch wenn du einen 
Protokoll Stack schreiben willst, welcher später auf allerlei anderen 
(CMSIS kompatiblen) Zielsystemen laufen soll. Das ist der Preis aber 
auch die große Chance von CMSIS.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> das Struct Gefummel kann portabel bleiben wenn ein anderer Cortex ganz
> andere IO Ports implementiert hat die vielleicht sogar nur ein Subset
> sind.

Bislang hatte ich CMSIS als eine Sammlung von Definitionen und 
Hilfsfunktionen für (a) den implementierungsübergreifenden Core und (b) 
die Peripherieeinheiten der jeweiligen Implementierung gesehen. 
Compilerübergreifend ja, stellt es aber eine 1:1 Abbildung der Register 
und Bits der konkreten Implementierung der Peripherieeinheiten dar.

Dass CMSIS davon abstrahierende will, das ist mir neu. Die aktuelle 
Version 1.30 jedenfalls vereinheitlicht nur die den Core betreffenden 
Aspekte.

Wenn sich CMSIS zu einem Layer entwickelt sollte, mit dem eine Anwendung 
für GPIO, SPI, CAN, die für STM32 enwickelt wurde, ohne grössere 
Änderungen auf Stellaris oder LPC1700 portierbar sei, dann Hut ab. Aber 
da weisst du offenbar mehr als ich, denn nichts an dem was ich davon 
bisher gesehen haben deutet darauf hin.

von (prx) A. K. (prx)


Lesenswert?

Das Gefummel hat auch nichts mit CMSIS zu tun, sondern betrifft die 
davon unabhängig entstandene Peripherielib von ST. Und dort hätte ST 
schon ein bischen üben können, wenn sie das gewollt hätten, nämlich 
zwischen den einzelnen bestehenden Varianten wie STR7, STR9 und STM32, 
um nur die 32-Bit Versionen zu nennen. Indes sind die entsprechenden 
Libs von STR9 und STM32 abgesehen von der mir schwer zugänglichen 
Vorliebe für structs noch deutlich inkompatibler zueinander als die 
zugrunde liegende Hardware.

Was ich als Erstes für Controller zusammenbaue sind eben solche 
Abstraktionen. Nicht für GPIO, das halte ich für ziemlich sinnlos, 
sondern für komplexe Module wie beispielsweise UART und CAN, die bei 
allem Unterschied in der Hardware nach oben hin durchaus ähnlich sein 
können. CMSIS hilft mir dabei, das CAN-Modul für STM32 von GCC ggf. auf 
IAR portieren zu können, aber eine Implementierung für STR9 ist 
notwendigerweise völlig anders.

von J. V. (janvi)


Lesenswert?

A. K. schrieb:

> Dass CMSIS davon abstrahierende will, das ist mir neu. Die aktuelle
> Version 1.30 jedenfalls vereinheitlicht nur die den Core betreffenden
> Aspekte.

Wie man bei CMSIS lesen kann:
The Middleware Access Layer is adapted by the Silicon Vendor for the 
device specific peripherals used by middleware components. The 
middleware access layer is currently in development and not yet part of 
this release.

> Wenn sich CMSIS zu einem Layer entwickelt sollte, mit dem eine Anwendung
> für GPIO, SPI, CAN, die für STM32 enwickelt wurde, ohne grössere
> Änderungen auf Stellaris oder LPC1700 portierbar sei, dann Hut ab. Aber
> da weisst du offenbar mehr als ich, denn nichts an dem was ich davon
> bisher gesehen haben deutet darauf hin.

Vielleicht sollten wir mal den STM8 genauer anschauen anstelle STM32 mit 
Arm7+9 zu vergleichen. Hier gibt es Compiler von Cosmic welche als gut 
otpimierend bekannt sind. Der STM8 ist eigentlich ein alter Motorola 
HC05 aber mit ausgerechnet fast der gleichen Peripherie wie der STM32. 
Damit kopiert STM das Controller Continuum von Freescale auf die eigenen 
Produkte. Oder wieso sollte ST neben den kleinen STM32 sonst noch eine 
neue 8 bit Familie auf den Markt bringen? Mit weniger als 2k Ram kann 
sich die Implementation die Structs nicht mal auf dem Stack erlauben und 
auch ein C Compiler braucht Qualitäten die aus einer Zeit stammen wo 
Großrechner ähnliche RAM Größen mit handgewickelten Ferritkernen hatten.

Trotzdem scheint der STM8 gleich ein nicht-ARM zu sein, welcher von der 
CMSIS Idee profitiert. Was dabei von ARM kommt, bzw. CMSIS ist und was 
von STM kommt verschwimmt da natürlich etwas weil man nicht weis, was 
sich STM selbst ausgedacht hat und was von CMSIS vorgeschrieben wurde 
wie es zu machen ist.

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> The Middleware Access Layer is adapted by the Silicon Vendor for the
> device specific peripherals used by middleware components.

Da bin ich mal gespannt.

Ein herstellerübergreifender Standard für zig völlig verschiedene 
Varianten von I/O-Modulen? Sowas klingt für mich irgendwie nach 
jahrelangen Komiteesitzungen und monströsem Ergebnis.

Oder ein eher lose definiertes Framework, in den jeder Hersteller seine 
chipspezifische und damit eben nicht portable Variante einigermassen 
stilkonform einbringen kann?

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

J. V. schrieb:
> Cortex soll ja eine CPU sein, bei welcher man keinen Assembler mehr
> braucht.  Faktor 2 traue ich mir gegenüber dem GCC aber allemal in
> vielen Fällen zu.

Nicht überraschend.

> Ein ausserordentlicher Spaß ist natürlich den in ASM vorliegenden
> Startup in C umzuschreiben um zu sehen ob GCC alles richtig
> macht.

Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben
wurde halte ich für eine verpasste Chance.

> void (* const Interrupts[])(void) _attribute_ ((section("vector"))) =

Wobei gerne vergessen wird (so auch bei CMSIS), dass man durchaus auch
mehrere Vector Tables aufsetzen kann. Also bitte in ein typedef
packen. Und dann das Alignment nicht vergessen:
1
/* Vector tables for Cortex-M3 must be aligned. The minimum alignment
2
 * is 32 words. The alignment of the vector table is calculated from
3
 * its size rounded up to the next power of two.
4
 */
5
#define _vec_tbl_align(a) (((16+(a) <= 32) ? 32 :
6
                            ((16+(a) <= 64) ? 64 :
7
                             ((16+(a) <= 128) ? 128 : 256))) * 4)
8
9
__attribute__ ((aligned( _vec_tbl_align(NUM_INT) ))) ...

Gruß
Marcus
http://www.doulos.com/arm/

von J. V. (janvi)


Lesenswert?

>Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben
>wurde halte ich für eine verpasste Chance.

das wird wahrscheinlich schwierig weil viel Symbole an dieser Stelle aus 
den Linkerfiles kommen oder irgendwo in den Quellen absolut passend zu 
den Speichergrößen festgenagelt sind. (Stacktop muss übrigens +2 heissen 
weil sonst wegen predekrement die oberste RAM Stelle nicht genutzt 
wird).

Es würde dabei dann aber nicht schaden bei CMSIS zumindest für GCC ein 
verbindliches Makefile mitzuliefern welches die entsprechende Nutzung 
mit passendem Linker File demonstriert. Wenn dieses dann für Keil, IAR 
und andere nicht passt, sollen diese Hersteller fürs Geld was tun und 
ein entsprechendes makefile bzw. Linkerfile selbst anpassen und 
bereitstellen.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

J. V. schrieb:
>>Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben
>>wurde halte ich für eine verpasste Chance.
>
> das wird wahrscheinlich schwierig [...]

So? Siehe auch cmsis_v1p20\Core\CM3\startup\{gcc,iar,tasking}

Gruß
Marcus
http://www.doulos.com/arm/

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Marcus Harnisch schrieb:
> J. V. schrieb:
>>>Dass der CMSIS startup code nicht tool-unabhängig in C geschrieben
>>>wurde halte ich für eine verpasste Chance.

Tool-unabhängig war zugegebenermaßen nicht der richtige Ausdruck. 
Assembler-unabhängig wäre wohl korrekter. Oder /weitestgehend 
tool-unabhängig/. Man wird tool-abhängig natürlich die Linker Defined 
Symbols auflösen müssen. Davon ist allerdings ein großer Teil des 
Startup codes nicht betroffen.

Dass der Startup code für einen Core, der damit beworben wird, dass er 
die Verwendung von Assembler unnötig macht, gerade in Assembler 
implementiert wird, entbehrt nicht einer gewissen Ironie.

Gruß
Marcus
http://www.doulos.com/arm/

von J. V. (janvi)


Lesenswert?

> Du hattest meine Notiz mit -ffunction-sections gelesen?

ja klar, besten Dank. Leider habe ich --print-gc-sections nicht zum 
laufen gekriegt weshalb ich dann etwas vorsichtiger File für File den 
Parameter dazugesetzt habe. Hier das Resultat:

Ohne Optimierungen: 47732 Bytes
adc 45372 Bytes
dma 45092 Bytes
flash 42895 Bytes
gpio 41672 Bytes
rcc 40808 Bytes
spi 39100 Bytes
tim 39092 Bytes
uart 30440 Bytes
misc 30246 Bytes

Man sieht, dass der Uart (wahrscheinich wegen der meist unbenutzten 
Synchron Modis) am meisten ausmacht. Die Anwendung passt jetzt in ein 
32k Low Density, was anderes habe ich in meinem Zielsystem nicht und es 
funktioniert noch immer alles prima.

Natürlich bin ich mit der Codegröße noch nicht zufrieden und ich schätze 
mal in Assembler hätte ich das in vielleicht 2 kbyte geschrieben. Also 
ziehen wir mal am Optimierer die Register. Von -O0 auf -Os bei allen 
Files ergibt das noch eine Gesamtgröße von 26016 Bytes. Leider 
funktioniert meine Anwendung jetzt auch nicht mehr. Eine grobe Analyse 
hat gezeigt, dass ein Interrupt zuviel kommt und anschliessend 
unpassende DMA Vorgänge daran schuld sind. Sehr schnell stellt sich 
stm32F10x_tim.c als Übeltäter heraus. Wenn ich hier was anderes als -o0 
nehme geht nix mehr. Das riecht danach, dass irgendwo ein volatile 
vergessen wurde oder GCC wegen der DMA auf die Fresse fliegt. Bei 
Gelegenheit guck ich vielleicht mal genauer. Ohne optimierten Timer gibt 
die Version mit rund 26kbyte aber noch immer ein erbärmliches Bild.

Das kommt davon, weil GCC für ein sprintf in der libc von malloc bis 
fseek in die Vollen langt. Derweil hat Hitex bei meiner Version 
offensichtlich überhaupt nichts an der lib optimiert. Der Versuch printf 
auf putchar umzuleiten endet in isatty mit einem Software Breakpoint. 
Eine eigenes bin2dez reduziert die Codegröße nun auf
(Trommelwirbel) gesamt: 6648 Bytes und es tut noch immer alles.

Es wäre interessant was Codesourcery, Rowley, Raisonance und so an libs 
mit kleinen printf können. Beim ST7 hatte ich mal einen kleinen printf 
von Cosmic der das Meiste konnte (keine float) und grad mal 2k Code 
gemacht hatte. Wäre also nett, was ein typischer printf mit %x oder %d 
bei den Anderen so an Code beiträgt und ob die gebührenpflichtigen 
Vollversionen der GCC vielleicht sogar mit Lib Quellen daherkommen?

von (prx) A. K. (prx)


Lesenswert?

J. V. schrieb:

> Das kommt davon, weil GCC für ein sprintf in der libc von malloc bis
> fseek in die Vollen langt.

Weshalb ich für solche Fälle meine eigene portable etwas vereinfachte 
und float-freie Version von printf() habe, die mir den ganzen 
platzraubenden newlib-Kram erspart.

> Es wäre interessant was Codesourcery, Rowley, Raisonance und so an libs
> mit kleinen printf können.

Für Crossworks kann ich das mal nachsehen. Bei Codesourcery tippe ich 
auf die Newlib, und die ist alles andere als platzsparend.

Crossworks hat für's Debugging eine eigene debug_stdio, in der die 
kompletten Funktionen in den PC ausgelagert sind (quasi per RPC) und die 
dementsprechend platzsparend ist. Funktioniert natürlich nur, wenn der 
Debugger die Kontrolle hat.

von (prx) A. K. (prx)


Lesenswert?

Crossworks V2 (GCC 4.4.2):

Ohne printf             1208 Bytes
debug_printf            1269 Bytes
eigenes rprintf         2212 Bytes
Crossworks printf       3116 Bytes

-Os, ohne float-support und nur ein Minimum an UART-Funktion ohne 
Interrupt.

Mein rprintf braucht 748 Bytes, der Rest der 1K Differenz sind ein paar 
Runtime-Funktionen für 64-Bit Arithmetik und Strings und der UART-Code.

von Random .. (thorstendb) Benutzerseite


Angehängte Dateien:

Lesenswert?

> Mein rprintf braucht 748 Bytes

Ich weiss natürlich nicht, was das alles kann :-)
Im Anhang mal meine Version, die ich mir mal ausgedacht hab. Wollte 
wissen, wie va_args funktionieren, und wie klein ich das krieg. Ca. 
200Bytes mit armcc auf -O3.

Kann:
- printf formatstring
- %i, %c, %s, führende nullen bis max. 8
- \t, \r, \n

Kann nicht:
- Noch kein Hex (kommt noch)
- Kaffee kochen

Bis auf HEX also das wichtigste drin.

---
Könnt ihr mit spielen, zerreissen, kaputtdiskutieren, selbst verwenden, 
erweitern, ..., was auch immer.
Konstruktive Vorschläge bzgl. Codegrössenverkleinerung oder 
Erweiterungen stets willkommen :-)


VG,
/th.

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

In ChaNs Beispielen zu seiner FAT-library findet man 
monitor.c/monitor.h. Dieses "rprintf" (er nennt es xprint) nutze ich 
gerne und oft. Bietet aber auch kein %f und %lf. 
(http://elm-chan.org/fsw/ff/ffsample.zip)

Weiterhin ganz schick ist "stdio.c" aus den Software-Beispielen zu AT91 
(UVZ utils) von Atmel. Lässt sich auch gut portieren und "überlagert" 
printf der libc (newlib bei CS G++, Yagarto, DevkitARM, WinARM etc.). 
Link-Reihenfolge beachten... Spart #defines beim "Vorentwickeln" von 
C-Code auf PC und späterer Anwendung auf µC.

Speicherplatzbedarf für beide habe ich bis dato nicht ermitteln müssen.

von Daniel B. (dbuergin)


Lesenswert?

Hättet Ihr diese Diskussion nicht etwas früher führen können ? (grins)

Nach diversen Frusterlebnissen mit der newlib (ich sehne mich nach der
avr-libc zurück....), habe ich:

http://www.sparetimelabs.com/tinyprintf/index.html

gefunden und bei mir implementiert. Funktioniert bis jetzt gut. Hat
auch keine Floatsupport, aber das stört mich nicht, macht man ja eh.
nicht habe ich von Euch gelernt ;-)

Daniel

von J. V. (janvi)


Lesenswert?

>gerne und oft. Bietet aber auch kein %f und %lf.
>(http://elm-chan.org/fsw/ff/ffsample.zip)

der Link geht leider grade nicht. Die Variadic Version von Thorsten 
scheint mir aber ziemlich ausgekocht zu sein. Va_args Makros sind schon 
in meinem ersten C Buch von 1987 drinnen aber ich habe sie noch nie 
benutzt. Ich habe den Flush weggelassen und fputc durch ein eigenes 
Putchar ersetzt. fputc aus der Lib tut bei mir sowieso nicht und es wird 
dann nicht jedesmal ein Filpointer stdout auf den Stack geschoben. Mit 
-Os komme ich auf 440 Bytes und mit -O0 auf 988 Bytes was ich vor allem 
gegenüber der newlib als sehr ordentlich gelten lassen möchte. Besten 
Dank an Thorsten - die 200 Bytes scheinen ohne Literal Pool gezählt zu 
sein ?

Auf der Wunschliste stehen natürlich noch die rechtsbündige Ausrichtung 
mit Leerzeichen anstelle führender Nullen, (evtl. auch Linksbündig) und 
zum Debuggen eben %x bzw. %X

>Nach diversen Frusterlebnissen mit der newlib (ich sehne mich nach der
>avr-libc zurück....)

Klar kann man sagen, daß die Lib Sache der Compilerbauer ist. Newlib 
wurde halt für RAM Maschinen mit MMU und so gemacht und da hat eine 
Reentrant und über File-Write voll gepufferte Ausgabe schon seine 
Berechtigung die vielleicht auch noch bei einem Cortex-A passen mag. 
Beim Cortex-M kocht dagegen jetzt wieder jeder sein eigenes Süppchen was 
wegen mehr oder weniger Salz letztendlich wieder inkompatibel ist. 
Vielleicht ist es ja eine Überlegung wert, ob CMSIS dabei nicht doch 
naheliegende Äquivalente zur avr-libc sozusagen als cm3-libc definieren 
kann, wobei dann die Compilerhersteller noch immer irgendwas eigenes 
machen könnten wenn sie meinen dies besser zu können.
Über stdout kann man letztendlich ebenso wie beim Startup streiten, ob 
es besser beim Compiler oder einer CMSIS HAL aufgehoben ist.

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

J. V. schrieb:
>>gerne und oft. Bietet aber auch kein %f und %lf.
>>(http://elm-chan.org/fsw/ff/ffsample.zip)
>
> der Link geht leider grade nicht.

Ja. ChaN mag offentsichtlich keine "deep links". Über die Homepage 
gehen: http://elm-chan.org dort gibt es derzeit einen direkten Link 
(update FatFs Module, dann "Samples") ansonsten über "Software" 
durchhangeln.

von Klaus W. (mfgkw)


Lesenswert?

A. K. schrieb:
> A. K. schrieb:
>
>>   UNIT.REGISTER = 1<<ENABLE | 1<<DISABLE | 1<<EXPLODE;
>
> PS: Das hat auch den Vorteil, dass man zur besseren Dokumentation der
> Einstellungen
>     UNIT.REGISTER = 1<<ENABLE | 0<<DISABLE | 0<<EXPLODE;
> schreiben kann. Also auch Bits explizit reinschreiben kann, die man
> gezielt nicht setzen will.

Das könnte man auch bei Masken ähnlich erreichen:
1
    UNIT.REGISTER = 1*ENABLE | 0*DISABLE | 0*EXPLODE;

von Random .. (thorstendb) Benutzerseite


Lesenswert?

J. V. schrieb:
> Die Variadic Version von Thorsten
> scheint mir aber ziemlich ausgekocht zu sein. Va_args Makros sind schon
> in meinem ersten C Buch von 1987 drinnen aber ich habe sie noch nie
> benutzt. Ich habe den Flush weggelassen und fputc durch ein eigenes
> Putchar ersetzt. fputc aus der Lib tut bei mir sowieso nicht und es wird
> dann nicht jedesmal ein Filpointer stdout auf den Stack geschoben. Mit
> -Os komme ich auf 440 Bytes und mit -O0 auf 988 Bytes was ich vor allem
> gegenüber der newlib als sehr ordentlich gelten lassen möchte. Besten
> Dank an Thorsten - die 200 Bytes scheinen ohne Literal Pool gezählt zu
> sein ?
>
> Auf der Wunschliste stehen natürlich noch die rechtsbündige Ausrichtung
> mit Leerzeichen anstelle führender Nullen, (evtl. auch Linksbündig) und
> zum Debuggen eben %x bzw. %X
>


Hi,

vielen Dank für's Lob!

Dadurch, dass der C Code dabei ist, kannste das printf ja etwas 
erweitern.
Die 200Bytes (oder genauer: 190) waren mit Cortex-M3 und armcc V3.x 
(Keil uVision 3.x) und -O3, allerdings fehlte der Support für die 
führenden nullen im thprintf.

---
Die "Gemeinheit" hat wohl keiner entdeckt **grins**
Ich verwende einen Pointer zum zwischenspeichern einer Zahl. Dies tut 
man normalerweise nicht, ist hier aber ok, da nicht schreibend drauf 
zugegruffen wird.

Ansatz war - neben va_args - wie man das möglichst klein kriegt in C :-)


---
Verwendung frei, wenn es irgendwo auftaucht würde ich mich über einen 
Hinweis a'la der Kommentare sehr freuen.


VG,
/th.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

> Die Variadic Version von Thorsten
> scheint mir aber ziemlich ausgekocht zu sein.

In der Tat steckt da doch einiges an Zeit drin.

> Ich habe den Flush weggelassen und fputc durch ein eigenes
> Putchar ersetzt.

Passt. Ich selbst habe ein "tierisches", OS like retargeting laufen, was 
ganz unten an der stdio anflanscht. Zusammen mit einem (eigenen) 
virtuellen Dateisystem a'la /dev/hw_driver.
Daher gehen alle höheren Funktionen den standard Weg, da sie dann immer 
richtig rauskommen.

Mein eigendliches putchar auf UART0,1,2,3, ITM, LCD, GLCD, whatEver 
kommt dann ein paar ebenen tiefer im Gerätetreiber :-)
Dies wird dann per f(re)open gesteuert. Ist sehr angenehm/flexibel, wenn 
auch auf dem µC alles OS like machen kann, z.B.

Cmd> echo "Hallo, Welt" > /dev/glcd0


VG,
/th.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

btw ... HEX fehlt noch.
Muss ich die Tage mal ergänzen :-)

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.