Embedded-Software-Design

UML für kleine Embedded-Systeme

21.03.2007 | Autor / Redakteur: Andreas Willert* / Martina Hafner

Bild 2: Das Verhalten einer Klasse (Objekt) kann in Form einer Zustandsmaschine modelliert werden
Bild 2: Das Verhalten einer Klasse (Objekt) kann in Form einer Zustandsmaschine modelliert werden

Ohne die Möglichkeit ein Modell äquivalent zu transformieren, zum Beispiel in eine ANSI-C-Applikation, erscheint die UML in Embedded-Projekten oft nicht einsetzbar. Wo die wirklichen Grenzen liegen, zeigt dieser Beitrag.

Im Grunde ist die UML eine Notation genau wie auch ANSI-C oder Pascal. Ein Unterschied zwischen den prozeduralen Notationen und der UML sind unter anderem Konstrukte zur Beschreibung der Architektur. Dies beinhaltet auch Befehle zur Beschreibung des Laufzeitverhaltens. Aus diesem Grund spricht man bei der UML auch von einer Modellierungssprache und bei ANSI-C von einer Programmiersprache.

Bei der Programmierung in C macht erst der Einsatz eines Compilers die Anwendung wirklich effizient und das gleiche gilt für den Einsatz der UML in Bezug auf einen Codegenerator. Übersetzt werden UML-Modelle in eine Programmiersprache wie C oder C++ oder Java. Angenommen ein Codegenerator erzeugt aus einem UML-Modell C-Code, dann gibt es ein Problem. Der verfügbare Vorrat an Design-Elementen der UML lässt sich nicht direkt mit dem Befehlsvorrat von ANSI-C darstellen.

Ein Beispiel: In der UML können Reaktionszeiten in Form von Timern modelliert werden, ANSI-C hat diese Möglichkeit nicht, abgesehen von der expliziten Programmierung eines Timers auf dem eingesetzten Mikrocontroller. Aber dieser Weg der Programmierung macht die Applikation abhängig von der eingesetzten HW-Architektur und das würde bedeuten, der Codegenerator müsste Kenntnisse über die eingesetzte Hardware haben und das ist nicht erwünscht.

Framework sorgt für Hardwareunabhängigkeit

Weder das UML-Modell noch der Codegenerator sollten Abhängigkeiten von der eingesetzten Hardware haben, sondern grundsätzlich nur auf den ANSI-C Befehlsvorrat zurückgreifen. Eine mögliche Lösung ist es, dem Codegenerator ein Framework zur Verfügung zu stellen, in dem alle nicht in ANSI-C direkt abbildbaren Modellelemente in Form von Pattern implementiert sind. Damit ist die Hardware vom Modell entkoppelt und der Codegenerator kann individuell eingesetzt werden. Das Framework jedoch muss jeweils an die eingesetzte Hardware adaptiert werden.

Was muss nun ein derartiges Framework zur Verfügung stellen? Hier sind die wichtigsten Pattern aus Sicht einer Embedded-Architektur.

  • Eigenschaften von Klassen,
  • Instanziierung von Objekten aus den Klassen,
  • Timer,
  • Mailboxen,
  • Events,
  • Messages.

Einige dieser Pattern umfassen üblicherweise ein Echtzeit-Betriebssystem (RTOS). Im Idealfall wird also unser Framework wiederum die Dienste eines RTOS nutzen. Der Codegenerator kann nicht direkt auf die RTOS-Dienste zugreifen, weil er dann Kenntnisse des spezifischen Betriebssystems haben muss.

In der allgemeinen Welt der Softwareentwicklung, in der es nur wenige Betriebssysteme (Windows, Linux) gibt, ist das leicht möglich. In der Embedded-Entwicklung mit ihrer großen Anzahl sehr unterschiedlicher Laufzeitsysteme ist dies nicht praktikabel.

Modellierung mit der UML

Schauen wir uns nun einmal genauer an, wie ein Architektur-Design mit der UML modelliert werden kann und wie daraus eine lauffähige C-Applikation entstehen kann. Eines der Basisdiagramme der UML zur Modellierung des Architektur-Designs sind die Klassen bzw. Objektdiagramme. Mit ihnen wird analog zur hirarchischen Dekomposition die Software aus Sicht der Applikation in Klassen aufgeteilt.

Äquivalent zur prozeduralen Programmierung in einer Hochsprache entsprechen die Klassen den Modulen. Jede Klasse wiederum hat Attribute (Variablen), Methoden (Funktionen) und eine main()-Funktion. Diese stellt das implizite Verhalten einer Klasse dar. Die Klasse ist die statische Beschreibung; zur Laufzeit wird eine Klasse zu einem Objekt instanziiert. Aus der prozeduralen Sicht könnte es etwa so gesehen werden: Die Klasse ist die Beschreibung der Daten und des Verhaltens und liegt im ROM. Die Instanziierung eines Objektes legt die in der Klasse beschriebenen Daten real im RAM an.

Aus UML-Sicht kann eine Klasse ein implizites Verhalten haben. Wie bereits beschrieben, hat jede Klasse neben Attributen und Methoden auch eine main()-Funktion. So wie jede Applikation aus mehreren Modulen besteht, existieren auch mehrere Klassen und damit mehrere unabhängige Verhalten. Im übertragenen Sinne könnte man sagen, es existieren mehrere main()-Funktionen parallel. Anschaulich ist evtl. auch der Vergleich mit einer Task oder einem Prozess in einem Betriebssystem. Die Verhalten der einzelnen Klassen können sich zum Beispiel gegenseitig unterbrechen, wenn die Klassen das Attribut „Aktiv“’ bekommen.

Zustandsautomaten modellieren das Verhalten

Das eigentliche Verhalten innerhalb einer Klasse wird in der Regel von einem Zustandsautomaten in Form von Statecharts modelliert.

Stellen wir uns vor, eine der Klassen hat ein Verhalten wie in Bild 2 zu sehen ist: zwei Zustände mit einem Übergang der durch ein Event angestoßen wird. Dieses wiederum könnte durch ein externes Ereignis hervorgerufen werden, das auf Interrupt-Service-Routinen reagiert, die wiederum das Event erzeugen. Zur Laufzeit würde also das aus der Klasse instanziierte Objekt auf dieses externe Ereignis reagieren.

Ein anderer Zustands-Übergang in unserem Beispiel ist zeitlich gesteuert. Innerhalb von Zustandsautomaten können Zeitabhängigkeiten modelliert werden wie in Bild 2 durch den Timerbefehl tm(100) dargestellt. 100 ms nach Eintritt in den State_two wird der Übergang in den State_one durchgeführt.

Bis jetzt wurden folgende UML-Konstrukte genutzt:

  • Klassen und daraus instanziierte Objekte,
  • Events zur Steuerung der Zustandsübergänge und
  • Timer.

Stellen Sie sich vor, Sie sollten diese Architektur in ANSI-C realisieren, dann müssten Sie ebenfalls Mechanismen zur Abbildung dieser Elemente codieren. Wenn ein Codegenerator aber nur auf den reinen Befehlsvorrat innerhalb von ANSI-C zugreifen soll, müssen ihm für die Realisierung dieser Mechanismen die entsprechenden Code-Pattern zur Verfügung gestellt werden. Im einfachsten Fall geschieht das in Form eines Betriebssystems.

UML-Modellierung für Applikationen ohne Betriebssystem

Wir haben schon festgestellt, dass in der UML- Welt meistens vorausgesetzt wird, dass ein derartiges Laufzeitsystem vorhanden ist, auf dem die modellierten Verhalten realisiert werden können. Es handelt sich in der Regel um Windows, UNIX (Linux) MacOS oder ein anderes Betriebssystem. Anders sieht es in der Embedded-Welt der kleinen Applikationen aus. Dort wird nicht in jedem Fall ein RTOS eingesetzt, beispielsweise aus Performance -Gründen. Kann dann trotzdem mit der UML modelliert werden?

Eine mögliche Lösung ist der bewusste Verzicht auf bestimmte UML -Konstrukte. Zum Beispiel könnte man darauf verzichten, Klassen aktiv zu machen. Das würde bedeuten, dass die Verhalten der Klassen kooperativ zueinander sind (run to completion) und es würde kein preemptives Schedulingverhalten benötigt. Das entspräche in etwa dem Verhalten einer intelligenten Main-Loop. Anstatt dass alle Verhalten der Klassen wie in einer Main-Loop nacheinander ausgeführt würden, werden sie entsprechend der im System erzeugten Events ausgeführt. Ein primitiver Scheduling- Mechanismus würde, wenn ein Event im System entsteht, immer das Verhalten der Klasse anstoßen, das gerade auf dieses Event wartet.

Anstelle von Events so genannte Guards einsetzen

Eine weitere Möglichkeit wäre, in der Modellierung der Zustandsautomaten anstelle von Events so genannte Guards einzusetzen. Guards sind logische Operationen auf Variablen. In diesem Fall würde kein Event-Mechanismus zur Implementierung der Architektur benötigt werden, sondern das logische Verhalten würde über Inhalte von Variablen gesteuert werden.

Die UML ist grundsätzlich sehr gut geeignet, um auch kleinere Embedded-Applikationen zu modellieren. Es ist jederzeit möglich auf entsprechende Konstrukte zu verzichten und damit die Applikation einfach und kompakt zu halten. Wichtig ist die Kenntnis darüber, welche Konstrukte der UML zu welchen Architektur-Implementationen führen.

Die Firma Willert Software Tools hat verschiedene Frameworks entwickelt, die es ermöglichen mit dem UML Tool Rhapsody von Telelogic aus einem UML Model plattform spezifischen C-Code zu generieren. Die Tabelle zeigt verschiedene Varianten mit ihren dementsprechenden Eigenschaften.

*Andreas Willert ist Geschäftsführer und Gesellschafter der Firma Willert Software. Kontakt: awillert@willert.de

 

Wann UML, wann besser C?

Kommentar zu diesem Artikel abgeben

Schreiben Sie uns hier Ihre Meinung ...
(nicht registrierter User)

Kommentar abschicken
copyright

Dieser Beitrag ist urheberrechtlich geschützt. Sie wollen ihn für Ihre Zwecke verwenden? Infos finden Sie unter www.mycontentfactory.de (ID: 199828 / Software-Entwurf & Echtzeit-Design)