Suchen

PIL: Testgetriebene Entwicklungsmethode für komplexe Algorithmen

| Autor / Redakteur: Anto Michael, Llarina Lobo Palacios und Sebastian Zuther* / Sebastian Gerstl

Um zu überprüfen, ob die Implementierung von Algorithmen in Mikrocontroller auch geklappt hat, werden Tests nach Hardware-in-the-Loop (HIL) und Software in the Loop (SIL) immer populärer. Beide Ansätze haben jedoch Schwachstellen. Eine „Processor in the Loop“ (PIL) Umgebung könnte sich besser eignen.

Firma zum Thema

Aufbau einer typischen Hardware-in-the-Loop Simulation. Eine HIL-Umgebung dient oft zur finalen Überprüfung von Algorithmen in einer virtuellen Umgebung, ehe sie in einem Mikrocontroller implementiert werden. Doch lässt sich hier nur schwierig herausfinden, wie sich Optimierungen etwa auf die tasächliche Laufzeit auswirken.
Aufbau einer typischen Hardware-in-the-Loop Simulation. Eine HIL-Umgebung dient oft zur finalen Überprüfung von Algorithmen in einer virtuellen Umgebung, ehe sie in einem Mikrocontroller implementiert werden. Doch lässt sich hier nur schwierig herausfinden, wie sich Optimierungen etwa auf die tasächliche Laufzeit auswirken.
(Bild: Embedded.com)

Eine der typischen Aufgaben von Embedded-Software in Automotive-Anwendungen war es, Daten aus Sensoren zu erfassen und die Aktuatoren im Fahrzeug zu steuern. Seit Beginn dieses Jahrhunderts liegt der Schwerpunkt jedoch mehr darauf, den Fahrer auf verschiedenste Weise zu unterstützen. Zunächst beschränkte sich dies auf die Hinderniswarnung bei niedrigen Geschwindigkeiten. Später kamen Fahrzeuge auf den Markt, die eigenständig einen freien Parkplatz erkennen und unter Aufsicht des Fahrers automatisch einparken konnten. Immer mehr Fahrzeuge werden inzwischen mit Spurwechselassistenten und automatischer Geschwindigkeitsreglung ausgestattet. Heute geht der Trend hin zu automatisierten Fahrplattformen. Ausschlaggebend beim autonomen Fahren ist das Erfassen der Fahrzeugumgebung. Das Erfassungssystem erhält Daten aus Sensoren, z.B. in Ultraschall-, Kamera-, Laser oder Radarsystemen. Diese Daten müssen verarbeitet werden, damit das Fahrzeug über seine Umgebung Bescheid weiß. Die Verarbeitung erfolgt mittels komplexer mathematischer Algorithmen, die in der Embedded-Software implementiert sind. Die Embedded-Software läuft auf kleinen Mikrocontrollern, deren Ressourcen – Speicher und Laufzeit – begrenzt sind.

Die Entwicklung des Algorithmus beginnt typischerweise mit der Konzeptvalidierung, die mithilfe von leistungsstarken Tools mit umfassenden, direkt einsatzbereiten Bibliotheken durchgeführt wird. Gute Beispiele hierfür sind MATLAB und Python. Das Konzept lässt sich einfach in diese Tools implementieren. Die Daten aus den Sensoren werden dem Algorithmus strukturiert zugeführt und die Leistung des Algorithmus wird evaluiert. Wenn das Konzept funktioniert, geht es in die nächste Phase über – die Embedded-Entwicklung.

In der Embedded-Entwicklung wird das in C oder C++ entwickelte Kernkonzept implementiert. Den Vorteil, auf einsatzbereite Bibliotheken zurückgreifen zu können, bieten die meisten Mikrocontroller nicht. Die ersten Entwicklungsschritte erfolgen auf dem Software-in-the-Loop (SIL) Setup, einer PC-basierten Umgebung, welche die auf dem PC gespeicherten Logfiles der Sensormessungen ausliest und dem Algorithmus zuführt. Der Algorithmus wird weiterentwickelt, bis das gewünschte Maß an Usability und Akzeptanz erreicht wird; danach geht er in die Systemintegration über.

Bei der Systemintegration des Algorithmus wird dieser direkt mit den Daten aus den Sensoren in Verbindung gebracht. Der Algorithmus wird mit den Daten getestet, die an der Systemschnittstelle – meist ein Bus vom Sensor zum Mikrocontroller, auf welchem der Code ausgeführt wird – bereitgestellt werden. Die Datenbereitstellung erfolgt nicht synchron, und bei der Algorithmusverarbeitung können Interrupts auftreten. Zunächst wird das System auf einem Hardware-in-the-Loop-Setup getestet. Hier simuliert ein Simulationstool das Verhalten des Sensors und überträgt die Daten an das System. Wenn das System ein bestimmtes Maß an Akzeptanz erzielt, folgt der Fahrzeugtest.

SIL-Umgebungen: Überblick

Das auf dem PC ausgeführte SIL ist ein leistungsstarker Mechanismus zur Entwicklung komplexer Algorithmen. Mithilfe von Debug-Tools untersucht der Entwickler den Code und analysiert dabei umfangreiche Daten, mit denen sich Probleme beheben oder neue Ansätze zur Problemlösung finden lassen. Für Algorithmen wie Tracking oder Umgebungserkennung visualisiert das SIL die Umgebung und die darin befindlichen Objekte. Für Entwickler ist eine solche Umgebung äußerst hilfreich. Die Fehleridentifizierung und -behebung beanspruchen viel weniger Zeit, und Ideen lassen sich zügig entwickeln und auf ihre Wirksamkeit hin prüfen. Doch auch ein SIL-Setup hat Schwachstellen:

Das SIL-Setup läuft auf dem PC; daher lässt sich nicht abschätzen, welcher Umfang an Ressourcen auf dem Target-Mikrocontroller erforderlich ist, auf dem der Code ausgeführt werden soll. Der Speicherbedarf lässt sich in der SIL-Umgebung bis zu einem gewissen Grad abschätzen, die erforderliche Laufzeit jedoch nicht.

In manchen Mikrocontrollern steigern Hardwarebeschleuniger die Rechengeschwindigkeit; ein gutes Beispiel ist SIMD (Single Instruction Multiple Data). Diese Hardwarebeschleuniger liefern nicht immer die gleichen Ergebnisse wie der PC; dies liegt an minimalen Differenzen beim Abschneiden oder Runden von Gleitkommawerten. Teile des Codes können an Coprozessoren weitergeleitet werden, die bestimmte Operationen schneller ausführen. Das Programm, das die Daten zwischen diesen Einheiten überträgt, könnte Fehler enthalten, die im SIL-Setup nicht erkennbar sind.

HIL-Umgebungen: Überblick

Das HIL-Setup [2][4] ist der letzte Schritt vor dem Übergang des Mikrocontrollers in den Fahrzeugtest. Die Software- oder Hardwareverbindungen zwischen dem HIL und dem Fahrzeug-Setup ändern sich typischerweise nicht; daher ermöglicht das HIL-Setup effiziente Tests – doch in den frühen Phasen des Entwicklungsprozesses treten auch Schwachstellen zutage.

Der Algorithmus ist während der Entwicklungsphase manchmal noch nicht auf Code- und Stackgröße optimiert. Er kann für den Mikrocontroller geeignet sein, wenn dieser als Standalone-Komponente in ein Betriebssystem integriert wird, das nur die grundlegenden Tasks ausführt. Das HIL-Setup erfordert jedoch eine volle Systemintegration mit allen anderen Komponenten, und das Betriebssystem muss alle erforderlichen Operationen unterstützen. Es kann also sein, dass sich mit dem HIL-Setup die Leistungsfähigkeit des Algorithmus in dieser Phase des Entwicklungsprozesses nicht testen lässt.

Wenn die Codegröße auf dem Target unter Kontrolle ist und der HIL-Setup passt, tritt häufig ein weiterer KO-Faktor zutage – die Laufzeit. Der Code ist womöglich nicht so umfassend optimiert, dass er unter Berücksichtigung der Echtzeit-Laufzeitanforderungen ausgeführt werden kann. Oder er ist noch in der Evaluierungsphase, und es muss zunächst untersucht werden, ob sich der aktuelle Algorithmus für dieses Target eignet. Ist dies nicht der Fall, muss auf einen anderen Algorithmus zurückgegriffen werden.

Die HIL-Simulationsumgebung läuft meist auf dem PC oder einem Echtzeit-Simulator. Dabei erhält das System nicht zwingend bei jeder Iteration auf dem HIL [7] im gleichen Zeitschritt die gleichen Eingangswerte aus der Simulation. Es kann auch vorkommen, dass das System Interrupts aus anderen Events erhält, die nicht unbedingt bei jedem Durchlauf gleichzeitig stattfinden. Ein Beispiel hierfür ist ein Benutzereingriff. Aufgrund dieser minimalen Abweichungen im HIL-Setup lässt sich nicht sicherstellen, dass das System bei jedem Durchlauf identische Ergebnisse generiert.

Der wichtigste Aspekt beim Debuggen und Beheben eines Fehlers ist die zuverlässige Reproduzierbarkeit. Da das Systemverhalten im HIL nicht wirklich reproduzierbar ist, debuggen die meisten Algorithmus-Entwickler die entsprechenden Fehler dort ungern. Manchmal ist HIL-Debugging jedoch erforderlich, besonders dann, wenn für bestimmte Codeabschnitte Hardwarebeschleuniger für eine höhere Laufzeit zum Einsatz kommen.

Da die Laufzeiten der einzelnen Durchläufe auf dem HIL-Setup variieren, eignet es sich nicht für ein zuverlässiges Tracking der Laufzeitänderungen in den Software-Releases. Wenn neue Features implementiert wurden, hat dies oft erhebliche Auswirkungen auf die Laufzeit. Wurden jedoch keine größeren Features implementiert, sind Schwankungen in der Laufzeit für Entwickler und Integratoren schwer nachzuvollziehen.

Bild 1: HIL-Laufzeitschwankungen über die Durchläufe
Bild 1: HIL-Laufzeitschwankungen über die Durchläufe
(Bild: Valeo)

Bild 1 zeigt abweichende Laufzeiten der gleichen Software mit den gleichen Eingangswerten, die im gleichen Szenario nacheinander in drei Durchläufen auf einem HIL-Setup getestet wurden. In einem HIL-Setup kann ein Debugger mit Profiling-Funktionalität eingesetzt werden, um den Codepfad und die für jede der Funktionen aufgewendete Zeit zu prüfen.

Wenn diese Funktionen optimiert werden, würde bei der Ausführung des Codes nicht zwingend wieder die höchste Laufzeit an gleicher Stelle erzielt. Es lässt sich also nicht einfach testen, inwieweit sich Optimierungen auf die Laufzeit auswirken. Oft sollen Optimierungen auch keine Änderungen an den Ausgangswerten eines Systems verursachen – mit Ausnahme einer geringeren Laufzeit. Mit dem HIL-Setup lässt sich jedoch nicht ohne weiteres sicherstellen, dass sich der System-Ausgangswert nach der Optimierung nicht ändert.

Die Aufzeichnung von Debugdaten aus dem HIL-Setup ist eingeschränkt, da die an der Debugschnittstelle verfügbare Bandbreite bei den meisten Echtzeitsystemen begrenzt ist. Angeblich gleicht das HIL-Setup alle Schwachstellen des SIL aus. Auf dem HIL-Setup lassen sich zwar tatsächlich Fehler reproduzieren, dennoch bietet es keine debugfreundliche Umgebung.

Der PIL-basierte Ansatz

Um den oben beschriebenen Nachteilen der HIL- und SIL-Umgebungen zu begegnen, bedarf es einer anderen Testmethode. Dabei soll der Einsatz von HIL und SIL nicht in Frage gestellt werden. Es werden zusätzliche Setups vorgestellt, die deren Schwachstellen adressieren. Werden Probleme am richtigen Ort zur rechten Zeit erkannt und behoben, so lassen sich die Kosten für Entwicklung und Test enorm senken.

Der hier vorgeschlagene Ansatz verwendet das Processor-in-the-Loop (PIL) Setup, das die Nachteile des HIL adressieren und die Funktionalität des SIL erweitern soll. Neue Herangehensweisen gelten für alle Stakeholder – Entwickler, Integratoren und Führungskräfte – zunächst als zusätzliche Belastung. Um dieser Problematik zu begegnen, ist die PIL-Umgebung so ausgelegt und implementiert, dass sie sich mit geringem Aufwand einrichten lässt. Die PIL-Umgebung ist entweder direkt oder indirekt mit dem SIL verbunden, und alle im SIL verfügbaren Visualisierungs- und Debugmechanismen werden genutzt. Die PIL-Umgebung ermöglicht den Test der Software auf ihrer Zielhardware, wenn auch in einer Nicht-Echtzeit-Umgebung. Daraus ergeben sich folgende Vorteile:

  • Probleme im Entwicklungsprozess werden früher erkannt:
  • -> Numerische Instabilitäten (z.B. durch Gleitkommazahlen mit einfacher Genauigkeit)
  • -> Compiler-Probleme
  • Die Wiederholbarkeit der Tests ermöglicht Laufzeitoptimierungen und das zuverlässige Testen identifizierter und behobener Bugs.
  • Isoliertes Profiling und tiefgehende Analyse des Schrittes mit der höchsten Laufzeit
  • Kostengünstiger als andere Validierungsmethoden (z.B. HIL)
  • -> Die Test Bench ist kostengünstig einzurichten und einfacher zu debuggen als der HIL.

Voraussetzungen für den PIL-Ansatz

Das PIL-Setup ist im Wesentlichen eine datengetriebene Umgebung; das HIL dagegen ist zeitgetriggert. Die PIL-Umgebung „schläft“, bis alle relevanten Daten für einen Zeitschritt eingegangen sind. Sobald alle Daten für einen Schritt verfügbar sind, wird der Schritt ausgeführt. Die Ergebnisse werden gesammelt, und das System wartet, bis die Eingangswerte für den nächsten Schritt eintreffen. Die Übertragung der Eingangswerte und der Empfang der Ausgangswerte werden über die SIL-Umgebung gesteuert. Daher ist ein funktionierendes SIL-Setup erforderlich. Die in den Abschnitten zum SIL erläuterten Vorteile des SIL-Setups kommen in der PIL-Umgebung zum Tragen.

Das PIL-Setup muss ermöglichen, dass der zu testende Code auf der Zielhardware kompiliert werden kann. Anders als beim HIL ist hierfür keine vollständige Systemintegration vonnöten. Er reicht, wenn der zu testende Code kompiliert und zugänglich ist.

Der zu testende Code sollte klar definierte Eingangsschnittstellen haben, eine Konfigurationsmöglichkeit zum Initialisieren der Komponente sowie eine Updatefunktion, die die Eingangswerte jedes Schrittes verarbeitet und die Ausgangswerte der Komponente mit weiteren Debugdaten sammelt, die an verschiedenen Stellen im Algorithmus erfasst werden.

Weiterhin ist ein Kommunikationslink zwischen dem PC, auf dem das SIL läuft, und der Zielhardware erforderlich. Die Konfigurationsdaten für die Initialisierung und die Eingangswerte für jeden Verarbeitungsschritt müssen vom PC an die Zielhardware übertragen werden und die Ausgangswerte des Algorithmus sowie die Debugdaten von der Zielhardware an den PC. Der Kommunikationslink definiert also die Geschwindigkeit, mit der das PIL laufen kann. Die meisten kleinen Mikrocontroller haben keine High-Speed-Kommunikationsschnittstellen. In diesem Fall stellen Hardware-Debugger Schnittstellen bereit, über die sich die Daten direkt in bestimmte Speicherabschnitte schreiben lassen.

Zuletzt bedarf es noch eines Protokolls, um die erforderlichen Daten zwischen dem PC und dem Target zu übertragen. PC und Mikrocontroller haben unter Umständen nicht die gleiche Hardwarearchitektur; dies kann zu Problemen mit der Byte-Reihenfolge führen. Auch das Padding und Alignment komplexer Datenstrukturen können sich unterscheiden. Dann ist es nicht ohne Weiteres möglich, die Daten mittels Memory Dump zwischen den beiden Blöcken zu übertragen. Ein Serialisierungsprotokoll für den Datentransfer ist erforderlich.

Folgendes ist für die PIL-Umgebung vonnöten:

1. Funktionierende SIL-Umgebung, die Eingangswerte bereitstellt und Ausgangswerte erfasst

2. Zu testender Code kompiliert und geflasht auf der Zielhardware mit einem einfachen Betriebssystem, das mindestens die die für das PIL-Setup erforderlichen Schnittstellen unterstützt

3. Eingangswerte, Ausgangswerte und Debugdaten des zu testenden Codes

4. Aktiver Kommunikationslink zwischen PC (SIL) und Zielhardware

5. Kommunikationsprotokoll für den Datentransfer – Serialisierung und Deserialisierung

PIL-Setup

Bild 2: PIL-Setup
Bild 2: PIL-Setup
(Bild: Valeo)

Ein Standard-PIL-Setup beinhaltet einen PC, der Zugriff auf die Logfiles der Eingangswerte hat und das SIL ausführen kann, ein Evaluierungsboard mit dem Target-Mikrocontroller und einen Hardware-Debugger als Schnittstelle. Das Evaluierungsboard, das mit dem Mikrocontroller mit der Zielhardware verbunden ist, wird an die Stromversorgung angeschlossen. Ein Hardware-Debugger, der den Mikrocontroller flashen und mit diesem kommunizieren kann, wird an den PC angeschlossen. Die Kommunikation zwischen PC und Zielhardware erfolgt über den Debugger (siehe Bild 2).

Auch andere Kommunikationsmechanismen, wie CAN oder Ethernet, sind denkbar. Der Objektcode wird über den Debugger auf den Mikrocontroller geladen. Das SIL wird kompiliert und liefert so die Eingangswerte, die für den auf dem Target zu testenden Code benötigt werden. Das SIL serialisiert die Eingangswerte für den aktuellen Zeitschritt und überträgt sie an das Target. Wenn das Target die Daten empfangen hat, deserialisiert es die Eingangswerte und führt den zu testenden Code aus. Ausgangswerte und Laufzeit werden erfasst, serialisiert und zurück an das SIL übertragen.

Bild 3: Datenfluss in einem PIL-Setup
Bild 3: Datenfluss in einem PIL-Setup
(Bild: Valeo)

Das SIL wird direkt vom Anwender gesteuert. Es kann durchgehend laufen, dabei erfolgt eine Aktualisierung in jedem Schritt, oder für eine detaillierte Analyse angehalten werden, z.B. um die Ergebnisse des aktuellen Zeitschritts auf dem SIL und dem PIL abzugleichen. Bild 3 zeigt den Datenfluss der vorgeschlagenen PIL-Lösung. Tools wie MATLAB unterstützen zudem die Codeverifikation und -validierung mit einer PIL-Simulation von modellbasierten, in Simulink implementierten Algorithmen, wie auf der entsprechenden Webseite beschrieben [6].

PIL – Protokoll für den Datentransfer

Die größte Herausforderung im PIL-Setup besteht darin, den Vorgang der Datenserialisierung und -deserialisierung zu vereinfachen. Das Serialisieren und Deserialisieren von Daten mit manuell erzeugtem Code ist umständlich und fehleranfällig und bedeutet zusätzlichen Aufwand beim Einrichten der Umgebung. Fehler im Serialisierungsprozess können dazu führen, dass viel Zeit für das Debuggen erforderlich ist, um die Fehlerquelle aufzuspüren und zu beheben. Um diesen Problemen zu begegnen, ist es nötig, den Prozess der Codegenerierung für die Serialisierung zu automatisieren.

Ein effizienter Ansatz für die Datenserialisierung sind Google Protocol Buffer [3]. Jedoch ist der für die Protocol Buffer erzeugte Code voluminös und auf den Mikrocontrollern schwierig auszuführen. NanoPb [1], eine schlankere Implementierung der Google Protocol Buffer, kann hier eingesetzt werden. Dennoch erfordern die Google Protocol Buffer die Generierung von Protokolldateien, die das Nachrichtenformat definieren. Diese Dateien könnten entsprechend der erforderlichen Datenstrukturen auch automatisch erzeugt werden.

Dieser Ansatz wurde jedoch vermieden, damit die Codegröße auf dem Embedded-Target reduziert und ein zweistufiger Konvertierungsprozess – in Protokolldateien und dann die Serialisierung mithilfe von NanoPb - umgangen wird. Zudem sehen die Protocol Buffer bei der Speicherung und Wiederverwendung von Daten eine Rückwärtskompatibilität vor. Da die PIL-Umgebung dies nicht leistet, wurde dieser Ansatz verworfen und der nachfolgend beschriebene einfachere Ansatz gewählt.

Für das Erzeugen des Serialisierungscodes sind die Datenstrukturen zu erfassen, die Bestandteil der Eingangs- und Ausgangswerte des zu testenden Codes sind. Alle erfassten Datenstrukturen erhalten eine eigene Identität. Ein Präprozessor identifiziert die Abhängigkeiten der erfassten Datenstrukturen. Der Serialisierungscode wird nicht nur für diese Datenstrukturen generiert, sondern auch für deren Abhängigkeiten.

Darüber hinaus liefert der Präprozessor eine Abbildung der Datenstruktur in einem Abstract Syntax Tree (AST). Die AST-Abbildung wird auf die Blätter des Baumes geparst, bis ein primitiver Datentyp zur Verfügung steht. Primitive Datentypen sind z.B. signed/unsigned Ganzzahlen von 8, 16, 32 und 64 Bit, Single und Double Precision Floats. Jeder primitive Datentyp wird je nach Bytebreite in ein serialisiertes Byte-Array codiert. Wenn an einem Blatt eine nicht-primitive Datenstruktur erkannt wird, wird die entsprechende Serialisierungsfunktion aufgerufen. Die Abhängigkeitsauflösung stellt sicher, dass die Serialisierungsfunktion für diese Datenstrukturen verfügbar ist.

In den meisten Fällen müssen die Eingangs- und Ausgangswerte aus unterschiedlichen Quellen akkumuliert werden. Auch stehen eventuell nicht alle Eingangs- und Ausgangswerte in allen Zeitschritten zur Verfügung. Daher muss es möglich sein, unterschiedliche Serialisierungsstreams für die Übertragung zusammenzuführen. Dazu wird für alle erfassten Datenstrukturen ein kleiner Header am Beginn des serialisierten Streams angefügt. Er enthält die ID und Länge der Datenstruktur.

Der Prozess der Codeerzeugung ist in der Buildumgebung eingebettet. Wenn eine neue Datenstruktur auf der Serialisierungsliste hinzukommt oder eine bestehende Datenstruktur verändert wurde, wird der Serialisierungscode aktualisiert. Für die Änderungen der Datenstrukturen ist keine Rückwärtskompatibilität erforderlich, da in diesem Prozess keine Speicherung der Eingangs- und Ausgangswerte erfolgt. Bei jeder Änderung der Eingangswerte wird die SIL-Umgebung aktualisiert. Dabei werden die Eingangswerte aufbereitet, und der zu testende Code verwendet diese aktualisierten Eingangswerte. Der Serialisierungsprozess wird also von Änderungen nicht beeinträchtigt.

Anwendung und Ergebnisse

Wenn die PIL-Umgebung eingerichtet ist, ist nur sehr wenig oder gar keine Wartung vonnöten. Sie hat verschiedene Use-Cases zur Erweiterung der SIL-Umgebung. Dieser Abschnitt beleuchtet einige PIL-Anwendungen.

Test und Validierung

Auf dem PIL-Setup lässt sich durch Plausibilitätsprüfungen feststellen, ob der Code ähnliche Ergebnisse liefern würde, wie sie vom SIL erwartet werden. Sowohl die SIL- als auch die PIL-Umgebung sind datengetrieben, und die Eingangswerte weichen nicht voneinander ab. Demnach sind bei Ganzzahlberechnungen die Ergebnisse identisch. Bei Gleitkommaberechnungen können die Ergebnisse aufgrund von minimalen Rundungsdifferenzen sowie der Optimierung von Zwischenoperationen auf dem PC und Target geringfügig abweichen.

Das PIL-Setup liefert bei jedem Durchlauf zuverlässig identische Ergebnisse. Abweichende Ergebnisse können nur aufgrund von Codeänderungen zustande kommen. Wurde der Code nicht geändert und die Ergebnisse weichen bei den einzelnen Durchläufen ab, ist dies ein klarer Hinweis darauf, dass der Code fehlerhaft ist. Oft liegt ein nicht initialisierter Speicherzugriff vor; in seltenen Fällen auch ein Compilerfehler. Wenn die Ergebnisse der Durchläufe abweichen, werden die Debug-Ausgangswerte an verschiedenen Prüfstellen im zu testenden Code erfasst, um die Anomalie besser einzugrenzen.

Laufzeitoptimierung

Da auf dem PIL-Setup bei Ausführung desselben Codes mit denselben Eingangswerten die gleichen Laufzeitergebnisse erzielt werden – anders als bei Ausführung der Software auf dem HIL – kann es auch für Optimierungen verwendet werden. Die meisten Embedded-Systeme haben eine zyklische Updaterate. Da das PIL schrittweise abläuft, lässt sich der Zeitschritt mit der höchsten Laufzeit identifizieren, indem man die Laufzeit jedes einzelnen Schrittes betrachtet. Wird ein bestimmter Schritt identifiziert, ist noch zu ermitteln, welcher Codepfad in diesem Schritt durchlaufen wurde. Dazu können die in diesem Zeitschritt erfassten Debugdaten analysiert werden.

Oder man verwendet den Profiler, der auf manchen Debuggern für das Target zur Verfügung steht. Der Profiler liefert Informationen darüber, welcher Codepfad durchlaufen wurde, wie oft eine bestimmte Funktion ausgeführt wurde und wie die Laufzeit jedes Durchlaufs war. Auch auf HIL-Setups lässt sich ein Profiling durchführen. Da sich die HIL-Laufzeit jedoch nicht zuverlässig reproduzieren lässt, eignet sich das PIL-Setup besser für die Laufzeitoptimierung. Der in einem bestimmten Schritt durchlaufene Codepfad wird analysiert, und mögliche Optimierungen werden vorgenommen. Dann wird das PIL erneut ausgeführt.

Wenn sich die Optimierung nicht auf die Ergebnisse auswirken soll, stellt man bei einer Prüfung die Ergebnisse der Durchläufe vor und nach der Optimierung gegenüber. Sind alle Ergebnisse wie vorgesehen, geht man zum nächsten Schritt mit der höchsten Laufzeit über, bis der gewünschte Leistungsgrad erreicht wird.

Regressionstest

In den meisten Automotive-Anwendungen kommen beim Software Engineering agile Entwicklungsmethoden zum Einsatz, mit häufigeren Releases als bei der herkömmlichen Softwareentwicklung. Die Laufzeit der verschiedenen Systeme muss innerhalb der geforderten Grenzwerte liegen, um die Performance des Systems sicherzustellen. Mit dem PIL lässt sich dies hervorragend überprüfen.

Für jeden Zeitschritt werden die Eingangswerte aus dem SIL in eine Datei geschrieben. Vor jedem Release wird das PIL automatisch mit dem vordefinierten Input-File ausgeführt, und die Laufzeit wird mit der Laufzeit aus dem vorherigen Release verglichen. Für einen Laufzeitanstieg wird ein Abnahmekriterium festgelegt. Wenn die Laufzeit den festgelegten Grenzwert überschreitet, wird dies sofort dem Entwickler gemeldet, so dass er die Ursache für den Laufzeitanstieg direkt ermitteln kann. In manchen Fällen ist ein Anstieg begründet, z.B. wenn neue Features implementiert wurden. Dennoch muss die Laufzeit innerhalb der Grenzwerte des Systems liegen, und das PIL trägt erheblich dazu bei, dass dies der Fall ist.

Vergleich und Evaluierung verschiedener Targets

Die PIL-Umgebung lässt sich auch für einen Vergleich verschiedener Mikrocontroller heranziehen. Die Rechenleistung eines Mikrocontrollers wird längst nicht mehr anhand der Taktfrequenz abgeschätzt. Ein schnellerer Speicherzugriff kann beispielsweise die Laufzeit erheblich beschleunigen. Mit Single Instruction, Multiple Data (SIMD), Arm NEON-Architekturen auf Arm-Devices uvm. lässt sich die Rechenzeit massiv verkürzen. Die NEON-Intrinsic-Funktion des Arm ermöglicht die gleichzeitige Berechnung von vier Gleitkommaoperationen. Matrixoperationen, insbesondere die Matrixmultiplikation, lassen sich also fast vier Mal schneller ausführen als ohne die NEON-Intrinsic.

Bild 4: Laufzeitvergleich
Bild 4: Laufzeitvergleich
(Bild: Valeo)

Die Laufzeitevaluierung auf dem Target macht es einfacher, geeignete Hardware für neue Projekte auszuwählen. Auch lässt sich ermitteln, ob der Algorithmus auf der Zielhardware laufen kann oder ob ein anderer Algorithmus implementiert werden muss. Auf Bild 4 ist der Laufzeitvergleich eines Systems auf zwei verschiedenen Mikrocontrollern dargestellt. Die beiden Targets sind Mikrocontroller mit einer Taktgeschwindigkeit von 120 MHz. Laut der PIL-Testergebnisse ist Target B etwa 25% schneller als Target A. Dies weist darauf hin, dass in einem Projekt beim Wechsel von Target A auf Target B die Laufzeit verringert und damit anderen Funktionen zur Verfügung gestellt werden kann.

Resümee

Der Beitrag beleuchtet die Schwachstellen von SIL- und HIL-Umgebungen bezüglich der Implementierung und Validierung komplexer mathematischer Algorithmen in Mikrocontrollern. Als ergänzender Ansatz wird die PIL-Umgebung in Kombination mit SIL vorgeschlagen. Es werden Mechanismen implementiert, um den Aufwand für die Einrichtung einer PIL-Umgebung möglichst gering zu halten. Die Erzeugung des Datenformats für die Kommunikation zwischen PC und Target wird in den Software-Buildprozess eingebettet. Damit lassen sich Fehler bei der Datenumwandlung und -übertragung vermeiden. Die PIL-Umgebung liefert frühzeitig Hinweise darauf, ob die Hardware den Algorithmus in der verfügbaren Laufzeit ausführen kann. Optimierungen können implementiert und direkt auf der Zielhardware getestet werden. Zudem ist ein Vergleich unterschiedlicher Mikrocontroller möglich.

(Der Beitrag wurde mit freundlicher Genehmigung der Autoren dem Embedded Software Engineering Kongress Tagungsband 2018 entnommen. Übersetzung Sabine Pagler)

Literaturhinweise

[1] Petteri Aimonen. NanoPb. https://jpa.kapsi.fi/nanopb/

[2] M. Bacic. 2005. On hardware-in-the-loop simulation. In Decision and Control, 2005 and 2005 European Control Conference. CDC-ECC ’05. 44th IEEE Conference on Decision and Control. IEEE, Seville, Spain, Spain. https://doi.org/10.1109/CDC. 2005.1582653

[3] Google Protocol Buffers. http://code.google.com/apis/ protocolbuffers/

[4] R. Boot ; J. Richert ; H. Schutte ; A. Rukgauer. 1999. Automated test of ECUs in a hardware-in-the-loop simulation environment. In Computer Aided Control System Design, 1999. Proceedings of the 1999 IEEE International Symposium. IEEE, Kohala Coast, HI, USA. https://doi.org/10.1109/CACSD.1999.808713

[5] Luiz S. Martins-Filho, Adrielle C. Santana, Ricardo O. Duarte, and Gilberto Arantes Junior. 2014. Processor-in-the-Loop Simulations Applied to the Design and Evaluation of a Satellite Attitude Control. In Computational and Numerical Simulations, Prof. Jan Awrejcewicz (Ed.). InTech. https://doi.org/10.5772/57219

[6] MathWorks [n. d.]. Code Verification and Validation with PIL and External Mode. Retrieved April 30, 2018 from https://de.mathworks.com/help/supportpkg/beaglebone/examples/ code-verification-and-validation-with-pil-and-external-mode.html

[7] H. Thane ; D. Sundmark ; J. Huselius ; A. Pettersson. 2003. Automated test of ECUs in a hardware-in-the-loop simulation environment. In Proceedings International Parallel and Distributed Processing Symposium. IEEE, Nice, France. https://doi.org/ 10.1109/IPDPS.2003.1213515

* Anto Michael ist Softwarearchitekt bei Valeo und spezialisiert auf die Implementierung komplexer mathematischer Algorithmen in Embedded-Software auf Mikrocontrollern für Fahrerassistenzsysteme im Kfz.

Artikelfiles und Artikellinks

(ID:46648893)