Entwicklung für Multicore-SoCs Trace-Daten im Bild

Steigende Taktfrequenzen sowie der Trend zu Multicore-SoCs haben zur Folge, dass die Datenmengen beim Tracen von Mikrocontroller-Applikationen beständig wachsen. Die größte Herausforderung hierbei ist die Analyse und Aufbereitung der gewonnenen Informationen. In den Bereichen Profiling, Code-Coverage oder Execution-Sequence-Diagram lassen sich diverse Eigenschaften einer Anwendung beobachten, die dann dazu dienen, Fehler aufzuspüren und die Leistung zu optimieren oder gar die Softwarequalität zu verbessern.

Bei der Fehlersuche und Optimierung einer modernen Mikrocontroller-Applikation muss das Debug- und Testwerkzeug während des Entwicklungszyklus‘ verschiedenen Anforderungen gerecht werden. Obwohl auch für Embedded-Software die alte Regel gilt, jede Codezeile mindestens einmal im Debugger abzuarbeiten, ist der Test von Algorithmen nicht die eigentliche Domäne von High-End-Debuggern.

Zum einen lassen sich die Algorithmen oft auch auf dem PC testen oder sind durch Codegenerierung entstanden. Zum anderen erfüllen zumeist auch einfachste Debugger die Anforderungen für algorithmische Tests. Zudem wird Software selten von Grund auf neu entwickelt. Vielmehr existiert meist schon ein beachtlicher Grundstock an Programmcode von vorausgegangenen Produkten, der auf eine neue Hardwareplattform portiert werden soll.

Für solche integrativen Aufgaben besteht eine wichtige Anforderung an ein Testwerkzeug darin, die laufende Applikation mit geringer oder keiner Veränderung des Zeitverhaltens zu beobachten. Leistungsfähige Debugger bieten heutzutage in engem Zusammenspiel mit On-Chip-Komponenten vielfältige Möglichkeiten, das laufende System zu beobachten.

So erlauben moderne Debug-Schnittstellen wie »Device Access Port« (DAP) von Infineon, »Serial Wire Debug« (SWD) von ARM oder die NEXUS-kompatible (IEEE-ISTO 5001-2003) »OnCE«-Schnittstelle von Freescale beispielsweise das Lesen und Schreiben von Target-Speicher zur Laufzeit.

Damit kann der Debugger Programmvariablen periodisch lesen. Dadurch lassen sie sich direkt anzeigen oder kontinuierlich in eine Datei speichern. Viel interessanter noch ist aber eine grafische Darstellung zusammen mit einem Zeitstempel (Bild 1).

Wenn sogar mehrere Variable, die mathematisch verknüpft eine physikalische Prozessgröße repräsentieren, als solche dargestellt werden können, ist das besonders hilfreich.

Debug und Trace

Die Debug-Schnittstellen DAP und SWD erlauben auch das Auslesen des Befehlszählers bei laufendem Mikrocontroller. Zeichnet man den Befehlszähler periodisch auf und gleicht dies mit den symbolischen Informationen der Applikation ab, lässt sich so eine statistisch basierte Übersicht über das Laufzeitverhalten der Applikation erstellen. Durch entsprechende Visualisierung ist auf einen Blick zu erkennen, in welchen Funktionen der Befehlszähler am häufigsten festgestellt wurde (Bild 2).

Unter Berücksichtigung der Funktionsgröße lassen sich hier Engpässe sehr schnell erkennen. Königsdisziplin bei der Beobachtung einer laufenden Mikrocontroller-Applikation ist jedoch der Programm- und Daten-Trace. Dabei werden Informationen während der Ausführung der Applikation aufgezeichnet, um die Befehlsbearbeitung und/oder die Datenzugriffe später zu rekonstruieren. Dies geschieht entweder durch Ausgabe der gewonnenen Informationen über Port-Pins und Speicherung auf einem externen Gerät oder durch Speicherung auf dem Chip selbst.

Bei der externen Variante erfolgt die Datenübertragung entweder parallel mit einer Portbreite von vier, acht oder sogar 16 Pins, aufgrund der ständig steigenden Datenmenge kommen zunehmend aber auch serielle Hochgeschwindigkeitsschnittstellen, beispielsweise mit »Aurora«-Protokoll (von Xilinx entwickelt), zum Einsatz. Die Speichergrößen selbst liegen dabei im High-End im Gigabyte-Bereich.

Bei der internen Variante gilt es, zwischen mit internem Trace-Speicher ausgestatteten Serien-SoCs und Mikrocontrollerfamilien, für die spezielle sogenannte Emulation-Devices zur Verfügung stehen, zu unterscheiden. Ein klassischer Trace-Speicher-Vertreter für SoCs der ersten Kategorie ist der Embedded-Trace-Buffer (ETB) »Coresight« der Firma ARM, den Chipherstellern lizenzieren und implementieren können.

Er ist typischerweise nur wenige Kilobyte groß und eignet sich damit nur für sehr kurze Aufzeichnungen oder Messaufgaben, bei denen sehr wenig Daten anfallen, etwa die Beobachtung einer sich selten ändernden Variable. Spezielle Emulation-Devices hingegen, wie sie zum Beispiel Infineon für ihre »TriCore«-MCUs anbietet, sind mit bis zu 768 KByte Trace-Speicher sowie einer ausgefeilten Trigger- und Filterlogik zur Steuerung der Aufzeichnung ausgestattet. Damit steht nicht nur deutlich mehr Platz für die Aufzeichnung zur Verfügung, sondern es kann sehr detailliert bestimmt werden, was aufgezeichnet wird.

Eine Frage der Darstellung

In allen Fällen stellt sich die Frage, wie sich die gewonnenen, dekodierten und gespeicherten Daten für den Anwender am effizientesten visualisieren lassen. Das klassische Trace-Window mit Anzeige des Programmablaufs und gegebenenfalls auch der Datenzugriffe dient vor allem der direkten Fehlersuche. Mit entsprechender Konfiguration kann man so beispielsweise feststellen, warum eine Variable außerhalb eines zulässigen Werte-bereiches beschrieben wurde, oder die einen Trap verursachende Programmstelle einkreisen (Bild 3).

Allerdings lässt sich mit den Trace-Daten noch viel mehr anfangen. Ein Beispiel ist die Erstellung eines Profiling-Diagramms aus einer Aufzeichnung der Programm-abarbeitung. Das Diagramm beruht, anders als die oben erwähnte statistische Auswertung der Laufzeit auf Basis der periodischen Erfassung des Befehlszählers, auf exakten Messwerten. Darüber hinaus lassen sich bei Vorhandensein von Zeitstempeln in den Trace-Daten diese nicht nur auf der Grundlage der gezählten Befehle darstellen, sondern auch auf Basis der gemessenen CPU-Laufzeit.

Durch Bestimmung der Funktionseintritte innerhalb des Datenstroms lassen sich die Anzahl der Funktionsaufrufe und damit eine durchschnittliche Funktionslaufzeit ermitteln und darstellen (Bild 4).

Mithilfe von Profiling-Diagrammen können Entwickler nicht nur Funktionen mit hohem Anteil an der CPU-Last einfach auffinden, auch Schwächen in der Softwarearchitektur lassen sich damit häufig schnell erkennen.

Die sequenzielle Ausführung der Funktionen wiederum lässt sich hervorragend mit Hilfe eines »Execution Sequence Diagrams« visualisieren.

Nicht nur die Unterbrechung der linearen Programmausführung durch Interrupts kann damit detailliert dargestellt und untersucht werden (Bild 5).

Auf einen Blick erkennbar sind auch Fehler in der Interrupt-Priorisierung, beispielsweise wenn ein niedrig priorisierter Interrupt stets von einem höher priorisierten verdrängt wird und die zugehörige Funktion nicht oder zu selten zur Ausführung kommt. Sehr gut lassen sich aus den Trace-Daten auch Code-Coverage-Informationen gewinnen. Solche Coverage-Analysen sind fester Bestandteil von Softwarezertifizierungen.

Man unterscheidet verschiedene Formen der Abdeckung, zum Beispiel C0 auf Ebene der einzelnen Maschinenbefehle oder C1 für jede Programmverzweigung. Eine Möglichkeit ist, die Ergebnisse der Analyse abhängig vom Darstellungsmodus direkt im Programmfenster durch farbige Markierung der Hochsprachenzeile beziehungsweise des einzelnen Maschinenbefehls sichtbar zu machen. 

Interessanter, weil wesentlich übersichtlicher, ist jedoch die grafische Darstellung der Codeabdeckung in einem Coverage-Balkendiagramm, aufgeschlüsselt nach Funktionen und bei Bedarf sogar bis zur einzelnen Zeile im Quelltext (Bild 6).

Um die Vorteile, die sich aus der Visualisierung der Trace-Daten für den Anwender ergeben, auch in eigene Workflows einbinden beziehungsweise teilautomatisiert nutzen zu können, können moderne Debug- und Trace-Tools wie die »Universal Debug Engine« (UDE) von PLS alle Trace-Auswertungen als XML-Datei, einem leicht maschinenlesbaren Format, abspeichern.

Darüber hinaus können Scripts oder andere Applikationen die Ergebnisse auch direkt über COM (Component Object Model) abfragen. Wie die genannten Beispiele zeigen, lassen sich die bei aktuellen SoCs anfallenden Trace-Daten nicht nur zur Fehlersuche mit Hilfe des klassischen Trace-Fensters, sondern auch auf der Ebene des System-Debuggings sinnvoll nutzen. Voraussetzung dafür sind allerdings entsprechend leistungsfähige Werkzeuge zur grafischen Visualisierung und Auswertung mit angepassten Diagrammen für Profiling, Code-Coverage und Execution-Sequence.

Über den Autor:

Heiko Riessland leitet das Produktmarketing bei PLS.