Forum: Compiler & IDEs Optimierung bei USART Registerzugriff


von André A. (nummer5) Benutzerseite


Lesenswert?

Hallo,
wenn ich folgenden Code compiliere (avr-gcc -mmcu atmega328p -O3)
1
#include <avr/io.h>
2
3
void ucsra_rw()
4
{
5
    UCSR0A |= (1 << 0);
6
}
7
8
void ucsra_rw_test()
9
{
10
    uint8_t tmp = UCSR0A;
11
    tmp |= (1 << 0);
12
    UCSR0A = tmp;
13
}
14
15
void ucsra_w()
16
{
17
    UCSR0A = (1 << 0);
18
}
19
20
void ucsra_r()
21
{
22
    UCSR0A;
23
}
24
25
int main()
26
{
27
    return 0;
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.

von Uwe (Gast)


Lesenswert?

Ich hätte eine Frage, warum mit -O3 ?

von André A. (nummer5) Benutzerseite


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

Beide Varianten sind gleich lang und gleich schnell:
1
  lds r24, 0x00C0
2
  ori r24, 0x01
3
  sts 0x00C0, r24
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
Weshalb siehst du die zweite als nachteilig an?

von André A. (nummer5) Benutzerseite


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

Peter Dannegger schrieb:
> LD/ST braucht wie LDS/STS 2 Takte.

Nein. LD Z+ braucht zwei, LD Z nur einen Takt.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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).

von Oliver (Gast)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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...

von Oliver (Gast)


Lesenswert?

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

von André A. (nummer5) Benutzerseite


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.