% Content-encoding: UTF-8 \documentclass[ngerman]{article} \usepackage[T1]{fontenc} \usepackage[latin1]{inputenc} \setcounter{secnumdepth}{0} \setcounter{tocdepth}{0} \newcommand{\qed}{\hfill \mbox{\raggedright \rule{.07in}{.07in}}} \begin{document} \title{T4 — ein Embedded–Forth–Experiment}[T4 Embedded–Forth–Experiment] \author{Jörg Völker} \maketitle Seit nunmehr fast 6 Jahren bringt die tematik GmbH unter dem Markennamen \emph{Servonaut} (\url{www.servonaut.de}) Module für den Truckmodellbau bzw. Funktionsmodellbau im Maßstab 1:16 bis 1:8 auf den Markt. Die Fahrtregler, Lichtanlagen und Truck--Soundmodule sind bis auf wenige Ausnahmen in Forth programmiert. \begin{multicols}{2} Unser erstes Modellbau--Produkt, eine Kombination aus Fahrtregler, Lichtanlage und einem kleinen Bordcomputer, wurde 2001 noch auf Basis eines 68HC11 und unter Holon11 entwickelt. Nach dem Erfolg dieses ersten Moduls musste dann allerdings leider gleich ein Wechsel der Prozessorfamilie stattfinden, denn der 68HC11 wird von Freescale (ehemals Motorola) nicht weitergeführt und ist für einfache Anwendungen zu teuer. Schade — Holon11 hatte sich bestens bewährt. Die Wahl fiel auf die kleinere 68HC08--Familie. Zwar ist dieser Prozessor etwas einfacher aufgebaut als ein 68HC11, dafür sind zahllose auch sehr kleine Varianten mit Flash--Speicher und leistungsstarker Peripherie zu fairen Preisen verfügbar. Alle Typen sind zudem in--circuit--programmierbar, und durch die höheren Taktfrequenzen im Vergleich zum 68HC11 wird der weniger leistungsfähige Befehlssatz mehr als kompensiert. Für den 68HC08 konnten wir allerdings weit und breit nur ein Forth--System finden, und zwar das nanoFORTH von Rafael Deliano. Da diese Forth--Variante auf dem Zielsystem selbst läuft, benötigt sie jedoch für unsere Zwecke zu viel Speicher und ist auf Chips mit weniger als 24kByte nicht sinnvoll einzusetzen. In dieser Not und unter Zeitdruck wurde deshalb zunächst ein Zwischenschritt unternommen und lediglich ein Forth--Kern und ein innerer Interpreter für Direct--Threaded--Code erstellt. Die Programmierung erfolgte dann jedoch weiterhin in Assembler, die Forth--Programmteile wurden in Form von Konstanten--Tabellen eingegeben — ein recht mühsames und fehlerträchtiges Unterfangen. Immerhin konnten so bewährte Programmteile von Holon11 übernommen werden. \setcounter{figure}{1} \begin{figure*}[t] \begin{center} \includegraphics[width=0.8\textwidth]{2007-02/T4-ScreenShot1}\\ \caption{\label{T4-ScreenShot1}T4-Oberfläche orientiert sich entfernt an Holon11.} \end{center} \end{figure*} Auf die Dauer konnte dies kein befriedigender Zustand sein. Aber vor dem letzten Schritt hin zu einem eigenen kompletten Forth--System wurde lange gezögert. Da ich noch nie ein vollständiges Forth implementiert hatte, war der Aufwand schwer abzuschätzen. Es geht dabei schließlich um kommerzielle Produktentwicklung. Zeit ist hier nun einmal Geld. Ich hatte offen gesagt große Sorge, mich zu verzetteln. Nach jahrelanger Software--Entwicklung für Industrie--Waagen in C stand ein Umstieg auf ein kommerzielles C für den 68HC08 als Alternative allerdings auch nie zur Debatte. \end{multicols} \vfill \setcounter{figure}{0} \begin{figure} \begin{center} \includegraphics[width=0.7\textwidth]{2007-02/T4-UniWellhausen2}\\ \caption{\label{T4-UniWellhausen2}Mit T4 programmierter Modellbau-Truck} \end{center} \end{figure} \setcounter{figure}{2} \vfill \begin{multicols}{2} Wie kann man nun aber bei der Implementierung von Forth das Kosten/Nutzen--Verhältnis optimieren, wie kommt man mit wenig Aufwand zu einem einigermaßen komfortablen System? Alle Vorgehensweisen haben ihre Vor-- und Nachteile. Wie man ein Stand--Alone--Forth auf einem Zielsystem erstellt ist relativ gut dokumentiert, und es gibt viele Vorlagen, an denen man sich orientieren kann. Außerdem entspricht das Ergebnis noch am ehesten einem klassischen Forth. Leider ist der Speicherbedarf hoch. Auch der Code, der nur zur Übersetzungszeit benötigt wird, (und das Dictionary) belegt Speicherplatz im Zielsystem. Der Komfort bei der Entwicklung hält sich in Grenzen, da ein Embedded System oft keinen Massenspeicher besitzt und nur über das Nadelöhr einer seriellen Schnittstelle und einen PC auf Quellen zugegriffen werden kann. Ein typischer Cross--Compiler dagegen, der vollständig auf einem PC als Entwicklungsrechner läuft, könnte einigen Luxus heute üblicher integrierter Oberflächen bieten. Nur eben zwei entscheidende Forth--Eigenarten nicht: Die Erweiterbarkeit der Sprache und das interaktive Entwickeln und Debuggen werden nicht unterstützt. Bleiben noch die \emph{großen} Lösungen, darunter die kommerziellen Systeme wie SwiftX von Forth,Inc., VFX Forth von MPE, und in gewisser Weise auch Holon11. Hier wird ein Cross--Compiler mit einem Forth auf dem Entwicklungsrechner kombiniert, und ein Interface zum Zielsystem erlaubt zudem interaktives Arbeiten. Optimal — nur leider nicht gerade an einem Wochenende implementiert. Allein die Notwendigkeit, mehrere Adressräume im PC zu verwalten, erhöht die Komplexität wesentlich, und fast alle Forth--Worte müssen zudem in zwei Versionen vorliegen, für den Host und für den jeweiligen Targetprozessor. Die ersten beiden Ansätze kommen auf Grund ihrer Einschränkungen für mich nicht in Frage, die dritte Variante ist zu aufwändig — Was also tun? Not macht zum Glück erfinderisch — so auch in diesem Fall. Schaut man sich einmal die Quellen für ein Stand--Alone--Forth, z.~B.\ ein altes FIG--Forth genauer an, dann fällt auf, das die längsten Worte, die gleichzeitig am meisten Speicher belegen und beim Übersetzen den größten Teil der Rechenzeit benötigen, natürlich mit dem interaktiven Interpreter, der Compilierung und dem Dictionary zu tun haben: Accept, Parse, Find, Create usw. Dazu kommt noch die Zahlenkonvertierung mit Number, \verb|<#|, \verb|#|, \verb|#>|, usw. Diese Worte werden in einem Embedded--System (typischerweise) wenn überhaupt nur in der Entwicklungsphase benötigt. Die Grundidee besteht nun darin, nur diese Worte zusammen mit dem Dictionary auf einen Entwicklungsrecher auszulagern — den eigentlichen Interpreter-- und Compilerkern aber im Zielsystem zu belassen. Die Übersetzung wird also durch das Zielsystem gesteuert, der Entwicklungsrechner ist nur ein Slave, eine Art intelligentes Terminal mit Forth--spezifischen Zusatzfunktionen. Dieser Ansatz ist ein Kompromiss. Es werden zwar nicht alle nur für die Übersetzung erforderlichen Worte aus dem Zielsystem entfernt, aber der weitaus überwiegende Teil. Der Charakter eines traditionellen interaktiven Forth--Systems bleibt vollständig erhalten. Die Komplexität ist dabei nicht nennenswert höher wie bei einem Stand--Alone Forth. Da der PC sowohl das Dictionary als auch alle Quellen verwalten kann, sind komfortable Hilfe-- und Suchfunktionen einfach umzusetzen. \begin{figure*}[t] \begin{center} \includegraphics[width=0.8\textwidth]{2007-02/T4-ScreenShot2}\\ \caption{\label{T4-ScreenShot2}Ein einfaches Demo wird übersetzt und ausgeführt.} \end{center} \end{figure*} Das Projekt wurde nach einigem Zögern dann im Sommer 2005 unter dem Namen \emph{T4} in Angriff genommen. Für die PC--Software fiel die Wahl auf Delphi, einfach weil bei uns Erfahrung mit diesem System vorhanden ist. Die Oberfläche orientiert sich entfernt an Holon11 und ist aufgeteilt in vier fest zugeordnete Bereiche. Je ein großes Fenster stehen für den Editor und das Terminal, d.h. die direkte Verbindung zum Forth--Interpreter im Zielsystem zur Verfügung. Zwei kleine Fenster dienen der Navigation in den Quelldateien und dem direkten Einblick in das Dictionary. Um schnell voranzukommen, wurden wo immer möglich fertige Delphi--Komponenten eingesetzt, auf Kosten der Effizienz und Geschwindigkeit. Da Speicherplatz im PC im Überfluss vorhanden ist, können im Dictionary beliebige Zusatzinformationen abgelegt werden. T4 merkt sich für jedes neu definierte Wort die Quelldatei und Zeilennummer, außerdem wird die vollständige Zeile, in der die Definition erfolgt ist, mit abgespeichert. Ist die Quelle entsprechend aufgebaut, hat jeder Dictionary--Eintrag somit automatisch auch einen hilfreichen Kommentar, z.~B.\ in der Form \verb|rot (n1 n2 n3 -- n2 n3 n1)| Ein Doppelklick auf ein Wort im Editor holt den entsprechenden Eintrag in das Dictionary--Fenster — ein Doppelklick in das Dictionary lädt wiederum den Quelltext eines Worts direkt in den Editor. Auf dem Zielsystem wird ein Forth--Kernel benötigt, der notwendigerweise in Assembler erstellt werden muss. Der Kernel umfasst zunächst einmal alle grundlegenden Worte. Compiler und Interpreter sind weitgehend bereits in Forth erstellt, allerdings notgedrungen im Kernel noch von Hand codiert. Viele Worte des Compilers, wie if, else, then usw., können dagegen schon aus einer Forth--Quelle nachgeladen werden. Die Kommunikation zwischen PC und dem Zielsystem erfolgt seriell, dabei hat die Schnittstelle eine Doppelfunktion. Neben der ganz normalen ASCII--Ein-- und --Ausgabe bei einem laufenden Anwender--Programm werden vom Interpreter und Compiler dagegen ASCII--Steuerzeichen übertragen. Der eigentliche Ablauf ist verblüffend einfach, ein Beispiel: Im Zielsystem ist Accept wie folgt definiert: \begin{verbatim} : accept ( -- u ) 12 emit key ; \end{verbatim} Das Steuerzeichen 12 startet im PC die eigentliche Accept--Funktionalität. Es wird eine Zeile gelesen und im TIB, ebenfalls im PC, abgelegt. Danach sendet der PC die Anzahl der gelesenen Zeichen gleichsam als Quittung an das Zielsystem zurück. Der Ablauf ist immer gleich: Das Zielsystem sendet ein Kommando, der PC antwortet mit bis zu drei Byte. Ein zusätzliches Handshake ist so nicht erforderlich. An die Schnittstelle werden minimale Anforderungen gestellt, noch nicht einmal ein Buffer (im Target) ist erforderlich. Die Verarbeitung im Zielsystem ist nämlich normalerweise schnell genug, um beim Empfang der Daten mit der Übertragungszeit zweier Stopp--Bits auszukommen. Die Schnittstelle kann so notfalls auch vollständig in Software erstellt werden, ohne Timer, Interrupts oder UART--Hardware zu benötigen oder zu blockieren. Bei unseren Modulen werden in der Entwicklungsphase Port--Pins zur Kommunikation genutzt, die später dann die Funktion von Konfigurations--Jumpern übernehmen. \begin{figure*}[t] \hfil \begin{minipage}[b]{0.45\textwidth} \begin{center} \includegraphics[width=0.8\textwidth]{2007-02/T4-K30}\\ \caption{\label{T4-K30}Truck-Fahrtregler mit Lichtanlage, noch in bedrahteter Technik mit dem 68HC11} \end{center} \end{minipage}\hfil \begin{minipage}[b]{0.45\textwidth} \begin{center} \includegraphics[width=0.8\textwidth]{2007-02/T4-M20Plus}\\ \caption{\label{T4-M20plus}Aktueller 20A Fahrtregler für Funktionsmodelle mit Lichtanlage auf der Basis eines 68HC08} \end{center} \end{minipage} \hfil \end{figure*} Das Konzept von T4 ist von der Art der Implementierung des Forth auf dem Target zunächst einmal unabhängig. Welche Implementierung \emph{optimal} ist, hängt völlig vom Prozessor und den Randbedingungen ab, pauschale Aussagen kann man da meiner Meinung nach nicht machen. Für unsere Anwendungen soll möglichst viel Funktion in möglichst kleinen (preiswerten) Chips untergebracht werden. Die Rechenleistung ist dabei eher zweitrangig. Immerhin hat ein 68HC08 je nach Taktfrequenz schon den vier-- bis achtfachen Durchsatz eines Commodore C64, für viele Anwendungen ist das völlig ausreichend. Und zeitkritische Funktionen sowie Interrupt--Services werden eh bevorzugt in Assembler erstellt. Die Wahl fiel nach einigen Voruntersuchungen auf eine Kombination von direkt--threaded und token--threaded Forth. Der innere Interpreter kann bis zu 128 7--Bit Token erkennen und einen Adressraum bis zu 32 kByte über 15--Bit Adresstoken abdecken. Ein Bit unterscheidet die beiden Fälle. Die in Assembler codierten Worte werden dabei grundsätzlich mit Byte--Token angesprochen, die über Forth compilierten Worte dagegen mit 15--Bit--Adressen. Einige weitere Optimierungen führen dazu, dass z.~B.\ das Wort \verb|2DUP| in nur vier Byte codiert werden kann: \begin{verbatim} $FD jsr ,x $44 dc.b over $44 dc.b over $4B dc.b next \end{verbatim} \medskip Der Einsatz eines \emph{klassischen} inneren Interpreters hat zudem den Vorteil, das sich auch ein Single--Step Debugging schön einfach implementieren läßt. Für ein Multitasking wird ebenfalls das typische kooperative Konzept mit \verb|pause| angewendet, bei unseren Modellbau--Projekten werden meistens zwei bis vier Tasks eingesetzt. \verb|Create|~--~\verb|Does>| darf natürlich auch nicht fehlen. Da T4 aber direkt in einen Flash--Speicher kompiliert, wurde aus \emph{technischen} Gründen auf das ältere \verb|| Konstrukt zurückgegriffen. Die Übersetzung in den Flash--Speicher hat zusammen mit einem Marker--Mechanismus (ähnlich Forget) den unschätzbaren Vorteil, dass nach einem Absturz des Zielsystems in der Entwicklungsphase oft nur einige Sekunden vergehen, bis nach dem Reset die Arbeit (und das heißt in diesem Fall die Fehlersuche) fortgesetzt werden kann. \vspace*{2em} ~ \end{multicols} \vfill \begin{figure} \begin{center} \begin{quotation} \listinginput[1]{1}{2007-02/T4-codefragment1.fs} \end{quotation} \caption{\label{T4-codefragment1}\texttt{swap} ist in Assembler codiert. \texttt{rot} belegt in handcodiertem Forth bereits nur noch 6 Byte.} \end{center} \end{figure} \begin{multicols}{2} Verzichtet man auf Zahlenkonvertierung (im Target) und doppelt--genaue Arithmetik so benötigt ein Minimal--System mit Multitasking nur etwas über 2kByte Speicher. Der äußere Interpreter belegt davon ca.\ 230 Byte, der Compiler rund 260 Byte und die nötigen Flash--Routinen etwa 220 Byte. Single--Step und ein einfaches Dump belegen nochmals ca.\ 170 Byte. Zusammen sind das weniger als 1kByte, die nur in der Enwicklungsphase benötigt werden und später \emph{nutzlos} in der Seriensoftware verbleiben. Bereits ab 4k Flash ist so ein sinnvoller Einsatz möglich, mit Platz für bis zu 1000 Forth--Token für die Applikation. Die Schnittstelle kann noch auf Halb--Duplex minimiert werden, dann ist der kleinste sinnvoll einsetzbare Microcontroller ein 4k--Chip z.~B.\ ein 68HC908QT4 im DIL8-- oder SO8--Gehäuse mit noch 5 freien Ein--/Ausgängen (Zwei werden für die Versorgungsleitung benötigt, ein weiterer Pin für die Kommunikation mit T4 in der Entwicklungsphase.) T4 wurde, kaum dass es einigermaßen einsetzbar war, sofort für einem Software--Redesign eines Fahrtreglers mit Lichtanlage verwendet. Der Funktionsumfang der Baugruppe konnte ein gutes Stück erweitert werden, denn durch den nun sehr kompakten Code wurde wieder Speicherplatz im Controller frei. Bis heute wurden fünf Projekte unterschiedlicher Komplexität mit T4 realisiert, und das System soll auch für zukünftige Entwicklungen, soweit irgendwie möglich, zum Einsatz kommen. Leider gab es bisher keine Zeit, den PC--Teil von T4 weiter zu optimieren. Es gibt noch einige Mängel, allem voran in der Fehlerbehandlung, die nur rudimentär existiert. Die Arbeit an einer AVR--Variante wurde bereits aufgenommen, ist aber noch nicht abgeschlossen. Fazit: Mit dem beschriebenen Ansatz kann ein Forth für die Cross--Softwareentwicklung vergleichsweise einfach und in kurzer Zeit implementiert werden. Alle wesentlichen Merkmale von Forth, wie interaktives Arbeiten und Debuggen, inkrementelles Compilieren und die Erweiterbarkeit der Sprache, bleiben erhalten. Zusätzlich kann einiges an Komfort in der Entwicklungsumgebung realisiert werden. Die Software im PC ist weitgehend unabhängig vom Zielprozessor. In der Praxis hat sich T4 gut bewährt und die Entwicklungszeiten deutlich reduziert. Das Konzept eignet sich allerdings nicht für alle Prozessor--Typen und Anwendungen gleich gut. Ein Flash--Speicher mit geringer Blockgröße und die Fähigkeit des Prozessors, auf den eigenen Code--Speicher zuzugreifen, sind Voraussetzung. Im Forth--Sinn nicht optimal gelöst ist die Entwicklung auf Assembler--Ebene und die geringe Flexibilität des Dictionaries, dessen Struktur nur im PC selbst geändert oder erweitert werden kann. Die Interaktion zwischen PC und Zielsystem setzt außerdem der Übersetzungsgeschwindigkeit Grenzen. Der fertige Code für die Serie muss zudem mit einem zusätzlichen Tool aus dem Target kopiert werden. \qed \end{multicols} \vfill \begin{figure} \begin{center} \includegraphics[width=0.7\textwidth]{2007-02/T4-MBS.eps}\\ \caption{\label{T4-MBS}Die Servonaut--Werbe--Trucks --- Forth driven} \end{center} \end{figure} \end{document}