Verfügbare Richtlinien für unterbrechungsgesteuerte Verarbeitung

Gibt es verfügbare Richtlinien für die Codemenge in einem kritischen Abschnitt der Interrupt-gesteuerten Verarbeitung?

Meine persönliche Faustregel lautet, dass der kritische Teil (dh der zwischen dem Deaktivieren von Interrupts und dem Aktivieren von Interrupts) jeder Interrupt-gesteuerten Verarbeitung nicht mehr als etwa ein Dutzend Codezeilen umfassen sollte (einschließlich der in jeder aufgerufenen Funktion/Bibliothek/Makro). ) und dass die Verarbeitung so linear wie möglich sein sollte.

Ich würde etwas in der Art erwarten:

disable_interrupts
if error_condition:
   set_error_flag
else:
   small_data_transfer
   set_ready_flag
enable_interrupts

Ich wurde jedoch gebeten, ein bestehendes Projekt zu untersuchen, bei dem einige der Interrupts Handler haben, bei denen die Anrufdiagramme nicht auf ein Blatt A1 passen (alles im kritischen Abschnitt).

Um dies zu verdeutlichen, handelt es sich um ein "Bare-Metal"-Projekt ohne implementiertes Betriebssystem/Scheduler - und alle Interrupt-Handler beginnen mit dem Deaktivieren aller Interrupts - d ein Deaktivierungs-/Aktivierungsabschnitt). Es gibt eine Vielzahl von Interrupts und Hardwareabhängigkeiten, aber soweit ich sehen kann, werden einige der ISRs Tausende von Zeilen innerhalb des "kritischen" Abschnitts ausführen.

Ich weiß, dass dies ein Problem ist und wirklich komplett neu geschrieben werden muss, aber ich kann keine Standards finden, einschließlich MISRA, auf die ich verweisen kann, anstatt nur " nach meiner Erfahrung " zu sagen - um das Projektmanagement zu überzeugen, auf das ich hinweisen muss einige anerkannte Standards.

Daher meine Frage: Kann mich jemand auf Standards oder Richtlinien hinweisen, die ich verwenden kann, um meine Erfahrung zu untermauern? (Oder liege ich natürlich total falsch).

Haben Sie nur eine einzige Unterbrechungspriorität? Ich weiß, dass ich einige Projekte durchgeführt habe, bei denen die gesamte Anwendung im Interrupt-Handler mit niedriger Priorität lebte. Es verhielt sich im Grunde wie eine Interrupt-getriggerte Hauptschleife. Unter der Annahme, dass Sie über ausreichende Interrupt-Prioritätsstufen verfügen, sehe ich nicht, warum diese Art von Struktur ein Problem darstellt.
Ein weiterer Kommentar wäre, dass ich einige Protokolldecoder geschrieben habe, die ziemlich groß waren, aber der tatsächliche Ausführungspfad durch den Decoder pro Interrupt war kurz. Ein Anrufpfaddiagramm dafür wäre meiner Meinung nach unverhältnismäßig groß und würde die tatsächliche Interrupt-Effizienz eher falsch darstellen.
Ihre Terminologie ist verwirrend. Einerseits scheinen Sie hauptsächlich über die ISR (Interrupt Service Routine) zu sprechen. Sie erwähnen aber auch immer wieder "kritischen Abschnitt", der normalerweise für die Abschnitte von Nicht-ISR-Code reserviert ist, in denen Interrupts deaktiviert sind, wodurch verhindert wird, dass die ISR ausgeführt wird. Kannst du deine Frage etwas präzisieren?
@DaveTweed - Die Frage wurde oben erweitert, aber im Grunde verwende ich in diesem Zusammenhang "Critical", um Teile der ISR zu bezeichnen, in denen alle anderen Interrupts deaktiviert sind.
Setzen Sie niemals ein printf() in einen Interrupt ... es ist eine relativ große Funktion.
Sie haben also ein System, das verschachtelte Interrupts zulässt, und es hört sich so an, als hätten die Designer des vorhandenen Codes dies nicht sehr sorgfältig geplant. Klingt nach Chaos. Wenn die kritischen Abschnitte in niederprioren Interrupts lang sind, wird die Reaktion des Systems auf die vermutlich wichtigeren hochprioren Interrupts verzögert. Ich wäre versucht, den vorhandenen Code beiseite zu legen und nur auf der Grundlage der externen Anforderungen eine eigene High-Level-Architektur für das Gesamtsystem zu entwickeln. Erst dann würde ich zum vorhandenen Code zurückkehren, um zu sehen, welche Teile möglicherweise wiederverwendbar sind.
@DaveTweed - Sehr wahr, aber ich brauche mehr als anakdotische Beweise, um das Management davon zu überzeugen, dass das vorhandene System wahrscheinlich nie das Maß an Zuverlässigkeit bieten wird, das sie suchen.
Alles, was Sie aus dem Internet erhalten, einschließlich dieser Website, wird vom Management als "anekdotisch" angesehen. Die einzige Möglichkeit, die benötigten harten Daten zu erhalten, besteht darin, die vorhandene Implementierung einem Stresstest zu unterziehen, um die Fehler aufzuzeigen.
Ich hatte gehofft, einige Verweise auf Spezifikationen oder Studien zu erhalten, auf die ich sie verweisen könnte. Belastungstests sind ein Problem, da es außer dem eingesetzten System keinen anderen Prüfstand gibt (schlechte Nachrichten, die ich kenne) und die Bereitstellung für Tests nicht praktikabel ist Option, da es an einem sehr herausfordernden Ort eingesetzt wird.
OK, wenn dies einmalig ist und es derzeit funktioniert, warum führen wir überhaupt diese Unterhaltung? "Wenn es nicht kaputt ist, repariere es nicht."
Weil es kaputt ist , indem es bei mäßiger Belastung zeitweise ausfällt, und ich gebeten wurde, es zu reparieren - schnell! Die Tatsache, dass es ungefähr 8 Mannjahre gedauert hat, um es in diesen Zustand zu versetzen ... Auch Einmalprodukte haben die Angewohnheit, überall von Marketingabteilungen verkauft zu werden, wie ich sicher bin, dass viele von uns die Erfahrung gemacht haben.
8 Mannjahre? Das ist eine teure Sauerei :)

Antworten (4)

Im Allgemeinen ist die Verteilung der CPU-Zeit zwischen ISR-Code und Nicht-ISR-Code sehr anwendungsabhängig.

Wenn die meiste Arbeit in Nicht-ISR-Code erledigt wird, dann ja, Sie möchten die ISRs im Allgemeinen so kurz wie möglich halten (aber nicht kürzer, um ein berühmtes Zitat zu paraphrasieren).

Ich habe jedoch harte Echtzeit-DSP-Anwendungen gesehen (und gebaut), in denen 90% der Arbeit in der ISR erledigt wird und der Nicht-ISR-Code nur Aufgaben behandelt, die nicht zeitkritisch sind, wie Konfiguration, Status und Benutzeroberfläche.

Beide Ansätze sind in tief eingebetteten Systemen vollkommen gültig.

Als allgemeine Richtlinie gilt, dass kritische Abschnitte so kurz wie möglich sein sollten.

Was Sie wirklich tun müssen, ist die Länge des kritischen Abschnitts zu berücksichtigen, wenn Sie die Planbarkeit des Aufgabensatzes überprüfen. Das hängt davon ab, wie Sie die Aufgaben planen, aber ich denke, es könnte so einfach sein, wie die Ausführungszeit des kritischen Abschnitts zur Ausführungszeit jeder Aufgabe mit niedrigerer Priorität hinzuzufügen.

Leider ist dies ein Bare-Metal-Projekt, kein OS/Scheduler und kein Priority-Handling-System – alles ist in kritischen Bereichen – sogar Housekeeping!
Sie benötigen kein Betriebssystem, keinen Planer oder kein System zur Prioritätsverwaltung, um eine Planbarkeitsanalyse durchzuführen, sie kann für jede Reihe von Aufgaben durchgeführt werden, die Vorrang verwenden. Vielleicht ist die Frage, die Sie sich bezüglich der Länge der kritischen Abschnitte stellen müssen, ... wen interessiert das? Geht etwas kaputt, wenn die kritischen Abschnitte zu lang sind?
Ja, Joe - lange Sperren, um Dinge zu beschädigen, da die anderen Interrupts gesperrt werden (sogar während der Haushaltsführung), sie ignoriert werden und Nachrichten beschädigt werden/verloren gehen.
Dann brauchen Sie eine Planbarkeitsanalyse! Sie müssen sicherstellen, dass alle Aufgaben in der gewünschten Zeit beantwortet werden.
Und das ist natürlich eines der Probleme bei langen komplexen ISRs – dass es aufgrund der Komplexität oft unpraktisch ist, eine solche Analyse in einem angemessenen Zeitrahmen durchzuführen.

Ich denke, Sie müssen die Ausführungszeit des unterbrechungsfreien Codes bezüglich der schnellsten Reaktionszeit (Latenz) und Variabilität dieser Zeit (Jitter) berücksichtigen, die Sie vom Mikrocontroller benötigen. Es mag nicht intuitiv sein, was das ist – es kann etwas sein, das „dringend“, aber nicht „wichtig“ ist.

Zum Beispiel musste ein Controller, den ich vor einigen Jahren entworfen hatte, mit RS-232-Eingängen umgehen, den Ausgang mit dem Steueralgorithmus aktualisieren, den Dual-Slope-ADC steuern, ein LED-Display und eine Tastatur scannen (einschließlich Entprellen) und schreiben ein langsames EEPROM, und um einen elektromagnetischen Piepser zu steuern (und wahrscheinlich habe ich noch ein paar andere Dinge, die ich vergessen habe). Es stellte sich heraus, dass die kritischste Reaktionszeit die periodische Unterbrechung des Piepsers (alle 250 us) war, gefolgt von der LED-Anzeige, hauptsächlich aus kosmetischen Gründen. Wenn also 10 % Jitter im Pieper-Toggle akzeptabel waren, sind das 25 usec an unterbrechungsfreiem Code (was nicht viele Anweisungen waren, aber genug, um bestimmte Dinge atomar zu machen). Dieser spezielle Prozessor unterstützte verschachtelte Interrupts, so dass IIRC I den periodischen Interrupt-Code nach dem ersten Abschnitt reentrant machte.

Wenn Sie anstehende Interrupts während langer Codeabschnitte verschieben, kann es in Ordnung sein, wenn Ihre Anforderungen an Latenz und Jitter in Bezug auf die Ausführungszeit der "kritischen Teile" ziemlich entspannt sind.

Mein üblicher Ansatz besteht darin, die Codeausführungszeit in ISRs zu minimieren und so schnell wie möglich zurückzukehren (vielleicht Dinge wie entprellte Eingabeereignistokens in kreisförmige Warteschlangen zu stopfen, Flags zu setzen und auszusteigen). Wenn beispielsweise ein komplexer Steueralgorithmus alle 200 ms mit maximal 20 ms Jitter ausgeführt werden muss, würde ich alle 200 ms ein Flag im Timer-Interrupt setzen und dieses Flag außerhalb des ISR abfragen.

Abgesehen von der Erfüllung der Designanforderungen wird die Zuverlässigkeit durch diesen Ansatz verbessert.

Soweit es darum geht, eine Art Richtlinie zu finden, die dies besagt, schlage ich vor, sich einige der Richtlinien für sicherheitskritische Software anzusehen. Möglicherweise finden Sie es nicht direkt angegeben, aber Sie können es möglicherweise aus anderen Aussagen ableiten. UL1998 IEC 60730 usw. In diesem FAA-Dokument heißt es:

Interrupts und deren Auswirkung auf Daten müssen in sicherheitskritischen Bereichen besonders beachtet werden. Die Analyse sollte verifizieren, dass Unterbrechungen und Unterbrechungsbehandlungsroutinen keine kritischen Datenelemente verändern, die von anderen Routinen verwendet werden.

Und

Von besonderer Bedeutung für die Sicherheit ist die Sicherstellung der Integrität sicherheitskritischer Daten gegen versehentliche Änderung oder Überschreibung. Überprüfen Sie beispielsweise, ob die Interrupt-Verarbeitung sicherheitskritische Daten stört.

Beides wird schwieriger, wenn die Größe des Unterbrechungscodes zunimmt.

Kurze (in Bezug auf maximalen Jitter und Latenz) häufige Interrupts können als effektive Verringerung der Prozessorgeschwindigkeit analysiert werden, anstatt sich mit schlimmsten Anhäufungen langer Interrupts zu den kritischsten Zeiten auseinandersetzen zu müssen, was ebenfalls für eine schnelle Ausführung spricht als einfacher Code innerhalb von ISRs.

Das Vermeiden von Verschachtelungsinterrupts erscheint mir nicht allzu schlimm oder sogar absurd. Tatsächlich können verschachtelte Interrupts mehr Probleme verursachen, als sie lösen.

Ich beziehe mich speziell auf den Speicher, der zum Speichern/Wiederherstellen des Kontexts beim Betreten/Verlassen der ISR verwendet wird. Üblicherweise speichert jede ISR auf einfachen Architekturen den gesamten erforderlichen Kontext des Codes, den sie unterbrochen hat, beispielsweise auf dem Stapel des Prozessors. Bei der Entwicklung der Software muss dieser Speicher berücksichtigt werden.

Wenn zum Beispiel ISR A 10 Bytes Kontext zum Speichern/Wiederherstellen benötigt, ISR B 15 und ISR C 12 Bytes benötigt, lautet die Berechnung/Schätzung einfach:

Irgendwo während der Ausführung von nicht kritischen Abschnitten kann irgendeine der ISRs ausgelöst werden. Wenn nun die ISRs nicht verschachtelt werden dürfen, beträgt der für den Kontext erforderliche maximale Speicher max( 10, 15, 12 )= 15 Bytes. Wenn jede der ISRs jede andere dieser 3 ISRs unterbrechen kann, sind im schlimmsten Fall 10 + 15 + 12= 37 Bytes Kontext, die gleichzeitig gespeichert werden müssen . Daher muss zu jedem Zeitpunkt während der Ausführung (von nicht-atomarem Code) mindestens genug Speicher für diese 37 Bytes Kontext verfügbar sein, gegenüber 15 im Fall der Nichtverschachtelung.

Je mehr ISRs sich dem „Verschachtelungsspiel“ anschließen, desto deutlicher wird das Problem natürlich sichtbar.

Wenn mehrere Tasks gleichzeitig ausgeführt werden und ISRs den Kontext im aktuellen Stapel des Codes speichern, den sie unterbrechen, müssen Sie eine Kontextgröße für den schlimmsten Fall pro Task berücksichtigen .

Dieses Argument hängt natürlich von der verwendeten HW-Architektur ab. Es ist möglicherweise nicht anwendbar, wenn der HW/Interrupt-Controller fortgeschrittener ist und spezielle Merkmale aufweist, um den Kontextwechsel zu/von ISRs zu handhaben.

Abgesehen davon erfordern komplexe ISRs tendenziell mehr Kontextdaten, die gespeichert/wiederhergestellt werden müssen, als einfache, sodass kleine, prägnante ISRs normalerweise empfohlen werden, da sie mehrere Vorteile bieten:

  1. schnelle Ausführungszeit, möglicherweise nachgebend
    1. kürzere kritische Abschnitte und damit
    2. weniger Jitter
  2. kleineren Kontext, was zu
    1. weniger Worst-Case-Speicherverbrauch und (architekturabhängig)
    2. noch kürzere Ausführungszeit (da weniger Zustandsinformationen von A nach B nach A übertragen werden müssen)

Wenn am Ende alle ISRs auf ihr Minimum reduziert werden, wie wenn nur ein Flag gesetzt wird, kann die Frage, ob das Verschachteln von ISRs zulässig ist oder nicht, eindeutig zugunsten des Nicht-Verschachtelns beantwortet werden, da der Vorteil des Verschachtelns von ISRs verschwindet.