Experience Embedded

Professionelle Schulungen, Beratung und Projektunterstützung

Auswahl und Einsatz eines Betriebssystems für Mikrocontroller

Autor: Frank Benkert, FRB Computersysteme GmbH

Beitrag - Embedded Software Engineering Kongress 2017

 

Entwicklungsprojekte für neue embedded Produkte sehen sich zu Beginn meist mit der immer gleichen, grundlegenden Frage konfrontiert:  Mit oder ohne Betriebssystem? Wenn ohne, wie dann? Wenn mit, welches? Es gibt für diese Frage natürlich keine allgemeingültige Antwort. Betrachtet man jedoch die Anforderungen an das neue Produkt genauer, so ergibt sich die Antwort oft von selbst. Welche Fakten helfen aber wirklich bei der Entscheidung? Wie erarbeitet man diese Informationen? Welche Fragen muss man stellen? Wer kann diese beantworten? Im Folgenden soll anhand eines Projektes aus der Praxis aufgezeigt werden, wie dieser Auswahlprozess und die spätere Integration erfolgreich gelingen kann.

Das Projekt

Als Vorlage für die Ausführungen unten dient ein reales Projekt, welches die Neuentwicklung einer Gerätefamilie zur Steuerung von Großmotoren zum Ziel hatte. Das Projekt kam nach fast zwei Jahren Entwicklungszeit vor wenigen Monaten zu einem erfolgreichen Abschluss (innerhalb des geplanten Budgets). Einige Ausprägungen der Geräte laufen bereits produktiv, andere befinden sich noch in Zertifizierungs- bzw. Zulassungsverfahren.

Als Plattform kam ein Arm Cortex-M7 von ATMEL (automotive Variante) zum Einsatz. Dieses Dokument soll als allgemeine Grundlage dienen, weshalb die Formulierungen (wo immer möglich) sich nicht direkt auf das Projekt beziehen. Direkte Bezüge auf das geschilderte Entwicklungsprojekt sind in kursiver Schrift.

Was ist ein Betriebssystem?

Um die Frage nach dem "Mit oder Ohne" überhaupt stellen zu können, muss klar sein, was die einzelnen Beteiligten unter dem Begriff Betriebssystem verstehen.

Je tiefer ein Entwickler im System arbeitet, desto differenzierter ist seine Sicht auf das "Ding" zwischen Hardware und Applikation. Unabhängig davon, auf welche Definition man sich letztendlich einigt, ist es äußerst wichtig, das gleiche Verständnis von diesem Begriff zu haben.

Im Projekt einigte man sich auf die Formulierung: "Ein Betriebssystem besteht aus einem Task-Scheduler mit angebundener Treiberarchitektur". Dabei war die Größe oder Vollständigkeit der "Treiberarchitektur" nicht ausschlaggebend.

Mit oder ohne Betriebssystem

Die Beantwortung dieser Frage ist viel einfacher als viele meinen. Als Entscheidungsgrundlage dienen nur wenige  wichtige Punkte:

  • Benötigt das Produkt mehrere nebenläufige Ausführungsstränge (Tasks)?
  • Sind diese Tasks aktiv (oder reagieren sie nur auf äußere Ereignisse)?
  • Gibt es zeitkritische Anforderungen an die gegenseitige Abarbeitung?

 

Sollte mindestens eine dieser Fragen mit "Ja" beantworten werden, so ist man gut beraten, zumindest schon mal einen Scheduler einzusetzen.

Die Frage nach aktiv oder reaktiv weist in die Richtung von Interrupts. Ein rein reaktives System kann z.B. mit mehreren Interrupt-Routinen ebenfalls mehr als einen  Ausführungsstrang besitzen. Diese sind dann allerdings (normalerweise) sequentiell. Eine Ausnahme bilden Systeme mit "Interrupt-Prioritäten" und "Nested-Interrupts". Eine solche Technik kann man bereits als Pseudo-Scheduler bezeichnen.

Sollte man sich trotz eines "Ja" gegen einen Scheduler entscheiden, so wird das Projekt mit absoluter Sicherheit neben dem eigentlichen Produkt zusätzlich einen Scheduler als Output liefern.

Um die Frage nach der Notwendigkeit einer Treiberarchitektur zu beantworten, gibt es hingegen tatsächlich nur einen einzigen Punkt:

  • Gibt es Hardware, welche der Applikation gegenüber abstrahiert werden soll(te)?

 

Ein "Ja" bedeutet, dass mindestens ein Treiber benötigt wird. Zusammen mit der Antwort auf die Frage nach dem Scheduler lässt sich nun sehr leicht die grundlegendste Frage nach dem "Mit oder Ohne" beantworten.

Das Projekt entschied sich für das "Mit".

Anforderungen an das Betriebssystem

Um später eine Auswahl zwischen den hunderten am Markt vertretenen Betriebssystemen treffen zu können, ist es wichtig, die Anforderungen in einer Art Matrix zusammenzutragen. Die folgende Liste soll einen Einstieg in die Fragestellung geben. Je nach Produkt muss diese natürlich erweitert, verändert oder gekürzt werden.

Wird der Prozessor-Kern unterstützt?

Prozessoren unterstützen Betriebssysteme mittlerweile mit einer Vielzahl von integrierten Funktionen. Was früher im Scheduler von Hand implementiert werden musste, wird heute über wenige Assemblerbefehle direkt vom Prozessor erledigt. Von Automatismen zur Taskumschaltung über Stackmanagement bis hin zur Prüfung von Berechtigungen sind viele Funktionen mittlerweile in die Prozessoren integriert. Dafür ist es jedoch notwendig, dass der Kern des Scheduler diese Besonderheiten kennt und ansteuert. Nur so ist es möglich, die optimale Leistung aus dem Prozessor zu holen.

Unterstützt der Scheduler die notwendigen Scheduler-Strategien?

Aus der Entwicklung von embedded Produkten quasi nicht mehr wegzudenken ist ein Echtzeit-Scheduler. Dieser hat dafür zu sorgen, dass Deadlines eingehalten werden und die Vorhersagbarkeit gewährleistet bleibt. Für dessen Umsetzung ist zunächst lediglich eine strikt prioritätsgetriebene Umsetzung ausreichend. Doch was ist, wenn mehrere Tasks auf der gleichen Priorität arbeiten müssen? Welche von diesen wird dann bevorzugt?

Hierfür gibt es zusätzliche Strategien wie "Round Robin" oder "First In, First Out". Auch sind Scheduler erhältlich, welche Fairness auf die eine oder andere Art garantieren. Allerdings werden diese nur selten in embedded Systemen gefordert.

Welche Granularität muss der Scheduler bieten?

Ein embedded System muss teilweise im Mikrosekunden-Raster Entscheidungen treffen oder Aktionen triggern können. Abhängig von der angestrebten Systemarchitektur ist es wichtig zu fragen, ob z.B. eine externe Nachricht alle 2 Millisekunden verarbeitet werden kann oder ein Aktorwert innerhalb einer Deadline von 300 Mikrosekunden neu beschrieben wird. Hat man dann einen Scheduler mit festem 20ms Raster, so ist das zwar schnell, aber unbrauchbar.

Werden Synchronisation und Signalisierung unterstützt?

Sobald mehrere Tasks innerhalb eines Systems laufen, bleibt es nicht aus, dass diese sich Ressourcen teilen müssen. Um den Zugriff auf diese Ressourcen zu synchronisieren sind atomare Mechanismen erforderlich. Üblicherweise sind diese in Form von "Counting Semaphores" abgebildet, auf denen wiederum verschiedene andere Synchronisationsmechanismen aufgebaut werden können.

Benötigt das Projekt Interprozess-Kommunikation (IPC)?

Werden Informationen in mehreren Tasks verarbeitet, so kann es notwendig werden, Nachrichten zwischen den Systemteilen auszutauschen. Dies kann z.B. über Pipes oder Queues geschehen. Sofern das Produkt diese Problemstellung aufweist, sollte ein Betriebssystemkandidat auch gegen diese Anforderung geprüft werden.

Beinhaltet das System Unterstützung für MMUs?

Immer mehr embedded Systeme sind mit Memory Management Units (MMUs) ausgestattet. Je nach Größe des Systems bergen diese MMUs unterschiedlich viele Funktionen - vom einfachen Cache-Management (welcher Speicherbereich wird durch den CPU-Cache abgebildet?) über Zugriffsteuerung bis hin zu komplexen Mappingaufgaben innerhalb eines virtuellen Adressraums. Je nach Anforderung des Produktes und Gegebenheiten der Hardware stellt dies eine hilfreiche Funktion des Betriebssystems dar.

Beinhaltet das System Unterstützung für Speichermanagement?

Die Standard-Schnittstellen zur dynamischen Speicherallokation sind jedem Programmierer gut bekannt: "malloc" und "free". Hinter diesem sehr einfachen Interface verbergen sich jedoch komplexe Strategien, um z.B. eine Fragmentierung des Speichers zu reduzieren. Falls das Produkt eine solche Funktion benötigt, ist es wichtig, im Betriebssystem bereits erprobte und getestete Mechanismen hierfür vorzufinden.

Unterstützung für Peripherie und Schnittstellen

Auch wenn die Anbindung von Peripherie oft als Referenzimplementation bereits durch die Hersteller zur Verfügung gestellt wird, so spart es dennoch viel Arbeit, wenn ein ähnlicher Baustein oder die geforderte Schnittstelle bereits als fertiger Treiber für genau dieses Betriebssystem existiert. Bevorzugt natürlich bereits fertig "abgehangen" und getestet, zumindest aber mit einem Satz von Testfunktionen, welche auf der eigenen Hardware abgearbeitet werden können.

Untersützung IDE und Debugging

Ob ein Betriebssystem mit seiner eigenen "full featured IDE" geliefert wird, in die bereits der Compiler integriert ist, oder ob man die Komponenten selbst zusammenstellen kann, ist unerheblich. Wichtig bei der Bewertung dieses Punktes ist, dass ein geschlossener Kreislauf vom Programmieren in einem komfortablen Editor über die Kompilation und das Aufbringen des Kompilats auf das Target bis hin zum Debuggen auf Zeilenebene möglich ist. Letzteres lässt sich natürlich bei manchen Produkten nur durch Emulatoren erreichen. Diese Möglichkeiten sollten im Zweifel getestet werden, bevor man sich für eine Lösung entscheidet.

Liegt der Quellcode des Betriebssystems vor?

Auf embedded Systemen ist die Verzahnung zwischen Applikation, Treibern und Scheduler naturgemäß sehr eng. Das bedingt oft, dass während des Debuggings fremde Funktionen des besseren Verständnisses wegen durchlaufen werden wollen. Viele kommerziellen Anbieter gewähren deshalb unter bestimmten vertraglichen Bedingungen einen begrenzten Einblick in ihre Sourcecodes, um dieses Vorgehen zu unterstützen. Bei Open-Source-Systemen stellt sich diese Frage naturgemäß nicht.

Welcher Lizenz unterliegt das Betriebssystem?

Eine Frage, die nicht nur die Techniker, sondern auch die Produktverantwortlichen umtreibt. So viele Lizenzmodelle es in der Open-Source-Welt gibt, so viele gibt es auch in der Welt der kommerziellen Produkte. Was welche Lizenz genau bedeutet, muss am jeweiligen Produkt detailliert geprüft werden. Z.B. ist eine kommerzielle Lizenz pro Entwicklungsstandort bei verteilten Teams ebenso schädlich wie eine Lizenz, welche die Nennung des Autors im Handbuch bei einem Produkt ohne Kundendokumentation verlangt.

Support und Community

Oft vernachlässigt, aber trotzdem wichtig ist die Frage nach der Community. Selbst der kommerzielle Support des Herstellers kann eine rege Diskussionsgruppe im Internet nur schwer ersetzen. Ein Ort, an dem unbürokratisch Fragen beantwortet, Codeschnipsel gepostet oder Fehler gemeldet werden, ist oft mehr wert als ein Supportvertrag. Seit einiger Zeit gehen auch kommerzielle Anbieter dazu über, Mailinglisten zu hosten, in denen sowohl Anwender als auch Supportmitarbeiter sich gegenseitig austauschen können.

Neben dieser allgemein gehaltenen Liste spielten im realen Projekt noch viele weitere Aspekte eine Rolle, z.B. die Unterstützung für die Schnittstellen I2C, SPI, QSPI und USB, sowie Dateisysteme und Feldbusse. Weiterhin wurden in die Bewertung weiche Faktoren, wie Zukunftssicherheit, Lizenzrisiko, Testbarkeit und Einarbeitungsaufwand, mit einbezogen.

Auswahlprozess

Für den Auswahlprozess sollte man sich auf einige wenige aussichtsreiche Kandidaten begrenzen. Als Hilfe zur Vorauswahl können neben (leider oft veralteten) Listen aus dem Internet und Informationen der Anbieter z.B. auch Erfahrungen von beteiligten Entwicklern dienen. Der Auswahlprozess selbst ist nicht so trivial, wie man glauben könnte, weshalb es gut ist, so wenige Alternativen wie möglich zu sichten. Es empfiehlt sich maximal fünf Bewerber aufzunehmen.

Anders als vermutet besteht der Prozess nicht einfach aus dem Ankreuzen von Features, sondern vorrangig aus Recherche in vorhandener Dokumentation im Internet sowie Anfragen an den Support. Ob das in der Hitliste genannte Feature XY tatsächlich das geforderte X und Y zusammen ist oder ob der Hersteller darunter nur eine Teilmenge versteht, ist oft nur schwer nachvollziehbar und muss aufwändig recherchiert werden.

Für den Auswahlprozess sollte eine vernünftige Deadline vorgegeben werden. Man kann sich bei der Analyse von Features schnell verzetteln. Auch empfiehlt es sich als Antworten neben Ja und Nein auch "sehr wahrscheinlich" oder "zu XX Prozent" zuzulassen, um Zeit zu sparen.

Hat man die Bewertungsmatrix annähernd vollständig, so kommen abhängig von der Beantwortung der Feature-Fragen noch weitere Größen hinzu:

  • Welcher Aufwand entsteht durch eine unzureichende / nicht vorhandene Unterstützung eines Features?
  • Welches Risiko birgt die Unsicherheit, ob ein Feature (nur teilweise) unterstützt wird?
  • Welches Risiko birgt ein fehlender Support bzw. eine fehlende oder nur sehr kleine Community?

 

Nach einem mehrwöchigen Auswahlverfahren, in dem viele Aspekte nicht nur per Dokumentation und Supportanfragen, sondern auch per Tests evaluiert werden mussten, entschied sich das Projekt für das Open Source Betriebssystem NuttX [1].

Was ist NuttX [1] - und warum diese Entscheidung?

Verkürzt gesagt ist NuttX ein Echtzeit-Betriebssystem mit einer POSIX-kompatiblen Treiber- und Schnittstellenarchitektur unter BSD-Lizenz. Es wurde erstmals 2007 von Gregory Nutt veröffentlicht und wird seither aktiv unter seiner Führung weiterentwickelt. Da NuttX unter BSD-Lizenz steht, muss kein Nutzer (außer in seinem Handbuch) bekanntgeben, dass er NuttX nutzt. So war es lange Zeit sehr still um das System. Wenige Veröffentlichungen verwiesen auf das System, wie z.B. das Open-Hardware-Projekt PX4 [2] - eine (Quadrocoper-) Autopilot-Hardware in Zusammenarbeit mit der ETH Zürich.

Die Anzahl der unterstützten Prozessortypen aber zeigt, dass sehr wohl viel mehr Interesse bestand, als die öffentliche Wahrnehmung vermuten ließ. Aktuell umfasst diese Liste von ARM bis  ZiLOG Z80 über 150 Prozessorfamilien. Zuzüglich einer Linux/Cygwin Simulationsplattform.

Die Entscheidung für NuttX fiel im Projekt denkbar knapp, da zum damaligen Zeitpunkt noch nicht alle vom Projekt benötigten Funktionen und Features integriert waren. Mittlerweile sind sie es – durch Upstreaming der Sourcecodes.

Die Downloadzahlen [3] der NuttX-Homepage zeigen, dass Europa hinter China und USA nur an dritter Stelle rangiert. Auch das mag ein Grund dafür sein, dass wir in Europa bisher noch so wenig von NuttX hörten. In den letzten Jahren wurde das Interesse an NuttX jedoch immer größer, was z.B. an der steigenden Beitragszahl auf der Mailingliste abzulesen ist. Weiterhin hat die erst in diesem Jahr gestartete LinkedIn-Gruppe mittlerweile über 1000 User. Auch bekennen sich Firmen mittlerweile öffentlich zu NuttX. Z.B. brachte Anfang des Jahres die Firma SONY eine Familie von Digital Voice Recordern (z.B. ICD-SX2000) auf den Markt und veröffentlichte ihre Weiterentwicklungen an NuttX in Zusammenhang mit diesen Produkten für die Reintegration in die Mainline unter ihrem Firmennamen.

Wir dürfen gespannt sein, wie das Wachstum weiter geht.

Goodies

NuttX bietet neben vielen "normalen" Features aber auch Highlights, welche es aus der Masse der embedded Systeme hervorstechen lassen.

C++11 Support

Während der rudimentäre C++-Support bereits seit mehreren Jahren über die µClibC++ gegeben war, wurde 2016 die C++11 kompatible "libc++" aus dem LLVM-Projekt integriert.

"Ist wie Linux"

Die Programmierung von NuttX unterscheidet sich kaum von der von Linux - von "pthreads" über "everything is a file" innerhalb eines virtuellen Dateisystems bis zu einer Shell, welche über die serielle Schnittstelle erreichbar ist.

Diagnosebefehle wie "free" oder "ps" können gleichermaßen aktiviert werden wie "dd" oder "hexdump".

Das bedeutet, dass der Einarbeitungsaufwand für Programmierer von (normalerweise größeren) embedded Linux-Systemen sehr gering ist. Auch können bestehende Sourcecodes von eigenen Funktionsbibliotheken fast nahtlos nach NuttX übernommen werden.

Tickless Scheduler

Standard-Scheduler nutzen den zyklischen Takt eines Timer-Bausteins, um ihre Scheduler-Abarbeitung zu bedienen. Dabei wird bei jedem Tick eine Funktion angesprungen, welche die Verwaltung der Tasks erledigt. Dadurch tritt ab einer bestimmten Frequenz (Tick-Geschwindigkeit) eine sehr hohe Interruptlast auf, welche gerade embedded Systeme stark beeinträchtigt. NuttX bietet hier das Feature "Tickless Scheduler", bei welchem der Scheduler errechnet, wann er die nächste Aktion ausführen muss, und programmiert den Timer-Baustein (quasi als dynamischen Wecker) auf diesen Zeitpunkt. Dadurch sind höhere Genauigkeiten bei niedriger Frequenz und damit niedriger Interruptlast möglich.

Dynamisches Composite USB-Device

NuttX bietet nicht nur Host-USB-Treiber, sondern auch Device-USB an. Ein recht neues Feature ist das zur Laufzeit konfigurierbare Composite USB-Device. Mit diesem ist es z.B. möglich, innerhalb eines Composite-Devices zusätzliche serielle Schnittstellen für Maintenance-Mode oder Debugging zu aktivieren, welche in den Standardeinstellungen nicht aktiv sind.

Visual Studio mit VisualGDB [4] als IDE

Da NuttX Open Source ist kann die Taskverwaltung komplett eingesehen werden. Konfigurierbare Debugger-Plugins können entsprechend die Taskstrukturen und Stackframes auslesen und ermöglichen so ein komfortables Debuggen.

Im Projekt kam ein Atmel Cortex-M7 Prozessor zum Einsatz. Die Anbindung an den Hardware-Debugger erfolgte über die SWD-Schnittstelle. Die Aufbereitung der Information wurde über ein Debugger-Plugin umgesetzt, so dass die Anzeige in Visual Studio vom Debuggen eines lokalen (Windows-)Prozesses nicht zu unterscheiden war.

Fazit

Die Auswahl eines Betriebssystems für einen Mikrocontroller ist ein aufwändiger Prozess und sollte nicht unterschätzt werden. Für Projekte, welche über Monate oder Jahre laufen, ist es unabdingbar, eine nicht unerhebliche Zeit in die Evaluation verschiedener Systeme zu investieren.

Rückblickend war die Entscheidung des Entwicklungsprojektes für NuttX richtig, auch wenn (wie bereits ausgeführt) die Faktenlage nicht ganz eindeutig war.

Verweise

[1] http://www.nuttx.org/

[2] https://pixhawk.org/

[3] leider nur bis 2015 verfügbar

[4] https://visualgdb.com/

 

Beitrag als PDF-Datei downloaden


Echtzeit - MicroConsult Trainings & Coachings

Wollen Sie sich auf den aktuellen Stand der Technik bringen?

Dann informieren Sie sich hier zu Schulungen/ Seminaren/ Trainings/ Workshops und individuellen Coachings von MircoConsult zum Thema Embedded- und Echtzeit-Softwareentwicklung.

 

Training & Coaching zu den weiteren Themen unseren Portfolios finden Sie hier.


Echtzeit - Fachwissen

Wertvolles Fachwissen zum Thema Embedded- und Echtzeit-Softwareentwicklung steht hier für Sie zum kostenfreien Download bereit.

Zu den Fachinformationen

 
Fachwissen zu weiteren Themen unseren Portfolios finden Sie hier.