Hallo,
Ich habe heute in den vom avrgcc generierten assemblercode mal
reingelinst und glaube da sinnlosen code zu sehen. vermutlich überseh
ich ja nur etwas aber würde mich mal interessieren ob mir jemand meinen
denkfehler erklären kann oder ob ich tatsächlich über einen compilerbug
gestolpert sein sollte:
1
UCHAR TransformByLookUp(long In)
2
{
3
62e: 9b 01 movw r18, r22
4
630: ac 01 movw r20, r24
5
UCHAR Result, cTemp;
6
uint16_t In_h = (uint16_t)(In>>16);
7
632: ca 01 movw r24, r20
8
634: bb 27 eor r27, r27
9
636: 97 fd sbrc r25, 7
10
638: b0 95 com r27
11
63a: ab 2f mov r26, r27
12
63c: dc 01 movw r26, r24
13
...
Mir geht es speziell um die letzten beiden assemblerbefehle. Wieso
kopiert er von r27 nach r26 daten um dann im nächsten schritt den inhalt
von r24-25 nach r26-27 zu kopieren? die zeile könnte man doch
weglassen?!?
Ich verwende gcc 4.3.2 in winavr 20090313. Ich wollte schonmal auf den
neueren aus avrstudio 5 umsteigen, jedoch hat dieser bedeutend (ca. um
10%) größeren code generiert (Falls jemand mir sagen kann wie ich am
schnellsten herausfinden kann was da am code größer geworden ist wäre
ich natürlich auch sehr dankbar. Das Programm ist leider so komplex
geworden, dass ich ungern die ganzen beiden lss dateien lesen und
vergleichen will ;) )
Sebastian Steppeler schrieb:> oder ob ich tatsächlich über einen compilerbug> gestolpert sein sollte
Naja, "bug" würde ich das nicht nennen, eher "ausgelassene
Optimierungsmöglichkeit".
Der gcc hat ja nicht den Anspruch, den
"kleinstmöglichsten/schnellstmöglichen" Binärcode zu erzeugen, sondern
erstmal die Aufgabe korrekt zu Compilieren.
Oder: mit handoptimiertem ASM kann man fast immer noch ein paar
Taktzyklen rausholen.
Nachdem deine GCC-Version von 2009 ist: Hast du mal eine aktuelle
dagegengetestet?
Naja ist schon klar, dass die optimierung nicht perfekt ist, aber dass
er so offensichtlich sinnlosen code erzeugt ist mir schleierhaft, ich
würde nichtmal erwarten dass er mir sone zeile produziert wenn die
optimierung komplett aus wäre, aber compilerbau ist eine wissenschaft
für sich und ich weiß nicht ob ich das so einfach beurteilen kann.
Habe gerade mal den gcc4.5.1 gegengetestet, der macht diesen fehler
nicht, aber wie gesagt der code ist 10 % größer, das kann ich mir leider
nicht leisten. Werde mal die aktuellste version runterladen und
spaßeshalber testen.
noch eine frage: gibt es unter windows schon neuere avr-gccs als den
4.3.3 ?
das ist der aktuelle aus dem neusten winavr.
In Avrstudio 5 ist 4.5.1 integriert, den habe ich schon getestet. Der
macht den fehler nicht aber viel zu großen code...
Meine Kristallkugel sagt, daß In 32-Bit signed ist. Das letzte MOV
gehört zu dem signed-Shift.
Vielleicht hülft -f[no-]split-wide-types
Um wirklich Aussageb machen zu können, bitte compilierbaren Code
posten, nicht ein Märlchencode wo man Typen und Code-Kontext aus dem
Kaffeesatz lesen muss.
Εrnst B✶ schrieb:> Der gcc hat ja nicht den Anspruch, den> "kleinstmöglichsten/schnellstmöglichen" Binärcode zu erzeugen, sondern> erstmal die Aufgabe korrekt zu Compilieren.
Hmm also -Os teilt dem Compiler doch genau diesen Wunsch mit. So klein
wie möglich.
> Oder: mit handoptimiertem ASM kann man fast immer noch ein paar> Taktzyklen rausholen.
Da gehe ich nicht ganz d'accord mit dir ;-) Ob das "fast immer" so
stimmt glaube ich nicht. Ausserdem muss man sich fragen zu welchem
Preis? Die Wahrscheinlichkeit ist groß das am Ende die ganze Funktion
nicht mehr (immer) genau das tut was sie soll. Oftmals stellen sich doch
auf den ersten Blick unnötige ASM Befehle bei genauerem hinsehen als
unverzichtbar heraus.
Gerade bei diesem Beispiel, warum sollte der Compiler völlig unnötige
Befehle an den Schluss hängen? Ich meine dass er mal ungeschickt
optimiert kann vorkommen, aber sowas.
Und ja, hier würde mich die Funktion in C Quellcode auch interessieren.
Ohne kann man da sowieso nicht viel sagen.
gruß cyblord
PS: Müsste dieser Thread nicht in die gcc Rubrik?
cyblord ---- schrieb:> Hmm also -Os teilt dem Compiler doch genau diesen Wunsch mit. So klein> wie möglich.
Ja, und? Woher soll er das wissen?
Das ist doch kein Mensch, sondern der arbeitet formale Algorithmen
ab. Wenn da eine bestimmte Optimierungsmöglichkeit noch nicht
erfasst worden ist, kann der Compiler sie auch nicht passend
generieren.
-Os wird gern überschätzt. Dabei ist die Option so dokumentiert:
"Benutze all die Optimierungen, die über -O1 hinausgehen, und von denen
bekannt bzw. zu erwarten ist, dass sie nicht zu einer Vergrößerung
des generierten Codes führen."
> PS: Müsste dieser Thread nicht in die gcc Rubrik?
Wäre sinnvoll, ich schieb' ihn mal.
Jörg Wunsch schrieb:>> PS: Müsste dieser Thread nicht in die gcc Rubrik?>> Wäre sinnvoll, ich schieb' ihn mal.
Ich hatte ihn hier gepostet, weil es ja speziell um den avr-gcc ging,
aber ist ja dann ganz gut so.
Johann L. schrieb:
> Meine Kristallkugel sagt, daß In 32-Bit signed ist. Das letzte MOV> gehört zu dem signed-Shift.>> Vielleicht hülft -f[no-]split-wide-types
Das kann man an dem codeschnipsel ja auch erkennen(siehe zeile 1).
Was genau ist ein signed-Shift?
hier mal der komplette code der funktion:
1
// lookup table:
2
longlLookUpl[63];
3
uint16_tiLookUph[64];
4
5
UCHARTransformByLookUp(longIn)
6
{
7
UCHARResult,cTemp;
8
uint16_tIn_h=(uint16_t)(In>>16);
9
/*if(In<0)
10
return 0;*/
11
12
//Result = 0;
13
14
// << + loop slows down conversion so lets write it out
Sebastian Steppeler schrieb:> hier mal der komplette code der funktion:
code.c:1:1: error: unknown type name 'UCHAR'
code.c: In function 'TransformByLookUp':
code.c:3:3: error: unknown type name 'UCHAR'
code.c:4:3: error: unknown type name 'uint16_t'
code.c:4:20: error: 'uint16_t' undeclared (first use in this function)
code.c:4:20: note: each undeclared identifier is reported only once for
each function it appears in
code.c:18:6: error: 'iLookUph' undeclared (first use in this function)
code.c:51:8: error: 'lLookUpl' undeclared (first use in this function)
Sebastian Steppeler schrieb:> Das kann man an dem codeschnipsel ja auch erkennen(siehe zeile 1).> Was genau ist ein signed-Shift?
Signed Variablen werden anders geschoben als unsigned-Variablen, da ja
im ersten Fall das Vorzeichen erhalten bleiben muss. Beim Schieben nach
rechts muss bei einem negativen Wert das oberste Bit weiterhin gesetzt
bleiben, sonst verliert die Variable ja ihr Vorzeichen! Genau das
berücksichtigt der gcc, wo Du denkst, das wäre hyperfluid.
Daher: Ändere die Variable "In" in "unsigned long" statt "long".
Schiebereien von vorzeichenbehafteten Variablen machen nur in den
wenigsten Fällen Sinn.
cyblord ---- schrieb:> Warum benutzt du UCHAR als Datentyp? Warum nicht char oder uint8_t> stattdessen? Gibts da einen Vorteil?
Hat wohl MS irgendwann eingeführt, entweder kannten die da noch nicht
stdint.h oder es hat historische Gründe, stammt also noch aus der
Urzeit, wo es stdint.h noch nicht gab.
Siehe auch:
http://msdn.microsoft.com/en-us/library/cc230382%28v=prot.10%29.aspx
Um das Beispiel übersetzen zu können fehlen noch folgende typedefs,
deklarationen, defines, was-auch-immer:
UCHAR
uint16_t
iLookUph
lLookUpl
uint16_t gibt's zum Beipiel mit #include <stdint.h>
sorry hab da wohl dann doch noch was vergessen mitzuliefern ;) ist immer
blöd eine funktion aus einem projekt zu posten. UCHAR ist eine dumme
alte microsoft angewohnheit von mir, die ich nutze weil sie schneller zu
tippen geht als uint8_t ;) ich editier mal den code so um, dass er
compilierbar sein sollte.
die bei den lookuparrays hatte ich schon mit einem edit nachgeliefert
ohne ihn zu bezeichnen, wer konnte ahnen, dass ihr so schnell seid ;)
1
#include<avr/io.h>
2
#include<math.h>
3
#include<stdio.h>
4
#include<stdlib.h>
5
#include<util/delay.h>
6
#include<avr/wdt.h>
7
#include<avr/interrupt.h>
8
9
10
#define UCHAR unsigned char
11
12
13
// lookup table:
14
longlLookUpl[63];
15
uint16_tiLookUph[64];
16
17
UCHARTransformByLookUp(longIn)
18
{
19
UCHARResult,cTemp;
20
uint16_tIn_h=(uint16_t)(In>>16);
21
/*if(In<0)
22
return 0;*/
23
24
//Result = 0;
25
26
// << + loop slows down conversion so lets write it out
Frank M. schrieb:> Sebastian Steppeler schrieb:>> Das kann man an dem codeschnipsel ja auch erkennen(siehe zeile 1).>> Was genau ist ein signed-Shift?>> Signed Variablen werden anders geschoben als unsigned-Variablen, da ja> im ersten Fall das Vorzeichen erhalten bleiben muss. Beim Schieben nach> rechts muss bei einem negativen Wert das oberste Bit weiterhin gesetzt> bleiben, sonst verliert die Variable ja ihr Vorzeichen! Genau das> berücksichtigt der gcc, wo Du denkst, das wäre hyperfluid.>> Daher: Ändere die Variable "In" in "unsigned long" statt "long".> Schiebereien von vorzeichenbehafteten Variablen machen nur in den> wenigsten Fällen Sinn.
Okay dast mit dem signed shift ist mir jetzt klar, kannte ich sogar aber
war mich nicht klar was du meintest.
Aber wo siehst du hier ein shiften? Ich sprach doch von zwei mov
operationen von denen die zweite die erste überflüssig macht.
Btw. Der avr assembler hat eine instruktion für arithmetisches shiften,
also macht es vom rechenaufwand keinen unterschied ob man signed oder
unsigned shifts hat.
Volkmar Dierkes schrieb:> Sebastian Steppeler schrieb:>> noch eine frage: gibt es unter windows schon neuere avr-gccs als den>> 4.3.3 ?>> Schau zum Beispiel mal hier:> Beitrag "Re: Ist WinAVR tot ?"
Vielen dank für diesen tollen link. Ich habe die version 4.7.0 mal
runtergeladen und installiert und mein projekt kompiliert. Ergebnis:
Das Kompilat ist jetzt noch kleiner als mit den 4.3.x versionen des
compilers und die überflüssigen instruktionen sind auch nicht mehr da.
War echt ne super hilfe :)
Sebastian Steppeler schrieb:> Okay dast mit dem signed shift ist mir jetzt klar, kannte ich sogar aber> war mich nicht klar was du meintest.> Aber wo siehst du hier ein shiften? Ich sprach doch von zwei mov> operationen von denen die zweite die erste überflüssig macht.
Neuere avr-gcc Versionen erkennen das auch, bzw. daß der high-Teil des
geschobenen Wertes nicht benötigt wird.
Mit dem alten Compiler ist's eben so, daß erst signed geschoben wird:
In R18..R21 steht ein signed 32-Bit Wert, der um 16 nach rechts
geschoben und dabei nach R24..R27 kopiert wird:
movw r24, r20
eor r27, r27
sbrc r25, 7
com r27
mov r26, r27
Danach folgt dann die Extraktion des 16-Bit Wertes, was dem Cast
entspricht.
In neueren Compilerversionen taucht dieser Shift garnicht mehr auf, er
wird quasi implizit erledigt.
Sebastian Steppeler schrieb:> Aber wo siehst du hier ein shiften? Ich sprach doch von zwei mov> operationen von denen die zweite die erste überflüssig macht.
Warum hast Du es nicht einfach ausprobiert?
Hier mein Test mit WinAVR-20100110 (AVR-Studio 4 mit aktualisierter
WinAVR-Version):
1
long In;
2
uint16_t x;
3
4
x = (uint16_t) (In>>16);
5
ee: 80 91 24 01 lds r24, 0x0124
6
f2: 90 91 25 01 lds r25, 0x0125
7
f6: a0 91 26 01 lds r26, 0x0126
8
fa: b0 91 27 01 lds r27, 0x0127
9
fe: cd 01 movw r24, r26
10
100: bb 27 eor r27, r27
11
102: 97 fd sbrc r25, 7
12
104: b0 95 com r27
13
106: ab 2f mov r26, r27
14
108: 90 93 23 01 sts 0x0123, r25
15
10c: 80 93 22 01 sts 0x0122, r24
Jetzt mit unsigned long:
1
unsigned long In;
2
uint16_t x;
3
4
x = (uint16_t) (In>>16);
5
ee: 80 91 24 01 lds r24, 0x0124
6
f2: 90 91 25 01 lds r25, 0x0125
7
f6: a0 91 26 01 lds r26, 0x0126
8
fa: b0 91 27 01 lds r27, 0x0127
9
fe: cd 01 movw r24, r26
10
100: aa 27 eor r26, r26
11
102: bb 27 eor r27, r27
12
104: 90 93 23 01 sts 0x0123, r25
13
108: 80 93 22 01 sts 0x0122, r24
Wie dir vielleicht auffällt, sieht der Code nicht nur anders, sondern
auch küzer aus. Auch Dein angemerktes MOV ist verschwunden.
Johann L. schrieb:> In neueren Compilerversionen taucht dieser Shift garnicht mehr auf, er> wird quasi implizit erledigt.
Das ist zwar ein nettes Feature, leider verleitet es den gemeinen
Programmierer, sich zukünftig noch dümmer anstellen zu dürfen.
Der Compiler wirds schon richten ;-)
Frank M. schrieb:> Sebastian Steppeler schrieb:>>> Aber wo siehst du hier ein shiften? Ich sprach doch von zwei mov>> operationen von denen die zweite die erste überflüssig macht.>> Warum hast Du es nicht einfach ausprobiert?> ...
Naja man kann halt nicht alles wissen, und ich muss gestehen mir war
nicht bewusst, dass die schiebeoperatoren in c sich um signed und
unsigned scheren. Daher meine verwunderung. Aja und dann hab ich den
shift nicht erkannt, weil ich vergas, dass ich ja um vielfache von 8
shifte. Manchmal ist man einfach ein bisschen blind und muss auf die
augen getreten werden ;)
Danke für die vielen umfangreichen antworten. Ich habe auf jedenfall was
gelernt :)
Sebastian Steppeler schrieb:> Naja man kann halt nicht alles wissen, und ich muss gestehen mir war> nicht bewusst, dass die schiebeoperatoren in c sich um signed und> unsigned scheren.
Ja, das ist so. Hier mal ein kleines Beispiel-Programm unter Linux:
1
#include<stdio.h>
2
3
int
4
main(void)
5
{
6
inti=0xFFFFFFFF;
7
unsignedintui=0xFFFFFFFF;
8
9
i>>=1;
10
ui>>=1;
11
12
printf("0x%08x 0x%08x\n",i,ui);
13
}
Ausgabe:
0xffffffff 0x7fffffff
Es kommt also ein komplett anderes Ergebnis heraus. Daher sollte man
sich prinzipiell angewöhnen, bei Shifts immer (bis auf ganz wenige
Ausnahmen) unsigned-Variablen zu verwenden. Sonst macht man sich u.U.
das ganze Ergebnis kaputt.
> Ich habe auf jedenfall was gelernt :)
Das geht mir jeden Tag so. C ist eine Sprache, bei der man nie auslernt
:-)
Falls es wirklich auf die Codegröße ankommt, bring eine Restrukturierung
des Codes wesentlich mehr als ein paar optisch unhübsche MOV
einzusparen.
Mit folgendem Code schrumpft die Größe auf rund 1/3; von ~420 auf ~150
Johann L. schrieb:> Falls es wirklich auf die Codegröße ankommt, bring eine Restrukturierung> des Codes wesentlich mehr als ein paar optisch unhübsche MOV> einzusparen.
Das war mir schon klar, dass der code als schleife kürzer ist. In der
Urversion wars auch eine schleife, aber hier kommt es auf
ausführungsgeschwindigkeit (jeder takt weniger zählt) an ;) daher hab
ihc mir die schleifen gespart.
Ihr müsst wissen die funktion stammt aus meinem sensorsystem fürs
klavier ( http://sebion.wordpress.com ) und da das ding 8 kanäle a 1,2
khz sample rate bewältigt muss zumindest dieser teil des codes schnell
gehen.
Sebastian Steppeler schrieb:> hier kommt es auf ausführungsgeschwindigkeit> (jeder takt weniger zählt) an
Und warum wird dann auf Codegröße optimiert?
Frank M. schrieb:> Johann L. schrieb:>> In neueren Compilerversionen taucht dieser Shift garnicht mehr auf, er>> wird quasi implizit erledigt.>> Das ist zwar ein nettes Feature, leider verleitet es den gemeinen> Programmierer, sich zukünftig noch dümmer anstellen zu dürfen.>> Der Compiler wirds schon richten ;-)
Du meinst also, daß ein Compiler nicht gut optimieren sollte?
Quasi als erzieherische Maßnahme.
Johann L. schrieb:> Du meinst also, daß ein Compiler nicht gut optimieren sollte?
Doch, schon. Aber ich finde schon, dass auch der Programmierer noch
"mitdenken" sollte. Der Mensch verliert sonst den Blick dafür, selbst
den Code zu optimieren - er wird schlampig.
Ich will es mal überspitzt darstellen: Heraus kommen dann zum Beispiel
im PC-Bereich Programme, die (mittlerweile) mehrere GB an RAM verbraten,
obwohl es bei "kluger" Programmiererei gar nicht notwendig wäre.
Johann L. schrieb:> Sebastian Steppeler schrieb:>>> hier kommt es auf ausführungsgeschwindigkeit>> (jeder takt weniger zählt) an>> Und warum wird dann auf Codegröße optimiert?
Guter Einwand. Ich habe den schalter damals aus mehreren gründen auf
diesem wert belassen: Erstens hab ich gelesen, dass diese einstellung
empfohlen wird, da die anderen optimierungsstufen bei minimalem
performancevorteil signifikant mehr code erzeugen, was bei mir auch
stimmte, da der avr zu 150 % voll ist, wenn ich maximal auf
geschwindigkeit -O3 optimiere und zweitens wusste ich damals nicht, dass
ich die optimierung für einzelne quellcodedateien unterschiedlich setzen
kann. Des weiteren habe ich den zeitkritischen code natürlich versucht
möglichst effizient zu schreiben und ich denke so das meiste
optimierungspotential schon ausgeschöpft.
Im moment fahre ich ganz gut mit der einstellung, sollte es nochmal eng
werden(leider kann es das sowohl in der ausführungszeit als auch in der
codegröße passieren, und ich versuche zu vermeiden einen größeren
controller zu brauchen) hab ich ja immer noch einen optimierungsschalter
den ich hochschalten kann.
Sebastian Steppeler schrieb:> Ihr müsst wissen die funktion stammt aus meinem sensorsystem fürs> klavier ( http://sebion.wordpress.com ) und da das ding 8 kanäle a 1,2> khz sample rate bewältigt
das sind 9.6kHz. Sollte noch gut bewältigbar sein.
> muss zumindest dieser teil des codes schnell> gehen.
Was genau ist die Aufgabe dieses Codes?
(long ist nicht unbedingt ein angenehmer Zeitgenosse für deinen AVR. Und
warum du hier unbedingt ein Vorzeichen brauchst, ist so auch nicht
wirklich ersichtlich)
Karl Heinz Buchegger schrieb:> Sebastian Steppeler schrieb:>>> Ihr müsst wissen die funktion stammt aus meinem sensorsystem fürs>> klavier ( http://sebion.wordpress.com ) und da das ding 8 kanäle a 1,2>> khz sample rate bewältigt>> das sind 9.6kHz. Sollte noch gut bewältigbar sein.
Sagen wir es ist noch unter der schmerzgrenze. Immerhin läuft darauf
eine kleine physikengine ;)
>> muss zumindest dieser teil des codes schnell>> gehen.>> Was genau ist die Aufgabe dieses Codes?>> (long ist nicht unbedingt ein angenehmer Zeitgenosse für deinen AVR. Und> warum du hier unbedingt ein Vorzeichen brauchst, ist so auch nicht> wirklich ersichtlich)
Dieser code rechnet energiewerte in midi werte um und die Energie kann
in diesem fall auch negativ werden, was aber bei der umrechnung keine
rolle mehr spielt, da bei negativen werten die berechnung abgebrochen
wird. ich brauch leider die 32 bit um keine numerischen ungenauigkeiten
zu riskieren. Aber keine sorge ich weiß was ich tue und der code
funktioniert ja auch prima, es muss nur noch das eine oder andere
feature ergänzt werden.
Johann L. schrieb:> Sebastian Steppeler schrieb:>>> Habe gerade mal den gcc4.5.1 gegengetestet, [...] aber wie gesagt>> der code ist 10 % größer>> Der neueste Win-Build ist vermutlich da:> Beitrag "Re: neue Windows-AVR-Toolchain für Atmega, Atxmega">> Was mich interessiert ist, welche Codegrößen du mit den Compilern> verschiedenen Compilerversionent (wohl 4.3, 4.5 und 4.7) erreicht hast.>> Bei der 4.7 helfen u.U auch die Optionen von>> http://sourceforge.net/apps/mediawiki/mobilechessboar/index.php?>title=Avr-gcc
Also wie gesagt der 4.7.0 hatte das beste resultat, darum bin ich jetzt
auch mal pauschal auf den umgestiegen.
Die codegrößen schauen bei meinem projekt so aus:
4.3.2: 12392 bytes
4.3.3: 12388 bytes
4.5.1: 13538 bytes (avrstudio 5 version)
4.7.0: 11710 bytes
Sebastian Steppeler schrieb:> Also wie gesagt der 4.7.0 hatte das beste resultat, darum bin ich jetzt> auch mal pauschal auf den umgestiegen.
Wie kann man im AVR-Studio 4 "pauschal" darauf umsteigen? Ich habe es
momentan nur geschafft, indem ich unter
Project -> Configuration Options -> Custom Option
das Häkchen "Use WinAVR" gelöscht habe und die Pfade auf den 4.7er
avr-gcc bzw. make manuell eingetragen habe.
Aber das ist ja projektspezifisch. Wie kann man das im AVR Studio 4
pauschal umstellen, so dass es für alle Projekte gilt?
Entschuldigung so habe ich das wort pauschal nicht gemeint ;)
Ich meinte bloß dass ich mal davon ausgehe, dass die neuste version mir
keine zusätlichen fehler machen wird und ich darum zumindest für dieses
projekt darauf umsteige.
Ich benutzte avrstudio 5 und da kann man das in der tat generell für
alle projekte einstellen und für jedes einzelne projekt ausnahmen
konfigurieren.
Bei avrstudio 4 hab ich keine ahnung, da ich es seit geraumer zeit nicht
mehr nutze, aber so weit ich mich erinnere musste man da doch winavr
separat runterladen und in avrstudio dann sagen wo sich winavr befindet?
oder machte man das für jedes projekt einzeln? sorry ist schon zu lang
her.
Sebastian Steppeler schrieb:> Ich benutzte avrstudio 5 und da kann man das in der tat generell für> alle projekte einstellen und für jedes einzelne projekt ausnahmen> konfigurieren.
Ich habe es jetzt so gelöst, dass ich die Toolchain nach Backup des
WinAVR20100110-Verzeichnisses einfach dorthinein kopiert habe und damit
alle älteren Dateien überschrieben habe. So brauche ich keine
Umstellungen zu machen.
> Bei avrstudio 4 hab ich keine ahnung, da ich es seit geraumer zeit nicht> mehr nutze, aber so weit ich mich erinnere musste man da doch winavr> separat runterladen und in avrstudio dann sagen wo sich winavr befindet?> oder machte man das für jedes projekt einzeln? sorry ist schon zu lang> her.
Man muss wohl WinAVR zuerst installieren und erst dann AvrStudio. Das
Ding findet dann wohl automatisch WinAVR. Ist bei mir aber auch zu lang
her. Ich habe jedenfalls nichts gefunden, um das nachträglich noch
umzustellen. Änderung von PATH reichte jedenfalls nicht.
Gruß und Dank,
Frank
mal ne dumme frage, gibts nen grund warum du noch an avrstudio4 hängst?
die 5er version ist doch viel komfortabler. Das einzige was mir nen
bisschen fehlt sind die fenster, die einem die register zeigen, muss man
deren namen nicht immer auswendig wissen ;)
Sebastian Steppeler schrieb:> mal ne dumme frage, gibts nen grund warum du noch an avrstudio4 hängst?
Ich mag kleine, aber feine Lösungen :-)
Ich habe hier desöfteren in der Vergangenheit gelesen, dass AvrStudio 5
ein riesengroßes Monster ist. Die Größe von über 600 MB des
Full-Installers hat mich da schon ein wenig abgeschreckt. Ausserdem
liebe ich es, wenn Programme schnell starten. Da ist zum Beispiel ein
Riesen-Unterschied zwischen MS Visual C++ Express 2008 und 2010. Ich
habe beide installiert und wähle meist die 2008er. Die ist schneller
gestartet ;-)
> die 5er version ist doch viel komfortabler.
Alles, was ich brauche, ist ein Editor und F7 zum Compilieren ;-)
Ausserdem denke ich mir, dass wenn ich jemandem meine Projekte anbiete
(z.B. IRMP), es für ihn leichter ist, wenn er 4er Projekt-Dateien
bekommt. Im Zweifel kann man diese mit beiden Programmversionen laden -
umgekehrt geht das bestimmt nicht mehr.
> Das einzige was mir nen> bisschen fehlt sind die fenster, die einem die register zeigen, muss man> deren namen nicht immer auswendig wissen ;)
Das Fenster hat mich noch nie interessiert, das ist bei mir
standardmäßig zu.
Aber Scherz beiseite: Ich habe mir heute morgen mal die 5.1er Version
von AvrStudio heruntergeladen und werde das mal am Wochenende
ausprobieren.
Frank M. schrieb:> Ausserdem denke ich mir, dass wenn ich jemandem meine Projekte anbiete> (z.B. IRMP), es für ihn leichter ist, wenn er 4er Projekt-Dateien> bekommt. Im Zweifel kann man diese mit beiden Programmversionen laden -> umgekehrt geht das bestimmt nicht mehr.
Das ist 4er oder 5er die Wahl zwischen Pech und Schwefel ;-)
Wenn du keine künstlichen Hürden durch Erfordernisse an die
Build-Umgebung aufbauen willst, dann liefere Makefiles mit aus. Punkt.
Sebastian Steppeler schrieb:> Die codegrößen schauen bei meinem projekt so aus:> 4.3.2: 12392 bytes> 4.3.3: 12388 bytes> 4.5.1: 13538 bytes (avrstudio 5 version)> 4.7.0: 11710 bytes
Danke für die Werte.
Das würde bedeuten, daß 4.5 um über 15% größeren Code generiert als 4.7.
Irgendwie kann ich garnicht glauben, daß der sooo schlecht ist.
...andererseits war die ständig mieser werdende Codequalität von avr-gcc
4.x der Grund für mich, mal ins Getümmel der GCC-Entwicklung
reinzuschnuppern.
Jörg Wunsch schrieb:> Das [der Compiler] ist doch kein Mensch, sondern der arbeitet> formale Algorithmen ab.
Was zur Frage führt, was denn ein Mensch "abarbeitet", wenn er ein
(Assembler-)Programm schreibt ;-)
Optimierende Compiler suchen ihr Glück ja darin, den Code quasi zu
vaporisieren und in hunderten von Passes die Ursuppe neu zu arrangieren
um schliesslich das Compilat zu erhalten.
Ein Compiler arbeitet also quasi auf der Ebene von Elekronen und Quarks,
wo Menschen auf der Ebene von Molekülen, Kristallen oder noch
komplexeren Strukturen denken.
Wenn es zum Beisipel darum geht, Instruktionen geschickt zu kombinieren
um bestimmte Aktionen darzustellen, dürften Menschen einem Compiler
haushoch überlegen sein — insbesondere dann, wenn der Instruktionssatz
nicht-orthogonal ist oder viele schwer zu beschreibende Instruktionen
beinhaltet, die ein Compiler nur schwerlich (optimal) einsetzen kann.
Typische Beispiele sind Bitgefummel oder geschickte Kombination
arithmetischer Befehle, etwa bei Divisionsroutinen.
Zumindest solange nicht zu viele Instruktionen dazu notwendig sind. In
diesem Fall würde ein Mensch das Problem in kleinere Einheiten
aufteilen, diese nahezu optimal lösen, und die Einzellösungen dann
puzzleartig zusammensetzen; wobei der Hauptaufwand/Performanceverlust
beim Zusammenkitten liegt, d.h. bei Registergeschubse oder anderwärtiger
Resourcenverwaltung.
Ein Compiler rührt jedoch einfach nur im Codebrei seiner Ursuppe, mit
lediglich ungefähren Anhaltspunkten und Heuristiken ob die Aktionen in
einem Pass denn nun günstig sind oder nicht. Liegt die Heuristik
daneben, liegt das Kind im Brunnen und bleibt auch da liegen, weil
nachfolgende Passes eine ungünstige Transformation oftmals nicht
rüchgängig machen können.
Der Multi-Pass Ansatz entspricht also in etwa einem Nebelspaziergang,
bei dem man sich in einer nur schemenhaft erkannbaren Umgebung bewegt
und ein besonders interessantes Ziel anzusteuern hat.
Und sowas wie spekulative Compilierung mit Verfolgung unterschiedlicher
Pfade und Auswählen des besten gibt es auch nicht. Einfach deshalb, weil
niemand stundenlang warten will, bis der Compiler endlich das stolze
Resultat präsentiert nachdem er tausende von unterschiedlichen
Kombinationsmöglichkeiten durchexerziert und wieder verworfen hat. Bei
Schachcomputern, die auf Weltklasseniveau spielen, ist man lange
Antwortzeiten gewohnt — aber sowas bei einem Compiler? Und dann auch
noch bei jedem Modul? Nur, um nach der nächsten Quelländerung wieder
alles durchnudeln zu lassen und das verherige Compilat Makulatur ist?
Ob bei Compilerbau/-theorie anderer Ansätz wie menschenähnlichere
Problemlösungsstrategien verfolg oder überhaupt Gegenstand der Forschung
sind, weiß ich nicht. Die Forschung scheint sich momentan eher in
Richtung formaler Verifizierbarkeit und neuer
Transformationen/Darstellungen für das Vaporisier-Modell zu bewegen.
Sebastian
würdest Du bitte noch die Compiler- und Linkoptionen nennen, bei denen
Du diese Ergebnisse erhalten hast ?
Sebastian Steppeler schrieb:> Also wie gesagt der 4.7.0 hatte das beste resultat, darum bin ich jetzt> auch mal pauschal auf den umgestiegen.>> Die codegrößen schauen bei meinem projekt so aus:> 4.3.2: 12392 bytes> 4.3.3: 12388 bytes> 4.5.1: 13538 bytes (avrstudio 5 version)> 4.7.0: 11710 bytes
ich verwende avr-gcc 4.3.5 und den avr-gcc gcc-4.6.3 alternativ unter
ubuntu.
Mit diesen Einstellungen/ Optionen verändere/ optimiere ich meine
Projekte:
Uwe S. schrieb:> Sebastian>> würdest Du bitte noch die Compiler- und Linkoptionen nennen, bei denen> Du diese Ergebnisse erhalten hast ?>
hatte für alle versionen das hier eingestellt:
compiler: -funsigned-char -funsigned-bitfields -DF_CPU=16000000UL -Os
-ffunction-sections -fpack-struct -fshort-enums -Wall -c -gdwarf-2
-std=gnu99 -fdata-sections -mmcu=atmega16
linker: -Wl,-lm -Wl,--gc-sections -mmcu=atmega16
habe dann mal noch -fno-split-wide-types -mcall-prologues
und -Wl,--relax hinzugefügt und kam auf 11156 mit dem gcc 4.7.0
was hat es eigentlich mit dem tinystack flag auf sich? da steht im
manual dass nur das LSbyte vom stackpointer geändert wird, aber ich
dachte immer der stackpointer wird von push und pop automatisch 16 bit
lang geändert?!?
kann ich mir gut vorstellen, aber was tut sie denn? bei mir verkleinert
sie den code um ein paar bytes, aber ich lasse vorsichtshalber mal die
finger davon.
Sebastian Steppeler schrieb:> aber was tut sie denn?
Dann, wenn der Stackpointer zu manipulieren ist (für das Einrichten
eines Stackframes) nur SPL statt SPL + SPH modifizieren.
Für die Controller, bei denen es nur SPL gibt, sollte das der
Compiler inzwischen automatisch ordentlich machen, und für alle
anderen ist die Option einfach schlicht nur gefährlich.
Ähnlich unsinnig und gefährlich ist -mshort-calls. Dummerweise hat
irgendjemand bei AVR Studio 5 dafür noch eine Checkbox mit eingebaut.
:-(
Sebastian Steppeler schrieb:> hatte für alle versionen das hier eingestellt:>> -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
Diese Optionen verändern das ABI (Binary Interface) des Codes. Wenn sich
das positiv auf Codegröße auswirkt dann ist das ein angenehmer
Seiteneffekt, zu den Optimierungsoptionen sind diese Schalter aber nicht
zu zählen.
> -Os
Unter den genannten Optionen die einzige, die wirklich was an den
verwendeten Optimierungsalgorithmen dreht. Ein Link auf weitere Optionen
hatte ich oben schon genannt.
> -ffunction-sections -fdata-sections
-fdata-sections ist in neueren avr-gcc zweischneidig. Man kann sich
entscheiden zwischen -fdata-sections und Constant Merging. Konstanten in
unterschiedlichen Sections können logischerweise nicht gemergt werden.
> was hat es eigentlich mit dem tinystack flag auf sich?
Das wird eigentlich nur compilerintern verwendet um die Erzeugung der
Multilibs zu steuern und den jeweiligen Multilib-Default für einen
bestimmten µC auszuwählen, nämlich abhängig davon, ob der SP des µC 8
oder 16 Bits groß ist.
Zwar kann man das Flag auch von Hand setzen und einen 16-Bit SP so
behandeln lassen als hätte er nur 8 Bit, d.h. SPH wird als 0 angenommen,
aber davon würd ich wie oben bereits gesagt abraten. Zum Beipiel kümmert
sich der Startup-Code nicht um dieses Flag, initialisiert also SPH für
16-Bit µC mit einem Wert ≠ 0.
Der GCC mach noch andere unsinnige Sachen.
Ein Port zugriff wird erst an einer höheren Optimierung zu einem
Befehl.
Vorher holt er das Port Register, macht die Operation und schreibt dann
das Register wieder zurück.
Geil ist das wenn man im Interrupt auf den selben Port zugreift.
Da kannst Du dich dumm und dusselig suchen.
Peter schrieb:> Ein Port zugriff wird erst an einer höheren Optimierung zu einem> Befehl.> Vorher holt er das Port Register, macht die Operation und schreibt dann> das Register wieder zurück.>> Geil ist das wenn man im Interrupt auf den selben Port zugreift.> Da kannst Du dich dumm und dusselig suchen.
Du meinst sowas?
1
PORTD|=1;
Bitte zeige, wo im Sprachstandard zugesichert wird, daß dies in eine
einzige Maschineninstruktion zu übersetzen ist!
Daß avr-gcc das bei höheren Optimierungen macht ist ein Bonbon, mehr
nicht. Wenn Code davon ausgeht, daß dies immer der Fall ist, ist der
Code schlicht und einfach nicht robust denn er macht Annahmen, die
nirgends spezifiziert sind!
> Bitte zeige, wo im Sprachstandard zugesichert wird, daß dies in eine> einzige Maschineninstruktion zu übersetzen ist!> ...
Mh das ist interessant, vielleicht sollte man das mal ins avrgcc
tutorial aufnehmen, dass man nicht davon ausgehen kann, das portzugriffe
atomar sind. Würde ich als anfänger nämlich nie auf die idee kommen da
nen fehler zu suchen...
Peter schrieb:> Ein Port zugriff wird erst an einer höheren Optimierung zu einem> Befehl.
Bis auf die Tinys/Megas der ersten Generation (wie Mega8/16/32) sind die
AVRs mittlerweile mit einer XOR-Funktion auf die Ports ausgestattet,
indem nach PINx geschrieben wird. Das ermöglicht unabhängig von
Portadresse und Optimierungsgrad atomare Pinoperationen.
Wenn man im Interrupt zwar den gleichen Port aber nicht die gleichen
Pins anspricht kann man im Hauptprogramm
PORTA &= ~mask;
PORTA |= mask;
durch
PINA = PORTA & mask; // vgl PORTA ^= PORTA & mask
PINA = ~PORTA & mask; // vgl PORTA ^= ~PORTA & mask
ersetzen. Alle Pins ausserhalb der Maske bleiben unbeeinflusst, auch
wenn ein Interrupt zwischenrein funkt. Das funktioniert auch dann wenn
"mask" keine Konstante ist.
Die Ersatzsequenz rechts ist hingegen nicht atomar.
Johann L. schrieb:>> -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums>> Diese Optionen verändern das ABI (Binary Interface) des Codes.
-fpack-struct ist ein no-op, der sich hartnäckig in allerlei
Makefiles hält.
Da der AVR keinerlei memory alignment constraints kennt, werden
struct tags immer auf Bytegrenzen ausgerichtet.
-funsigned-char und -funsigned-bitfields vertuschen bestenfalls
Fehler in schlampig programmiertem Code. Wer es im dritten Jahr-
tausend immer noch nicht schafft, "char", "signed char" und
"unsigned char" sauber auseinander zu halten, dem gehört auf die
Finger geklopft. Wer zu faul ist, seine bitfields als unsigned
zu deklarieren, wenn sie vorzeichenlos sein sollen, dem ist kaum
noch zu helfen.
-fshort-enums löst man besser durch ein _attribute_ am jeweiligen
enum. Wenn man das projektweit (bspw. über eine Headerdatei)
konsitent durchzieht, dann ist man wenigstens nicht mehr davon
abhängig, dass alle Objektdateien des Projekts (auch bspw. die, die
aus Bibliotheken hinzu kommen) gleichermaßen mit dieser Option
compiliert worden waren.
Sebastian Steppeler schrieb:> Mh das ist interessant, vielleicht sollte man das mal ins avrgcc> tutorial aufnehmen, dass man nicht davon ausgehen kann, das portzugriffe> atomar sind. Würde ich als anfänger nämlich nie auf die idee kommen da> nen fehler zu suchen...
Das steht wie gesagt in keinem Sprachstandard (und auch in keiner
ABI/EABI).
Ich würde mal davon ausgehen, daß das avr-gcc Tutorial nicht die
Intention hat, ein komplettes C Tutorial zu sein. Dann bräuchte man zig
C-Tutorials: Eines im avr-gcc, eines im arm-gcc, eines im avr32-gcc,
eines im...
Das avr-gcc Tutorial kann sich darauf beschränken zu erklären, was über
den Standard hinaus geht oder implementation defined ist wie:
• Wie groß ist ein short, long, ...?
• Wie groß sind Zeiger?
• Wie Definiert man eine ISR?
• Wie greift man auf SFRs zu?
• Wie macht man einen Codeausschnitt atomar?
Das war's auch schon. Wobei die letzten beiden Punkte lediglich
Anwendung von AVR-Libc Makros bedeutet und absolut nichts speziell mit
avr-gcc zu tun haben.
Dinge wie Timer-Benutzung, ADC-Verwendung, etc. sind auch nicht avr-gcc
spezifisch, Das einzige was man dazu braucht, ist:
• Wissen, wie man auf SFRs zugreift
• Wissen, wie man I/O-Module bedienent, d.h. Handbuch lesen
Ob die SFRs nun in Atmel-Assembler, in GNU-Assembler, in C mit avr-gcc,
in C mit Imagecraft, in C mit IAR, ... bedient werden ist doch sowas von
Wurscht. Die Vorgehensweise ist immer gleich, und ich verstehe nicht,
warum das x-mal an unterschiedlichen Stellen immer und immer wieder
durchgekaut wird.
Stattdessen wäre es viel sinnvoller, Tutorials für einzelne Module wie
ADC oder Timer zu haben, die unabhängig von einer bestimmten Sprache
sind und vielmehr das Modul und seine Bedienung und Möglichkeiten
erklären als auf Geklimper mit einzelen Kommandos abzufahren, die eine
klare Sicht eher verstellen als befördern.
Wer nicht in der Lage ist, diese Transferleistung zu erbringen, wie ein
SFR nun in einer konkreten Sprache zu bedienen ist, ist einfach noch
nicht bereit für die Aufgabe, finde ich.
Johann L. schrieb:> Das avr-gcc Tutorial kann sich darauf beschränken zu erklären, was über> den Standard hinaus geht oder implementation defined ist wie: ...
Im Prinzip könnte es das, tut es aber gottseidank nicht wirklich, denn
dann hätte ich daraus nicht so viel gelernt und mir vieles erstmal
zusammensuchen müssen. Als ich mit der avr programmiererei begonnen habe
hatte ich nämlich von interrupts und atomaren zugriffen garkeinen
schimmer (hatte davor immer nur windows/directx/opengl anwendungen
geschrieben und musste mich mit multithreading oder interrupts nie
wirklich auseinandersetzen). Auch das berühmte "volatile" war mir bis
dato immer völlig schleierhaft gewesen. Aber gerade soetwas wie
interrupt handling ist doch gerade im avr bereich ein ganz wichtiges
thema, was wie ich finde in dem tutorial sehr gut behandelt wird. Für
anfänger sicher unverzichtbar.
Ob das thema mit dem atomaren portzugriff da reingehört, da kann man
sicher drüber streiten.
Johann L. schrieb:> Stattdessen wäre es viel sinnvoller, Tutorials für einzelne Module wie> ADC oder Timer zu haben, die unabhängig von einer bestimmten Sprache> sind und vielmehr das Modul und seine Bedienung und Möglichkeiten> erklären als auf Geklimper mit einzelen Kommandos abzufahren, die eine> klare Sicht eher verstellen als befördern.
Da gebe ich dir prinzipiell recht, aber letzlich tut das avrgcc tutorial
das doch fast. Man muss sich die codebeispiele doch nur wegdenken und
wenn man nur eine beschreibung der funktionseinheit und deren
grundsätzliche bedienung haben will, dann liefert das datenblatt gerade
bei atmels avrs das doch recht gut.
Tutorial hin oder her.
Fakt ist das jeder andere Compiler erkennt das ein Portzugriff gemacht
werden soll und gleich den entsprechenden Atomaren Befehl dafür nimmt.
Wenn man nun vom IAR her kommt und auf den GCC umsteigt, kann es zu
Problemen kommen und man versteht erst mal nicht warum, denn der Code
hatte ja mit dem IAR funktioniert.
Was ist nun das richtige (habe gerade keinen GCC zur Hand)?
PORTA &= ~mask;
oder
PORTA = PORTA & mask; // PINA kann ich nichts zuweisen
oder
PORTA ^= PORTA & mask
Peter schrieb:> Fakt ist das jeder andere Compiler erkennt das ein Portzugriff gemacht> werden soll und gleich den entsprechenden Atomaren Befehl dafür nimmt.
Aber auch nur dann, wenn der Port bitadressierbar ist. Was bei AVRs in
grösseren Gehäusen nicht unbedingt auf alle Ports zutrifft. Migration
von einem Port zu einem anderen kann also ebenfalls überraschen.
Peter schrieb:> Tutorial hin oder her.>> Fakt ist das jeder andere Compiler erkennt das ein Portzugriff gemacht> werden soll und gleich den entsprechenden Atomaren Befehl dafür nimmt.
Das macht avr-gcc ja auch.
Wenn du aber -O0 setzt und damit explizit anforderst, GCC im
Erbsenzähl-Modus zu betreiben, dann bekommst du das auch. Konkret: Ein
PORTB |= 1;
sind 3 Operationen: Ein Laden, ein OR und ein Schreiben.
> Wenn man nun vom IAR her kommt und auf den GCC umsteigt, kann es zu> Problemen kommen und man versteht erst mal nicht warum, denn der Code> hatte ja mit dem IAR funktioniert.
Das hat wie gesagt weniger mit dem Verständnis des Compilers zu tun,
sondern mit dem Verständnis der Sprache und welche (nicht erfüllten)
Implikationen man daraus ableitet.
Daraus, daß "etwas funktioniert", lässt sich nichts über die Robustheit
von Code ableiten. Siehe die schlottrige (Nicht-)Verwengung von
volatile, Nicht-Beachtung von Strict Aliasing, etc. in altem Code, was
dann in neueren Compilerversionen oder mit Optimierung zu Problemen
führt.
Gerne wird dann pauschal über "Compilerfehler" schwadroniert.
Johann L. schrieb:> Daraus, daß "etwas funktioniert", lässt sich nichts über die Robustheit> von Code ableiten. Siehe die schlottrige (Nicht-)Verwengung von> volatile, Nicht-Beachtung von Strict Aliasing, etc. in altem Code
In neuem auch... Ich hatte dazu mal einen wenig ergiebigen kurzen Disput
mit einem Spezi von Coocox, deren RTOS Source Code völlig ohne
"volatile" auskam (Barriers fand ich auch nicht). Mir schien das sehr
nach dem Motto "wieso, es funktioniert doch" gestrickt zu sein. Die
Antwort auch.
Nun so pauschal würde ich dann nicht "geht doch" sagen.
An die Regeln der Programmiersprache muss ich mich schon halten.
Es ist halt nur schade das alle AVR Compiler es vernünftig umsetzen, nur
der GCC erst ab einer höheren Optimierung.
Das es AVRs gibt bei denen das nicht bei jedem Port geht war mir nicht
bekannt, der grösse bei mit ist der ATMega644.
Das man generell aufpassen muss was man wo macht sollte wohl klar sein.
Ob man nun im Interrupt auf Variablen zugreift oder IOS da kann was
schief laufen.
Das mit dem Port zugriff war mir halt nur mal unangenehm aufgefallen,
weil ein Timing nicht mehr passte nachdem ich den Compiler gewechselt
hatte.
Da waren auch keine Interrupts aktiv da ich nur Daten über Pins raus
geschoben hatte.
Peter schrieb:> Nun so pauschal würde ich dann nicht "geht doch" sagen.> An die Regeln der Programmiersprache muss ich mich schon halten.
Und wo steht da, also in der Sprachdefinition/spezifikation, daß diese
oder jene Operation atomar oder durch bestimmte Maschinenbefehle
abzubilden ist? Oder in welchem (E)ABI?
Und in welcher Spezifikation/(E)ABI steht, daß ein C-Befehl innerhalb
einer bestimmten Zeit/Tickanzahl abgehandelt werden muss?
Peter schrieb:> Es ist halt nur schade das alle AVR Compiler es vernünftig umsetzen, nur> der GCC erst ab einer höheren Optimierung.
Das hängt sehr stark von deiner Vorstellung von "vernünftig" ab. Wer
unbedingt Code ohne Optimierung nutzen muß, dabei aber trotzdem auf
"vernünftige" Optimierungen nicht verzichten kann, der muß gcc ja nicht
nutzen. Dein "alle" impliziert ja,daß du da beliebeig viele Alternativen
zu Wahl hast. gcc ist ein C-Compiler, kein Mikrocontroller-Compiler.
Oliver
A. K. schrieb:> Bis auf die Tinys/Megas der ersten Generation (wie Mega8/16/32) sind die> AVRs mittlerweile mit einer XOR-Funktion auf die Ports ausgestattet,> indem nach PINx geschrieben wird. Das ermöglicht unabhängig von> Portadresse und Optimierungsgrad atomare Pinoperationen.A. K. schrieb:> Peter schrieb:>>> // PINA kann ich nichts zuweisen>> Bei einem ATtiny2313 oder ATmega88 kannst du das sehr wohl.
Funktionieren tut es zwar, im Datenblatt ist PINx aber immer noch als
nicht beschreibbar angegeben. Funktionsgarantie ist also nicht
vorhanden.
vn nn schrieb:> Funktionieren tut es zwar, im Datenblatt ist PINx aber immer noch als> nicht beschreibbar angegeben.
Weil das PINx-Register selbst natürlich nicht beschreibbar ist;
das bildet ja den aktuellen Zustand der Eingänge ab.
> Funktionsgarantie ist also nicht> vorhanden.
Doch. Die XOR-Funktion ist an anderer Stelle dokumentiert, ihre
Funktion ist damit sehr wohl garantiert. Wenn sie es nicht wäre,
hätte man das Feature ja gar nicht erst einbauen müssen. Ein "kann
funktionieren, muss aber nicht" hülfe ja keinem Anwender.
Jörg Wunsch schrieb:> Doch. Die XOR-Funktion ist an anderer Stelle dokumentiert, ihre> Funktion ist damit sehr wohl garantiert. Wenn sie es nicht wäre,> hätte man das Feature ja gar nicht erst einbauen müssen. Ein "kann> funktionieren, muss aber nicht" hülfe ja keinem Anwender.
Wo konkret? Habe schon mehrmals danach gesucht, und dass in der Register
description von PINx steht, dass dieses lediglich gelesen werden kann,
wäre da alles andere als sinnvoll.
Jörg Wunsch schrieb:> Weil das PINx-Register selbst natürlich nicht beschreibbar ist;> das bildet ja den aktuellen Zustand der Eingänge ab.
Natürlich muss es beschreibbar sein. Dass der Schreibzugriff umgeleitet
wird, ändert ja nichts daran, dass ich was reinschreibe.
Edit: Tatsächlich steht es z.B. im Datenblatt des ATTINY2313, dort sind
auch die Register dementsprechend als beschreibbar gekennzeichnet. Bei
den anderen oben genannten Controllern ist dies allerdings nach wie vor
meines Erachtens nicht der Fall, darauf zu verweisen, dass dies bei
allen neueren möglich wäre also nur bedingt zu empfehlen.
vn nn schrieb:> Bei> den anderen oben genannten Controllern ist dies allerdings nach wie vor> meines Erachtens nicht der Fall, darauf zu verweisen, dass dies bei> allen neueren möglich wäre also nur bedingt zu empfehlen.
Datenblatt ATmega48/88/168 (die alten, ohne "P"):
1
12.2.2 Toggling the Pin
2
3
Writing a logic one to PINxn toggles the value of PORTxn, independent
4
on the value of DDRxn. Note that the SBI instruction can be used to
Jörg Wunsch schrieb:> Datenblatt ATmega48/88/168 (die alten, ohne "P"):>> 12.2.2 Toggling the Pin>> Writing a logic one to PINxn toggles the value of PORTxn, independent> on the value of DDRxn. Note that the SBI instruction can be used to> toggle one single bit in a port.
Nett.
Das bedeutet also, daß auf diesen Registern SBI komplett anders reagiert
als IN/OR/OUT. Was wiederum bedeutet, daß der Compiler zum Beispiel
keinesfalls
SFR |= 1;
als
SBI SFR,0
übersetzen darf da sich beide signifikant unterscheiden.
Johann L. schrieb:> Das bedeutet also, daß auf diesen Registern SBI komplett anders reagiert> als IN/OR/OUT.
SBI hat einfach nur keinen Sinn auf einem PINx. Nein, es ist immer
noch (bis auf das Timing) Äquivalent zu IN/OR/OUT, aber auch das ist
dort sinnlos.
Ohne das eingebaute XOR-Feature würde halt die OUT-Phase komplett
ignoriert, mit dem Feature toggelt sie die PORTx-Pins, für die von
PINx eine 1 gelesen worden ist, zusätzlich das Pin, das hineine
verODERt worden war.
Bislang interessiert das den Compiler alles nicht. Prinzipiell
könnte der Compiler jedoch im Wissen darum (und bei Kenntnis der
Controllertypen, in denen es implementiert ist) ein
PORTX ^= Y;
umsetzen als
*(&PORTX - 2) = Y;
Jörg Wunsch schrieb:> Johann L. schrieb:>> Das bedeutet also, daß auf diesen Registern SBI komplett anders reagiert>> als IN/OR/OUT.>> SBI hat einfach nur keinen Sinn auf einem PINx. Nein, es ist immer> noch (bis auf das Timing) Äquivalent zu IN/OR/OUT, aber auch das ist> dort sinnlos.
IN/OR/OUT und SBI reagieren unterschiedlich, weil SBI nur ein Bit
toggelt, während IN tatsächlich vom PIN SFR liegt und i.d.R mehr Bits
gesetzt sind als im OR angegeben.
Johann L. schrieb:> IN/OR/OUT und SBI reagieren unterschiedlich, weil SBI nur ein Bit> toggelt
Nein. Auch SBI implementiert ein read-modify-write(*), nur eben nicht
unterbrechbar in einem Befehl. Wenn man das auf ein PINx-Register
anwendet, würde also der gleiche Unfug rauskommen, weil das Ergebnis
davon abhängt, welche Bits man vom Eingaberegister bereits gelesen
hat und welche nicht.
(*) Aus diesem Grunde steht auch die ausdrückliche Warnung in den
Datenblättern:
1
· Bit 4 — ADIF: ADC Interrupt Flag
2
3
...
4
Beware that if doing a Read-Modify-Write on ADCSRA, a pending
5
interrupt can be disabled. This also applies if the SBI and CBI
Jörg Wunsch schrieb:> Johann L. schrieb:>> IN/OR/OUT und SBI reagieren unterschiedlich, weil SBI nur ein Bit>> toggelt>> Nein. Auch SBI implementiert ein read-modify-write(*), nur eben nicht> unterbrechbar in einem Befehl. Wenn man das auf ein PINx-Register> anwendet, würde also der gleiche Unfug rauskommen, weil das Ergebnis> davon abhängt, welche Bits man vom Eingaberegister bereits gelesen> hat und welche nicht.
Jetzt bin ich total verwirrt, denn oben zitiertest du:
>> Note that the SBI instruction can be used to>> toggle one single bit in a port.
Jörg Wunsch schrieb:> Nein. Auch SBI implementiert ein read-modify-write(*), nur eben nicht> unterbrechbar in einem Befehl. Wenn man das auf ein PINx-Register> anwendet, würde also der gleiche Unfug rauskommen, weil das Ergebnis> davon abhängt, welche Bits man vom Eingaberegister bereits gelesen> hat und welche nicht.
Annahme: SBI PINA,0 entspricht der Sequenz
temp = PINA;
temp |= 1<<0;
PINA = temp;
wovon jedenfalls der Compiler ausgeht, da er alle Operationen
SFR |= bit;
im bitadressierbaren Bereich in
SBI SFR,bit
übersetzt.
Wenn er das bei
PINA |= 1<<0;
ebenfalls macht, dann führt dies zu einem anderen Ergebnis als
SBI PINA,0;
Im ersten Fall werden alle Pins getoggelt, die bei Lesen von PINA eine 1
lieferten, zzgl. Pin 0. Im zweiten Fall hingegen nur Pin 0.
Johann L. schrieb:> Jetzt bin ich total verwirrt, denn oben zitiertest du:>>>> Note that the SBI instruction can be used to>>> toggle one single bit in a port.
Stimmt natürlich, das würde dem widersprechen.
Ja, das ist verwirrend.