Forum: Mikrocontroller und Digitale Elektronik AT91SAM7 -> Interrupts verschachteln (nested IRs)


von Tom W. (nericoh)


Lesenswert?

Hallo zusammen,

zwar habe ich hier im Forum einige wenige Beiträge gefunden, die auch 
nested IRs beim AT91SAM7 behandeln, allerdings mit etwas anderer 
Zielsetzung. Falls ich doch einen passenden Thread übersehen haben 
sollte, bitte ich um kurze Info!

Ich verwende das RealView MDK / uVision

Folgendes möchte ich tun:

mit dem PIT des AT91 möchte ich einen Interrupt auslösen. Da es 
passieren kann, dass erneut ein PIT-IR erzeugt wird, bevor die vorherige 
ISR abgearbeitet ist, möchte ich es zulassen, dass diese ISR mehrfach 
vom AIC aufgerufen werden kann (mir ist klar, dass das zunächst 
ungewöhnlich und nach Pfusch klingt, es steckt aber eine wohldurchdachte 
Firmwarearchitektur dahinter, falls es wen interessiert, erkläre ich ich 
demjenigen das auch gerne näher;p).

Soweit meine bisherige Recherche in Datenblättern, Foren, Handbüchern 
etc. ergeben hat, beherrscht der AT91 grundsätzlich verschachtelte 
("nested")IRs. Die entscheidenden Fragen sind:
- kann ich auch mehrere IRs von gleicher Prio verschachteln ?
- falls ja, ist es darüber hinaus möglich, dass über eine einzige ISR zu 
tun (die dann eben mahrach verschachtelt wird)?

Das einfach zyklische Anspringen der ISR und auch der Rücksprung 
funktionieren bereits Problemlos, nur eben die Verschachtelung klappt 
(noch) nicht. Bei Bedarf poste ich auch gerne den Code, mit dem ich das 
bisher realisiere.

Wäre wahnsinnig dankbar, wenn mir dabei jemand weiterhelfen könnte, da 
ich das dringend für meine Bachelorarbeit benötige und mein Zeitrahmen 
äußert knapp bemessen ist.

MfG,
neri aka Tom

von Robert T. (robertteufel)


Lesenswert?

Hallo Tom,

das Stichwort, das Du benoetigst fuer die Suche ist "reentrant" und ist 
eine Stufe kniffliger als ein Nested Interrupt
Nur mal ein Link zum Wiki
http://en.wikipedia.org/wiki/Reentrant_%28subroutine%29

Hoffe das hilft Dir weiter in der Suche, ist allerdings nicht ohne!

Gruss, Robert

von Tom W. (nericoh)


Lesenswert?

Hi Robert,

ich danke dir schonmal für den Hinweis! Werde mich da morgen wieder 
intensiv mit beschäftigen - Kniffliges bin ich mittlerweile gewohnt;)

MfG,
Tom

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Neben dem Atmel Interupt Controller, musst Du auch einige Besonderheiten 
des ARM7 Kerns kennen, um nested interrupts erfolgreich zu 
implementieren. Siehe dazu einige Beispielprogramme im MDK Verzeichnis. 
Deine Lieblingssuchmaschine könnte auch weiterhelfen.

Viel Erfolg
Marcus

von Tom W. (nericoh)


Lesenswert?

Nach einigen Weiteren Untersuchungen und Überlegungen komme ich zu dem 
Schluss, dass ich momentan noch ein viel grundlegenderes Problem habe:

laut Datasheet des AT91SAM7 soll in einer ISR zuallererst das "Interrupt 
Vector Register" (AIC_IVR) gelesen werden. Wenn ich das aber tue, bleibt 
der Controller an dieser Stelle hängen. Mir ist rätselhaft, warum er das 
tut.

Hier die relevanten Auszüge aus meinen Quellcode, sieht irgendjemand da 
draußen, was ich falsch mache?

1
//init the Advanced Interrupt Controller
2
*AT91C_AIC_IECR  = 0x00000002;  //enable peripheral ir
3
AT91C_AIC_SMR[1] = 0x00000021;  //prio of periph-ir, pos. edge triggered  
4
AT91C_AIC_SVR[1] = (AT91_REG) ISR_periph1; //set adress to branch to if PIT IR occurs
5
6
//init of the Periodic Internal Timer
7
*AT91C_PITC_PIMR   = ( 0x000FFFFF & ( TIMER_PARA_MicroCycle * (TIMER_PARA_SysClock / (16 * 1000) ) ) ) ;    //set cycle of timer
8
*AT91C_PITC_PIMR   |= 0x03000000;    //enable the timer and the corresponding IR
9
10
__irq void ISR_periph1 (void)           //executed every time a peripheral IR occurs
11
{
12
  //tempvar = *AT91C_AIC_IVR;         //sobald ich diesen Regzugriff mache, wird die nächste Codezeile nichtmehr ausgeführt -> warum bloß?
13
  
14
  SET_PWR_LED(ON);
15
      
16
  if ( *AT91C_PITC_PISR )          //IR caused by Periodic Interval Timer ?  
17
  {     
18
    uscount += TIMER_PARA_MicroCycle;
19
20
    if (uscount == 1000)        //when 1000us (=1ms) passed
21
    {  
22
      uscount = 0;          //reset mscounter
23
      task_1ms();
24
    }   
25
26
    *AT91C_AIC_ICCR    = 0x00000002;  //clear the IR-Bit
27
    tempvar   = *AT91C_PITC_PIVR;    //reading PIVR resets the timer ir
28
29
  }
30
31
  *AT91C_AIC_EOICR   = 0xFFFFFFFF;   //end of IR
32
}

Anmerkung: ich habe das hier stark verkürzt und vereinfacht, um besser 
nachvollziehbar zu machen, was grundsätzlich passiert.

Solange ich den Lesezugriff auf das IVR rauslasse, wird die ISR auch 
brav zyklisch aufgerufen.

von Tom W. (nericoh)


Lesenswert?

Halt, Kommando zurück, auf einmal bleibt er da nichtmehr hängen... Werde 
das erstmal eingehender untersuchen, bevor ich euch damit weiter 
behellige.

Gruß,
Tom

von Tom W. (nericoh)


Lesenswert?

Habe jetzt einen weiteren Beitrag zu der IVR-Problematik erstellt, da 
die Diskussion hier nicht hinpasst:

Beitrag "AT91SAM7 + uVsion3 IR-Handling"

Sobald ich das Problem geklärt habe, gehts hier weiter :)

Tom

von klaus (Gast)


Lesenswert?

> Soweit meine bisherige Recherche in Datenblättern, Foren, Handbüchern
> etc. ergeben hat, beherrscht der AT91 grundsätzlich verschachtelte
> ("nested")IRs. Die entscheidenden Fragen sind:
> - kann ich auch mehrere IRs von gleicher Prio verschachteln ?
> - falls ja, ist es darüber hinaus möglich, dass über eine einzige ISR zu
> tun (die dann eben mahrach verschachtelt wird)?

Bei Nested-Interrupts mußt du vor allem das SPSR sowie alle bentutzten 
Register auf den Stack legen bevor du Interrupts wieder zuläßt. Das ist 
notwendig damit dein Interrupt Handler reentrant wird. Für non-Nested 
Interrupts ist das nicht nötig, da hier einfach die Register-Bank 
geswitched wird (z.B. von IRQ nach FIQ). Bei Nested-Interrupts 
unterbricht aber beispielsweise ein IRQ einen anderen IRQ, d.h. es wird 
keine Register-Bank geswitched. Dadurch zerstört der zweite Aufruf die 
Register-Zustände des vorherigen Handlers kommt aller Regel einem 
Absturz der Software gleich...

von Tom W. (nericoh)


Lesenswert?

Ah, interessant, das war mir in der Form nicht bewusst. Ich bin davon 
ausgegangen, dass der gesamte Kontext einer ISR, die durch einen anderen 
ISR unterbochen wird, einfach auf dem Stack gesichert und anschließend 
wiederhergestellt wird, eben so als würde eine normale Funktion 
unterbrochen. Und wenn das so wäre, wäre ja eine (mehrfache) 
Verschachtelung kein Problem, solange der Stack nicht überläuft. Kann 
mir jemand was dazu sagen, warum das so ist wie von Klaus beschrieben? 
Mir leuchtet noch nicht ein, warum damit nicht einfach so verfahren wird 
wie mit Sprung und Rücksprung bei einer regulären Funktion...

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Tom W. schrieb:
> Kann mir jemand was dazu sagen, warum das so ist wie von Klaus
> beschrieben?

RISC. Mache die einfachen Sachen einfach und die komplizierten Dinge
möglich.

Bei Cortex-M hat man das anders gemacht und lässt hier den Prozessor
selbständig seinen Kontext sichern.

> Mir leuchtet noch nicht ein, warum damit nicht einfach so verfahren
> wird wie mit Sprung und Rücksprung bei einer regulären Funktion...

Wird es ja -- fast. Nur ist beim Funktionsaufruf der Zeitpunkt
bekannt, zu dem der Kontext gesichert werden muss. Der Compiler
erzeugt den notwendigen Code. Bei einem Ereignis kann das naturgemäß
nicht erfolgen und der Entwickler muss den Code selbst implementieren.

Gruß
Marcus

von klaus (Gast)


Lesenswert?

Tom W. schrieb:
> Mir leuchtet noch nicht ein, warum damit nicht einfach so verfahren wird
>
> wie mit Sprung und Rücksprung bei einer regulären Funktion...

Wenn ein Interrupt auslöst wird das CPSR das ja deine Flags enthält in 
das SPSR des jeweiligen Modus (also der neuen Register-Bank) kopiert. 
Unter normalen Umständen muss der Interrupt Handler es nicht extra 
sichern, da wenn z.B. ein IRQ ausgelöst hat IRQ Interruts gesperrt 
werden. Wenn du IRQs im Interrupt handler nun wieder einschaltest, würde 
aber ein neuer IRQ den im SPSR Register gespeicherten Wert wieder 
überschreiben.


PS: Ich hoffe ich habe mich nicht mißverständlich ausgedrückt. Nicht 
alle Register werden umgeschaltet (sind also auf separaten 
Register-Bänken); eine Übersicht ist z.B. hier:

http://public.beuth-hochschule.de/~heineman/ESL6/ARM-Interrupt.pdf

von Martin (Gast)


Lesenswert?

... Bei Nested-Interrupts mußt du vor allem das SPSR sowie alle 
bentutzten
Register auf den Stack legen bevor du Interrupts wieder zuläßt. ...

Ist das nicht die Aufgabe des Compilers?

von klaus (Gast)


Lesenswert?

> Ist das nicht die Aufgabe des Compilers?

Wenn es entsprechende Optionen gibt über die entsprechender Entry- und 
Exit-Code erzeugt werden kann dann ja. Non-Nested interrupts werden aber 
wohl dann die Default-Einstellung sein...

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Martin schrieb:
> ... Bei Nested-Interrupts mußt du vor allem das SPSR sowie alle
> bentutzten
> Register auf den Stack legen bevor du Interrupts wieder zuläßt. ...

Und den Mode wechseln und dessen Kontext sichern, freilich nachdem man
das Stack alignment überprüft hat, etc. Kann man sich ja als inline
Assembler Makro schreiben.

> Ist das nicht die Aufgabe des Compilers?

Das Dumme ist nur, das Teile dieses erheblichen Overheads eben nicht
immer notwendig sind und eine Syntaxerweiterung, die dieses Framework
automatisch generiert, in einer gegebenen Situation nicht passend
wäre.

Gruß
Marcus

von (prx) A. K. (prx)


Lesenswert?

Es zeigt sich darin das Alter der Architektur und ihr Designziel.

Das Interrupt-Konzept der ARMs ist noch fast identisch mit der ersten 
Version davon aus Mitte der 80er (erst die Cortexe brechen damit) und 
beim Design dieses ersten ARM hatte niemand im Auge, dass daraus sowas 
wie ein Standard für 32-Bit Controller mit einer Lebensdauer von 
mindestens 3 Jahrzehnten werden könnte. Der war nur als Triebwerk 
bestimmter Spiel&Lern-Computer konzipiert worden.

Die andere Kursichtigkeit, der nur 64MB adressierende Program Counter 
(R15 = 24-Bit PC und 8-Bit PSR), wurde bald repariert. Die 
Fehlkonstruktion bei den Interrupts blieb. Leider. Warum man das damals 
nicht gleich mit reparierte ist mir ein Rätsel.

Mit RISC-Sparsamkeit hat das nur insofern zu tun, als man offenbar 
meinte, für den Einsatzzweck mit 2 Interrupt-Prioritäten ohne Nesting 
auszukommen. Sonderlich komplex wäre ein Konzept mit einfacher 
Unterstützung von Nesting nicht gewesen. Es hätte völlig ausgereicht, 
bei Interrupts den PC nicht in R14 sondern in einem ausschliesslich 
dafür reservierten Spezialregister zu retten, vgl. SPSR.

von Maxx (Gast)


Lesenswert?

Marcus Harnisch schrieb:
> Und den Mode wechseln und dessen Kontext sichern

Wollte ich nochmal herausheben: Nicht vergessen den Mode zu wechseln!

Neben dem SPSR wird auch R14_irq bzw LR_fiq (die jeweiligen LR) 
überschrieben. Damit wird ein BL innerhalb einer unterbrechbaren 
IRQ-Mode-Routine zum Stolperstein, weil die Rücksprungaddresse verloren 
gehen kann.

von (prx) A. K. (prx)


Lesenswert?

Man kann das auch brav bei ARM nachlesen: 
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka11008.html

Ein anderer Ansatz besteht darin, als ISR zunächst einen zentralen in 
Assembler geschriebenen Interrupt-Handler anzuspringen, der diesen 
Verwaltungskram erledigt, und darin dann den vom Interrupt-Controller 
gelieferten Vektor aufzurufen. Bremst ISRs ohne Nesting etwas ab, man 
wird damit aber völlig unabhängig von irgendwelchem Compiler-Zauber weil 
die ISRs nun wie bei den Cortexen ganz normale C Funktionen sind.

von gerhard (Gast)


Lesenswert?

hallo tom,
ich würde dir empfehlen die atmel beispiele (software packages) mal 
anzusehen.
in board_cstartup_keil.s findest du den entsprechenden irq-handler der 
auch nested interrupt unterstützt.

gruss
gerhard

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

A. K. schrieb:
> Das Interrupt-Konzept der ARMs ist noch fast identisch mit der ersten
> Version davon aus Mitte der 80er (erst die Cortexe brechen damit)

Nein, nur Cortex-M. Bei den anderen hat sich da nichts geändert.

> Warum man das [Exception Handling] damals nicht gleich mit reparierte ist
> mir ein Rätsel.

Ich vermute, dass der Verlust der Kompatibilität als zu schmerzhaft 
eingeschätzt wurde, vor allem, da durchaus praktikable Workarounds 
existieren. In der v6 Architektur hat man schließlich einige Befehle 
ergänzt (SRS, CPS und RFE), die den top-level handler erheblich 
vereinfachen. In Applikationsprozessoren, die letztlich alle ARM 
Prozessoren zu ihrer Zeit waren, sind die Kosten dieser Einschränkung 
vergleichsweise gering.

Erst mit dem Cortex-M3 hat man direkt auf den Microcontroller Markt 
gezielt, wodurch man die Änderung des Modells rechtfertigen konnte.

--
Marcus

von (prx) A. K. (prx)


Lesenswert?

Marcus Harnisch schrieb:

> Ich vermute, dass der Verlust der Kompatibilität als zu schmerzhaft
> eingeschätzt wurde,

Sicher, aber die ging mit der Abkehr von der 26-Bit Adressierung und der 
deshalb neuen Register CPSR und SPSR sowieso schon vor die Hunde. Das 
wäre der exakt richtige Zeitpunkt für ein analoges Register SPC gewesen. 
Danach nicht mehr, das stimmt.

> Erst mit dem Cortex-M3 hat man direkt auf den Microcontroller Markt
> gezielt, wodurch man die Änderung des Modells rechtfertigen konnte.

Mag auch damit zusammenhängen, dass dies m.W. die einzigen Cores ohne 
ARM-Modus sind, somit eine Kompatibilität zum bisher zwangsläufig darin 
landenden ISR-Code dazu ohnehin nicht drin war.

von Tom W. (nericoh)


Lesenswert?

Danke erstmal für die vielen Gedanken und Anregungen zum Thema!

Die nested IRs habe ich inzwischen am Laufen, werde jetzt noch 
versuchen, reentrant zu implementieren. Ich hoffe ich werde 
anschließend die Zeit finden, hier ein HowTo zu posten, damit alle, die 
auch mal vor diesem Problem stehen, sich da nicht so lange mit 
aufhhalten müssen ;p

Tom

von Tom W. (nericoh)


Lesenswert?

Falls noch jemand nach der Lösung suchen sollte:
der Hinweis von prx auf das ARM Infocenter hat die Lösung gebracht

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka11008.html

Ich stehe auf Anfrage auch gerne zur Unterstützung zur Verfügung.

Tom

von Lothar (Gast)


Lesenswert?

Auch Cortex-A und Cortex-R nutzen noch das IRQ/FIQ Konzept. Re-entrante 
Interrupts sind auch nicht schwierig. Der IRQ Handler sieht doch am 
Anfang welcher Interrupt kommt, kann prüfen ob der vorher schon lief und 
unterbrochen wurde, und entsprechend den Kontext sichern. Üblich ist für 
die IRQ eine Queue anzulegen, meist doppelt verkettete Liste, wo man 
sich die aktiven und pending IRQ merkt und auch nach Prioriäten 
rumschiebt. Damit sind praktisch unbegrenzt viele Interrupts und 
Instanzen möglich, solange der Stack reicht. Beim FIQ Handler sollte man 
keine Instanzen zulassen.

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
Noch kein Account? Hier anmelden.