9. Januar 2007

Studienarbeit: HSE Free OSEK — wie ich mein erstes Betriebssystem selbst geschrieben habe

Zeitraum2006–2007
Kunde / KontextHochschule Esslingen · Studienarbeit
RolleEntwickler & Architekt (2er-Team)
ErgebnisNote 1,0 (sehr gut)
Problem

Kommerzielle OSEK-Betriebssysteme waren teuer und geschlossen; für Lehre und kleine Projekte fehlte eine offene, verständliche Alternative. Und ein RTOS versteht man erst wirklich, wenn man selbst eines gebaut hat.

Soll

Ein preemptives, prioritätsbasiertes Echtzeitbetriebssystem nach OSEK-Standard von Grund auf entwickeln — portabel für zwei Mikrocontroller-Architekturen und vollständig dokumentiert.

Ist / Ergebnis

Lauffähiges RTOS (~6 kByte) für ARM7 (LPC2148) und Freescale HCS12: Kernel, zwei Scheduler, Assembler-Context-Switching inkl. eigener Context-Speicher-Lösung gegen Interrupt-Verschachtelung. Note 1,0, Open Source, im Forum veröffentlicht und von HSE-Nachfolgern weitergeführt.

Tech-Stack
CAssemblerOSEK OSARM7 · LPC2148Freescale HCS12Keil µVisionCodeWarriorJTAG
Mitgebrachte Fähigkeiten
C-ProgrammierungMikrocontroller-GrundlagenDatenblätter lesenBetriebssystem-Konzepte
Entwickelte Fähigkeiten
RTOS-Kernel-DesignAssembler-Context-SwitchingScheduling-VerfahrenPriority-CeilingPortierung über ArchitekturenTechnische DokumentationOpen-Source-Zusammenarbeit

Studienarbeit an der Hochschule Esslingen · offizieller Titel: „Entwicklung eines OSEK-konformen Echtzeitbetriebssystems"

Das hier ist der Anfang von allem. Bevor ich Jahre später Cloud-Plattformen orchestriert habe, habe ich ganz unten angefangen — auf der Registerebene eines Mikrocontrollers, mit einem selbst geschriebenen Betriebssystem.

Wie es dazu kam

Ein Jahr zuvor hatte ich im Studium das Fach Betriebssysteme (Note: gut). Die Vorlesung orientierte sich stark an „MicroC/OS-II” von Jean J. Labrosse — dem Klassiker, der ein kleines Echtzeitbetriebssystem Zeile für Zeile auseinandernimmt. Mein Kommilitone Mario und ich hatten das gesamte Studium vom ersten Tag an zusammen bestritten, und wir waren von dem Thema begeistert. Also beschlossen wir, unsere Studienarbeit bei Prof. Dr. Jörg Friedrich genau dorthin zu legen: nicht ein Betriebssystem benutzen, sondern eines bauen.

Buchcover: MicroC/OS-II — The Real-Time Kernel von Jean J. Labrosse

Unser „Lehrbuch": MicroC/OS-II — The Real-Time Kernel von Jean J. Labrosse. Einen ganzen Monat habe ich damit verbracht, dieses Buch zu lesen, bevor ich die erste Zeile schrieb. Es zeigt einen kompletten RTOS-Kernel bis ins Detail — die perfekte Grundlage, um zu verstehen, was man selbst nachbauen will.

Auf Amazon ansehen →

Bei der Recherche stießen wir auf OSEK OS — den Automotive-Standard für Steuergeräte-Betriebssysteme. Unsere Region lebt traditionell vom Automobil, und OSEK arbeitet, anders als µC/OS-II, prioritätsgesteuert. Genau die Frage „Wie funktioniert hier eigentlich das Scheduling?” war der Auslöser. Wir entschieden: Wir entwickeln auf Basis der OSEK-Spezifikation und dem Wissen aus dem µC/OS-II-Handbuch ein eigenes RTOS — „HSE Free OSEK”, quelloffen, als kostenlose Lernplattform für Studenten, die verstehen wollen, wie ein Betriebssystem wirklich funktioniert.

Warum ich überhaupt so tief in Mikrocontroller wollte? Wegen meines Praxissemesters. Ich war bei Leuze electronic in Owen und habe dort auf einem 8051-basierten µC einen Treiber für ein OLED-Display (96×96 Pixel, SPI-Ansteuerung, 2005) sowie prototypisch einen Remissionsstufen-Detektor zum Auslesen von Barcodes in der Etikettenerstellung entwickelt. Danach war klar: In dieses Themengebiet will ich tiefer.

Die Arbeitsteilung

Konzeptionell haben wir alles gemeinsam gemacht — das µC/OS-II-Buch gelesen, diskutiert, wie die OSEK-Spezifikation zu interpretieren ist, und die Arbeitspakete agil miteinander definiert und aufgeteilt, lange bevor ich das Wort „agil” beruflich zum ersten Mal hörte.

Wir haben von Anfang an auf Portabilität geachtet: einen modularen Kern, der überall läuft, und einen mikrocontroller-spezifischen Teil, der Context-Switch, Interrupt-Handling und Register-Initialisierung übernimmt. Diese Trennung — generischer Kern vs. Hardware-Abstraktion — ist übrigens dasselbe Prinzip, nach dem ich heute Plattformen schneide.

Die Hardware teilten wir uns auf: Mario nahm den Freescale HCS12 (16-Bit) auf einem von der Hochschule gestellten Dragon12-Board, ich den NXP/Philips LPC2148 (ARM7, bis 60 MHz) auf einem OLIMEX-Board — ein günstiges Hobby-Setup mit JTAG-Debugger und ganzen zwei Breakpoints. Bescheidenes Werkzeug zwingt einen, genau zu verstehen, was man tut.

Dragon12 Evaluationboard mit Freescale HCS12
Marios Board: Dragon12 mit Freescale MC9S12DP256B (16-Bit HCS12)
OLIMEX Development-Board mit NXP LPC2148 ARM7
Mein Board: OLIMEX mit NXP/Philips LPC2148 (ARM7, 60 MHz)

Das Herz: der Context Switch

Das Herzstück jedes Betriebssystems ist der Context Switch — der Moment, in dem eine Task angehalten, ihr vollständiger Zustand gesichert und eine andere fortgesetzt wird. Auf Registerebene, in Assembler.

Und genau hier lag auf meinem LPC2148 das interessanteste Problem der ganzen Arbeit. Beim ARM7 haben Interrupts ihren eigenen Stack: Tritt ein Interrupt auf, wechselt der Prozessor in den IRQ-Modus, der Stack-Pointer springt auf den ISR-Stack, und die Arbeitsregister R0–R12 landen dort. Passiert nun innerhalb der ISR ein Context Switch und die unterbrochene Task kommt bis zum nächsten Interrupt nicht wieder in den Zustand Running, wird der ISR-Stack überschrieben — und der Context der Task ist verloren.

Meine Lösung: Ich habe den Task-Context nicht mehr auf dem Stack gehalten, sondern jeder Task einen festen Context-Speicher zugewiesen — 18 Register pro Task, portierbar über die Konstante CONTEXTSIZE.

Task 1 läuft (Running) Context sichern ContextStorage[T1] R0..R12SP / LR PC · CPSR18 Register fester Speicher, nicht der Stack Scheduler wählt Task 2 Task 2 wird fortgesetzt Context laden ContextStorage[T2] R0..R12SP / LR PC · CPSR18 Register übersteht verschachtelte ISRs
Der Kniff auf dem LPC2148: Jede Task bekommt ihren eigenen Context-Speicher (Pointer TCBContextStack im TCB). So kann eine verschachtelte Interrupt-Service-Routine den Stack nicht mehr überschreiben — implementiert in den Assembler-Routinen __ContextSwitch() und __ISRCtxSw().

Vorteile: Der Stack wächst nicht unkontrolliert, und der Context ist beim Debuggen jederzeit ohne Umweg einsehbar. Für den Switch innerhalb eines Interrupts brauchte es eine eigene Methode, weil dort zusätzlich der ISR-Stack ins Spiel kommt.

Zwei Scheduler und eine echte Messung

Wir haben nicht nur einen Scheduler gebaut, sondern zwei Implementierungen desselben prioritätsbasierten Verfahrens, um sie vergleichen zu können: eine über verkettete Listen (Ready-Queue) und eine mit Bitmap-Scheduling (BitOSMapTbl[], erweitert um mehrere Tasks pro Priorität).

Der entscheidende Unterschied liegt im Trade-off zwischen Geschwindigkeit und Kapazität:

  • Bitmap-Scheduling findet die höchstpriore Task in nahezu konstanter Zeit — egal, ob 3 oder 300 Tasks bereit sind, die Auswahl dauert praktisch immer gleich lang und ist damit immer deutlich schneller. Der Preis: Die Zahl der Prioritätsebenen ist fest durch die Breite der Bitmap begrenzt (je nach Konfiguration z. B. 8, 16, 32 oder 64) — ein hartes Maximum, das zur Compile-Zeit feststeht.
  • Die verkettete Liste muss die Ready-Queue durchsuchen, ihre Zeit wächst linear mit der Zahl der bereiten Tasks (genau die gemessenen 517 ns pro Vergleich, siehe unten). Dafür kennt sie keine feste Obergrenze: Sie bleibt bis an die Speichergrenzen des Controllers erweiterbar.

Kurz: konstante Geschwindigkeit gegen ein festes Task-Limit — oder wachsende Suchzeit gegen praktisch unbegrenzte Task-Zahl. Genau diese Art Trade-off begegnet mir bis heute ständig, nur heißt sie dann „Hash-Lookup vs. Scan” oder „feste vs. elastische Ressourcen”.

Anzahl bereiter Tasks → Auswahlzeit → Verkettete Liste wächst linear · bis Speichergrenze Bitmap-Scheduling konstant & schnell hartes Limit = Bitbreite (8/16/32/64)
Bitmap-Scheduling bleibt unabhängig von der Task-Zahl konstant schnell, stößt aber an ein festes Limit (die Bitbreite). Die verkettete Liste wird mit jeder zusätzlichen Task langsamer, skaliert dafür bis an die Speichergrenze des Controllers.

Und ich wollte es nicht bei der Theorie belassen. In der µVision3-Simulation habe ich die Suchzeit zum Einsortieren einer Task in die Ready-Queue am LPC2148 bei 60 MHz gemessen: 517 ns pro Vergleichsoperation. Daraus die Beispielrechnung — bei 10 Tasks im Ready-Zustand dauert die Suche (10 − 1) × 517 ns = 4,653 µs. Ehrlich dazugeschrieben habe ich auch die Kehrseite: Bei langsamerem Takt und vielen Ready-Tasks kann genau dieses Verfahren so viel Zeit fressen, dass die Echtzeitfähigkeit kippt. Ein Betriebssystem baut man mit Blick auf den Worst Case, nicht auf den Durchschnitt.

Öffentlich gemacht — und Feedback aus der Community

Am 4. Oktober 2006 habe ich das Projekt im Forum von mikrocontroller.net vorgestellt und um Ideen, Testleser und Mitstreiter gebeten. Die Resonanz war klein, aber ehrlich und fachlich — genau das, was ein lernendes Projekt braucht:

„Das sieht ja schon nett aus!” — MartinK

Es entwickelte sich eine richtige technische Diskussion: über Scheduling-Varianten, das Priority-Ceiling-Flag, ISR-Kategorien und den Overhead beim Registersichern. Ein Nutzer nahm den Quellcode gleich auseinander und lieferte seitenweise fundierte Verbesserungsvorschläge; selbst Forenlegende „peda” (Peter Dannegger) schaute vorbei. Ich habe die V0.1 als ZIP (221 KB) offen angehängt und die vollständige Dokumentation unter meiner damaligen Hochschul-Adresse www2.hs-esslingen.de/~frruit00 bereitgestellt (die Seite existiert heute nicht mehr) — das Projekt sollte anschließend von einer Studentengruppe der HS Esslingen weitergeführt und Richtung AUTOSAR OS entwickelt werden. So diente es anderen als Referenz und Inspiration.

⬇ Studienarbeit als PDF (115 Seiten)

→ Der Original-Forenthread von 2006/2007

Was sonst noch drin steckte

HSE Free OSEK war kein Spielzeug. Implementiert waren unter anderem die volle OSEK-Task-API (ActivateTask, TerminateTask, ChainTask, Schedule …), Event-Management (SetEvent, WaitEvent …), Resource-Management nach Priority-Ceiling-Protokoll (eine Task, die eine Ressource hält, erbt deren höhere Priorität — so werden Prioritätsinversion und Deadlocks vermieden) sowie Counter und Alarme als Software-Timer. Und — das gehört zur Ehrlichkeit einer Ingenieursarbeit — wir haben im Ausblick offen benannt, was nicht fertig war: das Message-Betriebsmittel fehlte, die Interrupt-Kategorie 2 war unvollständig. Reife heißt auch, die eigenen Lücken sauber zu dokumentieren.

Danke, Mario

Franjo und Mario, 2009 in den USA

Dieses Projekt gehört zur Hälfte Mario Penava. Wir haben das gesamte Studium vom ersten Tag an gemeinsam bestritten, jede Hürde und jede Prüfung zusammen genommen — und am Ende ein Betriebssystem gebaut. Mario ist heute nicht nur ein alter Freund, sondern mein Trauzeuge. Er ist seit 2007 bei Leuze electronic und macht dort als Head of Technical Sales einen herausragenden Job. Ich bin wirklich stolz auf ihn — wir beide haben es seit der Hochschule ganz schön weit gebracht.

Mario und ich, 2009 in den USA — Jahre nach der Studienarbeit, immer noch beste Freunde.

LinkedIn · Xing

Was mir davon bis heute geblieben ist

Die Note war 1,0 — die beste Einzelnote meines Studiums. Aber das ist nicht, was hängen geblieben ist.

Geblieben sind die Grundlagen: Scheduling, Interrupts, Speichermanagement, Single-Thread-Multitasking. Die Fähigkeit, das Datenblatt eines Mikrocontrollers zu lesen und in C bis auf die Registerebene runterzugehen. Diesen tiefen technischen Scope kann ich bis heute abrufen — auch in modernen, komplexen IT-Infrastrukturprojekten. Denn die wesentlichen Bausteine der Informatik wiederholen sich immer wieder, nur mit anderen Begriffen und in anderem Gewand:

Embedded · 2007 Platform · heute Scheduling Pipeline-Priorisierung Context-Speicher State-Management Hardware-Abstraktion Plattform-Schnitt
Dieselben Konzepte, anderes Gewand: Was ich 2007 auf dem Mikrocontroller gelernt habe, taucht heute im Platform Engineering wieder auf.

Wer einmal ein Betriebssystem von Grund auf gebaut hat, sieht in jeder Cloud-Plattform dieselben alten Fragen wieder.

← Zurück zum Lab