Ich wollte eigentlich nur das Programm durch Verwenden der uart Routinen als Inline beschleunigen, aber mit Inline funktioniert garnichts mehr. Also habe ich mir den erzeugten Code angeschaut (Optimierung aus Codegröße) und diesen Mist gefunden (ohne inline): - Der Compiler liest der Stackpointer ein, Manipuliert den Wert und schreibt ihn am Ende wieder korrekt zurück, obwohl der Stackpointer nie benötigt wird. - Der Compiler schaltet die Interrupts für die gesamte Funktion aus. - Der Compiler legt lokalen Variablen in den RAM obwohl die Register reichen würden. Kann mir jemand erklären, warum der Compiler so einen Mist macht ?
> Kann mir jemand erklären, warum der Compiler > so einen Mist macht ? Er macht zumindest weniger ,,Mist'' als du beim Interpretieren seines Codes.
hallo, du hast da meiner meinung nach sehr stark vereinfacht. der stackpointer wird manipuliert, das ist fakt. aber der reihe nach. also zuerst wird der SP in r28 und r29 geladen, und dieses dann mit 'sbiw r28,1' um einen dekrementiert (ist eine word-operation, betrifft also auch r29) danach wird mit 'in _tmp_reg_,__SREG__' das statusregister gerettet in ein temporäres register. wozu? um danach den alten zustand wieder herstellen zu können, denn mit dem nachfolgendem 'cli' wird das I-Flag des status-registers SREG gelöscht. die irq's ausgeschaltet. dann wird der stack high geschrieben 'out _SP_H_,r29'. so, jetzt wird das SREG wieder hergestellt: out _SREG_,__tmp_reg__ das bedeutet auch, das, wenn vorher dem 'cli' die irq's an waren, sind sie auch wieder an, und umgekehrt auch! warum erst nachher das low-byte des stack geschrieben wird, weiß ich nicht. bye kosmo
Kann mir jemand sagen, warum der Compiler die IR sperrt? Ich sehe im Quellcode nichts, was ihn sazu veranlassen könnte. Oder fehlt vom Quellcode noch etwas wichtiges? Danke, Stefan
um die atomarität sicherzustellen, vermute ich. d.h. zum geschriebenen r29 muss das vorhergehende r28 passen. nötig sind zwei cycles für das schreiben des 16bit-SP. wäre vll dumm, nach dem schreiben des ersten bytes von nem irq unterbrochen zu werden, der dann möglicherweise 'böse' ist und irgendwas verändert. dann würde das andere byte geschrieben, aber passt nicht mehr zum veränderten wert. 'out' ist atomar 1 cycle. also ist es vollkommen legal, das cli schon vor dem zweiten 'out' wieder rückgängig zu machen. oder, jörg?
Mein Exemplar von WinAVR erzeugt völlig anderen Code, sowohl mit also auch ohne Optimierung. Den vollständigen Stack-Frame kriege ich nur ohne Optimierung, dann aber wird rr28 auch zu Adressierung lokaler Daten benutzt. Welche Version, welche Compiler-Flags? Der Stack-Pointer wird nach rr28 kopiert, wo er als Basis zur Adressierung lokaler Daten dient. Allerdings macht er das bei mir nur wenn auch nötig. "Kann mir jemand sagen, warum der Compiler die IR sperrt?" Überleg mal was passiert, wenn mitten in der Manipulation des Stack-Pointers ein Interrupt eintrifft. "Optimierung aus" Dann erwarte bitte auch keinen optimalen Code.
"das cli schon vor dem zweiten 'out' wieder rückgängig zu machen." Liegt an einer Eigenheit des AVR: Bei SEI ist dokumentiert, dass nach dem Einschalten von Interrupts immer erst der nächste Befehl ausgeführt wird (dieses Verhalten ist für den Sleep-Mode einigermassen wichtig). Bei OUT SREG ist das auch so, nur nicht dokumentiert.
Anscheinend kommt der Compiler nicht mit dem selbst erstellen Array zurecht, das ich in den externen RAM gelegt habe: http://www.mikrocontroller.net/forum/read-2-417813.html#new typedef unsigned char (*fram)[XSize]; const fram pixel = (fram)0x8000; Jetzt habe ich die noinit section in den externen RAM verlagert und in der noinit section das unsigned char [YSize][XSize] Array erstellt, und der Code sieht normal aus. Jetzt kommt die Funktion komplett ohne push/pop aus.
Nein, doch nicht. Ich hatte mich verschaut. Auch mit einem normalen Array macht der Compiler diesen Mist.
Nur wenn ich die uart_getchar() Funktion als inline deklariere, wird der Code normal (sogar ohne push und pops). Dafür funktioniert das Programm aber nichtmehr... #define FT245_Data _SFR_MEM8((unsigned)256*16) #define RXF (PIND&32) _inline_ static unsigned char uart_getchar(void) { while (RXF); return FT245_Data; }
Es war ein Timingproblem mit dem FT245, jetzt geht die Software mit der uart_getchar Funktion auch als inline. Aber auch hier bläht der Compiler den Code unnötig auf: Aus while (PIND&32); return FT245_Data; macht er machnmal .L44: sbic 48-0x20,5 rjmp .L44 lds r24,4096 aber manchmal auch .L48 in r24,48-0x20 clr r25 movw r18,r24 andi r18,lo8(32) andi r19,hi8(32) sbrc r24,5 rjmp .L48 lds r24,4096
> Aber auch hier bläht der Compiler den Code unnötig auf Es ist bekannt, dass WinAVR unnötig großen Code erzeugt.
Es ist bekannt, dass WinAVR gar keinen Code erzeugt. Es installiert nämlich nur die Tools. WinAVR ist kein Compiler. Es ist aber auch bekannt, dass Compiler manchmal Dinge machen, die man als Mensch so nicht machen würde, dafür sind es Maschinen. Zuweilen tun sie das sogar zum Positiven: dank ihrer stupiden Vorgehensweise entdecken sie Optimierungen, für die wir als Mensch ein Brett vorm Kopf haben. Zuweilen coden sie natürlich auch Dinge, von denen ein Mensch sofort offensichtlich sagt, dass man dies oder jenes weglassen kann. Es ist auch bekannt (nach dem Lesen dieses Threads), dass der Herr Benedikt sichtlich Probleme hat, funktionierenden Code zu verfassen und erst Recht welche, den generierten Code vernünftig im ersten Anlauf zu interpretieren. Ich bin mir bei obigem Code-Schnipsel ziemlich sicher, dass der Compiler nicht erst rr18/19 belegt, um dann trotzdem nur r24 auszuwerten. Das lässt sich aber nur im gesamten Kontext erkennen. Außerdem dürfen sich auch Compiler weiter entwickeln, und GCC 3.4.6 ist natürlich nicht mehr der Stein der Weisen -- GCC 4.1.x generiert im Allgemeinen für den AVR bereits deutlich kompakteren Code.
Da möchte ich auch noch meinen Senf dazugeben: Es ist auch bekannt, dass Compiler im Allgemeinen besseren Assembler Code produzieren als ca. 95% aller Programmierer. Mit anderen Worten: Um die meisten Compiler zu schlagen, muss man schon ziemlich gut in Assembler sein. Jemand der nur ab zu in Assembler programmiert hat gegen einen vernünftigen Compiler so gut wie keine Chance.
@Jörg "... GCC 3.4.6 ist natürlich nicht mehr der Stein der Weisen -- GCC 4.1.x generiert im Allgemeinen für den AVR bereits deutlich kompakteren Code." Das neueste WINAVR installiert aber nur 3.4.6 Wie kann man denn auf 4.1.x updaten ? Peter
> Es ist bekannt, dass WinAVR gar keinen Code erzeugt. > Es installiert nämlich nur die Tools. WinAVR ist kein > Compiler. WinAVR installiert auch keine Tools, dass mach der Installer.
ich liebe immer diese "bug reports"... man beachte folgendes regelwerk: 1. der compiler hat immer recht (ausgenommen er kommt direkt aus dem cvs.. dann hat er nur fast immer recht) vorallem wenn er aus der GCC stammt 2. ein compiler der gut optimiert erzeigt immer code den man nicht versteht 3. rtfm 4. rtfw 5. bugreports erst wenn die neuste version auch den bug hat (ich nehme mal an dass es was neueres gibt wenn jörg schonmal meint 3.4.6 nichtmehr der stein der weisen sei...) 6. poste alle infos sonst bekommt man keine klaren bzw nützlichen antworten
'Das neueste WINAVR installiert aber nur 3.4.6 Wie kann man denn auf 4.1.x updaten ?' das ist auf der Seite von P. Fleury beschrieben. Habe ich zufällig bei unserer Bootloader Diskussion entdeckt, er hat seine letzte Version bereits mit dem 4.1 übersetzt.
@Hans Was brauchst du noch an Infos ? Ich habe den Code der kompletten Funktion einschließlich aller Funktionen die darin aufgerufen werden gepostet. Und falls der Compiler recht hat, dann kann mir bestimmt jemand erklären warum der Compiler diesen (aus seiner Sicht) sinnvollen Weg eingeht. Gibt es eigentlich demnächst wieder mal eine neue WinAVR Version ?
Vollständigen Quellcode. Vollständig!! Also alles was fürs übersetzen benötigt wird, inklusive Makefile usw. Idealerweise nicht in Form von 20MB Plattenabzug, sondern soweit reduziert, dass nur noch der problematische Teil in Form von ein paar Zeilen übrig bleibt. Welcher Compiler? Also beispielsweise welches WinAVR.
Wenn es so einfach wäre, hätte ich das schon längst gemacht. Allerdings darf ich den kompletten Code nicht veröffentlichen. Ich habe jetzt mal alles weggelassen, außer den beiden Funktionen, das Problem existiert weiterhin. GCC Version: avr-gcc (GCC) 3.4.5 WinAVR Version vom 25.01.2006
Hat vielleicht einer die gcc.exe V4.1.1 schon fertig ? Peter
Interessant. Definiere mal uart_getchar() mit "unsigned" statt "unsigned char" als Ergebnis. Macht in dieser Funktion Null Unterschied, weil's der Compiler ohenehin so hält. Aber dafür bleibt der LCD-Routine der Frame erspart.
Mit unsigned int oder nur unsigned wird der Code um 46Bytes kleiner und der ganze Mist ist weg. Aber warum ist das so ? Kann mir das jemand erklären ?
Auch interessant: Original Code mit -O3 compiliert: keine lokalen Variablen und der Compiler inlined automatisch.
> und der ganze Mist ist weg. Ich nehme an, mit Mist meinst du die 1 lokale Variable die nach der Optimierung übrig geblieben ist und deretwegen im Funktionsprolog der Stackpointer korrigiert wird. > Aber warum ist das so ? Kann mir das jemand erklären ? Ist nicht ganz durchsichtig warum. Ich denke es könnte daran liegen, dass automatisches inlinen bei einer Optimierungs- stufe Os ausgeschaltet ist. Normalerweise macht inlinen den Code größer und Os hat sich ja auf die Fahnen geheftet, möglichst kleinen Code zu produzieren. In deinem Fall ist es aber umgekehrt, da das inlinen Optimierungsmöglichkeiten eröffnet, die letztlich zum Wegfall der 1nen übriggebliebenen lokalen Variablen eröffnet und damit den ganze Stackpointer Kram unnötig macht. Man darf halt nicht vergessen, dass Optimierungsstufen auf Heuristiken beruhen. Es ist ja nicht so, dass der Compiler bei Os einmal mit Auto-Inlining compiliert und einmal ohne und schaut was kleiner ist. Das erklärt aber nicht den Unterschied zwischen unsigned int und unsigned char. Der ist mir auch noch ein Rätsel.
> Das erklärt aber nicht den Unterschied zwischen unsigned int und > unsigned char. Vermutung: unsigned char wird nach integer promotion rules zuerst zu `int' erweitert und erst von dort ggf. nach unsigned int. Im Anhang übrigens der Code, der der GCC 4.1.0 daraus erzeugt (mit den Werten aus dem Makefile, nur -g der Übersichtlichkeit halber entfernt). Sieht besser aus als 3.4.x, würde ich mal sagen.
@Peter Dannegger http://www.atmanecl.net/EnglishSite/softwareEnglish.htm http://www.atmanecl.net/download/avrgcc411.zip
Reicht es aus, avrgcc411.zip in den WinAVR Ordner zu entpacken ? Ich habe es mal gemacht, und es scheint zu funktionieren. Es hat sich echt einiges geändert, die meisten Programme werden damit etwas kleiner.
> Es hat sich echt einiges geändert, die meisten Programme > werden damit etwas kleiner. Sach ich doch. Im Durchschnitt ist die Optimierung beim AVR mit dieser Version besser als mit den 3.x-ern. Sicher findest du allemal pathologische Fälle, wo der 3.x-er GCC besseren Code erzeugt hat, aber in der realen Welt ist 4.1.x wirklich ein Fortschritt (nachdem zuvor die 3er Versionen bezüglich des AVR-Targets, das ja nicht gerade GCC-Mainstream ist, graduell leicht schlechter geworden waren).
Ich hab die 4.1.1 ausprobiert. Bei vielen Projekten kriegt man tatsächlich eine ganz leichte Reduzierung (1..2%). Nur mein Jumbo-LED Beispiel wird dafür gleich 7% größer. Allerdings erhalte ich nun immer folgende 2 Warnungen: C:\DOKUME~1\danni\LOKALE~1\Temp/cc6naaaa.s:69: Warning: expression dangerous with linker stubs C:\DOKUME~1\danni\LOKALE~1\Temp/cc6naaaa.s:70: Warning: expression dangerous with linker stubs Da scheint also irgendwas noch nicht ganz astrein zu sein, weshalb der WINAVR immer noch die alte 3.4.6. Version benutzt. So schlimm sind ja die 1..2% mehr Code nun auch nicht. Peter
Einige der Warnungen sind ganz sinnvoll. So erzeugt 4.1.1 z.B. Warnungen wenn man pointer mit unterschiedlichen Vorzeichen übergibt. Es gibt nichts schlimmeres als ein Compiler bei dem standardmäßig die Warnungen fast alle abgeschaltet sind. Eine Klammer hinter einem Funktionsaufruf vergessen, und man wundert sich warum nichts geht. WinAVR gibt bei sowas (zum Glück) eine Warnung aus.
> Allerdings erhalte ich nun immer folgende 2 Warnungen: > Warning: expression dangerous with linker stubs Das wäre mal interessant, dass du dir die Stelle in den Assemblerdateien dazu ansiehst. Dazu musst du über alle Einzelschritte gehen, also statt mit -c von .c über eine temporäre Assemblerdatei nach .o zuerst mit -S von .c nach .s (generierter Assemblercode des Compilers) gehen und dann auf diese Datei nochmal den avr-as ansetzen. Das hat übrigens überhaupt nichts mit GCC 4.1.x zu tun, sondern das kommt von den linker stubs, die für den Support der ATmega256x eingebaut worden sind. Der Patch ist derzeit noch experimentell, AtmanAVR hat ihn aber bereits eingebaut. Diskussionen darüber am besten auf avr-gcc-list (at nongnu.org) führen, dort liest Björn Haase, der Autor des Patches, mit. > Da scheint also irgendwas noch nicht ganz astrein zu sein, weshalb > der WINAVR immer noch die alte 3.4.6. Version benutzt. Quatsch mit Soße. Beim letzten WinAVR war der GCC für Eric Weddington nicht zu schaffen, beim nächsten wird 4.1.1 dabei sein -- und zwar mit dem experimentellen ATmega256x-Patch.
@Jörg, also die Warnungen kommen von diesen beiden Zeilen (48,49) in main.s: subi r30,lo8(-(gs(.L12))) sbci r31,hi8(-(gs(.L12))) Warscheinlich erzeugt jede switch-Anweisung mit Sprungtabelle diese Warnungen. Da ich ja eh nie vorhabe, >=Mega128 einzusetzen, dürften die Warnungen also belanglos sein. Peter
gs ist ein Operator, den Björn dafür neu eingeführt hat... Ich kann mir vorstellen, was die Warnung bedeutet: bei dieser Operation könnte es passieren, dass man eine 128-KB-Grenze überschreitet. In diesem Falle wäre man natürlich aufgeschmissen... Wie geschrieben, wenn du das diskutieren willst, ist die avr-gcc-list das Forum der Wahl.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.