Forum: Compiler & IDEs WinAVR 20030913 veröffentlicht


von Joerg Wunsch (Gast)


Lesenswert?

Sonst habe ich hier Erics Ankündigung meist auch übersetzt, aber
erstens ist sie diesmal ziemlich lang, zweitens bin ich gesundheit-
lich noch nicht ganz auf dem Posten und möchte mir das daher sparen.

Damit also nur mal kurz der Hinweis auf Erics originale Ankündigung
in AVRfreaks:

http://www.avrfreaks.net/phorum/read.php?f=2&i=11301&t=11301#11301

Wenn jemand mit irgendeiner englischen Formulierung dort ein arges
Verständnisproblem hat, möge er hier fragen, und ich werde mich
bemühen, das zu erläutern.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Wenn ich das richtig verstanden habe, wurde leider nur an irgendwelchem
Zusatz-Schnulli rumgedoktert.


Der Compiler selber wurde leider nicht angefaßt.

Das Bit0-Problem ist jedenfalls immer noch da (siehe Anhang).

Einen Bit-Test von 4 Byte auf 16 Byte aufzublähen sieht erstmal nicht
viel aus, aber das ist ja auch nur ein minimiertes Beispiel.
Über einen größeren Code läppert sich da doch ganz schön was zusammen.


Überhaupt ist der WINAVR sehr 16-Bit lastig, womit man ja eine
8-Bit-Maschine doch ganz schön quält. D.h. er erweitert fast immer auf
16 Bit um schließlich das High-Byte wieder wegzuschmeißen.


Auch die Bibliotheken wollen wohl die heilige 16Bit-Kuh auf gar keinen
Fall schlachten, obwohl ich bezweifeln will, daß jemals einer ein
AVR-UNIX bauen will.


Was ich dagegen vermisse, und was wirklich sinnvoll ist, sind die
generic pointer, wie z.B. beim Keil.
Ist ja hübsch, daß alle Funktionen mehrfach vorhanden sind, je nachdem
ob Flash, SRAM oder EEPROM. Bloß damit ist leider die ganze
ANSI-Kompatibilität zum Teufel.
Aber die 3-Byte Pointer beim Keil, wo dann das 3. Byte den Speichertyp
definiert sind einfach absolut genial. Man hat dann wieder einen
flat-memory vom Programmierer aus gesehen und kann ANSI-Code schreiben.
Der Compiler legt die printf(), schanf() Formatstrings usw. automatisch
ins Flash und verschwendet keine Rechenzeit + SRAM mit unnützen
Kopierorgien.


Was ich mir noch wünschen würde ist, daß ich für Assembler-Interrupts
Register reservieren könnte, die dann der Compiler auch in Ruhe läßt.
So ein PUSH/POP kostet ja schließlich 4 Zyklen und das ist bei einem
Interrupt schon sehr teuer.

Beim Keil geht man den Weg, daß ja in etwa 90% der Fälle nur bis zu 3
Paramter einer Funktion übergeben werden und die restlichen 10% können
ja den Stack benutzen. D.h man muß doch nicht unbedingt 32 Register
freihalten, für den Fall, daß einer 16 Parameter übergeben will und so
unvernünftig ist, dies nicht mit einem Pointer zu machen.

Beim Keil gibts da die USING-Direktive, damit kann man einen
Registersatz reservieren und damit ist der dann für den Linker tabu.



Ich will das jetzt nicht als Meckerei verstanden wissen, sondern ich
wollte nur mal aufzeigen, wo noch massig Verbesserungspotential
vorhanden ist.


Beim Keil mußte ich jedenfalls mein Vorurteil, daß C wesentlich
langsameren und größeren Code erzeugt, aber derart gründlich
revidieren, da könnte ich mich heut noch schämen.
Aber beim AVR sind da immer noch große Unterschiede, die den Assembler
oftmals nur so davonpreschen lassen.



Peter

von Peter Fleury (Gast)


Lesenswert?

Peter,

Welche avrgcc Version verwendest du ?

Ich kann dein Beispiel mit der aktuellen WinAVR Version 20030913 gar
nicht kompilieren, wegen einer Fehlermeldung bei #include <signal.h>

Beim aktuellen AVRGCC sind die AVR spezifischen Includes in "avr"
Unterverzeichnis, deshalb muss die includes ändern:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

Mit dieser Modifikation kann ich dein Beispiel mit dem Sample Makefile
von WinAVR-20030913 fehlerfrei kompilieren,
der Test auf Bit0 wird aber effizient durchgeführt:


SIGNAL (SIG_OVERFLOW0)
{
  ca:  1f 92         push  r1
  cc:  0f 92         push  r0
  ce:  0f b6         in  r0, 0x3f  ; 63
  d0:  0f 92         push  r0
  d2:  11 24         eor  r1, r1
  d4:  2f 93         push  r18
  d6:  8f 93         push  r24
  d8:  9f 93         push  r25
  unsigned char i;

  i = PIND;
  da:  80 b3         in  r24, 0x10  ; 16
  if( i & 1 ){
  dc:  99 27         eor  r25, r25
  de:  80 ff         sbrs  r24, 0     <- Test auf Bit 0 !
  e0:  09 c0         rjmp  .+18       ; 0xf4
    if( i & 2 )
  e2:  20 91 00 01   lds  r18, 0x0100
  e6:  81 ff         sbrs  r24, 1
  e8:  02 c0         rjmp  .+4        ; 0xee
      j--;
  ea:  21 50         subi  r18, 0x01  ; 1
  ec:  01 c0         rjmp  .+2        ; 0xf0
    else
      j++;
  ee:  2f 5f         subi  r18, 0xFF  ; 255
  f0:  20 93 00 01   sts  0x0100, r18
  }
}
  f4:  9f 91         pop  r25
  f6:  8f 91         pop  r24
  f8:  2f 91         pop  r18
  fa:  0f 90         pop  r0
  fc:  0f be         out  0x3f, r0  ; 63
  fe:  0f 90         pop  r0
 100:  1f 90         pop  r1
 102:  18 95         reti

von Peter D. (peda)


Lesenswert?

@Peter

Ich verwende schon die neue, aber ich habe alle Includes nach
"include" verschoben, da ich keine Lust habe, dem AVR eine Extrawurst
zu braten.
Alle anderen Compiler benutzen ja auch den Standard-Pfad.


Warum es bei Dir geht ist mir eine Rätsel.

Ich benutze folgende Batch:

avr-gcc.exe -Os -mmcu=at%mcu% -Wall -g -o main.out *.c
avr-objdump.exe -t -h -S main.out >%main%.lst
avr-objcopy.exe -O ihex main.out %main%.hex


Benutzt Du vielleicht spezielle Compiler-Optionen ?


Peter

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

Hi

ich kann das Ergebnis von Peter F. nur bestätigen. Irgendwas machst du
wohl verkehrt.
Ich hab mal ein makefile angehängt dem du meine Compileroptionen
entnehmen kannst.

BTW:
Warum alles was zum avr gehört in /avr steht hat ja Jörg schonmal
erklärt.

Matthias

von Peter D. (peda)


Lesenswert?

@Matthias,

danke, ich hab mir Dein Make angesehen und einen Unterschied
festgestellt:

Sobald ich als Quellfile "test.c" eingebe gehts.
Wenn ich aber "TEST.C" eingebe kommt mein obiges Listing raus.
Da muß aber erstmal einer drauf kommen.

In der DOS-Box (W98SE) wird aber "*.c" grundsätzlich nach Großschrift
expandiert.
Weiß da jemand Rat ?
Warum macht der Compiler überhaupt diesen Unterschied ?
Gibts da vielleicht einen Compilerschalter ?


Peter

von Peter Fleury (Gast)


Lesenswert?

Das WinXX-Filesystem merkt sich die Gross/Klein Schreibung bei
File/Directory Namen, nur der DOS-Box ignoriert es.
Der Windows-Explorer zeigt aber die Gross/Klein Schreibung von
Pathnamen richtig an.

Das Makefile zusammen mit Make und den avr-gcc Tools erkennen aber die
Gross/Kleinschreibung von Filenamen korrekt.

Deshalb sollte zum kompilieren das Makefile verwendet werden und nicht
ein Batch File.

Tips wie man das WinAVR Beispiel Makefile anpassen muss gibt's hier:
http://www.avrfreaks.com/filednload.phpurl=/Tools/ToolFiles/376/install_config_WinAVR.pdf

von Peter Fleury (Gast)


Lesenswert?


von Matthias (Gast)


Lesenswert?

Hi

.C steht AFAIK für C++.

Matthias

von Peter D. (peda)


Lesenswert?

Ich hab jetzt einfach in avr-gcc.exe ".C" durch ".Z" ersetzt. Nun
funktioniert alles wie gewünscht.

Der Grund, warum ich kein Make verwenden will ist, daß es nicht mit
Platzhaltern "*.c" zurechtkommt. D.h. ich müßte jedesmal alle
Source-Files manuell eintragen.
Und eine Batch kann das eben.


Peter

von Matthias (Gast)


Lesenswert?

Hi

das macht man einmal für das Projekt und verwaltet dann pro Projekt ein
makefile. Und in einer Zeile einmal den Dateinamen einzutragen ist ja
durch den Komforforteil von makefiles locker aufgewogen. Aber wir
kennen ja deine Einstellung: "Was der Bauer nicht kennt das frisst er
nicht" ;-)

Matthias

von Peter D. (peda)


Lesenswert?

@Matthias,

"...das macht man einmal für das Projekt..."

ich arbeite nach der Top-Down Methode, d.h. es entstehen viele kleine
Objekte erst wärend des Programmierens. 50 Source Files sind bei mir
nichts ungewöhnliches. Ich schreibe nach dem Adlersuchsystem und da ist
jede Tipperei-Einsparung herzlich willkommen.


"...Komforforteil von makefiles..."

Ich weiß, die Batch kompiliert immer alles komplett, das fällt aber auf
den heutigen PCs kaum ins Gewicht.
Dafür hat die Batch eben den Vorteil der Wildcards und auch Errorlevel
kann man auswerten oder Abfragen machen usw.


"...Was der Bauer nicht kennt..."

Ich würde eher sagen "Warum soll ich was anderes nehmen, wenn sich das
bisherige bereits glänzend bewährt hat"
Bestimmt würden sich auch umgekehrt ne Menge GCC-Gurus mit dem Keil
schwer tun. Das ist ja nur menschlich.
Und mit meinem GCC-Patch bin ich auch voll zufrieden.


Peter

von Joerg Wunsch (Gast)


Lesenswert?

@Peter Danneger:

Bin gerade aus dem Urlaub zurück und kann mich daher erst heute
Deiner ,,Meckerliste'' widmen.

Erstmal: Du hast das Prinzip von WinAVR und Opensource generell
einfach noch gar nicht verstanden.  WinAVR stellt lediglich
existierende Sachen Windows-Nutzer-freundlich zusammen, nicht
mehr, aber auch nicht weniger.  Eric Weddington hat damit genügend
Arbeit (da ein guter Teil dieser Software nun einmal unter Unix
entstanden ist und nach wie vor vorrangig dort gepflegt wird), so
daß er selbst nur an einigen Teilen aktiv mitentwickelt (namentlich
avr-libc und avrdude).

,,wurde ... leider nur an irgendwelchem Zusaatz-Schnulli
rumgedoktort'' zeigt, daß Dir das grundlegende Gefühl für
Opensource fehlt.  Es ist nicht ,,man'', der hier etwas entwickelt,
sondern es sind die Leute, die es tun.  Jeder Einzelne.  Damit
etwas passiert, benötigt es eine Triebkraft, die besteht zu einem
gewissen Prozentsatz auch und natürlich aus Ehrgeiz, aber auch zu
einem Gutteil daraus, daß jemand in irgendeiner Form eine Motivation
besitzt.  Wenn Du also irgendein Feature ganz besonders dringend
benötigst, dann bist genau Du es, der diese Triebkraft besitzt.
Im Gegensatz zu kommerzieller Software bezahlst Du aber niemanden
dafür, daß er Dir das dann irgendwann implementiert oder auch nicht.
Du kannst nun also entweder hoffen, daß irgendjemand anders das
Feature auch mal für so wichtig findet, daß er dahinein Zeit
investieren möchte -- oder Du fängst es selbst an.

,,Kenn ich mich ja gar nicht aus!'' ist übrigens keine akzeptable
Ausrede.  Das ist wie mit der Regel, daß man nur Pilze essen soll,
die man kennt: zum Zeitpunkt seiner Geburt kennt man gar keine, und
die Kentnis über alle anderen erwirbt man sich mit der Zeit.

Wenn Dir dieses Opensource-Modell nicht gefällt, nun, dann bist Du
hier einfach falsch.  Dann such Dir einen kommerziellen Anbieter,
kaufe sein Produkt, und benutze dessen Support.

,,WinAVR'' ist übrigens nicht ,,16-bit lastig'', kann es gar nicht
sein: es ist ja nur eine Sammlung von Werkzeugen.  Du meinst
vermutlich den gcc (in seiner Ausprägung als avr-gcc), aber dann
schreib das bitte auch.  Daß `int' standardmäßig 16 bits breit ist,
ist, und das sollte Dir nicht fremd sein (Du bist ja kein C Neuling)
eine Forderung des C-Standards.  Daß Ausdrücke in C oftmals als
`int' interpretiert werden, ist ebenfalls eine Forderung des
Standards.  Wenn Dir das alles nicht gefällt, weil Du 5000000
Mobiltelefone im Jahr verkaufen willst und daher jeden Cent bei den
Controllern sparen mußt, dann ist vermutlich C gemäß dem
anerkannten Standard nicht die Programmiersprache Deiner Wahl.
Daraus kannst Du Dir mehrere Auswege suchen:

. Du kannst in Assembler programmieren.  Hohe Softwarekosten, aber
  die amortisieren sich trotzdem durch die Stückzahl.

. Du kannst einen Compiler für eine Programmiersprache benutzen,
  die besser auf die Bedürnisse von Microcontrollern ausgerichtet
  ist als C -- wenn Du denn einen findest, der gut genug ist, auch
  sonst das nötige sinnvolle Maß an Optimierungen zu bieten, an die
  man sich bei guten C-Compilern eben gewöhnt hat.

. Du kannst versuchen, einen Kompromiß zu finden.

Das soll natürlich keineswegs heißen, daß die Optimierungen des
avr-gcc nicht an manchen Stellen auch noch besser sein könnten.
Pathologische Fälle lassen sich ohnehin immer leicht finden, aber
auch Dinge, die im sehr praktischen Betrieb nennenswertes Potential
für Einsparungen bieten, sind zu finden.  Es sei an die Möglichkeit
einer globalen Substitution von CALL/JUMP durch RCALL/RJMP bei den
MCUs > 8 KB ROM verwiesen.  Wurde in der avr-gcc Liste schon als
Möglichkeit diskutiert.

In die dritte Variante da oben kann man -mint8 vom avr-gcc hinein
sortieren.  Der Compiler verläßt damit die Forderungen des C
Standards, wird im Gegenzug ein wenig resourcensparender.  Der
Haken bei der Sache ist aber einfach, daß die Standard-C-
Bibliothek in ihren Funktionsprototypen durch den Standard
festgelegt ist und daß dort sehr oft ein Typ `int' vorgeschrieben
ist, der von der Forderung implizit ausgeht, daß ein `int'
mindestens den Wertebereich -32767 bis +32767 umfassen muß.  Als
Beispiel sei mal getc() genannt, das in der Lage sein muß, einen
von allen legalen Zeichen-Werten verschiedenen Wert mit dem Namen
EOF zurückzugeben.  Diese Forderung läßt sich mit einem 8-Bit Typ
`int' nicht realisieren, wenn zugleich auch die Forderung nach
den legalen Zeichenwerten 0...255 gestellt wird.  (Für reines 7-bit
ASCII wäre es mit einem 8-bit `int' realisierbar, aber wer will
das schon?)

Unseren Standpunkt zur -mint8 Kompatibilität der avr-libc bzw. deren
Header-Files habe ich neulich auf der avr-libc Entwicklerliste mal
zusammengefaßt und dabei keinen Widerspruch seitens der anderen
Entwickler geerntet: wird werden nichts tun, um das Funktionieren
von -mint8 zu behindern, wir werden gemeldete Bugs nach Möglichkeit
beseitigen (wenn ein Patch beiliegt, der keine weiteren Neben-
wirkungen besitzt, dann steigen die Chancen für die Beseitigung
logischerweise), aber die ausgelieferte libc.a ist halt ohne -mint8
compiliert und folglich zu einem mit -mint8 compilierten Objekt
nicht ABI-kompatibel.

Ein möglicher Ausweg daraus wäre, daß man bei -mint8 auch noch eine
separat compilierte libc.a linkt (das kann der Compiler-Treiber
automatisch machen), die dann selbst mit -mint8 compiliert worden
ist.  Aber siehe obiges Beispiel, ein Teil der derzeit in der
Bibliothek enthaltenen Funktionen ist wohl damit gar nicht sinnvoll
compilierbar, ein Teil wiederum wird nicht mit standardkonformen
Funktionsprototypen compilierbar sein.  Darum könnte man sich mit
geschickten #ifdefs und cpp-Makros herummogeln, d. h. im Falle von
-mint8 mutieren die Header-Files ihre Prototypen in solche, die
nicht mehr standardkonform sind (stört nicht, da der ganze Compiler
ja ohnehin nicht mehr konform ist) bzw. bieten keinen Prototypen
für die dann nicht vorhandenen Funktionen an.  Nicht zu vergessen
sei die Dokumentation: für jedes Feature, jede Funktion wäre dann
anzugeben, ob es bei -mint8 verfügbar ist (und ggf. der abweichende
Prototyp).  Das ist ein gutes Stück arbeit, aber wenn das jemand
ernsthaft vorhat und es dann auch bereit ist weiter zu pflegen,
wird es sicherlich keinen Widerspruch geben.  Von den derzeit
vorhandenen aktiven avr-libc Entwicklern hat niemand die nötige
Motivation dafür.

Die Argumentation ,,es solle ja kein Unix darauf laufen'' ist
übrigens so ziemlich die dämlichste, die ich je gelesen habe.
Erstens gibt es sicherlich eine Reihe von Mini-Betriebssystemen
für MCUs, und viele davon würden vermutlich genaus übern Haufen
fallen wie ein (altes) Unix, wenn `int' plötzlich nicht mehr
standardkonform wäre.  Zweitens sind selbst die ältesten Compiler
für Mikroprozessoren, die ich so kenne, vor allem auch Turbo Pascal
für CP/M (aber auch alle CP/M C-Compiler) von 16-bit integer
Datentypen ausgegangen -- auf einer viel schwachbrüstigeren CPU als
einem AVR.

Die `generic pointer' klingen auf den ersten Augenblick ja hübsch,
aber sind natürlich eine arge Pessimierung: zur Laufzeit muß dann
für jeden Zeiger geprüft werden, in welchem Speichersegment er
liegt -- obwohl wahrscheinlich bis auf wenige Ausnahmen der
Programmierer bereits gewußt hat, in welchem Segment er liegt, und
er (statt der Bibliothek) hätte die passende Funktion bereits
wählen können.  Aber ansonsten: nur zu, diskutiere Deine GCC Patches
auf der avr-gcc Liste mit anderen Entwicklern, ich denke schon, daß
Du diesen oder jenen dafür begeistern könntest...

Gleiches gilt natürlich für Deine Idee, in ISRs bestimmte Register
zu reservieren, wenngleich ich hier vielleicht sehe, daß Du sogar
jemanden finden könntest, der die Idee aufgreift und die
Implementierung vorantreibt, wenn Du es nur bei den passenden
Leuten diskutierst.  Allerdings solltest Du Dich natürlich vorher
über die tatsächlichen Gegebenheiten bessern informieren :), so
werden natürlich keinesfalls 32 Register für Parameter reserviert
sondern nur 8 -- und das genügt schon dann nicht mehr wirklich,
wenn da außer bißchen Klimbim vielleicht noch ein uint32_t oder
float ins Spiel kommt.  Der Rest wird dann dennoch in Registern
übergeben, aber nicht mehr in reservierten, d. h. der Aufrufer muß
mit PUSH/POP arbeiten (was offensichtlich insgesamt effektiver ist,
als wenn der Aufgerufene die Parameter laufend wieder aus dem
Stack popel müßte).

printf()-Formatstrings kannst Du auch beim avr-gcc im ROM haben, Du
hast die volle Kontroller: benutze PSTR() und die *_P() Varianten
der Funktionen, und schon bist Du dabei.  Der Zugriff auf die
Strings ist dann naturgemäß langsamer, aber im umgekehrten Falle
hättest Du wiederum ja keine Möglichkeit, den Compiler zu zwingen,
die schnellere Variante (String im RAM) überhaupt zu generieren.
Nachdem ich auch ein bißchen im AVR-Butterfly-Code gestochert habe
(der ja für den IAR geschrieben ist, der vieles automatisch im ROM
handhaben kann) bin ich mittlerweile gar nicht mehr so sehr davon
überzeugt, daß diese Automatismen wirklich so gut sind, wie ich
anfangs dachte...  Die avr-gcc Variante (die ja mehr aus der Not
geboren ist, daß der GCC sehr stark von-Neumann-lastig ist) zwingt
halt den Programmierer, sich immer gleich Gedanken um das
Speichermodell zu machen.

von Peter D. (peda)


Lesenswert?

@Joerg,

vielen Dank für Deine ausführlichen Worte. Da habe ich Dich wohl auf
dem falschen Fuß erwischt.

Die Hauptsache, das Bit0-Verhalten, das mich so gestört hatte habe ich
ja lösen können. Wenngleich mir zwar nicht klar ist warum die DOS-Namen
"*.C" sowas bewirken, aber egal, jetzt läufts.


Ansonsten ist der WINAVR schon sehr gut benutzbar, läuft auf Anhieb.
Mein allererster Versuch mit dem AVR-GCC scheiterte damals schon bei
der Installation von Cygwin, was ja nun nicht mehr benötigt wird.


Es ist mir auch schon kar, daß die AVR-Architektur nur sehr schwer aus
C optimalen Code generieren läßt. Dazu müßte man ja
funktionsübergreifend eine Art Registerrenaming durchführen und sowas
stelle ich mir nicht so einfach vor.
Der Herr Keil hat beim C51 bestimmt auch lange getüftelt, ehe er das
effiziente Data-Overlaying hingekriegt hatte.


Aber selber an sowas mitzuprogrammieren traue ich mir nicht zu.
Mir haben es die kleinen MC-Programme angetan, wo ich jedes Byte noch
persönlich kenne.
Bei den Windows-Programmen ist mir immer wieder unheimlich, womit diese
denn bloß ihre hunderte MB (bald schon GB) sinnvoll
ver(sch)wenden.


Ein bischen hadere ich ja immer noch mit den fehlenden
Interruptprioritäten beim AVR im Vergleich zum 8051. Deshalb mein
Bestreben, die Interrupts so kurz wie möglich zu halten.
Da ist ja gerade auch ein Thread über Registervariablen, vielleicht
kann ich so Register für Interrupts benutzen. Und irgendwo werde ich
wohl auch finden, wie man Interrupts in Asssembler mit in ein
C-Programm einbindet.


Ich verstehe durchaus, daß Anfängerfragen nerven, die immer wieder
gestellt werden. Aber manchmal sind das Manual lesen und auch verstehen
verschiedene Dinge, bzw. man überliest oder findet einfach nicht die
richtige Stelle.
Das mit dem malloc()+devopen() habe ich immer noch nicht 100%-ig
kapiert, aber egal, genug die Gurus genervt :-)



Peter

von Joerg Wunsch (Gast)


Lesenswert?

Nein nein, Du hast mich überhaupt nicht auf dem falschen Fuß
erwischt. ;-)  Mich ärgert immer nur die häufig anzutreffende
,,Konsumenten-Mentalität'' im Zusammenhang mit Opensource.  Liegt
vielleicht daran, daß ich Opensource schon sehr lange kenne und
dann auch schnell Spaß gefunden habe, daran mitzuarbeiten (in den
verschiedensten Formen, beginnend beim einfachen Patch für den
Emacs, über Mitarbeit an einem Betriebssystem in teilweise
,,leitender Rolle'', bis nun zu den AVR-Sachen).  Ich empfand es
immer als eine Befreiung, mit Projekten arbeiten zu dürfen, bei
denen man auch den Sourcecode einsehen kann.

> Wenngleich mir zwar nicht klar ist warum die DOS-Namen "*.C"
> sowas bewirken, aber egal, jetzt läufts.

Dazu ist sicher in bißchen Unix-Background hilfreich.  Unix war
wohl das erste System, das sich darauf besonnen hat, daß der
ASCII-Zeichensatz ja auch Kleinbuchstaben enthält.  BIS DAHIN WAR
ES DURCHAUS GÄNGIG, DAß DIE PROGRAMMIERER AN IHREN COMPUTERN
AUSSCHLIEßLICH ALLES IN GROßBUCHSTABEN GETIPPERT HABEN.
(Implizites CAPS LOCK im Terminal eingeschaltet.  Sowas war, als
ich mit einem PDP-11 Clone begonnen habe, auch bei uns noch gang
und gäbe.  Man mußte erstmal ins Terminal-Setup und das
ausschalten.)  Man erkannt das an vielen Dingen: das Dateisystem
unterscheidet Groß- und Kleinbuchstaben, und beide bedeuten etwas
Verschiedenes.  Ich kann also zwei verschiedene Dateien
"Makefile" und "makefile" in einem Verzeichnis haben.  Auch die
Programmiersprache C war wohl die erste, bei denen der Bezeichner
Foo etwas anderes als foo ist -- heute sehen wir das als völlige
Normalität an.

Mit dem Dateisystem, das sich Groß-/Kleinschreibung nicht nur
gemerkt hat (das machen praktisch alle Dateisysteme, selbst CP/M
konnte das, obwohl offiziell immer alles in Großbuchstaben
konvertiert worden ist) sondern auch unterscheidet, hat sich dann
auch herausgebildet, daß in bestimmten Fällen Dateinamensendungen
in Groß- oder Kleinschreibung eine unterschiedliche Bedeutung
haben.  Anders als bei CP/M, RSX-11, VMS, MS-DOS (aber genauso
wie z. B. bei RIO, einem uralten Z80-Betriebssystem) ist bei Unix
der Dateinamenssuffix mitsamt dem trennenden Punkt ein ganz
regulärer Namensbestandteil ohne ausgezeichnete Bedeutung im
Dateisystem.  Er muß nicht zwingend existieren, und es kann auch
beliebig viele Punkte im Namen geben.  Da Unixer besonders
schreibfaul waren (was der Sage nach auf die unsäglich lahmen
alten 100 Bd Fernschreiber zurückgeht, die anfangs als Terminals
gedient haben) und alles sehr kurz gemacht haben (zweibuchstabige
Kommandos gibt es beispielsweise für alle wesentlichen
Operationen), haben sie typisch auch nur einbuchstabige Suffixe
gewählt.

Damit hat es sich dann eingebürgert, daß beispielsweise zwischen
zwei Formen von Assemblerdateien unterschieden wird, die auf .s
bzw. .S enden (das ,s' steht wohl für `source', stammt also aus
einer Zeit, in der noch vorwiegend Assemblerquellen geschrieben
worden sind): Dateien, die auf .s enden, sind klassische
Assemblerquellen, während solche, die auf .S enden, zuerst durch
den C-Präprozessor gezogen werden.  Damit kann man dessen
Manipulationsmöglichkeiten benuten (#include, #define usw.), was
sich im Laufe der Zeit als recht praktisch erwiesen hat.
Mittlerweile sind wir eigentlich an dem Punkt, da niemand mehr .s
Dateien selbst schreibt sondern bestenfalls .S, während das
Zwischenergebnis des C-Compilers in Dateien liegt, die auf .s
enden -- diese können dann beliebig gelöscht werden, wenn sie
nicht mehr gebraucht werden.  Unter Unix kann man gefahrlos ein
"rm *.s" machen, unter DOS würde das auch *.S mit löschen...

Anyway, in gleicher Linie wurde es bei Einführung von C++ dann
üblich, diese Dateien statt auf .c (C-Quelldateien) einfach auf
.C enden zu lassen.

Damit bin ich nun endlich wieder am Ausgangspunkt angekommen:
indem Du dem Compiler-Treiber (was anderes ist das Kommando
avr-gcc selbst nicht, es ruft nur die einzelnen Schritte mit
passenden Parametern auf, compiliert oder linkt aber nichts
selbst) die Dateinamen mit dem großen C im Suffix um den Hals
gehängt hast, hat er den Compiler angewiesen, sie als C++-Quellen
zu behandeln.  Nun sind zwar die meisten sauber geschriebenen
Standard-C-Quellen auch zugleich gültige C++-Quellen, aber die
Semantik ist zuweilen eben doch verschieden...  Das hat zu Deinem
Bit0-Problem geführt.

Du hättest das übrigens auch durch eine explizite Forderung
verhindern können, die Programmiersprache als C zu betrachten:
»-x c«.

> Es ist mir auch schon kar, daß die AVR-Architektur nur sehr
> schwer aus C optimalen Code generieren läßt.

Naja, gar nicht so zwingend.  Gerade beim Design des AVR hat man
rechtzeitig (durch Kooperation mit IAR) darauf geachtet, einen
Prozessor zu bauen, der möglichst gut auf die Bedürfnisse von
Hochsprachcompilern abgestimmt ist.  In irgendeinem Dokument auf
der Atmel-Seite ist das ja erläutert.  Allerdings sind die
Vorstellung von »optimal« wohl nicht immer deckungsgleich mit
Deinen Vorstellungen davon ;-), zumal es ja ohnehin immer nur
einen Kompromiß zwischen Geschwindigkeit und Codegröße geben
kann.  Zuweilen kommen mir Deine Argumente hier auch ein bißchen
»religiös« vor: Du plädierst für die generic pointers, bei denen
die Entscheidung über den tatsächlichen Speicherbereich erst zur
Laufzeit erfolgt (was ja auf jeden Fall Laufzeit kosten wird),
andererseits stören Dich die PUSH/POPs für die in einer ISR
benutzten Register, weil sie paar Taktzyklen kosten.

> Bei den Windows-Programmen ist mir immer wieder unheimlich,
> womit diese denn bloß ihre hunderte MB (bald schon GB) sinnvoll
> ver(sch)wenden.

Wäre mir auch unheimlich dabei :), zum Glück mache ich kein
Windows.  Ich kann Dir aber versichern, daß man sich mit der
nötigen Energie in jeden ordentlichen Quellcode reinfinden kann.

> Ein bischen hadere ich ja immer noch mit den fehlenden
> Interruptprioritäten beim AVR im Vergleich zum 8051.

Nun, ich kenne die Prioritäten zwar vom 8259 aus dem PC ein
bißchen, aber in der Praxis werden die dort auch praktisch nur
einmal fest vergeben und dann nie wieder angefaßt.  Ich habe kein
Gefühl, wie oft man die bei MCS51 wirklich benutzt hat...

> Deshalb mein Bestreben, die Interrupts so kurz wie möglich zu
> halten.

Das ist sicherlich sowieso immer die sinnvollste Variante.  Eine
ISR macht ganz schnell das, was zeitkritisch zu erledigen ist,
und setzt dann ein Flag, das später die asynchrone
Weiterbearbeitung des Ereignisses ermöglicht.

Aber Du solltest bei Deinen Betrachtungen eins nicht vergessen:
Register sind das Wertvollste, was ein Prozessor zu bieten hat,
ausgiebige Nutzung von Registern spart Rechenzeit und im Falle
des AVR auch Code ein, insofern halte ich die derzeit vom GCC
(und nicht nur von ihm) gewählte Strategie, ggf. ein paar
Register durch PUSH/POP freizuräumen, für eine durchaus
sinnvolle.  Anders gesagt: »Never try to optimize something
before you have profiled it.«  Es kann gut sein, daß Du in einem
Timer-Interrupt, der alle 100 CPU-Takte gerufen wird, mit allem
wirklich geizen solltest und auch durch Reservierung einer fest
gebundenen Registervariablen etwas gewinnst, aber wenn Du zu
viele Registervariablen fest bindest, entziehst Du dem Compiler
Register, die er zur Optimierung anderer Stellen benötigen würde.

> Und irgendwo werde ich wohl auch finden, wie man Interrupts in
> Asssembler mit in ein C-Programm einbindet.

Was ist so schwierig an Assembler-Interrupts und ihrer Einbindung
in C?  Stell' eine konkrete Frage, und sie wird sich beantworten
lassen.  Aber siehe oben, ich würde mir die Mühe nur für wirklich
zeitkritische Dinge machen.

> Ich verstehe durchaus, daß Anfängerfragen nerven, die immer
> wieder gestellt werden.

Nein, ich sehe das nicht als nervige Anfängerfragen, sondern als
eine sinnvolle Diskussion, die naturgemäß natürlich auch mal mit
provokativen Argumenten geführt wird.

von Schmittchen (Gast)


Lesenswert?

Danke für den sehr interessanten Ausflug in die Historie und hinter die
Kulissen.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

@Joerg,

vielen Dank.

Es freut mich sehr, daß Du andere (provokative) Meinungen auch als
Diskussionsanregeung verstehst und nicht gleich ausfallend wirst.

Das -x c hat geholfen und ich habe nun meinen Patch wieder entfernt.
Steht ja auch eindeutig in der untersten Zeile des Hilfetextes. Nachdem
man weiß, was der Grund ist, fällt es sofort ins Auge, aber vorher
sieht man eben den Wald vor lauter Bäumen nicht.


Zu den generic Pointern, das ist natürlich nur die eine Hälfte der
Medaille. Beim Keil sind die Bibliotheksfunktionen (z.B. strcpy(),
printf()) so geschrieben, daß sie generic Pointer bekommen, d.h. sie
funktionieren auf jedem Speicherbereich.


Die andere Seite sind die "memory specific pointer", z.B.:

char code *foo;

bezeichnet einen Pointer auf den Flash.
Der Vorteil davon ist, man kann auch auf solche Pointer sämtliche
C-üblichen Pointeroperationen anwenden bzw. solche Pointer an
Funktionen übergeben usw.
Mit:

#ifdef _TURBOC_
#define char
#define data
#define xdata
#endif

kann ich diese Specifier einfach ausblenden und dann solche Routinen
auch auf dem PC testen.

Der Vorteil ist wie gesagt, daß der C-Compiler ohne weiteres Zutun die
richtigen Zugriffsbefehle bzw. Unterfunktionen verwendet.

In einer universellen Bediensoftware habe ich allein etwa 16kB an Code
benötigt für verschiedenste Strukturen, um die verschiedensten Module
zu installieren und zu bedienen.
Das Gerät hat 16 Modulschächte und sucht in einer Liste aus den
möglichen Modulkonfigurationen diejenige aus, als die es arbeiten
soll.
Danach werden diese Module installiert und nur die änderbaren Parameter
Sollwert, Istwert) im RAM abgelegt. Alle anderen (z.B. Anzeigetext,
Zahlenformat, Remotestring usw.) werden immer direkt aus dem Flash
gelesen. Damit ist nur ein minimaler RAM belegt.

Anbei mal eine kleine Beispielroutine daraus, die Pointeroperationen
(.,->) verwendet.


Memory specific Pointer können auch an Funktionen übergeben werden, die
generic Pointer erwarten und werden dann entsprechend erweitert, aber
nicht umgekehrt.



Beim GCC habe ich den Eindruck, daß es nicht so einfach ist, Strukturen
im Flash abzulegen und auf diese dann mit den entsprechenden Operatoren
(.,->) zuzugreifen.


Peter

von Withold.Schuetze (Gast)


Lesenswert?

Vielen Dank Joerg fuer die fundierten Aussagen.

Ich bin zwar auch schon eine Weile im Geschäft, konnte dennoch
genug Neues dazu lernen.

Peter, an paar Worte an Dich:
Unkenntniss mit deftigen Sprüchen zu ersetzen ist keine gute Wahl.
Würdest Du so prgrammieren, wie hier Dein Tonfall ist, ich würde wohl
Zweifel kriegen ob Deine Software was taugt.
Ich kann Dir nur sagen, dass ich u.a. den Keil-Compiler kenne, den
sogar sehr gut. Die Erfahrungen damit haben mir die Freude am avr-gcc
mehr als verdoppelt :-) Deine Parallelen sind sachlich nicht
nachzuvollziehen.
Ausserdem hab ich nicht vergessen, wie es bei mir angefangen hat.
Durch das Lesen von Programmen Anderer viel mir mein Unwissen auf. Also
hab ich gelernt. Auch, anfangs Unverständliches zu begreifen, indem ich
erst mal Hausaufgaben gemacht habe. Lies die Quellen der avr-lib und
lerne...
Ob Win9x eine Plattform für die Entwicklung ernsthafter Software eine
gute Wahl ist, dass möchte ich arg bezweifeln.

Genug gemeckert ;)
Peter, Dir viel Erfolg.

Jörg & co: Danke für Eure Arbeit! Weitermachen!

von Joerg Wunsch (Gast)


Lesenswert?

@Peter:

> Zu den generic Pointern, das ist natürlich nur die eine Hälfte der
> Medaille. Beim Keil sind die Bibliotheksfunktionen (z.B. strcpy(),
> printf()) so geschrieben, daß sie generic Pointer bekommen, d.h. sie
> funktionieren auf jedem Speicherbereich.

Das ist aber praktisch `late binding' im OO-Sinne, d. h. es
verplempert einfach Rechenzeit.  Um die 4 Takte für ein paar PUSHs und
POPs brauchst Du Dir dann auch keine Gedanken mehr machen. ;-)

> Die andere Seite sind die "memory specific pointer", z.B.:

> char code *foo;

> bezeichnet einen Pointer auf den Flash.

...und verletzt den C-Standard.  »code« ist ein Bezeichner, der im
Namensraum der Applikation liegt, d. h. Du bist vollauf berechtigt,
sowas zu schreiben:

char code[] = { ... };

Sie hätten, wenn schon, dann __code usw. nehmen sollen.  GCC's
_attribute_ Konstrukt ist das Ganze dann in allgemeiner Form.

Es ist richtig, avr-gcc kann fremde Speicher-Regionen nicht
automatisch wie Variablen behandeln, d. h. Du kannst auf Zeigern
dorthin keine Zeigeraroperationen direkt anwenden, sondern mußt das
,,zu Fuß'' erledigen.  Das liegt im Wesentlichen daran, daß die
gesamte Architektur von GCC und den begleitenden Tools auf CPUs mit
von-Neumann-Architektur ausgerichtet ist, die nur einen Speicherraum
kennen.

Andererseits, nachdem ich den Code für AVR Butterfly gesehen habe (der
für den IAR-Compiler gezimmert ist, und dieser kann mit verschiedenen
Speicherbereichen umgehen) und die Verrenkungen, die dann teilweise
nötig sind, um dem Compiler seinen Automatismus an irgdeiner Stelle
wieder abzugewöhnen, bin ich mittlerweile gar nicht mehr so sehr davon
überzeugt, daß die avr-gcc-Variante so ein schlimmer Nachteil ist wie
ich anfangs dachte.  Vom erzeugten Code her ist es sowieso egal, es
ist nur die Bequemlichkeit für den Programmierer, die dabei etwas
leidet.

von Peter D. (peda)


Lesenswert?

@Withold,

"Ich kann Dir nur sagen, dass ich u.a. den Keil-Compiler kenne, den
sogar sehr gut. Die Erfahrungen damit haben mir die Freude am avr-gcc
mehr als verdoppelt :-)"

Du scheinst ja schlechte Erfahrungen mit dem Keil gemacht zu haben. Bei
mir ist es nunmal das genaue Gegenteil.
D.h. der Keil hat extrem wenig Bugs, erzeugt schnellen, kompakten und
nachvollziehbaren Code und geht sparsam mit RAM um.
Ich muß allerdings dazu sagen, daß ich vorher jahrelang den 8051 in
Assembler programmiert habe, d.h. ich hatte daher schon gute Kenntnisse
im sparsamen Umgang mit Variablen.


"Deine Parallelen sind sachlich nicht nachzuvollziehen."

Das ist doch gerade der Witz an C, daß Programme möglichst portabel
sein sollen. Und dazu muß es nun mal erlaubt sein, zwischen den
Compilern Parallelen zu ziehen und Unterschiede zu benennen.
Und das hat ja nun auch zu fruchtbaren Meinungen geführt. Zumindest ich
kann jetzt einige Meinungen besser nachvollziehen (ich muß sie ja nicht
teilen).


Peter

von Peter D. (peda)


Lesenswert?

@Joerg,

"Das ist aber praktisch `late binding' im OO-Sinne, d. h. es
verplempert einfach Rechenzeit.  Um die 4 Takte für ein paar PUSHs und
POPs brauchst Du Dir dann auch keine Gedanken mehr machen. ;-)"

Das sind aber 2 verschiedene paar Schuhe. Das eine bezog sich auf
schnelle Interrupts und da würde ich nie im Traum dran denken, im
Interrupt printf() zu verwenden.
Im main() kann ich das in der Regel verschmerzen und habe mit dem
besser lesbaren Code auch eine adäquate Gegenleistung.

Ich wollte ja auch nie, daß der GCC 100% Keil kompatibel sein muß. Ich
wollte doch nur mal das Konzept der generic/ memory specific Pointer
zur Diskussion stellen, bzw. den nicht Keil-Kennern erläutern.


"Vom erzeugten Code her ist es sowieso egal, es ist nur die
Bequemlichkeit für den Programmierer, die dabei etwas leidet."

Genau so isses.


Natürlich bin ich derzeit immer noch in der Phase des Reinschnupperns
in den AVR-GCC. Aber immerhin liefen meine Testprogramme auf dem STK500
bisher auf Anhieb. Und der ATMega8 mit 1kB RAM verzeiht es einem auch
erstmal auf __LPM()-Verrenkungen zu verzichten und trotzdem kleinere
Tabellen und Strukturen zu verwenden.


Nochmals vielen Dank für Deine ausführlichen Informationen.

Peter

von Withold.Schuetze (Gast)


Lesenswert?

@Peter,

Du hast Recht, meine Erfahrungen sind das Resultat jahrelanger
praktischer Arbeit. Dabei trat z.B. massiv Ärger mit C51(Keil) auf.
Ich betone aber, das ich deshalb C51 nicht in Grund und Boden stampfe.

Wie Du auch habe ich mit Assembler begonnen. Sehr früh begann ich nach
verfügbaren Alternativen zu suchen, um genau die von Dir angesprochene
Transparenz zu erreichen. Die Wege gingen über Pascal und Forth (immer
für Controller).
Aus meinen Jahren Praxis heraus bestätigt sich der Schluss: Es gibt
keine Eierlegende-Woll-Milch-Sau.
Für mich persönlich zieht ein Argument besonders: "Traue nur der
Software deren Quelle Du hast". Die Vorteile liegen auf der Hand.

"Das mit dem malloc()+devopen() ... (P.D.)" ist für mich ein
besonders wichtiger Punkt. Damit hat die OpenSource-Gemeinde Grosses
geleistet. Knie Dich mal rein! Mir fehlt im Moment die Zeit, um ein
paar Erfahrungen damit zu dokumentieren.

Ansonsten finde ich jede Diskussion gut, solange mir niemand seine
Meinung "aufzwingen" will. Mit dieser Ausnahme ist alles nur
nützlich.

Frohes Schaffen ;)

Withold

von Michael Schlagmüller (Gast)


Lesenswert?

hmm, also von mir auch nochmal Danke für Diskussion...
ich hab früher alles per Assembler programmiert auf der AVR Kiste und
bin so dankbar dafür endlich nen gescheites Tool zur Hand zu haben, mit
dem sich äußerst komfortabel mit dem µController arbeiten lässt.
Von der Performace her kann ich mich auch nicht beschweren. Klar könnte
man das eine oder andere besser machen, aber dann schaut man sich halt
den erzeugten Code im Assembler an und wenn es wirklich sooo
zeitkritisch ist macht man halt ein wenig inline Assembling :)

Also auch nochmal von mir vielen Dank an Jörg & Co.... echt unglaublich
geniale Arbeit die ihr da Leistet. Ich hoffe dass ich auch mal mir die
Zeit nehmen werde mich bei OpenSource ein wenig produktiver
einzusetzten... Thx a lot...

von Joerg Wunsch (Gast)


Lesenswert?

Naja, ich helfe nur ein bißchen in der avr-libc mit...  Das ganze
großartige Werk vor allem des GCC, aber natürlich auch all der Tools
ringsum ist das Werk von mittlerweile Generationen von Freiwilligen.

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.