Also aus dem Code: if (gdurchlauf < (uint8_t)(gtemp & 0x0f)) { erhalte ich das Assembler Listing: .stabn 68,0,63,.LM6-__vector_5 .LM6: mov r24,r18 clr r25 andi r24,lo8(15) andi r25,hi8(15) cp r20,r24 cpc r21,r25 brge .L5 für mich sieht das so aus als ob der Code fürs Vergleichen von 16Bit Werten ausgelegt ist. Soweit ich den Assembler Code richtig verstehe setzt er Register r25 auf Null und führt dann mit den oberen 8Bit von 0x000f eine logische UND Verknüpfung durch, wobei das Ergebnis in Register 25 gespeichert wird. Dann werden zweimal jeweils zwei Register irgendwie verglichen (ich kann halt nicht sonderlich gut Assembler) und abhängig vom Ergebnis ein Sprung ausgeführt. Nach meiner Überlegung müsste jedoch Register r25 immer Null sein (0 AND 0 ergibt 0) und somit eigentlich wegoptimiert werden. Desweiteren habe ich versucht durch (uint_8t) dem Compiler mitzuteilen dass 8Bit für die Berechnung genügen, aber der erzeugte Code ist der gleiche. Alle von mir im obrigen Code Ausschnitt verwendeten Variablen sind vom Typ uint_8t. Verstehe ich blos den Code grundlegend falsch oder wie bringe ich gcc (Aufruf enthält die Anweisung -Os zur Optimierung) dazu den Code zu optimieren?
>Ist (uint_8t) = (unsigned char) ?
Ja, uint_8t ist in der standartmäßig mit installierten
avr/include/inttypes.h durch
typedef unsigned char uint8_t;
definiert.
Ich hab jetzt nochmal den kompletten Quelltext angehängt
Hi logische Operationen (wie dein gtemp & 0x0f) werden laut C-Standard immer in 16-Bit (oder in sizeof(int)*8, das weiß ich jetzt gerade nicht auswendig) ausgeführt. Matthias
Dass standartmäßig mit 16Bit gerechnet wird hab ich auch schon gelesen :-) Nur wird in http://www.mikrocontroller.net/forum/read-2-96892.html#96901 beschrieben dass mann allen anschein nach dem Compiler dazu bringen kann trotzdem mit 8Bit zu rechnen. Und das hab ich mit dem vorgestelltem (uint8_t) versucht, nur wird dies vom Compiler ignoriert. Ich möchte halt die Interrupt Routine möglichst klein und schnell halten.
Hi caste doch mal die einzelenen Operatoren (insbesondere die Konstante) vor der eigentlichen Operation nach uint8_t. Matthias
Damit muß man sich abfinden, der GCC castet oft in Vergleichen und immer in switch() nach 16 Bit, auch wenn es gar keinen Sinn macht. Will man das in Vergleichen vermeiden, hilft nur das Ergebins erstmal einer 8-Bit Variablen zuzuweisen und diese dann zu testen. Das macht natürlich den Code länger und damit unleserlicher. Sollte man also nur machen, wenn es wirklich auf das letzte Quentchen Flash oder die µs ankommt. Peter
Danke, der Tipp die If Verzweigung in zwei Zeilen zu teilen hat es gebracht. Die Interrupt Routine dürfte jetzt ungefähr 100 Takte weniger zur Abarbeitung benötigen und da die Routine mehrere Tausend mal pro Sekunde aufgerufen wird, dürfte dies einiges sparen. Der copilierte Code wurde um 30Byte kleiner, was bei einem ATMEGA32 aber sicher nicht sonderlich wichtig ist.
Ich habe hier ein paar Bemerkungen aus dem Handbuch kopiert: Why does the compiler compile an 8-bit operation that uses bitwise operators into a 16-bit operation in assembly? Bitwise operations in Standard C will automatically promote their operands to an int, which is (by default) 16 bits in avr-gcc. To work around this use typecasts on the operands, including literals, to declare that the values are to be 8 bit operands. This may be especially important when clearing a bit: var &= ~mask; /* wrong way! The bitwise "not" operator (~) will also promote the value in mask to an int. To keep it an 8-bit value, typecast before the "not" operator: var &= (unsigned char)~mask; Alles klar? Viele Grüsse Harry
@Harry: Das dachte ich bisher auch immer. Aber die WINAVR-Version vom April 2004 macht das -je nach angegebenen Options- anders: Aus folgendem Code: #define ov_event _BV(2) ... uint8_t my_tmr0_event; ... if (my_tmr0_event & ov_event){ my_tmr0_event &= ~ov_event; } macht der Compiler diesen Output, an dem nur noch das (clr r25) zuviel ist. Das bleibt aber, egal wieviel und wo ich typecaste: 193:interrupt.c **** if (my_tmr0_event & ov_event){ 323 .LM17: 324 0052 842F mov r24,r20 325 0054 9927 clr r25 326 0056 82FF sbrs r24,2 327 0058 02C0 rjmp .L6 194:interrupt.c **** my_tmr0_event &= ~ov_event; 329 .LM18: 330 005a 4B7F andi r20,lo8(-5) Übersetzt ist das Ganze mit dem Standard-Makefile von Jörg: Compiling: interrupt.c avr-gcc -c -mmcu=atmega16 -I. -g -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=prn_int.lst -std=gnu99 -Wp,-M,-MP,-MT,interrupt.o,-MF,.dep/interrupt.o.d interrupt.c -o interrupt.o Dieses Verhalten liegt meiner Meinung nach an der -fshort-enums Option: (Auszug aus Tkinfo -> gcc): `-fshort-enums' Allocate to an `enum' type only as many bytes as it needs for the declared range of possible values. Specifically, the `enum' type will be equivalent to the smallest integer type which has enough room. *Warning:* the `-fshort-enums' switch causes GCC to generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface. Stefan
Hallo! Hat sich da schon eine Lösung ergeben? Mein Kollege hat das gleiche Problem, aber casten hilft nichts, wobei das Problem nur bei Verwendung von Konstanten auftritt. 213:leuchtturm.c **** if ((unsigned char)(c & (unsigned char) 8) == 0) 570 .LM48: 571 0286 8091 0000 lds r24,c 572 028a 9927 clr r25 573 028c 9C01 movw r18,r24 574 028e 3695 lsr r19 575 0290 2795 ror r18 576 0292 3695 lsr r19 577 0294 2795 ror r18 578 0296 3695 lsr r19 579 0298 2795 ror r18 580 029a 81E0 ldi r24,lo8(1) 581 029c 90E0 ldi r25,hi8(1) 582 029e 8227 eor r24,r18 583 02a0 9327 eor r25,r19 584 02a2 8170 andi r24,lo8(1) 585 02a4 9070 andi r25,hi8(1) 586 02a6 0097 sbiw r24,0 587 02a8 29F0 breq .L27 214:leuchtturm.c **** { 215:leuchtturm.c **** j++; 589 .LM49: 590 02aa 8091 0000 lds r24,j 591 02ae 8F5F subi r24,lo8(-(1)) 592 02b0 8093 0000 sts j,r24 593 .L27: 216:leuchtturm.c **** }
Wie schon geschrieben, ich habe mich einfach mit zwei Zeilen Quellcode begnügt. Aus: if (gdurchlauf < (gdbyte & 0x03)) { wurde: gtemp = gdbyte & 0x03; //Dies in der If Verzweigung ->16Bit if (gdurchlauf < gtemp) {
Hallo Fritz, mit welchen Optionen hast Du den Code übersetzt welcher mc wie ist c definiert? Bei mir ist cc lokal als unsigned char definiert, dann kommt folgender Code raus, der eigendlich kürzer nicht sein kann: 116:buskoppler.c **** if ((unsigned char)(cc & (unsigned char) 8) == 0){ 290 .LM28: 291 0086 F3FC sbrc r15,3 292 0088 02C0 rjmp .L9 117:buskoppler.c **** c = 1; 294 .LM29: 295 008a 81E0 ldi r24,lo8(1) 296 008c 8983 std Y+1,r24 297 .L9: Übrigens mit der neuesten gcc-Version und Jörgs Muster-Makefile (mit wenigen Änderungen). Viele Grüße, Stefan
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.