Heutige Mikrocontroller müssen beispielsweise Echtzeit-Steuerungsalgorithmen verwalten, Highspeed-Kommunikationsprotokolle decodieren und hochfrequente Sensor-Signale verarbeiten. Herkömmliches Polling reicht dabei nicht mehr: Wenn die CPU in einer Endlosschleife die Schnittstellen abfragt, um nachzusehen ob neue Daten angekommen sind, bleiben CPU-Ressourcen unnötig blockiert und Reaktionszeiten steigen, um jeden I/O-Kanal und jede Peripherie zuverlässig zu bedienen. Jede neu aktivierte Peripheriefunktion erhöht die Reaktionsdauer und ändert das Applikations-Timing, da die CPU-Schleife, welche die Peripherieblöcke abfragt und verwaltet, immer länger und länger wird. Irgendwann geraten das Applikations-Timing und das Echtzeitverhalten aus dem Tritt.

Schritt 1: Interrupt statt Polling

Um das zu vermeiden, haben die meisten Embedded-Entwickler damit begonnen, Interrupts zu verwenden. Sie erfüllen die Echtzeit-Anforderungen damit einfacher und können immer mehr Peripherie verwalten. Interrupts können jedoch nur bestimmen, wann ein Echtzeit-Ereignis aufgetreten ist. Zur Reaktion ist die CPU weiter nötig, um I/Os und Peripherie zu lesen und Daten zu verarbeiten. Eine Interrupt-Serviceroutine unterbricht schlimmstenfalls andere latenzsensitive Aufgaben und ihr Kontextwechsel bringt einen Overhead mit sich. Der Entwickler muss mit zusätzlichen Latenzen rechnen, wenn mehrere Interrupts gleichzeitig auftreten. Das System ist damit schwerer berechenbar und die Effizient leidet. Je mehr Funktionen ein Mikrocontroller erfüllen muss, desto schlimmer wird die Lage. Multi-Chip-Lösungen, die das Echtzeit-Verhalten leichter garantieren können, sind meist zu teuer.

Auf einen Blick

Um Embedded-Mikrocontroller werden mit immer mehr Funktionen effizient auszunutzen, sind architektonischen Neuerungen nötig. Datentransfer und Datensignalisierungsaufgaben von der CPU in eigenständige Hardware auszulagern, verbessert den Determinismus, senkt Latenz und erhöht Konsistenz, Vorhersagbarkeit und die Systemzuverlässigkeit. Nebenbei wir das Entwickeln der Software einfacher.

Damit ein Mikrocontroller die hohen Datenraten und Frequenzen von Echtzeit-I/Os und Peripherieblöcken handhaben kann, sollte er seine umfangreiche Peripherie unabhängig von der CPU verwalten können. Eine Möglichkeit ist es, die Datenverarbeitung, den Datentransfer und die Datensignalisierungsaufgaben innerhalb der MCU voneinander zu trennen. Der Entwickler kann dann die Parameter für die CPU-Last, die Datentransferraten oder Interrupt-Frequenz ändern, ohne das Gesamtsystemverhalten oder andere Aufgaben zu beeinträchtigen. DMA-Controller (Direct Memory Access) erleichtern die Datenübertragung, und integriertes Event-Routing zwischen den Subsystemen entlasten die CPU um das I/O- und Peripherie-Management. Die CPU hat dann mehr Zeit für ihre Hauptaufgabe: die Datenverarbeitung.

Schritt 2: Daten ohne die CPU übertragen

Integrierte Coprozessoren sind in Embedded-Mikrocontrollern weit verbreitet. Sie übernehmen ganze Aufgaben, unterstützen rechenintensive Teile von Algorithmen und erhöhen somit die Rechenleistung der MCU. DMA- und Event-Systeme sind Embedded-Entwicklern weniger vertraut und werden deshalb nicht oft verwendet. Doch genau diese Systeme haben deutliche Vorteile, wenn es um den Determinismus des Systems geht. Die meisten Embedded-Anwendungen sind nicht sehr CPU-lastig, sondern primär mit der Peripherie und dem Echtzeit-Verhalten beschäftigt.

Tabelle 1: DMA-Controller können einen Großteil der Kommunikationsperipherie verwalten und übertragen Daten ohne Mithilfe der CPU.

Tabelle 1: DMA-Controller können einen Großteil der Kommunikationsperipherie verwalten und übertragen Daten ohne Mithilfe der CPU.Atmel

DMA-Controller übernehmen Datentransferaufgaben. Sie verschieben zum Beispiel Peripherie-Registerdaten in das SRAM. Ein Entwickler kann den DMA-Controller so konfigurieren, dass er sämtliche Daten von einer peripheren Einheit wie einem A/D-Wandler oder USART in das SRAM schreibt, damit die Daten für die CPU zur weiteren Verarbeitung zur Verfügung stehen. Da Polling- oder Interrupt-gesteuerte Datentransfers entfallen, ist die CPU-Last Null bis tatsächlich eine Datenverarbeitung anfällt. Die Datenraten sind hoch und gut vorhersehbar, da die CPU-Last für den Datentransfer niedrig gehalten bleibt (Tabelle 1).

DMA-Controller können beim Datentransfer die CPU also erheblich entlasten. Embedded-Entwickler haben damit bis zu 30 oder 50 % mehr CPU-Zyklen zur Verfügung. Wenn sich in dieser Konstellation die CPU-Last ändert, besteht viel weniger Risiko für den System-Determinismus. Die Datenverarbeitungs- und Datentransferaufgaben sind getrennt und unabhängiger voneinander.

Schritt 3: Autonom auf Events reagieren

Noch weniger Entwickler sind mit Event-Systemen vertraut, die zusammen mit dem DMA-Controller arbeiten und die Datensignalisierung allein verwalten. Ein Event-System ist ein Bus, der interne Signale von einer Peripherie zu einer anderen innerhalb der MCU verbindet. Tritt in einem Peripherieblock eine bestimmte Bedingung auf, kann sie Aktionen in einem anderen Peripherieblock auslösen, ohne die CPU einzubinden – bei Atmel-Bausteinen läuft das mit nur zwei Zyklen Latenz.

Das System ähnelt menschlichen Reflexen. Wir ziehen die Hand aus dem Feuer, ohne vorher das Gehirn zu bemühen. Traditionellerweise muss die Peripherie die CPU unterbrechen, um eine beliebige Aktion durchzuführen – auch beim Lesen der Peripherie. Durch das direkte Weiterleiten von Events zwischen der Peripherie, entlastet das Event-System die CPU von diesen Interrupts (Bild 1). Entwickler haben die Flexibilität, Peripherie so zu konfigurieren, dass sie verschiedenen Event-Kanälen folgt. Damit definieren sie die passende Event-Verteilung für ihre Applikation.

Bild 1: Der Event-System-Controller reagiert auf Ereignisse und leitet sie an andere periphere Einheiten weiter, ohne dabei die CPU zu belasten.

Bild 1: Der Event-System-Controller reagiert auf Ereignisse und leitet sie an andere periphere Einheiten weiter, ohne dabei die CPU zu belasten.Atmel

Zum Beispiel könnte das Event-System bei jedem Umschalten des analogen Komparators (AC)

  • eine A/D-Wandlung starten
  • Timer/Counter-PWM-Zyklen abschalten oder neu starten
  • I/O-Pins schalten, um externe Hardware zu synchronisieren
  • einen Timer-Counter erhöhen
  • einen Zeitstempel erfassen.

Da keine Interrupts oder CPU-Zyklen erforderlich sind, kann die Signalfrequenz zwischen den Peripherieblöcken wechseln, ohne den Determinismus des Gesamtsystems zu gefährden oder Echtzeitaufgaben zu beeinträchtigen. Die CPU-Last kann sich ebenfalls ändern, ohne die Peripherie-interne Signalisierung zu beeinflussen.

Datentransfer und Signalisierung besser ohne CPU

Die Kombination aus DMA-Controller und Event-System übernehmen Aufgaben von der CPU, die eine CPU bei weitem nicht so effizient erledigen kann. Dennoch gehören sie zu den Kernaufgaben in Embedded-Designs: Der DMA-Controller verwaltet den Datentransfer, während das Event-System festlegt, wann diese Transfers stattfinden – mit geringer Latenz und hoher Genauigkeit. Das Event-System stellt also sicher, dass die vom DMA verwalteten Werte abgetastet oder mit der richtigen Zeit/Frequenz ausgegeben werden. Der Großteil der CPU-Auslastung für Interrupts, Kontextwechsel und andere Funktion ist damit abgedeckt.

Für die CPU bleibt mehr Zeit zur Datenverarbeitung. Änderung an Datenverarbeitung,  Datentransfers und Datensignalrate beeinflusst sich kaum noch gegenseitig. Damit lassen sich komplexere Systeme leichter beherrschen und mehr MCU-Funktionen nutzen, ohne den System-Determinismus zu gefährden.

DMA-Controller und Event-Systeme besitzen mehrere Kanäle. Entwickler können damit eine Schaltung konfigurieren, die parallel zur Haupt-CPU arbeitet und mehrere Echtzeit-Aufgaben gleichzeitig in deterministischer Weise koordiniert. Der Grad an Parallelismus steigt deutlich, ohne eine leistungshungrigen CPU mit hoher Taktzahl und komplexem Echtzeit-Betriebssystem zu benötigen.

Rechen-Exempel

Ein simple Rechnung verdeutlicht die Vorteile: Für jeden Interrupt braucht ein System mindestens 50 Zyklen. Selbst einfachste Interrupts benötigen so lange, bis der Mikrocontroller den Kontext für einige Register speichert, auf eine Peripherie zugreift, die Daten speichert, den Kontext wiederherstellt und einen Pipeline-Flush durchführt.

Einzelne Interrupts stellen keine Herausforderungen dar. Echtzeit-Fristen einzuhalten wird dann schwierig, wenn verschiedene Interrupts gleichzeitig auftreten. Tritt zum Beispiel ein Interrupt mit höherer Priorität auf, der 75 Zyklen CPU-Zeit braucht, dann steigt die Latenz eines niedrigerprioren Interrupts, wenn die Aufgabe mit höherer Priorität ihren Ablauf unterbricht. Die Latenz für die Task mit geringerer Priorität steigt von 50 auf 125 Zyklen.

Mit jedem zusätzlichen Interrupt steigt die Worst-Case-Latenz für Interrupts mit niedrigerer Priorität und der Determinismus sinkt. Eine Task mit 50 Zyklen kann mehrere Male unterbrochen werden und dann Hunderte bis Tausende von Zyklen benötigen. Hier den Durchblick zu behalten, braucht genau Rechnung und/oder Simulation. Weiß ein Entwickler, dass die Latenz 50 Zyklen beträgt, kann er dies während der Datenverarbeitung berücksichtigen. Variiert sie jedoch zwischen 50 und 500 Zyklen, wird es heikel. Der Entwickler könnte eine Latenz von 200 Zyklen annehmen und jede Variation als Fehler ausweisen. Die Worst-Case-Latenz kann Echtzeit-Fristeinschränkungen hervorrufen und die Systemzuverlässigkeit beeinträchtigen. Softwareänderungen werden dann zu einer Herausforderung.

Bild 2: Der Event-System-Controller besitzt acht Kanäle, auf denen die Peripherie, DMA und PWM sowie Timer und Zähler miteinander kommunizieren.

Bild 2: Der Event-System-Controller besitzt acht Kanäle, auf denen die Peripherie, DMA und PWM sowie Timer und Zähler miteinander kommunizieren.Atmel

Bild 2 zeigt in einem Blockdiagramm, wie Event-System und DMA zusammenarbeiten. Der A/D-Wandler ist hier mit einem Sensor verbunden und tastet diesen ab, um Daten zu sammeln. Ein interner Zähler ist auf die Abtastfrequenz eingestellt, um regelmäßige und genaue Abtastintervalle zu erhalten – ohne CPU-Unterstützung. Hat der A/D-Wandler seine Abtastung beendet und die Wandlung abgeschlossen, löst er den DMA aus, um den Wert über das Event-System zu speichern.

Weitere Vorzüge

Für Aufgaben mit höheren Frequenzen ermöglichen ein Event-System und ein DMA-Controller etliche Sonderfunktionen:

  • Genaue Zeitstempel: Mit Zeitstempel-Samples lassen sich Signale zu externen Ereignissen besser synchronisieren. Bei einer Latenz von zwei Zyklen sind diese Zeitstempel wesentlich genauer als jene, die eine Interrupt-Serviceroutine markiert. Diese kann mehreren Hundert Zyklen aufweisen.
  • Kleinerer Stack: Je weniger Interrupts gleichzeitig zur CPU gelangen, desto kleiner kann Stack sein. Jede Interrupt-Serviceroutine muss den Kontext speichern und möglicherweise Dutzende Register in den Stack verschieben. Die Abschaffung mehrerer Kontextebenen kann die nötige Stack-Größe erheblich senken und damit RAM sparen.
  • Mehr Sicherheit bei der Skalierung: Selbst wenn man bei der gleichen MCU-Familie bleibt, hat ein höherwertiges System mit mehr Funktionen auch mehr Interrupts, was den Gesamt-Determinismus verschlechtert. Der Übergang eines Designs zu einer höher integrierten MCU kann daher die Signallatenz beeinträchtigen und damit auch die Genauigkeit – bei der Abtastung sowie bei der Ausgabe.
  • Einfache Software-Änderungen: Da das Event-Handling ohne CPU-Intervention auskommt, lässt sich die Software ändern, ohne das Echtzeitverhalten zu verändern. Selbst wenn mehr CPU-Zeit erforderlich ist, um zusätzliche Funktionen zu verarbeiten, bleiben das Event-Handling und die Reaktionszeit identisch.

Block-Denken in der Software

Um Software für die beschriebene Mikrocontroller-Architekturen effizient zu entwickeln, ist mehr als nur eine neue Toolchain nötig. Peripherietreiber, Low-Level-Routinen, Scheduler und/oder Betriebssystemen zu schreiben ist zu zeitaufwändig und komplex, um es für jedes neue Projekt durchzuführen. Die meisten MCU-Hersteller bieten nicht nur Mikrocontroller, sondern auch zahlreiche Code-Beispiele, Bibliotheken und Peripherie-Treiber an. Einige liefern eine komplette und standardisierte API für ihre Mikrocontroller. Damit erübrigen sich das Cut & Paste, das Importieren und Anpassen von Code aus verschieden Quellen wie Datei-Downloads, Applikationsschriften oder älteren Projekten.

Die Software-API besteht als Teil der Toolchain, und der Entwickler kann sofort mit dem Schreiben seines eigenen Codes beginnen. Einfache, vordefinierte Funktionen dienen zum Zugriff auf und zur Konfiguration sämtlicher Peripherie – ohne eigene Low-Level-Treiber schreiben zu müssen.

Die Software-API von Atmel nutzt auch den DMA-Controller und das Event-System. Die Module sind wie Peripherie konfiguriert; daher konfigurieren ein paar Zeilen Initialisierungscode das Event-System – und definieren, welche Periphere-Bedingungen Aktionen und Tasks in anderen Peripherieblöcken initiieren. Nach der Konfiguration arbeitet das Event-System genau und zuverlässig. Timing und Verhalten ändern sich nicht, solange der Entwickler nicht bewusst die Modulkonfiguration anpasst. Damit ist es einfacher, die Anwendung zu entwickeln und zu testen. Das Event-System-Timing bleibt zuverlässig zwischen Software-Änderungen und Revisionen, selbst wenn diese die CPU-Last erhöhen oder verringern.

Win-Win-Situation

Dank der Kombination aus DMA- und Event-System reagieren auch komplexe Embededd-Systeme zuverlässig und vorhersehbar auf Ereignisse. Normalerweise wären dazu ein komplexes Echtzeit-Betriebssystem nötig sowie eine Worst-Case-Betrachtung, welche Ereignisse wann auftreten. Da DMA- und Event-System unabhängig von der CPU arbeiten und die Ereignisse parallel verarbeiten, reduziert sich dieser Aufwand. Zusätzlich bleiben mehr CPU-Ressourcen frei: Sie kann komplexere Aufgaben erledigen oder länger im Schlafmodus bleiben.