Interrupt-Zeitverhalten AVR

26/05/2011 - 17:31 von Heiko Nocon | Report spam
Hallo,

ich habe offensichtlich ein Problem mit einer race condition und komme
nicht dahinter, wo die her kommt. Also zuerst mal eine so weit wie
möglich vereinfachte Beschreibung des Systems.

Es gibt einen Interrupt von Timer2, der immer möglichst zeitnah
ausgeführt werden soll. Deswegen kommen die normalen Mechanismen zur
Serialisierung der anderen Interrupts nicht in Frage, sie würden die
globale Interruptfreigabe zu lange blockieren.

Neben diesem T2-Interrupt gibt es noch zwei weitere Interruptquellen,
ADC und UDRE. Deren Handler greifen auf gemeinsame Daten zu. Der jeweils
andere Interrupt muß also verhindert werden, wenn einer der Handler
ausgeführt wird, außerdem muß auch eine erneute Auslösung eines bereits
aktiven Interrupts verhindert werden, solange dessen Handler noch làuft.
Außerdem gibt es noch eine Steuerung der Interruptfreigabe für UDRE. Im
UDRE-Handler kann bei Eintreten bestimmter Situationen entschieden
werden, daß die Auslösung des UDRE-Int nicht mehr lànger nötig ist, im
ADC-Handler hingegen kann die Entscheidung fallen, daß UDRE-Auslösung
doch wieder nötig wird.

Und als wàre das zusammen nicht schon kompliziert genug, ist die ADC
nicht freilaufend, sondern wird vom freilaufenden Timer1 (ohne
Interrupthilfe, sondern über den Autotrigger-Mechanismus) getriggert und
das auch nur dann, wenn der T2-Int in bestimmten Situationen sozusagen
eine Freigabe dafür erteilt.

Die Idee zur Realisierung des Systems bestand darin, die vorlàufige
Interruptsperrung, die normalerweise über SREG_I automatisch bei
Interruptauslösung realisiert wird, so schnell wie möglich durch Sperren
der einzelnen Interrupt-Enable-Flags zu ersetzen. Die Handler von ADC
und UDRE sehen also in etwa so aus (ist ein Device mit MMIO, also nicht
über die sts wundern, wo man eigentlich out erwarten würde, außerdem
habe ich den ganzen Kram mit dem Retten von Registern und Flags
weggelassen, er entspricht dem üblichen Vorgehen)

adc_int:
sts UCSR0B,UARTDISABLE ;löscht UDRIE0
sts ADCSRA,ADCDISABLE ;löscht ADIE und ADATE
mov ADCCTRL,ADCDISABLE ;setzt diesen Zustand
als "default" für die Laufzeit des Handlers.
sei
;ab hier müßte meiner Meinung nach immer noch sichergestellt sein, daß
;einerseits weder eine erneute Auslösung von ADC noch eine Auslösung von
;UDRE erfolgen kann, andererseits aber der T2-Int wieder möglich ist.
;-
;Spiel mit den gemeinsamen Daten
;-
;je nach Sachlage kann dabei die
;Entscheidung fallen, daß die Dienste
;von UDRE wieder benötigt werden, dann
;passiert das (ohne Berücksichtigung
;eines vorigen Zustands) durch:
sbr UARTCTRL,1<<UDRIE0
;das bewirkt natürlich noch garnix, erst
;nach Abschluß aller Operationen an den
;gemeinsam genutzten Daten erfolgt ein:
sts UCSR0B,UARTCTRL
;ab hier kann der UDRE-Int wieder erfolgen
;bzw. wird oft auch sofort ausgelöst.
;--
;Weitere Spiele
;--
;Das, was hier kommt, ist relativ lànglich
;aber kurz genug, daß die Gesamtrechenzeit
;problemlos ausreicht, auch wenn der Kram
;immer wieder durch Timer- oder UDRE-Ints
;unterbrochen wird. Irgendwann ist der
;Handler jedenfalls fertig und dann
;passiert folgendes:
sbr ADCCTRL,1<<ADIE
sts ADCSRA,ADCCTRL
;das setzt sicher ADIE, ADATE aber nur u.U.
;siehe t2_int!
ret

udre_int:
sts UCSR0B,UARTDISABLE ;löscht UDRIE0
sts ADCSRA,ADCDISABLE ;löscht ADIE und ADATE
sei
;ab hier müßte meiner Meinung nach immer noch sichergestellt sein, daß
;einerseits weder eine erneute Auslösung von UDRE noch eine Auslösung
;von ADC erfolgen kann, andererseits aber der T2-Int wieder möglich ist.
;-
;Spiel mit den gemeinsamen Daten
;-
;je nach Sachlage kann dabei die
;Entscheidung fallen, daß die Dienste
;von UDRE nicht mehr benötigt werden, dann
;passiert das (ohne Berücksichtigung
;eines vorigen Zustands):
cbr UARTCTRL,1<<UDRIE0
;nach ein paar weiteren Spielchen
;ist er dann fertig und macht dies:
sts UCSR0B,UARTCTRL
;UDRE wieder freigeben oder auch nicht
sts ADCSRA,ADCCTRL
;ADC-Geschicht in den "aktuellen" Zustand
;versetzen. Was gerade aktuell ist, bestimmen der
;T2- und ADC-Handler
ret

;Jetzt fehlt noch der T2-Int. Der ist eigentlich ganz normal konstruiert
;und kann aus Sicht der anderen beteiligten Handler praktisch jederzeit
;auftreten. Ausnahme ist die Zeitspanne zwischen der Auslösung eines
;der konkurrierenden Ints und dem bald darauf folgenden SEI.
t2_int:
-
Irgendwelche Spiele
-
;Bei bestimmten Spielstànden entscheidet
;der Handler, daß beim nàchsten Überlauf von
;T1 die ADC-Wandlung
;gestartet werden soll, bei deren Abschluß dann
;irgendwann noch spàter der ADC-Int erfolgt
sbi TIFR1,TOV1
;Triggerflag zurücksetzen
sbr ADCCTRL,(1<<ADATE)
sts ADCSRA,ADCCTRL
;Autotrigger ermöglichen
reti

Als Anmerkung noch: ADCCTRL,UARTCTRL,ADCDISABLE und UARTDISABLE sind
natürlich global reservierte Register, die nirgendwo sonst benutzt
werden und beim Start des Systems mit sinnvollen Werten vorbelegt
wurden.

Soweit die Idee und der Kern der Implementierung. Wàre schön, wenn's wie
geplant funktionieren würde, tut es aber nicht. Lustig ist: Ein paar in
den ADC-Inthandler eingestreute nops sorgen dafür, daß die Sache
problemlos làuft (übrigens làuft sie auch im Simulator problemlos), es
sieht also sehr nach einer race condition aus. Mir ist halt nur nicht
klar, wo die herkommen soll. Meiner Meinung nach ist die einzige Chance
dafür eine (fast) gleichzeitige Auslösung von ADC- und
UDRE-Int und das auch nur unter der Annahme, daß bei einem einmal
"pending" gewordenem Int ein nachtràgliches Löschen des entsprechenden
Interrupt-Enable-Flags die Auslösung des Int bei der nàchsten Chance
(also nach dem SEI) nicht mehr verhindern kann. Ist das so? Die
Beschreibungen im Datenblatt würde ich mal als ein "nein" deuten und
beim UDRE bin ich nach diversen Tests mir auch ziemlich sicher, daß es
nicht der Fall ist. Beim ADC-Int hingegen scheint aber diese Möglichkeit
zu bestehen.
Und wenn's das nicht ist: Aber wo steckt dann der Fehler?

Vielen Dank im Voraus für's Mitdenken.
 

Lesen sie die antworten

#1 Olaf Kaluza
26/05/2011 - 17:57 | Warnen spam
Heiko Nocon wrote:

Und wenn's das nicht ist: Aber wo steckt dann der Fehler?
Vielen Dank im Voraus für's Mitdenken.



Also ich hab irgendann ab der Haelfte nicht mehr verstanden was du
eigentlich willst.

Das bedeutet vermutlich das du entweder einen Prozessor verwenden
solltest der Interruptprioritaeten kennt, oder aber dir eine andere
einfachere Loesung fuer dein Problem einfallen lassen solltest.

Naja, oder ich bin zu bloed. :-)

Olaf

Ähnliche fragen