Forum: Mikrocontroller und Digitale Elektronik Cortex-M4 dynamisch IRQ prio ändern -> hardfault


von Vincent H. (vinci)


Lesenswert?

Grüß euch

Ich hab grad ein kurioses Problem mit einem STM32L431. Und zwar würde 
ich gerne die Priorität eines laufenden Interrupts ändern. Das sieht in 
Pseudo-Code folgendermaßen aus:
1
void isr() {
2
  // do stuff
3
  NVIC_setPriority(IRQn, 10u);
4
  // do some more stuff
5
  NVIC_setPriority(IRQn, 0u);
6
}

Leider führt das ganze im laufenden Betrieb zu einen Hardfault. Zeit und 
Ort wann der Hardfault auftritt scheinen recht zufällig übers gesamte 
Programm verteilt zu sein.

Ein Blick auf die Fault Status Register verraten, dass im UFSR Register 
das INVPC Bit gesetzt ist:
1
INVPC Invalid PC load UsageFault, caused by an invalid PC load by EXC_RETURN: 
2
0 = no invalid PC load UsageFault 
3
1 = the processor has attempted an illegal load of EXC_RETURN to the PC, as a result of an invalid context, or an invalid EXC_RETURN value. When this bit is set to 1, the PC value stacked for the exception return points to the instruction that tried to perform the illegal load of the PC.

EXC_RETURN ist dabei stets 0xFFFFFFE9:
1
0xFFFFFFED Return to Thread mode, exception return uses floating-point state from PSP and execution uses PSP after return.

Leider erschließt sich mir nicht ganz wo genau jetzt das Problem liegt 
und ich hab auch keine Idee wie ich das näher eingrenzen soll.

Ebenfalls interessant ist, dass folgende Variante, die den Interrupt 
vorm Änderung der Priorität sperrt, funktioniert:
1
void isr() {
2
  // do stuff
3
  NVIC_disableIRQ(IRQn);
4
  NVIC_setPriority(IRQn, 10u);
5
  NVIC_enableIRQ(IRQn);
6
  // do some more stuff
7
  NVIC_disableIRQ(IRQn);
8
  NVIC_setPriority(IRQn, 0u);
9
  NVIC_enableIRQ(IRQn);
10
}

Irgendwer eine Idee?

von Johnny B. (johnnyb)


Lesenswert?

Vincent H. schrieb:
> Irgendwer eine Idee?

Klingt vielleicht doof, aber ich würde sowas gar nicht machen.
Welchem Zweck soll es denn dienen, zur Laufzeit die Prioritäten zu 
ändern?

von Vincent H. (vinci)


Lesenswert?

Zum Beispiel wenn "// do stuff" wichtig ist, "// do some more stuff" 
aber weniger wichtig als was anderes.

von Stefan F. (Gast)


Lesenswert?

Vielleicht darf man die Prio nicht innerhalb einer ISR ändern.

von Vincent H. (vinci)


Lesenswert?

Stefan ⛄ F. schrieb:
> Vielleicht darf man die Prio nicht innerhalb einer ISR ändern.

Ich bilde mir ein der ARMv7-M kann das.

von Jo mei (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Vielleicht darf man die Prio nicht innerhalb einer ISR ändern.

Ohne es genauer zu wissen stelle ich mir vor dass der NVIC
eine Art eigenen Stack verwaltet, und dieser Stack kommt
während einer Änderung der Prioritäten im Interrupt-Kontext
durcheinander. So, dass es krachen muss ....

Johnny B. schrieb:
> Klingt vielleicht doof, aber ich würde sowas gar nicht machen.

Ich auch nicht.
Wo es doch durchaus machbar ist dies im Nicht-Interrupt-Kontext
zu setzen.

von Stephan (Gast)


Lesenswert?

Hi Vincent,

Nachfrage:
Du möchtest erreichen, dass der //do more stuff
mit einer geringeren Prio
[vom RTOS drumherum?] ausgeführt wird??

Kennst Du z.B. diesen Text?:

https://community.arm.com/developer/ip-products/system/b/embedded-blog/posts/cutting-through-the-confusion-with-arm-cortex-m-interrupt-priorities

Vg Stephan

von Stefan F. (Gast)


Lesenswert?

Ist denn klar, dass 0 die höchste Priorität ist?

von Vincent H. (vinci)


Lesenswert?

Den Text kenn ich, RTOS nutze ich keines. Ein Workaround via PendSV wäre 
wohl denkbar um den selben Effekt zu erzielen, aber ich würd auch gerne 
verstehn wieso der Hardfault triggered...

von Stephan (Gast)


Lesenswert?

Vincent H. schrieb
> um den selben Effekt zu erzielen,

Welchen Effekt denn genau?

von Vincent H. (vinci)


Lesenswert?

Stephan schrieb:
> Vincent H. schrieb
>> um den selben Effekt zu erzielen,
>
> Welchen Effekt denn genau?

Dass die Interrupt Priorität für "// do some more stuff" gesenkt wird 
und Interrupts höherer Priorität diesen Teil unterbrechen können.

von Johnny B. (johnnyb)


Lesenswert?

Vincent H. schrieb:
> Stephan schrieb:
>> Vincent H. schrieb
>>> um den selben Effekt zu erzielen,
>>
>> Welchen Effekt denn genau?
>
> Dass die Interrupt Priorität für "// do some more stuff" gesenkt wird
> und Interrupts höherer Priorität diesen Teil unterbrechen können.

Ach Du meinst Nested-Interrupts. Das ist natürlich nochmals eine völlig 
andere Geschichte und würde ich noch weniger anwenden als während der 
Laufzeit nur die Prioritäten zu ändern.
https://developer.arm.com/docs/dui0553/latest/cortex-m4-peripherals/nested-vectored-interrupt-controller

Du schreibst, dass Du noch kein RTOS einsetzt; lasse Dir doch von den ST 
Tools mal FreeRTOS einbinden. Damit lässt sich super arbeiten und die 
meisten Anwendungsfälle elegant abdecken.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Mglw. musst Du eine Barriere setzen.

von Stephan (Gast)


Lesenswert?

Johnny B. schrieb:
> Vincent H. schrieb:
>> Stephan schrieb:
>>> Vincent H. schrieb
>>>> um den selben Effekt zu erzielen,
>>>
>>> Welchen Effekt denn genau?
>>
>> Dass die Interrupt Priorität für "// do some more stuff" gesenkt wird
>> und Interrupts höherer Priorität diesen Teil unterbrechen können.
>
> Ach Du meinst Nested-Interrupts. Das ist natürlich nochmals eine völlig
> andere Geschichte und würde ich noch weniger anwenden als während der
> Laufzeit

"The Guru" schreibt aber hier explizit (im M3/M4 habe ich auf die 
Schnelle auch nix gegenteiliges gefunden)
https://community.arm.com/developer/ip-products/processors/f/cortex-m-forum/2683/changing-interrupt-priority-to-prevent-nesting
das es tatsächlich mit ARMv7 möglich ist, das dynamisch IN der ISR zu 
machen... (ob das jetzt Sinn macht ist eine andere Frage)...

Der HardFault kommt also möglicherweise woanders her:
Mein Tippp (s.o): Doch ein Stack problem? oder der NVIC kommt eben doch 
durcheinander, wenn man in einer ISR > 0 dann wie Vincent die höchste 
Prio mit "NVIC_setPriority(IRQn, 0u)" setzt...

Stephan

von Vincent H. (vinci)


Lesenswert?

Das interessante ist, dass ich so eine Änderung der Prioritäten bereits 
erfolgreich in die andere Richtung genutzt habe. Ich hab in einem Teil 
meines Codes eine Interrupt gesteuerte State-Machine die zwischen 2x 
Interrupts die Priorität ganz hochdreht weil ein einzelner State-Wechsel 
extrem zeitkritisch ist, der Rest aber eher "best effort".

Dort geschieht das ändern der Priorität aber halt nur 1x, quasi
1
void state0() {
2
  NVIC_setPriority(IRQn, 0u);
3
}
4
5
void state1() {
6
  NVIC_setPriority(IRQn, normal_prio);
7
}

Das funktioniert tadellos.


Ruediger A. schrieb:
> Mglw. musst Du eine Barriere setzen.

Keine Änderung.
1
void isr() {
2
  // do stuff
3
  NVIC_setPriority(IRQn, 10u);
4
  __DSB();
5
  __ISB();
6
  // do some more stuff
7
  NVIC_setPriority(IRQn, 0u);
8
  __DSB();
9
  __ISB();
10
}

von Bernd K. (prof7bit)


Lesenswert?

Also ich meine ich hätte mal irgendwo gelesen man soll die Priorität 
setzen bevor man ihn enablet. Kanns aber nicht beschwören, hab aber 
auch noch nicht ausprobiert was passiert wenn man davon abweicht oder 
gar solche akrobatische Stunts wie Du da zu machen beabsichtigst 
durchführt.

Nur ein einziges mal in einer ganz dunklen Stunde als es draußen 
geblitzt und gedonnert hat hab ich mich mal selbst ertappt wie ich in 
den nächtlichen bläulichen Schein des Bildschirms getaucht und der 
Vernunft fast schon ganz entrückt aus einem Akt der puren Verzweiflung 
heraus in einem Interrupt auf den Systick warten wollte, dann dachte ich 
"Scheiße, das kann doch jetzt wohl nicht mein Ernst sein in Erwägung zu 
ziehen diesen IRQ zeitweise niedriger zu priorisieren nur um hier jetzt 
auf die Zeit warten zu können!?" - Ich habs schnell wieder verworfen und 
einen Tag später nach einigen tieferen Umbauten war das Problem auf die 
saubere Art gelöst.

: Bearbeitet durch User
von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Ok, noch ein paar Ideen:

1. Läuft ein Anderer ISR auf einer Priorität zwischen den beiden Prios, 
zwischen denen Du deinen faultenden ISR togglest? Wenn ja, kann man das 
testweise ändern (nur um herauszufinden, ob der Fault damit beeinflusst 
wird)?

2. Wenn Du die Prio nur herunter- aber nicht mehr heraufsetzt, tritt der 
fault dann auch noch auf?

3. (Variante)

void isr() {
  // do stuff
  NVIC_disableIRQ(IRQn);
  NVIC_setPriority(IRQn, 10u);
  NVIC_enableIRQ(IRQn);
  // do some more stuff
  NVIC_disableIRQ(IRQn);
  NVIC_setPriority(IRQn, 0u);
  NVIC_enableIRQ(IRQn);
}

Reicht es, das Disablen und enablen nur in einer der beiden Pfade zu 
machen, damit das Problem weggeht? Wenn ja in welchem?

Meine momentane Vermutung ist, dass das back-to-back chaining von ISRs 
durcheinandergerät. Wenn dem so ist, wird es nichts geben, was Du 
dagegen tun kannst, ausser eben das disablen (reicht Dir das nicht?).

von Theor (Gast)


Lesenswert?

Ruediger A. schrieb:
> Ok, noch ein paar Ideen:
>
> [...]
>
> Meine momentane Vermutung ist, dass das back-to-back chaining von ISRs
> durcheinandergerät. Wenn dem so ist, wird es nichts geben, was Du
> dagegen tun kannst, ausser eben das disablen (reicht Dir das nicht?).

Meine Vermutung geht in ungefähr die selbe Richtung.

Ich habe vielleicht was nicht verstanden oder überlesen, aber kann es 
sein, dass Du:

1. Die Priorität genau desjenigen Interrupts änderst, in dessen ISR Du 
Dich gerade befindest?

2. Du die Priorität eines momentan noch höherprioren oder niederprioren 
Interrupts änderst, der gerade ansteht.

3. Gibt es überhaupt Pending Interrupts, während Du das tust?

von Vincent H. (vinci)


Lesenswert?

Ruediger A. schrieb:
> Ok, noch ein paar Ideen:
>
> 1. Läuft ein Anderer ISR auf einer Priorität zwischen den beiden Prios,
> zwischen denen Du deinen faultenden ISR togglest? Wenn ja, kann man das
> testweise ändern (nur um herauszufinden, ob der Fault damit beeinflusst
> wird)?

Ja, auf so gut wie jeder Ebene zwischen 0 und 10 läuft ein anderer 
Interrupt. Theoretisch könnte ich alle Prios mal raufschieben ja.

> 2. Wenn Du die Prio nur herunter- aber nicht mehr heraufsetzt, tritt der
> fault dann auch noch auf?

Nein.

> 3. (Variante)
>
> void isr() {
>   // do stuff
>   NVIC_disableIRQ(IRQn);
>   NVIC_setPriority(IRQn, 10u);
>   NVIC_enableIRQ(IRQn);
>   // do some more stuff
>   NVIC_disableIRQ(IRQn);
>   NVIC_setPriority(IRQn, 0u);
>   NVIC_enableIRQ(IRQn);
> }
>
> Reicht es, das Disablen und enablen nur in einer der beiden Pfade zu
> machen, damit das Problem weggeht? Wenn ja in welchem?
>
> Meine momentane Vermutung ist, dass das back-to-back chaining von ISRs
> durcheinandergerät. Wenn dem so ist, wird es nichts geben, was Du
> dagegen tun kannst, ausser eben das disablen (reicht Dir das nicht?).

Es scheint dass das disablen/enablen an jener Stelle reicht wo wieder 
auf die höhere Prio geschalten wird.


Theor schrieb:
> Ruediger A. schrieb:
>> Ok, noch ein paar Ideen:
>>
>> [...]
>>
>> Meine momentane Vermutung ist, dass das back-to-back chaining von ISRs
>> durcheinandergerät. Wenn dem so ist, wird es nichts geben, was Du
>> dagegen tun kannst, ausser eben das disablen (reicht Dir das nicht?).
>
> Meine Vermutung geht in ungefähr die selbe Richtung.
>
> Ich habe vielleicht was nicht verstanden oder überlesen, aber kann es
> sein, dass Du:
>
> 1. Die Priorität genau desjenigen Interrupts änderst, in dessen ISR Du
> Dich gerade befindest?

Ja.

> 2. Du die Priorität eines momentan noch höherprioren oder niederprioren
> Interrupts änderst, der gerade ansteht.

Nein. Es wird nur die Prio des gerade aktiven IRQs geändert.

> 3. Gibt es überhaupt Pending Interrupts, während Du das tust?

Höchstwahrscheinlich ja.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Vincent H. schrieb:
> Ruediger A. schrieb:
>
>> 2. Wenn Du die Prio nur herunter- aber nicht mehr heraufsetzt, tritt der
>> fault dann auch noch auf?
>
> Nein.
>

das klingt fast so, als ob der ISR wieder ansteht, während er 
abgearbeitet wird und sich deswegen "selbst unterbricht." Ich denke 
nicht, dass der NVIC das abhandeln kann (Du hast ja z.B. im SCB ein 
Register für die Prio des momentan anstehenden Interrupts, was für keine 
Verschachtelung von ISRs ausserhalb der statischen Hierarchie spricht).

Was ist wenn Du den ISR nicht auf NVIC Ebene, sondern auf 
Peripherieebene abstellst während er seine Priorität hochsetzt? Kannst 
Du das Löschen des Interruptrequests in der Peripherie auf nach dem 
Hochstellen verschieben?

: Bearbeitet durch User
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.