Hallo Leute,
sagt mal, gibt's eigentlich eine einfache Variante/Schreibweise um dem
GCC zu erklären, *direkt/effizient* auf das High/Low-Byte eines
16-Bit-Wertes zuzugreifen?
uint16_t word;
uint8_t lo;
uint8_t hi;
hi = word >> 8;
lo = word & 0xff;
das ist klar - wird aber (soweit ich bisher gesehen habe) vom Compiler
leider nicht auf die wirklichen Hi-/Lo-Byte-Zugriffe optimiert ...
Die Kombination aus
typedef union whl {
uint16_t word;
struct {
uint8_t lo;
uint8_t hi;
};
};
ist so umständlich zu schreiben (bla.word, bla.lo, bla.hi) und man muß
die Werte bei der Zuweisung auch noch typecasten ...
Gibt's noch was einfacheres?
Vielleicht ein geschicktes Macro?
Ich steh irgendwie grad auf'm Schlauch ...
- Karl
Karl F. schrieb:> das ist klar - wird aber (soweit ich bisher gesehen habe) vom Compiler> leider nicht auf die wirklichen Hi-/Lo-Byte-Zugriffe optimiert ...
Der GCC Bug-Report ist
http://gcc.gnu.org/PR41076
Könntest du (oder jemand, der sich dazu berufen fühlt), weitere
Beispiele zusammenstellen und dort hochladen? Und bitte ohne Includes.
Danke :-).
argh
das ist ja wiedermal typisch ich. Voll in die Falle getreten.
Und dann auch noch ein Bug, der schon vor zwei Jahren
reported wurde und immernoch nicht gefixed ist ...
Ich glaub, ich brauch den nicht nochmal submitten, oder?
Das will scheinbar nicht verstanden werden ...
seufz
Karl F. schrieb:> Ich glaub, ich brauch den nicht nochmal submitten, oder?
Nein. Es gibt doch schon einen Eintrag. Du kannst auch einen neuen
öffnen, der dann als DUPLICATE gekennzeichnet wird. In dem Falle beachte
aber auch, daß gcc 4.3 nicht mehr unterstützt wird. Da es sich bei dem
"Bug" lediglich um ein Optimierungsproblem handelt, sollte ein möglichst
neuer Compiler verwendet werden (z.b. 4.6.1) um sicherzustellen, daß der
Fehler nicht bereits behoben wurde und man nur warme Luft produziert.
Einen avr-gcc 4.6.1 für Win32 gibt's in
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=841595#841595> Das will scheinbar nicht verstanden werden ...
Was wird nicht verstanden?
jau - ich hab nicht richtig gelesen - inzwischen ist der Bug ja gefixed.
Hat ja nur fast zwei Jahre gedauert ...
Hmm ... selbst das relativ neue AVR studio 5 verwendet nur gcc 4.5.1
und die Ardiono-IDE bringt gcc 4.3.2 mit ...
Kriegt man den neuen da ohne Probleme reingeflickt oder geht dann
wieder irgendwas anderes nicht? Hast Du den schon im Einsatz?
Ich kann grad keine unnötige Arbeit und Fehlersuche gebrauchen ...
Danke
- Karl
A. K. schrieb:> Karl F. schrieb:>>> Hat ja nur fast zwei Jahre gedauert ...>> Fehlende Optimierung eines Spezialfalls hat nicht grad oberste> Priorität.
naja, 8-bittiger Zugriff auf Daten ist in einem 8-Bit uC IMHO nicht
gerade ein Spezialfall. Naja, es gibt ja immernoch Assembler ...
- Karl
Karl F. schrieb:> naja, 8-bittiger Zugriff auf Daten ist in einem 8-Bit uC IMHO nicht> gerade ein Spezialfall.
Das mag sein, aber 8Bit µc gehören nun mal nicht zu den primären
Zielplattformen des GCC, und dementsprechend sind derartige
Optimierungen doch ein Spezialfall.
Karl F. schrieb:> Kriegt man den neuen da ohne Probleme reingeflickt oder geht dann> wieder irgendwas anderes nicht? Hast Du den schon im Einsatz?
Bitte von Anfang an lesen!
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=108357&start=all&postdays=0&postorder=ascKarl F. schrieb:> Das will scheinbar nicht verstanden werden ...Karl F. schrieb:> Hat ja nur fast zwei Jahre gedauert ...
Mich wundert immer die Ansprüchlichkeit, die einer freien Software wie
GCC entgegengebracht wird, siehe.
Beitrag "GCC: Arg unzulängliche Optimierungen?"
Jeder kann sich avr-gcc Distributionen herunterladen oder nach Gusto
selbst erzeugen, ohne auch nur einen Groschen dafür hinzulegen. Und das
für einen Compiler (GCC), der mit vielen kommerziellen Compilern
mitziehen kann oder sogar weit in den Schatten stellt, etwa was
Verfügbarkeit sowie Plattform- und Architekturabdeckung oder
Erweiterbarkeit (Plugins) angeht. Und obendrauf gibt's auch noch die
Quellen! Ditto für binutils, avr-libc, avrdude, ...
Und was die PRs angeht, so gibt es für viele noch nicht einmal
vernünftige Bugreports und/oder man muss solche Kommentare lesen:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49764#c3> inzwischen ist der Bug ja gefixed
Nein, ist er nicht. Er steht auf UNCONFIRMED.
Es gibt viele Möglichkeiten mit so einem Bug umzugehen:
0. Man ignoriert das Problem und geht zur Tagesordnung über.
1. Man behebt ihn selbst. Immerhin hat man die Quelle.
2. Man wartet, bis irgendjemand ihn behebt.
3. Man versucht zumindest andere Entwickler bei der Entwicklung zu
unterstützen; etwa durch aussagekräftige Bugreports, Benchmarks,
Vorabversionen (Release-Candidates) um mehr Fehler vor einer
Release zu erkennen, etc.
4. Man bezahlt einen Entwickler, ihn zu beheben bzw. kauft sich
einen professionellen avr-gcc Support (keine Ahnung ob's sowas
gibt).
5. Man kauft einen kommerziellen Compiler mit Support.
0. und 2. sind bei weitem am beliebtesten, also der Herde nach!
4. und 5. dürften für viele nach Aufräumen der Portokasse ausscheiden.
1. ist nicht wirklich populär und der aktuelle Trend geht dahin, daß die
AVR-Unterstützung in GCC immer mehr versandet und irgendwann nicht mehr
nutzbar ist und daher ganz aus GCC entfernt wird, weil sich niemand
darum kümmert. Dann gibt's auch keine Fehler mehr und vor allem keine,
bei denen man 2 Jahre rumsitzen muss, bis sich jemand erbarmt für den
trivialen Bugfix.
Gerade im Feld von Compileranwendern dürfte es einige Entwickler geben,
die mächtig was auf dem Kasten haben an Erfahrung, Abstraktionsvermögen,
Hardwarekenntnissen und anderen Skills, die sich vortrefflich für die
Mitarbeit in einem Open-Source Projekt wie GCC eignen. An Baustellen und
Herausforderungen mangelt is dort und in anderen Teilen wie binutils
oder avr-libc nun wirklich nicht. Und sei es nur, Testprogramme für die
avr-gcc Testsuite beizutragen. Dazu muss man keine Zeiler der
Compilerquelle gesehen haben oder anfassen.
> Ich kann grad keine unnötige Arbeit und Fehlersuche gebrauchen ...
Wer kann schon unnötige Arbeit oder Fehlersuche gebrauchen? Sag ich auch
beim nächsten avr-gcc Bugreport...
Johann L. schrieb:> Mich wundert immer die Ansprüchlichkeit, die einer freien Software wie> GCC entgegengebracht wird
Sorry, Du hast ja vollkomen Recht! Das vergisst man nur viel zu oft.
Normalerweise bin ich derjenige, der das sagt!
... manche Dinge werden mit der Zeit wohl zu selbstverständlich ...
Zu Deiner 6-Punkte-Liste:
0. Das Problem ignorieren kann ich nicht, da es grade zu sehr stört.
1. Selber fixen kann ich es aber auch nicht - dazu kenn ich mich viel
zuwenig mit compilern aus. Und wenn dann brauch ich dafür Zeit zum
probieren, die ich im Moment grad echt nicht habe ...
Aber mit dem Wissen dass das so ist, kann ich mir zumindest einen
geeigneten Workaround basteln - zur Not ein Assembler-Macro.
Punkt 3 hat sich in diesem Fall erübrigt und 4+5 kommen - wie Du bereits
richtig vermutet hast - gerade nicht in Frage.
Bleibt (MIR) in diesem Fall eigentlich nur Punkt 2.
Compilerbau ist nunmal nicht gerade meine Stärke - da gibt es andere.
Und dort wo ich kann, tu ich auch gern was für andere!
Off-Topic:
Da ATmel selbst den AVR-GCC im AVR-Studio einsetzt hätte ich eigentlich
erwartet, dass die den irgendwie subventionieren!? Der Erfolg der
Prozessorfamilie liegt ja schließlich nicht zuletzt an der freien
Verfügbarkeit der Entwicklungstools.
- Karl
trotzdem nochmal zurück zur urprünglichen Frage:
(ganz unabhängig von compiler bug oder nicht)
gibt es nun noch eine andere, möglichst elegante Variante um direkt
auf das HIGH oder LOW Byte eines 16bit-Wertes zuzugreifen oder nicht?
irgendwas nach dem Muster
hi = _HI(word)
lo = _LO(word)
Ich kann gut damit leben, dass der Compiler bestimmte Dinge nicht
vollautomatisch 100%ig optimieren kann - dafür gibt es viel zu viele
Möglichkeiten, ganz klar.
Aber dann würde ich mir wünschen entsprechende Statements zu haben,
mit denen ich selber den Code nach Wunsch optimieren kann bzw. dem
Compiler ganz genau mitteilen, was ich möchte.
Gruß
- Karl
lo = _LO(word)
entspricht
lo = word & 0xFF;
hi = _HI(word)
enspricht
hi = *(((uint8_t *) &word) + 1);
Je nach Byte-Order kann das natürlich auch genau andersherum sein.
Wie der daraus erzeugte Code Deines Compilers aussieht, müsstest Du
allerdings selbst untersuchen, weder nutze ich avr-gcc noch habe ich den
hier irgendwo installiert.
Rufus Τ. Firefly schrieb:> lo = _LO(word)>> entspricht>> lo = word & 0xFF;
das hab ich probiert und was bei mir herauskam war genau das was da
steht:
1. laden eines 16-bit Wertes in ein Registerpaar
2. and mit 0x00ff
natürlich funktioniert das - ist aber nicht effizient.
> hi = *(((uint8_t *) &word) + 1);
dsa typecasting muss ich nochmal versuchen.
Analog müßte dann ja auch ein:
lo = *((uint8_t *) &word);
oder gleich ein
lo = (uint8_t) word;
funktionieren - werd ich nochmal testen.
Ich wollte ja auch nur wissen, ob es evtl. noch ein tolles Kommando
gibt, dass ich in meiner grenzelosen Unwissenheit nicht kenne. Ich hab
nämlich schon mehr als einmal das Rad neu erfunden und hinterher
festgestellt, dass es das schon gibt ... ärgerlich.
Wenn es nichts dergleichen gibt, fein. Damit kann ich auch leben.
Vielen Dank für die Antworten.
Gruß
- Karl
Karl F. schrieb:> Da Atmel selbst den AVR-GCC im AVR-Studio einsetzt hätte ich eigentlich> erwartet, dass die den irgendwie subventionieren!? Der Erfolg der> Prozessorfamilie liegt ja schließlich nicht zuletzt an der freien> Verfügbarkeit der Entwicklungstools.
Das verstehen eben nicht alle Hardwarehersteller, insbesondere auch im
Bezug auf GCC, der hier IMO unterschätzt wurde/wird, vor allem auch im
Hinblick auf sein Potential. Vergleicht man den Aufwand, den in PowerPC,
x86, Sparc, ARM und wie sie alle heissen reingesteckt wird (und
natürlich auch die Ergebnisse, die dabei herauskommen), dann wird klar,
daß avr-gcc ein absolutes Waisenkind ist.
Die Atmel-Policy ist mir nicht bekannt; aktive avr-gcc Entwickler von
seiten Atmels sind mir nicht bekannt – ausser Eric, der aber in binutils
und avr-libc unterwegs ist und WinAVR releast. Von Anitha hab ich länger
nix mehr gehört. Atmel scheint eher darauf konzentriert, private
Änderungen in seinem avr-gcc Fork zu pflegen (ATXmega, ATwinzig,
FixedPoint, ...) und bringt diese aber nicht ins offizielle Repository
ein — sei es aus lizenzrechtlichen Gründen oder aus technischen
Widerständen.
Gerüchteweise hat Atmel GCC-Entwickler gesucht aber keine gefunden, was
mir durchaus glaubhaft plausibel erscheint. Momentan liegt die
offizielle Toolchain brach, und nur hin und wieder verirrt sich jemand
dahin. Die Atmel-Entwicklung dürfte i.W. damit beschäftigt sein, die
ganzen Patches up to date zu halten.
Karl F. schrieb:> gibt es nun noch eine andere, möglichst elegante Variante um direkt> auf das HIGH oder LOW Byte eines 16bit-Wertes zuzugreifen oder nicht?
Am elegentesten ist der Weg ohne Verrenkung über die Operationen in
deinem OP.
Ansonsten ... kommt drauf an
· Soll es leserlich sein?
· Soll es effizient sein?
· Bezieht es sich auf Register oder Speicher?
· Soll nur lesend und/oder schreibend zugegriffen werden?
· Welcher C-Standard?
· Ist (Inline-)Asm ok?
· Spielt Portabilität eine Rolle (Endianess)
· Wie sieht's mit Type Punning/Strict Aliasing aus?
· Wozu brauchst du es überhaupt?
· Gibt es ein übersetzbares Beispiel?
Johann L. schrieb:>> Ansonsten ... kommt drauf an>> · Soll es leserlich sein?> · Soll es effizient sein?> · Bezieht es sich auf Register oder Speicher?> · Soll nur lesend und/oder schreibend zugegriffen werden?> · Welcher C-Standard?> · Ist (Inline-)Asm ok?> · Spielt Portabilität eine Rolle (Endianess)> · Wie sieht's mit Type Punning/Strict Aliasing aus?> · Wozu brauchst du es überhaupt?> · Gibt es ein übersetzbares Beispiel?
Nun, in erster Linie soll es effizient sein.
Die meisten Zugriffe, um die es geht, sind lesend.
Inline-ASM ist vollkommen ok, Portabilität ist nicht gefragt.
(wenn ich die Zeit hätte, würde ich vermutlich das ganze Programm in ASM
schreiben, aber in C geht das halt doch ein ganzes Stückchen schneller)
"Type Punning/Strict Aliasing":
Hab ich nicht grade unter http://gcc.gnu.org/bugs/#known gelesen, dass
man das nicht utn soll?
Beispiele wofür das gebraucht wird:
Ich übergebe einen uint16_t an eine Funktion und möchte in dieser
Funktion z.B. (zunächst) nur wissen, ob die Zahl gerade ist. Dafür
reicht es, das lo-Byte zu testen ...
Oder ich möchte wissen, ob der Wert größer als 1024 ist - dafür reicht
ein Test des hi-bytes. Bzw. zum Vorzeichen testen reicht auch das
Hi-Byte.
Weiterhin war es die unsägliche immer wiederkehrende Division durch 10,
z.B. wenn man eine Zahl zur Ausgabe vorbereitet (Display, Seriell):
Dazu hatte ich mit Routinen wie dieser experimentiert:
Q: http://www.cs.uiowa.edu/~jones/bcd/decimal.html
1
voidputdec(int16_tn)
2
{
3
uint8_td4,d3,d2,d1,d0,q;
4
5
if(n<0){
6
putchar('-');
7
n=-n;
8
}
9
10
d1=(n>>4)&0xF;
11
d2=(n>>8)&0xF;
12
d3=(n>>12)&0xF;
13
14
d0=6*(d3+d2+d1)+(n&0xF);
15
q=(d0*0xCD)>>11;
16
d0=d0-10*q;
17
18
d1=q+9*d3+5*d2+d1;
19
q=(d1*0xCD)>>11;
20
d1=d1-10*q;
21
22
d2=q+2*d2;
23
q=(d2*0x1A)>>8;
24
d2=d2-10*q;
25
26
d3=q+4*d3;
27
d4=(d3*0x1A)>>8;
28
d3=d3-10*d4;
29
30
putchar(d4+'0');
31
putchar(d3+'0');
32
putchar(d2+'0');
33
putchar(d1+'0');
34
putchar(d0+'0');
35
}
Dabei war mir aufgefallen, dass die anfängliche Aufteilung:
1
d1=(n>>4)&0xF;
2
d2=(n>>8)&0xF;
3
d3=(n>>12)&0xF;
nicht besonders elegant übersetzt wird.
Auch bei Dingen wie:
1
q=(d1*0xCD)>>11;
wäre es effizienter sagen zu können:
1
q=_HI((d1*0xCD))>>3;
denn auch hier reicht das hi-Byte ...
Außerdem sind hier beide Werte - sowohl d1 als auch 0xCD nur Bytes
(uint8_t), aber ich glaube (bin mir nicht mehr ganz sicher) der Compiler
hatte die erst mal bedie in 16bit gewandelt und dann erst eine 16bit-
Multiplikation gemacht, obwohl eine 8bit-Multiplikation vollkommen
gereicht hätte ...
Langer Rede kurzer Sinn:
An manchen Stellen - wenn man optimieren möchte - wäre es schön, dem
Compiler durch irgendwelche Flags, typecasting, _atribute_ oder sonst
was genau sagen zu können, was er tun soll, statt vom Compiler
Optimierungswunder zu erwarten. Letztendlich weiß doch nur der
Programmierer, was er bezwecken will ...
Gruß
- Karl
Vorzeichen machen nur das Leben schwer. Ausserdem kommt's in der Ausgabe
doch nicht wirklich auf Geschwindigkeit an (es sei denn, du malst auf ne
Kathodenstrahlröhre ;-)).
Johann L. schrieb:> Atmel scheint eher darauf konzentriert, private> Änderungen in seinem avr-gcc Fork zu pflegen (ATXmega, ATwinzig,> FixedPoint, ...) und bringt diese aber nicht ins offizielle Repository> ein — sei es aus lizenzrechtlichen Gründen oder aus technischen> Widerständen.
Eher aufgrund fehlender Kapazität. Der Xmega-Patch wäre wohl in einem
Stadium, dass man ihn tatsächlich einbringen könnte, der Lizenzkram
ist mittlerweile ja wohl in Sack und Tüten. Keine Ahnung, ob dies nun
auf Erics Tisch liegt, vermutlich. Der Tiny-Kram ist nach letzten
Erfahrungen wohl noch reichlich buggy, und wenn sie überhaupt jemanden
im Moment haben, der irgendwas am GCC entwickelt (ich glaube mich zu
erinnern, da mal einen indisch klingenden Namen gelesen zu haben, aber
nicht Anitha, die macht eher avr-libc), dann wird der wohl damit zu
tun haben, die Tiny10-Familie da in Gang zu bekommen. Von fixed point
weiß ich gerade nichts. Wenn sie da interne Patches haben, dann ist
es irgendwas, was mal bei avrfreaks gepostet worden war, nichts
eigenes.
> Gerüchteweise hat Atmel GCC-Entwickler gesucht aber keine gefunden, was> mir durchaus glaubhaft plausibel erscheint.
GCC-Interna sind leider nicht gerade ein einfaches Feld. Da genügt
es nicht nur, einen Willen zu haben, etwas beizusteuern, sondern man
muss sich schon mächtig in RMS' Denkweise reinfühlen können. Ich tu'
mir vieles im Opensource-Bereich an, aber da muss ich leider auch
passen. So wird es (leider) auch einigen anderen gehen, es genügt
also für einen CPU-Hersteller nicht, nur überhaupt bereit zu sein,
jemanden zu bezahlen dafür, sondern man muss erstmal jemanden finden,
der es überhaupt kann (und dann muss der auch noch bezahlbar sein ;).
Wow ... da hab ich ja eine Lawine losgetreten ...
Eigentlich hab ich ja nur gefragt, ob es eine Funktion oder ein Makro
gibt,
die/das ich evtl. nicht kenne - sowas wie die _BV() Makros z.Beispiel.
Makro macht natürlich nur dann Sinn wenn es was ist, was dann vom
Compiler auch wirklich effizient übersetzt wird, so wie z.B. ein
PORTB |= 0x40
oder
PORTB |= _BV(6)
wirklich in ein
sbi PORTB, 6
übersetzt wird.
Johann L. schrieb:>> Vorzeichen machen nur das Leben schwer. Ausserdem kommt's in der Ausgabe> doch nicht wirklich auf Geschwindigkeit an (es sei denn, du malst auf ne> Kathodenstrahlröhre ;-)).
Den Spruch hab ich jetzt schon mehrfach zu hören bekommen.
Das mit dem putchar() ist ja auch nur ein Beispiel.
MIR kommt es bei meiner Programmieraufgabe sehr wohl auf Geschwindigkeit
an, wobei die Erläuterung der Gründe hier eigentlich off-topic ist.
Nur so viel:
Es passiert alles in Interrupt-Routinen, die ja bekanntlich so kurz wie
möglich sein sollten.
Ich erhalte z.B. Messwerte in einer Interrupt-Routine und Display bzw.
Serialport wird in einer anderen Interrupt-Routine behandelt.
Wenn ich jetzt die Werte erst mal ans main() übergeben muss, um sie dort
"langsam" zu behandeln, muss ich die entsprechenden Variablen alle als
volatile deklarieren was die Sache auch nicht unbedingt schneller macht.
Bleibt die Behandlung vollständig in Interrupte (die sich gegenseitig
nicht unterbrechen) kann ich mir das sparen.
Gruß
- Karl
Ich kann
Jörg Wunsch schrieb:> Johann L. schrieb:>> Atmel scheint eher darauf konzentriert, private>> Änderungen in seinem avr-gcc Fork zu pflegen (ATXmega, ATwinzig,>> FixedPoint, ...) und bringt diese aber nicht ins offizielle Repository>> ein — sei es aus lizenzrechtlichen Gründen oder aus technischen>> Widerständen.>> Eher aufgrund fehlender Kapazität. Der Xmega-Patch wäre wohl in einem> Stadium, dass man ihn tatsächlich einbringen könnte, der Lizenzkram> ist mittlerweile ja wohl in Sack und Tüten. Keine Ahnung, ob dies nun> auf Erics Tisch liegt, vermutlich.
So wie ich ihn verstanden habe, ja. Ich wundere mich allerdings, daß es
nicht längst committed oder zumindest im Review ist – wobei Eric
letzteres ja garnicht braucht. Sooo viel Zeit ist nicht mehr,
angepeiltes Ende von 4.7 Stage 1 ist Ende Oktober. Ich kann mir aber gut
vorstellen, daß er ziemlich mit WinAVR 4.6.2 beschäftigt ist.
> Der Tiny-Kram ist nach letzten Erfahrungen wohl noch reichlich buggy,
Von mir aus kann das Tiny-Zeugs bei Atmel bleiben. Ich versteh immer
noch nicht, was sie mit diesem Silizium wirklich wollen, bzw. was
sie/jemand mit diesem Silizium + GCC will.
> und wenn sie überhaupt jemanden> im Moment haben, der irgendwas am GCC entwickelt (ich glaube mich zu> erinnern, da mal einen indisch klingenden Namen gelesen zu haben, aber> nicht Anitha, die macht eher avr-libc), dann wird der wohl damit zu> tun haben, die Tiny10-Familie da in Gang zu bekommen.
Abnikant Singh?
> Von fixed point weiß ich gerade nichts.> Wenn sie da interne Patches haben, dann ist es irgendwas, was mal> bei avrfreaks gepostet worden war, nichts eigenes.
AFAIK von Sean D'Epagnier aka. geckosenator (mit FSF-CA).
>> Gerüchteweise hat Atmel GCC-Entwickler gesucht aber keine gefunden, was>> mir durchaus glaubhaft und plausibel erscheint.>> GCC-Interna sind leider nicht gerade ein einfaches Feld. Da genügt> es nicht nur, einen Willen zu haben, etwas beizusteuern, sondern man> muss sich schon mächtig in RMS' Denkweise reinfühlen können.
RMS ist da schon lange nicht mehr unterwegs, und ich denke, daß sich in
jedem Compiler dieser Liga ähnliche Strukturen, Probleme und
Problemlösungen ergeben: Rumtanzen auf SSA-Trees, Data- und Code-Flow
Analyse, PRE, CSE, DSE, CIM, LCM, Registerallocation, Instruction
Combining, Scheduling und weiß-der-Teudel-was-noch für Optimierungen und
Algorithmen.
Der Teufel im Detail sieht dagegen überall anders aus, und bei den
vielen Details in GCC gibt's auch ensprechend viele Teufelchen.
Wenn man damit anfängt, kann es schon frustrieren sein, sich an einer
"trivialen" Aufgabe wie den hier angesprochenen
Byte-Operationen/-Optimierungen die Zähne auszubeissen um nach
Tagen/Wochen frustriert aufzugeben – abgesehen von sonstigen Schikanen
wie Regression-Tests, Reviews und zu schauen, daß nicht anderer Code
schlechter wird.
> Ich tu' mir vieles im Opensource-Bereich an, aber da muss ich> leider auch passen. So wird es (leider) auch einigen anderen gehen,
Schade eigentlich. Aber bei dir denk ich daß du ansonsten schon genug
Projekte am Bein hast. Wenn man allerdings die Hacks, Würgarounds und
Verwünschungen und fruchtlosen Forendiskussionen in einen Topf wirft,
könnte man locker 10 AVR-Backends chic machen oder komplett neu
hochziehen ;-)
> es genügt also für einen CPU-Hersteller nicht, nur überhaupt bereit> zu sein, jemanden zu bezahlen dafür, sondern man muss erstmal> jemanden finden, der es überhaupt kann (und dann muss der auch> noch bezahlbar sein ;).
An der nächsten Straßenecke wird man einen Richard Guenther, Ian L.
Taylor, Joseph S. Myers oder Michael Meissner garantiert nicht finden.
Die Arbeitgeber lesen sich wie ein who-is-who: Google, IBM, SuSE, ARM,
Codesourcery, Redhead, ... Ich möchte garnicht wissen, was so jemand
kostet, und Geld alleine macht's da bestimmt nicht. So jemand will auch
ein entsprechendes Arbeits- und Lebensumfeld, Team, kreative Freiheit
usw. vorfinden und nicht als lone Hacker in einem Konzern untergehen, wo
er hier und da ein paar Tweaks oder Erweiterungen an einem ferner-liefen
Backend machen darf.
Atmel macht hier vermutlich den Fehler zu glauben, einen Entwickler
einfach einkaufen zu können. Stattdessen muss hier auch kontinuerlich
daran gearbeitet werden, ein eigenes Compilerteam aufzubauen und eine
Mindestmaß an kritischer Masse zu erreichen, um
Compilerentwicklung/anpassung sinnvoll zu machen. Arbeit gibt's da
genug, und ich weiß auch nicht, wie Atmel den AVR32-Port gestemmt hat.
Ansonsten ist das AVR-Backend quasi tot. Anatoly ist nicht mehr aktiv
und Andy Hutchinson seit Jahren nicht mehr gesehen. Eric fügt
bestenfalls neue copy-paste Derivate ein und Denis beschränkt sich aufs
Review der spärlichen Patches (immerhin!).
Karl F. schrieb:> Eigentlich hab ich ja nur gefragt, ob es eine Funktion oder ein Makro> gibt, die/das ich evtl. nicht kenne -> sowas wie die _BV() Makros z.Beispiel.> Makro macht natürlich nur dann Sinn wenn es was ist, was dann vom> Compiler auch wirklich effizient übersetzt wird, so wie z.B. ein>> PORTB |= 0x40> oder> PORTB |= _BV(6)>> wirklich in ein> sbi PORTB, 6> übersetzt wird.
_BV mach aber nix ausser Textersatz und hilft die Quelle zu obfuskieren
;-) Die Abbildung auf SBI et al. macht der Compiler auch ohne dieses
tolle Makro.
> Johann L. schrieb:>>>> Vorzeichen machen nur das Leben schwer. Ausserdem kommt's in der Ausgabe>> doch nicht wirklich auf Geschwindigkeit an (es sei denn, du malst auf ne>> Kathodenstrahlröhre ;-)).>> Den Spruch hab ich jetzt schon mehrfach zu hören bekommen.> Das mit dem putchar() ist ja auch nur ein Beispiel.> MIR kommt es bei meiner Programmieraufgabe sehr wohl auf Geschwindigkeit> an, wobei die Erläuterung der Gründe hier eigentlich off-topic ist.
Naja, man darf einen Blick über den Tellerrand wagen.
> Nur so viel:> Es passiert alles in Interrupt-Routinen, die ja bekanntlich so kurz wie> möglich sein sollten.>> Ich erhalte z.B. Messwerte in einer Interrupt-Routine und Display bzw.> Serialport wird in einer anderen Interrupt-Routine behandelt.
Standardansart ist dann mit zwei atomaren volatile-Variablen V1 und V2:
ISR1 → V1 → V2 → ISR2
wobei die Applikation die Umwandlungsroutine V1 → V2 ausführt. Und
natürlich reduziert man die Zugriffe auf V1/V2 auf ein Minimum d.h. der
Algorithmus operiert nicht auf diesen Variablen sondern mach sich lokale
Kopien.
> Wenn ich jetzt die Werte erst mal ans main() übergeben muss, um sie dort> "langsam" zu behandeln, muss ich die entsprechenden Variablen alle als> volatile deklarieren was die Sache auch nicht unbedingt schneller macht.
S.o.
> Bleibt die Behandlung vollständig in Interrupte (die sich gegenseitig> nicht unterbrechen) kann ich mir das sparen.
Zurück zu Thema :-)
Der verlinkte Artikel ist interessante Lekture und es kommt ein Punkt
hinzu:
· Gibt es einen Hardware-Multiplier?
Ohne diesen nimmt man die Subtraktion von 10000/1000/100/10 wie im
Artikel ganz oben beschrieben.
Mit MUL kommt einem die C-Semantik quer denn 6·char wird gemäß dieser
auf 16-Bit Ebene ausgeführt. Um die Arithmetik wirklich auf 8 Bits zu
drücken und den Algorithmus optimal umzusetzen fürht also kein Weg an
Assembler vorbei. Bei avr-gcc kommt hinzu, daß nach jedem MUL das
0-Register gelöscht werden muss und kein Multiply-Add auf R0/R1
ausgeführt wird [1], d.h. das Multiplikationsergebnis muss immer aus
R0/R1 herausbefördert werden.
Auf C-Ebene bekommt man das nicht so gut hin wie mit Assembler und der
Code wird zunehmend unleserlich. Bei uint8_t
1
a+b+c
werden also mindestens erweiternde Additionen ausgeführt anststt das auf
8-Bit-Ebene zu tun.
1
unsignedchart=a+b;
2
t+=c;
kann sich da anbieten, was teilweise Casts überlegen ist.
Ähnlich sieht es mit Multiplikationen aus, die immer auf 16 Bits
erweitern. Erschwerend kommt da hinzu, daß erst in 4.7 erweiternde
Multiplikationen besser implementiert sind: http://gcc.gnu.org/PR49687
Das allerdings nur wenn es ein MUL gibt, der Wolf, den man sich ohne MUL
machen muss, war mir echt zu viel...
Zur Extraktion der Nippel kann man sich erst mal die Bytes besorgen:
1
uint16_tn=...;
2
uint8_thi,lo;
3
uint8_tn3,n2,n1,n0;
4
5
hi=n>>8;
6
lo=n;
7
8
n3=hi>>4;
9
n2=hi&0xf;
10
n1=lo>>4;
11
n0=lo&0xf;
Dann wie beschrieben häppchenweise weiter.
Oder eben tabula rasa und mit Assembler anfangen.
[1] Da würde sich auch ne Riesen-Baustelle auftun:
Schweinkram - mein letzter Antwortpost ist wohl irgendie in /dev/null
verlorengegangen. Naja, vermutlich sitzt das Problem - wie fast immer -
vor der Tastatur ...
Johann L. schrieb:>>>> wirklich in ein>> sbi PORTB, 6>> übersetzt wird.>> _BV mach aber nix ausser Textersatz und hilft die Quelle zu obfuskieren> ;-) Die Abbildung auf SBI et al. macht der Compiler auch ohne dieses> tolle Makro.
klar - deswegen hatte ich ja auch "PORTB |= 0x40" oder "PORTB |=
_BV(6)" geschrieben.
> Der verlinkte Artikel ist interessante Lekture ...
... das fand ich auch ;-)
Besonders die ASCII-Arithmetik hatte es mir angetan:
(http://www.cs.uiowa.edu/~jones/bcd/bcd.html#ascii)
Aber das ist ein ganz anderes Thema ...
Nun denn, dann werde ich wohl dort, wo es darauf ankommt,
mit inline-Assembler arbeiten.
Zurück zur Compiler-Optimierung:
Manche Compiler erkennen doch auch Dinge wie "x /= 2 und ersetzen diese
durch "x >>= 1" u.ä. Da wäre es doch naheliegend, auch "x /= 10" durch
eine effizientere Divisionsroutine zu ersetzen als die normale Division.
Immerhin ist 10 (nach 2) mit an Sicherheit grenzender Wahrscheinlichkeit
die am häufigsten verwendete Divisor-Konstante überhaupt ...
Aber ich weiß: wir sind hier nicht bei "wünsch Dir was" ;-)
Nun denn, nochmal vielen Dank Johann für die Erläuterungen.
Und - wie mein Boss jetzt sagen würde - keep up the good work!
Gruß
- Karl
Karl F. schrieb:> Zurück zur Compiler-Optimierung:> Manche Compiler erkennen doch auch Dinge wie "x /= 2 und ersetzen diese> durch "x >>= 1" u.ä. Da wäre es doch naheliegend, auch "x /= 10" durch> eine effizientere Divisionsroutine zu ersetzen als die normale Division.
Sofern wir von avr-gcc reden musst du dafür auf avr-gcc 4.7 warten (oder
dir nen Snapshot selber generieren).
Für eine Speed-optimierte Division durch 10 sieht der Code aus wie
1
unsignedcharudiv10_speed(unsignedcharn)
2
{
3
returnhigh(205*n)>>3;
4
}
Frag mich jetzt nicht, wie der Code für /5 oder /3 oder signed aussieht.
Das ist alles GCC-Magie, die es schon längt gibt. Man muss lediglich in
der avr-Beschreibung im richtigen Loch rumstochern um sie zu bekommen
;-)
Für Size-optimierten Code wird wie bisher eine Division ausgeführt weil
Code aus der libgcc wiederverwendet werden kann.
> Immerhin ist 10 (nach 2) mit an Sicherheit grenzender Wahrscheinlichkeit> die am häufigsten verwendete Divisor-Konstante überhaupt ...
Ich würd tippen daß 256 häufiger vorkommt als 10.
Johann L. schrieb:>> Für eine Speed-optimierte Division durch 10 sieht der Code aus wie>
1
>unsignedcharudiv10_speed(unsignedcharn)
2
>{
3
>returnhigh(205*n)>>3;
4
>}
5
>
klar, reziproke Multiplikation ...
Nur bei Werten größer 8 Bit ist das leider nicht mehr ganz so einfach
...
ABER:
1
returnhigh(205*n)>>3;
2
^^^^
DAS ist doch genau das, wonach ich suche!
Wie komme ich direkt an das high (oder low) Byte eines uint16_t ???
Und damit meine ich jetzt eben nicht ein ">> 8" bzw. "& 0xff"
> Frag mich jetzt nicht, wie der Code für /5 oder /3 oder signed aussieht.
findet sich übrigens alles auch in dem Artikel:
http://www.cs.uiowa.edu/~jones/bcd/divide.html>> Immerhin ist 10 (nach 2) mit an Sicherheit grenzender Wahrscheinlichkeit>> die am häufigsten verwendete Divisor-Konstante überhaupt ...>> Ich würd tippen daß 256 häufiger vorkommt als 10.
Ja, klar, ich meinte ja auch alle 2er-Potenzen.
Genauso wie ich mit 10 nicht nur 10, sondern auch 100, 1000, 10000 usw.
gemeint hab.
Gruß
- Karl
>> klar, reziproke Multiplikation ...> Nur bei Werten größer 8 Bit ist das leider nicht mehr ganz so einfach
So klar finde ich das nicht. Das Pronzip ist klar aber nicht, warum
man es für eine gegebene Konstante für alle Dividenden ohne Fehler
machen kann. Aber darüber haben sich schon genug Jungs den Kopf
zerbrochen und ich brauch's zum Glück nicht. Für das obige Beispiel hab
ich einfach geschaut, was avr-gcc 4.7 ausspuckt.
Übrigens macht er es auch für 16-Bit Divisionen mit bekanntem Divisor.
> ABER:>
1
>returnhigh(205*n)>>3;
2
>^^^^
3
>
> DAS ist doch genau das, wonach ich suche!
Nö, das ist nur Pseudo-Code um zu veranschaulichen, wie GCC es macht.
Der liefert es natürlich als Assembler-Code.
> Wie komme ich direkt an das high (oder low) Byte eines uint16_t ???> Und damit meine ich jetzt eben nicht ein ">> 8" bzw. "& 0xff"
Die Werte durch eine Union durchzuschleusen (s. Anhang) gibt bestimmt
fürchterlich unleserlichen Code.
Ok... hab's mit mal im Detail angeschaut für folgenden Code (modulo
Tippfehler):
1
#include<stdint.h>
2
3
#define put_digit(X) *s++ = (X)
4
5
#if 0
6
#define umul_hi(a,b) \
7
({ uint8_t _c; \
8
asm ("mul %1, %2" "\n\t"\
9
"mov %0, R1" "\n\t"\
10
"clr __zero_reg__"\
11
: "=r" (_c) : "r" ((char) a), "r" ((char) b));\
12
_c;})
13
#else
14
#define umul_hi(a,b) \
15
({ uint8_t _a=a, _b=b; \
16
uint8_t _c=_a*_b; _c;})
17
#endif
18
19
voidputdec(uint16_tn,char*s)
20
{
21
uint8_td4,d3,d2,d1,d0,q;
22
23
if(n>=0x8000)
24
{
25
put_digit('-');
26
n=-n;
27
}
28
29
d0=n;
30
d1=d0>>4;
31
d0&=0xf;
32
33
d2=n>>8;
34
d3=d2>>4;
35
d2&=0xf;
36
37
d0+=6*(d3+d2+d1);
38
q=umul_hi(d0,0xCD);
39
q>>=3;
40
d0-=10*q;
41
42
d1=q+9*d3+5*d2+d1;
43
q=umul_hi(d1,0xCD);
44
q>>=3;
45
d1-=10*q;
46
47
d2=q+2*d2;
48
q=umul_hi(d2,0x1A);
49
d2-=10*q;
50
51
d3=q+4*d3;
52
d4=umul_hi(d3,0x1A);
53
d3-=10*d4;
54
55
put_digit(d4+'0');
56
put_digit(d3+'0');
57
put_digit(d2+'0');
58
put_digit(d1+'0');
59
put_digit(d0+'0');
60
put_digit(0);
61
}
Die Funktion macht keine Ausgabe, sondern schreibt lediglich 7 Zeichen
in den Übergebenen char*. Dadurch spart man Funktionsaufrufe und die
Funktion wird zum Blatt. Sie nimmt ein unsigned int entgegen.
avr-gcc -Os -mmcu=atmega8 (Größe in Bytes)
1
Asm ohne Asm
2
3.4.6 254 252
3
4.2.2 290 262
4
4.3.3 260 260
5
4.5.2 206 206
6
4.6.1 186 186
Für 4.6 gibt es ausser dem n=-n am Anfang keine 16-Bit Operationen mehr
im Code, d.h. auf C-Ebene dürfte bei ca. 180 Bytes Ende der Fahnenstange
sein – zumindest für 4.6.
"Asm" bezieht sich auf den Inline-Asm für den High-Teil einer
8*8-Multiplikation. Als einziger gewinnt dadurch 4.2.
Fazit
Der Code ist ausser dem kurzen Stückchen am Anfang linear. Gehen wir
großzügigerweise davon aus, daß die eigentliche Routine ohne den String
zu schreiben 160 Ticks braucht, dann landen wir bei 40 Ticks pro
Ziffer!!! (mit 4 Ziffern veranschlagt)
Da ist ein simples Abziehen von 10000, 1000, 100, 10 wesentlich
einfacher zu implementieren und zu lesen und im Mittel womöglich sogar
schneller, zumal du wahrscheinlich nicht avr-gcc 4.6 im Einsatz hast!
Oben habe ich mit 4 Ziffern verglichen, weil man bei der Abzieh-Methode
Ziffer 0 für lau bekommt.
Bei mir sieht eine Abzieh-Methode so aus, wobei dort keine führenden
Nullen ausgegeben werden. Die Funktion ist i.W. auf Größe optimiert (62
Bytes):
1
staticconstuint16_tpows10[]PROGMEM=
2
{
3
10000,1000,100,10
4
};
5
6
// Wandelt N in eine ASCII-Dezimalzahl um. Die Darstellung wird
7
// mit abschliessender '\0' als Stringende nach STR geschrieben.
8
// Return: Adresse der abschliessenden '\0'.
9
char*u16_to_string(char*str,uint16_tn)
10
{
11
registerconstuint16_t*pasm("r30")=pows10;
12
13
uint16_tpow10;
14
uint8_tnot0=0;
15
16
do
17
{
18
pow10=pgm_read_word_inc(p);
19
charc='0';
20
21
while(n>=pow10)
22
not0=1,n-=pow10,c++;
23
24
if(not0)
25
*str++=c;
26
27
}while(!(pow10&2));// pow10 != 10
28
29
// Einer
30
*str++=n+'0';
31
*str='\0';
32
33
returnstr;
34
}
Um 12345 auszugeben messe ich da 165 Ticks und für eine 1 nur 81. Für
9999 allerdings ≈300.
Kleine Zahlen kommen statistisch viel häufiger vor als große Zahlen. Ja,
das ist wohl eine Binsenweisheit. Das gilt aber auch erstaunlicherweise
für Ziffern!!
Daher bekommt durchschnittlich eine Subtraktionsmethode sogar nochmals
Schub...
Abdul K. schrieb:> Kleine Zahlen kommen statistisch viel häufiger vor als große Zahlen. Ja,> das ist wohl eine Binsenweisheit. Das gilt aber auch erstaunlicherweise> für Ziffern!!
Nicht von Binsen, sondern von Benford ;-)
http://de.wikipedia.org/wiki/Benfordsches_Gesetz
Johann L. schrieb:> Um 12345 auszugeben messe ich da 165 Ticks und für eine 1 nur 81. Für> 9999 allerdings ≈300.
Danke Johann, dass Du Dir die Zeit genommen hast.
Mir ist ein definiertes Zeitverhalten lieber als ein variables.
d.h. immer 200 Ticks sind mir lieber als durchschnittlich 160
die aber manchmal auch 300 sein koennen.
Sowas fuehrt naemlich zu den schoenen race-conditions, bei denen
irgendwas irgendwann mal an die Wand läuft und kein Schwein weiss warum.
Am liebsten ist mir also eine Routine, die ganz ohne "if" auskommt.
- Karl
Johann L. schrieb:
(Xmega)
>> Keine Ahnung, ob dies nun>> auf Erics Tisch liegt, vermutlich.>> So wie ich ihn verstanden habe, ja. Ich wundere mich allerdings, daß es> nicht längst committed oder zumindest im Review ist – wobei Eric> letzteres ja garnicht braucht. Sooo viel Zeit ist nicht mehr,> angepeiltes Ende von 4.7 Stage 1 ist Ende Oktober. Ich kann mir aber gut> vorstellen, daß er ziemlich mit WinAVR 4.6.2 beschäftigt ist.
Ja, vermute ich auch, ich müsste ihn mal fragen.
>> Der Tiny-Kram ist nach letzten Erfahrungen wohl noch reichlich buggy,> Von mir aus kann das Tiny-Zeugs bei Atmel bleiben. Ich versteh immer> noch nicht, was sie mit diesem Silizium wirklich wollen,
Verkaufen. ;-)
Ganz ehrlich: wenn da nicht irgendwelche Millionenstückzahlen dahinter
stecken würden, hätte sowas sicher niemand dort angefangen. Es ist ja
letztlich eine recht umfangreiche Produktentwicklung mit einem neuen
Core, nicht nur das 125. Derivat eines bereits existierenden AVRs, bei
dem man "nur" die Funktionsblöcke mal neu sortieren und anordnen muss.
Wenn du nun siehst, dass man die Teile bei Digikey für 44 Cent bekommt
(Stückpreis bei Abnahme einer Rolle) und Digikey auch noch was
verdienen will dabei, dann werden die Teile vielleicht 10 oder 15 Cent
Gewinn abwerfen. Nun halt' das mal gegen eine Produktentwicklung mit
Kosten von (grob geraten) vielleicht 10 Millionen, dann sollte klar
werden, um welche Stückzahlen es hier geht — und dass die paar
Hobbyisten in dieser Rechnung ganz gewiss keine Rolle spielen.
> bzw. was> sie/jemand mit diesem Silizium + GCC will.
Naja, auch dort hast du bei einer Produktentwicklung in C eine bessere
Kosteneffizienz, nicht nur bei der Erstellung, sondern auch bei der
Pflege. (Außerdem soll's die Teile ja am Ende wohl bis zu einem
ATiny40 geben.)
Was ich mir bei derart kleinen Controllern gut vorstellen kann ist,
dass sie einen Markt im sicherheitstechnischen Bereich haben: eine
Firmware dieser Dimension lässt sich mit brauchbarem Aufwand noch
komplett testen, mit all ihren Eventualitäten.
>> (ich glaube mich zu>> erinnern, da mal einen indisch klingenden Namen gelesen zu haben, aber>> nicht Anitha, die macht eher avr-libc)> Abnikant Singh?
Ja, Abnikant.
>> Ich tu' mir vieles im Opensource-Bereich an, aber da muss ich>> leider auch passen. So wird es (leider) auch einigen anderen gehen,>> Schade eigentlich. Aber bei dir denk ich daß du ansonsten schon genug> Projekte am Bein hast.
Ja, das ohnehin. Außerdem darfst du nicht vergessen: ich bein kein
Informatiker, ich bin Elektronikingenieur (eigentlich Elektronik-
technologe). Ich habe nur während meines Studiums halt auch
programmieren gelernt, weil mir schon damals klar war, dass die
fachspezifischen Computeraufgaben nur von den Fachleuten selbst gelöst
werden können, nicht etwa von Informatikern. Von denen kann man nur
Hilfe bei Algorithmen und Werkzeugen (Compilern ;-) erwarten.
Insofern fehlen mir einfach mal einige wesentliche Grundlagen im
Hinblick auf Compiler-Architektur, und ich habe weder Zeit noch
Nerven, das nachzuholen.
> Atmel macht hier vermutlich den Fehler zu glauben, einen Entwickler> einfach einkaufen zu können. Stattdessen muss hier auch kontinuerlich> daran gearbeitet werden, ein eigenes Compilerteam aufzubauen und eine> Mindestmaß an kritischer Masse zu erreichen, um> Compilerentwicklung/anpassung sinnvoll zu machen.
Es ist wohl in einer Firma, die sich als Hardwarehersteller sieht,
nicht ganz einfach, in der Chefetage die Notwendigkeit von Kompetenz
im Softwarebereich ins Blickfeld zu rücken. Kommt hinzu, dass AVR
historisch ja überhaupt nicht in Richtung GCC geguckt hat, sondern
sich erstmal voll auf IAR stützen wollte. Der AVR-GCC ist eine reine
Community-Entwicklung, und es hat einige Jahre gedauert, bis Atmel
dann erkannt hat, wie viel ihnen dieses Teil an Reputation und damit
perspektivisch auch an Gewinn wirklich bringt.
> Ansonsten ist das AVR-Backend quasi tot. Anatoly ist nicht mehr aktiv> und Andy Hutchinson seit Jahren nicht mehr gesehen. Eric fügt> bestenfalls neue copy-paste Derivate ein und Denis beschränkt sich aufs> Review der spärlichen Patches (immerhin!).
Denis als ursprünglicher Autor hatte sich von AVR eigentlich komplett
zurückgezogen und mittlerweile andere GCC-Backends gebastelt
(möglicherweise bezahlt, keine Ahnung). Anatoly ist dieses Jahr Vater
geworden, und da es sein erstes Kind ist (und nicht das dritte wie bei
Eric und mir, und die gehen nun auch schon alle in die Schule ;),
schränkt das verständlicherweise sein Freizeitbudget erst einmal
kräftig ein. Aber es soll ja da noch einen Johann-Georg geben, der
mittlerweile sehr aktiv ist. :-) So wechseln halt die Gesichter über
die Jahre, und das ist meines Erachtens das beste Zeichen dafür, dass
es durchaus am Leben ist.
dummy schrieb:> #define _HI8(w) (((tBuffer16*)(&w))->_a[1])> #define _LO8(w) (((tBuffer16*)(&w))->_a[0])> so, gehts eigentlich in allen meinen Projekten ...
Glück gehabt. Bezeichner, die mit einem Unterstrich, gefolgt von
einem Großbuchstaben beginnen, sind "reserved for the implementation".
Lass den Unterstrich lieber weg, auch wenn er dir vielleicht den
Code "professioneller" aussehen lassen mag. ;-)
>> so, gehts eigentlich in allen meinen Projekten ...
Nicht ganz, das tBuffer16 steht an der falschen Stelle.
Richtig ist:
1
typedefunion{
2
uint16_t_w;
3
uint8_t_a[2];
4
}tBuffer16;
Funktioniert so aber nur auf Little-Endian-Prozessoren...
Frage: Kann man "At-Compile-Time" den Endian des Prozessors (am besten
per Preprocessor) herausfinden?
Ich habs ausprobiert für einen ATmega168 mit dem avr-gcc 4.3.3:
1
#include<inttypes.h>
2
3
#define F_CPU 8000000LU //CPU Takt
4
5
volatileuint8_tlow;
6
volatileuint8_thigh;
7
volatileuint16_tword=0x1234;
8
9
typedefunion
10
{
11
uint16_t_w;
12
uint8_t_a[2];
13
}tBuffer16;
14
15
#define HI8(w) (((tBuffer16*)(&w))->_a[1])
16
#define LO8(w) (((tBuffer16*)(&w))->_a[0])
17
18
intmain()
19
{
20
low=word&0xFF;
21
high=word>>8;
22
low=LO8(word);
23
high=HI8(word);
24
}
Ergebnis:
1
int main ()
2
{
3
low = word & 0xFF;
4
5e: 80 91 60 00 lds r24, 0x0060
5
62: 90 91 61 00 lds r25, 0x0061
6
66: 80 93 63 00 sts 0x0063, r24
7
high = word >> 8;
8
6a: 80 91 60 00 lds r24, 0x0060
9
6e: 90 91 61 00 lds r25, 0x0061
10
72: 90 93 62 00 sts 0x0062, r25
11
low = LO8(word);
12
76: 80 91 60 00 lds r24, 0x0060
13
7a: 80 93 63 00 sts 0x0063, r24
14
high = HI8(word);
15
7e: 80 91 61 00 lds r24, 0x0061
16
82: 80 93 62 00 sts 0x0062, r24
17
}
18
86: 80 e0 ldi r24, 0x00 ; 0
19
88: 90 e0 ldi r25, 0x00 ; 0
20
8a: 08 95 ret
Beim Maskieren bzw. Schieben wird tatsächlich das komplette Wort in zwei
8-Bit-Register eingelesen, obwohl nur eines von den beiden anschließend
genutzt wird.
Bei der Makro-Version Hi8()/LO8() entfällt jeweils der überflüssige
Lesebefehl. Gefällt mir ganz gut, bis auf die fehlende Portabilität
bzgl. Endian der CPU.
Gruß,
Frank
Frank M. schrieb:> Beim Maskieren bzw. Schieben wird tatsächlich das komplette Wort in zwei> 8-Bit-Register eingelesen, obwohl nur eines von den beiden anschließend> genutzt wird.
Hallo Frank,
danke für's ausprobieren.
Das ist ja das, was mich so genervt hatte ...
Einerseits ist der Compiler intelligent genug, ganze Schleifen bzw.
Variablen wegzuoptimieren - auch wenn man das manchmal gar nicht will ;)
und andererseits werden hier Register geladen, deren Inhalt nie
gebraucht wird. Sowas müsste sich doch in einem zweiten Durchlauf
erschlagen lassen, oder?
> Bei der Makro-Version Hi8()/LO8() entfällt jeweils der überflüssige> Lesebefehl. Gefällt mir ganz gut, bis auf die fehlende Portabilität> bzgl. Endian der CPU.
Gefällt mir auch gut! Sobald ich anfange bei ATmega & Co. irgendwelche
besonderen Hardware-Ressourcen auszunutzen (Timer, ICP, usw.) hat sich
das mit der Portabilität ohnehin ganz schnell erledigt ...
Gruß
- Karl
Karl F. schrieb:> Gefällt mir auch gut!
Ich habe es gerade mal mit einem realen Source aus der Praxis
ausprobiert:
1
#include<inttypes.h>
2
3
#define F_CPU 8000000LU //CPU Takt
4
5
typedefunion
6
{
7
uint16_t_w;
8
uint8_t_a[2];
9
}tBuffer16;
10
11
#if 1
12
#define HI8(w) (((tBuffer16*)(&w))->_a[1])
13
#define LO8(w) (((tBuffer16*)(&w))->_a[0])
14
#else
15
#define HI8(w) ((w) >> 8)
16
#define LO8(w) ((w) & 0xFF)
17
#endif
18
19
void
20
itox(unsignedchar*buf,uint8_ti)
21
{
22
if(i<10)
23
{
24
*buf=i+'0';
25
}
26
else
27
{
28
*buf='A'+i-10;
29
}
30
}
31
32
void
33
itoxx(unsignedchar*buf,uint8_ti)
34
{
35
itox(buf,(i&0xF0)>>4);
36
itox(buf+1,i&0x0F);
37
}
38
39
void
40
itoxxxx(unsignedchar*buf,uint16_ti)
41
{
42
itoxx(buf,HI8(i));
43
itoxx(buf+2,LO8(i));
44
}
45
46
intmain()
47
{
48
unsignedcharbuffer[4];
49
itoxxxx(buffer,0x1234);
50
}
Witzigerweise braucht hier die Makro-über-union-Version 16 Byte mehr als
die Maskier-/Schiebevariante. Hier ist also das Verhältnis ungünstiger.
Das Verhältnis bleibt auch so, wenn man die ito*-Funktionen static
deklariert, und sie dann inline übersetzt werden. Wahrscheinlich verhält
sich der gcc lediglich bei volatiles so ungünstig beim Low-Byte-Zugriff
über Schieben/Maskieren. Ich hatte in meinem künstlichen Beispiel oben
mit Absicht volatiles gewählt, um Optimierungen seitens des Compilers
auszuschließen. Vielleicht war das eine falsche Testbedingung ;-)
Hier der Assembler-Output:
Maskieren/Schieben:
1
void
2
itoxxxx (unsigned char * buf, uint16_t i)
3
{
4
6c: ff 92 push r15
5
6e: 0f 93 push r16
6
70: 1f 93 push r17
7
72: 8c 01 movw r16, r24
8
74: f6 2e mov r15, r22
9
76: 67 2f mov r22, r23
10
itoxx (buf, HI8(i));
11
78: e7 df rcall .-50 ; 0x48 <itoxx>
12
itoxx (buf + 2, LO8(i));
13
7a: c8 01 movw r24, r16
14
7c: 02 96 adiw r24, 0x02 ; 2
15
7e: 6f 2d mov r22, r15
16
80: e3 df rcall .-58 ; 0x48 <itoxx>
17
}
18
82: 1f 91 pop r17
19
84: 0f 91 pop r16
20
86: ff 90 pop r15
21
88: 08 95 ret
Makros über union:
1
void
2
itoxxxx (unsigned char * buf, uint16_t i)
3
{
4
6c: 0f 93 push r16
5
6e: 1f 93 push r17
6
70: df 93 push r29
7
72: cf 93 push r28
8
74: 00 d0 rcall .+0 ; 0x76 <itoxxxx+0xa>
9
76: cd b7 in r28, 0x3d ; 61
10
78: de b7 in r29, 0x3e ; 62
11
7a: 8c 01 movw r16, r24
12
7c: 7a 83 std Y+2, r23 ; 0x02
13
7e: 69 83 std Y+1, r22 ; 0x01
14
itoxx (buf, HI8(i));
15
80: 6a 81 ldd r22, Y+2 ; 0x02
16
82: e2 df rcall .-60 ; 0x48 <itoxx>
17
itoxx (buf + 2, LO8(i));
18
84: c8 01 movw r24, r16
19
86: 02 96 adiw r24, 0x02 ; 2
20
88: 69 81 ldd r22, Y+1 ; 0x01
21
8a: de df rcall .-68 ; 0x48 <itoxx>
22
}
23
8c: 0f 90 pop r0
24
8e: 0f 90 pop r0
25
90: cf 91 pop r28
26
92: df 91 pop r29
27
94: 1f 91 pop r17
28
96: 0f 91 pop r16
29
98: 08 95 ret
Fazit: ich bleibe beim Maskieren/Schieben. Wenn der Source nicht so
künstlich, sondern eher praxisgerecht ist, ist die portable Variante
wohl sogar die bessere.
Gruß,
Frank
Naja, das größte Problem hab ich eigentlich bei volatiles, sprich in ISR
Routinen wo es mitunter wirklich völlig unnötig (und störend) ist, alle
zwei/view Bytes zu laden, wenn ich nur eines brauche.
Es geht (mir) auch nicht immer um Code-Größe, sondern öfters auch mal um
Geschwindigkeit. Wenn der Speicher nicht reicht, nehm ich zur Not den
nächstgrößeren AVR - wenn ich mit der Geschwindigkeit nicht hinkomme,
muß ich auf eine ganz andere CPU umsteigen - das ist deutlich mehr
Aufwand.
Oft ist (mir) auch reproduzierbare Geschwindigkeit wichtig. Deswegen
hatte ich auch weiter oben geschrieben, dass mir die 10000/1000/100/10
Subtraktionsmethode nicht so gut gefällt. Mich stört manchmal schon,
dass alleine das Konstrukt if/else je nach Bedingung einen Takt mehr
oder weniger braucht - das kann man aber leicht mit einem asm("nop")
wieder grade biegen.
Ich muß mich wohl noch mehr mit inline Assembler beschäftigen.
Assembler kann ich. C auch. Aber beim inline-Assembler tut der
Compiler nicht immer so ganz das, was ich gerne hätte :-/
naja ... dies ist ja ein recht langer Thread geworden, obwohl es
eigentlich nur um den direkten Zugriff auf ein Byte ging ;-)
Danke an alle, die mitgegrübelt haben - scheint ja alles nicht so
einfach zu sein.
Gruß
- Karl
Karl F. schrieb:> Naja, das größte Problem hab ich eigentlich bei volatiles, sprich in ISR> Routinen wo es mitunter wirklich völlig unnötig (und störend) ist, alle> zwei/view Bytes zu laden, wenn ich nur eines brauche.
Sowas darf der Compiler garnicht "optimieren", weil es die
volatile-Korrektheit zerstören würde.
Auch wenn zwischenzeitlich ähnliches geschrieben wurde, hier noch mein
'Senf' dazu:
Frank M. schrieb:> Ich habs ausprobiert für einen ATmega168 mit dem avr-gcc 4.3.3:
...
1
>typedefunion
2
>{
3
>uint16_t_w;
4
>uint8_t_a[2];
5
>}tBuffer16;
6
>
7
>#defineHI8(w)(((tBuffer16*)(&w))->_a[1])
8
>#defineLO8(w)(((tBuffer16*)(&w))->_a[0])
Ich habe es eben mal in mein Projekt eingesetzt und kam leider zu einem
negativen Ergebnis, zumindest an der Stelle, an der ich direkt ein
Rechenergebnis verwenden wollte:
Das gibt schon mal eine Fehlermeldung, es wird eine Variable als
Parameter für HI8() benötigt, und kein Ausdruck.
Aber selbst
1
uint16_ttemp=max_delay*rnd();
2
out_pwm_ptr->option|=HI8(temp)&DELAY_MASK;
bringt kein optimales Ergebnis, es benötigt 14 Bytes mehr als das
Anfangs erwähnte Union-Konstrukt, das ich üblicherweise in solchen
Fällen verwende.
Was bei mir alternativ zu dem Union-Konstrukt auch klappt:
Wenn ich den Ausdruck ins HI8() reinziehe, gibt es zwar keine
Fehlermeldung, der Compiler benötigt aber insgesamt 2 Byte mehr (wobei
mir auf die Schnelle nicht klar geworden ist, wo er die benötigt).
Volkmar
@Frank M. (ukw) Benutzerseite
>Beim Maskieren bzw. Schieben wird tatsächlich das komplette Wort in zwei>8-Bit-Register eingelesen, obwohl nur eines von den beiden anschließend>genutzt wird.
Das ist auch richtig so, der Compiler tut genau das, wass er nach
Konvention auch tun soll: Die Variable "word" und der direkte Zahlenwert
"0xFF" werden als integer behandelt (beim AVR 16Bit)
Konsequentes Casting auf 8 Bit sollte aber das Problem lösen, aber
natürlich wäre es schöner, wenn der Optimizer diese Fälle selber
optimieren würde...
Peter schrieb:> Das ist auch richtig so, der Compiler tut genau das, wass er nach> Konvention auch tun soll: Die Variable "word" und der direkte Zahlenwert> "0xFF" werden als integer behandelt (beim AVR 16Bit)
Ja, natürlich. Zumindest beim Schieben um 8 Bit nach rechts muss er
"word" als Integer behandeln. Aber er braucht den Low-Wert von "word"
dafür nicht extra in einem Register ablegen. Naja, ob er das bei einer
volatile-Variable trotz Nichtgebrauch machen soll, darüber lässt sich
trefflich streiten :-)
> low = (uint8_t)word & (uint8_t)0xFF;
Deine Casts hier sind unnötig, da kannst Du auch direkt
low = word;
schreiben. Es kommt auch derselbe Assembler-Output raus. Der Compiler
muss einen 16-Bit-Wert in einen 8-Bit-Variable quetschen. Er macht genau
dasselbe mit und ohne Maskierung und Cast.
> high = (uint8_t)word >> 8;
Dein Cast hier ist falsch! Wenn Du einen 8-Bit-Wert (durch Deinen Cast)
um 8 Bit nach rechts schiebst, kommt immer 0 raus ;-)
Beweis:
high = (uint8_t)word >> 8;
6a: 80 91 60 00 lds r24, 0x0060
6e: 90 91 61 00 lds r25, 0x0061
72: 10 92 62 00 sts 0x0062, r1
"word" wird nach r24/r25 geladen und anschließend wird in "high" das
Register r1 (was wohl 0 ist) gespeichert.
Auch hier wird die volatile-Variable "word" geladen, obwohl sie danach
komplett ignoriert wird.
Gruß,
Frank
Johann L. schrieb:> Sowas darf der Compiler garnicht "optimieren", weil es die> volatile-Korrektheit zerstören würde.
Das sehe ich anders. Ich kann nicht erkennen, warum ein unnötiges Laden
des Highbyte in ein Register bei
low = word >> 8;
die "volatile-Korrektheit" mehr erhält als wenn er dieses unterlassen
würde. Bei der union-Variante lädt er das Highbyte ja auch nicht.
Zur Erinnerung:
1
high = word >> 8;
2
6a: 80 91 60 00 lds r24, 0x0060
3
6e: 90 91 61 00 lds r25, 0x0061
4
72: 90 93 62 00 sts 0x0062, r25
5
6
high = HI8(word);
7
7e: 80 91 61 00 lds r24, 0x0061
8
82: 80 93 62 00 sts 0x0062, r24
Meines Erachtens ist der Befehl an der Stelle 6a: hyperfluid - egal, ob
volatile oder nicht.
Es werden hier also alle 4 Byte von "longword" in Register geladen.
Anschließend wird das Register, welches das oberste Byte beherbergt, in
ein 5. Register umgeladen, danach werden die anderen 3 Register
gelöscht(!) und letztendlich wird das Register r24 dann in low
gespeichert. Das könnte man reduzieren auf 2 Zeilen:
Frank M. schrieb:> Das sehe ich anders. Ich kann nicht erkennen, warum ein unnötiges Laden> des Highbyte in ein Register bei>> low = word >> 8;>> die "volatile-Korrektheit" mehr erhält als wenn er dieses unterlassen> würde.
Wenn "word" volatile markiert ist, dann zwingst du damit den
Compiler, es auf jeden Fall komplett zu lesen, mit allen Bits,
egal, ob sie danach gebraucht werden oder nicht. Denn volatile
besagt ja genau das: "Tu, was ich dir hingeschrieben habe, egal
ob du denkst, dass das nützlich ist."
> Bei der union-Variante lädt er das Highbyte ja auch nicht.
Weil dort dein Typecast-Gewurschtel dem Compiler exakt sagt, dass
er nur 8 bit davon lesen soll.
Frank M. schrieb:> Es werden hier also alle 4 Byte von "longword" in Register geladen.
Der "Knüller" daran ist nur, dass du offenbar die Semantik hinter
volatile einfach nicht verstanden hast. Du kannst nicht mit volatile
die Optimierung unterdrücken und dich anschließend drüber aufregen,
dass er nicht optimiert.
Frank M. schrieb:> Das sehe ich anders. Ich kann nicht erkennen, warum ein unnötiges Laden> des Highbyte in ein Register bei
Denk beispielsweise an I/O-Register, bei denen der Lesevorgang selbst
schon was auslöst, wie bei den 16-Bit Timern vom AVR. Wenn du da den
ersten der beiden Ladebefehle weglässt, dann liest der zweite Ladebefehl
Unsinn.
Jörg Wunsch schrieb:> Wenn "word" volatile markiert ist, dann zwingst du damit den> Compiler, es auf jeden Fall komplett zu lesen, mit allen Bits,> egal, ob sie danach gebraucht werden oder nicht. Denn volatile> besagt ja genau das: "Tu, was ich dir hingeschrieben habe, egal> ob du denkst, dass das nützlich ist."
Dann habe ich "volatile" bisher falsch verstanden. Ich dachte bisher,
ich sage damit dem Compiler:
"Der Wert dieser Variablen kann sich während der Verarbeitung ändern. Du
musst ihn also immer neu lesen und darfst ihn nicht (über Register)
cachen".
Danke, da habe ich wieder etwas dazugelernt :-)
A. K. schrieb:> Denk beispielsweise an I/O-Register, bei denen der Lesevorgang selbst> schon was auslöst, wie bei den 16-Bit Timern vom AVR. Wenn du da den> ersten der beiden Ladebefehle weglässt, dann liest der zweite Ladebefehl> Unsinn.
Vielen Dank für das plastische Beispiel des I/O-Registers. Das hat mich
endgültig überzeugt.
Bisher konnte ich mir einfach kein Szenario vorstellen, wie die
Daten-Integrität eines volatiles verletzt werden könnte, wenn man auf
das Lesen einzelner Bytes desselben verzichtet. Klar, bei einem
I/O-Register wäre das u.U. fatal.
Man lernt nie aus :-)
Sorry, ich muss jetzt doch noch mal quengeln ;-)
Ich habe jetzt alle volatiles entfernt. Leider werden immer noch alle
Bytes der 32-Bit-Variablen gelesen, obwohl sie nicht genutzt werden.
Code:
1
#include<inttypes.h>
2
3
uint8_tlow;
4
uint32_tlongword=0x12345678;
5
6
uint8_tlow2;
7
uint32_tlongword2=0x12345678;
8
9
typedefunion
10
{
11
uint16_t_w;
12
uint8_t_a[4];
13
}tBuffer32;
14
15
#define HI8L(lw) (((tBuffer32*)(&lw))->_a[3])
16
17
intmain()
18
{
19
low=HI8L(longword);
20
low2=longword2>>24;
21
}
Ergebnis:
1
int main ()
2
{
3
low = HI8L(longword);
4
5e: 80 91 63 00 lds r24, 0x0063
5
62: 80 93 68 00 sts 0x0068, r24
6
low2 = longword2 >> 24;
7
66: 80 91 64 00 lds r24, 0x0064
8
6a: 90 91 65 00 lds r25, 0x0065
9
6e: a0 91 66 00 lds r26, 0x0066
10
72: b0 91 67 00 lds r27, 0x0067
11
76: 8b 2f mov r24, r27
12
78: 99 27 eor r25, r25
13
7a: aa 27 eor r26, r26
14
7c: bb 27 eor r27, r27
15
7e: 80 93 69 00 sts 0x0069, r24
16
}
17
82: 80 e0 ldi r24, 0x00 ; 0
18
84: 90 e0 ldi r25, 0x00 ; 0
19
86: 08 95 ret
Das Makro machts also in 2 Zeilen (wie eigentlich erwartet), der Shift
braucht unverändert dafür 9 Zeilen - auch ohne volatile.
Ich schiebe das jetzt mal auf die schlechte Unterstützung des gcc von
8-Bit-Prozessoren.... und finde mich damit ab :-)
Frank M. schrieb:> Sorry, ich muss jetzt doch noch mal quengeln ;-)>> Ich habe jetzt alle volatiles entfernt. Leider werden immer noch alle> Bytes der 32-Bit-Variablen gelesen, obwohl sie nicht genutzt werden.
Also mein avr-gcc macht die Zugriffe kurz:
1
externcharc;
2
externunsignedlongl;
3
4
voidshift24(void)
5
{
6
c=l>>24;
7
}
8
9
voidshift16(void)
10
{
11
c=l>>16;
12
}
13
14
charrshift24(void)
15
{
16
returnl>>24;
17
}
18
19
voidshift25(void)
20
{
21
c=l>>25;
22
}
Wird mit avr-gcc-4.6.1 -S -Os zu
1
shift24:
2
lds r24,l+3
3
sts c,r24
4
ret
5
6
shift16:
7
lds r24,l+2
8
sts c,r24
9
ret
10
11
rshift24:
12
lds r24,l+3
13
ret
> Ich schiebe das jetzt mal auf die schlechte Unterstützung des gcc von> 8-Bit-Prozessoren.... und finde mich damit ab :-)
No, eher auf eine atwas angestaubte avr-gcc Version. Im avr-Backend
gibt's dafür keine Zauberei, es wird alles im maschinenunabhängigen Teil
erledigt.
Wenn allerdings nicht auf einzelne Bytes zugegriffen wird, wie das bei
shift25() der Fall ist, wird's länglich: Laden, Shiften, Speichern; fein
säuberlich getrennt:
1
shift25:
2
lds r24,l
3
lds r25,l+1
4
lds r26,l+2
5
lds r27,l+3
6
ldi r18,25
7
1: lsr r27
8
ror r26
9
ror r25
10
ror r24
11
dec r18
12
brne 1b
13
sts c,r24
14
ret
Wer das im avr-Backend (oder sonstwo in GCC) smarter machen will, kann
es gerne tun. Mir ist das Feld zu schwierig, und mit meinem momentanen
Wissensstand über GCC und Zeit, die ich in der Lage bin, darin zu
investieren, werde ich es nicht anfassen.
Vor einiger Zeit gab es einen ähnlich gelagerten Optimierungsfall ohne
Shift; was daraus geworden ist, weiß ich nicht. Wahrscheinlich nix.
http://lists.gnu.org/archive/html/avr-gcc-list/2011-05/msg00001.html
Und für 16-Bit Linksshifts, von denen der High-Teil nicht verwendet
wird, hat's immerhin 4½ Jahre gebraucht...
http://gcc.gnu.org/PR29560
Johann L. schrieb:> Also mein avr-gcc macht die Zugriffe kurz:> [...]> Wird mit avr-gcc-4.6.1 -S -Os zu> [...]
Gibt es eine einfache Möglichkeit, den avr-gcc-4.6.1 mit dem AVR Studio
4 für Windows zu verheiraten?
Jörg Wunsch schrieb:> Johann L. schrieb:>> bzw. was>> sie/jemand mit diesem Silizium [ATtiny] + GCC will.>> Naja, auch dort hast du bei einer Produktentwicklung in C eine bessere> Kosteneffizienz, nicht nur bei der Erstellung, sondern auch bei der> Pflege. (Außerdem soll's die Teile ja am Ende wohl bis zu einem> ATtiny40 geben.)>> Was ich mir bei derart kleinen Controllern gut vorstellen kann ist,> dass sie einen Markt im sicherheitstechnischen Bereich haben: eine> Firmware dieser Dimension lässt sich mit brauchbarem Aufwand noch> komplett testen, mit all ihren Eventualitäten.
Gibt für größere AVRs oder andere µC aber ebenso; ein Programm wird ja
nicht deshalb besser statisch analysierbar, daß man das Silizium bis zur
Unkenntlichkeit eindampft. Ich tippe eher auf Billigkram auf China wo's
auf 1/100 Cent ankommt.
Für Sicherheitstechnik fürde ich auch nicht einen neuen Core einsetzen
sondern einen, wo man sich halbwegs sicher sein kann, daß alle
Silicon-Bugs bekannt sind. Zudem evtl. Radiation Hardening oder was auch
immer und in dem Bereich wird eh nicht so gebitpopelt, zB keine
Optmiierung im Compiler erlaubt, etc.
>>> Ich tu' mir vieles im Opensource-Bereich an, aber da muss ich>>> leider auch passen. So wird es (leider) auch einigen anderen gehen,>>>> Schade eigentlich. Aber bei dir denk ich daß du ansonsten schon genug>> Projekte am Bein hast.>> Ja, das ohnehin. Außerdem darfst du nicht vergessen: ich bein /kein/> Informatiker, ich bin Elektronikingenieur (eigentlich Elektronik-> technologe).
Informatiker bin ich auch nicht, ich komm von der Algebraische
Zahlentheorie her.
> Insofern fehlen mir einfach mal einige wesentliche Grundlagen im> Hinblick auf Compiler-Architektur, und ich habe weder Zeit noch> Nerven, das nachzuholen.
Davon muss man zum Glück nicht viel wissen, wenn man nur ein Backend
etaw aufpolieren möchte. Zazu muss man nicht im Gedärm von GCC
rumrühren.
>> Atmel macht hier vermutlich den Fehler zu glauben, einen Entwickler>> einfach einkaufen zu können. Stattdessen muss hier auch kontinuerlich>> daran gearbeitet werden, ein eigenes Compilerteam aufzubauen und eine>> Mindestmaß an kritischer Masse zu erreichen, um>> Compilerentwicklung/anpassung sinnvoll zu machen.>> Es ist wohl in einer Firma, die sich als Hardwarehersteller sieht,> nicht ganz einfach, in der Chefetage die Notwendigkeit von Kompetenz> im Softwarebereich ins Blickfeld zu rücken. Kommt hinzu, dass AVR> historisch ja überhaupt nicht in Richtung GCC geguckt hat, sondern> sich erstmal voll auf IAR stützen wollte [...], und es hat einige> Jahre gedauert, bis Atmel dann erkannt hat, wie viel ihnen dieses> Teil an Reputation und damit perspektivisch auch an Gewinn wirklich> bringt.
Hätten sie besser mal Wikipedia gelesen ;-)
1
GCC has been ported to a wide variety of processor architectures, and
2
is widely deployed as a tool in commercial, proprietary and closed
3
source software development environments. GCC is also available for
4
most embedded platforms, for example [...] The compiler can target a
5
wide variety of platforms, including [...]. Several companies make a
6
business out of supplying and supporting GCC ports to various
7
platforms, and chip manufacturers today consider a GCC port almost
8
essential to the success of an architecture.
> Der AVR-GCC ist eine reine Community-Entwicklung
Keine Ahnung, wer was warum macht im GCC. Jedenfalls gehort einiges
dazu, gcc für ein neues Target anzupassen, als Hobby hat Denis das
garantiert nicht gemacht.
Übrigens ist avr-gcc bereits über 11 Jahre alt. Das avr-Backend wurde am
11. Februar 2000 eingespielt, allerdings noch als Frau ohne Unterleib;
letzerer kam dann am 16. Februar hinzu:
http://gcc.gnu.org/viewcvs?view=revision&revision=31935http://gcc.gnu.org/viewcvs?view=revision&revision=32002>> Ansonsten ist das AVR-Backend quasi tot. Anatoly ist nicht mehr aktiv>> und Andy Hutchinson seit Jahren nicht mehr gesehen. Eric fügt>> bestenfalls neue copy-paste Derivate ein und Denis beschränkt sich aufs>> Review der spärlichen Patches (immerhin!).>> Denis als ursprünglicher Autor hatte sich von AVR eigentlich komplett> zurückgezogen und mittlerweile andere GCC-Backends gebastelt> (möglicherweise bezahlt, keine Ahnung).
Weißt du für welche Maschine(n)? Würd mich interessieren. Ansonsten ist
Klatsch und Tratsch nicht so mein Metier :-)
> Aber es soll ja da noch einen Johann-Georg geben, der mittlerweile> sehr aktiv ist. :-) So wechseln halt die Gesichter über die Jahre,> und das ist meines Erachtens das beste Zeichen dafür, dass es durchaus> am Leben ist.
Naja, das ist Tropfen auf den heißen Stein, die wirklichen Nüsse sind so
nicht zu knacken. Das würde locker eine volle Stelle abgeben.
Adacore mischt doch bei avr/gcc mit (GNAT etc.), warum versucht's Atmel
nicht mal bei denen...?
Frank M. schrieb:> AVR Studio
¿¿¿ Was ist AVR Studio???
Johann L. schrieb:> Ok... hab's mit mal im Detail angeschaut für folgenden Code (modulo> Tippfehler):
1
>#defineumul_hi(a,b) \
2
>({uint8_t_c; \
3
>asm("mul %1, %2""\n\t"\
4
>"mov %0, R1""\n\t"\
5
>"clr __zero_reg__"\
6
>:"=r"(_c):"r"((char)a),"r"((char)b));\
7
>_c;})
8
...
9
>#defineumul_hi(a,b) \
10
>({uint8_t_a=a,_b=b; \
11
>uint8_t_c=_a*_b;_c;})
12
>#endif
Muss natürlich heissen
1
#define umul_hi(a,b) \
2
({ uint8_t _c, _a=(a), _b=(b); \
3
asm ("mul %1, %2" "\n\t"\
4
"mov %0, R1" "\n\t"\
5
"clr __zero_reg__"\
6
: "=r" (_c) : "r" ((char) _a), "r" ((char) _b));\
7
_c;})
8
...
9
#define umul_hi(a,b) \
10
({ uint8_t _a=a, _b=b; \
11
uint16_t _c=_a*_b; _a=_c>>8; _a;})
12
#endif
Warum sagt denn keiner was? ;-)
Damit ist die Zeitmessung Makulatur, also nochmal:
avr-gcc -Os -mmcu=atmega8 (Größe in Bytes)
1
Asm ohne Asm
2
3.4.6 254 214
3
4.2.2 290 244
4
4.3.3 260 226
5
4.5.2 206 176
6
4.6.1 186 160
Irgendwie kann ich diese Ergebnisse nicht ganz glauben, bzw. daß es so
große Unterschiede zwischen den Compiler-Versionen gibt und daß
Inline-Assembler das Resultat verschlechtert (vermultich weil kein CSE
mehr möglich ist. Hier stirbt gerade eine Legende ;-)
... 4.3 zeigt eine nicht gerade überzeugende Registeralliokierung, da
ist 4.5 merklich besser, allerdings stolpert der über fake X-addressing
(PR46278). 4.6 sieht brauchbar aus. Das Ergebnis für 4.3 im Vergleich
mit 3.4 ist plausibel: 3.4 war keine schlechte Version, allerdings kann
er noch kein SSA und macht viele Operationen unnötigerweise als int –
liegt aber dennoch vor 4.3.
Was 4.7 so treibt, danach schau ich mal die Tage. Momentan ist eine
Optimierung in der Mache, die nochmals ein paar Bytes/Ticks soll.
Johann L. schrieb:> Gibt für größere AVRs oder andere µC aber ebenso; ein Programm wird ja> nicht deshalb besser statisch analysierbar, daß man das Silizium bis zur> Unkenntlichkeit eindampft.
Nein, aber dadurch, dass es insgesamt klein bleibt. Wenn man aber
sowieso klein bleiben muss für eine bestimmte Aufgabe, dann kann
man auch das Silizium klein machen und damit billig.
> Ich tippe eher auf Billigkram auf China wo's> auf 1/100 Cent ankommt.
Nicht nur in China wird billig gebaut. ;-)
> Hätten sie besser mal Wikipedia gelesen ;-)
Gab's damals noch nicht. ;-)
>> Der AVR-GCC ist eine reine Community-Entwicklung>> Keine Ahnung, wer was warum macht im GCC. Jedenfalls gehort einiges> dazu, gcc für ein neues Target anzupassen, als Hobby hat Denis das> garantiert nicht gemacht.
Da hab' ich keine Ahnung. Von Anatoly kann ich dir mit Sicherheit
sagen, dass er das nur als Hobby gemacht hat, denn mit ihm habe ich
mich schon unterhalten können (so gut wie's geht: er kann zwar
Englisch lesen und schreiben, aber nur mit Wörterbuch, sodass wir
uns für die mündliche Kommunikation weitgehend auf mein ziemlich
rostiges Russisch stützen mussten ;-). Bei Marek Michalkiewicz
bin ich mir auch einigermaßen sicher, dass er das damals alles als
studentisches Freizeitprojekt gemacht hat. Nach dem Studium ist er
dann zu Intel und hat sich aufgrund irgendwelcher Klauseln in seinem
Arbeitsvertrag dann lieber aus der Opensource-Welt zurückgezogen.
> Übrigens ist avr-gcc bereits über 11 Jahre alt.
Ja, ohne das genaue Datum zu kennen, hatte ich das so ungefähr im
Gefühl. Ich habe Ende des Jahres nach langer Abstinenz in diesem
Bereich ein Controller-Projekt mit einem PIC gemacht. Das war,
nach all den vielen Jahren, die ich zuvor in C, C++ oder Skript-
sprachen programmiert habe, so frustrierend, den Assemblerhaufen
zum Spielen zu bekommen, dass ich mir vorgenommen habe, dass mein
nächster Controller einer mit einem C-Compiler sein wird. Aufgrund
weiterer Randbedingungen (sollte nach Möglichkeit Opensource sein,
Pflichtforderung war Lauffähigkeit auf FreeBSD) bin ich dann beim
AVR mit seinem noch vergleichsweise jungen Port von GCC, binutils
(immerhin schon, davor wurde ja nur mit AVRa assembliert) und den
Anfängen der avr-libc gelandet.
>> Denis als ursprünglicher Autor hatte sich von AVR eigentlich komplett>> zurückgezogen und mittlerweile andere GCC-Backends gebastelt>> (möglicherweise bezahlt, keine Ahnung).>> Weißt du für welche Maschine(n)?
Hab' ich vergessen. Wenn man jetzt nach seinem Namen gugelt, scheint
er vor allem bei OpenERP zugange zu sein.
Frank M. schrieb:> Johann L. schrieb:>> Frank M. schrieb:>>> AVR Studio>> ¿¿¿ Was ist AVR Studio???>> Die IDE von ATMEL, in welcher standardmäßig avr-gcc 4.3.3 verwendet> wird.
Na ja, erstens weiß der Johann das, und zweitens ist die Antoert in
Bezug auf das Studio 4 falsch. Das Studio 4 ist völlig unabhängig von
irgend einem avr-gcc. Das ruft lediglich über ein plugin den avr-gcc,
der gerade auf dem System installiert ist, auf. Die Compilerveriosn ist
dem Studio 4 egal.
Oliver
Oliver schrieb:> Na ja, erstens weiß der Johann das,
Das hab ich befürchtet. Angesichts der multiplen Fragezeichen von Johann
war ich dann aber doch etwas verunsichert. Offenbar waren das versteckte
Ironie-Tags ;-)
> und zweitens ist die Antoert in> Bezug auf das Studio 4 falsch. Das Studio 4 ist völlig unabhängig von> irgend einem avr-gcc. Das ruft lediglich über ein plugin den avr-gcc,> der gerade auf dem System installiert ist, auf. Die Compilerveriosn ist> dem Studio 4 egal.
Das dachte ich mir bereits. Damit reduziert sich meine ursprüngliche
Frage auf die unausgesprochenen Worte: Wo bekomme ich den avr-gcc-4.6.1
für Windows her? Auf Sourceforge finde ich nur einen WinAVR vom
20.01.2010. Das ist aber meines Wissens nach die gcc-Version 4.3.3, die
ich schon habe.
Gruß,
Frank
Edit:
Habs gefunden: Beitrag "avr-gcc 4.6.1 für Windows"
Danke
Frank M. schrieb:> Wo bekomme ich den avr-gcc-4.6.1> für Windows her?
Wahrscheinlich da, wo Johann seine Version auch herbekommen hat: aus
dem Quellcode.
Jörg Wunsch schrieb:> Wahrscheinlich da, wo Johann seine Version auch herbekommen hat: aus> dem Quellcode.
Danke, das weiß ich auch ;-) Bereits 1985 habe ich den gcc als
CrossCompiler für VME-Bus-Systeme ohne jegliches Betriebssystem auf
UNIX-System-V-Rechner portiert. Wie das geht, weiß ich also auch. Aber
es ist Arbeit ;-)
Und im fortgeschrittenen Alter wird man da etwas "fauler" und schaut
erstmal, ob es nicht schon einer gemacht hat. Daher meine Frage. Ich bin
auch unter
Beitrag "avr-gcc 4.6.1 für Windows"
fündig geworden. Mittlerweile habe ich den avr-gcc-4.6.1 laufen und
direkt erste Tests gemacht: Johann hat recht, der "neue" gcc reduziert
tatsächlich die byteweisen Shifts auf 2 Befehle. Sehr schön. Schade,
dass avr-size wegen -C noch nicht funktioniert. So hätte ich direkt mal
fertige AVR-Projekte von der Größe her vergleichen können.
So warte ich jetzt ungeduldig auf den avr-gcc-4.6.2, welcher dann wohl
als nächste WinAVR-Version rauskommen soll - so wie ich das verstanden
habe.
Vielen Dank an Johann.
Frank M. schrieb:> Schade,> dass avr-size wegen -C noch nicht funktioniert.
Dann lass das blöde -C weg. Nimm die Standardeinstellung, und
vergleich die paar Zahlen im Kopf. Ich fand diesen Hack von Eric
schon immer gruselig, vor allem deshalb, weil er absolut keine
Chance hat, jemals seinen Weg in die binutils zu finden.
hmmm. ich hab das mal mit einem nativ generierten avr-gcc-4.6.2-exp
erzeugt (oben ist -rc1), dafür bekomme ich
1
Asm ohne Asm
2
4.6.1-rc1 186 160
3
4.6.2-exp 186 190
4
4.7.0-exp 184 190
Das -exp steht füht "experimental" und -rc für "release candidate".
Irgendwas scheint im 4.6 faul zu sein.
Frank M. schrieb:> Beitrag "avr-gcc 4.6.1 für Windows"
Das ist nur eine prerelease zu Rumspielen und dafür, sich den Compiler
vor der geplanten WinAVR-Release mal näher betrachten zu begutachten zu
können. Die "Release Notes" stehen in dem Link zu avr-freaks.
Inzwischen wurde schon einige Fehler behoben, siehe avr-gcc Bugs im
Wiki.
Frank M. schrieb:> Mittlerweile habe ich den avr-gcc-4.6.1 laufen
Macht er wenigstens, was er soll? Der Unterschied 160 (die Version -rc1,
die du hast) und -exp dürften eigentlich kein Unterschied zeigen.
Johann L. schrieb:> Das ist nur eine prerelease zu Rumspielen und dafür, sich den Compiler> vor der geplanten WinAVR-Release mal näher betrachten zu begutachten zu> können. Die "Release Notes" stehen in dem Link zu avr-freaks.
Ja, ich hatte gelesen, dass es sich um eine RC1 handelte.
> Macht er wenigstens, was er soll? Der Unterschied 160 (die Version -rc1,> die du hast) und -exp dürften eigentlich kein Unterschied zeigen.
Ich hatte es nur mal angetestet mit den obigen Codebeispielen. Ich werde
in den nächsten Tagen mal einige Projekte (IRMP, SOUNDRX, MCURSES) mit
dem avr-gcc-4.6.1 übersetzen und durchchecken. Kann aber etwas dauern,
bis ich dazu komme.
Gruß,
Frank