====== Software Lockstep ====== **Software Lockstep** ist ein Mechanismus zur Erkennung von Berechnungsfehlern — verursacht durch kurzzeitige Prozessorstörungen (SEUs in der CPU, Latch-Fehler, Timing-Anomalien). Der Compiler **dupliziert kritische Rechenoperationen** und vergleicht die Ergebnisse beider Berechnungen vor jedem dauerhaften Speicherzugriff oder ''return''. Stimmen sie nicht überein, greift ein kontrollierter Recovery-Pfad. Im Unterschied zu Hardware-Lockstep (zwei physische Kerne, synchron getaktet, Hardware vergleicht Ausgaben) läuft Software Lockstep auf einem einzelnen Kern — der Compiler fügt Instruktions-Redundanz in den Maschinencode ein. → Verwandt: [[lyx_-_programmiersprache:do-178c:triple_modular_redundancy|TMR]] · [[lyx_-_programmiersprache:do-178c:memory-scrubbing|Memory Scrubbing]] · [[lyx_-_programmiersprache:do-178c|DO-178C Hauptseite]] ---- ===== 1. Aktivierung ===== Software Lockstep wird per ''@integrity''-Pragma auf Funktions- oder Unit-Ebene aktiviert: // Auf Funktionsebene — nur diese Funktion ist geschützt @dal(A) @flight_crit @integrity(mode: software_lockstep, interval: 50) @stack_limit(2048) @wcet(200) fn ComputeControlOutput(state: ^FlightState): FlightCommands { // Alle ALU/FPU-Operationen hier werden dupliziert var elevator: int64 := Clamp(state.pitch_error / 10, -30, 30); var throttle: int64 := Clamp(500 + state.alt_error / 5, 0, 1000); return FlightCommands { elevator: elevator, throttle: throttle }; } // Auf Unit-Ebene — schützt alle Funktionen der Unit @integrity(mode: software_lockstep, interval: 50) unit autopilot.core; Der ''interval''-Parameter (in ms) gibt an, wie häufig der Lockstep-Check implizit ausgelöst wird, wenn die Funktion in einem Polling-Kontext aufgerufen wird. Bei reinen Einzel-Aufrufen ist er ohne Wirkung — der Check findet immer statt. ---- ===== 2. Was der Compiler generiert ===== Für eine Funktion mit ''software_lockstep'' erzeugt der Compiler konzeptuell folgenden Code: ==== Quellcode ==== @integrity(mode: software_lockstep, interval: 50) fn ComputeAltitudeError(current: int64, target: int64): int64 { var delta: int64 := target - current; var clamped: int64 := Clamp(delta, -5000, 5000); return clamped; } ==== Generierter Maschinencode (konzeptuell) ==== // Primäre Berechnung var p_delta: int64 := target - current; var p_clamped: int64 := Clamp(p_delta, -5000, 5000); // Redundante Berechnung (separat in anderen Registern) var r_delta: int64 := target - current; var r_clamped: int64 := Clamp(r_delta, -5000, 5000); // Vergleich vor return if (p_clamped != r_clamped) { LockstepFault("ComputeAltitudeError: mismatch"); // → Safety-Panic oder Recovery-Handler } return p_clamped; Der Compiler wählt **unterschiedliche physische Register** für primäre und redundante Berechnung, damit ein einzelner Latch-Fehler in einer Registerbank nicht beide Berechnungen gleichzeitig trifft. ---- ===== 3. Einschränkungen und Regeln ===== ^ Regel ^ Begründung ^ | Kein ''@extern fn'' in lockstep-geschützten Funktionen | Compiler hat keinen Zugriff auf externen Binärcode — kann ihn nicht duplizieren | | ''@wcet'' muss etwa **2× den normalen Wert** annehmen | Jede Operation wird zweimal ausgeführt | | Kein ''software_lockstep'' auf Funktionen mit Seiteneffekten (I/O, HW-Register) | I/O-Operationen dürfen nicht doppelt ausgeführt werden | | Empfohlen: Kombination mit ''@redundant'' für Variablen | Schutz der Berechnung (Lockstep) + Schutz des Speichers (TMR) | | Kein ''software_lockstep'' auf ISR-Funktionen ohne ''@no_opt'' | ISR-Code muss deterministisch bleiben | // FALSCH — I/O in lockstep-Funktion @integrity(mode: software_lockstep, interval: 50) fn BadFunction(): void { WriteHardwareRegister(UART_TX, 'A'); // Würde Zeichen doppelt senden — Fehler } // RICHTIG — I/O auslagern, nur reine Berechnung schützen @integrity(mode: software_lockstep, interval: 50) fn ComputeOutput(sensor: int64): int64 { return Clamp(sensor * 3 / 2, 0, 1000); // Reine Berechnung — sicher } fn SendOutput(val: int64): void { WriteHardwareRegister(UART_TX, val); // I/O außerhalb des Lockstep-Schutzes } ---- ===== 4. WCET-Interaktion ===== Software Lockstep verdoppelt den Instruktionsaufwand. ''@wcet'' muss entsprechend angepasst werden: // Ohne Lockstep: geschätzte WCET 80 µs @wcet(80) fn NormalFunction(x: int64): int64 { return x * x + x / 2; } // Mit Lockstep: WCET auf ~2× erhöhen @integrity(mode: software_lockstep, interval: 50) @wcet(180) // 2× + Overhead für Vergleich und Recovery-Pfad fn ProtectedFunction(x: int64): int64 { return x * x + x / 2; } Der Compiler zeigt bei fehlerhaft gesetztem ''@wcet'' auf lockstep-Funktionen eine Warnung: warning[W0244]: @wcet(80) on software_lockstep function ComputeControlOutput Estimated protected WCET: 167 µs exceeds declared budget: 80 µs Consider: @wcet(180) or higher ---- ===== 5. Vergleich der Integritätsmodi ===== ^ Merkmal ^ software_lockstep ^ scrubbed ^ hardware_ecc ^ | Schützt | ALU/FPU-Berechnungen | Code-Segment im RAM | RAM-Daten (Hardware) | | Erkennt | Rechenfehler | Bit-Flips im Code | Bit-Flips in Daten | | WCET-Impact | ~2× | Gering (Hintergrund) | Keiner | | Hardware-Anforderung | Keine | Keine | ECC-RAM zwingend | | Einsatz | DAL-A Kernberechnungen | DAL-A/B auf Embedded | Safety-kritischer ECC-RAM | | Komb. mit @redundant | Empfohlen | Nicht nötig | Redundant zu ECC | ---- ===== 6. Recovery-Handler ===== Wenn eine Lockstep-Abweichung erkannt wird, greift der Standard-Recovery-Pfad (''panic''). Für kontrolliertes Verhalten kann ein Custom-Handler gesetzt werden: // Recovery-Handler: wird bei Lockstep-Fehler aufgerufen // Signatur fest: fn(fn_name: pchar, location: pchar): void fn LockstepRecovery(fn_name: pchar, location: pchar): void { PrintStr("LOCKSTEP FAULT in: "); PrintStr(fn_name); PrintStr("\n"); PrintStr("Location: "); PrintStr(location); PrintStr("\n"); // Umschalten auf Backup-Computer, Safety-Zustand einleiten, etc. ActivateBackupSystem(); } // Recovery-Handler registrieren (einmalig beim Start) fn main(): int64 { SetLockstepRecoveryHandler(LockstepRecovery as int64); // ... return 0; } > **DO-178C-Anforderung:** Der Recovery-Handler selbst darf **nicht** mit ''software_lockstep'' geschützt sein — er ist der Notfallpfad, der läuft wenn der Schutz versagt hat. Er sollte so minimal wie möglich sein: Status loggen, Backup aktivieren, sicheren Zustand einleiten. ---- ===== 7. DAL-Empfehlung ===== ^ DAL ^ software_lockstep ^ Begründung ^ | A | **Zwingend** für Kernberechnungen | Katastrophaler Ausfall — maximaler Schutz | | B | Empfohlen | Gefährlicher Ausfall — Investition gerechtfertigt | | C | Optional | WCET-Impact meist nicht vertretbar | | D/E | Nicht sinnvoll | Overhead ohne Sicherheitsgewinn | ---- Letzte Aktualisierung: 2026-05-22