Oberon-System auf einem erschwinglichen FPGA-Board implementiert

Originalautor: Niklaus Wirth
von Niklaus Wirth
Professor (im Ruhestand)
Eidgenössische Technische Hochschule (ETH)
Zürich, Schweiz

1988 vervollständigten und veröffentlichten Jürgen Gutknecht und ich die Oberon-Programmiersprache [1, 2], die der Nachfolger von zwei anderen Sprachen war, Pascal und Modula-2, die ich zuvor entwickelt hatte. Die Oberon-Sprache wurde ursprünglich von uns als rationaler und effektiver als Modula-2 konzipiert, wodurch es für Studenten des akademischen Bildungssystems einfacher wurde, Informatik zu beherrschen. Ohne an dem Erreichten festzuhalten, bauten wir 1990 ein modernes Betriebssystem (OS) für Workstations, das Windows und Textverarbeitungsfunktionen verwendete. Dann haben wir ein Buch veröffentlicht, das die Details sowohl des Oberon-Compilers als auch des Betriebssystems mit demselben Namen enthüllt. Das Buch mit dem Titel "Project Oberon" enthielt den Quellcode des Systems.

Einige Jahre später schlug mein Freund Paul Reed vor, einen Nachdruck des Buches zu veröffentlichen, da es für das Studium der Systemarchitektur von Bedeutung ist und einen guten Ausgangspunkt für diejenigen darstellt, die zuverlässige Systeme von Grund auf neu erstellen möchten.

Es ergab sich jedoch ein ernstes Hindernis. Der ursprüngliche Compiler wurde für einen Prozessor erstellt, der bereits vom Markt verschwunden ist. Also habe ich beschlossen, den Compiler für den modernen Prozessor neu zu schreiben. Im Laufe einiger Recherchen konnte ich jedoch keinen Prozessor finden, der meine Kriterien in Bezug auf Klarheit, Regelmäßigkeit und Einfachheit vollständig erfüllt. Also musste ich meinen eigenen Prozessor entwerfen. Diese Idee wurde dank des modernen PPVM-Chips (User-Programmable Gate Array, FPGA) verwirklicht, mit dem ich Hardware wie ein Softwaresystem erstellen konnte. Darüber hinaus gab mir die Wahl von Xilinx® FPGA die Möglichkeit, das System neu zu gestalten, ohne viel vom ursprünglichen Projekt von 1990 abzuweichen.

Der neue RISC-Prozessor ist auf der kostengünstigen Digilent Spartan®-3-Karte mit einem statischen Einzel-Megabyte-Speichermodul (SRAM) implementiert. Alle meine Hardware-Verbesserungen betrafen die Mausschnittstelle und die SD-Karte, die die Festplatte im alten System ersetzt.

Das Buch und der Quellcode des gesamten Systems sind auf der projectoberon.com- Website verfügbar [3,4,5]. Dort steht auch eine separate S3RISCinstall.zip- Datei zur Verfügung . Es enthält Anweisungen, ein Image des Dateisystems für die SD-Karte und Bit-Konfigurationsdateien für das FPGA (in Form von PROM-Dateien für den Flash-Speicher der Spartan-3-Karte), Designteile für die Hardwareschnittstelle der SD-Karte und der Maus.

RISC-Prozessor

Der Prozessor wird durch das RISC5-Verilog-Modul dargestellt und besteht aus einer Recheneinheit (ALU), einem Array von 16 32-Bit-Registern und einem Steuermodul mit einem IR-Befehlsregister und einem PC-Programmzähler (siehe Details - ca. übersetzt).

Der Prozessor verarbeitet 20 Befehle: vier zum Bewegen, Verschieben und Drehen; vier für logische Operationen; vier für ganzzahlige Arithmetik; vier für Gleitkommaberechnungen; zwei für den Speicherzugriff und zwei für die Verzweigung.

RISC5 wird aus dem RISC5Top-Kontext importiert, der Schnittstellen für verschiedene Geräte enthält, die dem Speicher und dem SRAM zugeordnet sind (256 MB x 32 Bit). Das ganze System (Abb. 1)
Abb. 1. Systemdiagramm mit Verilog-Modulen.

enthält die folgenden Verilog-Module (die Anzahl der Zeilen jedes Moduls wird angezeigt):
RISC5TopUmwelt194
Risc5Prozessor201
Multiplikatorganzzahlige Arithmetik 47
Teiler24
FPAdderGleitkomma-Arithmetik98
FPMultiplier33
FPDivider35
SPISD-Karte und Sender / Empfänger25
Vid1024 x 768 Videocontroller73
PS2Tastatur25
MausMaus95
RS232TRS232 Sender23
RS232RRS232 Empfänger25

Ich habe ein Schwarzweiß-VGA-Display in den Speicher gemappt, sodass es 1024 x 768 x 1 Bit pro Pixel = 98.304 Byte belegt, im Wesentlichen 10 Prozent des gesamten verfügbaren Speichers in 1 Megabyte. Der Zugriff auf die SD-Karte, die 80 Megabyte vom ursprünglichen System ersetzt hat, erfolgt über die Standard-SPI-Schnittstelle, die Bytes oder 32-Bit-Wörter akzeptiert und serialisiert. Die Tastatur und die Maus werden über die standardmäßige serielle PS-2-Schnittstelle angeschlossen. Darüber hinaus gibt es serielle asynchrone RS-232-Kabel und eine 8-Bit-Parallel-E / A-Mehrzweckschnittstelle. Das RISC5Top-Modul enthält außerdem einen Millisekundenzähler.

Oberon Betriebssystem

Die Betriebssystemsoftware enthält einen Kernel, der einen Speicherzuordner mit einem Garbage Collector, ein Dateisystem mit einem Bootloader, ein Textsystem, ein Viewer-System und einen Texteditor enthält.

Ein Modul namens "Oberon" ist der zentrale Task-Manager und "System" ist das grundlegende Befehlsmodul. Die Aktion wird durch Klicken mit der mittleren Maustaste auf den Text des Formulars "MP" in einer beliebigen Anzeige aufgerufen, wobei P der Name der im Modul M deklarierten Prozedur ist. Wenn M nicht verfügbar ist, wird es automatisch geladen.
Die meisten Textbearbeitungsbefehle werden mit normalen Mausklicks aufgerufen, bei denen die linke Taste das Einfügemarke bedient, die aktuelle Position im Text markiert und die rechte Taste für die Hervorhebung des Texts zuständig ist.

Das Kernel-Modul umfasst die Datenträgerverwaltung und einen Garbage Collector.
Die Betrachter sind gekachelt und überlappen sich nicht. Das Standardlayout zeigt zwei vertikale Spuren mit einer unbegrenzten Anzahl von Zuschauern an. Sie können sie vergrößern oder verkleinern sowie in die Titelleiste ziehen. In Abb. Abbildung 2 zeigt die Benutzeroberfläche eines Monitors, der mit Tastatur und Maus an eine Spartan-3-Platine angeschlossen ist.


Abb. 2. Die Benutzeroberfläche auf dem Monitor und Spartan-3 unten rechts.

Ein geladenes System belegt 112.640 Bytes im modularen Speicher (21 Prozent) und 16.128 Bytes im Heap (3 Prozent). Es besteht aus den folgenden Modulen (mit der Anzahl der Zeilen), die in Abb. 3:
Kernel271 (innerer Kern)
Filedir352
Dateien505
Module (Lader)226
Zuschauer216 (äußerer Kern)
Texte532
Oberon411
MenuViewer208
Textrahmen874
System420
Bearbeiten233

Beachten Sie, dass das Laden des Systems beim Starten oder Neustarten nur 2 Sekunden dauert, einschließlich des Durchsuchens des Dateiverzeichnisses nach Garbage Collection.

Oberon-

Compiler Der Compiler ist in das System integriert und verwendet eine einfache rekursive Top-Down-Parsing-Methode. Die Aktivierung des Compilers erfolgt mit dem Befehl ORP.Compile @. Der Parser empfängt Zeichen von einem Scanner, der Kennungen, Zahlen und Sonderzeichen (wie BEGIN, END, + und andere) verarbeitet. Dieses Design hat seine Eignung und Eleganz in vielen Anwendungen bewiesen und ist in meinem Buch Compiler Construction [6,7] beschrieben.

Der Parser ruft die Prozeduren aus dem Codegenerierungsmodul auf, das dem Codearray direkt Anweisungen hinzufügt. Verzweigungsbefehle werden durch Sprungadressen am Ende der Kompilierung generiert, wenn alle Sprungpunkte bereits bekannt sind.

Alle variablen Adressen werden relativ zum Basisregister berechnet. Dies ist R14 (Stapelzeiger) für lokale Variablen (festgelegt beim Eingeben der Prozedur zur Laufzeit) oder R13 für globale und importierte Variablen. Basisadressen werden auf Anforderung aus der globalen Modultabelle geladen, deren Adresse im Register R12 gespeichert ist. Das Register R15 wird für Rücksprungadressen gemäß der RISC-Architektur verwendet. Die übrigen Register R0 - R11 stehen zur Auswertung von Ausdrücken und zur Übergabe von Verfahrensparametern zur Verfügung.

Der gesamte Compiler besteht aus vier relativ kleinen und effizienten Modulen (die Anzahl der Zeilen ist jeweils angegeben):
ORPParser968
ORG Code-Generator1120
ORBBasis def435
ORSScanner311

Der Compiler belegt 115.912 Bytes (22 Prozent) des modularen Speicherplatzes und 17.508 Bytes (4 Prozent) des Heapspeichers (vor der Kompilierung). Sein Quellcode ist ungefähr 65 Kilobyte groß. Das Kompilieren des Compilers selbst dauert auf einem 25-MHz-RISC-Prozessor nur wenige Sekunden [8].

Der Compiler generiert immer Überprüfungen für Array-Indizes und NIL-Zeiger. Dies erzeugt Traps (Trap, Trap - ca. Transl.) Im Falle eines Verstoßes. Diese Technik garantiert einen hohen Schutz vor Fehlern und Beschädigungen. Tatsächlich kann die Integrität des Systems nur durch die Verwendung von Operationen des SYSTEM-Pseudomoduls wie PUT und COPY verletzt werden. Diese Vorgänge sollten in begrenztem Umfang nur in den Gerätetreibermodulen verwendet werden und sind in der Importliste unter dem Namen SYSTEM leicht zu finden. Das gesamte System wird auf Oberon selbst programmiert, ohne dass Assembler-Codes verwendet werden müssen.

Ich habe mich für das Digilent Spartan-3-Board aufgrund seiner Erschwinglichkeit und Einfachheit entschieden, was es für Bildungseinrichtungen geeignet macht, die ganze Sets für Klassen erwerben. Der große Vorteil ist auch das Vorhandensein von statischem RAM auf der Karte, mit dem Sie eine direkte Verbindung (Schnittstelle) herstellen können (und sogar Bytes lesen können). Leider verwenden die neuesten Karten dynamisches RAM, das zwar umfangreicher, aber schwieriger anzuschließen ist, jedoch zusätzliche Schaltungen für die Aktualisierung und Initialisierung (Kalibrierung) erfordert. Eine solche Schaltung kann nicht weniger komplex sein als der gesamte Prozessor mit statischem RAM. Selbst wenn der Controller auf einem Chip ausgeliefert wird, verstößt er gegen unser Prinzip, dass alles für die Steuerung zugänglich sein sollte.

Gedanken am Ende

Vor mehr als 40 Jahren stellte C. Hoar fest, dass Studierende in allen Bereichen der Wissenschaft und Technologie von einer Vielzahl von Beispielen für ernsthafte Strukturen beeinflusst werden, bevor sie ihre eigenen experimentellen Erfahrungen sammeln. Programmier- und Designprogramme unterstreichen dieses Paradigma. Die Schüler müssen von Anfang an Programme schreiben, anstatt verschiedene Muster zu studieren.
Der Grund für diese schreckliche Situation war, dass es fast keine Literatur mit qualifizierten Beispielen gab. Aus diesem Grund habe ich mich entschlossen, die Situation zu korrigieren, und 1975 das Buch „Algorithmen und Datenstrukturen“ geschrieben. Zukünftig habe ich (zusammen mit J. Gutknecht) im Rahmen des Lehrgangs für Betriebssysteme das Oberon-System entworfen (1986–88).
Nach einer Weile wurde der Programmierunterricht nicht merklich verbessert, da die Systeme dramatisch kompliziert und größer wurden. Obwohl die Open Source erkannt wurde, konnte er die Situation nicht ändern, da die meisten Programme nur erstellt wurden, um sie so schnell wie möglich auszuführen, und nicht, um sie für eine Person besser zu verstehen.
Ich gehe weiterhin kühn davon aus, dass alle Programme nicht nur für den Computer, sondern auch für das menschliche Verständnis erstellt werden sollten. Sie müssen verfügbar sein. Diese Aufgabe ist viel komplizierter als das Erstellen ausführbarer Programme, selbst wenn diese korrekt und effizient sind. Dies impliziert, dass keine Assembler-Inserts vorhanden sein sollten.

Das Ergebnis des Ignorierens des menschlichen Faktors führt dazu, dass es nicht überall sehr sorgfältig gestaltete Anwendungen gibt, die durch Debugging in den Betriebszustand versetzt werden, was manchmal düstere Folgen hat. Um Verständlichkeit zu erreichen, lohnt es sich, sich an Einfachheit und Ordnung zu halten, unnötigen Schmuck abzulehnen und unter Vermeidung von Schnickschnack zwischen herkömmlichem und praktischem zu unterscheiden.

Die geringe Größe solcher Systeme zeigt, wie klein viel erreicht werden kann. Die Abmessungen des Oberon-Betriebssystems sind im Vergleich zu modernen Betriebssystemen lächerlich klein, obwohl es ein Dateisystem, einen Texteditor und ein Fenstersystem enthält. Ein Nebeneffekt ist, dass ein paar einfache Regeln sehr einfach zu erlernen sind, um später darauf zurückgreifen zu können.

Der Vorteil der Kürze besteht schließlich darin, dass Sie ein solches System ohne Angst vor unbekannten Funktionen wie Hintertüren sicher aufbauen können. Dies ist eine wichtige Funktion, die für sicherheitskritische Systeme angesichts der zunehmenden Gefahr von Angriffen auf die Systemintegrität von großer Bedeutung ist. Ebenso wichtig ist, dass die Hardware unseres Systems keine versteckten Teile enthält. Niemand kann Garantien für Systeme geben, die auf einer Grundlage aufgebaut sind, die für das gesamte Verständnis unzugänglich ist.

Dankbarkeit

Ich bin Paul Reed für seinen unschätzbaren Beitrag sehr dankbar. Er schlug vor, dass ich das Buch „Project Oberon“ redigiere und auch vorschlage, das gesamte System auf FPGA neu zu implementieren. Sex war eine unerschöpfliche Quelle der Ermutigung. Es war seine Idee, die Festplatte durch eine SD-Karte zu ersetzen, und er stellte auch SPI-, PS-2- und VID-Verilog-Schnittstellen zur Verfügung.

Referenzen

1. www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.Report.pdf
2. www.inf.ethz.ch/personal/wirth/Oberon/PIO.pdf
3. www.inf.ethz.ch /personal/wirth/ProjectOberon/index.html
4. www.inf.ethz.ch/personal/wirth/Oberon/PIO.pdf
5. www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.Report.pdf
6. www.inf.ethz.ch/personal/wirth/CompilerConstruction/CompilerConstruction1.pdf
7. www.inf.ethz.ch/personal/wirth/CompilerConstruction/CompilerConstruction2.pdf
8. www.inf.ethz.ch/personal/wirth/ProjectOberon/PO.Applications.pdf (Kap. 12)

Anhang

Lolas Sprache und ihre Übersetzung in Verilog



Die Hardwarebeschreibungssprache (HDL) mit dem Namen Lola wurde 1990 definiert, um die Grundlagen des Hardwaredesigns zu vermitteln. Es war eine Zeit, in der Textdefinitionen anfingen, Schaltpläne zu ersetzen, und zu dieser Zeit wurden die ersten FPGAs verfügbar, obwohl sie das industrielle Niveau noch nicht erreicht hatten. Für Lola wurde ein Compiler erstellt, der zum Laden in FPGA geeignete Bitdateien generiert. Bit-Dateiformate werden von Algotronix, Inc. veröffentlicht. und Concurrent Logic Inc. Beide Formate ermöglichten die Arbeit mit Zellen mit relativ einfachen Strukturen, die für die automatische Verdrahtung optimal sind.

Nach den Ergebnissen meines Projekts zur Überarbeitung von Oberon für FPGA entstand die Idee, auch Lola wiederzubeleben. Da Xilinx-FPGA-Zellen komplexer sind, haben wir uns nicht getraut, die Platzierung und Verkabelung zu implementieren, ungeachtet der Tatsache, dass Xilinx sich weigerte, sein proprietäres Bitdateiformat zu öffnen.

Die naheliegende Entscheidung war, einen solchen Lola-Compiler zu erstellen, der keine proprietären Bitdateien generiert, sondern die Übersetzung in eine Sprache ermöglicht, für die Xilinx ein spezielles Tool bereitstellt. Wir haben Verilog gewählt. Diese Lösung implizierte eine etwas extravagante Problemumgehung: Zunächst muss das Lola-Modul analysiert, dann übersetzt und schließlich erneut analysiert werden. In all diesen Phasen müssen wir sicherstellen, dass der Lola-Compiler über eine ordnungsgemäße Fehlerkontrolle und Typprüfung verfügt.

Um die Entwicklung von Lola-2 voranzutreiben, mussten wir alle Module des RISC5-Prozessors auf Lola neu formulieren. Welches wurde getan.

Sprache Lola

Lola ist eine kleine, lakonische Sprache im Stil von Oberon (siehe www.inf.ethz.ch/personal/wirth/Lola/Lola2.pdf ). Der Kürze halber werden wir hier nur ein Textbeispiel zu Lola zeigen (Abb. 1). Die Einheit des Quelltextes wird als Modul bezeichnet. Der Header definiert den Namen des Moduls, die Namen und Typen der Eingabe- und Ausgabeparameter. Dem Header folgt ein Abschnitt für Deklarationen lokaler Objekte wie Variablen und Register. Als nächstes folgt der Abschnitt zum Bestimmen der Werte von Variablen und Registern. BYTE definiert ein Array von 8 Bits.

MODUL Counter0 (IN CLK50M, rstIn: BIT;
	IN SWI: BYTE; OUT-LEDs: BYTE);
TYP IBUFG: = MODUL (IN I: BIT; OUT O: BIT) ^;
VAR clk, tick0, tick1: BIT;
	clkInBuf: IBUFG;
	REG (clk) zuerst: BIT;
	cnt0: [16] BIT; (* halbe Millisekunden *)
	cnt1: [10] BIT; (* halbe Sekunde *)
	cnt2: BYTE;
BEGIN-LEDs: = swi.7 -> swi: swi.0 -> cnt1 [9: 2]: cnt2;
	tick0: = (cnt0 = 49999);
	tick1: = tick0 & (cnt1 = 499);
	rst: = ~ rstIn;
	cnt0: = ~ rst -> 0: tick0 -> 0: cnt0 + 1;
	cnt1: = ~ rst -> 0: tick1 -> 0: cnt1 + tick0;
	cnt2: = ~ rst -> 0: cnt2 + tick1;
	clkInBuf (CLK50M, clk)
END Counter0.

Abb. 1. Der Quelltext auf Lola zeigt den Zähler für Sekunden und Millisekunden an, der auf den Anzeigen der Karte angezeigt wird.

Lola-

Compiler Der Compiler verwendet eine einfache rekursive absteigende Parsing-Methode. Sie wird für Lolas ausgewählten Quellcode mit dem Befehl LSC.Compile @ aktiviert. Der Parser empfängt Zeichen von einem Scanner, der Kennungen, Zahlen und Sonderzeichen (wie BEGIN, END, + und andere) verarbeitet. Dieses Design hat seine Eignung und Eleganz in vielen Anwendungen bewiesen und ist im Buch Compiler Construction (Part 1 and 2) beschrieben.

Anstatt Verilog-Texte direkt im laufenden Betrieb zu generieren, erstellt der Parser zunächst einen Operatorbaum, der für die weitere Verarbeitung besser geeignet ist. Dieser Ansatz hat den Vorteil, dass jede gewünschte Ausgabe einfach von einem geeigneten Übersetzer generiert werden kann. Einer davon ist der Übersetzer in Verilog. Erster LSV.List-Befehl outputfile.v. Ein anderes Team kann in VHDL übersetzen oder einfach den Baum ausgeben. Der Dritte kann eine Netzliste zur weiteren Bearbeitung durch den Scout erzeugen.

Somit besteht der gesamte Compiler aus mindestens vier relativ kleinen und effizienten Modulen:
LssScanner159
LSBBasis52
LSCCompiler / Parser503
LSVVerilog-Generator215

Anweisungen zur Übertragung von Lola nach Verilog finden Sie hier: www.inf.ethz.ch/personal/wirth/Lola/LolaCompiler.pdf .

Unterschiede zwischen Software- und Hardware- „Programmen“

In der Vergangenheit wurden große Anstrengungen unternommen, damit HDL-Sprachen wie „normale“ Programmiersprachen aussehen. Darüber hinaus hat HDL unter anderen PLs „Doubles“, die sich an ihren Stil anpassen. Zum Beispiel kam Verilog von C, VHDL von Ada und Lola von Oberon. Wir glauben jedoch, dass es wichtig ist, die grundlegenden Unterschiede zwischen diesen beiden Klassen zu erkennen, insbesondere wenn syntaktische Ähnlichkeiten oder sogar Identitäten vorliegen. Was sind diese grundlegenden Unterschiede?

Um die Erklärung zu vereinfachen, beschränken wir uns bei unserer Analyse auf synchrone Schaltungen, dh solche, bei denen alle Register an einen einzigen Takt gebunden sind. Im Allgemeinen sind Synchronschaltungen ein gutes Architekturparadigma, dem nach Möglichkeit gefolgt werden sollte.

Ferner ist es offensichtlich, dass alle Elemente der Schaltung buchstäblich gleichzeitig arbeiten. Jede Variable und jedes Register wird durch einen einzigen Ausdruck definiert (Kombinationsschaltung). Mehrfachzuordnungen sind nicht sinnvoll. Wir können uns leicht vorstellen, dass jedes HDL-Programm in einer großen Endlosschleife enthalten ist, da Zuweisungen zu Registern und Variablen in jedem Zyklus erfolgen.

John von Neumanns Idee, eine Sequenzer-basierte Prozessorarchitektur einzuführen, war brillant. Der Sequenzer enthält ein Befehlsregister, nach dem in jedem Zyklus bestimmte Muster ausgewählt und die anderen ignoriert werden, was zur geschickten Wiederverwendung verschiedener Teile der ALU führt. Maßnahmen oder Schritte sind ihrer Natur nach sequentiell, so dass es möglich ist, Variablen Werte neu zuzuweisen, je nachdem, wie der Programmzähler sie auf bestimmte Stellen im Programm und in der Reihenfolge der Anweisungen bezieht. Die Sequenzer-Idee ermöglichte es, riesige Programme auf relativ einfachen Schaltkreisen auszuführen.

Lola-2 ist also HDL im Stil von YP Oberon. Der hier vorgestellte Compiler übersetzt Lola-Module in Verilog-Module. Die Vorteile von Lola liegen sowohl in der einfachen und vertrauten Struktur der Sprache als auch in der Hervorhebung des Compilers für die Typprüfung und die erweiterte Fehlerdiagnose. Ein kompletter Satz von Modulen für den RISC-Prozessor, beschrieben auf Lola: www.inf.ethz.ch/personal/wirth/Lola/index.html

Jetzt auch beliebt: