Teil 6 der TSN-Serie
Mit offenen Lösungen zum eigenen Endpunkt
Das TSN-Ökosystem basiert auf einer großen Anzahl an Standards. Für spezifische Anwendungsfälle und dazu benötigte Endpunkte werden oft nur wenige davon benötigt. Das Tutorial vermittelt einen Praxis-Einstieg, wie mit offenen Lösungen ein TSN-Endpunkt entwickelt werden kann.
Welche Standards Endpunkte in einem TSN-Netzwerk unterstützen müssen, ist sehr applikationsspezifisch. Für viele Anwendungen ist eine Zeitsynchronisation der lokalen Applikationen bereits ausreichend, andere benötigen lediglich synchronisierte Zeitstempel für Daten, weitere sollten Datenströme oder Datenklassen zu exakten Zeitpunkten oder Zeitschlitzen senden können. All dies lässt sich mit verfügbaren offenen Lösungen und verfügbarer Hardware bereits heute schon realisieren.
Für das folgende Tutorial wird ein lauffähiges Linux-System mit PREEMPT_RT Echtzeit-Kernel vorausgesetzt. Als Hardwareplattform dient ein Standard-PC, inklusive Netzwerkkarte mit hardwarebasierter Zeitstempel-Einheit (beispielsweise Intel i210). Für die Erzeugung und Installation des Echtzeit-Kernels sei auf die Anleitung „HOWTO setup Linux with PREEMPT_RT properly“ im Realtime Linux Wiki der Linux Foundation verwiesen [1].
Zur Verwendung der TSN-Funktionalitäten ist mindestens die Kernelversion 5.3 nötig (für die Beispiele kam die Version 5.6 17-rt10 zum Einsatz). Die gegebenen Konfigurations- und Code-Beispiele beziehen sich auf ein Debian-basiertes System, sollten sich jedoch auch auf andere Distributionen leicht übertragen lassen. Die Ausführungen und Beispiele basieren auf den Anleitungen des Open-Source-Projekts AccessTSN [2], „TSN Documentation Project for Linux“ von Intel/AVnu [3], der Anleitungen im Realtime Linux Wiki der Linux Foundation [4] sowie dem „PubSub_realtime“-Beispiel des open62541-Projekts [5].
Auf diese Anleitungen sowie den Manpages der verwendeten Software-Werkzeuge sei auch hinsichtlich weiterer Details und Hintergründe verwiesen.
Zeitsynchronisation mit LinuxPTP (802.1AS)
Für die bei TSN benötigte Zeitsynchronisation der Netzwerk-Teilnehmer kann LinuxPTP (ptp4l) verwendet werden. Es sollte mindestens die Version 2.0 zum Einsatz kommen. LinuxPTP kann über die Paketquellen der Distributionen installiert oder der Quellcode selbst compiliert werden (siehe Listing 1).
Im Beispiel kommt die Zeitsynchronisierung entsprechend IEEE 802.1AS zum Einsatz. LinuxPTP bringt eine passende Konfigurationsdatei bereits mit. Diese wird nach der Installation vom Installationsverzeichnis ins Konfigurationsverzeichnis kopiert. Zur Zeitsynchronisation wird ptp4l mit der 802.1AS Konfigurationsdatei auf der gewünschten Netzwerk.Schnittstelle (im Beispiel eine Intel i210) gestartet (siehe Listing 2).
Das Schlüsselwort -2 legt das OSI-Layer für die PTP-Kommunikation fest, für IEEE 802.1AS wird in diesem Beispiel Ethernet spezifiziert. Der Parameter step_threshold=1 sorgt für eine schnellere Konvergenz.
LinuxPTP synchronisiert die Hardware Clock im Netzwerk-Interface (PHC) mit den anderen Netzwerk-Teilnehmern. Um Anwendungen auf diese synchronisierte Netzwerkzeit beziehen zu können, muss die Systemuhr auf die PHC synchronisiert werden. Da die PHC die internationale Atomzeit (TAI) und die Systemuhr die koordinierte Weltzeit (UTC) als Zeitbasis verwenden, muss der Offset zwischen den beiden Zeitbasen konfiguriert werden. Hierzu lässt sich der Befehl in Listing 3 nutzen.
Anschließend gilt es, die Synchronisation der Systemuhr mit dem Werkzeug phc2sys zu starten (Listing 4). Hierzu sind die zu verwendende PHC als Master und die Systemuhr als Slave anzugeben. Auch bei phc2sys kann über den Parameter step_threshold=1 für eine schnellere Konvergenz gesorgt werden. Zusätzlich kann mit dem Schlüsselwort w darauf gewartet werden, dass die PHC durch ptp4l synchronisiert ist.
Das Network-Time-Protokoll (NTP) muss auf dem System deaktiviert werden, um Interferenzen mit phc2sys zu vermeiden. Ebenso dürfen nur jeweils eine Instanz von ptp4l und phc2sys ausgeführt werden.
Die beiden genannten Werkzeuge zur Zeitsynchronisierung können auch als Systemdienste ausgeführt werden, dazu sind die Dienstkonfigurationen entsprechend der gezeigten Startbefehle zu ändern.
Je nach Systemkonfiguration und Auslastung sollten die Ausführungsprioritäten der Zeitsynchronisierungsdienste erhöht werden, damit diese Dienste genügend Rechenzeit erhalten. Ein Beispiel dazu findet sich im ‚Pub/Sub_realtime‘-Beispiel von open62541.
Da durch eine Konfiguration der Netzwerk-Warteschlangen die Netzwerk-Schnittstellen neugestartet werden müssen und es damit zu Zeitsprüngen kommen kann, sollte die Netzwerk-Konfiguration vor dem Start der Zeitsynchronisation abgeschlossen sein.
Zeitgesteuerte Datenübertragung
In Linux ist die Unterstützung der gesteuerten Datenübertragung von TSN im Linux Traffic Control (TC) System umgesetzt. Verschiedene Warteschlagentypen und Prinzipien (Queuing Disciplines, qdiscs) sorgen für die Sortierung und Sendezeitpunkte der zu übertragenden Datenpakete. Mehrere davon lassen sich für TSN nutzen. Größtenteils beziehen sich die Qdiscs und die gezeigten Einstellungen auf den Sendepfad, für reine Datenempfänger sind sie also unwichtig.
Senden zu einem bestimmten Zeitpunkt (ETF)
Das zeitgesteuerte Senden lässt sich in TSN dazu nutzen, um beispielweise zyklische Streams zu realisieren. Dabei wird ein Datenpaket zu einem spezifizierten Zeitpunkt gesendet. Diesen Sendezeitpunkt muss die Anwendung vor Senden des Paketes bestimmen und zusammen mit dem Paketinhalt der Linux-Netzwerkschicht übergeben. Die Netzwerkschicht von Linux kümmert sich durch die Nutzung der ETF-Qdisc (Earliest TXTime First) um den korrekten Versand des Datenpakets. Dabei kann auch eine Hardware-Unterstützung verwendet werden, um eine höhere Präzision zu erreichen. Dies wird beispielsweise von dem Intel-i210-Netzwerk-Controller unterstützt. Die Funktion nennt sich ‚LaunchTime‘.
Die ETF-Qdisc arbeitet für jede Warteschlange separat und kann auch nur für einzelne Warteschlangen aktiviert beziehungsweise konfiguriert werden. Für die Nutzung von ETF müssen also zuerst die Warteschlangen und die Zuordnung der Linux-Socketprioritäten zu den Warteschlangen konfiguriert werden. Dies geschieht über die Qdisc MQPRIO. Diese realisiert die Zuordnung der Linux-Socket-Prioritäten auf Traffic-Klassen und schließlich auf Hardware-Warteschlangen (sofern verfügbar). Listing 5 zeigt ein Konfigurationsbeispiel unter Verwendung einer Intel i210 mit vier Warteschlangen.
Für die Konfiguration wird das Kommandozeilenwerkzeug tc verwendet.
Der gezeigte Befehl legt eine MQPRIO Qdisc an unterster Stelle des i210-Netzwerk-Interfaces mit der ID 6666 an. Weiterhin definiert der Befehl num_tc 3 drei Traffic-Klassen (Klasse 0 bis Klasse 2). Danach sind die 16 verfügbaren Linux-Socket-Prioritäten (0-15) den Traffic-Klassen zuzuordnen. Dafür wird nach dem ‚map‘-Schlüsselwort für jede Socket-Priorität die zugehörige Traffic-Klasse angegeben.
Im gezeigten Beispiel erfolgt die Zuordnung der Socket-Priorität 3 der Traffic-Klasse 0 und die Zuordnung der Socket-Priorität 2 der Traffic-Klasse 1. Alle anderen Socket-Prioritäten sind der Traffic-Klasse 2 zugeordnet. Über das Schlüsselwort ‚queues‘ werden anschließend die Traffic-Klassen den Warteschlangen zugeordnet. Dabei sind die Traffic-Klassen in strikter aufsteigender Reihenfolge zu konfigurieren. Der erste Eintrag gilt somit der Traffic-Klasse 0 und so weiter. Der jeweilige Eintrag spezifiziert die Anzahl an Warteschlangen mit Offset. Im Beispiel wird der Traffic-Klasse 0 die erste Warteschlange (also Warteschlange Q0) und der Traffic-Klasse 1 die zweite Warteschlange (also Q1) zugeordnet. Der Traffic-Klasse 2 sind im dritten Eintrag zwei Warteschlangen ab dem Offset 2 (also Warteschlange Q2 und Q3) zuzuordnen. Zum Schluss ist über ‚hw 1‘ das Hardware-Offloading zu aktivieren.
Nachdem die Warteschlangen angelegt sind kann die ETF-Qdisc konfiguriert werden (siehe Listing 6). Das gezeigte Beispiel legt eine ETF-Qdisc auf die erste im vorherigen Beispiel angelegte Warteschlange (ID 6666:1). Der clockid-Parameter legt die für das zeitgesteuerte Senden zu verwendende Uhr fest. Die verwendete CLOCK_TAI ist die einzige unterstützte. Für eine korrekte Funktion muss diese Uhr synchronisiert werden (siehe Abschnitt Zeitsynchronisierung).
Der Parameter ‚delta‘ spezifiziert die Zeitspanne, um die ein Paket vor dem spezifizierten Sendezeitpunkt an die Hardware übergeben wird. Diese Zeitspanne ist für jedes System separat zu bestimmen. Sie lässt sich über das Werkzeug cyclictest abschätzen. Details sind im „TSN Documentation Project for Linux“ zu finden. Das Beispiel verwendet eine Zeitspanne von 500 µs. Abschließend spezifiziert das Schlüsselwort offload die Aktivierung der Hardware-Unterstützung.
Senden in Traffic-Klassen nach Qbv (TAPRIO)
Neben dem zeitgesteuerten Senden unterstützt Linux die TSN Funktionalität „Enhancements for Scheduled Traffic“ (IEEE802.1Qbv). Jede Traffic Klasse bekommt dabei einen Zeitschlitz für die Übertragung zur Verfügung gestellt. Unter Linux ist die Funktionalität in der TAPRIO-Qdisc (Time Aware Priority) umgesetzt. Diese wird ähnlich zur MQPRIO-Qdisc (siehe Abschnitt ETF) konfiguriert. Zusätzlich werden die Zeitschlitze als Gate-Control-List parametriert. Mangels frei verfügbarer Netzwerk-Schnittstellen mit Qbv-Unterstützung verwendet das Beispiel in Listing 7 kein Hardware-Offloading.
Der Parameter ‚base-time‘ spezifiziert den Startzeitpunkt des Schedules in Nanosekunden. Die Einträge ‚sched-entry‘ konfigurieren die Zeitschlitze. Dabei steht jeder Eintrag für einen Zeitschlitz. Das Schlüsselwort ‚S‘ sorgt für das Öffnen der danach spezifizierten Traffic-Klassen. Die Traffic-Klassen werden als hexadezimaler Parameter angegeben, bei dem jedes Bit eine Traffic-Klasse repräsentiert. Der Zeitschlitzeintrag ist durch die Angabe der Zeitschlitzlänge in Nanosekunden angegeben.
Im Beispiel sind somit drei Zeitschlitze parametriert. Während des ersten 300-µs-Zeitschlitzes wird die Traffic-Klasse 0 geöffnet, während des zweiten 300-µs-Zeitschlitzes werden die Traffic-Klassen 0 und 1 geöffnet und während des 400-µs-Zeitschlitzes ist die Traffic-Klasse 2 geöffnet. Die Parameter ‚flags‘ und ‚txtime-delay‘ aktivieren einen TxTime-assistierten Modus und die zugehörige Zeitspanne. Der Parameter ‚clockid‘ spezifiziert die für die Zeitstempel zu verwendende Uhr.
Die Umsetzung der Anwendung
Die Entwicklung einer Anwendung, die in Echtzeit auf dem beschriebenen System ausgeführt werden und dabei die beschriebenen TSN-Funktionalitäten verwenden soll, kann auf Basis der Quellcode-Beispiele im Realtime Linux Wiki erfolgen. Der beschriebene zyklische Echtzeit-Thread sollte zur Synchronisierung immer die CLOCK_TAI als Uhr verwenden. Vor Beginn des Echtzeit-Zyklus muss die Anwendung gegebenenfalls mittels clock_nanosleep() auf den Start des Schedules (bei TAPRIO) warten. Für die Anwendung mit einem Netzwerk-Schedule kann die Anwendung einen normalen Socket mit gewünschtem Typ öffnen und anschließend die Socket-Priorität entsprechend der Qdisc-Konfiguration anpassen (Listing 8).
Um das zeitgesteuerte Senden zu nutzen, gilt es für den Socket die Option ‚SO_TXTIME‘ zu aktivieren. Dabei ist mit jedem Datenpaket ein vorher berechneter Sendezeitpunkt zu übergeben. Bei der Berechnung des Sendezeitpunktes aus dem aktuellen Zeitpunkt ist eine gewisse Zeitspanne für die Ausführung des Linux- Netzwerkstacks zu berücksichtigen. Auch hier sollte CLOCK_TAI als Uhr verwendet werden. Ebenfalls sind die Ausführungsprioritäten des Linux-Netzwerkstacks und der Echtzeit-Anwendung entsprechend anzupassen (Listing 9).
Eine Basis für viele Anwendungen
Mit den beschriebenen Werkzeugen lassen sich bereits heute einfache, verteilte Echtzeit-Anwendungen auf Basis von TSN realisieren. Wie genau diese aussehen, ist sehr applikationsspezifisch und muss im Einzelfall entschieden werden. Ein komplettes Beispiel, welches mehrere Endpunkte enthält und ein typisches Automatisierungsszenario abbildet, findet sich auf den Webseiten des Projekts ‚AccessTSN‘ [2].
Quellenhinweise:
[1] https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/start
[2] https://www.accesstsn.com/
[3] https://tsn.readthedocs.io/
[4] https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/start
[5] https://github.com/open62541/open62541/tree/master/examples/pubsub_realtime























