ARM TechCon 2016 Cortex-M23 und –M33 sind erste ARMv8-M-CPUs

Nachdem ARM vor einem Jahr seine neue ARMv8-M-Architektur für Mikrokontroller vorgestellt hatte, wurden auf der TechCon 2016 die ersten zwei Produkte vorgestellt: Cortex-M23 und –M33 heißen die CPUs, welche dank TrustZone-Technologie vernetzte Anwendungen im Zeitalter des IoT sicherer machen werden.

Der größte Fortschritt bei ARMv8-M ist zweifelsohne die Implementierung der TrustZone-Technologie, welche Speicher, Register, Code, Interrupts, Stacks und Peripherie in sichere und unsichere Bereiche unterteilt, ohne anders als bei Cortex-A dabei Determinismus und Latenzzeiten (bis auf wenige Ausnahmen) zu beeinträchtigen. Damit kann man z.B. Firmware und Middleware von Drittherstellern gegen Zugriffe von außen isolieren und sichere Verschlüsselungsmechanismen für IoT-Anwendungen implementieren. Im Detail hatten wir die ARMv8-M-Architektur bereits in unserem Beitrag über ARMv8-M beleuchtet, so daß sich dieser Beitrag ganz auf die neuen CPUs konzentrieren kann.

Cortex-M23: Der bessere Cortex-M0+

Nachdem es bei ARM-CPUs künftig zwei Sub-Profile „aus einem Guß“ geben wird ( „Baseline“ und „Mainline“), implementiert der Cortex-M23 das Baseline-Profil, das einige Verbesserungen gegenüber ARMv6-M bringt, wie einen Hardware-Dividierer (bislang gab es nur einen Hardware-Multiplizierer). Heute sieht man viele MCUs mit einem proprietären Hardware-Dividierer, der an den Bus angehängt wird. Der Nachteil ist natürlich eingeschränkte Software-Kompatibilität.

Weitere neue Instruktionen betreffen Verzweigungen über den gesamten 32-bit-Adressraum oder einen Vergleich-mit-Null-und-Sprung . Bislang musste der Compiler bei ARMv6-M bei weiten Sprüngen mehrere kleine 16-bit-Sprünge hintereinander erzeugen.

Die sogenannten „Wide immediate Moves“ MOVT und MOV.W können in der Instruktion abgelegte 16-bit-Werte in die oberen bzw. unteren 16 bit eines Registers laden. Ein Fetch ist damit nicht notwendig. Interessant ist das, wenn der Kunde im Speicher einen Bereich als „Execute Only“ definiert hat, um z.B. das Auslesen einer Firmware zu verhindern. Konstanten musste man somit außerhalb dieses Speicherbereiches ablegen, da ein Lesezugriff in einem „Execute Only“-Bereich ja verboten ist und zu einer Exception führt. Damit sind diese Konstanten aber auch potentiell von Hackern auslesbar. Mit diesen neuen Befehlen MOVT und MOV.W können die Konstanten in einem „Execute-Only“ –Speicherbereich versteckt werden und sind gegen Auslesen geschützt.

Wichtig für Multicore-Chips ist die Möglichkeit, exklusiv-Zugriffe auf Daten mittels Semaphoren zu erreichen. Dies war bislang nur in ARMv7-M, nicht jedoch in ARMv6-M möglich. Mit dem „Baseline“-Profil wird diese Eigenschaft auch für die kleinen CPU-Cores möglich.

Last but not least kann jetzt der Status aller Interrupts individuell getrackt werden. Jeder Interrupt kann ja mit einer Priorität versehen werden. Angenommen, es sind 4 Interrupts anhängig, während ein höher priorisierter Interrupt abgearbeitet wird. Man könnte somit die Priorität der 4 anhängigen dynamisch verändern um festzulegen, welcher Interrupt als nächstes abgearbeitet wird.

Die zweistufige Pipeline hat der Cortex-M23 hingegen vom Cortex-M0+ geerbt. Die Folge ist, dass Sprünge nur 2 Taktzyklen benötigen und damit auch die Interrupt-Latenz bei 15 Zyklen verbleibt. Lediglich bei einem Wechsel vom „sicheren“ in den „unsicheren“ Bereich oder umgedreht beim Auftreten eines Interrupts steigt diese Latenz auf 24 Taktzyklen, da der vollständige Registersatz gesichert werden muß. Diese Werte beinhalten die Prioritätserkennung, die Verschachtelung, die Sicherung der Register auf dem Stack, die Verzweigung in die Interrupt-Routine und den Beginn der Ausführung der ersten Anweisung in der Interrupt-Routine. Der Cortex-NVIC führt alle diese Operationen in Hardware aus. Andere Hersteller geben teilweise geringere Latenzen in Hardware an, die Prozessoren müssen jedoch ggf. einige der genannten Funktionen in Software ausführen, was die Gesamtlatenz erhöht.

Der M23 ist wie der M0+ extrem einfach aufgebaut. Er beinhaltet eine von Neumann-Architektur (einheitlicher I/O-Bus für Daten und Befehle), und unterstützt kein TCM. Er holt Daten und Anweisungen direkt über eine einzige AMBA-AHB5-Schnittstelle (Cortex-M3/M4 und M33 haben zwei AHB5-Schnittstellen). Der konfigurierbare Nested-Interrupt-Controller (NVIC) ist eng mit dem Core verbunden und unterstützt bis zu 240 Interrupts in vier Ebenen und damit erheblich mehr als der Cortex-M0+ (32 Interrupts in 4 Ebenen). Sicherer und unsicherer Status teilen sich beim M23 die 240 Interrupts. In Keils Tool-Chain können die Interrupts einfach per Anklicken eines Auswahlfeldes als sicher oder unsicher klassifiziert werden.

Wie beim M0+ können beim M23 alle 2 Taktzyklen über den 32-bit-Bus jeweils zwei 16-bit-Werte gemeinsam aus dem Flash-Speicher geladen werden, was natürlich besonders bei sequentiellem Code Vorteile bringt, da weniger häufig auf den stromfressenden Flash-Speicher zugegriffen werden muss. Desweiteren kann mit der „Schnellzugriffsoption auf I/O“ in jeweils einem statt zwei Taktzyklen auf I/Os zugegriffen werden, weil parallel zum AHB-5-Bus ein weiterer Port installiert wird. Über den kann dann doppelt so schnell auf GPIOs und Register zugegriffen werden (eine LOAD/STORE-Anweisung kostet normal 2 Taktzyklen), für Speicherzugriffe eignet sich der Schnellzugriffs-Port dagegen nicht.

Besonders für Echtzeitanwendungen wichtig ist die optionale Möglichkeit, die Interrupt-Vektor-Tabelle vom Flash-Speicher ins RAM zu verschieben. Da z.B. die Löschung eines Flash-Blocks mehrere ms dauern kann und der Controller in dieser Zeit „blind“ für Interrupts ist, weil die Interrupt-Vektoren im für diesen Zeitraum blockierten Flash-Speicher liegen, ist er für diverse Echtzeitanwendungen unbrauchbar. Beim M23 kann man die Vektor-Tabelle ins RAM verschieben, so daß auch während eines Flash-Schreib- oder Lösch-Vorgangs Interrupts verarbeitet werden können.