ELEKTRONIKPRAXIS Softwarearchitektur und Embedded-UML · 1 Softwarearchitektur und Embedded-UML mit...

26
1 Softwarearchitektur und Embedded-UML mit Echzeitbetriebssystemen (RTOS) -Möglichkeiten und Grenzen- Anne Gregor Olaf C. Winne Quategra GmbH Karl-Heine-Str. 99, 04229 Leipzig T. +49 341 49 12 335 F. +49 341 49 12 336 [email protected] Zusammenfassung: Der erfolgreiche Einsatz von UML (Unified Modelling Language) und Echtzeitbetriebssystemen (RTOS) für die Softwareentwicklung von kleinen und mittleren Embedded-Systemen setzt eine gute Softwarearchitektur voraus. Knappe Ressourcen, harte Echtzeitanforderungen und hardwarenahe Programmteile können die sinnvolle Anwendung der UML mit Codegenerierung in modellbasierter Softwareentwicklung für diese Systeme einschränken. Kenntnisse und Erfahrungen an den Grenzen der hardwarenahen Programmierung zum RTOS und zum generierten UML Code sind erforderlich, um die Vorteile zu optimieren.

Transcript of ELEKTRONIKPRAXIS Softwarearchitektur und Embedded-UML · 1 Softwarearchitektur und Embedded-UML mit...

1

Softwarearchitektur und Embedded-UML mit Echzeitbetriebssystemen (RTOS)

-Möglichkeiten und Grenzen-

Anne Gregor Olaf C. Winne

Quategra GmbH Karl-Heine-Str. 99, 04229 Leipzig

T. +49 341 49 12 335 F. +49 341 49 12 336 [email protected]

Zusammenfassung: Der erfolgreiche Einsatz von UML (Unified Modelling Language) und Echtzeitbetriebssystemen (RTOS) für die Softwareentwicklung von kleinen und mittleren Embedded-Systemen setzt eine gute Softwarearchitektur voraus. Knappe Ressourcen, harte Echtzeitanforderungen und hardwarenahe Programmteile können die sinnvolle Anwendung der UML mit Codegenerierung in modellbasierter Softwareentwicklung für diese Systeme einschränken. Kenntnisse und Erfahrungen an den Grenzen der hardwarenahen Programmierung zum RTOS und zum generierten UML Code sind erforderlich, um die Vorteile zu optimieren.

2

Moderne Softwareentwicklung für kleine und mittlere Embedded-Systeme

Rahmenbedingungen heutiger Embedded-Systeme Kleine und mittlere Embedded-Systeme charakterisieren sich heute vor allem durch [4]:

• Mikrocontroller 8-, 16- bis 32-Bit • 2-512 kByte ROM • 32-128 kByte RAM • Zeitkritische Abläufe, harte Echtzeitanforderungen, Interrupts • Kein dynamischer Speichercontroller • Betriebssysteme sind nicht Standard, oft properitäre

Laufzeitsysteme (Main-Loop) • Hardwarenahe Programmierung in C oder Assembler • Sicherheitskritische Funktionen

Programm- und Datenspeicher ist begrenzt. Die Abläufe sind häufig zeitkritisch und werden nicht selten durch Interrupts beeinflusst. Dabei wird oft Systemverhalten durch Priorisierung der Interrupts gestaltet. Insgesamt werden in kleinen (deeply-) Embedded-Systemen kaum Betriebssysteme mit dynamischer Speicherverwaltung eingesetzt, da diese den meist geforderten Determinismus der Anwendung nicht gewährleisten. In den meisten Applikationen herrscht zudem das MAIN-Loop RTOS, um schlanke Systeme entwickeln zu können. Die Gründe für den überwiegenden Einsatz von C liegen nach wie vor in technischen Gegebenheiten (Verfügbarkeit von Compilern, Ressourcen des Systems, Sicherheitsvorschriften) und in anderen Gebieten (Verfügbarkeit von Know-How, vorhandene Investitionen, fertige Programmteile) [4]. Nicht zuletzt kennzeichnen sich kleinere Embedded-Systeme zunehmend durch sicherheitskritische Funktionen, da der Einsatz kleiner Steuerungen zunehmend in sichere Bereiche vordringt (elektrische Lenkung, elektrische Bremsen, Bremsassistenten, Autopiloten und viele andere Einsatzgebiete).

3

Komplexität von Embedded-Softwareprojekten Die Software von Embedded-Systemen wird dabei zunehmend komplexer und dabei in seinen bisherigen Rahmenbedingungen gleichzeitig immer anspruchvoller (schneller, sicherer, zuverlässiger und kleiner). Was ist unter der wachsenden Komplexität zu verstehen? Zum einen wächst natürlich die Softwaregröße je Mikrocontroller mit leistungsfähigerer Vernetzung und aufwändigeren Protokollen (z.B. TCP/IP und USB als Standardschnittstellen der nächsten Gerätegenerationen). Zum anderen tragen wachsende Anforderungen an Echtzeit, Sicherheit, Verfügbarkeit und Funktionalität erheblich zum vorhandenen Fehlerpotential bei der Entwicklung bei. Für komplexe Software-Systeme sind Erfahrungen und Kompetenzen in Softwareentwurf und Architektur erforderlich. Bisher nicht ausreichend berücksichtigte Themen der Softwarearchitektur werden zukünftig den Erfolg von Embedded-Softwareprojekten maßgeblich mitbestimmen und sind heute schon als Wettbewerbsfaktor zu betrachten[1].

Abbildung 1: Steigende Programmgröße / Prozessorarchitektur [2]

In der Praxis sind dagegen derzeit oft Vorgehensmodelle und Entwicklungsmethoden anzutreffen, die aus „alten“ Zeiten der 8- und 16-Bit Programmierung (meist ohne kommerzielles Echtzeit-Betriebssystem(RTOS)) stammen und nun bei ungleich komplexeren Systemen beibehalten werden.

0

100

200

300

400

500

600

700

800

900

1000

Av

era

ge

Nu

mb

er

of

K L

ine

s o

f C

od

e

8 Bit 16 Bit 32 Bit

Processor Architecture

4

Zusammenfassend beschreibt das folgende Zitat aktuelle und zukünftige Tendenzen: „Industrien der unterschiedlichsten Anwendungsbereiche haben einen neuen gemeinsamen Kernbereich: Softwareintensive, in Netzwerke eingebundene multimediale Daten verarbeitende eingebettete Systeme“[1]

Perspektiven von Embedded-Softwareprojekten Durch die ständig wachsende Komplexität der Software für kleine und mittlere echtzeitfähige Systeme (Embedded-Systeme für z.B. Automotive, Luftfahrt, Steuerungstechnik) ist die Bedeutung von Softwarearchitektur, objektorientierten Ansätzen und Echtzeitbetriebssystemen in der Programmierung und Wiederverwendung von Bibliotheken und Komponenten stark gestiegen. Um den neuen Anforderungen gerecht zu werden, sind zunehmend Methoden und Werkzeuge des Entwurfs von komplexen IT-Anwendungen anzutreffen, wie z.B. die Modellierung mit UML - teilweise mit integrierter Code-Generierung (CASE Tools). Weiterhin werden immer häufiger komplexe Bibliotheken (Treiber, Protokollstacks wie TCP/IP oder USB und Graphical-User-Interfaces GUI) zugekauft oder wieder verwendet. Diese sind meist prozedural geschrieben und müssen mit der objektorientierten Welt „verheiratet“ werden. Weil dazu im Bereich der Embedded-Systeme sehr hohe Anforderungen an die Echtzeitfähigkeit gestellt werden, ist die Integration dieser Komponenten mittels objektorientierten Konstrukten in vielen Projekten problematisch, insbesondere da das herkömmliche Schichtenmodell (Hardware-Treiber-RTOS-Applikation) aus Gründen der Echtzeitanforderung nicht immer eingehalten werden kann.

Was bedeutet Kreativität und Steigerung der Produktivität in der Softwareentwicklung? Was aber soll nun das Mittel sein? Was sind die Methoden?

5

Bisher wurde Embedded-Software aus kleinen überschaubaren Anfängen heraus entwickelt. An die enorm wachsende Komplexität wurde vor 10 Jahren nicht gedacht. Einige Routinen in der Main-Loop, ein paar Interrupt Service Routinen, die Asynchronitäten und gemeinsamen Datenzugriffe per globale Flags synchronisiert, fertig war das System.

Quelle: Willert Software Tools Irgendwann wurde das System jedoch größer, die Abhängigkeiten wuchsen (aber Achtung: die meisten Abhängigkeiten wachsen nicht linear, sondern exponentiell) und das System hat niemand mehr wirklich verstanden. Oder nur noch einige wenige, die Helden. Durch neue Mitarbeiter sinkt dann natürlich die Effizienz spürbar, weil die einzigen kompetenten Mitarbeiter nun mit der langwierigen Einarbeitung neuer Leute beschäftigt sind. Solche Systeme kommen oftmals zu einem gewissen Punkt an ihre Grenzen und werden schwere Pflegefälle, die Ressourcen binden und Innovationen verhindern.

6

Ein Beispiel:

Verkettungen dieser if-else Konstrukte in sequentiellen oder überlappenden (Interrupt-Service-) Funktionsaufrufen bestimmen das Systemverhalten. Wenn in Fkt_A Änderungen erfolgen, kann das Einfluss auf das Laufzeitverhalten und auf die Funktionalität von Fkt_B, C oder D haben. Das Verhalten der einzelnen Funktionen ist separat nicht nachvollziehbar und nicht entkoppelt. Fehler und Änderungen können systemweiten Einfluss haben. Eine Software dieser Art ist riskant, schlecht wart- und erweiterbar. Generell gilt: Je loser ein System gekoppelt ist, desto einfacher können seine Bestandteile konfiguriert und neu kombiniert werden (Wiederverwendbarkeit) [9] und das Risiko sinkt (Fehlervorbeugung, Fehlervermeidung).

7

Quelle: Willert Software Tools Werden die einzelnen Bestandteile eines Systems beispielsweise durch die Verwendung eines RTOS entkoppelt, so wird dadurch die Verständlichkeit, aber auch die Wartbarkeit und Erweiterbarkeit der Software wesentlich verbessert. Die Wahrscheinlichkeit des Auftretens systemweiter Fehler sinkt. Fehler innerhalb von Komponenten sind mit gesenktem Aufwand zu beheben [9].

8

Verbesserung der Entwicklung von Embedded-Systemen mit RTOS, objektorientierten Ansätzen und UML Die beschriebene Steigerung der Produktivität von Embedded-Softwareprojekten kann durch den Einsatz von geeigneten Softwarearchitekturen und der Verwendung von objektorientierten Methoden erreicht werden. Weiterhin ist der Einsatz von UML zum Systementwurf und Systemdesign geeignet. Die UML kann zur Analyse, Entwurf und Modellierung von Softwareprojekten verwendet werden. Dabei stellt die UML Notation diverse Diagramme und Elemente zur Verfügung, um ein Softwaresystem aus verschiedenen Sichten zu beschreiben [11]. Statisches Modell:

- Anwendungsfalldiagramm - Klassendiagramm - Objektdiagramm - Komponentendiagramm - Einsatzdiagramm

Dynamisches Modell:

- Zustandsdiagramm - Sequenzdiagramm - Kollaborationsdiagramm - Aktivitätsdiagramm

Die UML bildet sich letztendlich auf objektorientierte Konstrukte ab. Diese müssen in C darstellbar sein, damit die Anwendung erfolgreich ist. Da dies (je nach vorhandenen Ressourcen) nur teilweise möglich ist, ist der verwendbare UML Vorrat nicht immer nutzbar [4][6]. Folgend die wichtigsten objektorientierten Konstrukte:

• Klassen • Instanzierung von Objekten • Timer • Mailboxen • Events, Messages • Ports

9

Einige dieser Konstrukte werden durch ein (Embedded-) Betriebssystem (RTOS, Real Time Operating System) zur Verfügung gestellt (Timer, Mailboxen, Events, Messages und ein Scheduler mit Taskverwaltung), andere werden mit C nachgebildet. Aus eventuellen Mangel an dynamischem Speicher können nicht alle objektorientierten Bestandteile sinnvoll und ressourcensparend in C implementiert werden. Daher wird gern auf einige verzichtet (Vererbung, Überladung, intensive dynamische Instanzierung von Objekten zur Laufzeit).

Embedded-UML Es sind die Besonderheiten von Embedded-Systemen in objektorientierten Systemen zu berücksichtigen. Dies gilt generell, sobald in einer Software ein RTOS zum Einsatz kommen soll. Dabei ist insbesondere auf

• Systembeschreibung / Architektur • Entkopplung der Programm-Module bezüglich Laufzeit, Funktion,

Daten und Priorität • Target-Unabhängigkeit, Simulation, Rapid-Prototyping • Verwendung vorhandener (getesteter) C-Module und Bibliotheken

zu achten. Ein Embedded-System benötigt hardwarenahe zum Teil hart echzeitkritische Reaktionen und Determinismus bei hardwarenahen Diensten. In diesem Kontext sind dies:

• Harte Echtzeit-Reaktionen • Interrupts, Interruptprioritäten • Hardwarenahe Routinen (z.B. DMA (Direct Memory Access)) • Determinismus

10

Grenzen des sinnvollen RTOS und UML Einsatzes Werden objektorientierte UML Konstrukte in C weggelassen, ist sie fast beliebig anwendbar und ohne RTOS in C umsetzbar. So können z.B. statt Messages sogenannte Guards (Bedingungen, „Wächter“) zur Steuerung von UML-Statecharts genutzt werden; Messages und Mailboxen entfallen dann (synchrones Modell). Allerdings kann dann durchaus der Nutzen der UML Notation in Frage gestellt sein, siehe dazu [5]. Eine oft genutzte Einschränkung ist der Verzicht auf Klassen und dynamischer Instanzierung. Stattdessen arbeiten kleine Embedded- Systeme mit statischen Objekten und Singleton-Objekten. Damit braucht das System keine dynamische Speicherreservierung (malloc), die in den meisten Embedded-Targets ohne MMU (Memory Mangement Unit) zur gefürchteten Speicherfragmentierung und damit zum sporadischen Fehlverhalten des Systems führen kann. Bestimmte (benötigte) Zusatzinformationen des Embedded- Systems sind nicht ausreichend beschreibbar (Interrupt-Services, Interrupt-Prioritäten, Verschachtelungen und hardwarenahe Routinen). Die UML stellt keine entsprechenden Möglichkeiten zur Verfügung. Auch sind nicht alle Programmteile über UML oder RTOS-Funktionalitäten umsetzbar. Beispielhaft ist hier die harte Echtzeit untersucht: Objekte können in UML „aktiv“ sein, sie haben zur Laufzeit einen eigenen Task/Thread. Dies ist über ein RTOS effizient darstellbar. Die maximale (Echtzeit-) Auflösung hängt stark von der Task- (Kontext-) Umschaltung des verwendeten Betriebssystems ab.

- 8051, 8-Bit, RTX51 (KEIL), bis 100µs [7] - C166, 16-Bit, ARTX (KEIL), bis 25 µs [7] - M16, 16-Bit, EmbOS (Segger), bis 32 µs [8] - ARM7, 32-Bit, ARTX-ARM (KEIL), bis 5 µs [7]

Die Interrupt-Latenzzeiten in den untersuchten Betriebssystemen liegen zwischen 50µs (8051) und 1,8µs(ARM7).

11

In vielen Applikationen wird daher auf Treiberebene die schnelle Datenverarbeitung oder Prozessreaktion ausgeführt, bevor über das RTOS ein Task zur Reaktion der Applikation initiiert wird. Wenn zum Beispiel über einen UART eine Befehlssequenz empfangen wird, werden zunächst alle Daten der Sequenz in der Treiberebene erfasst und vorverarbeitet, bevor die Applikation benachrichtigt wird. Damit wird nicht bei jedem Datumsempfang Rechenzeit für die Taskumschaltung benötigt. Es ist naheliegend, dass der Laufzeitanteil des RTOS keinen erheblichen Teil der verfügbaren Ressourcen verwenden soll. Anhand der Task-Umschaltzeiten kann abgeleitet werden, dass diese Mechanismen nur für wesentlich geringere Laufzeitanforderungen verwendet werden sollten. Die Zeit für einen Task-Wechsel liegt bei 8-Bit Mikrocontrollern deutlich im Millisekunden- und beim 32-Bit Mikrocontroller im 100µs Bereich (zusätzlich abhängig von der Anzahl der Nebenläufigkeiten, die diese Zeiten ebenfalls noch erhöhen können). Alle „härteren“ Anforderungen sollten weiterhin hardwarenah entworfen und implementiert werden. Neben den zeitlichen Aspekten muss jedoch auch der Speicherverbrauch durch den Einsatz eines RTOS und die Verwendung eines UML-CASE-Tools betrachtet werden. In [5] wird durch Erfahrung (Randbedingungen: Rhapsody in C, Codegenerierung, UML-Framework mit kleinem Betriebssystem) eine Grenze bei Systemen mit < 32kb ROM Applikation hergeleitet, unter der ein Einsatz von UML nicht mehr sinnvoll erscheint. Damit stellt sich die Frage nach dem effizienten Einsatz von UML in Embedded-Systemen. Bei sehr kleinen Projekten können sich somit der erhöhte Speicherverbrauch sowie die zusätzliche benötigte Rechenzeit durchaus negativ auswirken, wenn es gilt mit sehr begrenzten Ressourcen auszukommen.

12

Architektur Bei größeren Systemen ist auf die Grenze der Embedded-Paradigmen zur UML zu achten und ist damit ein wesentlicher Erfolgsfaktor von Embedded-UML-Projekten. Kann die Grenzbetrachtung in eine Softwarearchitektur einfließen und als Designvorgabe umgesetzt werden, treten deutlich weniger Schwierigkeiten beim Umsetzen von komplexen Projekten auf.

Abbildung 2: Verbrauchte Ressourcen in Codegröße (stat.)

und Laufzeit (dyn.) [2] So hat das objekt-orientierte Design deutliche Vorteile im System- (Applikation-) Layer, wo ca. 80% des Codes nur mit 20% der Laufzeit behaftet sind. Bei den 20% laufzeitkritischem Code kommen die Embedded-Erfahrungen voll zum Tragen.

13

Anforderungs- und Systemanalyse, Softwarearchitekturen Embedded-Systeme sind ereignisorientierte und keine datenorientierte Softwaresysteme. Der Fokus liegt auf der Reaktion von Systemen auf Ereignisse und der dabei stattfindenden Kommunikation einzelner Teile des Systems. „Die Softwarearchitektur beschreibt die Komponenten und Schnittstellen, aus denen das System besteht, und deren Zusammenspiel“[10] In Embedded-Systemen sind zu beachten:

• Echtzeitfähigkeit / Prioritäten • Funktionale Abhängigkeiten • Datenfluss–Abhängigkeiten • Zeitliche Abhängigkeiten

Grundlagen zur Softwarearchitektur Softwarearchitektur vereinigt in sich das Know-How eines Unternehmens und besitzt durch die Wiederverwendbarkeit einzelner Bestandteile einen projektübergreifenden Einfluß[10]. Ziel muss es sein, das Know-How des Unternehmens, dass prozessbezogen und nicht targetabhängig ist, so zu kapseln, dass es problemlos auf neuen Plattformen wieder verwendbar ist. Die Scheu davor, eine Embedded-Software neu zu entwerfen („Redesign“) liegt meist nicht in der Verwendung neuer Hardware (und neuer Mikrocontroller) sondern in der Umsetzung der Applikation darauf. Zur Beherrschung der Komplexität ist die Betrachtung der Architektur aus verschiedenen Perspektiven notwendig[12].

• Anforderungsperspektive (Requirements) • Perspektive des Systemdesigns • Perspektive von Infrastruktur und Verteilung

14

Im Folgenden betrachten wir die Perspektive des Systemdesigns, um eine beispielhafte Architektur zu entwerfen. Dazu gehören (bei vollständigem Entwurf)[2][12]: Statische Sichten: Komponenten, Klassen Struktur und Schnittstellen Dynamische Sichten: Verhalten, Mechanismen (Prioritäten) Objekte und Threads Die statische Sicht bildet sich übergeordnet als Schichtenmodell ab, das den Rahmen eines Entwurfsmusters vorgibt. Dabei ist auf die Besonderheiten von Embedded-Systemen einzugehen:

Abbildung 3: Vergleich native und Embedded-Systeme[2]

Bei nativen Systemen, wie sie beispielsweise aus dem PC-Bereich bekannt sind, werden die Schichten im Normalfall von oben nach unten durchlaufen. Bei diesem synchronen Kommunikations-Mechanismus werden alle Zeiten sowie die Prioritäten durch die Methodenaufrufe vererbt. Hingegen werden bei Embedded-Systemen die Schichten meist von unten noch oben durchlaufen (Interrupt/ Ereignis getrieben). Eine synchrone (zeitliche) Kopplung, wie sie bei nativen Systemen auftritt ist hierbei zu verhindern.

15

Dabei gibt es in Systemen, die ereignisgetrieben sind, die Frage, wie diese Schichten entkoppelt und wie Ereignisse (z.B. Interrupts) in den oberen Schichten erkannt werden. Dabei soll auch die Wiederverwendbarkeit der hardwarenahen Schicht betrachtet werden. Eine notwendige Identifikation in der Applikationsschicht von Ereignissen in der Hardwareschicht durch Polling ist dabei zu vermeiden. Hier können Messages und Events eingesetzt werden. Beide setzen jedoch einen Adressaten voraus. Damit müsste die hardwarenahe Schicht den Empfänger in der Applikation kennen und wäre somit nicht mehr uneingeschränkt wieder verwendbar. Die Wiederverwendbarkeit von Treibern oder Interrupt Service Routinen kann durch Callback-Funktionen oder Publisher-Subscribern verbessert werden.

Abbildung 4: Entkopplung und Wiederverwendbarkeit

hardwarenaher Schichten[2]

16

Herleitung eines geeigneten Schichtenmodells für kleine und mittlere Embedded-Systeme Diese Design-Pattern zur Entkopplung der Schichten sind häufig in Embedded-Systemen anzutreffen. Auch hier erkennt man, dass die objektorientierte Herangehensweise auf RTOS und Applikation sehr viele Vorteile bietet, die Anbindung und Implementierung der hardwarenahen Schichten jedoch Mechanismen benötigt, die schwer mit Objektorientierung und UML vereinbar sind.

Abbildung 5: Schichtenmodell komplexer Embedded-Systeme mit RTOS[2] Bei dem Schichtenmodell ist zu sehen, dass es Interrupt-Service- und Treiber-Routinen geben kann, die am OSAL und RTOS vorbei hardwarenahe Dienste realisieren und eventuell die Applikation im zeitunkritischen Teil unterrichten. Das gleiche gilt auch für bereits vorhandene Programm-Module oder zugekaufte Bibliotheken. Auch diese sollen im Gesamtsystem, teilweise auf allen Schichten, weiter eingesetzt werden. Beispiele sind Grafik-Bibliotheken oder sicherheitstechnische Überwachungen. Es gibt in vielen kleinen Embedded-RTOS keine standardisierte Treiber-Einbindung. Kommunikation, Meßdaten und ähnliches werden oft direkt zwischen Applikation und Hardware-Layer stattfinden. Grund dafür sind die beschriebenen Grenzen, wie zum Beispiel Echtzeitanforderungen.

17

Bei der Entwicklung von Embedded-Systemen ist diesen Gegebenheiten in der Praxis Rechnung zu tragen. Um die Applikation tatsächlich hardwareunabhängig zu entwerfen, genügt das klassische OSAL in der Regel nicht. Entweder erweitert man dieses stark oder es werden weitere Schichten in der Applikation geschaffen, die Hardwareabhängigkeiten außerhalb des OSAL kapseln. Für solche Fälle empfiehlt sich die Einführung eines „Adapter- oder Abstarktion-Layers“ in der Applikationsschicht. Dieser soll alle echtzeitkritischen und prozeduralen Dienste, die am Schichtenmodell vorbei gehen müssen, mit der objektorientierte Applikation verbinden und die Anwendung dieser Dienste vereinfachen.

Abbildung 6: Schichtenmodell mit hardwarenahen Objekten und Adapter

Wird dieses Vorgehen bei Echzeitsystemen sauber umgesetzt, können erfahrungsgemäß mehr als 90% der Applikation in einem neuen Projekt direkt wiederverwendet werden. Dasselbe trifft auf die Wiederverwendbarkeit der hardwarenahen Layer zu. Ziel ist, für die genannten Probleme eine Referenzlösung zu schaffen, die den Entwicklern gleichartiger Systeme zur Verfügung stehen kann und damit den Basisentwurf solcher Systeme erheblich vereinfacht.

18

Entwicklung einer Referenzlösung für ein „Adapter-Layer“ Im Folgenden soll eine Referenzlösung für ein „Adapter-Layer“ geschaffen werden, mit deren Hilfe ein Entwickler in der Lage sein soll die echtzeitkritischen und prozeduralen Dienste, die sich außerhalb des Schichtenmodells befinden, zu kapseln. Bei der Entwicklung eines solchen „Adapter-Layers“ müssen auf der Basis der Objektorientierung und der durch UML zur Verfügung gestellten Elemente folgende Vorüberlegungen stattfinden:

- Welche Strukturierung soll verwendet werden? - Wie können die Funktionalitäten in Klassen und Objekte gekapselt

werden? - Welche Form der Kommunikation zwischen Applikation und

„Adapter-Layer“ soll angewendet werden? - Können Elemente und Verhaltensweisen von Objekten

gegebenenfalls vereinheitlicht werden? Anhand dieser Punkte sollte schon zu Beginn der Implementierung erreicht werden, dass dem zu entwickelnden „Adapter-Layer“ ein einheitliches Konzept zugrunde liegt. Durch die Verwendung eines modularen und einheitlichen Aufbaus sowie die Vergabe von vereinheitlichten Bezeichnern für Objekte und Kommunikationselemente werden Erweiterungen und Änderungen vereinfacht. Außerdem wird die Einarbeitung in die Anwendung eines solchen „Adapter-Layers“ wesentlich erleichtert. Eine gute Strukturierung kann durch die Aufteilung in Pakete erzielt werden. Alle benötigten Objekte, Klassen und zusätzliche Komponenten für die Bereitstellung eines Dienstes wie beispielsweise die Bereitstellung eines Flash File Systems oder die Kommunikation via TCP/IP können hierbei jeweils in einem Paket abgelegt werden.

19

Abbildung 7: Paketstruktur des „Referenz-Adapter-Layers“

Je Dienst, der innerhalb des „Adapter-Layers“ gekapselt werden soll, muss die Entscheidung getroffen werden, ob eine Klasse oder ein Objekt zur Kapselung der Funktionalitäten geeigneter ist. Für einen Dienst, wie beispielsweise die CAN-Kommunikation, wird eine Klasse geeigneter sein, da für jede anzusteuernde Schnittstelle lediglich eine Instanz dieser Klasse erstellt werden muss. Für andere Dienste die auf dem System einzigartig sind kann hingegen ein (Singleton) Objekt verwendet werden.

Abbildung 8: Referenz-Klasse und –Objekt

20

Bei den verwendeten Operationen innerhalb von Klassen und Objekten erleichtern vereinheitlichte Namen die Anwendung, Erweiterung und Änderung von Funktionalitäten. Bei der Instanzierung von Klassen muss jedoch beachtet werden, dass diese nach Möglichkeit nicht zur Laufzeit angelegt werden (dynamische Speicherverwaltung). Besser für die Wahrung der Echtzeitfähigkeit des Systems ist das Anlegen einer Instanz bereits während der Modellierung. Für die Kommunikation zwischen Elementen der Applikation und des „Adapter-Layers“ gibt es verschiedene Ansätze. Für den internen Informationsaustausch können beispielsweise Callback-Funktionen verwendet werden, aber auch Ports oder Interfaces können für diese Form der Kommunikation eingesetzt werden.

Abbildung 9: Interner Informationsaustausch mittels Ports

Wurde ein Port für ein Element des „Adapter-Layers“ angelegt, so können mittels Interface-Objekten die Ein- und Ausgabe zugewiesen werden. Innerhalb dieser Interface-Objekte werden „Events“ angelegt, die zum eigentlichen Informationsaustausch zwischen den beiden Elementen genutzt werden. Werden innerhalb des „Adapter-Layer-Elements“ bestimmten Events Funktionen „zugewiesen“ (mittels Statecharts), so können auf diese Weise Funktionen aufgerufen („EingabeDienst1“) und Rückmeldungen („AusgabeDienst1“) realisiert werden.

21

Abbildung 10: Statechart von KlasseDienst1

Wie man sieht kann durch ein gut durchdachtes Konzept mit Vereinheitlichung bei der Namensgebung erzielt werden, dass sämtliche Funktionalitäten die mittels „Adapter-Layer“ gekapselt und auf einer hohen Ebene zur Verfügung gestellt werden ohne größere Einarbeitung angewendet werden können. Wird dieses Schema auch bei der Implementierung aller weiteren Dienste angewendet, so erhält man ein „Adapter-Layer“ welches vielseitig einsetzbar ist.

Abbildung 11: Sequenzchart für den Aufruf von „evDienst1Start“

Eine Applikation auf Basis einer solchen Schicht kann somit in allen Belangen unabhängig von den verwendeten Treibern und Bibliotheken implementiert werden. Im Falle einer Portierung auf eine neue Hardware muss lediglich der „Adapter-Layer“ angepasst werden. Die Applikation wird unverändert übernommen. Dadurch reduziert sich der Portierungsaufwand auf ein Minimum.

22

Exemplarische Implementierung eines „Adapter-Layer-Dienstes“ Im Folgenden soll die oben beschriebene Referenzlösung für die Implementierung eines TCP/IP-Dienstes adaptiert werden. Dadurch soll die praktische Umsetzung anhand einer real existierenden Problemstellung veranschaulicht werden. Die gegebene Tool-Chain besteht aus „Rhapsody in C“ von Telelogic, der Bridge WST52 von Willert Software Tools sowie dem RealView MDK 3.10 und der RL-ARM RealView® Real-Time Library der Firma Keil. Da innerhalb der Real-Time Library ein TCP/IP Protokoll Stack (TcpNet) zur Verfügung gestellt wird, der allerdings am Schichtenmodell vorbeigeht, soll dieser Dienst innerhalb des „Adapter-Layers“ zur Verfügung gestellt werden, um eine Entkopplung zwischen den Bibliotheks-Funktionen und der Applikation zu ermöglichen. Durch TcpNet werden sowohl alle grundlegenden Funktionalitäten zur Kommunikation via TCP/IP und UDP wie auch zusätzliche Applikationen (z.B. HTTP-Webserver, CGI-Scripting, Telnet) zur Verfügung gestellt. Für den Dienst werden innerhalb des „Adapter-Layers“ ausgewählte Funktionalitäten gekapselt, die durch die Bereitstellung von entsprechenden Operationen und Events realisiert werden. Als Funktionalitäten wurden im vorliegenden Fall die folgenden ausgewählt:

- Erstellen eines TCP/IP-Servers - Erstellen eines TCP/IP-Clients - Versenden von Daten via TCP/IP - Empfangen von Daten via TCP/IP - Beenden der TCP/IP-Kommunikation

23

Aufruf der Funktionalität Mögliche Rückmeldungen Operation evTcpServerCreate evTcpServerCreateOK

evTcpServerCreateFailed opTcpServerCreate

evTcpClientCreate evTcpClientCreateOK evTcpClientCreateFailed

opTcpClientCreate

evTcpDataSend evTcpDataSendOK evTcpDataSendFailed

opTcpDataSend

evTcpDataReceive evTcpDataReceiveOK evTcpDataReceiveFailed evTcpDataReceiveDone

opTcpDataReceive

evTcpConnectionClose evTcpConnectionCloseOK evTcpConnectionCloseFailed

opTcpConnectionClose

Im nächsten Schritt wird eine Klasse mit den benötigten Operationen angelegt und anschließend die Implementierung hinzugefügt. Außerdem werden noch die Interface-Objekte zur Sammlung der „Funktions-Aufrufe“ und Rückmeldungen benötigt, die dem speziell angelegten Port zugewiesen werden.

Abbildung 12: Instanz mit Port für eine TCP/IP-Verbindung

Das Verhalten dieser Klasse zur Realisierung einer Kommunikation via TCP/IP wird durch ein Statechart definiert. Darin können alle Zustände sowie die Zustandsübergänge dieses Elements des „Adapter-Layers“ grafisch modelliert werden.

24

Abbildung 13:Statechart für eine TCP/IP-Verbindung

Der Reaktionen auf den Aufruf einer Funktionalität lässt sich am besten durch ein Sequenzdiagramm darstellen. Darin wird veranschaulicht welche Events, Operationen und Funktionen ausgeführt werden und welche Objekte an der internen „Kommunikation“ beteiligt sind.

Abbildung 14: Erstellen eines TCP/IP-Servers (Sequenzchart)

25

Besonders anhand des Sequenzcharts kann man erkennen, dass das Objekt aus der Applikation lediglich mit Elementen aus dem „Adapter-Layer“ kommuniziert. Direkte Zugriffe auf die Bibliotheks-Funktionen werden nicht durchgeführt. Durch die daraus resultierende Entkopplung könnte zu einem späteren Zeitpunkt beispielsweise der hierbei verwendete TCP/IP Protokoll Stack ausgetauscht werden. Alle nötigen Änderungen können direkt im „Adapter-Layer“ durch Anpassungen der Operationen durchgeführt werden. Die Applikation kann anschließend unverändert wieder verwendet werden. FAZIT Die hier aufgezeigten Ansätze sind für eine Verbesserung der Softwareentwicklung im Bereich der Embedded-Systeme geeignet und empfehlenswert, um die Systeme für die aktuelle Generation zu entwickeln. Die Grenzen der beschriebenen Methoden sollten bekannt sein und in einer guten Architektur berücksichtigt sein. Die Kunst ist, zu erkennen, was wo gemacht wird (Applikation vs. Treiber- und Interrupt-Ebene) und wie diese Schichten so zusammenarbeiten, dass die Echtzeit und die Entkopplung des Applikations-Know-Hows gleichermaßen berücksichtigt wird. Dazu zeigt dieser Beitrag mögliche Lösungsansätze.

26

Quellenverzeichnis [1] Vortrag „Eingebettete Systeme –Die neue IT-Revolution“,Prof. Dr.-Ing. Werner Grass, Lehrstuhl für Rechnerstrukturen, Universität Passau, [email protected] [2]Vortrag „Softwarearchitektur und RTOS“, Andreas Willert, Willert Software Tool GmbH und Olaf Winne, Quategra GmbH, 2005-2007 [3] Standish Group (http://www.standishgroup.com) veröffentlicht in regelmäßigen Abständen den sogenannten „Chaos Report“ (hier 2000) [4] „Codegenerierung aus UML nach ANSI-C für kleine Embedded-Systeme“, Dr.Martin Geier, 16.12.2002, method park GmbH [5] „UML für kleine Embedded- Systeme“, Elektronik Praxis 07/07, Andreas Willert, Vogel Verlag [6] Object Based Development Using the UML and C, Mark Richardson, Senior Application Engineer, I-Logix (now Telelogic). [7] KEIL RTX Datasheets, www.keil.com [8] Segger EmbOS Datasheet, www.segger.de [9] Der pragmatische Programmierer, Andrew Hunt und David Thomas, Carl Hanser Verlag, ISBN 3-446-22309-6 [10] Modernen Softwarearchitektur, Johannes Siedersleben, dpunkt.verlag, ISBN 3-89864-292-5 [11] jetzt lerne ich UML, Joseph Schmuller, Mark+Technik Verlag, ISBN 3-8272-6591-6 [12] iSQI: „Grundlagen der Softwarearchitektur“, 2003