Hallo,
ich beschäftige mich gerade mit den ATxmegas dabei ist mir bei der neuen
API der AVRLibC eine schlechte Optimierung aufgefallen.
Beispiel:
Wird zu:
1 | LDI R24,0xAA Load immediate
| 2 | LDI R30,0xC0 Load immediate
| 3 | LDI R31,0x0A Load immediate
| 4 | STD Z+3,R24 Store indirect with displacement
|
Aber warum?
Viel Sinnvoller wäre doch:
1 | LDI R24,0xAA Load immediate
| 2 | STS 0x0AC3,R24 Store direct to data space
|
Die AVRLibC iox128a1.h sieht so aus:
1 | #define SPIE (*(SPI_t *) 0x0AC0) /* Serial Peripheral Interface E */
| 2 |
| 3 | typedef volatile uint8_t register8_t;
| 4 |
| 5 | /* Serial Peripheral Interface */
| 6 | typedef struct SPI_struct
| 7 | {
| 8 | register8_t CTRL; /* Control Register */
| 9 | register8_t INTCTRL; /* Interrupt Control Register */
| 10 | register8_t STATUS; /* Status Register */
| 11 | register8_t DATA; /* Data Register */
| 12 | } SPI_t;
|
Ich verstehe nicht warum der GCC nicht auf eine statische Adresse
Optimiert. Denn die Adresse ändert sich ja nicht sondern nur der Inhalt.
z.B. bei:
Macht der GCC es richtig und setzt dafür eine statische Adresse ein.
1 | LDI R25,0xBB Load immediate
| 2 | STS 0x0AC0,R25 Store direct to data space
|
Aber bei SPI.INTCTRL, SPI:STATUS ... nicht mehr.
Um das Problem zu lösen muss ich nun immer auf die "alte" API
zurückgreifen. Sprich: SPIE_DATA = 0xAA;
Ich verwende:
Atmel AVR Studio 5
Atmel AVR Toolchain 3.2.3.314
Windows
AVR 8-bits GNU Binutils 2.20.1
AVR 8-bits GNU Compiler Collection (avr-gcc) 4.5.1
AVRLibC 1.7.1
AVR 8-bits GNU Debugger (avr-gdb) 6.7.1
Gibt es dazu einen Patch o.ä.?
Zeig mal die gesamte Funktion und den erzeugten Code davon.
Julius F. schrieb:
> Um das Problem zu lösen muss ich nun immer auf die "alte" API
> zurückgreifen. Sprich: SPIE_DATA = 0xAA;
Bist du beim Speicher tatsächlich so knapp dran, dass diese 2 Bytes zum
echten "Problem" werden?
Ein Motiv für solche Adressierung ist, dass die Basisadresse ggf.
mehrfach verwendet werden kann, wenn auf benachbarte Register ebenfalls
zugegriffen wird. Das ist freilich bei AVRs aufgrund deren Knappheit an
Adressregistern seltener möglich als bei anderen Architekturen.
Hier:
1 | #include <avr/io.h>
| 2 |
| 3 | int main(void)
| 4 | {
| 5 | SPIE.DATA = 0xAA;
| 6 |
| 7 | while(!(SPIE.STATUS & SPI_IF_bm)){}
| 8 |
| 9 | SPIE.DATA = 0xBB;
| 10 |
| 11 | while(!(SPIE.STATUS & SPI_IF_bm)){}
| 12 |
| 13 |
| 14 | while(1)
| 15 | {
| 16 |
| 17 | }
| 18 | }
|
wird zu:
1 | SPIE.DATA = 0xAA;
| 2 | 0000010C 8a ea LDI R24,0xAA Load immediate
| 3 | 0000010D e0 ec LDI R30,0xC0 Load immediate
| 4 | 0000010E fa e0 LDI R31,0x0A Load immediate
| 5 | 0000010F 83 83 STD Z+3,R24 Store indirect with displacement
| 6 | while(!(SPIE.STATUS & SPI_IF_bm)){}
| 7 | 00000110 80 91 c2 0a LDS R24,0x0AC2 Load direct from data space
| 8 | 00000112 87 ff SBRS R24,7 Skip if bit in register set
| 9 | 00000113 fc cf RJMP PC-0x0003 Relative jump
| 10 | SPIE.DATA = 0xBB;
| 11 | 00000114 8b eb LDI R24,0xBB Load immediate
| 12 | 00000115 e0 ec LDI R30,0xC0 Load immediate
| 13 | 00000116 fa e0 LDI R31,0x0A Load immediate
| 14 | 00000117 83 83 STD Z+3,R24 Store indirect with displacement
| 15 | while(!(SPIE.STATUS & SPI_IF_bm)){}
| 16 | 00000118 80 91 c2 0a LDS R24,0x0AC2 Load direct from data space
| 17 | 0000011A 87 ff SBRS R24,7 Skip if bit in register set
| 18 | 0000011B fc cf RJMP PC-0x0003 Relative jump
| 19 | 0000011C ff cf RJMP PC-0x0000 Relative jump
|
Und dagegen die "alte" API:
1 | int main(void)
| 2 | {
| 3 | SPIE_DATA = 0xAA;
| 4 |
| 5 | while(!(SPIE_STATUS & SPI_IF_bm)){}
| 6 |
| 7 | SPIE_DATA = 0xBB;
| 8 |
| 9 | while(!(SPIE_STATUS & SPI_IF_bm)){}
| 10 |
| 11 | while(1)
| 12 | {
| 13 |
| 14 | }
| 15 | }
|
1 | SPIE_DATA = 0xAA;
| 2 | 0000010C 8a ea LDI R24,0xAA Load immediate
| 3 | 0000010D 80 93 c3 0a STS 0x0AC3,R24 Store direct to data space
| 4 | while(!(SPIE_STATUS & SPI_IF_bm)){}
| 5 | 0000010F 80 91 c2 0a LDS R24,0x0AC2 Load direct from data space
| 6 | 00000111 87 ff SBRS R24,7 Skip if bit in register set
| 7 | 00000112 fc cf RJMP PC-0x0003 Relative jump
| 8 | SPIE_DATA = 0xBB;
| 9 | 00000113 8b eb LDI R24,0xBB Load immediate
| 10 | 00000114 80 93 c3 0a STS 0x0AC3,R24 Store direct to data space
| 11 | while(!(SPIE_STATUS & SPI_IF_bm)){}
| 12 | 00000116 80 91 c2 0a LDS R24,0x0AC2 Load direct from data space
| 13 | 00000118 87 ff SBRS R24,7 Skip if bit in register set
| 14 | 00000119 fc cf RJMP PC-0x0003 Relative jump
| 15 | 0000011A ff cf RJMP PC-0x0000 Relative jump
|
>Bist du beim Speicher tatsächlich so knapp dran, dass diese 2 Bytes zum
>echten "Problem" werden?
Es geht mir nicht um die 2Byte Codegröße sondern um die Geschwindigkeit.
Immerhin sind es 2Takte mehr pro aufruf. Und das ganze beschränkt sich
ja nicht nur auf die SPI Struct sondern auf alle Bereiche.
Es macht einen unterschied ob ich einen Pin in 2Takten oder in 4Takten
Toggeln kann.
Yep, unschön, insbesondere weil die relative Adressierung an Stelle des
STS hier tatsächlich sinnvoll wäre, wenn Z für alle 4
Lade/Speicher-Operationen nur einmal geladen würde.
Rechne aber lieber nicht damit, dass sich das in absehbarer Zeit ändert,
denn fehlende Optimierung auf AVR in einem minder schweren Fall hat
vmtl. nicht grad hohe Priorität bei den Entwicklern.
Und wo hast du den Assembler-Code her? Vielleicht irgendwo aus dem
AVR-Studio-5-Debugger? Ist dir klar, dass das Studio5 den Debug-Build
mit -O0 übersetzt?
-O0 sieht anders aus. Nö, das ist reproduzierbar.
Was ich mit der Z-Optimierung meinte zeigt sich bei: 1 | #define SPIE (*(SPI_t *) 0x0AC0) /* Serial Peripheral Interface E */
| 2 |
| 3 | typedef volatile unsigned char register8_t;
| 4 |
| 5 | /* Serial Peripheral Interface */
| 6 | typedef struct SPI_struct
| 7 | {
| 8 | register8_t CTRL; /* Control Register */
| 9 | register8_t INTCTRL; /* Interrupt Control Register */
| 10 | register8_t STATUS; /* Status Register */
| 11 | register8_t DATA; /* Data Register */
| 12 | } SPI_t;
| 13 |
| 14 | SPI_t *p = &SPIE;
| 15 |
| 16 | int main(void)
| 17 | {
| 18 | p->DATA = 0xAA;
| 19 |
| 20 | while(!(p->STATUS & 1)){}
| 21 |
| 22 | p->DATA = 0xBB;
| 23 |
| 24 | while(!(p->STATUS & 1)){}
| 25 |
| 26 |
| 27 | while(1)
| 28 | {
| 29 |
| 30 | }
| 31 | }
|
denn das wird zu: 1 | lds r30,p
| 2 | lds r31,(p)+1
| 3 | ldi r24,lo8(-86)
| 4 | std Z+3,r24
| 5 | .L2:
| 6 | ldd r24,Z+2
| 7 | sbrs r24,0
| 8 | rjmp .L2
| 9 | ldi r24,lo8(-69)
| 10 | std Z+3,r24
| 11 | .L3:
| 12 | ldd r24,Z+2
| 13 | sbrs r24,0
| 14 | rjmp .L3
| 15 | .L8:
| 16 | rjmp .L8
|
> Und wo hast du den Assembler-Code her? Vielleicht irgendwo aus dem
> AVR-Studio-5-Debugger? Ist dir klar, dass das Studio5 den Debug-Build
> mit -O0 übersetzt?
Ja aus dem AVR-Studio 5 Disassembler bzw. der *.lss Datei aber ich glaub
nicht der er mir -O0 Code anzeigt denn wenn ich den Code mit -Os oder
-O3 übersetzte kommt auch ein anderer ASM Code dabei raus.
habe nun meine #define geändert:
1 | #define SPI_SEND(modul, data) while(!(modul.STATUS & SPI_IF_bm)){}; modul.DATA = data
|
in 1 | #define SPI_SEND(modul, data) while(!(modul##_STATUS & SPI_IF_bm)){}; modul##_DATA = data
|
Aufruf
1 | int main(void)
| 2 | {
| 3 | SPI_SEND(SPIE, 0xAA);
| 4 | }
|
Diese 4.5.1 ist eine von Atmel gepatchte Version des Compilers, d.h. der
entsprechende Bug-Report gehört nach atmel.com.
Auf einem "suberen" avr-gcc lässt sich dies nicht übersetzten und damit
auch nicht nachvollziehen.
Mit einer angepassten Quelle sehe ich das auch mit 4.3, 4.5 und 4.6.
1 | typedef struct
| 2 | {
| 3 | volatile unsigned char a,b,c,d;
| 4 | } SPI_t;
| 5 |
| 6 | #define SPIE (*(SPI_t*) 0x0AC0)
| 7 |
| 8 | void foo (void)
| 9 | {
| 10 | SPIE.d = 0xAA;
| 11 | while (!(SPIE.c & 0x80));
| 12 |
| 13 | SPIE.d = 0xBB;
| 14 | while (!(SPIE.c & 0x80));
| 15 | }
|
Wenn du magst, kannst du einen Bug-Report bei GCC machen. Da es nur ein
Optimierungsproblem ist, wird das frühestens in 4.7 behoben — zumindest
im GCC und falls es in 4.7 noch besteht. Oder du musst eben selber
Patches machen, besorgen oder zurückportieren, sobald es in 4.7/4.8
behoben ist.
Mit 4.7 habe ich es noch nicht getestet, und ich kann auch (noch) nicht
sagen, ob es am AVR-Teil hängt oder nicht.
Eigentlich gehört das ins GCC-Forum.
Das Problem entsteht beim tree→RTL Lowering.
> Diese 4.5.1 ist eine von Atmel gepatchte Version des Compilers, d.h. der
> entsprechende Bug-Report gehört nach atmel.com.
> Auf einem "suberen" avr-gcc lässt sich dies nicht übersetzten und damit
> auch nicht nachvollziehen.
Mit WinAVR 20100110 besteht das Problem auch.
Werde gleich mal aufm Linux System einen "sauberen" avr-gcc compilieren
mal gucken wie es dort aussieht.
> Wenn du magst, kannst du einen Bug-Report bei GCC machen.
Mein Schrift-Englisch ist jetzt nicht so gut das ich es machen würde.
Ich würde mich freuen wenn einer von euch es dort Posten könnte.
> Eigentlich gehört das ins GCC-Forum.
Ja, richtig. Hab es leider aus Reflex / Gewohnheit hier gepostet. Ein
Mod könnte es ja verschieben.
Julius F. schrieb:
>> Wenn du magst, kannst du einen Bug-Report bei GCC machen.
>
> Mein Schrift-Englisch ist jetzt nicht so gut das ich es machen würde.
> Ich würde mich freuen wenn einer von euch es dort Posten könnte.
Ist PR50448.
Danke dir.
Ich werde es weiterverfolgen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|