Hallo,
ich habe ein Problem, das mit den letzten Nerv kostet...
Ich verwende den avr-gcc mit dem -O3 Schalter, also hoechster
Code-Optimierung. Das moechte ich prinzipiell auch dabei belassen,
jedoch habe ich Stellen im Code, die vom Compiler wegoptimiert werden
und somit das Programm falsch laeuft. Verschiedene Kombinationen mit
volatile haben bisher noch nicht zum Erfolg gefuehrt. Durch die
Verwendung des Inline Assembler tauchten einige Teile wieder auf...
Hier ein Beispiel:
1
#define MAX_COUNT 7936 /* =0x1F00 */
2
uint8_tglobal_counter;
3
4
uint16_tcounts(){
5
uint16_tretvalue;
6
asmvolatile("in %A0, %1\n\t"
7
"lds %B0, %2\n\t"
8
:"=r"(retvalue)
9
:"M"(_SFR_IO_ADDR(TCNT0)),"m"(global_counter)
10
);
11
returnretvalue;
12
}
13
14
ISR(TIMER0_OVF_vect){
15
if(counts()>=MAX_COUNT)
16
TCCR0&=~(_BV(CS02)|_BV(CS00));
17
else
18
++global_counter;
19
}
Ich moechte den T0 interrupt eine bestimmte anzahl aufrufen und dann
z.B. den Timer anhalten. global_count wird bei den normalen aufrufen der
ISR incrementiert und ist quasi das obere byte vom Gesamt-Timerwert. Um
auf die gesamtzahl an takten zu kommen kombiniere ich die einzelwerte
aus TCNT0 und global_counter und gebe das ergebnis zurueck. Den inline
assembler nehme ich nur desshalb, da es schneller geht, als das, was der
avr-gcc selbst generieren wuerde (ich will die ISR kurz halten).
Wenn ich mir die lss-file anschaue, wird der else-Teil ueberhaupt nicht
umgesetzt, sondern wegoptimiert. Das will ich aber nicht. Simultan dazu
passiert dasselbe mit dem if-Teil, wenn ich zB. das >= durch ein <
ersetze.
ich schliesse daraus, dass avr-gcc den obigen ausdruck bei >= immer fuer
true haelt und den else-teil weglaesst.
dass die routine in der ISR eingebettet wird ist fuer mich ok und
verstaendlich, da sie im selben modul nicht woanders aufgerufen wird.
hier noch der lss-ausschnitt:
1
0000014a <__vector_9>:
2
3
ISR(TIMER0_OVF_vect) {
4
14a: 1f 92 push r1
5
14c: 0f 92 push r0
6
14e: 0f b6 in r0, 0x3f ; 63
7
150: 0f 92 push r0
8
152: 11 24 eor r1, r1
9
154: 8f 93 push r24
10
156: 9f 93 push r25
11
12
uint16_t counts() {
13
uint16_t retvalue;
14
15
asm volatile ( "in %A0, %1\n\t"
16
158: 82 b7 in r24, 0x32 ; 50
17
15a: 90 91 61 00 lds r25, 0x0061
18
19
if(counts() >= MAX_COUNT) {
20
TCCR0 &= ~(_BV(CS02) | _BV(CS00));
21
15e: 83 b7 in r24, 0x33 ; 51
22
160: 8a 7f andi r24, 0xFA ; 250
23
162: 83 bf out 0x33, r24 ; 51
24
25
} else {
26
++t0ov_count;
27
}
28
29
}
30
164: 9f 91 pop r25
31
166: 8f 91 pop r24
32
168: 0f 90 pop r0
33
16a: 0f be out 0x3f, r0 ; 63
34
16c: 0f 90 pop r0
35
16e: 1f 90 pop r1
36
170: 18 95 reti
Ich hoffe jemand weiss, woran es hakt...
sollte noch etwas unklar sein, fragt einfach.
PS: MCU ist ATmega8
Mark schrieb:> sollte noch etwas unklar sein, fragt einfach.
Ja. Ich
Was soll der Unsinn.
Du bist im Overflow Interrupt. Den Wert vom TCNT0 kann ich dir auch so
sagen. Der ist 0.
"Der ist 0."
Wird der Timer fuer die Dauer der ISR gestoppt? Dachte der laeuft
weiter, sodass ich den exakten wert, wie oben beschrieben, extra
bestimmen muss...
Mark schrieb:> "Der ist 0.">> Wird der Timer fuer die Dauer der ISR gestoppt? Dachte der laeuft> weiter, sodass ich den exakten wert, wie oben beschrieben, extra> bestimmen muss...
Natürlich läuft der weiter (welchen Vorteiler verwendest du?). Nur: Was
hilft dir dann deine Abfrage?
Du hast sowieso keine Kontrolle darüber wieviele Taktzyklen zum Beispiel
exakt vergehen, bis dein ISR Handler zum Zuge kommt. Du hast keine
Kontrolle darüber wieviele Takte benötigt werden um den ISR-Einstieg
(sichern auf den Stack) abzuwickeln.
Wenn du so auf jeden einzelnen Taktzyklus aus bist, dann ist die
Methodik sowieso Quatsch. Schalte den Timer in den CTC Modus und lass
die Hardware Takte zählen.
Mit deiner Systematik wirst du sowieso keine 7936 Timer-Takte abzählen
können. Schon alleine deswegen, weil deine ISR nur alle 256 Timer-Takte
aufgerufen wird.
Dann brauchst du auch keinen Inline Assembler (von dem ich sowieso nicht
überzeugt bin, dass der hier irgendetwas bringt)
Das ist zwar keine direkte Lösung für dein Assembler Problem. Ich bin
allerdings davon überzeugt
* das dir hier der Assembler keinen Vorteil bringt
* du das Pferd grundsätzlich falsch aufzäumst (kein CTC Modus benutzt)
(Ich denke aber, dass du dich bei den Constraint Markierungen vertan
hast. Der Compiler behandelt deine count() Funktion wie eine Funktion
die ein bekanntes konstantes Ergebnis liefert. Eines, welches er
offenbar größer als MAXCOUNT ansieht)
DANKE! Es geht jetzt; keine "Geistercodeteile" mehr. An CTC hatte ich
noch gar nicht gedacht, aber so ist es auch in Ordnung.
Vielen Dank und gute Nacht,
Mark
"Der Klassiker: global_counter nicht volatile."
das hatte ich probiert und hat im erzeugten code nichts veraendert.
(hatte retvalue zwischenzeitlich auch schon volatile, war dann durch den
Inline Assembler aber nicht mehr notwendig.)
Unabhängig davon, ob ein Code sinnvoll ist, muß er trotzdem richtig
compilieren.
Bei mir tut er das (AVR_GCC 4.3.3):
1
ISR(TIMER0_OVF_vect){
2
4a:1f92pushr1
3
4c:0f92pushr0
4
4e:0fb6inr0,0x3f;63
5
50:0f92pushr0
6
52:1124eorr1,r1
7
54:2f93pushr18
8
56:8f93pushr24
9
58:9f93pushr25
10
#define MAX_COUNT 7936 /* =0x1F00 */
11
uint8_tglobal_counter;
12
13
uint16_tcounts(){
14
uint16_tretvalue;
15
asmvolatile("in %A0, %1\n\t"
16
5a:82b7inr24,0x32;50
17
5c:90916000ldsr25,0x0060
18
);
19
returnretvalue;
20
}
21
22
ISR(TIMER0_OVF_vect){
23
if(counts()>=MAX_COUNT)
24
60:8050subir24,0x00;0
25
62:9f41sbcir25,0x1F;31
26
64:68f4brcc.+26;0x80<__vector_9+0x36>
27
TCCR0&=~(_BV(CS02)|_BV(CS00));
28
else
29
++global_counter;
30
66:80916000ldsr24,0x0060
31
6a:8f5fsubir24,0xFF;255
32
6c:80936000sts0x0060,r24
33
}
34
70:9f91popr25
35
72:8f91popr24
36
74:2f91popr18
37
76:0f90popr0
38
78:0fbeout0x3f,r0;63
39
7a:0f90popr0
40
7c:1f90popr1
41
7e:1895reti
42
returnretvalue;
43
}
44
45
ISR(TIMER0_OVF_vect){
46
if(counts()>=MAX_COUNT)
47
TCCR0&=~(_BV(CS02)|_BV(CS00));
48
80:83b7inr24,0x33;51
49
82:8a7fandir24,0xFA;250
50
84:83bfout0x33,r24;51
51
else
52
++global_counter;
53
}
54
86:9f91popr25
55
88:8f91popr24
56
8a:2f91popr18
57
8c:0f90popr0
58
8e:0fbeout0x3f,r0;63
59
90:0f90popr0
60
92:1f90popr1
61
94:1895reti
Ich würde auch dazu raten, immer Optimierung -Os zu nehmen.
Andere Optimierungen erzeugen deutlich größeren Code ohne merkbar
schneller zu sein.
Hier sieht man z.B., daß der Epilog (16 Byte) verdoppelt wird, nur um
ein RJMP (2 Zyklen) zu sparen.
Peter
Peter Dannegger schrieb:> Hier sieht man z.B., daß der Epilog (16 Byte) verdoppelt wird, nur um> ein RJMP (2 Zyklen) zu sparen.
Aber schneller ist es. :-) Ob der Aufwand in einem Verhältnis zum
Nutzen steht, muss natürlich jeder selbst für sich entscheiden.
Wenn am Ende des Projekts noch 43 % Flash ungenutzt sind, stört es
auch nicht, wenn man mit -O3 dann halt danach nur noch 23 %
ungenutzt hat, Geld gibt dir Atmel in beiden Fällen nicht zurück ;-),
und vielleicht spart die schnellere Implementierung ja noch etwas
Strom in einem batteriebetriebenen Gerät, weil sie sich schneller
wieder schlafen legen kann...
Ich stimme dir aber zu, dass der Code korrekt compiliert werden muss,
auch wenn er reichlich sinnfrei ist.
A. K. schrieb:> Der Klassiker: global_counter nicht volatile.
Warum soll man eine globale Variable, auf die ausschließlich aus einer
ISR (meinetwegen auch über eine aufgerufene Sub-Funktion) zugegriffen
wird, volatile machen? Mache ich nie. Und warum auch? Da eine ISR (und
alle darin aufgerufenen Unterfunktionen) niemals unterbrochen werden
kann (jedenfalls bei ATTiny/ATMEGA), gibt es auch keine Notwendigkeit,
so eine Variable volatile zu deklarieren. Kostet nur Code + Zeit ;-)
Gruß,
Frank
Frank M. schrieb:> Warum soll man eine globale Variable, auf die ausschließlich aus einer> ISR (meinetwegen auch über eine aufgerufene Sub-Funktion) zugegriffen> wird, volatile machen?
Dann nicht. Aber das war ja auch nicht das ganze Programm, daher war das
allenfalls zu vermuten, jedoch nicht sicher.