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.