Hallo in die geschätzte Expertenrunde,
vielleicht kann mir jemand auf die Sprünge helfen. Derzeit simuliere ich
einen ATTiny13A (default-Fuses) im AVR Studio 4.16.628 unter Linux mit
Wine.
Dabei ist mir eine Anomalie aufgefallen, für die ich keine Erklärung
habe.
Anbei voneinander nur leicht abweichende Assembler-Codes (die vielen
Leerzeilen werden hier scheinbar vom Forums-Editor eingefügt, weiß nicht
wo die herkommen. Im Original sind die nicht vorhanden).
Der eine nutzt drei Interrupts: einmal beim Match von OC0B im Timer0,
dann beim Überlauf des Timers und als dritten den Watchdog in der
Interrupt-only-Variante. Das funktioniert erst mal wie erwartet. Der
OC0B-Match-Interrupt wird ausgelöst, danach der erste Timer-Überlauf.
Der passende Interrupt wird auch hier ausgelöst und abgearbeitet - dann
bleibt der Timer0 einfach auf Null stehen und das Programm verbleibt in
der Endlosschleife, der Watchdog springt niemals an.
Darunter eine leicht abgewandelte Programmversion, die auf dem Vergleich
OC0B verzichtet, alles andere ist gleich. Hier ein ähnliches Verhalten:
Der erste Interrupt beim Timerüberlauf wird ausgelöst und abgearbeitet -
der Timer bleibt auch jetzt bei Null stehen und läuft nicht mehr.
Allerdings funktioniert im Gegensatz zur ersten Variante jetzt der
Watchdog, der wie erwartet etwa alle 32 simulierten Millisekunden
auslöst.
Hat zufällig jemand eine Erklärung?
Variante A: Drei Interrupts 1 | .nolist
| 2 | .include "tn13adef.inc" ; Define device ATtiny13A
| 3 | .list
| 4 | .def sreg_bak = R15
| 5 | .def rmp = R16
| 6 | .dseg
| 7 | .org SRAM_START
| 8 | .cseg
| 9 | .org 0x0000
| 10 | rjmp Main ; Reset vector
| 11 | reti ; INT0
| 12 | reti ; PCI0
| 13 | rcall isr_timer_overflow ; OVF0
| 14 | reti ; ERDY
| 15 | reti ; ACI
| 16 | reti; OC0A
| 17 | rcall isr_oc0b; OC0B
| 18 | rcall isr_watchdog ; WDT
| 19 | reti ; ADCC
| 20 |
| 21 | init_watchdog:
| 22 | wdr
| 23 | in rmp, MCUSR
| 24 | andi rmp, (0xff - (1<<WDRF))
| 25 | out MCUSR, rmp
| 26 | in rmp, WDTCR
| 27 | ori rmp, (1<<WDTIE)|(1<<WDP0) ; Interrupt-only und prescaler 32ms
| 28 | out WDTCR, rmp
| 29 | ret
| 30 |
| 31 | init_timer0:
| 32 | push rmp
| 33 | ldi rmp, 220
| 34 | out OCR0B, rmp
| 35 | ldi rmp,(1<<WGM01)
| 36 | out TCCR0A, rmp
| 37 | ldi rmp, (1<<CS01)
| 38 | out TCCR0B, rmp
| 39 | ldi rmp, (1<<TOIE0)|(1<<OCIE0B)
| 40 | out TIMSK0, rmp
| 41 | pop rmp
| 42 | ret
| 43 |
| 44 |
| 45 | Main:
| 46 | ldi rmp,Low(RAMEND)
| 47 | out SPL,rmp
| 48 | rcall init_watchdog
| 49 | rcall init_timer0
| 50 | sbi DDRB,1
| 51 | sei
| 52 |
| 53 | Loop:
| 54 | nop
| 55 | rjmp Loop
| 56 |
| 57 |
| 58 | isr_timer_overflow:
| 59 | in sreg_bak, SREG
| 60 | cbi PORTB,1
| 61 | out SREG, sreg_bak
| 62 | reti
| 63 |
| 64 | isr_oc0b:
| 65 | reti
|
Variante B: Zwei Interrupts 1 | .nolist
| 2 | .include "tn13adef.inc"
| 3 | .list
| 4 | .def sreg_bak = R15
| 5 | .def rmp = R16
| 6 | .dseg
| 7 | .org SRAM_START
| 8 | .cseg
| 9 | .org 0x0000
| 10 |
| 11 | rjmp Main
| 12 | reti ; INT0
| 13 | reti ; PCI0
| 14 | rcall isr_timer_overflow ; OVF0
| 15 | reti ; ERDY
| 16 | reti ; ACI
| 17 | reti; isr_oc0a; OC0A
| 18 | reti; rcall isr_oc0b; OC0B
| 19 | rcall isr_watchdog ; WDT
| 20 | reti ; ADCC
| 21 |
| 22 | init_watchdog:
| 23 | wdr
| 24 | in rmp, MCUSR
| 25 | andi rmp, (0xff - (1<<WDRF))
| 26 | out MCUSR, rmp
| 27 | in rmp, WDTCR
| 28 | ori rmp, (1<<WDTIE)|(1<<WDP0) ; prescaler 32ms
| 29 | out WDTCR, rmp
| 30 | ret
| 31 |
| 32 | init_timer0:
| 33 | push rmp
| 34 | ldi rmp, 220
| 35 | out OCR0B, rmp
| 36 | ldi rmp,(1<<WGM01)
| 37 | out TCCR0A, rmp
| 38 | ldi rmp, (1<<CS01)
| 39 | out TCCR0B, rmp
| 40 | ldi rmp, (1<<TOIE0)
| 41 | out TIMSK0, rmp
| 42 | pop rmp
| 43 | ret
| 44 |
| 45 |
| 46 | Main:
| 47 | ldi rmp,Low(RAMEND)
| 48 | out SPL,rmp
| 49 | rcall init_watchdog
| 50 | rcall init_timer0
| 51 | sbi DDRB,1
| 52 | sei
| 53 |
| 54 | Loop:
| 55 | nop
| 56 | rjmp Loop
| 57 |
| 58 |
| 59 | isr_timer_overflow:
| 60 | in sreg_bak, SREG
| 61 | cbi PORTB,1
| 62 | out SREG, sreg_bak
| 63 | reti
| 64 |
| 65 | isr_watchdog:
| 66 | in sreg_bak, SREG
| 67 | sbi PORTB,1
| 68 | out SREG, sreg_bak
| 69 | reti
|
Stephan E. schrieb:
> Hallo in die geschätzte Expertenrunde
Fuck! Kannst du lesen und das freundlicherweise befolgen?
"Längerer Sourcecode" ist wenn es mehr als eine Seite ist.
Ganz auf die Schnelle: 1 | ...
| 2 | reti; OC0A
| 3 | rcall isr_oc0b; OC0B
| 4 | rcall isr_watchdog ; WDT
| 5 | reti ; ADCC
|
mutet seltsam an, soll das nicht 'rjmp' heißen?
Wastl schrieb:
> Fuck! Kannst du lesen und das freundlicherweise befolgen?
Sorry, habe ich wohl übersehen. Und so lange ist's ja auch wieder nicht.
S. L. schrieb:
> rcall isr_watchdog ; WDT
> reti ; ADCC
> mutet seltsam an, soll das nicht 'rjmp' heißen?
Habe ich mal getestet, scheint beides zu funktionieren. Aber nachdem
auch im Tutorial überall rjmp verwendet wird, werde ich das künftig auch
tun. Danke für den Hinweis.
Ansonsten hat sich leider nichts geändert - der Timer0 bleibt nach dem
ersten Überlauf einfach stehen.
Wieder auf die Schnelle:
> scheint beides zu funktionieren
Kann ich mir nicht vorstellen - in der 1. Version zum Beispiel wird ja
grundsätzlich nach isr_oc0b auch noch isr_watchdog abgearbeitet.
Ansonsten: Simulator & Interrupts - ob das immer funktioniert? Da gab es
mal Beiträge ...
Und warum nicht der ATtiny85 - den könnte ich hier gegebenenfalls
realiter laufen lassen.
Stephan E. schrieb:
> Habe ich mal getestet, scheint beides zu funktionieren.
Nicht wirklich. Bei jedem Interrupt werden zwei Byte auf dem Stack
"verbraucht". Ist nur eine Frage der Zahl der Interrupts, bis der Stack
überlauft (bzw. eigentlich: unterläuft). Das sinnlose (und am Ende
tödliche) Wachsen des Stack sollte sich im Simulator wirklich sehr gut
verfolgen lassen.
> Ansonsten hat sich leider nichts geändert - der Timer0 bleibt nach dem
> ersten Überlauf einfach stehen.
Das kann nicht sein. Dem Timer ist der Stack egal, dem Timer ist
überhaupt egal, was die MCU tut. Einmal gestartet, läuft der einfach. Es
sei denn, die MCU hält ihn an. Dafür gibt es aber keinen Code.
Und übrigens: in deiner Variante A gibt es darüber hinaus auch keinen
Code für isr_watchdog. Das dürfte also nichtmal assemblierbar sein...
S. L. schrieb:
> ...
> reti; OC0A
> rcall isr_oc0b; OC0B
> rcall isr_watchdog ; WDT
> reti ; ADCC
> mutet seltsam an, soll das nicht 'rjmp' heißen?
In der Tat.
> Wachsen des Stack
?
Zurückgekehrt wird doch immer, oder? Nur an irrwitzigen Stellen.
S. L. schrieb:
> Ansonsten: Simulator & Interrupts - ob das immer funktioniert?
Jedenfalls bezüglich der MCU habe ich beim SimulatorV2 noch nie
Gegenteiliges feststellen können.
Bugs gibt es aber reichlich in der Simulation der Peripherie und auch
Peripherie, die praktisch garnicht simuliert wird. Bei letzterem kann
man natürlich ewig auf das Auftreten der entsprechende Interrupts
warten. Es werden keine erfolgen...
Wie auch immer, das Entscheidende scheint mir zu sein, dass angeblich
der Timer stoppt. Das tut er natürlich nicht. Weder in der Simulation
noch in der Realität. Ich tippe mal auf einen Bedienfehler.
Möglicherweise ausgelöst durch einen Anzeigefehler, der durch Wine
verursacht wird.
Ich kann mich erinnern, dass ich auch mal versucht habe, das Studio4
unter Wine zu betreiben, das dann aber wegen extremer Anzeigefehler
schon im Editor schnell wieder aufgegeben habe. Bis zum Einsatz des
Simulators bin ich also unter Wine niemals gekommen.
S. L. schrieb:
>> Wachsen des Stack
>
> ?
> Zurückgekehrt wird doch immer, oder?
Nicht im Simulator. Der kann Stack-Unterlauf diagnostizieren und hält
dann an. Gibt aber natürlich dann auch eine ensprechende Fehlermeldung
auf der Debugger-Console aus.
Nein, das meinte ich nicht, sondern: es gibt hier keinen Stack-Unterlauf
- wo sollte der auch auftreten?
S. L. schrieb:
> Nein, das meinte ich nicht, sondern: es gibt hier keinen Stack-Unterlauf
> - wo sollte der auch auftreten?
Es passiert folgendes:
IRQ:
- SREG_I wird gelöscht
- die Rücksprungadresse wird auf den Stack gelegt
- der Vektor wird angesprungen
Vektor:
- rcall legt legt erneut eine Rücksprungadresse auf den Stack
- springt das angegebene Ziel an
Ziel (ISR):
-wird abgearbeitet
-endet mit reti (setzt SREG_I, holt 1x Rücksprungadresse vom Stack)
-das ist die vom (falschen) rcall gesetzte Rücksprungadresse
-führt effektiv dazu, das der nächste Vektor in der Reihenfolge der
Vektortabelle durchlaufen wird.
Und jetzt wird es spannend. Beim Overflow steht auf dem nächsten Vektor
ein reti. Das holt die zweite Rücksprungadresse vom Stack (setzt das in
diesem Fall bereits gesetzte SREG_I) und springt zur Rücksprungadresse.
Alles gut hier, die Bilanz stimmt. Schwachsinn ist es trotzdem.
Aber, nun spiele mal selber durch, was bei OC0B passiert...
> Aber, nun spiele mal selber durch, was bei OC0B passiert...
Das, was ich bereits geschrieben hatte:
> in der 1. Version zum Beispiel wird ja grundsätzlich
> nach isr_oc0b auch noch isr_watchdog abgearbeitet.
Und anschließend wird bei 'reti ; ADCC' ins Hauptprogramm zurückgekehrt
- also ich sehe nach wie vor keinen Stack-Unterlauf.
S. L. schrieb:
>> Aber, nun spiele mal selber durch, was bei OC0B passiert...
>
> Das, was ich bereits geschrieben hatte:
>
>> in der 1. Version zum Beispiel wird ja grundsätzlich
>> nach isr_oc0b auch noch isr_watchdog abgearbeitet.
Wie kann das sein? isr_watchdog existiert in der "ersten Version" (vom
TO als Version A bezeichnet) doch garnicht.
Was also steht auf diesem Vektor wirklich?
> isr_watchdog existiert in der "ersten Version" (vom
> TO als Version A bezeichnet) doch garnicht.
In der "Monitor-Version" nicht, das stimmt, aber in der "Anhang-Version"
- war wohl ein copy&paste-Versehen von Stephan E.
an Stephan E.:
Zwei Anmerkungen (falls da Unkenntnis bestehen sollte):
- 'reti' wird zwar mit "Return from interrupt" beschrieben, ist aber
lediglich ein 'ret', welches zusätzlich das 'Global Interrupt Flag' in
SREG setzt.
- größere Programmblöcke können so auskommentiert werden: 1 | /*
| 2 | dies wird vorlaeufig stillgelegt:
| 3 | disable_overflow:
| 4 | push rmp
| 5 | in rmp, TIMSK0
| 6 | andi rmp, (0<<TOIE0)
| 7 | out TIMSK0, rmp
| 8 | pop rmp
| 9 | ret
| 10 | */
|
Vielleicht der falsche Controller im Simulator eingestellt? Denn der
wird nicht automatisch aus dem Editor übernommen.
Ich habs gerade mal probiert, der Timer bleibt nach dem ersten
ISR-Aufruf im Simulator stehen!
Falk B. schrieb:
> Ich habs gerade mal probiert, der Timer bleibt nach dem ersten
> ISR-Aufruf im Simulator stehen!
Welcher Simulator? V1 oder V2?
Mit V1 gehts auch nicht. Und das Interrupt-Flag wird verspätet gesetzt,
zumindest in der Anzeige der IO Register, da läuft die ISR schon ;-)
Aber auch bei Prescaler 1 bleibt der Timer stehen. Ein Bug im
AVR-Studio.
Falk B. schrieb:
> Mit V1 gehts auch nicht. Und das Interrupt-Flag wird verspätet gesetzt,
> zumindest in der Anzeige der IO Register, da läuft die ISR schon ;-)
> Aber auch bei Prescaler 1 bleibt der Timer stehen. Ein Bug im
> AVR-Studio.
Der V1-Simulator ist ziemlicher Rotz. Der bildet nichtmal die MCU
vollständig korrekt ab. Der V2 hingegen tut wenigstens dies, hat aber
auch immer noch ziemliche Mankos bezüglich der Peripherie. Für einen
Tiny13 sollte es aber eigentlich reichen, der hat ja nun nicht sehr viel
an Peripherie und im konkreten Fall ist nur Timer0 und Watchdog
involviert.
Aber klar: Ein Bug im Simulator wäre definitiv möglich. Kann man am
einfachsten dadurch umgehen, dass man halt echte Hardware verwendet...
Zunächst mal vielen Dank für die Antworten.
Was ich daraus mitgenommen habe:
- ISRs werden ab sofort nur noch per rjmp aufgerufen, nicht mehr per
rcall.
- Mehrzeilige Kommentare wie in z.B. c++ oder JavaScript möglich (wußte
ich bei assembler tatsächlich noch nicht)
- Der Timer-stop könnte tatsächlich ein Bug sein - ein extra Dank an die
Tester. Ggf. kann ich das ja mit einem extra Timer-init im Interrupt im
Quellcode "umgehen", mal schau'n.
Stephan E. schrieb:
> - Mehrzeilige Kommentare wie in z.B. c++ oder JavaScript möglich (wußte
> ich bei assembler tatsächlich noch nicht)
Der Avr-Asembler stellt (neben den C-Style-Kommentaren) auch eine
C-Style Präprozessor zur Verfügung. Das erleichtert auch manches...
Stephan E. schrieb:
> Habe ich mal getestet, scheint beides zu funktionieren.
Als Assemblerprogrammierer sollte man schon die Bedeutung der einzelnen
Befehle gelesen und verstanden haben. Sonst funktionieren Deine
Programme nur zufällig bzw. meistens nicht.
Zwischen JMP und CALL besteht ein gewaltiger Unterschied.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|