Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller Pius Horn BACHELORARBEIT zur Erlangung des akademischen Grades Bachelor of Science (B.Sc.) Studiengang Angewandte Informatik Fakultät Elektrotechnik, Medizintechnik und Informatik Hochschule für Technik, Wirtschaft und Medien Offenburg 30.03.2021 Durchgeführt bei der Firma querdenker engineering GmbH Betreuer Prof. Dr. D. Fischer, Hochschule Offenburg M.Sc. Florian Seibold, querdenker engineering GmbH
Horn, Pius: Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller / Pius Horn. – BACHELORARBEIT, Offenburg: Hochschule für Technik, Wirtschaft und Medien Offen- burg, 2021. 43 Seiten. Horn, Pius: Development and construction of an automated test system for peripherals of various mi- crocontrollers / Pius Horn. – BACHELOR THESIS, Offenburg: Offenburg University, 2021. 43 pages.
Eidesstattliche Erklärung Hiermit versichere ich eidesstattlich, dass die vorliegende Bachelor-Thesis von mir selbstständig und ohne unerlaubte fremde Hilfe angefertigt worden ist, insbesonde- re, dass ich alle Stellen, die wörtlich oder annähernd wörtlich oder dem Gedanken nach aus Veröffentlichungen, unveröffentlichten Unterlagen und Gesprächen ent- nommen worden sind, als solche an den entsprechenden Stellen innerhalb der Arbeit durch Zitate kenntlich gemacht habe, wobei in den Zitaten jeweils der Umfang der entnommenen Originalzitate kenntlich gemacht wurde. Ich bin mir bewusst, dass eine falsche Versicherung rechtliche Folgen haben wird. Ich bin damit einverstanden, dass meine Arbeit veröffentlicht wird, d. h. dass die Arbeit elektronisch gespeichert, in andere Formate konvertiert, auf den Servern der Hochschule Offenburg öffentlich zugänglich gemacht und über das Internet verbrei- tet werden darf. Offenburg, 30.03.2021 Pius Horn
Zusammenfassung Entwicklung und Aufbau eines automatisierten Testsystems für Peripherie verschiedener Mikrocontroller Das Ziel der vorliegenden Abschlussarbeit war es, ein automatisches Testsystem für die Entwicklung der Embedded-C++-Softwarebibliothek semf zu erstellen. Bei den Software-Modulen die dabei getestet werden, handelt es sich um sogenannte Hardware-Abstraktionen, die die Verwendung der von den Mikrocontroller Her- stellern bereitgestellten Hal-Bibliotheken vereinfachen. Die wohl größte Herausforderung dieser Abschlussarbeit bestand darin, die von dem zu testenden Gerät nach außen gesendeten Nachrichten zu validieren, so wie von außen kommende Nachrichten zu generieren, mit welchem die Leseoperationen des zu testenden Gerätes überprüft werden können. Mithilfe einer Helper-Gegenstelle konnte dies jedoch umgesetzt werden. Das Ergebnis ist ein System mit welchem bestehende und zu entwickelnde Hardware-Abstraktionen zuverlässig, automatisch und effizient validiert werden können. Diese Abschlussarbeit ist sowohl für Nutzer von semf, als auch für diejenigen Soft- wareentwickler interessant, die sich für das Testen von eingebetteten Systemen in- teressieren.
Abstract Development and construction of an automated test system for peripherals of various microcontrollers The aim of this thesis was to develop an automatic test system for the Development of the embedded C++ software library semf. The software modules that are tested are so-called hardware abstractions, which simplify the use of the Hal libraries pro- vided by the microcontroller manufacturers. Probably the biggest challenge of this thesis was to validate messages sent to the outside of the device, as well as to generate messages coming from outside, with which the read operations of the device to be tested can be checked. However, this could be implemented with the help of a helper remote station. The result is a sys- tem with which existing and to be developed Hardware abstractions can be validated reliably, automatically and efficiently. This thesis is for users of semf as well as for those software developers who are interested in testing embedded systems.
Inhaltsverzeichnis 1. Einleitung und Einführung 1 1.1. Motivation und Problemstellung . . . . . . . . . . . . . . . . . . . 2 1.2. Zielsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.3. Herausforderungen . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2. Stand der Technik 5 2.1. Embedded Systems Software . . . . . . . . . . . . . . . . . . . . . 5 2.1.1. C++ in Embedded Systems . . . . . . . . . . . . . . . . . . 5 2.1.2. C vs. C++ Wartbarkeit und Entwicklungszeit . . . . . . . . 10 2.1.3. Modulare Softwareentwicklung . . . . . . . . . . . . . . . 12 2.1.4. Portabilität . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2. Embedded C++ Library: semf . . . . . . . . . . . . . . . . . . . . 13 2.2.1. Was ist semf? . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.2. Motivation semf . . . . . . . . . . . . . . . . . . . . . . . 14 2.2.3. Komponenten . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.2.4. Hardware-Abstraktion . . . . . . . . . . . . . . . . . . . . 18 3. Hauptteil 20 3.1. Ausgangssituation . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.2. Konzeption . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.2.1. Design Gesamtsystem . . . . . . . . . . . . . . . . . . . . 21 3.2.2. Design Softwarearchitektur . . . . . . . . . . . . . . . . . 24 3.2.3. Testkonzept . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.3. Umsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.3.1. Software Device Under Test . . . . . . . . . . . . . . . . . 29 3.3.2. Interface Helper - Device Under Test . . . . . . . . . . . . 32 3.3.3. Software Helper . . . . . . . . . . . . . . . . . . . . . . . 37 3.3.4. Testfälle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.3.5. Testauswertung . . . . . . . . . . . . . . . . . . . . . . . . 40 4. Ergebnis 43 4.1. Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Tabellenverzeichnis v
Inhaltsverzeichnis Abbildungsverzeichnis i Quellcodeverzeichnis ii Literatur iii A. Anhang v A.1. Pinout-Tabelle des Helpers . . . . . . . . . . . . . . . . . . . . . . v A.2. Testfälle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vi vi
1. Einleitung und Einführung Für die Entwicklung von Softwareprojekten für eingebettete Systeme wird heut- zutage typischerweise entweder C oder C++ als Programmiersprache gewählt. Da- bei werden von Projekt zu Projekt immer wieder erneut Softwarekonstrukte be- nötigt, die die Handhabung von Ringspeichern, das Loggen von Daten in einen EEPROM-Speicherchip oder die Entprellung von Schaltern ermöglichen. Häufig bedeutet das für Softwareentwickler, dass für ähnliche Problemstellungen immer wieder Programmcode-Auszüge aus verschiedenen Quellen wie dem Internet oder eigenen Softwareprojekten zusammengeführt werden müssen. Sowohl bei C, ins- besondere aber bei C++ geht dieser Vorgang mit Abstrichen in der resultierenden Softwarearchitektur einher, da verschiedene Programmcode-Auszüge, bzw. Module nicht aufeinander abgestimmt sind. Dieser Ansatz unterliegt zudem der Problema- tik, dass er zeitaufwändig ist. Das C++-Framework semf (smart embedded framework) von dem Unternehmen querdenker engineering soll diesen Prozess der Entwicklung von Software für ein- gebettete Systeme vereinfachen und beschleunigen. Dies wird mit einer Vielzahl von Softwaremodulen, die von Entwicklern wiederverwendet werden können, er- möglicht. Diese Softwaremodule von semf lassen sich in hardwareunabhängige Module und Hardwareabstraktionen unterteilen, wobei sich diese Abschlussarbeit lediglich mit letzteren beschäftigt. Hardwareabhängige Module basieren auf der von den Mikrocontrollern bereitgestellten Peripherie und der zugehörigen Hardwa- re Abstraction Layer (HAL). Die HAL ermöglicht einen abstrahierten Zugriff auf die Hardware, wie zum Beispiel die Peripherie des Mikrocontrollers. Durch diese HAL-Schnittstelle kann eine Reduktion der Hardwareabhängigkeit ermöglicht wer- den, indem sie von dem Mikrocontroller Hersteller einheitlich bereitgestellt werden [Ro19]. Die von der Hardware abhängigen semf Module, beziehungsweise Hard- wareabstraktionen, lassen sich als eine Bibliothek verstehen, die sich eine Abstrak- tionsebene oberhalb dieser HAL befindet (siehe Abbildung 1.1). 1
1. Einleitung und Einführung Abbildung 1.1: Schichtenmodell semf Hardwareabstraktionen Projekte die mithilfe von semf entwickelt werden sind prinzipiell hardwareunab- hängig, da für die Peripherie von verschiedenen Mikrocontrollern entsprechende Implementierungen seitens semf verfügbar sind. Diese semf-Projekte verfügen ty- pischerweise über eine Programmcode Datei, die die Gesamtheit der Hardware wie- derspiegelt. Bei einer Hardwareänderung ist lediglich diejenige Datei anzupassen. Dass sich das Verhalten der Hardware-abhängigen semf Module für jeglichen Mi- krocontroller gleichermaßen verhält, beziehungsweise dass ihre fehlerfreie Funkti- on, soll im Rahmen dieser Abschlussarbeit getestet werden. 1.1. Motivation und Problemstellung Die Hardwareabhängigen Softwaremodule von semf wurden bis dato nicht durch- gängig getestet. Eine fehlerfreie Funktionalität, insbesondere bei verschiedenen spe- zielleren Anwendungsfällen, konnte bisher nur mit hohem manuellen Aufwand ge- währleistet werden. Um die Hardwarekomponenten zu testen, kann nicht auf ein- fache Unit-Tests zurückgegriffen werden. Dies liegt daran, dass die Tests auf der tatsächlichen Hardware ausgeführt werden müssen und die Kommunikation mit ei- ner Gegenstelle (ein weiterer Mikrocontroller) notwendig ist, um einige Peripherien testen zu können. Fehlerhaftes Verhalten der semf-Bibliothek kann sowohl auf Bugs in der Mikro- controller HAL oder auf Bugs in der semf-Bibliothek zurückzuführen sein. Diese Fehlverhalten sollen mithilfe eines automatisierten Testsystems erfasst werden kön- nen. Bei diesen Tests handelt es sich um Gray-Box Tests, da die interne Struktur der zu testenden Module für den Testersteller zwar partiell bekannt ist, die tatsächlichen Testroutinen jedoch lediglich von den von semf bereitgestellten Schnittstellen ab- geleitet werden [ST20]. 2
1. Einleitung und Einführung Die Module die auf den folgenden Peripherien basieren, könnten mit Hilfe dieses Testsystems getestet werden: • UART • SPI-Master/ SPI-Slave • I2C-Master/ I2C-Slave • CAN • PWM • GPIO • Input Capture • Analog In (mit und ohne DMA) • Analog Out (mit und ohne DMA) • Flash-Speicher Aktuell existieren semf Module für diese Peripherien für verschiedene STM32 Mi- krocontroller und für den Esp32S2 Mikrocontroller. Das Angebot an semf Imple- mentierungen für weitere Mikrocontroller wird jedoch in naher Zukunft ausgeweitet werden. Zu diesem Zeitpunkt können demnach lediglich die genannten Mikrocon- troller getestet werden. Die zu erstellenden Tests sollen mit möglichst wenig Aufwand und möglichst ohne Redundanz auf verschiedenen Mikrocontrollern lauffähig sein. 1.2. Zielsetzung Ziel dieser Abschlussarbeit ist es, sowohl die Software für ein automatisiertes Test- system zu entwickeln, als auch den tatsächlichen Aufbau der verschiedenen Mi- krocontrollern mit den Gegenstellen durchzuführen. Des Weiteren sollen möglichst viele Tests für verschiedene semf Module geschrieben werden, wobei das vollstän- dige Testen aller Hardwareabhängigen Module den zur Verfügung stehenden zeitli- chen Umfang dieser Abschlussarbeit überschreiten würde. Die zu erstellenden Tests sollen mindestens auf zwei verschiedenen Mikrocontrollern (zum Beispiel einem STM32F7-, einem STM32F3- und einem Esp32S2-Entwicklungsboard) lauffähig sein. Damit ist sichergestellt, dass die Tests unabhängig von dem Mikrocontroller 3
1. Einleitung und Einführung funktionieren. Eine gewünschte Anforderung an die Testroutinen ist, dass diese einfach verständ- lich und problemlos erweiterbar sind. Zudem soll möglich sein, dass die Tests für die verschiedenen Mikrocontroller automatisiert durchgeführt werden können. Hierzu gehört, dass die aktuellste Testsoftware automatisch auf die verschiedenen Mikrocontroller geflasht und anschließend gestartet wird. Außerdem sollen auf dem Hostsystem (Skript-Anwendung auf einem Server an welchen die Mikrocontroller angeschlossen sind) alle Fehlermeldungen zurück gegeben werden können. 1.3. Herausforderungen Die Umsetzung des automatisierten Testsystems ist mit verschiedenen Herausforde- rungen verbunden. Hierzu gehört zum Beispiel die Umsetzung der Kommunikation mit einer Gegenstelle zur Validierung der Peripheriefunktionalität. Dies birgt Kom- plexitäten wie die Synchronisation zwischen zu testendem Mikrocontroller und Ge- genstelle, sowie der allgemeinen Entwicklung der Gegenstelle. Eine weitere Herausforderung besteht darin, dass alle Tests nur einmal geschrieben werden sollen. Diese sollen auf allen Mikrocontrollern lauffähig sein sollen, die die entsprechende Peripherie besitzen. Zudem sollen die Mikrocontroller automatisch geflasht und anschließend die Tests gestartet und ausgewertet werden. 4
2. Stand der Technik Dieses Kapitel stellt die Grundlage für den Hauptteil dieser Abschlussarbeit dar. Dafür wird zuerst auf die Entwicklung von Embedded Software allgemein einge- gangen und anschließend auf die Embedded Software Entwicklung mit Zuhilfenah- me der Embedded-C++-Bibliothek semf. Diese Chronologie ermöglicht, dass die Motivation und Anwendungsfälle von semf, einleuchtend dargestellt werden. Ziel dieses Kapitels ist es, dass der/die Leser/in die Abschlussarbeit und deren Nutzen besser einordnen kann. 2.1. Embedded Systems Software Zu Beginn der Entwicklung eines jeden Embedded Software Projekts sollte man sich Gedanken über die zu verwendende Programmiersprache und die Softwarear- chitektur machen. In diesem Unterkapitel wird C zu C++ in verschiedenen Gesichtspunkten gegenüber gestellt und modulare und portable Softwarearchitektur erläutert. 2.1.1. C++ in Embedded Systems Viele Jahre wurde der überwiegende Anteil an Embedded Projekten mit der Pro- grammiersprache C umgesetzt [Vi20]. Durch ihre Vielseitigkeit, Kompaktheit und hohen Performanz des kompilierten Programmcodes etablierte sich C zu der Pro- grammiersprache, die auf die Anforderungen der Embedded Software Entwick- lern optimal zugeschnitten ist. Dies ist insbesondere bei überschaubaren und nicht allzu umfangreichen Projekten auch heute noch der Fall. Mit zunehmender Kom- plexität von Softwareprojekten werden jedoch die Einschränkungen von C immer 5
2. Stand der Technik deutlicher. Hierzu gehören insbesondere das fehleranfällige Ressourcenmanage- ment und die begrenzten Möglichkeiten Abstraktionen auf höherer Ebene mithilfe von Programmiersprachen-Funktionalitäten umzusetzen. Insbesondere für die Ent- wicklung von komplexen Applikationen ist somit ein hohes Maß an Zeit und Mühe notwendig [Vi20]. Zur gleichen Zeit unterliegt die im Jahr 1985 von Bjarne Stroustrup veröffentlichte Programmiersprache C++ einem ständigen Wandel in Form von hinzukommenden Features und internen Verbesserungen [St18a]. Diese Verbesserungen werden in fortlaufend erscheinenden Versionen veröffentlicht und tragen dazu bei, dass sich C++ als die beste Wahl für komplexe und moderne eingebettete Softwareprojekte etabliert hat [Vi20]. Die folgenden Features stellen einige Vorteile der Program- miersprache C++ gegenüber C dar: • Eine mächtige und vielseite Standardbibliothek. • Objekte und Vererbung. • C++ erweitert lediglich C. • Threads und zugehörige Speicherverwaltung als Teil der Programmierspra- che. Eine mächtige und vielseitige Standardbibliothek Die Programmiersprache C++ weist eine funktionsreiche Standardbibliothek auf. Viele Funktionalitäten die für C Entwickler noch das Aufsuchen von Drittanbie- ter Bibliotheken erfordern, sind mittlerweile in dieser Standardbibliothek enthalten. Durch diese Reduktion von externen Abhängigkeiten kann ein stabileres und vor- hersehbareres Verhalten des entwickelten Programmcodes gewährleistet werden. Ein weiterer wichtiger Aspekt ist die Wartbarkeit von Programmen: Weil sich nie- mand in Spezialbibliotheken einarbeiten muss, können standardkonforme Program- me auch durch anderes Personal mit weniger Aufwand gepflegt werden. Dabei wird die Kenntnis des Standards vorausgesetzt [Br17]. Der C++ Standard stellt häufig verwendete Datenstrukturen so wie Arrays, binäre Bäume und Hash-tabellen zur Verfügung. Diese sind allesamt generisch. Ihre Ver- wendung spart Zeit und resultiert in Applikationen die von Speicherleaks weniger gefährdet sind, als bei Datenstrukturen die eigens programmiert werden [St18b]. 6
2. Stand der Technik Darüber hinaus stellt die Standardbibliothek verschiedene Standardalgorithmen wie zum Beispiel find, sort, binary search, sowie Permutationen und Set-Operationen zur Verfügung. Diese können wiederum auf die zuvor genannten Datenstrukturen angewendet werden. Entwickler können sich somit auf diejenigen Aufgaben kon- zentrieren, die sich eine Abstraktionsebene oberhalb dieser Algorithmen und Da- tenstrukturen befinden. Eine weitere Weiterentwicklung gegenüber C, ist die von der C++ Standardbiblio- thek bereitgestellte String (std::string) Klasse. Diese weit verbreitete Verwendung des String Datencontainers in C++ erlaubt eine intuitivere Art mit Strings umzuge- hen als in C. Eine Auseinandersetzung mit Null Terminatoren ist hierbei nicht mehr notwendig [Po19]. In C werden Strings in Form von Char Arrays dargestellt und das String-Ende aus Konvention mit einem Null-Byte. Soll das Char-Array dynamisch erstellt werden, kann dies mit malloc durchgeführt werden. Dabei muss an das für die Nulltermina- tion benötigte Extrabyte gedacht werden. Die Speicherverwaltung liegt dabei in der Verantwortung des Entwicklers: 1 char *s1 = strdup("Hallo"); 2 char *s2 = malloc(strlen(s1) + 7); 3 strcpy (s2, s1); 4 strcat (s2, ", Welt"); 5 free (s1); 6 s1 = s2; Listing 2.1: Dynamische erzeugte Strings in C In C++ sind Strings Objekte, die über eine automatische Speicherverwaltung und Kontrolle verfügen, wodurch sie insbesondere für Programmierneulinge sicherer und einfacher zu verwenden sind: 1 std::string s = "Hello"; 2 s += ", Welt"; Listing 2.2: C++ Strings Außerdem stehen einige Funktion zur Stringmanipulation zu Verfügung, wie zum Beispiel append, find, copy oder replace. Objekte und Vererbung Programmiersprachen wie C, deren Applikationen als eine Liste aus Instruktionen zu verstehen sind, werden prozedurale Sprachen genannt. Diese Folgen an Instruk- 7
2. Stand der Technik tionen können in Funktionen und Dateien unterteilt werden. Für kleine Programme werden keine weiteren Organisationsprinzipien (auch Paradigmen genannt [La02]) benötigt: Der Entwickler erzeugt eine Folge von Instruktionen und der Computer führt diese aus. Je größer und komplexer die Anwendungen werden, desto deut- licher wird, dass das prozedurale Paradigma konzeptionelle Schwächen aufweist. Dies lässt sich insbesondere auf zwei Gründe zurückführen [Le19]. Zum einen ha- ben Funktionen uneingeschränkten Zugriff auf globale Daten. Zweitens, mit Funk- tionen und Daten die nicht konzeptionell miteinander in Verbindung stehen, lässt sich nur unbefriedigend ein Modell der Wirklichkeit abbilden. Abbildung 2.1 stellt Globale Daten Globale Daten Globale Daten Funktion Funktion Funktion Funktion Abbildung 2.1: Prozedurales Paradigma dar, wie in typischen prozeduralen Programmen mehrere Funktionen auf globale Daten zugreifen und dies zu einer unübersichtlichen Struktur von Zusammenhän- gen zwischen Funktionen und Daten führt. Dies erschwert außerdem die Wartbar- keit. Wird eine Änderung an einem globalen Datenobjekt vorgenommen, so müssen alle Funktionen die auf diese Daten zugreifen angepasst werden. C++ erweitert C um Klassen aus denen Objekte erzeugt werden können. Diese Ob- jekte zeichnen sich dadurch aus, dass sie sowohl Daten als auch Funktionalitäten in Form von Methoden miteinander koppeln, wobei die Objektmethoden mit den objekteigenen Daten arbeiten. Die Daten eines Objekts lassen sich typischerweise ausschließlich mit den Objektmethoden lesen und bearbeiten. Die somit erreichte Datenkapselung (siehe Abbildung 2.2) ist ein wichtiges Prinzip der objektorientier- ten Sprachen. Sollen Daten eines Objekts modifiziert werden, so ist das lediglich mithilfe der Methoden dieses Objekts möglich. Dies vereinfacht Schreiben, Debug- gen und Warten von Programmcode [La02]. Aus Klassen lassen sich außerdem Unterklassen ableiten, wodurch redundanter Pro- grammcode reduziert wird und die logische Struktur des Projekts verbessert werden 8
2. Stand der Technik Objekt Daten Objekt Objekt Member- Funktionen Daten Daten Member- Member- Funktionen Funktionen Abbildung 2.2: Objektorientiertes Paradigma kann. Dieses Konzept wird Vererbung genannt und dient der Wiederverwendbarkeit von Programmcode. Auch wenn das objektorientierte Paradigma gegenüber dem prozeduralen Paradig- ma viele Vorteile verfügt, lassen sich auch verschiedene Nachteile auflisten. Diese fallen gegenüber den Vorteilen nicht sonderlich ins Gewicht [Ob20]: • Erhöhte Kopplung durch Abhängigkeit zwischen Über- und Unterklassen. • Leichte Performanzkosten durch Generalisierungen [Go04]. • Umfangreiche Klassenhierarchien können die Programmgröße des Projekts aufblähen. • Auf Grund der Vererbung ist es nicht mehr ausreichend die Header-Datei ei- nes Moduls zu analysieren, um das Verhalten eine Programmcodedatei zu verstehen. Hierzu müssen nun auch die weiteren Headerdateien der Über- klassen betrachtet werden. Klassen-Browser können hier jedoch für Abhilfe sorgen. C++ erweitert lediglich C. Die Programmiersprache C++ wird auch als eine Obermenge von C bezeichnet [Mi06]. C Programmcode kann somit auch von einem C++-Compiler anstatt ei- nes C-Compilers übersetzt werden. Daher können zum Beispiel Projekte umgesetzt werden, die von dem C++-Sprachumfang lediglich die Funktionalität übernimmt 9
2. Stand der Technik Daten und Funktionen in Klassen, bzw. Objekte zu kapseln, davon abgesehen aber rein in C programmiert sind [He98]. Ursprüngliche C-Entwickler können daher verschiedene Sprachfunktionen von C++ übernehmen ohne den vollen Sprachum- fang auszunutzen. Das ist dann sinnvoll, wenn Entwickler zwar C beherrschen, je- doch nicht die Zeit aufwenden können und wollen, um den Sprachumfang von C++ ganzheitlich zu erlernen. Zudem können jene Features, welche für den hohen Per- formanzanspruch von eingebetteten Systemen zu laufzeit- oder speicherineffizient sind, von den Entwicklern einfach weggelassen werden. Das könnte beispielsweise Mehrfachvererbung, Templates, Exceptions, Namespaces oder zur Laufzeit durch- geführte Typerkennungen betreffen [Me14]. Auch wenn C++ lediglich als eine Erweiterung von C anzusehen ist, kann der Um- stieg von C zu C++ bedeuten, dass Zeit und Ressourcen in Fortbildungen investiert werden müssen um von den zusätzlichen Features von C++ profitieren zu können. Insbesondere der Wechsel von dem prozeduralen hin zu dem objektorientierten Pro- grammierparadigma kann für Entwickler eine größere Umstellung bedeuten. Threads und zugehörige Speicherverwaltung als Teil der Programmiersprache. Der C++11 Standard umfasst eine klar definierte Speicherverwaltung für Projekte die auf mehreren Threads basieren. Im Gegensatz dazu wurden in C hierfür exter- ne Bibliotheken wie zum Beispiel pthreads benötigt, um Applikationen mit meh- reren Threads zu entwickeln zu können. C++-Entwickler können hierzu auf die zu C++ zugehörige Standardbibliothek zurückgreifen. Zur Verfügung stehen zum Beispiel Klassen zur Threaderzeugung, Locks und Mutexe, konditionale Variablen und atomare Variablen. All dies ist auch für Embedded Systems Entwickler inter- essant. Diese Entwickler können somit Multithreading-Applikationen mit determi- nistischem Verhalten umzusetzen, die zusätzlich für alle möglichen Zielarchitektu- ren portierbar sind. 2.1.2. C vs. C++ Wartbarkeit und Entwicklungszeit Die Entwicklung und Wartung von Software ist teuer. Studien ergaben, dass die Wartungskosten dabei meistens mindestens 50% und manchmal sogar bis zu 90% 10
2. Stand der Technik der insgesamten Softwarekosten einnehmen [Gr03]. Außerdem ergab eine Studie von dem National Institute of Standards an Technology (NIST), dass die US-Wirtschaft jährlich ungefähr 60 Billionen Euro durch Softwarebugs und den dazugehörigen Aufwänden und Verlusten einbüßen [02]. Das Department of Computer Science and Engineering der Universität von Kalifornien, Riverside untersuchte die Auswirkung der Wahl zwischen C und C++ auf die Programmcodequalität und die Wartbarkeit [Pa11] und kam hierbei auf interessante Ergebnisse. Untersucht wurden hierbei vier größere Open-Source-Projekte wie zum Beispiel Firefox und Blender, deren Co- debasis größtenteils sowohl aus C als auch C++ bestehen. Dabei wurde folgenden Hypothesen aufgestellt und untersucht: • H1: Im Laufe der Zeit verdrängt der C++-Code den C-Code innerhalb eines Projekts. • H2: C++-Code weist eine höhere interne Qualität als C-Code auf. • H3: C++-Code ist weniger fehleranfällig als C-Code. Für die erste Hypothese (H1) wurden die Projekte über einen Zeitraum von un- gefähr zehn Jahren auf den Verlauf der verhältnismäßigen Anteile an C und C++ untersucht. Dabei wurde auf das Verhältnis zwischen C- zu C++-Dateien, sowie auf deren Verhältnis zwischen effektiven Codezeilen (eLOC) von C zu C++ geschaut. Mithilfe der erhobenen Daten und einer darauf angewendete linearen Regression konnte festgestellt werden, dass sich bei drei von vier der untersuchten Projekten das Verhältnis hin zu mehr C++ verschoben hat. Das deutet an, dass in Projekten die sowohl C- als auch C++-Code enthalten der C-Code tendenziell von C++-Code verdrängt wird. Für die Ermittlung der internen Code-Qualität der C- und C++-Programmcodedateien (H2) wurden diese auf ihre cyclomatic complexity und auf ihre interface complexity untersucht. Da die Normalisierung der Komplexität durch die Division der vorzu- findenden eLOC zu einer verzerrten Komplexität führt, wurde statt dem arithmeti- schen Mittel das geometrische Mittel für die Ermittlung der internen Komplexität gewählt. Die interne Code-Qualität lässt sich mitunter damit beschreiben, wie ein- fach der Code zu verstehen ist und ob er einfach zu warten ist. Die Komplexität gibt hierfür ein gutes Maß. Bei den ermittelten Komplexitäten der C++- und C-Dateien der vier untersuchten Applikationen konnte eine deutlich höhere Komplexität bei den C- gegenüber den C++-Dateien festgestellt werden. Dies lässt auf eine tenden- ziell bessere Code-Qualität bei C++-Dateien schließen. 11
2. Stand der Technik Für die Bestimmung der Fehleranfälligkeit (H3) wurden alle behobenen Bugs die- ser vier Applikationen darauf hin analysiert, welche C- und C++-Dateien für die Behebung der Bugs relevant waren. Die Bugdichte war hierbei bei dem C-Code entschieden höher als bei dem C++-Code. Die Studie gibt einige Hinweise darauf, dass die Verwendung von C++ gegenüber zu C eine höheren Programmcodequalität und bessere Wartbarkeit führt, was sich wahrscheinlich auch auf Embedded Systems Software übertragen lässt. 2.1.3. Modulare Softwareentwicklung Die Skalierbarkeit eines Softwareprojekts geht eng mit der Einhaltung des Pro- grammierprinzips einer modularen Softwarearchitektur [Li07] einher. Unter einer sogenannten funktionalen Modularität versteht sich die Separierung von Funktio- nalitäten in strukturelle Einheiten. Somit wird die interne und externe Kopplung von strukturellen Einheiten möglichst minimiert, wodurch diese Module vielfach innerhalb eines Projekts wiederverwendet werden können. Beispiele für die Umset- zung von Modularität sind Verwendungen von Klassen die durch Vererbung wie- derverwendet werden können, sowie generische Containerklassen und strukturell gekapselte Funktionalitäten. 2.1.4. Portabilität Embedded Systems Entwickler in Industriesegmenten wie Automotive-Steuerung, medizinische Ausrüstung und Verbraucherelektronik stehen unter der Herausfor- derung Produkte schnell und günstig zu entwickeln. Häufig werden Produkte mit Aussicht auf ganze Produktfamilien entwickelt. Dies stellt die Entwickler vor die Herausforderung, die Software auf einen anderen Mikrocontroller oder Geräte mit sich unterscheidenden Peripherien und Funktionalitäten zu portieren [G 96]. Wie aufwändig solch eine Portierung ist hängt damit zusammen, wie eng die Softwa- re an die zugrunde liegende Hardware gekoppelt ist und ist somit eine Frage der Softwarearchitektur. Ziel soll sein, möglichst viel Programmcode eines Vorgänger- modells eines Produkts wiederverwenden zu können. 12
2. Stand der Technik 2.2. Embedded C++ Library: semf Das Ziel dieser Abschlussarbeit ist es, die Embedded-C++-Bibliothek semf bes- ser testen zu können. Um zu verstehen wie das Testen funktionieren sollen, ist es wichtig zu wissen, was semf ist und wie semf aufgebaut ist. All dies soll in diesem Unterkapitel näher beleuchtet werden. 2.2.1. Was ist semf? semf ist eine Softwarebibliothek für die Embedded C++ Entwicklung. Sie umfasst eine Vielzahl an Softwarekomponenten, mit welchen wiederkehrende Standardauf- gaben gelöst werden können und somit Entwicklungszeit gespart werden kann. Die zur Verfügung gestellten Softwarekomponenten sind typischerweise generisch, so- dass sie durch die offene Klassenarchitektur einfach angepasst werden können. Ent- wickelt wird die Bibliothek von dem Unternehmen querdenker engineering welches sich auf die Entwicklung von Embedded Systemen mit Schwerpunkt Embedded Software spezialisiert hat. Selbstgeschriebene Softwarekomponenten im Bereich Embedded können von Ent- wicklern für weitere Projekte häufig wiederverwendet werden. So wurde von quer- denker engineering ein Repertoire an Softwaremodulen aufgebaut, auf welches für interne Projekte zurückgegriffen werden kann. Auf Basis dieses Repertoires kris- tallisierte sich im Laufe der Zeit das Produkt semf heraus, welches seit 2020 auch offiziell vermarktet wird. Die Module von semf wurden unter dem Anspruch entwi- ckelt, dass sie folgende vier Kriterien klar erfüllen müssen [Se20]: 1. Funktional 2. Wiederverwendbar 3. Testbar 4. Benutzerfreundlich Durch die Plattformunabhängigkeit ist semf somit für all jene interessant, die ein Embedded Software Projekt in der Programmiersprache C++ umsetzen möchten. 13
2. Stand der Technik 2.2.2. Motivation semf Um die Entwicklung von Embedded Software zu vereinfachen, existieren verschie- dene Programmbibliotheken die für den Entwickler in Frage kommen können. So gibt es zum Beispiel kommerzielle Produkte von dem Unternehmen Segger oder Qt, sowie Open-Source-Projekte wie embedOS oder die Software von Arduino. Wie sich semf von diesen genannten Projekten unterscheidet, wird nachfolgend erläu- tert. Hierzu werden die verschiedenen Projekte grob zusammengefasst. Segger Das Unternehmen Segger ist ein namhafter Hersteller von Software und Programmier- und Entwicklungstools für eingebettete Systeme. Hierzu gehört unter anderem das Echtzeitbetriebssystem embOS und verschiedene Middleware Treiber und Module in den Bereichen Konnektivität, Kryptografie und Sicherheit sowie in IoT, welche primär in C entwickelt wurden. Ein Interessent für die Software von Segger kann diese verschiedenen Treiber als jeweils eigenständige Module erwerben. Ein poten- zieller Nachteil kann sein, dass die verschiedenen Module nicht immer sehr flexibel sind. Hierzu ein Beispiel: Als Käufer interessiere ich mich für einen von Segger entwickelten Bootloader mit zugehörigem Updater, was prinzipiell out-of-the-box funktioniert. Dabei kann ich mich für einen von mehreren Bootloader-Ausführungen entscheiden. Diese unterscheiden sich zum Beispiel in der Übertragungsweise der für die Aktualisierung vorgesehenen Firmware. Entscheide ich mich für die Ausfüh- rungen mit dem USB-Interface als Übertragungsmedium, so ist der USB-Stack fest in den Bootstrap-Loader implementiert. Will ich nun den USB-Stack zu- sätzlich für einen anderen Anwendungsfall nutzen, zum Beispiel für das Aus- lesen und Ansteuerung des Geräts, so ist das nicht ohne weiteres möglich. Hierfür muss die Segger Software manuell angepasst werden. Dies ist wieder- um fehleranfällig, da fremder Programmcode überbearbeitet werden muss. Die Softwaremodule weißen zwar in sich eine hohe Qualität auf, kombinieren sich untereinander jedoch nur bedingt, was auf die grundlegende Architektur von Segger zurückzuführen ist. 14
2. Stand der Technik Qt Mit dem Toolkit Qt lassen sich grafische Benutzeroberflächen programmieren ,so- wie plattform- und hardwareunabhängige Projekte in der Programmiersprache C++ entwickeln. Dank der Plattformunabhängigkeit und der Benutzerfreundlichkeit ist Qt unter Entwicklern weit verbreitet. Qt eignet sich zudem ausgezeichnet für ein- gebettete Systeme, vorausgesetzt das zu entwickelnde Gerät verfügt über das Be- triebssystem Embedded Linux. Soll Qt für eingebettete Geräte verwendet werden, so ergibt sich die Voraussetzung, dass der zugrunde liegende Mikrocontroller um die entsprechende Rechenleistung und Speicherkapazitäten verfügt. Dies ist notwendig um Embedded Linux betreiben zu können. Für besonders günstige und durch die geringe Rechenleistung energieeffiziente Mikrocontroller ist Embedded Qt daher nicht ausgelegt. Mbed und Arduino Die kostenlosen Open Source Bibliotheken von Mbed oder von Arduino stellen ebenfalls ein Repertoire an verschiedenen Komponenten bereit, welche die Ent- wicklung von eingebetteten Geräten vereinfachen. Mbed ist für Arm Cortex-M Mi- krocontroller ausgelegt und wird in Kombination mit dem zugehörigen Betriebs- system Mbed OS verwendet. Arduino ist ein Unternehmen, dass sowohl Hardware als auch Software produziert, welche frei verwendet werden können [ar20]. Bei- de Bibliotheken verfügen über eine sehr aktive Community. Durch diese wird eine Vielzahl an Beispielprogrammen und Module bereitgestellt, welche für eigene Pro- jekte herangezogen werden können. Für industrielle, insbesondere für sicherheits- kritische Anwendungsfälle entspricht eine Community-basierte Bibliothek gegebe- nenfalls nicht den Ansprüchen in Kriterien wie Qualität, Dokumentation, Support und Zuverlässigkeit. semf Bei der Wahl der optimalen Bibliothek für die Entwicklung von eingebetteten Sys- temen steht den Entwicklern also eine breite Auswahl an Optionen zur Verfügung. Die Motivation hinter semf liegt darin, eine Bibliothek bereitzustellen welche ohne die zuvor genannten Nachteile verwendet werden kann. Die semf-Komponenten, 15
2. Stand der Technik welche in dem folgenden Unterkapitel näher erläutert werden, sind aufeinander ab- gestimmt. So ist zum Beispiel die Konfiguration des Bootloaders bezogen auf das Kommunikationsmedium variabel anpassbar und Kommunikationsprotokolle sind unkompliziert austauschbar. Semf ist sowohl in bare-metal Projekten, als auch in Projekten welche über ein Betriebssystem wie zum Beispiel FreeRTOS verfügen, verwendbar. 2.2.3. Komponenten Semf verfügt über verschiedene Komponenten, welche sich in verschiedene Kate- gorien gliedern lassen. Sie sind völlig losgelöst von der Hardware und können somit in Kombination mit jedem Mikrocontroller verwendet werden. Core Die Kernkomponenten sind ein zentraler Bestandteil der Library und vieler semf- Komponenten selbst. Hierzu gehören zum Beispiel verkettete Listen und Queues, sowie Ringpuffer Strukturen als auch eine Signal-Slot Funktionalität. Mit Signals lassen sich in semf, ähnlich wie in Qt, Callbackfunktionen ermöglichen. Ein Signal kann von dem Programm, wenn es eine entsprechende Bedingung erfährt, aktiviert werden (zum Beispiel zum Ende eines Interrupts). Ein Slot ist eine normale Funkti- on oder Methode, welchem ein Signal zugewiesen werden kann. Der zugewiesene Slot wird aufgerufen, wenn nachfolgend das Signal aktiviert wird. Gegenüber den gewohnten Herangehensweisen, bietet dies eine intuitivere Art auf externe Ereig- nisse innerhalb des Programmcodes einzugehen. Bootloader Um die Durchführung von Firmwareupdates der eingebetteten Systemen zu ermög- lichen, stellt semf eine Firmwareupdater- und Bootloaderfunktionalität bereit. Da- mit lässt sich unkompliziert Konfigurieren, wohin die Firmwareupdates geschrieben werden sollen, was für ein Verschlüsselungsverfahren verwendet werden soll und wie groß die zu schreibende Firmware maximal sein darf. Der Übertragungsweg, sei es zum Beispiel Ethernet, USB oder Uart, kann dann selbst gewählt und konfi- 16
2. Stand der Technik guriert werden. Für die Verschlüsselung der aktuellen und zu übertragene Firmware gibt es desktopseitig eine entsprechende Anwendung. Kommunikation Semf verfügt über mehrere standardisierte Interfaces für Kommunikationsproto- kolle wie zum Beispiel Spi, I2c, Can, Uart, Usb und Ethernet. Das ermöglicht einen einfachen Austausch des Kommunikationsstacks. Abbildung 2.3 gibt einen schematischen Einblick in die Klassenhierarchie der Communication-Komponente von semf, am Beispiel von Uart. Das von dem Interface Communication erben- de Uart-Interface kann unabhängig der tatsächlichen Hardware-Peripherie verwen- det werden. Die tatsächlichen Hardwarezugriffe passieren erst in den sogenannten Hardware-Abstrahierungen. Diese unterscheiden sich natürlich je nach Peripherie und werden in dem Unterkapitel 2.2.4 näher beschrieben. Da alle Interfaces der verschiedenen Kommunikationsarten von Communication erben und über die glei- chen Methoden wie write() und read() verfügen, kann der zu verwendende Kom- munikationsstack einfach ausgetauscht werden. Dies setzt jedoch voraus, das eine entsprechende Hardware-Abstraktion von semf zur Verfügung gestellt wird oder alternativ diese selbst implementiert wird. Communication + write() + read() … Uart # onDataAvailable() # onDataWritten() Kommunikationsinterface Hardware-Abstrahierung Stm32Uart + setFormat() + setBaud() Stm32F7Uart Stm32F1Uart Abbildung 2.3: Klassenhierarchie Uart 17
2. Stand der Technik Speicherverwaltung Für verschiedene Speichertechnologien (z.B. Spi-NorFlash, I2c-Eeprom) gibt es entsprechende semf-Komponenten, sowie ein Logger-Mechanismus und eine EEPROM- Virtualisierung. Processing Zu dieser Kategorie an Modulen gehören verschiedene Algorithmen, die bei der Verarbeitung von Daten unterstützen. Vom DataStream, der Daten korrekt zwischen Little- und Big-Endian umrechnet, bis zum LinearInterpolator, der effizient in bis zu dreidimensionalen Datenquellen interpolieren kann. System Semf umfässt Module die sich mit der Systemzeit befassen wie zum Beispiel die Erstellung von Softwaretimern und Zeitstempeln. 2.2.4. Hardware-Abstraktion Auch wenn semf konzeptionell architektonisch unabhängig von der Hardware auf- gebaut ist, gibt es auch Komponenten die grundlegend von der Hardware, wie zum Beispiel der Peripherie, abhängig sind. In Abbildung 2.3 wurde schon angedeutet, wie dieser Aspekt in semf umgesetzt wird. Anstelle von Stm32Uart könnte dort auch genauso gut Esp32s2Uart stehen. Die weitere Unterteilung von Stm32Uart in Stm32F7Uart und Stm32F1Uart kommt daher zustande, dass sich die Hal und der Funktionsumfang je nach Mikrocontrollerfamilie teilweise unterscheiden. Da sich die Hal je nach Mikrocontroller in ihrem Verhalten unterscheiden kann, sollen genau diese Implementierungen für verschiedene Mikrocontroller im Rahmen die- ser Abschlussarbeit getestet werden. Aktuell existieren Plug & Play Implementie- rungen für die folgenden Mikrocontrollerfamilien: Stm32L0, Stm32F1, Stm32F3, Stm32F4, Stm32F7 und ESP32-S2. Weitere Implementierungen werden aber fol- gen. 18
2. Stand der Technik Das Softwarearchitekturdesign von semf mit den Hardware-Abstraktionen ermög- licht eine in weiten Teilen von der Hardware unabhängige Softwareentwicklung. Eine Portierung auf einen anderen Mikrocontroller ist mit minimalen Aufwänden verbunden, vorausgesetzt seitens semf existiert eine zu diesem anderen Mikrocon- troller passende Hardware-Abstraktion. 19
3. Hauptteil 3.1. Ausgangssituation Zu Beginn dieser Abschlussarbeit fand das Testen von den Hardware-Abstraktionen lediglich manuell statt, wohingegen die hardwareunabhängigen Komponenten mit- tels klassischer Unit-Tests auf ihre Korrektheit überprüft wurden. In der Vergan- genheit führte die zum Teil unvollständige Validierung der Hardware-Abstraktionen des Öfteren zu aufwändigen Suchen nach Fehlern. Zum Beispiel besteht häufig kei- ne Gewissheit, ob ein Bug denn nun in der Hardware-Abstraktion oder in der sie verwendenden Applikation zu suchen ist. Ein weiteres Problem besteht darin, dass die Hal-Bibliotheken verschiedener Mikrocontrollerfamilien in den meisten Fällen identisch zueinander sind, sich aber in wenigen Aspekten unterscheiden. Dies führt dazu, dass das manuelle Testen dieser semf-Module jeweils auf verschiedenen Mi- krocontrollern durchgeführt werden müssten, was sehr zeitaufwändig ist und zu einem Übersehen von Fehlern führt. Dass das manuelle Testen aufwändig ist, hängt auch damit zusammen, dass die Validierung dieser Softwaremodule häufig nicht tri- vial ist, da sie auf externe Ereignisse, wie zum Beispiel einkommende Nachrichten, angewiesen sind. Die Entwicklung und der Aufbau eines automatisierten Testsystems für Periphe- rie verschiedener Mikrocontroller wurde bis zu diesem Zeitpunkt noch nicht an- gegangen, da das Repertoire an verschiedener Hardware-Abstrahierungen gerade erst zu wachsen begann. Da nun immer mehr Mikrocontrollerfamilien unterstützt werden sollen, stieg die Notwendigkeit dieses automatisierten Testsystems. Zudem lässt sich die Umsetzung weiterer Module für verschiedene Mikrocontroller mithil- fe eines automatischen Testsystems beschleunigen. Dies hängt unter anderem damit zusammen, dass den Entwicklern direkt ersichtlich wird, wenn etwas nicht wie ge- wünscht funktioniert. 20
3. Hauptteil Für diese Abschlussarbeit bestanden keine weiteren schon bestehenden Grundlagen bezüglich des Testsystems. 3.2. Konzeption Bevor die Umsetzung im Detail erläutert wird, wird in diesem Kapitel auf die Kon- zeption des Gesamtsystems, die Softwarearchitektur und auf das Testkonzept ein- gegangen. 3.2.1. Design Gesamtsystem Für das Design des Gesamtsystems gibt es verschiedene mögliche Optionen, die es abzuwägen gilt, um die bestehenden Anforderungen an das Testsystem optimal zu erfüllen. So sollen die Tests zuverlässig, das Testen flexibel und die Testdurchfüh- rung unkompliziert sein und in die Continuous Integration integriert werden kön- nen. Aus diesen Anforderungen ergab sich das in Abbildung 3.1 aufgezeigte Design des Gesamtsystems, dessen Umsetzung möglichst im Rahmen dieser Abschlussar- beit durchgeführt werden soll. DUT Helper Testserver Mikrocontroller Mikrocontroller Stm32F7 CI-Server Test-Server Stm32F4 Esp32S2 Abbildung 3.1: Design Gesamtsystem Nachfolgend werden die verschiedenen Instanzen einzeln erläutert: 21
3. Hauptteil Tests starten Die Tests sollen flexibel durchgeführt werden können. Damit ist gemeint, dass die Tests zu den Hardware-Abstrahierungen von semf auch einzeln ge- startet werden können oder wahlweise auch nur einzelne Mikrocontroller getestet werden können. Das Unternehmen querdenker engineering verfügt über einen CI-Server, in welchen das automatische Durchführen der Tests in- tegriert werden soll. Hier könnte ein weiterer Computer (zum Beispiel ein Raspberry Pi 1 )als Bridge zu den zu testenden Mikrocontrollern dienen. An diesem Test-Server werden dann die zu testenden MCUs angeschlossen und die Tests gesteuert und ausgewertet. Der Kommunikationskanal zwischen dem Test-Server und den zu testenden Mikrocontroller sollte so gewählt werden, dass möglichst wenig Datenleitungen benötigt werden, um die Übersichtlich- keit des Aufbaus nicht zu gefährden. Zu testende Mikrocontroller Für jeden von semf unterstützten Mikrocontroller wird ein entsprechendes Entwicklungsboard, welches über möglichst viele Peripherien verfügt, ausge- wählt, und mit dem Test-Server verbunden. Auf die zu testenden Mikrocon- troller (Device Under Test bzw. DUT) wird eine Software geladen, welche die Tests durchführt. Helper Mikrocontroller Einige Tests benötigen für die Validierung eine Kommunikation mit einem Helper Mikrocontroller. Dieser ist dafür zuständig, die von dem DUT an den entsprechenden Peripherien geschriebenen Daten zu lesen und somit zu va- lidieren. Der Helper soll außerdem verschiedenste Datenströme an verschie- denen Peripherien generieren, die wiederum von dem DUT gelesen werden können. So können die Lesefunktionalitäten seitens des DUT validiert wer- den. Die möglichen Alternativen und Abwägungen zu diesem Design werden im Fol- genden genauer erörtert: Braucht es den Helper? Da die Entwicklungsboards über mehrere Schnittstellen für die jeweiligen Pe- ripherien verfügen, lassen sich diese Peripherien auch ohne Helper testen. So könnte sich wie in Abbildung 3.2 schematisch angedeutet, für jedes Kom- 1 Ein Raspberry Pi ist ein Einplatinencomputer mit verschiedenen Schnittstellen 22
3. Hauptteil munikationsprotokoll die lesende und schreibende Komponente auf dem sel- bigen Entwicklungsboard befinden. Letztendlich wurde sich jedoch für die Abbildung 3.2: Alternativer Ansatz ohne Helper Variante in Abbildung 3.3 entschieden. Diese resultiert in einem Design mit Abbildung 3.3: Tatsächlicher Ansatz mit Helper einer deutlich höheren Komplexität, bei der die Kommunikation und Syn- chronisation mit dem Helper Device, sowie die allgemeine Logik des Helpers zusätzlich zu den Tests seitens des DUT umgesetzt werden müssen. Auch er- schwert dies das Debuggen, da potenzielle Bugs nun sowohl auf der DUT Instanz, als auch auf der Helper Instanz sein können. Die Entscheidung hin zum dem Ansatz mit einer Helper Gegenstelle bringt einen großen Vorteil mit sich: Man läuft nicht mehr in die Gefahr, dass man durch doppeltes Fehlverhalten seitens des DUT fälschlicher Weise auf ein po- sitives Verhalten rückschließen könnte. Diese potenzielle Fehlerquelle wird zumindest dadurch minimiert, dass von einer wahrscheinlichen Fehlerfreiheit des Helpers ausgegangen werden kann. Diese Annahme kann getroffen wer- den, da jeder auf dem DUT durchgeführte Test auch gleichzeitig als Neben- produkt den Helper validiert. Außerdem erleichtert der Helper die Entwick- 23
3. Hauptteil lung von neuen Treibern, da damit von Anfang an gegen eine funktionierende Gegenstelle getestet werden kann. Braucht es mehrere Helper? Abbildung 3.1 zeigt das Design des Gesamtsystems, wobei mit jedem DUT ein zugehöriges Helper Device verbunden wird. Denkbar wäre auch gewe- sen, alle DUTs mit jeweils der selben Helper Instanz zu verbinden. Durch die Designentscheidung hin zu einem jeweiligen Helper pro DUT ist man deutlich flexibler. Einzelne DUTs mit ihren jeweiligen Helpern können von dem Test-Server abgekoppelt werden und an einem Desktop-PC gedebuggt werden, ohne den Aufbau des Gesamtsystems zu stören. 3.2.2. Design Softwarearchitektur Das Design der Softwarearchitektur des Testsystems lässt sich wie in Abbildung 3.4 in drei Pakete, beziehungsweise Projekte unterteilen. Diese sind DUT, Helper- Interface und Helper. Abbildung 3.4: Paketdiagramm der Softwarearchitektur Helper-Interface Das Projekt Helper-Interface wird sowohl von den DUT Projekten, als auch von dem Helper Projekt importiert. Die Steuerung des Helpers durch das 24
3. Hauptteil DUT wird mittels dieses von beiden Instanzen benutzten Interfaces umge- setzt. Das Helper-Interface umfasst die beiden Pakete Protocol und Configu- rations. Protocol definiert, wie die Nachrichten aufgebaut sind und Configu- rations bildet die Einstellungen ab, mit denen die verschiedenen Peripherien konfiguriert werden können. Eine mögliche Configuration für GPIO könnte zum Beispiel sein: 1. INPUT 2. PULLUP 3. SET_GPIO. Dem Helper kann somit mitgeteilt werden, wie er das entsprechende GPIO zu konfigurieren hat. DUT Die DUT Pakete stehen für die zu testenden Mikrocontroller. Da jeder Mikro- controller eine andere Hal-Bibliothek hat und sich die Peripherien unterschei- den können, wird für jeden Mikrocontroller ein eigenes Projekt angelegt. Die- se unterscheiden sich aber lediglich in der Konfiguration der Hardware und der Hal. Das Abarbeiten der Tests wird mittels Objekten vom Typ Testcon- troller geregelt. Helper Der Helper importiert wie die DUTs das Helper-Interface und führt die An- weisung der DUT-Seite entsprechend durch. 3.2.3. Testkonzept Sollen Tests für Software-Module auf einem eingebetteten System umgesetzt wer- den, so stehen den Entwicklern verschiedene Frameworks zur Verfügung. Mögliche Frameworks sind zum Beispiel GoogleTest oder uCUnit. Diese wurden schon vor dieser Abschlussarbeit von den Entwicklern von semf für ähnliche Anwendungs- fälle ausprobiert und validiert. Als größte Schwäche stellte sich dabei heraus, dass Asynchronität von den Testframeworks nicht abgebildet werden konnte. Semf ba- siert jedoch mittels des Signal-Slot Moduls stark auf Asynchronität. Dies gilt insbe- sondere auch für die Kommunikation zwischen dem Helper und dem DUT. Daher wurde sich für den Anwendungsfall des Hardwaretesters gegen eine Verwendung von einem Testframework entschieden. Jene Aufgaben, die ein Testframework ver- einfachen würden, sind demnach selbst umzusetzen. Hierzu einige Beispiele: 1. Es gibt keine Makros wie TEST_ASSERT oder TEST_ASSERT_EQUAL. 2. Die Funktionalität von setUp und tearDown ist selbst zu implementieren. 25
3. Hauptteil 3. Das Auswerten der Testergebnisse muss selbst übernommen werden. Die Testdurchführung orientiert sich dabei an XUnit Testpattern [Me07]. Jeder Test durchläuft somit die in Abbildung 3.5 dargestellten Testphasen. Abbildung 3.5: XUnit Testphasen Die Peripherien unterschiedlicher Mikrocontroller unterscheiden sich teilweise in ihrem Funktionsumfang, wodurch nicht jeder Tests auf allen Mikrocontrollern lauf- fähig sein wird. Falls möglich, sollen die zu schreibenden Testroutinen jedoch mit verschiedensten Mikrocontrollern kompatibel sein. Die XUnit Testpatterns für automatisierte Tests raten an, verschiedene Testprinzipi- en einzuhalten. Teil des Testkonzepts dieser Abschlussarbeit ist es, diese Testprin- zipien möglichst umzusetzen. Dazu gehört zum Beispiel die Folgenden: Minimize Test Overlap Die Tests sollten so strukturiert sein, dass möglichst wenige Tests von einer bestimmten Funktionalität abhängig sind, da es wenig hilft, wenn diese Funk- tionalitäten mehrfach validiert werden. Verify One Condition per Test Häufig erscheint es effizient und verführerisch, einen einzelnen Test zum Tes- ten verschiedener Konditionen mehrfach zu verwenden. Somit könnte man sich das mehrfache Erzeugen eines Startzustandes ersparen. Diese Herange- hensweise sollte vermieden werden, da sie zum Beispiel das Lokalisieren von Fehlern erschwert. Communicate Intent Damit sich diese Tests auch in Zukunft einfach pflegen lassen, sollten diese klar und verständlich sein. Dies wird mit doxygen-Kommentaren 2 und ei- 2 Doxygen ist ein Programm, welches Kommentare aus dem Programmcode extrahiert und daraus eine Doku- mentation generiert 26
3. Hauptteil ner einheitlichen Namenskonvention für Testroutinen umgesetzt. Für die Na- menskonvention der Tests wurde sich auf die Folgene Darstellung geeinigt: module_functionlity_condition_expectedBehavior. Write the Tests First Test-Driven-Development bedeutet, dass zuerst die Tests, und anschließend die Implementierungen entwickelt werden [Be02]. Da die Tests für alle Hardware- Abstrahierungen wiederverwendet werden können, ist dies in unserem Fall automatisch geben. Der Ablauf der Entwicklung einer neuen Hardware-Abstraktion soll den in Abbildung 3.6 gezeigten Prozess durchlaufen. Das Pushen eines überarbei- Abbildung 3.6: Flussdiagramm: Entwicklung einer Hardware-Abstraktion teten Softwarestands von Hardware-Abstraktionen aktiviert serverseitig die Durchführung der Tests. Falls dabei Tests fehlschlagen, wird dies dem Ent- wickler mitgeteilt, so dass dies korrigiert werden kann. Die Tests können aber auch lokal an dem Arbeitsplatz durchgeführt werden. 27
3. Hauptteil 3.3. Umsetzung Wie die in dem vorherigen Kapitel beschriebene Konzeption in die Tat umgesetzt wurde, wird in diesem Kapitel behandelt. Aktuell werden sowohl der Mikrocontroller Stm32F3, als auch der Mikrocontrol- ler Stm32F7 mittels des Testsystems getestet. In Abbildung 3.7 ist zu sehen wie der Testaufbau des Stm32F3 Mikrocontrollers aufgebaut ist. Die Peripherien der Instan- Abbildung 3.7: Aktueller Testaufbau des Stm32F3-Mikrocontrollers zen Helper und DUT wurden dabei mit zurechtgeschnittenen Überbrückungskabeln verbunden. Auf die Software der abgebildeten Geräten wird nachfolgend eingegan- gen. Das Kapitel Umsetzung ist somit in die Beschreibungen der verschiedenen Softwareprojekte unterteilt, aus welchen sich die Programme des Helpers und der DUTs zusammensetzen. Die sich daraus ergebenden Unterkapitel lauten: 1. Software Device Under Test 2. Interface Helper - Device Under Test 3. Software Helper 4. Testfälle Als visuelle Unterstützung für die Erklärungen der Softwarearchitektur werden des Öfteren UML-Diagramme verwendet. Diese sind als Modell zu verstehen und stel- len verschiedene Details zum Teil nur verkürzt dar. Ziel der UML-Diagramme soll sein, die Gesamtheit der Systeme möglichst verständlich wiederzugeben. 28
Sie können auch lesen