Die Echtzeit- und Multitasking-Programmiersprache PEARL
Der Name PEARL steht für Process and Experiment Automation Realtime Language und darf nicht mit Perl (Practical Extraction and Report Language) verwechselt werden.
PEARL ist eine höhere Programmiersprache, die eine komfortable, sichere und weitgehend rechnerunabhängige Programmierung von Multitasking- und Echtzeit-Aufgaben erlaubt, und wurde seit 1977 in verschiedenen Entwicklungsstufen genormt, zuletzt 1998 als PEARL-90 (DIN 66253-2 1998, Berlin, Beuth-Verlag, 1998).
Wichtiger Grundsatz bei der Entwicklung von PEARL war, neben einer möglichst leichten Abbildbarkeit der prozessrechentechnischen Probleme, die einfache Erlernbarkeit für den Programmierer. Jeder, der schon eine prozedurale Programmiersprache kennt, wird sich in sehr kurzer Zeit mit PEARL anfreunden können.
Alle grundlegenden Datentypen und Sprachstrukturen anderer prozeduraler Programmiersprachen sind in PEARL vorhanden (>). PEARL bietet darüber hinaus komfortable Sprachelemente zur Bearbeitung von Multitasking- und Echtzeitaufgaben (>).
Datentypen:
- Grunddatentypen
- Festkomma, Gleitkomma
- Zeichen, Zeichenketten
- Bitvariablen, Bitketten
- Zusammengesetzte Datentypen
- Strukturen
- Felder beliebiger Dimension mit vorgebbaren Unter- und Obergrenzen bei den einzelnen Dimensionen.
- Funktionen, Prozeduren (Parameterübergabe per Value, per IDENT und als Zeiger)
- typbehaftete Zeiger auf alle Objekte, auch auf Funktionen und Prozeduren
- Typfreie Zeiger und Type-Casting
Blockstruktur, Gültigkeit von Objekten:
- Objektdeklarationen innerhalb eines
BEGIN
-END
-Blockes - Prozedur- und funktionsweite Objekte
- Modulweite Objekte
- Zugriff auf Objekte anderer Module durch globale Spezifikation von Daten
- Bekanntmachung von Objekten für andere Module durch globale Deklaration
Kontrollstrukturen:
- Bedingte Anweisungen
- IF-THEN-ELSE-FIN
- CASE-ALT-...-ALT-OUT-FIN
- Wiederholungen
- FOR-REPEAT-END
- WHILE-REPEAT-END
Genaue Informationen über den Aufbau von PEARL sind in
- DIN 66253-2 1998, Berlin, Beuth-Verlag, 1998
- PEARL 90 - Language Report, Version 2.2., GI-Fachgruppe 4.4.2, Bonn, GI Gesellschaft für Informatik e.V., 1998
enthalten.
Bessere Hardwareunabhängigkeit
Um eine Entkopplung von hardwareabhängigen Komponenten, wie z.B. Ein-/ und Ausgabeschnittstellen, zu dem hardwareunabhängigen Programm zu erreichen, wird ein PEARL-Modul in zwei Sektionen unterteilt:
- Im sogenannten Systemteil, der ab dem Schlüsselwort SYSTEM beginnt, werden die Namen für die hardwareabhängigen I/O-Schnittstellen bekannt gemacht, und ihre Eigenschaften definiert. Auch Interruptquellen sind hier zu definieren.
- Der sogenannte Problemteil, welcher mit PROBLEM beginnt, enthält beispielsweise Variablen, Konstanten, Tasks und Prozeduren. Tasks und Prozeduren können auf die im SYSTEM-Teil definierten Schnittstellen zugreifen.
Dem Betriebssystem bekannte Tasks können aus einem PEARL-Programm beliebig in ihrem Zustand verändert werden.
- ACTIVATE Taskname
Startet die Task mit dem Namen Taskname sofort. - TERMINATE Taskname
Die Task mit dem Namen Taskname wird abgebrochen. - SUSPEND Taskname
Die Task mit dem Namen Taskname wird angehalten. - CONTINUE Taskname
Die angehaltene Task mit dem Namen Taskname wird fortgesetzt. - PREVENT Taskname
Die Task mit dem Namen Taskname, deren Aktivierung mit einem Ereignis gekoppelt wurde (siehe (>) Beispiele), wird wieder ausgeplant. Sie wird also nicht mehr von diesem Ereignis aktiviert bzw. fortgesetzt.
Einplanung auf Ereignisse und Zeitpunkte
Die Aktivierung und die Fortsetzung von Tasks lässt sich auch an externe Ereignisse oder Zeitpunkte bedingt ausführen. Einplanungen zu einer festen Uhrzeit, aber auch Einplanungen beim Auftreten äußerer Ereignisse (Interrupts), sind möglich.
- ALL 0.00005 SEC ACTIVATE Highspeedregler;
Zyklische Aktivierung eines Reglers mit einer Frequenz von 20 kHz - AT 12:00 ALL 4 SEC UNTIL 12:30 ACTIVATE Mittagspause PRIO 1;
Zyklische Einplanung, alle 4 Sekunden zwischen 12:00 Uhr und 12:30 mit einer hohen Priorität - WHEN Feuer ACTIVATE Loesch;
Aktivierung der Task Loesch, wenn Interrupt Feuer eintrifft.
Eine Synchronisation von Tasks ist immer dann notwendig, wenn diese Daten gemeinsam nutzen.
Die drei folgenden Beispiele zeigen, wie sich mit Hilfe von Semaphor- (>)
und Boltvariablen (>) Tasks synchronisieren lassen.
Beabsichtigt eine Task A den Zugriff auf einen Datensatz, der gerade von einer TASK B bearbeitet wird,
so blockiert das Betriebssystem die Task A solange, bis B die Daten freigibt. Bei Semaphoren erlaubt PEARL
an Stelle einer Blockade auch das Testen mit Eintritt bei Freiheit (Schlüsselwort TRY
in Beispiel (>)).
Kritische Pfade entstehen, wenn 2 Tasks gleichzeitig auf gemeinsame Objekte zugreifen wollen. Mit Hilfe von Semaphor-Variablen (>) kann ein begonnener Zugriff ungehindert beendet werden, ohne von anderen Tasks unterbrochen zu werden, die ebenfalls zugreifen wollen.
PROBLEM; DCL A CHAR(255); ! String: 255 Bytes DCL SEMVAR SEMA PRESET(1); ! Init: Zugriff erlaubt DCL FAILED FIXED INIT(0); T1: TASK; DCL B1 CHAR(255); REQUEST SEMVAR; A=B1; RELEASE SEMVAR; END; T2: TASK; DCL B2 CHAR(255); REQUEST SEMVAR; A=B2; RELEASE SEMVAR; END; TEST: TASK; IF TRY SEMVAR THEN RELEASE SEMVAR; !Semaphor wieder freigeben ELSE FAILED=FAILED+1; FIN; ! Selbstaktivierung nach 10 Sekunden AFTER 10 SEC ACTIVATE TEST; END;
Produziert eine Task A Daten, die zur Weiterverarbeitung durch eine oder mehrere andere Tasks vorgesehen sind, so spricht man von einem Producer-Consumer-Schema. Hier ein Beispiel eines Ringspeichers mit 1024 Zeichen, von denen die lesende Task immer 2 gleichzeitig braucht.
PROBLEM; DCL PLATZDA SEMA PRESET(1024); ! Init: 1024 frei DCL ZEICHENDA SEMA PRESET(0); ! Init: Am Anfang leer HOLDATEN: TASK; REQUEST ZEICHENDA; REQUEST ZEICHENDA; HOLE_ZWEI_ZEICHEN_AUS_DEM_RINGPUFFER; RELEASE PLATZDA; RELEASE PLATZDA; END; SCHREIBDATEN: TASK; REQUEST PLATZDA; SCHREIB_EIN_ZEICHEN_IN_RINGPUFFER; RELEASE ZEICHENDA; END;
Producer-Consumer-Schemata entstehen auch bei der Messwerterfassung und der Automatisierung von Fertigungsprozessen.
Möchte der Programmierer erlauben, dass mehrere Tasks gleichzeitig einen Datensatz lesen dürfen
(im Beispiel die Tasks LESER1
und LESER2
), aber jeweils nur eine schreiben kann,
und dies auch nur, falls keine andere liest, sind BOLT-Variablen das Mittel der Wahl.
PROBLEM; DCL A CHAR(255); ! String: 255 Bytes DCL BOLTVAR BOLT; ! Default: frei LESER1: TASK; DCL L1 CHAR(255); ! ENTER erlaubt, dass alle weiteren Tasks mit ENTER ! ebenfalls weiterlaufen koennen ENTER BOLTVAR; L1=A; LEAVE BOLTVAR; END; LESER2: TASK; DCL L2 CHAR(255); ENTER BOLTVAR; L2=A; LEAVE BOLTVAR; END; SCHREIBER1: TASK; DCL S1 CHAR(255); ! Nach einem RESERVE laeuft eine Task nur weiter, ! wenn kein anderer Leser oder Schreiber einen ! ENTER/RESERVE durchgefuehrt hat. RESERVE BOLTVAR; A=S1; FREE BOLTVAR; END; SCHREIBER2: TASK; DCL S2 CHAR(255); RESERVE BOLTVAR; A=S2; FREE BOLTVAR; END;
Ein- und Ausgabe
PEARL sieht für die verschiedenen Ein- und Ausgabeformen jeweils eigene Schlüsselworte vor.
In den folgenden Beispielen steht eine alphic_dation
z.B. für eine Festplatte,
Terminal, LCD-Display und serielle oder parallele Schnittstelle, eine basic_dation
z.B. für eine analoge oder digitale Prozeß-Ein-/Ausgabe.
- Ausgabe
- Formatierte Ausgabe
PUT objekt1, ... TO alphic_dation BY formatliste;
- Binäre Ausgabe
WRITE objekt1, ... TO alphic_dation;
- Ausgabe an Prozeßperipherie
SEND objekt1, ... TO basic_dation;
- Formatierte Ausgabe
- Eingabe
- Formatiertes Einlesen
GET objekt1, ... FROM alphic_dation BY formatliste;
- Binäres Einlesen
READ objekt1, ... FROM alphic_dation;
- Einlesen aus der Prozessperipherie
TAKE objekt1, ... FROM basic_dation;
- Formatiertes Einlesen
Die Größenangaben in einzelnen Formatanweisungen können neben Konstanten auch durch Variablen und Funktionsaufrufe festgelegt werden und müssen nicht schon zur Compilezeit bekannt sein.
Spezielle Datentypen
- CLOCK, DURATION
Diese beiden Datentypen beschreiben Zeitpunkte und -räume. Bei den zeitlichen Einplanungen treten diese Datentypen ebenfalls auf. In den angegebenen Beispielen (>) ist12:00
eine Konstante vom TypCLOCK
und0.00005 SEC
eine Konstante vom TypDURATION
. Auch Berechnungen zwischen diesen Datentypen sind möglich, wie die beiden folgenden Beispiele zeigen.-
durationvar=clockvar-clockvar
-
clockvar=clockvar+durationvar
-
- INTERRUPT
Diese Datentypen müssen im Systemteil eines Modules mit einem Hardware- Interrupteingang verbunden werden. Im Problemteil können sie dann mit den oben beschriebenen Mitteln (>) (WHEN ..) zum Ändern eines Taskzustandes verwendet werden. - SEMA
Semaphor-Variablen dienen zur Synchronisation zwischen verschiedenen Tasks (>). Hauptsächlich dienen sie zum Schutz von Verbunddatenobjekten, die von mehreren Tasks gleichzeitig genutzt werden. Benutzt bei kritischen Pfaden (>) oder Producer-Consumer-Schemata (>). Eine Semaphor-Nachbildung lässt sich ohne Betriebssystem-Unterstützung nicht erreichen, da die gewöhnlichen Hochsprach-Operationen belegen, ggf. blockieren bzw. freigeben oder blockierte Task fortsetzen in einem Multitasking-System nicht unteilbar sind. - BOLT
Boltvariablen erlauben im Gegensatz zu Semaphoren einen gleichzeitigen Lesezugriff auf Daten von mehreren Tasks und blockieren schreibende Tasks, wenn eine andere einen Lese- oder Schreibzugriff begonnen hat (>). Bezüglich der Nachbildung von diesen Variablen ohne Betriebssystemunterstützung gelten die Ausführung zu Semaphoren (>) entsprechend.
Basierend auf den ehemaligen Webseiten des IRT Hannover unter Prof. Gerth
Autoren: Thomas Probol, Stefan Eilers, Thorsten Lilge