What is the DATAFLOW Runtime [German]

by Marco Wuelser

 

Die DATAFLOW Runtime stellt das Herzstück einer mit DATAFLOW Software entwickelten Embedded Applikation (Firmware) dar. Die Runtime wird direkt in die Firmware gelinkt (binary) oder kompiliert (source code). Sie läuft entweder direkt auf der Hardware (Bare Metal) oder als Prozess in einem RTOS oder OS (Siehe Runtime Integration).

Sie übernimmt folgende Aufgaben:

  • Initialisierung und Startup
  • Definition des Idle Verhaltens
  • Kommunikation zwischen Active Parts
  • Handling der Active Part Prioritäten
  • Bereitstellen von Software Timern
  • Integration von Hardware Interrupts
  • Kapseln der Nebenläufigkeit
  • Behandeln von Ausnahmen (Assertions)
  • Bereitstellen eines Logging Framework (Optional)
  • Erfassen der Runtime Statistik (Optional)

Mehr Informationen zu diesem Thema hier:

Runtime Distribution

Die Runtime wird in der Form von Packages bereitgestellt. Ein Package enthält die notwendigen Header Dateien und Bibliotheken (lib) sowie Dokumentation und Beispiele. Aktuell können die folgende Packages erworben werden :

Plattform Paket
Win32 (Visual Studio, MinGW)

cpp14-mingw32.runtime.binary.win32
cpp-vs2015.runtime.binary.win32

STM32 F030 (IAR)

Auf Anfrage

STM32 F103 MD (IAR)

cpp-iar840.runtime.binary.stm32f103md

STM32 F103 HD (IAR)

Auf Anfrage

STM32 F767 (IAR)

Auf Anfrage

TI AM3354 (IAR)

Auf Anfrage

NIOS2 (Eclipse)

cpp-nios2.runtime.binary.nios2core

EmbOS (IAR)

Auf Anfrage

Windows Embedded Compact 7 (Visual Studio)

Auf Anfrage

QNX (Eclipse)  

Die plattformunabhänigen lib-Files werden für jede Toolchain in einem extra Package bereitgestellt:

  • cpp14-mingw32.runtime.binary.core
  • cpp-nios2.runtime.binary.core
  • cpp-vs2015.runtime.binary.core
  • cpp-nios2.runtime.binary.core

Es ist auch möglich, für eine zusätzliche gebühr Packages mit dem Source Code der Runtime zu erwerben, wenn dies gewünscht ist:

  • cpp-generic.runtime.source.am3354
  • cpp-generic.runtime.source.embos
  • cpp-generic.runtime.source.midcore
  • cpp-generic.runtime.source.qnx
  • cpp-generic.runtime.source.stm32f030
  • cpp-generic.runtime.source.stm32f103hd
  • cpp-generic.runtime.source.stm32f103md
  • cpp-generic.runtime.source.stm32f767
  • cpp-generic.runtime.source.win32
  • cpp-nios2.runtime.source.nios2core
  • cpp-vs.runtime.source.wec7

Der platformunabhänige Source Code werden in einem extra package bereitgestellt:

  • cpp-generic.runtime.source.core

Runtime Integration

Wir unterschieden zwischen Bare Metal, RTOS und OS Runtime, wenn wir den Aufbau einer Applikation die mit der DATAFLOW Runtime umgesetzt wird betrachten.

Bare Metal

 

Die Applikation ruft Funktionen aus der Runtime (Runtime API) sowie dem Verwendeten HAL auf. Sie macht keine direkten Zugriffe auf die Zielhardware (Testbarkeit)

Die Runtime wird durch den SysTick der Zielhardware periodisch (z.B. jede Millisekunde) aufgerufen. Weiter kann sie die Hardware Interrupts ein- und ausschalten. Ansonsten macht sie aber selber keine weiteren Hardware Zugriff.
Der HAL greift auf die Register der Zielhardware zu.

RTOS

 

Die Applikation ruft Funktionen aus der Runtime (Runtime API) sowie dem Verwendeten HAL und RTOS Wrapper auf. Sie macht keine direkten Zugriffe auf die Zielhardware oder das RTOS (Testbarkeit)

Die Runtime wird als RTOS Task/Thread/Prozess registriert und von diesem periodisch aufgerufen. Weiter kann sie die Hardware Interrupts ein- und ausschalten.  Weiter verwendet sie RTOS Funktionen (z.B. Mailboxen) um Nachrichten zwischen Active Parts zu versenden. Active Parts werden ebenfalls als RTOS Task/Thread/Prozess registriert.
Der HAL greift auf die Register der Zielhardware zu.
Der RTOS Wrapper abstrahiert alle RTOS Zugriffe der Applikation (das Prinzip ist das gleiche wie beim HAL).
Das RTOS läuft direkt auf der Zielhardware, erlaubt es aber über einen HAL Peripherien direkt anzusteuern.

OS

 

Die Applikation ruft Funktionen aus der Runtime (Runtime API) sowie dem Verwendeten HAL und OS Wrapper auf. Sie macht keine direkten Zugriffe auf die Zielhardware oder das OS (Testbarkeit). Die Runtime wird als OS Thread registriert und von diesem periodisch aufgerufen. Sie verwendet das Message System des OS um Nachrichten zwischen Active Parts zu versenden. Active Parts werden ebenfalls als Threads registriert. Der OS Wrapper abstrahiert alle OS Zugriffe der Applikation (das Prinzip ist das gleiche wie beim HAL).
Das OS läuft direkt auf der Zielhardware und erlaubt es einer Applikation nicht, direkt auf die Hardware zuzugreifen. Daher müssen alle Hardware Zugriffe über das Entsprechende OS API und den OS Wrapper erfolgen.

Initialisierung und Startup

Die Runtime erlaubt die volle Kontrolle über die Initialisierung und Start der Zielhardware. Dazu werden verschiedene Callback Methoden aufgerufen. Zuerst wird eine Methode für die Hardware Initialisierung aufgerufen. In dieser kann z.B. die Click Frequenz, Interrupt Prioritäten oder Pin Muxing konfiguriert werden.

Anschliessend wird bei jedem Active Part die Initialize() Methode aufgerufen um den Zustand zu initialisieren.

Nach abgeschlossener Initialisierung, wird bei jedem Active Part die Start() Methode aufgerufen. Dies erlaubt es, bereits bei Start der Firmware Timer zu starten oder Nachrichten zu verschicken. Zuletzt werden alle Interrupts der Zielhardware aktiviert und die Runtime gestartet. Diese führt nun getrieben durch den System Tick Interrupt den Message Loop aus bis die Hardware ausgeschaltet wird (siege auch Idle Verhalten).

 

Idle Verhalten

Wenn die Runtime keine Timer oder Nachrichten zu Verarbeiten hat, wird die OnIdle() Callback Funktion aufgerufen. Dies erlaubt es dem Entwickler, genau zu definieren, wie sich die Hardware in diesem Fall verhalten soll. Es ist z.B. Möglich in einen Stromsparmodus zu wechseln. Die Runtime wird automatisch durch den nächsten Hardware Interrupt aufgeweckt (z.B. SysTick).

Kommunikation

Die Runtime übernimmt die Kommunikation zwischen den Active Parts. Dazu wird ein Event Buffer im Arbeitsspeicher verwendet. Wenn ein Active Part eine Nachricht verschickt, kopiert die Runtime diese in den Buffer. Nun wird jeder Empfänger der Nachricht anhand der Priorität (siehe unten) aufgerufen. Die Nachricht verbleibt im Buffer und kann von den Active Parts gelesenes aber nicht verändert werden. Nach Abschluss der Verarbeitung wird der Slot im Event Buffer wieder für eine neue Nachricht freigegeben.

Prioritäten

Um die Reihenfolge der Verarbeitung bei gleichzeitig versendeten Nachrichten zu definieren, kann jedem Active Part eine Priorität von 1 bis 32 vergeben werden. Die Runtime wird immer zuerst Nachrichten an Active Parts mit höherer Priorität verarbeiten. Grundsätzlich wird eine Nachricht vollständig abgearbeitet (Coorporative Multitasking) und darf daher nicht blockieren. Wird eine Nachricht an ein höher priorisiertes Active Part verschickt (auch aus einem Interrupt), wird die Runtime dieses zuerst abhandeln und anschliessend die Verarbeitung des Senders fortsetzen (Preemtive Scheduling).

Software Timer

Software Timer können von jedem Active Part gestartet und gestoppt werden. Wenn ein Timer ausläuft, wird das entsprechende Active Part über einen Methodenaufruf benachrichtig, genau wie wenn es eine Nachricht erhalten hat.

Hardware Interrupts

Hardware Interrupts werden mittels ISR Methoden (Interrupt Service Routine) implementiert. Diese Methoden sind in der Interrupt Tabelle (Interrupt Vector Table)hinterlegt und werden von Mikroprozessor angesprungen, wenn der entsprechende Interrupt aufgerufen wird. Innerhalb der ISR wird die Runtime über das Betreten und Verlassen des ISR informiert. Alle innerhalb des ISR verschickten Nachrichten werden nach verlassen des ISR umgehend verarbeitet. (Siehe Kommunikation und Prioritäten).

Bei verschachtelten Interrupts (Interrupt wird durch höher prioritsierten Interrupt unterbrochen: Nested IRQ) wird die Runtime die Nachrichten verarbeiteten, sobald der letzte Interrupt verlassen wurde. Welche Interrupts andere Interrupts unterbrechen können wird durch die Hardware Konfiguration des NVIC (Nested Vector Interrupt Controllers) des ARM Cores festgelegt.

Nebenläufigkeit

Die Runtime übernimmt die Handhabung der Nebenläufigkeit. Dazu wird sie für kritische nicht atomare Operationen (z.B. Lesen, Verändern, Schreiben) der Runtime die Hardware Interrupts kurzzeitig ausschaltet. Das stellt sicher, das keine Konflikte beim Zugriff auf Message Buffer etc. entstehen. Der Code innerhalb von Active Parts muss sich daher nicht um Nebenläufigkeit kümmern und kann geschrieben werden als ob die Methode ganz alleine auf dem System laufen würde. Wichtig hierbei ist, das der Zugriff auf Hardware Ressourcen (z.B. Peripherien wie GPIO Pins, USART, ..) nur durch jeweils ein einziges Active Part erfolgen darf. Falls mehrere Active Part die selbe Ressource verwenden müssen, sollte ein Active Part dien Zugriff kapseln und von den Anderen durch Nachrichten angesprochen werden.

Ausnahmen

Die DATAFLOW Runtime erlaubt es auch, das Verhalten bei Ausnahmen zu definieren. Dazu werden 3 Arten von Assertions verwendet:

  • Compiler
  • Debug
  • Ex

AssertCompiler:
Wird beim Kompilieren der Applikation ausgelöst. Kann verwendet werden um statische Annahmen zu überprüfen. z.B Grösse von Buffern oder Typen.

AsstertDebug:
Wird zur Laufzeit ausgelöst. Kann verwendet werden um Fehler im Code zu entdecken wenn z.B. ungültige Parameter übergeben werden.

AssertEx:
Wird verwendet um kritische Fehler zu behandeln, die ein normales Ausführen der Applikation verunmöglichen.

Das Verhalten bei AssertDebug und AssertEx kann über Callback Funktionen definiert werden. Dies erlaubt je nach Anwendung, das verhalten anzupassen und z.B. einen Neustart auszulösen, die Applikation anzuhalten oder andere Vorkehrungen zu treffen.

Logging Framework

Als Option bietet die Runtime ein Logging Framework an. Dieses erlaubt das registrieren eines Active Parts als Logger. Über eine Runtime Funktion kann anschliessend jederzeit eine Log Nachricht an dieses Active Part gesendet werden, ohne das dazu Channels und Ports definiert werden müssen. Das Format der Log Nachricht ist ebenfalls frei definierbar, genauso wie die Priorität des Loggers.

Runtime Statistik

Die DATAFLOW Runtime bringt eine Laufzeit Statistik Funktion mit. Diese muss beim kompilieren der Runtime aktiviert werden, da zusätzliche Aufrufe im Code eingebaut werden müssten, um die Daten zu erfassen. Im Binary Packages (siehe unten) gibt es eine Bibliothek mit aktivierter Statistik. Wann die Statistik aktiviert wurde, werden folgende Daten erfasst und als statische Variablen im Debugger ausgelesen werden:

  • Auslastung Event Buffer
  • Auslastung Timer Buffer
  • Verweildauer der Aufrufe in den einzelnen Active Parts
  • CPU Load (Benötigt einen Hardware Timer für akkurate Ergebnisse).

Go back