Hallo,
wenn ich folgenden Code compiliere (avr-gcc -mmcu atmega328p -O3)
1
#include<avr/io.h>
2
3
voiducsra_rw()
4
{
5
UCSR0A|=(1<<0);
6
}
7
8
voiducsra_rw_test()
9
{
10
uint8_ttmp=UCSR0A;
11
tmp|=(1<<0);
12
UCSR0A=tmp;
13
}
14
15
voiducsra_w()
16
{
17
UCSR0A=(1<<0);
18
}
19
20
voiducsra_r()
21
{
22
UCSR0A;
23
}
24
25
intmain()
26
{
27
return0;
28
}
generiert der gcc folgenden Assemblercode
1
00000080 <ucsra_rw>:
2
80: e0 ec ldi r30, 0xC0 ; 192
3
82: f0 e0 ldi r31, 0x00 ; 0
4
84: 80 81 ld r24, Z
5
86: 81 60 ori r24, 0x01 ; 1
6
88: 80 83 st Z, r24
7
8a: 08 95 ret
8
9
0000008c <ucsra_rw_test>:
10
8c: e0 ec ldi r30, 0xC0 ; 192
11
8e: f0 e0 ldi r31, 0x00 ; 0
12
90: 80 81 ld r24, Z
13
92: 81 60 ori r24, 0x01 ; 1
14
94: 80 83 st Z, r24
15
96: 08 95 ret
16
17
00000098 <ucsra_w>:
18
98: 81 e0 ldi r24, 0x01 ; 1
19
9a: 80 93 c0 00 sts 0x00C0, r24
20
9e: 08 95 ret
21
22
000000a0 <ucsra_r>:
23
a0: 80 91 c0 00 lds r24, 0x00C0
24
a4: 08 95 ret
Ich frage mich warum beim Read-Modify-Write das Z-Register benutzt wird.
Gibts es da einen Grund oder ist das ein kleiner Bug beim Optimieren?
Ich hätte gedacht, dass dies dabei rauskäme
1
lds r24, 0x00C0
2
ori r24, 0x01
3
sts 0x00C0, r24
4
ret
Die Wahl der Optimierung hat keinen Einfluss auf das Ergebnis. Es tritt
auch bei den andere USART Registern auf.
Uwe schrieb:> Ich hätte eine Frage, warum mit -O3 ?
Wie gesagt, bei allen Optimierungen (1,2,3,s) gibt es das gleiche
Ergebnis.
Ich benutze avr-gcc 4.7.0
A. K. schrieb:> Beide Varianten sind gleich lang und gleich schnell:>
1
> lds r24, 0x00C0
2
> ori r24, 0x01
3
> sts 0x00C0, r24
4
>
* 6 Bytes Flash
* 5 Takte
* 1 Register
>
1
> 80: e0 ec ldi r30, 0xC0 ; 192
2
> 82: f0 e0 ldi r31, 0x00 ; 0
3
> 84: 80 81 ld r24, Z
4
> 86: 81 60 ori r24, 0x01 ; 1
5
> 88: 80 83 st Z, r24
6
>
> Weshalb siehst du die zweite als nachteilig an?
* 10 Bytes Flash
* 7 Takte
* 3 Register
Das die Unterschiede in normalen Programmen egal sind ist mir klar.
Es wundert mich nur, dass die Optimierung hier den Umweg über das
Z-Register geht.
André Althaus schrieb:> * 6 Bytes Flash> * 5 Takte
Nein.
André Althaus schrieb:> * 10 Bytes Flash> * 7 Takte
Nein.
Wie A.K. schon sagte, beides ist gleich groß (10 Bytes) und gleich
schnell (5 Takte). Einzig der Unterschied in der Register-Nutzung
stimmt.
Stefan Ernst schrieb:> Wie A.K. schon sagte, beides ist gleich groß (10 Bytes) und gleich> schnell (5 Takte)
Nö.
Die beiden LDI bei Variante 2 hast Du vergessen, sind also 7 Takte.
LD/ST braucht wie LDS/STS 2 Takte.
Stefan Ernst schrieb:> LD Z nur einen Takt.
Dann sind aber alle AVR-Datenblätter falsch.
Nur bei den neuen ATtiny ist es 1 Takt (Xmega weiß ich nicht).
Stefan Ernst schrieb:> Wie A.K. schon sagte, beides ist gleich groß (10 Bytes) und gleich> schnell (5 Takte).
Auf auf einem Mega328p benötigt ein st und ein ld je zwei Takte, wie bei
allen anderen Megas auch. Die zweite Variante benötigt sieben Takte.
Oliver
Peter Dannegger schrieb:> Stefan Ernst schrieb:>> LD Z nur einen Takt.>> Dann sind aber alle AVR-Datenblätter falsch.> Nur bei den neuen ATtiny ist es 1 Takt (Xmega weiß ich nicht).
Hmm, ich hatte im Instruction-Set-Manual nachgeschaut. Dann ist es dort
falsch.
Stefan Ernst schrieb:> Hmm, ich hatte im Instruction-Set-Manual nachgeschaut. Dann ist es dort> falsch.
Genauer: In einer der beiden Dokus ist es falsch. Die Wetten sind m.E.
noch offen, in welcher.
Ich fand allerdings grad eben keinen (non-tiny) AVR, bei dem 1 Takt drin
steht. Auch nicht bei den ganz alten. Ich würde also nicht blid drauf
setzen, dass die Datasheets richtig liegen und die ISA_Ref falsch. Kann
auch andersrum sein, denn die Datasheets entstehen durch copy&paste,
Fehlerfortpflanzung inklusive.
Es sieht so aus, als ob LD Rn,Z nur bei den "Reduced Core tinyAVRs" in 1
Zyklus ausgeführt wird. So steht es jedenfalls in der Übersichtstabelle
auf Seite 13 des aktuellen Instruction-Set-Manuals (Rev. I). Bei den
Angaben auf Seite 93 wird nicht zwischen dem reduzierten und dem
gewöhnliche Core unterschieden, was IMHO ein Fehler ist. Im Datenblatt
des ATtiny4/5/9/10 ist die Zyklenzahl mit "1/2" angegeben. Die 2 bezieht
sich dabei auf den Zugriff auf den Programmspeicher. Für die
"gewöhnlichen" Tiny- und Mega-AVRs sind dann wohl 2 Zyklen richtig, so
wie es auch in den jeweiligen Dateblättern angegeben ist.
Die Anzahl der Zyklen sind für avr-gcc ziemlich wurscht. GCC erzeugt
i.W. Code, der den Boliden wie ARM oder x86 genehm ist; was die
maschinenunabhängigen Optimizer für Hänflinge wie AVR treiben
interessiert eigentlich keinen...
Yalu X. schrieb:> Für die> "gewöhnlichen" Tiny- und Mega-AVRs sind dann wohl 2 Zyklen richtig, so> wie es auch in den jeweiligen Dateblättern angegeben ist.
So dürfte es sein. Ganz genau weiß man es wohl erst, wenn man das mal
auf einem aktuellen Mega getestet hat.
Oliver
Stefan Ernst schrieb:> Wie A.K. schon sagte, beides ist gleich groß (10 Bytes) und gleich> schnell (5 Takte). Einzig der Unterschied in der Register-Nutzung> stimmt.
Stimmt, bei der Größe der LDS/STS Befehle hab ich falsch gedacht.
Ich hatte im Datenblatt geschaut und da stand 2 Zyklen drin, aber wie
oben schon geschrieben wurde, im Instruction Set steht 1 Zyklus.
Mir fällt grad kein Beispiel ein, wie ich den gcc dazu bringen könnte
das Z-Register für eine andere Operation in der gleichen Funktion zu
nutzen um zu schauen, ob der dann LDS/STS nutzt.
André Althaus schrieb:> Mir fällt grad kein Beispiel ein, wie ich den gcc dazu bringen könnte> das Z-Register für eine andere Operation in der gleichen Funktion zu> nutzen um zu schauen, ob der dann LDS/STS nutzt.
Das wird ihn nicht kümmern. Ich wette nen Keks.