Optimierung bei USART Registerzugriff

OP Persönliche Seite #3198770
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.
#3198824
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?
OP Persönliche Seite #3198916
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.
#3199116
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.
Moderator #3199202
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.
Gast #3199256
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
OP Persönliche Seite #3199510
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren