Hochschule Bremen University of Applied Sciences Projektbericht Titel: Implementierung eines Single Instruction Computers in VHDL Autor: Roul Oldenburger Zeitraum: Wintersemester 2001/2002 Projektleitung: Prof. Dr. Thomas Risse Prof. Dr. Sven Simon Projektbericht Single Instruction Computer Inhaltsverzeichnis 1. 2. 3. Einleitung ............................................................................3 Projektaufgabe und –ziel ...................................................5 Technischer Hintergrund und Kontext ............................5 3.1 3.2 3.3 4. 5. Grundlagen zum SIC .................................................................... 5 Hardware........................................................................................ 7 Software.......................................................................................... 8 Vorgehensweise...................................................................9 Der Single Instruction Computer......................................9 5.1 5.2 5.3 5.4 6. Blockschaltbild............................................................................. 12 Beschreibung in VHDL............................................................... 13 Programme in SBN ..................................................................... 16 Daten für Programme in SBN.................................................... 18 Simulation und Test des SIC ...........................................19 6.1 7. Testbench...................................................................................... 19 SBN Assembler .................................................................22 7.1 7.2 7.3 8. Übersetzungs-Hierarchie ............................................................ 22 Pseudo-Befehle............................................................................. 25 Konzept für einen Assembler in Perl......................................... 26 Ergebnisse und Diskussion ..............................................28 8.1 8.2 Zusammenfassung ....................................................................... 29 Letzter Stand................................................................................ 30 9. Ausblick.............................................................................31 10. Kurzanleitung ...................................................................33 10.1 10.2 10.3 10.4 10.5 WebPACK Projekt Manager.................................................. 33 ModelSim Simulator................................................................ 35 Pin-Zuweisung / UCF-Editor .................................................. 36 Download auf das FPGA ......................................................... 37 Anwendung des Assemblers.................................................... 39 11. Bibliographie und Referenzen.........................................40 11.1 11.2 11.3 Bücher ....................................................................................... 40 Webseiten.................................................................................. 40 Andere Quellen......................................................................... 40 Anhang .......................................................................................41 Verwendete Hardware und Software .................................................. 41 Synthese- / Compiler-Berichte in Auszügen........................................ 41 Inhalt des Projektverzeichnisses „Projekt_SIC“................................ 44 VHDL Quellcode des SIC ..................................................................... 47 VHDL Quellcode der Testbench .......................................................... 51 Perl Quellcode des „Assemblers“ ......................................................... 53 -2- Projektbericht Single Instruction Computer 1. Einleitung Seit der Entwicklung der ersten praktikablen Computer auf einem Chip liegen die Hersteller und Designer im Wettstreit um die CPU mit der besten Performance. Die Komplexität dieser Prozessoren stieg enorm an. Immer mehr Instruktionen für teilweise sehr spezielle Aufgaben und verschiedenste Adressierungsmodi wurden angeboten, um hochsprachlichen Konstrukten möglichst nahe zu kommen und damit z.B. bestimmte Datenstrukturen, Schleifen und Funktionsaufrufe möglich zu machen. Umfangreiche und komplexere Anwendungen sind in einer Hochsprache leichter umzusetzen als in einer, nur ein Dutzend Instruktionen umfassenden, Maschinensprache. In den 80’er Jahren wurden jedoch Stimmen laut, die die Meinung vertraten, eine auf wesentliche Instruktionen beschränkte Architektur könne bei gleichen oder gar geringerem Hardwareaufwand genauso leistungsfähig sein wie hochkomplexe Architekturen. Solche Computer nennt man „Reduced Instruction Set Computer“ (RISC), ihre Counterparts hingegen „Complex Instruction Set Computer“ (CISC). Viele zum Teil „überspezialisierte“ Instruktionen lassen sich durch Sequenzen von einer oder mehreren einfachen Anweisungen ersetzen. Der dadurch in der ProzessorArchitektur eingesparte Platz kann z.B. für breitere Bussysteme oder mehr CacheSpeicher verwendet werden. Diese einfachen Anweisungen sind kürzer und nicht so aufwendig zu dekodieren und damit wesentlich schneller abgearbeitet. Die Architekten entdeckten, dass sie mit einfachen RISC-Architekturen hochoptimierte Prozessoren mit sehr hohen Taktraten bauen konnten. Compiler-Bauer sehen diese Entwicklung zunächst unter dem Aspekt, dass das Programmieren wieder aufwändiger und unbequemer wird, denn die hochsprachlichen Konstrukte müssen nun wieder durch eine Reihe von nicht komplexen Instruktionen ersetzt werden. Dass sich RISC-Architekturen dennoch erfolgreich bewähren konnten, lag zum Teil daran, dass die Entwicklung von Hochsprachen und deren Compiler stark voran geschritten war und sich damit das Problem, (unbequem und aufwändig) in Maschinensprache für eine RISC-Architektur zu programmieren, erübrigte. Heutzutage gibt es jedoch Punkte, an denen die Philosophien miteinander verschmelzen und es schwer wird, einen Prozessor klar als RISC oder CISC einzuordnen. Neben der gängigen Charakterisierung eines RISC-Prozessors mit fixem Instruktions-Format, einfachem Load-Store-Befehlssatz, festverdrahteter Ablaufsteuerung (kein MikroProgramm) und intensivem Einsatz von Pipelining, gibt es Vorschläge diese anders zu definieren. Da es z.B. RISC-Prozessoren gibt, die für ihre Gleitkomma-Arithmetik doch auch wieder mikro-programmiert sind, schlägt Giloi die Bezeichnung Pipeline-SkalarProzessor vor [RSTSK]. Wie weit kann nun der Befehlssatz eines Prozessors reduziert werden ohne die Funktionalität zu beschränken? Könnte ein Prozessor mit so wenigen Instruktionen wie möglich, bei einer entsprechenden Taktung, nicht genauso leistungsfähig aber kostengünstiger sein als eine ausgereifte CISC-CPU? -3- Projektbericht Single Instruction Computer Diese und ähnliche Fragen entstehen, wenn man sich die Vorteile, die RISCArchitekturen haben und damit wirtschaftlich und wissenschaftlich interessant machen, vor Augen führt. Theoretisch ist es möglich, einen Computer zu entwerfen, der nur eine einzige Instruktion hat und dennoch alle grundlegenden Operationen ausführen kann, die ein „normaler“ Computer auch ausführt. Wie soll diese alleskönnende Anweisung aussehen? Immerhin muss der Computer grundlegende arithmetische und logische Funktionen ausführen, Daten im Speicher transportieren und den Verlauf eines Programms kontrollieren können. Es ist schwer sich vorzustellen, dass all dies durch eine einzige Anweisung möglich sein soll. Eine solche Instruktion ist die SBN-Anweisung. SBN steht für „Subtract and Branch if Negative“. Sie ist üblicherweise aus drei Argumenten aufgebaut, die allesamt Wörter im Speicher adressieren. Die an den ersten beiden Adressen enthaltenen Werte werden voneinander subtrahiert und sollte dass Ergebnis negativ sein wird das Programm an der dritten angegebenen Adresse, sonst an der nächstfolgenden Adresse fortgesetzt. Die Anweisung SBN A, B, C Wie sie ausgeführt wird Mem[A] := Mem[A] – Mem[B] if (Mem[A] < 0) then PC := C else PC := PC + 1 Wie oben beschrieben (s. [NOSIC]), basiert das System auf der grundsätzlichen Verlagerung der Intelligenz in Assembler und Compiler. Wo sonst ein hochentwickelter Compiler die hochsprachlichen Programmanweisungen z.B. eines C++ Programms in die verschiedenen komplexen Instruktionen eines Computers übersetzt und nach Möglichkeit optimiert, muss hier zunächst ein Assembler eingesetzt werden, der diese Maschinensprachen-Befehle (Kap. 7.2 Pseudo-Befehle) in Konstrukte aus SBNAnweisungen übersetzt. Ein beliebtes Beispiel ist die MOVE- oder COPY-Anweisung, durch die der Inhalt einer Adresse im Speicher in eine andere kopiert wird: oder MOV B, A CP B, A ADD $B, $0, $A sind Beispiele für Anweisungen, die bewirken, dass der Inhalt der Adresse oder des Registers A nach B kopiert wird. Ein entsprechendes SBN-Konstrukt könnte z.B. so aussehen [NOSIC]: start: SBN T, T, .+1 SBN SBN SBN T, A, .+1 B, B, .+1 B, T, .+1 # Mem[T] := Mem[T] – Mem[T] also wird # Mem[T] := 0 gesetzt # Mem[T] := 0 – Mem[A] = –Mem[A] # wie oben Mem[B] wird = 0 gesetzt # Mem[B] := 0 – Mem[T] = - - Mem[A] =Mem[A] Hierbei meint die Notation „ .+1“ die jeweils nächste Adresse; der PC wird in jedem Fall inkrementiert. Somit ist also in Folge dieser vier SBN Anweisungen der Inhalt der Adresse A nach Adresse B kopiert worden. [THSIC] [URISC] [NOSIC] -4- Projektbericht Single Instruction Computer 2. Projektaufgabe und –ziel Unter den in der Einleitung genannten Aspekten, erscheint der SIC (Single Instruction Computers) als eine interessante Prozessor-Architektur. Daher soll diese im Rahmen des RST-Labors näher untersucht und Versuche damit durchgeführt werden. Das Ziel dieses Projektes ist es, den Studenten und anderen Interessierten die Möglichkeit zu geben, am live existierenden Versuchsaufbau, Tests und Untersuchungen an einem SIC durchzuführen. Dafür ist angedacht, eine „Experimentier-Umgebung“ des Digitaltechnik-Labors zu nutzen, den SIC in VHDL zu realisieren und auf einem FPGA (Field Programmable Gate Array) „unterzubringen“. Ferner soll eine Entwicklungsumgebung entstehen, die das Programmieren des SIC in einer geeigneten Maschinensprache ermöglicht. Dazu ist ein Assembler notwendig, der die „komplexen“ Assembler-Anweisungen in Folgen und Kombinationen von SBN-Anweisungen übersetzt. Um volle Funktionalität des SIC zu erreichen, gilt es, möglichst für alle anzuwendenden Assembler-Anweisungen entsprechende Konstrukte zu finden. Es ist wünschenswert die „Experimentier-Umgebung“ des Digitaltechnik-Labors so nutzen, dass das FPGA wie eine übliche CPU auf Programme und Daten aus dem externen (nicht im FPGA angelegten) Speicher zugreift und die Daten bzw. der Datenspeicher nach Ablauf des Programms auslesbar ist. Dazu ist auch die Verwendung der auf dem Experimentier-Bord vorhandenen Schnittstellen und dazugehöriger Einund Ausgabegeräte möglich. Die Entwicklungsumgebung könnte in einem ausgebauten Zustand einen Hochsprachen-Compiler samt Debugger und Assembler unter einer grafischen Oberfläche umfassen und somit das Programmieren des SIC z.B. in PASCAL ermöglichen. Die Aufgabe dieser Arbeit ist es, eine Grundlage für das oben beschriebene Projektziel zu geben und ein System soweit vorzubereiten, dass erste Versuche mit einem SIC möglich sind und damit der Zyklus „programmiern-synthetisieren-simulierendownladen-testen“ wenigstens einmal erfolgreich durchlaufen und dokumentiert wird. 3. Technischer Hintergrund und Kontext 3.1 Grundlagen zum SIC Die Prozessor-Architektur des SIC benötigt nur wenige der sonst üblichen Komponenten eines Prozessors. Zunächst wird nur ein Register benötigt. Dies ist der Programmzähler (PC). Die sonst übliche ALU (Arithmetic Logic Unit) wird auf die Fähigkeit 2’er-Komplemente bilden und addieren zu können beschränkt. Je nach Art der Umsetzung könnte dies z.B. einen Carry-Ripple-Adder enthalten; ohne dies jedoch konkret festzulegen, wird das von der Synthese-Software bestimmt. Außerdem werden ein Programm- und ein Datenspeicher benötigt, deren Größe und Breite zunächst durch den verwendeten FPGA-Baustein begrenzt und durch das verwendete Befehlsformat, -5- Projektbericht Single Instruction Computer sowie den zugelassenen Wertebereich festgelegt werden. Schlussendlich wird eine Steuereinheit benötigt, die den Programmcode auswertet und dementsprechend über die Wahl der Adressen und die Änderung des Programmzählers bestimmt. Befehlssatz: Da der Befehlssatz des SIC nur aus der SBN Anweisung besteht, legt diese auch die Prozessor-Architektur weitgehend fest. Es werden keine Stacks zur Abarbeitung der Programme oder ein dediziertes Register (Akkumulator) eingesetzt. Die Operation wird direkt auf den verwendeten Adressen ausgeführt und solange nur FPGA-interner Speicher verwendet wird, ist der Zugriff auf den Speicher mit dem auf einem Feld von Registern vergleichbar. Da es nur einen Befehl gibt, braucht dieser auch nicht durch einen OP-Code kodiert zu werden. Was also von dem in der Einleitung vorgestellten SBN Befehl bleibt, sind die drei absoluten Speicheradressen. Dementsprechend wird eine Anweisung aus der dreifachen Anzahl an Bits bestehen, die nötig sind, um den gewünschten Speicherbereich zu adressieren. Dies kann unter Umständen bei einem großen Speicher zu einer sehr breiten Anweisung führen und dementsprechend viel Programmspeicher benötigen. Ob jedoch die Sprungadresse absolut oder relativ angegeben wird, bedeutet für die Hardware keinen großen Unterschied. Die Befehlsbreite verringert sich und könnte damit zu Einsparungen an Programmspeicher führen, allerdings wird ein entsprechender Addierer benötigt, der den Programmzähler um mehr als 1 erhöhen kann. Auf der anderen Seite bedeutet dies einen Mehraufwand für den Assembler. Die tatsächliche Adresse nach einem Sprung ist explizit zu ermitteln und bei Zielen, die außerhalb der Sprungweite liegen, muss das Programm um entsprechend viele SBN Anweisungen erweitert werden. Dies führt natürlich wieder zu einem Mehrbedarf an Speicherplatz und einem eventuell erheblich höheren Zeitbedarf für die Ausführung eines weiten relativen Sprunges im Gegensatz zu einem weiten absoluten Sprung. Dennoch ist der SBN Befehl zunächst mit einer relativen Sprungweite als drittem Argument implementiert. Datenpfad: Als erstes Element bestimmt der Inhalt des Programmzählers die Adresse der aktuellen Anweisung im Programmspeicher. Die ersten beiden Argumente der Instruktion enthalten die Adressen der beiden Operanden im Datenspeicher, das dritte Argument enthält die relative Sprungweite. Die Inhalte der beiden angegebenen Adressen im Datenspeicher werden voneinander subtrahiert. Das Ergebnis wird an die erste angegebene Adresse zurückgeschrieben und bestimmt, ob der Wert des Programmzählers inkrementiert oder ihm die relative Sprungweite hinzu addiert werden soll. Der aktuelle Wert des Programmzählers und eine 1 oder die relative Sprungweite werden dazu auf einen Addierer gegeben. Es sind Sprünge in beide Richtungen möglich, daher wird die Sprungweite im 2’er Komplement angegeben. Steuerung: Die voneinander zu subtrahierenden Operanden sind aus dem Datenspeicher zu entnehmen. Dazu müssen zunächst ihre Adressen ermittelt und diese abgefragt werden. Die ersten beiden Argumente der SBN Anweisungen können hierbei direkt zur Adressierung des Datenspeichers dienen. Sie bilden also zusammen mit der -6- Projektbericht Single Instruction Computer Ansteuerung des Speichers die erste Komponente der Steuereinheit. Die Operanden werden auf einen Subtrahierer gegeben und die Differenz gebildet. Liegt die Differenz im 2’er Komplement vor, so gibt das MSB (Most Significant Bit) an, ob das Ergebnis negativ oder positiv ist. Somit kann das MSB direkt als Steuerbit eingesetzt werden und bestimmen, ob der Programmzähler inkrementiert oder ihm die Sprungweite hinzuaddiert wird. Speicher-Aufbau und –Zugriff: Daten- und Programmspeicher werden als zwei voneinander getrennte Speicher behandelt. Dies ermöglicht eine flexiblere Handhabung und Nutzung des insgesamt vorhandenen Speichers, da Befehlswort- und Datenwortbreiten unabhängig voneinander sind. Wie oben im Abschnitt „Befehlssatz“ beschrieben, sind die SBN Anweisungen mit zwei oder drei Adress-Argumenten stark adressorientiert. Ihre Breite bestimmt sich letztlich aus dem verwendeten Adressbereich und der dazu notwendigen Anzahl an Bits. Die Breite des Datenspeichers hingegen richtet sich nach dem für die Operanden verwendetem Zahlenbereich. Die tatsächliche Implementierung der Speicher dieses SIC ist jedoch zunächst anders gewählt (ab Kap. 5.1). Das Programm wird als festverdrahtete Logik implementiert und bietet damit zeitliche Vorteile. Dies entspricht aber nicht der Zielvorstellung eines Programmspeichers, der bei laufendem Betrieb mit unterschiedlichen Programmen geladen und abgearbeitet werden kann. Es ist jedoch zunächst die praktikabelste Lösung, da es sich einfacher handhaben lässt. Zunächst werden Programm- und Datenspeicher direkt auf dem FPGA angelegt. Dementsprechend richtet sich der Speicherumfang nach dem verwendeten Baustein und der Größe der durch die SIC-Schaltung belegten Chipfläche. Näheres zu den Daten des verwendeten Spartan2 Chips von Xilinx ist in der Dokumentation im Projektverzeichnis ../Andere_Quellen/Spartan_FPGA_Doku zu finden. Üblicherweise werden Speicher immer byte- oder wortweise angesprochen und sind dahingehend organisiert und optimiert. Innerhalb eines FPGAs gibt es hier keine Vorgaben. Grenzen sind nur durch die verwendete Hardware gegeben. Speicher ließe sich bitweise aufbauen und ansteuern oder aber in beliebiger Anzahl von Bits. Hierarchische Speicherstrukturen sind möglich, ebenso wie einfach organisierter Speicher. Eine einfache Lösung ist es zum Beispiel, die Speicher als Felder von Bitvektoren aufzubauen, deren Breite der benötigten Befehlswortbreite oder dem benötigten Wertebereich entspricht. Wenn zu einem späteren Zeitpunkt externer Speicher verwendet wird, sind diese Parameter möglicherweise an diesen anzupassen und der interne Speicher könnte zu einem Cache umfunktioniert werden. 3.2 Hardware Als Hardware-Basis dient ein „FPGA Prototyping Kit“ der Firma Burch Electronic Designs. Dieses umfasst zunächst ein Experimentier-Bord (eine Platine) zusammen mit einem FPGA Chip Spartan2 der Firma Xilinx. Das FPGA (Field Programmable Gate Array) ist ein hochintegrierter Baustein auf Siliziumbasis und besteht aus einer großen -7- Projektbericht Single Instruction Computer Anzahl gleichmäßig angeordneter, universeller elektronischer Schaltungsblöcke. Diese Schaltungsblöcke können viele verschiedene Funktionen übernehmen. Je nachdem wie sie miteinander verbunden werden und welche Anschlüsse von ihnen genutzt werden, können sie zu den verschiedenen Formen von Flip-Flops und Logik-Gattern konfiguriert werden. Die Kombination vieler solcher konfigurierter Blöcke können komplizierte Schaltungen nachbilden. Die Blöcke sind durch ein umfassendes Netzwerk an Leitungen über den ganzen Baustein hinweg miteinander verbunden. Teile dieses Netzwerkes können als Busse ausgelegt werden. Auf jeden Fall aber verbinden sie die einzelnen Blöcke untereinander und mit den Anschlusspins des FPGAs. Da FPGAs frei programmierbar sind, bieten sie für eine Vielzahl von Anwendungen innerhalb von digitalen Schaltungen im experimentellen Bereich und in der Lehre eine kostengünstige und flexible Lösung. Sie können in Massen produziert werden, ohne dass vorher etwas über ihre spätere Verwendung bekannt sein muss und gleichzeitig bieten sie die Möglichkeit, eigene, ganz spezielle Designs umzusetzen. Durch den Einsatz von FPGAs wird die Entwicklung sogenannter 1-Offs wesentlich kostengünstiger. Die Datenblätter zu dem verwendeten Baustein sind unter ../Andere_Quellen/Spartan_FPGA_Doku zu finden. Ähnlich wie Speicherbausteine (RAM) verlieren FPGAs ihre Programmierung nach dem Abschalten oder Verlust der Versorgungsspannung. Damit erübrigt sich einerseits ein spezieller Löschvorgang, allerdings kommen sie dadurch auch nicht für jede Anwendung in Frage. Speziell diese Eigenschaft macht es notwendig, das FPGA in der Experimentier-Umgebung bei jeder Änderung des SIC-Programms oder Veränderung am SIC neu zu programmieren. Um gerade für den experimentellen Bereich und die Lehre eine vielseitige und leicht zu nutzende Umgebung zu bieten, gibt es für das Experimentier-Bord, das mit herausgeführten Anschlüssen und Schnittstellen versehen ist, eine Reihe von Zusatzplatinen. Diese bieten z.B. externen Speicher, 7-Segment-Anzeigen und eine Reihe LEDs (Light Emitting Diodes). Sie können leicht über die dafür vorgesehenen Anschlüsse und Flachbandkabel mit der Hauptplatine verbunden werden. Abbildungen und nähere Angaben sind im Projektverzeichnis unter .../SpartanBoardPläne/Burch Electronic Designs.htm oder aktuell unter der angegebenen Quelle [BURSB] zu finden. 3.3 Software Die Firma Xilinx bietet zur Entwicklung digitaler Schaltungen und zum Programmieren ihrer FPGA eigens Software an. Das Argument der Kundenbindung hat die größeren Hersteller von digitalen Schaltkreisen dazu veranlasst, selber Software anzubieten, die auf ihre Produkte optimiert oder nur für ihre Produkte nutzbar sind. Die in diesem Fall verwendete Software „WebPACK“ stellt eine komplette Entwicklungsumgebung dar. Sie unterstützt die Entwicklung digitaler Schaltungen in VHDL (VHSIC Hardware Description Language – Very High Speed Integrated Circuit) oder Verilog für eine bestimmte Auswahl an Xilinx Produkten. VHDL und Verilog sind Hardware Beschreibungssprachen. Sie sind sich in vielen Elementen mit höheren Programmiersprachen ähnlich, haben aber den entscheidenden Unterschied , dass die Beschreibung wirklich gleichzeitig ablaufender Aktionen nicht nur möglich sondern -8- Projektbericht Single Instruction Computer grundlegendes Element ist. Die Entwicklungsumgebung bietet eine Vielzahl an in die Oberfläche eingebundenen Werkzeugen an, von denen hier nur beispielhaft Formatumwandler, Routing-Tools und vor allem ein Simulator (ModelSim) genannt werden. Wesentlich sind: Der Editor zum Schreiben der Programme in den HDLs, der Compiler zum Übersetzen des Programmcodes, der Debugger zum Prüfen des Programms auf seine Korrektheit und ein Programm zur Erzeugung der FPGAProgrammier-Datei, die letztlich in den Baustein geladen wird. Alle diese Komponenten sind unter einem Projektmanagement zusammengefasst. Dieses sehr umfangreiche Software-Paket steht auf der Xilinx Homepage www.xilinx.com frei zum Download zur Verfügung. Die innerhalb dieses Projektes verwendeten Versionen sind im Projektverzeichnis unter ../Software/ zu finden. 4. Vorgehensweise An dieser Stelle soll kurz die Vorgehensweise zu Bearbeitung dieses Projektes beschrieben werden. Der Ablauf entspricht dabei grob der angegebenen Reihenfolge. Teilweise können Aufgaben oder Vorhaben aber auch parallel zu anderen stattfinden. 1. Hintergrundwissen verschaffen bzw. auffrischen a. SIC, Prozessor-Architekturen b. VHDL c. Verwendete Hard- und Software d. Perl 2. Einarbeitung in die verwendeten Werkzeuge a. WebPACK (4.0 / 4.1) b. ModelSim (5.1 / 5.5) c. ActivePerl (5.6 für Windows) 3. Funktionsweise der SBN Anweisung klären, festlegen 4. Datenpfad und Steuerung zur Umsetzung der SBN Anweisung entwickeln 5. Umsetzung in VHDL – dazu Tests und Simulation des SIC mit ModelSim 6. Test auf dem Experimentier-Bord 7. Entwicklung eines Konzepts für einen Assembler 8. Erste Umsetzung eines Assemblers für einen SIC in Perl 9. Test des erstellten Codes 10. Test des assemblierten Codes in der Entwicklungsumgebung und dem Simulator 11. Test auf dem Experimentier-Bord 5. Der Single Instruction Computer Die Art und Weise, in der der SIC implementiert wird, lässt die Darstellung des Ablaufs in Form eines Ablaufdiagramms als nicht sinnvoll erscheinen. Insbesondere ist hierfür die Umsetzung des Programmcodes (SBN Anweisungen) in ein Schaltnetz verantwortlich. Dieses Schaltnetz (Logik) erhält als Eingangssignal den Wert des Programmzählers und liefert die entsprechende SBN Anweisung in Form eines BitVektors zurück. Die Auswertung definierter Bereiche dieses Vektors ergibt dann wieder die drei Argumente der SBN Anweisung. Die oberen zwei Bereiche dieses Vektors liefern die Adressen (Steuersignale A, B) im Datenspeicher, die zur Ansteuerung der -9- Projektbericht Single Instruction Computer Multiplexer A und B dienen. Sie wählen in dem vorhandenen Adressbereich die gewünschten Speicheradressen aus und schalten ihre Inhalte (die Operanden) auf den Subtrahierer. Dort wird die Differenz gebildet und an die erste Quelladresse gelegt. Zur darauffolgenden Taktflanke (Schaltwerk) wird das Ergebnis zurückgeschrieben. Im selben Moment entscheidet das MSB des Ergebnisses (Steuersignal) darüber ob der Programmzähler inkrementiert wird oder ihm das dritte Argument (Sprungweite C) hinzuaddiert wird. Dieses Steuersignal wird von dem dritten Multiplexer ausgewertet, der je nach Wert des MSB einen zweiten Summanden (1 ⇒ C wird addiert, 0 ⇒ 1 wird addiert) auf den Addierer schaltet. Das Ergebnis dieser Addition wird zur selben Taktflanke in den PC geschrieben, wie das Ergebnis der Subtraktion in das entsprechende Quellregister geschrieben wird. Das folgende Prinzip-Schaltbild soll den Zusammenhang zwischen Schaltnetz und Schaltwerk verdeutlichen. Als Bezug darauf sind die Komponenten des Schaltwerkes (Flip-Flops) im Blockschaltbild mit „Register“ bezeichnet. X (Mealy) PCn PCn SN X’ (Mealy) PCn+1 SW PCn+1 Takt Abbildung 1: Mealy- und Moore-Signale an einem Schaltnetz Abbildung 1 zeigt, dass die Schaltung insgesamt aus einem Schaltnetz (SN) und einem Schaltwerk (SW) aufgebaut ist. Der wesentliche Unterschied zeigt sich in den Ein- und Ausgangssignalen. Während Mealy-Ausgangssignale eines Schaltnetzes (X’) nur abhängig sind von den Eingangssignalen des Schaltnetzes und dessen Funktion, sind Moore-Ausgangssignale (PCn+1) zusätzlich abhängig von dem Taktsignal, das an dem Schaltwerk anliegt. Das Mealy-Ausgangssignal stellt sich ohne Zeitverzögerung durch den Takt ein. Moore-Ausgangssignale erfahren erst mit dem Takt eine Änderung. Die im Schaltwerk enthaltenen Flip-Flops ändern ihre Zustände in Abhängigkeit vom Moore-Ausgangssignal (PCn+1) des Schaltnetzes erst zum nächsten Taktzyklus. - 10 - Projektbericht Single Instruction Computer Bezogen auf den SIC, beschreibt Abbildung 2 den Zusammenhang. Der Wert (Zustand) des Programmzählers (PC) und der Wert des Datenspeichers (Quelladresse A) sind die einzigen Moore-Signale. Während alle anderen Signale abzüglich ihrer Laufzeiten sofort zur Verfügung stehen, bekommen der Programmzähler und der Datenspeicher erst mit dem nächsten Takt ihre neuen Werte zugeschrieben. MSB 1 PCn a b C SN (a-b)=Diff= a n+1 PCn+1 A B C SW Takt Abbildung 2: Mealy- und Moore-Signale im SIC - 11 - PCn a b Projektbericht Single Instruction Computer 5.1 Blockschaltbild Die Signale A, B und C sind farblich gestaltet worden, damit nicht der Eindruck entsteht, sie wäre mit einander verbunden oder würden sich tatsächlich auf diese Weise kreuzen. „...“ in der Darstellung der Speicher soll andeuten, dass weitere Abschnitte oder Register vorhanden sein können, die hier nicht dargestellt sind. - 12 - Projektbericht Single Instruction Computer 5.2 Beschreibung in VHDL Der vollständige Code, zur Beschreibung dieses SIC in VHDL, ist im Anhang zu finden. Die Quelldatei befindet sich im Projektverzeichnis unter .../Software/ . Zunächst drei allgemeine Hinweise: Die Verwendung des Datentyps „STD_ULOGIC“ bzw. „STD_ULOGIC_VECTOR“ ist wann immer möglich vorzuziehen, da dieser aufgrund seiner Struktur Kurzschlüsse und damit die Zerstörung der verwendeten Hardware unter Umständen verhindern kann. VHDL ist strikt typisiert, d.h. es werden keine Typumwandlungen oder Anpassungen automatisch oder auf Verdacht vorgenommen. Des weiteren sei zur Wahl der Namensgebung gesagt, das die Endungen _s für Signale, _o für Ausgangssignale, _i für Eingangssignale und _c für Konstanten verwendet werden. Allgemein ist die Struktur der VHDL Dateien wie folgt aufgebaut: Einleitend findet sich ein Programmkopf bestehend aus Kommentaren. Kommentare werden in VHDL grundsätzlich durch „--" einen doppelten Bindestrich eingeleitet und reichen dann bis zum Ende der Zeile. Als nächstes finden sich Hinweise und Anweisungen zum Verwenden von Bibliotheken (LIBRARY, USE) und vordefinierter Komponenten. Daraufhin beginnt der eigentliche Inhalt der VHDL Datei mit der „ENTITY“. Sie beschreibt wie die Schaltung von außen her „aussieht“ und was für Einund Ausgänge sie hat. Das eigentliche Innenleben und die tatsächliche Funktionsweise der Schaltung wird dann unter „ARCHITECTURE“ beschrieben. Wenn man sich vor Augen führt, dass ein Schaltnetz, das irgendwelche logischen Funktionen enthält, diese im Prinzip ohne Verzögerung ausführt (abgesehen von den Laufzeiten der elektrischen Signale), dann muss es einen Bereich innerhalb des VHDL Codes geben, in dem die Reihenfolge, in der die Beschreibung steht keinen Einfluss auf die Abarbeitung dieser Logik hat. In der Regel ist man es gewohnt Programme in sequentieller Weise zu durchdenken und zu beschreiben. Da man hier aber auf die direkte Beschreibung der Hardware abzielt, muss hier anders vorgegangen werden. Als „Concurrent Statements“ werden zunächst alle in der Architecture angegebenen Beschreibungen betrachtet und später im Prinzip gleichzeitig ausgeführt. Um nun dennoch sequentielle Abläufe beschreiben zu können gibt es den „PROCESS“. Prozesse sind selber insgesamt ein „Concurrent Statement“, dennoch wird die innerhalb eines Prozesses beschriebene Funktion sequentiell abgearbeitet. - 13 - Projektbericht Single Instruction Computer Im Folgenden werden die wichtigsten Komponenten des SIC anhand des VHDL Codes erläutert: -- SBN Anweisungen im Programmspeicher CONSTANT Program : Program_type := Program_type'( "00111001110001", --> 1 "00111001010001", --> 2 "00011000110001", --> 3 "00011001110001", --> 4 "00111001110001", --> 5 "00111001100001", --> 6 "00100001000001", --> 7 "00100001110001", --> 8 "00001000100000" --> 9 ); Der komplette SBN-Programmcode ist als konstante innerhalb der Architecture angelegt. Das heißt zunächst, dass während des Betriebes keine Veränderungen an dem Programm vorgenommen werden können. Um ein Programm auf diese Weise angeben zu können, ist die Konstante als ein Feld von Bitvektoren definiert. Näheres zur Interpretation dieser Vektoren im Abschnitt 5.3 . Wichtig ist zunächst, dass diese Konstante in keiner Abhängigkeit zu einem Taktsignal steht und daher als Teil des Schaltnetzes synthetisiert wird. SIGNAL data_reg_s : data_reg_t; Allein diese Anweisung stellt zunächst den kompletten Datenspeicher (data_reg_t ist auch als ein Feld von Bitvektoren definiert) als Signale zur Verfügung, die aber erst später durch ihre Initialisierung und das Beschreiben in Abhängigkeit vom Taktsignal als Flip-Flops synthetisiert werden. -- Naechste SBN Anweisung instruction_s <= Program(pc_s); Das Anlegen des aktuellen Wertes des Programmzählers an das Schaltnetz liefert den dazugehörigen Bitvektor und damit die nächste SBN Anweisung. Mit der Initialisierung durch den Reset beginnt der Programmzähler automatisch bei 1 und damit liegt die erste Instruktion vor. -- Die naechsten moeglichen Programmzaehler pc_incr_s <= pc_s + 1; pc_jmp_s <= pc_s + jmp_range_s; In jedem Falle bestimmt das Schaltnetz die nächsten möglichen Werte des Programmzählers. Dies ist dadurch möglich, dass der aktuelle Wert des Programmzählers gegeben, 1 eine Konstante und die Sprungweite - 14 - Projektbericht Single Instruction Computer -- Sprungweite bei negativem Ergebnis jmp_range_s <= conv_integer (SIGNED(instruction_s (instr_argl3_c - 1 downto 0))); aus der Instruktion heraus bekannt ist. Da aus der Instruktion heraus die auszuwertenden Adressen des Datenspeichers ebenfalls bekannt sind, -- Adressen im Datenpeicher adr_a_s <= conv_integer (UNSIGNED(instruction_s(instr_wl_c - 1 downto instr_wl_c – instr_argl1_c))); adr_b_s <= conv_integer (UNSIGNED(instruction_s(instr_wl_c - instr_argl1_c - 1 downto instr_wl_c - instr_argl1_c - instr_argl2_c))); können auch direkt die zu subtrahierenden Operanden ermittelt werden. -- Daten aus Datenspeicher op_a_s <= conv_integer (SIGNED(data_reg_s(adr_a_s))); op_b_s <= conv_integer (SIGNED(data_reg_s(adr_b_s))); Zur Erklärung: Die Funktion „conv_integer“ wandelt einen als „SIGNED“ (mit Vorzeichen) oder „UNSIGNED“ (ohne Vorzeichen) deklarierten Bitvektor in einen Integer-Wert um. Mit „X downto Y“ findet eine Nummerierung statt und legt damit den Bereich des Bitvektors fest, der gewandelt werden soll. „instruction_s“ wiederum ist das Signal, das den Bitvektor der SBN Anweisung enthält. Da auch die Operanden selber bekannt sind, kann auch die dazugehörige Differenz durch das Schaltnetz ermittelt werden. -- Ergebnis diff_s <= op_a_s - op_b_s; diff_vec_s <= STD_ULOGIC_VECTOR(conv_std_logic_vector (diff_s, data_wl_c)); Zur Erklärung: Die Funktion „conv_std_logic_vector“ wandelt einen Integerwert in einen Bitvektor des Typs Standard-Logic einer bestimmten angegebenen Länge (data_wl_c) um. Die darüber gesetzte Umkonvertierung „STD_ULOGIC_VECTOR“, wandelt diesen Vector dann in einen Bitvektor des speziellen Typs Standard-Ulogic um. Aus dem Ergebnis selber bestimmt sich dann wieder (durch das MSB) welcher der möglichen Folgewerte des Programmzählers in frage kommt und zum nächsten Takt in das Programmzähler-Register geschrieben wird. -- Neuer Programmzaehler IF diff_s < 0 THEN pc_s <= pc_jmp_s; ELSE pc_s <= pc_incr_s; END IF; - 15 - Projektbericht Single Instruction Computer Dies ist wiederum Teil des Schaltwerkes, da es innerhalb des Prozesses steht. Der Prozess wird jeweils zur nächsten positiven Taktflanke ausgeführt. ELSIF (clk_i'EVENT AND clk_i='1') THEN Das ´EVENT beschreibt den Moment, in dem sich das angegebene Signal verändert, verknüpft mit der Bedingung, dass es dann 1 sein soll, entspricht der Bedingung eine positive Flanke des Signals zu erwarten. Der taktabhängige Prozess soll nun im noch einmal übergreifend dargestellt werden: reg : PROCESS (clk_i, reset_i) Begin IF reset_i = '0' THEN . . . ELSIF (clk_i'EVENT AND clk_i='1') THEN . . . END IF; End PROCESS; Zunächst gibt die „Sensitivity List“ (clk_i, reset_i) an, dass wann immer eine Veränderung im Zustand dieser Signale auftritt, der Prozess „angestoßen“ werden soll. Das heißt, sowohl bei negativen als auch positiven Flanken des synchronen Taktsignals und des asynchronen Resetsignals. Um dies sinnvoll umzusetzen, wird für den ersten Abschnitt des Prozesses die Bedingung „reset_i = ´0’“ aufgestellt und für den zweiten Abschnitt die bereits beschriebene Bedingung einer positiven Taktflanke. Dies hat zur Wirkung, dass die Initialisierung des SIC aufgrund des Löschen des Resetsignals passiert und das Zurückschreiben des Ergebnisses in den Datenspeicher sowie das setzen des Programmzählers auf seinen neuen Wert zu jeder positiven Taktflanke durchgeführt wird. 5.3 Programme in SBN Das implementierte Beispiel-Programm führt zwei Kopieranweisungen in der Weise durch, wie sie in der Einleitung beschrieben ist. Eine, als temporäres Register gewählte Adresse, wird zweimal in Folge gelöscht, erhält den negierten Wert des Quellregisters und wird dann selber zum Quellregister. Das Zielregister erhält dann von dort den zweimal negierten Wert, der ursprünglich kopiert werden sollte. Bei der genauen Ermittlung der SBN Anweisungen, sind ihre Aufteilung und die damit verbundenen Parameter zu beachten. So geben diese vier Konstanten, - 16 - Projektbericht Single Instruction Computer -- Befehlswortlaenge CONSTANT instr_wl_c : INTEGER := instr_argl1_c + instr_argl2_c + instr_argl3_c; -- 1. Argument in Instruktion hat n Bit CONSTANT instr_argl1_c : INTEGER := 5; -- 1. u 2. Arg => maxAnz addrbare Datenworte CONSTANT instr_argl2_c : INTEGER := 5; -- 3. Argument => max Sprungweite +/CONSTANT instr_argl3_c : INTEGER := 4; an wie breit eine Anweisung insgesamt und ihre drei Argumente für sich sind. Da es sich bei den ersten beiden Argumenten um absolute Adressen handelt, müssen ihre Breiten der Anzahl der zur Verfügung stehenden Datenregister -- Anz. Datenworte im Datenspeicher CONSTANT reg_nr_c : INTEGER := 32; entsprechen, um alle adressieren zu können. Wie viele Programmzeilen möglich sind und welche Wertebereich dementsprechend der Programmzähler haben darf, wird durch -- Anz. Befehlsworte im Programmspeicher CONSTANT line_nr_c : INTEGER := 9; bestimmt. Unter diesen Bedingungen lässt sich das folgende Programm so interpretieren: A B C Programmzeile "00111 00111 0001", --> 1 "00111 00101 0001", --> 2 "00011 00011 0001", --> 3 "00011 00111 0001", --> 4 "00111 00111 0001", --> 5 "00111 00110 0001", --> 6 "00100 00100 0001", --> 7 "00100 00111 0001", --> 8 "00001 00010 0000" --> 9 Zeile 1 löscht den Inhalt von Adresse 7 und enthält eine Sprungweite von +1. Da das Ergebnis der Subtraktion nicht negativ ist, wird der Programmzähler inkrementiert. Die Sprungweite von +1 bewirkt ein inkrementieren des Programmzählers auch bei negativen Ergebnissen. Das Programm wird also unabhängig vom Vorzeichen der ermittelten Differenzen Schritt für Schritt bis Zeile 9 abgearbeitet. Zeile 2 bewirkt, dass der negierte Inhalt von Adresse 5 nach Adresse 7 geschrieben wird. Zeile 3 löscht den Inhalt von Adresse 3. Zeile 4 schreibt nun den zweimal negierten Inhalt von Adresse 5 aus Adresse 7 nach Adresse 3. Zeile 5 bewirkt ein erneutes Löschen des Inhaltes der Adresse 7. Zeile 6 schreibt den negierten Inhalt von Adresse 6 nach Adresse 7. Zeile 7 löscht den Inhalt der Adresse 4. Zeile 8 schreibt nun den zweimal negierten Inhalt von - 17 - Projektbericht Single Instruction Computer Adresse 6 aus Adresse 7 nach Adresse 4 und Zeile 9 bewirkt eine Endlosschleife auf dieser Anweisung und damit eine theoretische „HALT“ Anweisung. Dies wird dadurch erreicht, dass die Inhalte der Adressen 1 und 2 fest auf 0 und 1 definiert sind und bei einem Reset so initialisiert werden. -- Datenregister 1,2 fuer HALT initialisiert data_reg_s (1) <= "0000000000000000"; data_reg_s (2) <= "0000000000000001"; Sehen sie hierzu auch den „VHDL Quellcode des SIC“ im Anhang. Diese Adressen können für die festen Werte 0 und 1 verwendet werden. Sie dürfen aber nie überschrieben werden, was durch ein bedingtes Zurückschreiben der Ergebnisse erreicht wird: IF i > data_reg_reserv_nr_c THEN data_reg_s(adr_a_s) <= diff_vec_s; data_reg_s(0) <= diff_vec_s; -- Ergebnis zur Ausgabe END IF; Die Konstante „data_reg_reserv_nr_c“ enthält dabei die höchste, für derartige Zwecke reservierte Adresse und schließt diese und alle darunter vom Zurückschreiben des Ergebnisses aus. Dies wiederum bedingt, dass alle reservierten Adressen von 0 an aufsteigend definiert werden. In diesem SIC ist zusätzlich die Adresse 0 als Standard Ergebnis-Register definiert. Diese Adresse ist zum Zwecke der Visualisierung mit einer Reihe von LEDs verknüpft. 5.4 Daten für Programme in SBN Um dem SBN Programm einen Sinn zu geben, sind definierte, nicht zufällige Daten innerhalb des Datenspeichers notwendig. Um dies zu erreichen wird der Datenspeicher beim einem Reset komplett initialisiert. Die Initialisierung läuft dabei in drei Abschnitten ab. Als erstes wird der Datenspeicher, abgesehen von den unteren, für fest definierte Werte reservierten Adressen, mittels einer Schleife gelöscht. Dann werden die Werte für die reservierten Adressen gesetzt und im letzten Schritt werden die eigentlichen Programmdaten gesetzt: -- Datenspeicher loeschen FOR i IN data_reg_reserv_nr_c + 1 TO reg_nr_c - 1 LOOP data_reg_s(i) <= (OTHERS => '0'); END LOOP; -- Die Register 0, 1 und 2 sind per Definition reserviert -- Standard Ergebnis-Register (0) initialisieren data_reg_s (0) <= "1010101010101010"; -- Datenregister 1,2 fuer HALT initialisiert data_reg_s (1) <= "0000000000000000"; data_reg_s (2) <= "0000000000000001"; - 18 - Projektbericht Single Instruction Computer -- Alle weiteren Register sind abhaengig vom Programminhalt -- Programmdaten initialisieren data_reg_s (5) <= "0000000000011111"; data_reg_s (6) <= "0000000000000001"; data_reg_s (7) <= "0011110010000111"; Diese Vorgehensweise ist so gewählt, damit die fest definierten Adressen nicht unnötig zweimal gesetzt werden und damit möglichst unzweideutige Initialisierungen erreicht werden. Im Falle der Programmdaten ist dies jedoch zunächst unpraktisch, da jedes Programm andere Adressen mit anderen Inhalten verwenden kann. Aus dem oben gezeigten Abschnitt ist zu erkennen, dass das Standard Ergebnis-Register mit dem Wert –21846 initialisiert wird. Dieser Wert (1, 0 Wechsel) ist lediglich wegen seiner Auffälligkeit zum leichteren Identifizieren bei der Simulation und beim Testen gewählt. Die Register 1 und 2 werden mit den Werten 0 und 1 initialisiert, so dass eine SBN Anweisung 1,2,0 (00001 00010 0000), die diese Register in dieser Reihenfolge verwendet immer zu einem negativen Ergebnis führt und einen Sprung bewirken wird. Bei einer Sprungweite von 0 wird dann eine Endlosschleife bewirkt. Dem aktuellen Wert des Programmzählers wird eine 0 hinzuaddiert und die gleiche Anweisung wird erneut ausgeführt. Diese Anweisung in Kombination mit diesen fest definierten Adressen im Datenspeicher wird hier als HALT Anweisung bezeichnet. Schlussendlich werden die Adressen 5 bis 7 mit Programmdaten initialisiert. Adresse 5 erhält dabei den Wert 31 und Adresse 6 den Wert 1. Diese beiden Werte sollen im Verlauf des Programms in die Adressen 3 und 4 kopiert werden. Adresse 7 enthält einen Wert, der zufällig gewählt ist, um die Notwendigkeit des Löschens von Adressen, die als temporäre Register verwendet werden sollen, zu verdeutlichen. 6. Simulation und Test des SIC Neben den üblichen Schreibtischtests, ist die Simulation mit einer entsprechenden Software essentiell (hier ModelSim von Model Technology Inc.), um die Funktionalität und den Ablauf zu überprüfen. ModelSim bietet eine Fülle von Informationen, verschiedene Möglichkeiten zur Präsentation des Models und der Daten, sowie die Möglichkeit den Ablauf schrittweise, abschnittsweise oder komplett zu simulieren. Die Simulation benötigt Testdaten in Form von zu simulierenden Eingangssignalen. Diese können manuell oder über eine Testbench eingestellt werden. 6.1 Testbench Eine Testbench kann man sich wie eine Experimentier-Schaltung vorstellen, in die die zu untersuchende Schaltung eingesetzt wird. Die Testbench kann Eingangssignale für die zu testende Schaltung erzeugen, die Ausgangssignale und internen Signale der Schaltung aufzeichnen und mit vorgegebenen Daten vergleichen. Hierzu besteht auch die Möglichkeit auf Dateien zuzugreifen oder aufgezeichnete Daten in Dateien zu speichern. Durch Meldungen kann während des Ablaufs der Simulation die gedachte Korrektheit oder Falschheit der Ergebnisse bekannt gegeben werden. Mittels einer - 19 - Projektbericht Single Instruction Computer Testbench kann also eine komplexe, voll automatische Testumgebung geschaffen werden. Eine solche Testbench ist jedoch von ihrem Schwierigkeitsgrad und von ihrem Aufwand her ein eigenes Projekt und wird daher hier nicht erstellt. Die Testbench, die hier verwendet wird, dient lediglich der Erzeugung der notwendigen Eingangssignale Clock (clk_i) und Reset (reset_i). An dieser Stelle soll nur der gewünschte Verlauf der Eingangssignale beschrieben werden. Der VHDL Code der Testbench selber ist im Anhang zu finden und die dazugehörige Datei befindet sich im Projektverzeichnis unter .../Software/ . Die in diesem Fall verwendeten Parameter erzeugen für eine Gesamtlaufzeit von 334,1 µs ein symmetrisches Taktsignal mit einer Periodendauer von 20 ns (entsprechend 50 MHz). Das Taktsignal beginnt mit einer logischen 0 und ändert dann seinen Zustand zu logisch 1 nach 10 ns. Das Resetsignal ist mit Beginn der Simulation für 25 ns auf 0 gesetzt. Es wird danach auf 1 gesetzt und wird bis zum Ende der Simulation gehalten. Abbildung 3: ModelSim – Wave Fenster (Signalverlauf der Simulation) Abbildung 3 zeigt den Signalverlauf innerhalb des SIC für die ersten 45 ns. Es wird das in 5.3 beschriebene Programm zum Kopieren zweier Adressen mit den angegebenen Werten, zusammen mit der oben beschriebenen Testbench, verwendet. ModelSim bietet an, die im Signalverlauf darzustellenden Signale explizit auszuwählen. Hier sind zunächst alle vorhandenen Signale dargestellt. Im weiteren Verlauf wird auf ein paar Signale, die zur Beschreibung des Ablaufs nicht erforderlich scheinen verzichtet. Die Signalnamen, die ganz links mit einem + gekennzeichnet sind, sind Vektoren von Signalen oder fassen gar mehrere Vektoren zu einer Gruppe zusammen. Dies ist an dem - 20 - Projektbericht Single Instruction Computer data_reg_s Signal zu erkennen. Es stellt den Inhalt des Datenspeichers dar und wird zunächst als ein Signal abgebildet. Ein Klick auf das Plus-Symbol klappt die darunter enthaltenen Vektoren bzw. Register auf. Jedes einzelne wiederum ( (0) bis (7) ) kann weiter aufgelöst werden, bis die Signale jedes einzelnen beteiligten Bits dargestellt sind. Sind Signale von vorneherein als Integer angelegt, so werden ihre Werte auch direkt dezimal angegeben (z.B. pc_s). Per Menü oder einem Rechts-Klick auf ein Signal können auch andere Darstellungsformen/ Basen gewählt werden. So ist für den Bitvektor diff_vec_s eine binäre Darstellung die Voreinstellung (weil als STD_ULOGIC_VECTOR definiert), unter RADIX kann jedoch z.B. eine dezimale Darstellung gewählt werden. Dies gilt in diesem Fall für das dazugehörige Signal diff_s. Es sei noch erwähnt, dass für die Wiedergabe in diesem Bericht eine andere Farbgebung gewählt wurde. ModelSim stellt die Signalverläufe normalerweise in grün auf schwarzem Hintergrund dar. In Abbildung 3 sind zunächst die initialisierten Werte und der Einfluss des Resets zu erkennen. Durch die Initialisierung des Programmzählers mit dem Wert 1, sind die Adressen, Operanden und das Ergebnis der Subtraktion, sowie der folgende Programmzählerstand von vornherein bekannt. Im realen Verlauf sollte die Initialisierung jedoch erst mit der ersten Änderung des Taktes (hier nach 10 ns) geschehen und danach würden alle diese Werte „bekannt“ sein. Da der Simulator jedoch mit einer eigenen Initialisierung (i.d.R. der kleinste Wert im zugelassenen Wertebereich) arbeitet, steht der Programmzähler bereits zu Beginn der Simulation auf eins. Nach 25 ns endet der Reset und das Signal wird auf logisch 1 gesetzt. Mit der nächsten positiven Taktflanke ( bei 30 ns) wird damit die erste Instruktion umgesetzt. Das Ergebnis wird zurückgeschrieben (data_reg_s (7)) und in das Standard ErgebnisRegister data_reg_s (0) geschrieben. Außerdem erhält der Programmzähler (pc_s = 2) seinen neuen Wert. Damit werden die nächsten Adressen im Datenspeicher (adr_a_s = 7, adr_b_s = 5) ermittelt, die neuen Operanden bestimmt (op_a_s = 0, op_b_s = 31) und das neue Ergebnis errechnet (diff_s = -31, diff_vec_s = 1111111111100001). Die möglichen Folgewerte für den Programmzähler (pc_inc_s = 3, pc_jmp_s = 3) sind ebenfalls bereits ermittelt. Abbildung 4: Simulation am Ende des SBN-Programms - 21 - Projektbericht Single Instruction Computer Abbildung 4 zeigt die Ausführung der Programmzeilen 8 und 9, sowie die erste Wiederholung von Zeile 9. Die vorletzte Instruktion (instruction_s) sagt dabei aus, dass die Operanden aus den Adressen 4 und 7 voneinander subtrahiert werden sollen und bei einem negativen Ergebnis dem Programmzähler 1 hinzuaddiert werden soll. Dementsprechend wird als nächstes Anweisung 9 ausgeführt. Das Ergebnis aus 8, also der Wert 1, wird dann in Adresse 4 und das Standard Ausgabe-Register (Adresse 0) geschrieben. Folglich wird auch auf den Ausgabeport der Wert 1 gegeben und lediglich eine LED sollte leuchten. Folgend wird die HALT Anweisung wiederholt bis zum Abbruch der Simulation ausgeführt. Die Instruktion in Programmzeile 9 besagt, dass die Werte der vordefinierten Adressen 1 und 2 voneinander zu subtrahieren sind und bei einem negativen Ergebnis der Programmzähler unverändert bleiben soll; womit die selbe Instruktion erneut ausgeführt wird. Da in den Adressen 1 und 2 die Werte 0 und 1 abgelegt sind, ist das Ergebnis der Subtraktion immer –1 und damit negativ. Da die vordefinierten Adressen vom Zurückschreiben des Ergebnisses ausgeschlossen sind, bleibt das Standard Ergebnis-Register unverändert und es leuchtet auch weiterhin nur eine LED. Der Signalverlauf gibt diesen Umstand in sofern wieder, als dass sich die Signalzustände auch mit der nächsten positiven Taktflanke nicht mehr ändern. Nach zufriedenstellendem Abschluss der Simulation der Schaltung, wird die FPGAProgrammier-Datei erzeugt und auf das FPGA geladen. Vorausgesetzt ein Taktsignal mit nicht zu hoher Frequenz, eine Betriebsspannung und alle weiteren Ein- und Ausgangssignale (Reset-Schalter und LEDs) sind korrekt angeschlossen, sollte nach einem Reset das Programm in Kürze abgelaufen sein. Da der Inhalt des Speichers zu diesem Zeitpunkt noch nicht ausgelesen werden kann, ist die Anzeige durch die LEDs die einzige Möglichkeit der Verifikation. Diese sollten den jeweils zuletzt in eine nicht reservierte Adresse zurückgeschriebenen Bitvektor anzeigen. Entsprechend der gewählten Pinbelegung (siehe User Constraints File 10.3) sind die Zustände der LEDs in entsprechender Reihenfolge zu interpretieren; hier ist das LSB an led_o(0) an Pin 48 zu finden, das MSB an led_o(15) an Pin 29. 7. SBN Assembler Wie eingangs erwähnt, soll eine Entwicklungsumgebung entstehen, die es möglich machen soll einen SIC auf Assembler- oder Hochsprachen-Ebene zu programmieren. Für den Abschluss dieser Projektaufgabe ist die Planung und Umsetzung eines Assemblers vorgesehen, der nach Möglichkeit als Basis für die Weiterentwicklung oder einer Erweiterung zu einer Entwicklungsumgebung dienen soll. 7.1 Übersetzungs-Hierarchie Eine allgemeine Beschreibung der Vorgehensweise und der typischerweise in der Übersetzung vorkommenden Abläufe wird hier dargestellt. Die folgende Grafik ist aus [CPOAD] Kapitel 3.9 entnommen und beschreibt den Weg eines HochsprachenProgramms von der Programmierung bis zur tatsächlichen Ausführung durch eine CPU. Im Anschluss an die Erläuterungen der einzelnen Komponenten wird das Modell auf - 22 - Projektbericht Single Instruction Computer diese Aufgabe reflektiert und dabei erläutert welche Komponenten derzeit betrachtet werden können und welche gegebenenfalls zu erweitern oder einzuschränken sind: Hochsprachen Programm z.B. „C“ Code Compiler Programm in Maschinensprache Assembler Objekt: Maschinensprachen Modul Objekt: Bibliothek Routinen (Maschinensprache) Linker Ausführbar: Maschinensprachen Programm Loader Programmspeicher Abbildung 5: Starting a program - 23 - Projektbericht Single Instruction Computer Compiler o Umsetzung eines Hochsprachen-Programms (ein paar Zeilen) in ein Maschinensprachen-Programm – eine symbolische Form von dem, das die Maschine „versteht“ (viel mehr Zeilen) Assembler o Primäre Aufgabe eines Assemblers ist es den Maschinensprachen Code in einen Maschinen-Code umzusetzen bzw. aus dem Maschinensprachen Programm eine Objekt Datei zu machen. Diese umfasst Maschinen Befehle Daten Informationen, die nötig sind, um die Befehle im Speicher später richtig zu platzieren o Der Assembler muss zu allen verwendeten Sprungmarken (Labels) die korrekte Adresse ermitteln. Diese Information wird dann in der Symbol Tabelle (Symbol Table) festgehalten. o Eine Objekt-Datei eines UNIX Systems z.B. enthält: Header – Größe und Position eventueller anderer Teile der Objekt-Datei Text Abschnitt – Maschinensprachen Code Daten Abschnitt – Statische und dynamische Daten Zuordnungsinformationen (Relocation information) – Befehle und Daten, die von absoluten Adressen im Speicher abhängig sind Symbol Tabelle – Bisher nicht aufzulösende, nicht definierter Sprungmarken Debugging Information – Informationen zur Fehlerbearbeitung / darüber wie die Module übersetzt wurden o Pseudo-Befehle – gängige Variationen von Maschinensprachen Befehlen, die nicht in Hardware implementiert werden müssen o Z.B. stellt die MIPS Hardware sicher, dass Register $zero immer den Wert 0 enthält und dieser nicht geändert werden kann ⇒ Durch die Verwendung dieses Registers sind Befehle umsetzbar, die gar nicht in der Architektur implementiert sind; häufig genanntes Beispiel dafür ist die move-Anweisung: move $t0, $t1 wird umgesetzt in add $t0, $zero, $t1 Register $t0 erhält damit den Wert 0 + den Wert in Register $t1, dies entspricht der Anweisung move $t0, $t1. ⇒ Pseudo-Befehle bereichern den Satz an Maschinensprachen Befehlen, den die Maschine kennt. Im obigen Beispiel lediglich auf Kosten eines einzelnen Registers, dass reserviert ist. ⇒ Der Assembler akzeptiert Pseudo-Befehle und maschinen-spezifische Befehle. o Assembler sind in der Lage Zahlen und Wertangaben in einer Reihe von Basen zu erkennen – typischerweise werden dezimale, hexadezimale und binäre Zahlen erkannt. - 24 - Projektbericht Single Instruction Computer Linker o Unabhängig voneinander kompilierte und assemblierte Prozeduren werden durch den Linker miteinander verknüpft und zu einem Ganzen zusammengesetzt. o Module werden symbolisch im Speicher platziert. o Adressen von Daten- und Befehls-Marken werden ermittelt. o Interne und externe Referenzen werden gepatched: o Die Zuordnungsinformationen und Symbol Tabellen jedes Objekt Moduls werden benutzt um alle unbestimmten Marken zu bestimmen (alte Adressen werden mit neuen aktualisiert). o Es werden alle Speicherbereiche ermittelt, die die Module belegen werden. o Alle absoluten Referenzen müssen neu zugeordnet werden um ihre wahre Position zu reflektieren. o Erzeugt eine ausführbare Datei. Sie hat das gleiche Format wie eine ObjektDatei, bis auf dass keine ungelösten Referenzen, Zuordnungsinformationen, Symbol Tabellen oder Debugging Informationen mehr enthalten sind. Loader o Das Betriebssystem liest die ausführbare Datei in den Speicher ein 1. Lesen des Exe-File Kopfes – ermitteln der Größen von Text und Daten Segment 2. Adressbereich erzeugen 3. Anweisungen und Daten kopieren 4. Parameter des Hauptprogramms auf den Stack kopieren 5. Initialisierung der Register und Setzen des Stack-Pointers auf die erste freie Position 6. Sprung zu einer „Start-Up“ Routine, die die Parameter kopiert und dann das Hauptprogramm aufruft. Nachdem das Hauptprogramm zurückkehrt, beendet die „Start-Up“ Routine das Programm mit einem „exit system call“. 7.2 Pseudo-Befehle Bei den folgend aufgeführten Pseudo-Assembler Anweisungen, wird davon ausgegangen, dass die Speicheradressen 0 bis 2 per Definition nicht beschrieben werden können und die Adressen 1 und 2 die Werte 0 und 1 enthalten. Darüber hinaus wird davon ausgegangen, dass Speicheradresse 0 das Standard Ergebnis-Register ist und immer dann mit dem Ergebnis der zuletzt ausgeführten Subtraktion neu beschrieben wird, wenn die erste Quelladresse nicht eine der Adressen 0 bis 2 ist. Die in den SBN Anweisungen verwendeten Adressen 0 bis 2 sind absolute Angaben, A und B sind beliebige andere absolute Adressen und „tmp“ steht für eine zur Zeit frei verfügbare Adresse, die als temporäre Adresse genutzt werden kann. Die Sprungweite ist entweder direkt und absolut angegeben oder als Argument C dargestellt. Für C soll 1 angenommen werden, wenn nicht explizit angegeben. - 25 - Projektbericht Single Instruction Computer PseudoBefehl Umsetzung in SBN Anweisungen Beschreibung und Synonyme HLT SBN 1, 2, 0 ADD SBN tmp, tmp, 1 SBN tmp, B, 1 SBN A, tmp, C SBN A, B, C Endlosschleife des Programms an dieser Programmzeile // HALT, HLD, HOLD Inhalt von B wird zu dem Inhalt von A nach A addiert // SUB NOP MOV INC DEC SBN 2, 1, 1 SBN tmp, tmp, 1 SBN tmp, B, 1 SBN A, A, 1 SBN A, tmp, C SBN tmp, tmp, 1 SBN tmp, 2, 1 SBN A, tmp, C SBN A, 2, C Inhalt von B wird von dem Inhalt von A nach A subtrahiert // SBN ohne Rückschreiben und ohne Sprung // Inhalt von B wird nach A kopiert, der Inhalt von A wird überschrieben // MOVE, CP, COPY Inhalt von A wird inkrementiert // INCR Inhalt von A wird dekrementiert // DECR Tabelle 1: Umsetzung von Pseudo-Befehlen in SBN (so far) 7.3 Konzept für einen Assembler in Perl Das hier vorgestellte Konzept ist ein erster Vorschlag zur Umsetzung eines Assemblers, der gängige Assembler-Befehle (hier Pseudo-Befehle) in SBN Anweisungen übersetzt und diese schlussendlich in eine, für den SIC verständliche, Form bringt. Bei gegenwärtigem Stand (Speicher wird FPGA-intern realisiert) sind wesentliche Unterschiede zu einer späteren Version des Systems zu berücksichtigen. Ziel ist es den SIC auf dem FPGA als CPU einzusetzen, Programm- und Datenspeicher stehen extern daneben über ein Bussystem zur Verfügung und können variabel mit unterschiedlichen Programmen und Daten geladen werden, ohne dass ein neuer SIC auf das FPGA geladen werden muss. Dies sollte beim Programmieren eines Assemblers beachtet werden. Die Assembler-Produkte sollten daher so erstellt werden, dass eine Anpassung des Assemblers an eine spätere Version des SIC nicht ein komplett neues Programm erfordern. Wesentliche Aufgaben des Assemblers sind: 1. 2. 3. 4. 5. Parametrierung des SIC Übersetzung des Pseudo-Codes Erstellung des Daten-Files Erstellung des Programm-Files Einbindung der Files in den statischen VHDL-Code bzw. Erstellung der vollständigen VHDL-Datei - 26 - Projektbericht Single Instruction Computer Wesentliche Ein- und Ausgabedateien sind: I. Datei mit statischem VHDL Code II. Assembler Quelldatei mit Pseudo-Code III. Datei mit dynamischem VHDL Code IV. Vollständige VHDL-Datei zur Erzeugung des Programmier-Files mit WebPACK VHDL Datei (dynamisch) Parameter SIC Parametrieren SIC Parameter VHDL Datei (statisch) Programmund Daten-File Pseudo-Code übersetzen PseudoAssembler Programm zusammenführen Bericht VHDL Datei (SIC vollständig) Diagramm 1: Datenfluss durch den Assembler Das Datenfluss-Diagramm zeigt die wesentlichen Ein- und Ausgabedaten und die Reihenfolge, in der sie verarbeitet oder erstellt werden. Das Assemblieren des PseudoCodes stellt einen sequentiellen Prozess dar, der im Diagramm widergespiegelt wird. Soweit dieser Vorgang erfolgreich verläuft, wird diese Folge eingehalten. Eingabefehler, unlesbare oder nicht auffindbare Dateien, sowie syntaktische Fehler innerhalb des Pseudo-Codes sollten selbstverständlich berücksichtigt werden und führen zu Abbrüchen dieser Sequenz. Es können aus jedem Prozess im Prinzip drei Pfade zu einem Abbruch des Vorgangs führen. Eingabe, Verarbeitung und Ausgabe sind diese drei „Austrittstellen“. So sollten z.B. unsinnige Parameter-Eingaben abgefangen werden, die Datei mit dem Pseudo-Assembler Programm muss vorhanden sein, beim Übersetzen des Pseudo-Codes muss die Syntax stimmen und die Ausgabedateien müssen sich erzeugen lassen können. Zunächst werden die Daten benötigt, nach denen der SIC dimensioniert werden soll. An dieser Stelle kann eine Standard-Konfiguration angeboten werden, die bekanntermaßen funktionstüchtig ist. Diese Daten fließen sozusagen als Kopf-Daten zusammen mit dem Pseudo-Code in den Übersetzungsprozess ein. Sie können auch optional bereits in dieser Datei enthalten sein. Nach der Übersetzung stehen die veränderbaren Inhalte des SIC-VHDL-Codes fest und können mit den feststehenden Bereichen des SIC-VHDL- 27 - Projektbericht Single Instruction Computer Codes zusammengeführt werden. Ein abschließender Bericht kann Auskunft über den Erfolg des Vorganges geben und die vollständige SIC-VHDL Datei steht dann zur Weiterverarbeitung durch WebPACK zur Verfügung. 8. Ergebnisse und Diskussion Im Verlauf der Arbeit an dem SIC stellt sich an mehreren Stellen heraus, dass besonders die unterschiedlichen Abstraktionsebenen ein hohes Maß an Hintergrundwissen erfordern, um Zwischenergebnisse und Endprodukt richtig deuten zu können. Angefangen mit der Deutung der SBN Anweisung und ihrer Umsetzung in VHDL Code, bedient man sich, der Einfachheit und Verständlichkeit halber, eines hochsprachlichen Ansatzes. Es zeigt sich aber bald, dass Synthese- und Implementierungs-Prozess diesen vom Verständnis her nicht 1 zu 1 umsetzen. Zu diesem Zeitpunkt ist es entscheidend, die Bedeutung von „Concurrent Statements“ und den Unterschied zwischen Schaltnetzen und Schaltwerken verstanden zu haben. Die zeitlichen Abfolgen der „Geschehnisse“ im FPGA und ihre Darstellung in VHDL sind nicht einfach auf die gewohnten, größtenteils linearen Strukturen eines HochsprachenProgramms zu übertragen. Fährt man nun mit der Simulation fort, muss man einfach wissen, dass ModelSim alle verwendeten Signale zunächst für sich initialisiert. Dies ist wiederum abhängig von der Einschränkung der Wertebereiche der Signale. Ist ein Signal z.B. einfach als Integer ohne Angaben zum Wertebereich definiert, so nimmt die Software einen 32-Bit-Wert an. Bei der Initialisierung wird dann immer der kleinste gültige Wert angenommen oder bei STD_LOGIC Signalen „U“ wie undefined. Diese Initialisierung findet völlig unabhängig vom weiteren gedachten Ablauf statt. Erst danach startet die Simulation und führt gegebenenfalls den vorgesehenen Reset durch und initialisiert die Schaltung dann wunschgemäß, doch bis dahin können schon die ersten Probleme entstanden sein. Wenn z.B. der Programmzähler nicht auf den korrekten Bereich (nämlich aller möglichen Programmzeilen im „Programmspeicher“) eingeschränkt ist, dann wird dieser auf einen Wert initialisiert, der auf eine nicht vorhandene Programmzeile verweist. Auf der anderen Seite ist diese erste Initialisierung, die ModelSim für sich ausführt, später nicht in der Hardware wiederzufinden. Sie ist nicht angelegt oder vorgesehen und bekanntermaßen befinden sich alle Flip-Flops einer digitalen Schaltung nach dem Einschalten in einem undefinierten Zustand. Dies ist auch der Grund dafür, warum der Datenspeicher beim Reset komplett gelöscht bzw. initialisiert werden muss. Des weiteren fällt auf, dass ModelSim bei dem Umfang an Informationen und der verschiedenen Möglichkeiten der Darstellung (viele Fenster, verschiedene Darstellungsformen, RADIX-Einstellung für Signale) ein wenig die Übersichtlichkeit verloren geht. Man braucht Zeit um sich einzugewöhnen und gute Augen um mit den teilweise sehr kleinen Darstellungen zurecht zu kommen. Hat man dann das System verstanden und findet sich auch zurecht, würde man z.B. gerne die Signalverläufe auf einfach Weise kommentieren und speichern. Diese Möglichkeit ist jedoch leider nicht gegeben. Etwas Abhilfe schaffen da die Möglichkeiten sich nur die interessanten Signale auszusuchen, eine geeignete Basis (RADIX) für die Wertangaben zu wählen, bestimmte Farben zu vergeben und sogenannte „Bookmarks“ einzufügen. Diese Bookmarks können betitelt werden und beziehen sich auf einen frei auswählbaren Abschnitt des Signalverlaufs, können aber auch keine Kommentare enthalten. - 28 - Projektbericht Single Instruction Computer Überschaut man alle Abschnitte dieser Arbeit, so stellt man fest, dass zumindest unter Einbeziehung eines Assemblers, sehr verschiedene Bereiche in diese Aufgabe mit einfließen. Angefangen bei der Verwendung einer umfangreichen Entwicklungsumgebung, verschiedener Software und Programmiersprachen, bis hin zum Einsatz von Hardware. Dies alles ist trotz der zunächst einfach erscheinenden Fragestellung eine umfangreiche Aufgabe. Mir hat die Arbeit an diesem Thema aber auf jeden Fall Spaß gemacht, auch da es so abwechslungsreich ist und die Arbeit für sich als abgeschlossener Schritt eine „runde Sache“ darstellt. Ich hoffe, das sie als Grundlage für weitere Arbeiten zum Thema SIC hilfreich ist. 8.1 Zusammenfassung Das Reduzieren des Befehlssatzes eines Prozessors auf nur eine einzige Instruktion unter Beibehaltung ausreichender Funktionalität ist möglich. Der genaue Umfang dieser Funktionalität ist durch weitere Untersuchungen und die Entwicklung weiterer PseudoBefehle zu ermitteln. Der für diese Reduktion erforderliche Aufwand in Software, ist der Faktor, der diesem „Gewinn“ in Hardware gegenübersteht. Auch hier sind weitere Untersuchungen nötig, um Aussagen darüber treffen zu können, welche Größen diese „Gegengewichte“ haben. Die Anzahl der bereits zu diesem Thema durchgeführten Arbeiten (s. Quellen und SIC_Sites) zeigt, dass dieses Thema wissenschaftlich interessant ist und im Bereich der Lehre gute Möglichkeiten bietet durch Experimente neue Erkenntnisse zu gewinnen oder zu vertiefen. Der während dieser Arbeit erstellte SIC kann durch folgende Eckdaten beschrieben werden: Parameter • • Datenwortlänge bzw. Anzahl der Bits für Datenwörter Befehlswortlänge bzw. Anzahl der Bits für SBN Anweisungen zusammengesetzt aus: Anzahl der Bits für das 1. Befehlsargument (A) Anzahl der Bits für das 2. Befehlsargument (B) Anzahl der Bits für das 3. Befehlsargument (C) • Nummer des letzten reservierten Registers (beginnend bei Datenwort Adresse 0) Architektur • • • • Getrennter Daten- und Programmspeicher Teilweise Implementierung von Pseudo-Befehlen durch Verwendung fest definierter Daten Adressen mit fest definierten Inhalten Fest definiertes Standard Ergebnis Register (Adresse im Datenspeicher) mit direkt verbundener Ausgabe auf LEDs als Anzeige Definierter Start von Programmen durch Reset, stabiler „Stopzustand“ durch HALT - 29 - Projektbericht Single Instruction Computer Synthese Ergebnisse Macro Statistics # Registers 16-bit register 4-bit register 1-bit register # Multiplexers 15-bit 8-to-1 multiplexer 2-to-1 multiplexer # Adders/Subtractors 15-bit subtractor 4-bit adder # Comparators 15-bit comparator less : 77 :1 :1 : 75 : 49 :2 : 47 :3 :1 :2 :1 :1 Minimum period: 16.704ns (Maximum Frequency: 59.866MHz Dies sind die wesentlichen Synthese-Ergebnisse bei der Verwendung des SyntheseProgramms von WebPACK Version 4.1 unter den nach der Installation gültigen Einstellungen und der Synthese des hier verwendeten VHDL-Codes. 8.2 Letzter Stand Im Verlauf der Tests und letzten Änderungen an dem VHDL Code stellt sich heraus, dass die verwendete Version der Entwicklungsumgebung bzw. des SyntheseProgramms und ihrer Einstellungen erheblichen Einfluss auf die Ergebnisse haben. So wird z.B. das von mir als „widersprüchlich“ bezeichnete Schleifen-Konstrukt im zweiten Abschnitt des Prozesses (zurückschreiben des Ergebnisses) unnötig, wenn die von mir privat eingesetzte Version 4.1 von WebPACK verwendet wird. Diese Version synthetisiert dann ohne Fehlermeldungen und Warnungen. Auf der anderen Seite sind die Ergebnisse für die maximalen Taktraten der Schaltungen nach der Synthese auf meinem PC deutlich geringer als die, die mit der älteren Version von WebPACK im Digitaltechniklabor erreicht werden. Dies ist jedoch möglicherweise auf unterschiedliche Konfigurationen des Synthese-Prozesses zurückzuführen. Eine letzte Änderung des VHDL Codes ergibt, dass der Programmspeicher bereits durch geringfügige Änderungen als änderbarer Speicher angelegt werden kann, der bei einem Reset ähnlich dem Datenspeicher initialisiert wird. Eine entsprechende Datei mit dem Namen „sip_prog_mem.vhd“ befindet sich im Projektverzeichnis unter .../Software/ . - 30 - Projektbericht Single Instruction Computer 9. Ausblick Projekt „SIC“ Die nächste wesentliche Aufgabe innerhalb dieses Projektes ist es, den SIC so einzurichten, dass eine Kommunikation mit einem PC z.B. über den Parallelport stattfinden kann. Ziel ist es die SIC-Programme flexibler zu wechseln und getrennt von der SIC Architektur in das FPGA zu laden und den Datenspeicher nach der Ausführung eines SIC-Programms auszulesen. Um dies zu erreichen wird folgendes Konzept vorgeschlagen: Es sind drei Teilaufgaben zu bearbeiten: 1. Anpassen der SIC Architektur an den Vorgang des Datenaustausches 2. Erstellen einer Software, die das Laden der SIC-Programme und Auslesen des Datenspeichers von einem PC aus ermöglicht 3. Anpassen des Assemblers zur Erzeugung von „SICexe“ Files, die nur noch den direkten binären Programmcode enthalten Es ist bekannt, dass im Rahmen des DGT-L bereits Versuche durchgeführt wurden, die eine Kommunikation zwischen PC-Tastatur und Experimentier-Bord per Parallelport beinhalteten. Soweit bekannt ist, wurde dabei ein „Parallelport-Protokoll“ auf dem FPGA implementiert, dass die ankommenden Daten angenommen und zur Anzeige gebracht hat. Diese Arbeit könnte einen wesentlichen Teil der Aufgabe 1 darstellen. Es wäre denkbar, dass nach einem Reset automatisch versucht wird eine Verbindung zu einer „Download-Software“ (Aufgabe 2) herzustellen, die nach einem Handshake die Programmdaten zur Verfügung stellt und nach Programm-Ende die Daten aus dem Datenspeicher wieder entgegennimmt. SIC Design Der SIC in der vorliegenden Fassung lässt die Frage offen, wie eventuelle Fehlergebnisse behandelt werden sollen. Im einzelnen sind damit der Überlauf bzw. das Verlassen des gültigen Wertebereichs und ein Grenzwertproblem gemeint, das bei der Verwendung des 2’er-Komplements auftreten kann. In einem 16-Bit vorzeichenbehafteten Wertebereich ist der Bitvektor des Wertes –32768 identisch mit dem seines 2’er-Komplements. Bei folgender Subtraktion ist dieses Problem leicht zu erkennen: 32767 – (-)32768 = 65535 ergibt bei der Verwendung der üblichen Methode zur Subtraktion (Addition des 2’er-Komplements) folgende Bitvektoren: 0111 1111 1111 1111bin (32767dez) 1000 0000 0000 0000bin (-32768dez) wird umgesetzt zu seinem 2’er Komplement ⇒ + = und dann zum ersten Operanden addiert + 1000 0000 0000 0000bin (-32768dez) = 1111 1111 1111 1111bin (-1dez) - 31 - 1000 0000 0000 0000 0111 1111 1111 1111 0000 0000 0000 0001 1000 0000 0000 0000 Projektbericht Single Instruction Computer Eine Möglichkeit wäre es, den Assembler die sich ergebenden Differenzen auf ihre Korrektheit hin überprüfen zu lassen. Da dies in direktem Zusammenhang mit der Datenwortbreite steht, müsste diese Überprüfung ebenfalls durch diesen Parameter anpassbar sein. Hierbei würde sich das oben beschriebene Problem durch eine einfache Überwachung der Einhaltung des gültigen Wertebereichs erübrigen. Der Assembler bzw. Debugger könnte den Assemblier-Vorgang unterbrechen und alle fragwürdigen Wertepaare / Zeilen ausgeben oder markieren und dabei angeben, dass die gewünschten Ergebnisse außerhalb des gültigen Wertebereichs liegen. Eine wenn auch nur bedingt sinnvolle Lösung in Hardware wäre es, eine einfache Überprüfung der Bedingung für einen Overflow (Minuend und Subtrahend haben gleiches Vorzeichen, das Vorzeichen der Differenz ist verschieden dazu) mit darauffolgendem Sprung auf eine HALT-Anweisung durchzuführen, falls ein Overflow festgestellt würde. Dies würde jedoch nur indirekt darauf aufmerksam machen, dass ein Problem besteht. Eine Überprüfung des Programmzählerstandes könnte ebenfalls hinzugefügt werden. Die momentane Fassung beschränkt sich darauf zu hoffen, dass das Programm als letzte Anweisung einen Sprung auf sich selbst (HALT) enthält oder aber der Ablauf sich zu einer anderen Endlosschleife mit gültigen PC-Werten entwickelt. Der VHDL Code stellt eine funktionale Beschreibung dar, die tatsächliche Umsetzung in Hardware ist daraus nicht direkt abzulesen. Es ist daher nicht sofort ersichtlich, auf welche Art und Weise das Synthese-Programm z.B. den Subtrahierer bildet. Eine eventuelle Design-Optimierung ist weniger durch eine Änderung des VHDL Codes als durch eine Änderung der Synthese-Vorgaben zu erreichen, jedoch können strukturelle Änderungen der Beschreibung auch Einfluss auf die Umsetzung haben (siehe dazu auch die verschiedenen Quellen bezüglich VHDL – z.B. afirstlo.pdf Kap. 9 unter .../Andere_Quellen/VHDL/ im Projektverzeichnis). Der Assembler Die gewählten Parameterwerte des SIC liegen innerhalb bestimmter Grenzen. Diese Grenzen werden zunächst durch die Hardware bestimmt. In welchen Grenzen sich jedoch diese Werte bewegen können, ist möglicherweise empirisch zu ermitteln. Diese Grenzwerte sollten dann zur Überprüfung der verwendeten Werte neuer Designs durch den Assembler herangezogen werden. Aus den angegebenen Parametern ergibt sich auch der zulässige Wertebereich. Bei der Verwendung absoluter Werte, sollte der Assembler die Zulässigkeit überprüfen. Ebenso wie die einzelnen Operanden können die Ergebnisse der Subtraktionen oder in der Abstraktion darüber liegenden Berechnungen den zulässigen Wertebereich verlassen. Grundsätzlich sind auch Datenabhängigkeiten zu beachten. Für Sprünge deren Sprungweite die der maximalen Sprungweite (Argument C) überschreiten, ist eine geeignete Methode zur Erweiterung eines großen Sprunges zu mehreren kleinen zu finden. Auch hierbei ist die Abhängigkeit zu den SIC Parametern zu beachten. - 32 - Projektbericht 10. Single Instruction Computer Kurzanleitung Diese Kurzanleitung stellt nicht den Anspruch der Vollständigkeit. Sie soll aber die wesentlichen Schritte, die zur Erstellung der Datei führen, die zur Programmierung des FPGA verwendet wird, beschreiben. Dabei wird davon ausgegangen, dass 1. WebPACK (Version 4.1) und ModelSim XE (Version 5.5b) korrekt installiert sind. 2. In WebPACK ein Projekt korrekt angelegt ist - Device Family = Spartan2 - Device = xc2s200-5pq208 - Design Flow = XST VHDL und 3. In ModelSim ebenfalls ein Projekt korrekt angelegt ist - VHDL Datei des SIC dem Projekt hinzugefügt - VHDL Datei der Testbench dem Projekt hinzugefügt - VHDL Dateien nur mit der Endung „.vhd“ verwendet werden 10.1 WebPACK Projekt Manager Abbildung 6: Die WebPACK Entwicklungsumgebung - 33 - Projektbericht Single Instruction Computer Das zunächst größte Fenster (auf der rechten Seite) des Projektmanagers ist der Editor zum editieren des VHDL Codes. Hier können Änderungen direkt an dem VHDL Quellcode vorgenommen werden. Der Projektmanager verfolgt Veränderungen am Quellcode und reagiert automatisch auf das Speichern von Änderungen oder das Starten von z.B. dem Implementierungsprozess ohne vorher gespeichert zu haben. Folglich werden die Zustandsanzeigen (Haken, Ausrufungszeichen oder Kreuze) für die einzelnen Prozesse im „Process View“ wieder aktualisiert. Der „Process View“ bietet die vier übergeordneten Punkte/ Prozesse „Design Entry Utilities“ (für weitere Werkzeuge), „Synthesize“ (zur Synthese der gewünschten Hardware Komponenten), „Implement Design“ (Anpassung an das verwendete FPGA) und „Generate Programming File“. Prozesse bzw. ausführbare Entwicklungsschritte (ausführbare Teilprogramme) sind als zirkulierende Pfeile dargestellt. Ein Doppelklick auf einen solchen Pfeil startet den dazugehörigen Prozess. Solange an dem VHDL Code direkt gearbeitet wird ist es zunächst ausreichend die Syntax zu checken und bei größeren Veränderungen wiederholt die Synthese komplett zu starten. Ob das gewünschte Design in oder zu dem gewählten Baustein passt wird das Starten des Implementierungs-Prozesses beantworten. Verlaufen beide Hauptprozesse fehlerfrei, kann der Prozess zur Erzeugung der Programmier-Datei eingestellt und dann gestartet werden. Ein Rechtsklick auf diesen Prozess öffnet ein Menü, das unter dem Punkt „Properties“ die Eigenschaften und Einstellungen dieses Prozesses anzeigt. Unter den „General Options“ sollte zunächst nur „Run Design Rules Checker (DRC)“ und „ASCII Configuration File“ abgehakt sein. Dieser Prozess wird dann die Programmier-Datei „sip.rbt“ erzeugen. - 34 - Projektbericht 10.2 Single Instruction Computer ModelSim Simulator Abbildung 7: Alle Ansichten des ModelSim Simulators Nachdem die VHDL Dateien dem Projekt hinzugefügt worden sind, ist es wichtig sie in der richtigen Reihenfolge zu kompilieren. Dazu wird unter dem Menüpunkt „Projekt“ das Fenster „Compile Order“ geöffnet. Hier ist die Reihenfolge der Projektdateien so zu sortieren, dass der Code des SIC zuerst und der Code der Testbench danach kompiliert wird. Anschließend kann unter dem Menüpunkt „Projekt“ mit „Compile All“ der Kompiliervorgang gestartet werden. Nachdem der VHDL Code kompiliert wurde, werden im kleinen Projektfenster oben links unter dem Reiter „Library“ die angelegten Bibliotheken angezeigt. Mit einem Doppelklick auf das Symbol der Testbench, wird das komplette Design geladen. Anschließend wird unter dem Menüpunkt „View“ mit dem Punkt „All“ die in Abbildung 7 gezeigte Ansicht geöffnet. Da das Design nun geladen ist, erscheint der Reiter „sim“ im Projektfenster. Hier ist die „dut“ auszuwählen, woraufhin sich der Inhalt der beteiligten Fenster ändert. - 35 - Projektbericht Single Instruction Computer Um bei der Simulation den Signalverlauf angezeigt zu bekommen, müssen aus dem Fenster „Signals“ alle Signale, deren Signalverlauf von Interesse ist, ausgewählt und in das Fenster „Wave“ gezogen werden. Dies kann z.B. nach Drag & Drop Manier geschehen. Als letzter Schritt ist die Simulation unter dem Menüpunkt „Run“ mittels „Run –all“ zu starten. Zur leichteren Analyse des Signalverlaufs sollte das „Wave“ Fenster auf volle Größe gestellt werden. Des weiteren ist die Einstellung des Zooms und des gewählten Zeitabschnittes im Fenster zu beachten. 10.3 Pin-Zuweisung / UCF-Editor Als letzten Schritt vor der endgültigen Erzeugung der Programmier-Datei, ist die PinZuweisung vorzunehmen. Ohne eine konkrete Pin-Zuweisung werden die Ein- und Ausgangsports der VHDL-Entity auf unbestimmte Pins verteilt. Damit man also weiß an welches Pin z.B. das Clock-Signal anzuschließen ist, sollte mit dem UCF-Editor eine Zuweisung stattfinden. Dabei sind die durch die Hardware gegebenen Einschränkung zu beachten (s. a. Projektverzeichnis .../Andere_Quellen/SpartanBoardPläne/ PinBelegungen/), denn nicht jedes Pin kann z.B. für das Clock-Signal verwendet werden. Mittels eines einfachen Editors oder aber des User Constraints File Editors kann dann eine UC-Datei erstellt werden, die das konkrete Mapping der Ports zu den Pins beschreibt. Der Editor, der in Abbildung 8 gezeigt wird, kann über WebPACK aufgerufen werden. Dazu ist unter der Toolbox „Design Entry Utilities“ im „Process View“ der Eintrag „User Constraints“ aufzuklappen und dann per Doppelklick auf „Edit Implementation Constraints (Constraints Editor)“ das Programm zu starten. - 36 - Projektbericht Single Instruction Computer Abbildung 8: Constraints Editor Ist die Ansicht der Ports ausgewählt (Abbildung 8 Mitte links), dann sind alle in der Entity angegebenen Ports sichtbar. Durch einfaches Editieren des Feldes „Location“, kann für jeden Port ein bestimmter Pin des FPGA angegeben werden. Die Einträge sind so wie in der Abbildung zu sehen vorzunehmen. Mit einem großen „P“ und der Pinnummer direkt dahinter. Der Eintrag ist erst gültig, wenn entsprechend dem Eintrag eine Zuweisungszeile z.B. „ NET ``led_o<12>`` LOC = ``P33``; “angezeigt wird. Dazu ist einfach ein weiteres Location Feld anzuklicken oder mit Enter zu bestätigen. Dieses Ergebnis (UCF Datei) muss gespeichert werden. Nach dem Schließen des Editors wird man von WebPACK aufgefordert die bisherigen Projektergebnisse und Einstellung teilweise zurückzusetzen. Nach dem Reset ist dann der Übersetzungsvorgang zu wiederholen und das Starten des „Generate Programming File“ Programms erzeugt eine Datei zum Programmieren des FPGA, dass jetzt die neue PinZuweisung beachtet. 10.4 Download auf das FPGA Für den Download auf das FPGA wird ein separates Programm verwendet. Dieses Programm ist die „BEDLOAD.exe“, die sich der Vollständigkeit halber in dem Projektverzeichnis unter .../Software/Bedload/ befindet. Dieses Programm verwendet den Parallelport des PC zum Programmieren des FPGA. Es wurde lediglich auf dem PC im Digitaltechnik Labor eingesetzt, deswegen können an dieser Stelle keine näheren - 37 - Projektbericht Single Instruction Computer Informationen zur Installation oder Konfiguration gegeben werden, sofern dies notwendig sein sollte. Abbildung 9: Das BEDLOAD Interface Nach dem Start der BEDLOAD.exe, wird das in Abbildung 9 gezeigt Fenster geöffnet. Bei der Verwendung des Experimentier-Bords und der dazugehörigen ProgrammierEinheit, ist die Parallelport-Adresse 378 hex einzustellen. Unter dem Menüpunkt File, ist dann die von WebPACK erstellte Programmier-Datei „sip.rbt“ zu laden. Bevor nun der Download ausgeführt wird, muss man sich noch vergewissern, dass das Experimentier-Bord mit Betriebsspannung versorgt ist (grüne LED leuchtet) und korrekt mit der Programmier-Einheit verbunden ist. Des weiteren muss der Frequenzteiler des Quarzoszillators auf eine nicht zu hohe Frequenz eingestellt sein. Nun kann durch klicken auf Download der Programmier-Vorgang gestartet werden. BEDLOAD quittiert den Vorgang mit einer Meldung, die Auskunft darüber gibt, ob erfolgreich programmiert wurde oder ob es Fehler gegeben hat. Praktisch sofort nach Abschluss des Downloads, startet der Programmablauf innerhalb des FPGA. Entsprechend der Betätigung des Reset-Tasters und der Beschaltung mit dem Taktsignal, kann nun das SIC-Programm gestartet werden. Die Erfahrung im Umgang mit BEDLOAD hat gezeigt, dass fehlerhafte Programmierungen vermieden werden können, wenn das Programm für jeden Programmier-Vorgang erneut gestartet und eingestellt wird. - 38 - Projektbericht 10.5 Single Instruction Computer Anwendung des Assemblers Zunächst sollte der Perl Interpreter, der unter .../Software/Perl/ zu finden ist, installiert sein. Des weiteren sind alle anderen Dateien, die in diesem Verzeichnis liegen, notwendig: „SIC_ASS.pl“ ist das Perl-Skript, das den Assembler darstellt. „stat_sic.vhd“ ist die VHDL Datei mit dem statischen Anteil des SIC (sozusagen dem Gerüst) und „source.txt“ ist eine Textdatei mit Pseudo-Code, der als Beispiel für ein SIC-Assembler-Programm dienen soll. Alle diese Dateien müssen sich im gleichen Verzeichnis befinden. Dieses Verzeichnis muss entweder den Perl Interpreter enthalten oder er muss im Pfad des Systems eingetragen sein. Die Vorgehensweise ist dann wie folgt: Zunächst erstellt man eine Textdatei mit dem gewünschten Pseudo-Code. Dazu kann ein Textverarbeitungsprogramm oder ein einfache Editor verwendet werden. Der Pseudo-Code muss im Normalfall einen Eintrag für eine temporär verwendbare Adresse im Datenspeicher, verschiedene Einträge zur Initialisierung des Datenspeichers und das Programm selber enthalten. Einträge zur Parametrierung des SIC sind zunächst optional. Es kann aber auch die Beispieldatei verwendet werden. Um den Pseudo-Code dann zu assemblieren, startet man den Assembler, in dem man einfach das Skript „SIC_ASS.pl“ ausführt. Im Laufe der Abarbeitung des Skripts, wird der „Source-Code“ übersetzt und in das VHDL Gerüst eingesetzt. Das Ergebnis ist dann anhand der vom Assembler erstellten VHDL Datei „sic_ass.vhd“ zu begutachten. Diese Datei wird dann für die Erzeugung der FPGA Programmierdatei verwendet und in den weiter oben beschriebenen Kompiliervorgang mit WebPACK eingesetzt. Das Perl-Skript selber wird im einzelnen folgende Schritte abarbeiten: Als erstes wird der Name der Quelldatei erfragt. Dieser muss vollständig mit Endung und gegebenenfalls Pfad angegeben werden. Anschließend erfragt das Skript, ob die Quelldatei die Parameter für den SIC enthält oder nicht. Wird hier mit nein (n/N) geantwortet, so werden die Werte aller notwendigen Parameter folgend einzeln erfragt. Andernfalls ermittelt das Skript die Parameter aus der Datei. Im nächsten Abschnitt bestimmt das Skript alle weiteren Komponenten, die notwendig sind um eine vollständige Beschreibung eines SIC in VHDL zu erhalten: Die temporäre Adresse des Datenspeichers, die für alle notwendigen Zwischenschritte verwendet wird, die Daten zur Initialisierung des Datenspeichers und die Pseudo-Befehle. Diese Komponenten werden in den entsprechenden VHDL Code übersetzt und in das VHDL Gerüst eingesetzt. Abschließend wird die Zieldatei erzeugt und der komplette VHDL Code hinein geschrieben. - 39 - Projektbericht Single Instruction Computer 11. Bibliographie und Referenzen 11.1 Bücher [CPOAD] David A. Patterson, John L. Hennessy, Computer Organization and Design – The Hardware / Software Interface, 2. Edition, Morgan Kaufmann Publishers, San Mateo, (1998), ISBN: 155860491X [CPOAA] William Stallings, Computer Organization and Architecture, 5. Edition, Prentice Hall, Upper Saddle River, (2000), ISBN: 0-13-085263-5 [SWENG] Ian Sommerville, Software Engineering, 6. Edition, Addison-Wesley, (2001), ISBN: 0-201-39815-X 11.2 Webseiten [URISC] Douglas W. Jones, The Ultimate RISC, http://www.cs.uiowa.edu/~jones/arch/risc/, University of Iowa, (Nov 2001) [THSIC] Sarah Mount, The Single Instruction Computer, http://www.infradead.org/~snim2/Undergrad/SIC/documents/Proposal/Pr oposal.html, (Okt 1999/ Nov 2001) [NOSIC] Single Instruction Computer, http://www.cas.mcmaster.ca/~cs1md3/topics/topics-sic.html, (Nov 2001) [BURSB] Burch Electronic Designs, Making FPGA Prototyping Easy, http://www.burched.com.au/, (Nov 2001) 11.3 Andere Quellen [RSTSK] Prof. Dr. Thomas Risse, Rechner-Strukturen, Skript zur Vorlesung RST des Fachbereichs Elektrotechnik/Informatik der Hochschule Bremen, www.weblearn.hs-bremen.de/risse/rst/, (2000) [FPGAV] Carsten Thiele, Klaus Bicker, Bericht zum Vortrag FPGA, Vortrag im Rahmen des RST-Labors des Fachbereichs Elektrotechnik/Informatik der Hochschule Bremen, www.weblearn.hsbremen.de/risse/RST/SS01/FPGA/FPGA.pdf, (Sep 2001) - 40 - Projektbericht Single Instruction Computer Anhang Verwendete Hardware und Software Die verwendete Hardware besteht im Kern aus einem FPGA Chip der Firma Xilinx: Spartan II XC2S200, die genaue Bezeichnung des im Entwicklungstool verwendeten Bausteins ist xc2s200-5pq208-XST. Als Experimentier-System wird das B3-Spartan2+ FPGA Board der Firma Burch Electronic Designs verwendet. Nähere Informationen sowie Abbildungen sind in [BURSB] zu finden. Die Pinbelegungen des FPGA und des Experimentierbords sind unter /Andere_Quellen/ SpartanBoardPläne/PinBelegungen und ../ Spartan_FPGA_Pläne / zu finden. Synthese- / Compiler-Berichte in Auszügen Auszüge aus dem Synthese-Bericht: ---- Source Options . . RAM Style : Auto Mux Style : Auto . . Diese Synthese Einstellungen geben unter anderem an, dass die Art und Weise, wie RAM und Multiplexer synthetisiert werden sollen, automatisch durch den Compiler bestimmt werden. ---- General Options Optimization Criterion : Speed Optimization Effort :1 . . Unter den allgemeinen Einstellungen ist z.B. das Optimierungs-Kriterium (hier Geschwindigkeit der resultierenden Schaltung) und der dazu angesetzte Aufwand zu finden. Synthesizing Unit <sip>. Related source file is C:/xilinx_webpack/bin/FN_SIP/fn_sip/fn_sip.vhd. WARNING:Xst:646 - Signal <instruction_s<13>> is assigned but never used. WARNING:Xst:646 - Signal <instruction_s<12>> is assigned but never used. . . Im Verlauf der Synthese werden unter Umständen Fehlermeldungen, die unbedingt zu beheben sind und Warnungen ausgegeben. Speziell die oben angegebenen Warnungen - 41 - Projektbericht Single Instruction Computer stellen jedoch kein Problem dar. Aufgrund der verwendeten Daten und SBN Anweisungen, kann es sein, dass z.B. eine Befehlswortbreite von 16 Bit notwendig ist, einzelne Bits davon aber innerhalb dieses Programms nie abgefragt werden. Sie werden gegebenenfalls bei der Optimierung entfernt. Summary: inferred inferred inferred inferred 95 D-type flip-flop(s). 3 Adder/Subtracter(s). 1 Comparator(s). 95 Multiplexer(s). In der Zusammenfassung werden die gefundenen Grundbausteine und ihre Anzahl angegeben. Macro Statistics # Registers 16-bit register 4-bit register 1-bit register # Multiplexers 15-bit 8-to-1 multiplexer 2-to-1 multiplexer # Adders/Subtractors 15-bit subtractor 4-bit adder # Comparators 15-bit comparator less : 77 :1 :1 : 75 : 49 :2 : 47 :3 :1 :2 :1 :1 Die Macro Statistik gibt dann Aufschluss darüber, welche Komponenten durch die Verknüpfung der in der Zusammenfassung aufgezählten Grundbausteine synthetisiert werden. Timing Summary: --------------Speed Grade: -5 Minimum period: 16.704ns (Maximum Frequency: 59.866MHz) Minimum input arrival time before clock: 10.460ns Maximum output required time after clock: 8.449ns Maximum combinational path delay: No path found Die Timing-Zusammenfassung gibt Informationen über die wahrscheinlich maximal zu verwendende Taktfrequenz an. Diese Information ist wichtig für die spätere Einstellung des Frequenzteilers des Quarzoszillator auf dem Experimentier-Bord. Auszüge aus dem Übersetzungs-Bericht: Checking timing specifications ... Checking expanded design ... - 42 - Projektbericht Single Instruction Computer NGDBUILD Design Results Summary: Number of errors: 0 Number of warnings: 0 Der Übersetzungs-Prozess überprüft unter anderem wie das Synthese-Ergebnis zur gewählten Hardware passt. Sollten die Spezifikationen des gewählten Bausteins nicht eingehalten werden, so würden hier Warnungen oder Fehlermeldungen ausgegeben werden. Auszüge aus dem Mapping-Bericht: Section 6 - IOB Properties -------------------------+-------------------------------------------------------------------------------------------------------+ | IOB Nam| Type | Direction | IO Standard | Drive | Slew | Reg (s) | Resistor | IOB | | | | | | Strength | Rate | | | Delay | +-------------------------------------------------------------------------------------------------------+ | clk_i | GCLKIOB | INPUT | LVTTL | | | | | | | led_o<0> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<10>| IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<11>| IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<12>| IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<13>| IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<14>| IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<15>| IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<1> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<2> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<3> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<4> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<5> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<6> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<7> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<8> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | led_o<9> | IOB | OUTPUT | LVTTL | 12 | SLOW | | | | | reset_i | IOB | INPUT | LVTTL | | | | | | +-------------------------------------------------------------------------------------------------------+ Der Mapping-Bericht gibt z.B. Auskunft über die verwendeten Ports. - 43 - Projektbericht Single Instruction Computer Inhalt des Projektverzeichnisses „Projekt_SIC“ Quellen o Andere_Quellen DLXSim M17einMISC-Dateien PIC18C RST_Assembler_einfachProz-Dateien Spartan_FPGA_Pläne SpartanBoardPläne SurveyStackComputersAuchM17-Dateien VHDL WebPackTutorial XilinxISEtutorial o CtBerichte o FPGA_Bericht o SIC_sites Final year CST projects suggestions.htm GNOME_IDE_SIC_Manual-Dateien Homework.htm Lab-CIS 577 Computer Architecture.htm Part II Project A Single Instruction Computer-Dateien Single Instruction Computer-Dateien The Single Instruction Computer-Dateien The Ultimate RISC o Weiterf_Quellen Control.jpg Datapath.jpg DLX_script.pdf CPU-Design-HOWTO-html.tar dv_Vorles_fhAugsb.pdf fpgacpu_org - FPGA CPU News.htm proz-4.pdf Software o Perl Doku o Warp_52 o webPack_alt o WebPack4 Beschreibung: - DLXSim Projektbericht als HTML-Dokument über einen DLX-Simulator. - M17einMISC Ausschnitt einer Auflistung spezieller Prozessor-Architekturen – speziell M17 als ein Minimum Instruction Set Computer. - PIC18C - 44 - Projektbericht Single Instruction Computer Teile des Referenz Manuals zum PIC 18 C Mikrocontrollers als pdfDokumente – Beispiel für einen „einfachen“ Mikroprozessor, der mehrfach für den Einstieg in µProzessor-Architekturen empfohlen wurde. - RST_Assembler_einfachProz Homepage der Vorlesung „Hardwarearchitektur“ von Dr. Karl Bühler (Uni Freiburg) – speziell Kapitel 6 als Beschreibung zur Realisierung eines „einfachen Prozessors“. - Spartan_FPGA_Pläne Dokumentation und Schaltpläne zur Spartan-II FPGA Familie. - SurveyStackComputersAuchM17 Eine Übersicht zu Computern mit Stack-Hardware Unterstützung – speziell auch der als MISC geltende M17 (s.o.). - VHDL Verschiedene Quellen zur Programmiersprache VHDL (Manuals, Cookbooks, Links und diverse Bibliotheken). Speziell „The VHDL Resource Page.htm“ ist eine reichhaltige Quelle. - WebPackTutorial Tutorials zu der Xilinx Entwicklungsumgebung „WebPack“. - XilinxISEtutorial Tutorial zu der Xilinx Entwicklungsumgebung „ISE“. - CtBerichte Artikel zum Thema MISC der Ausgabe 07/91. - FPGA_Bericht Bericht im Rahmen des RST-Labors über das XC4010XL FPGA von XILINX. Speziell die Beschreibung der Komponenten des FPGA und der Implementierung einer CPU darauf sind hilfreich. - GNOME_IDE_SIC_Manual Manual für eine SIC-Entwicklungsumgebung (IDE). - Homework.htm Aufgabenblatt mit Fragen zum SIC. - Final year CST projects suggestions.htm Vorschläge zu Projektthemen – speziell SIC. - Lab-CIS 577 Computer Architecture.htm Beschreibung zu einer Laboraufgabe zu einem SIC. - Part II Project A Single Instruction Computer Homepage zu einem Projekt über einen SIC. - 45 - Projektbericht Single Instruction Computer - Single Instruction Computer Ein HTML-Dokument das den SIC und die sbn-Instruktion einführend gut erläutert. - The Single Instruction Computer Weitere HTML-Dokumente zu einem SIC-Projekt. - The Ultimate RISC Beschreibung einer ultimativen RISC Architektur. - Control.jpg Ein Entwurf von mir für eine Multicycle Steuereinheit (nach [CPOAD]). - Datapath.jpg Ein Entwurf von mir für einen Multicycle Datenpfad (nach [CPOAD]). - DLX_script.pdf Kurzes Skript zur Architektur der DLX. http://www.inf.tu-dresden.de/~ap3/PAMP/DLX_script.pdf - CPU Design HOW-TO Dieses HOW-TO ist ein HTML-Dokument, das einen Einblick in die Themen CPU Design und CPU Architekturen gibt. - dv_Vorles_fhAugsb.pdf Skript zur Vorlesung „Datenverarbeitungssysteme“ von Prof. Stark (FH Augsburg). - fpgacpu_org - FPGA CPU News.htm Webseite zum Thema Prozessor-Design auf FPGAs von GRAY Research LLC. - proz-4.pdf Unterlagen der BTU Cottbus zum Thema Prozessor-Architekturen. - ActivePerl-5.6.1.626-MSWin32-x86-multi-thread.msi Installationspaket für die Skript-Sprache Perl auf einer MS Windows Umgebung. - Perl Tutorial.htm Ein Tutorial zu Perl als HTML-Dokument vom National Center for Supercomputing Applications (NCSA). - pickingUpPerl.pdf Ein Tutorial zu Perl, das als pdf-Dokument und als HTML-Dokument vorliegt. Es ist nach der GNU Free Documentation License lizenziert. Als leichte Kost zum Einstieg geeignet. - Robert's Perl Tutorial.htm Noch ein Tutorial zu Perl, das als HTML-Dokument vorliegt. Nicht so übersichtlich, aber etwas breiter angelegt. - 46 - Projektbericht Single Instruction Computer - WARP_52 Entwicklungsumgebung für VHDL. - webPack_alt und WebPack4 Entwicklungsumgebung inklusive Simulator für VHDL und VERILOG. VHDL Quellcode des SIC --#################################################### --# --# R. Oldenburger --# T. Risse --# S. Simon --# --# Version 1.3.0 31. Januar 2002 --# Datei "sip.vhd" --# SIP -> Single Instruction Processor --# --##################################################### --##################################################### --# --# Change log --# Letzte Aenderung 6. Februar 2002 --# --# Erlaeuterungen hinzugefuegt --# --# Dies ist die korrekte Art und Weise einen Prozess --# mit Takt und Reset zu verknüpfen, um Flip-Flops zu --# erzeugen. Desweiteren sind jetzt alle nicht takt--# abhaengigen Elemente als Concurrent Statements ausgefuehrt. --# --###################################################### --###################################################### --# --# Kommentare: --# --# Es fehlt weiterhin eine Ergebniskontrolle, die die --# Weiter-Verarbeitung des Programms nach einem Overflow --# verhindert. --# --####################################################### LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_SIGNED.ALL; - 47 - Projektbericht Single Instruction Computer ENTITY sip IS PORT (clk_i : IN STD_ULOGIC; reset_i : IN STD_ULOGIC; led_o : OUT STD_ULOGIC_VECTOR(15 downto 0) -- LED-Ausgabeport auf LED leiste zu mappen ); END; -- Parameterliste ARCHITECTURE rtl OF sip IS -- Datenwortlaenge CONSTANT data_wl_c : INTEGER := 16; -- Anz. Datenworte im Datenspeicher CONSTANT reg_nr_c : INTEGER := 8; -- Anz. Befehlsworte im Programmspeicher CONSTANT line_nr_c : INTEGER := 9; -- 1. Argument in Instr hat n Bit CONSTANT instr_argl1_c : INTEGER := 5; -- 1. u 2. Arg => maxAnz addrbare Datenworte CONSTANT instr_argl2_c : INTEGER := 5; -- 3. Argument => max Sprungweite +/CONSTANT instr_argl3_c : INTEGER := 4; -- Nr. des obersten/letzten reservierten Registers CONSTANT data_reg_reserv_nr_c : INTEGER := 2; -- Befehlswortlaenge CONSTANT instr_wl_c : INTEGER := instr_argl1_c + instr_argl2_c + instr_argl3_c; -- groesste Sprungweite vorwaerts CONSTANT MAX_jmp_c : INTEGER := 2 ** (instr_argl3_c - 1) - 1; -- groesste Sprungweite rueckwaerts CONSTANT MIN_jmp_c : INTEGER := 2 ** (instr_argl3_c - 1) * (-1); -- Groesster Operandenwert CONSTANT MAX_value_c : INTEGER := 2 ** (data_wl_c - 1) - 1; -- Kleinster Operandenwert CONSTANT MIN_value_c : INTEGER := 2 ** (data_wl_c - 1) * (-1); -- Datenspeicher Definition TYPE data_reg_t IS ARRAY (NATURAL RANGE 0 TO reg_nr_c-1) OF STD_ULOGIC_VECTOR (data_wl_c-1 DOWNTO 0); -- Programmspeicher Definition TYPE Program_type IS ARRAY (1 TO line_nr_c) OF STD_ULOGIC_VECTOR (instr_wl_c-1 DOWNTO 0); - 48 - Projektbericht Single Instruction Computer -- SBN Anweisungen im Programmspeicher CONSTANT Program : Program_type := Program_type'( "00111001110001", --> 1 "00111001010001", --> 2 "00011000110001", --> 3 "00011001110001", --> 4 "00111001110001", --> 5 "00111001100001", --> 6 "00100001000001", --> 7 "00100001110001", --> 8 "00001000100000" --> 9 ); SIGNAL instruction_s : STD_ULOGIC_VECTOR (instr_wl_c-1 DOWNTO 0); SIGNAL diff_vec_s : STD_ULOGIC_VECTOR (data_wl_c-1 DOWNTO 0); SIGNAL data_reg_s : data_reg_t; -- Programmzaehler hat nur Werte von 1 bis .. SIGNAL pc_s : INTEGER RANGE 1 TO line_nr_c; SIGNAL pc_incr_s : INTEGER RANGE 1 TO line_nr_c; SIGNAL pc_jmp_s : INTEGER RANGE 1 TO line_nr_c; -- Diff., Op. haben nur Werte zwischen .. und .. SIGNAL diff_s : INTEGER RANGE MIN_value_c TO MAX_value_c; SIGNAL op_a_s : INTEGER RANGE MIN_value_c TO MAX_value_c; SIGNAL op_b_s : INTEGER RANGE MIN_value_c TO MAX_value_c; SIGNAL adr_a_s : INTEGER RANGE 0 TO reg_nr_c-1; SIGNAL adr_b_s : INTEGER RANGE 0 TO reg_nr_c-1; SIGNAL jmp_range_s : INTEGER RANGE MIN_jmp_c TO MAX_jmp_c; BEGIN -- concurrent statements -- Naechste SBN Anweisung instruction_s <= Program(pc_s); -- Adressen im Datenpeicher adr_a_s <= conv_integer (UNSIGNED(instruction_s(instr_wl_c - 1 downto instr_wl_c - instr_argl1_c))); adr_b_s <= conv_integer (UNSIGNED(instruction_s(instr_wl_c - instr_argl1_c - 1 downto instr_wl_c - instr_argl1_c - instr_argl2_c))); -- Sprungweite bei negativem Ergebnis jmp_range_s <= conv_integer (SIGNED(instruction_s (instr_argl3_c - 1 downto 0))); - 49 - Projektbericht Single Instruction Computer -- Daten aus Datenspeicher op_a_s <= conv_integer (SIGNED(data_reg_s(adr_a_s))); op_b_s <= conv_integer (SIGNED(data_reg_s(adr_b_s))); -- Ergebnis diff_s <= op_a_s - op_b_s; diff_vec_s <= STD_ULOGIC_VECTOR(conv_std_logic_vector (diff_s, data_wl_c)); -- Die naechsten moeglichen Programmzaehler pc_incr_s <= pc_s + 1; pc_jmp_s <= pc_s + jmp_range_s; -- Ausgabe auf LEDs led_o <= data_reg_s (0); -- clocked process reg : PROCESS (clk_i, reset_i) Begin IF reset_i = '0' THEN -- Datenspeicher loeschen FOR i IN data_reg_reserv_nr_c + 1 TO reg_nr_c - 1 LOOP data_reg_s(i) <= (OTHERS => '0'); END LOOP; -- Die Register 0, 1 und 2 sind per Definition reserviert -- Standard Ergebnis-Register (0) initialisieren data_reg_s (0) <= "1010101010101010"; -- Datenregister 1,2 fuer HALT initialisiert data_reg_s (1) <= "0000000000000000"; data_reg_s (2) <= "0000000000000001"; -- Alle weiteren Register sind abhaengig vom Programminhalt -- Programmdaten initialisieren data_reg_s (5) <= "0000000000011111"; data_reg_s (6) <= "0000000000000001"; data_reg_s (7) <= "0011110010000111"; -- Programmzaehler initialisiert pc_s <= 1; -- Dies ist der eigentliche Prozess ELSIF (clk_i'EVENT AND clk_i='1') THEN - 50 - Projektbericht Single Instruction Computer -- Alle Register aktualisieren, -- zweifelsfrei Abhaengigkeit zum Taktsignal schaffen -- und damit kompletten Datenspeicher als Flip-Flops zu synthetisieren. -- Vorgehensweise ohne diese (widerspuerchliche) Schleife schafft -- Missverstaendnisse bei der Interpretation des Datenspeichers -- durch das Synthese Programm bei Versionen aelter als 4.1. FOR i IN 0 TO reg_nr_c - 1 LOOP IF adr_a_s = i THEN -- Ergebnis zurueckschreiben (nur nicht in reservierte Register) IF i > data_reg_reserv_nr_c THEN data_reg_s(adr_a_s) <= diff_vec_s; data_reg_s(0) <= diff_vec_s; -- Ergebnis zur Ausgabe END IF; END IF; END LOOP; -- Neuer Programmzaehler IF diff_s < 0 THEN pc_s <= pc_jmp_s; ELSE pc_s <= pc_incr_s; END IF; END IF; End PROCESS; END rtl; VHDL Quellcode der Testbench --#################################################### --# --# R. Oldenburger --# T. Risse --# S. Simon --# --# Version 1.1 23. Januar 2002 --# Datei "sip_tb.vhd" --# SIP -> Single Instruction Processor --# TB -> TestBench --# --##################################################### LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL; USE IEEE.STD_LOGIC_SIGNED.ALL; - 51 - Projektbericht Single Instruction Computer ENTITY sip_tb IS -- keine Ports fuer Testbench noetig END sip_tb; ARCHITECTURE rtl OF sip_tb IS -- Clock und Reset Parameter CONSTANT ClkStartVal : STD_ULOGIC := '0'; CONSTANT ClkDelay : TIME := 10 NS; CONSTANT ClkWidth : TIME := 10 NS; CONSTANT ClkPeriod : TIME := 20 NS; CONSTANT ClkStop : TIME := 20*16705 NS; CONSTANT resDelay : TIME := 25 NS; -- Bekanntmachen des Testobjekts (einzusetzende Komponente) COMPONENT sip PORT ( clk_i : IN STD_ULOGIC; reset_i : IN STD_ULOGIC; led_o : OUT STD_ULOGIC_VECTOR(15 downto 0) ); END COMPONENT; SIGNAL clk_i, reset_i : STD_ULOGIC; SIGNAL led_o : STD_ULOGIC_VECTOR(15 downto 0); begin -- Design Under Test DUT : sip -- Port Map verbindet Ein- und Ausgaenge des DUT -- mit denen der Testbench PORT MAP ( clk_i => clk_i, reset_i => reset_i, led_o => led_o ); clkGen : PROCESS variable counter : integer; - 52 - Projektbericht Single Instruction Computer -- Prozess erzeugt Clock- und Resetsignal BEGIN clk_i <= ClkStartVal; -- so beginnt der Clock reset_i <= '0','1' after resDelay; -- so verlaeuft der Reset ClkLoop : WHILE now < ClkStop LOOP -- Clock bis dahin erzeugen -- Dies erzeugt das Taktsignal clk_i <= TRANSPORT NOT ClkStartVal AFTER ClkDelay, ClkStartVal AFTER ClkWidth + ClkDelay; WAIT FOR ClkPeriod; END LOOP ClkLoop; -- Abbruch und Ausgabe einer Meldung nach Ende der Signalerzeugung ASSERT false REPORT "Clockgeneration was stopped due to ClkStop" SEVERITY ERROR; WAIT; END PROCESS ClkGen; END rtl; Perl Quellcode des „Assemblers“ ############################################### # # Roul Oldenburger # Thomas Risse # Sven Simon # # Version 1.0 12. Februar 2002 # # SIC_ASS -> Single Instruction Computer Assembler # ############################################### ############################################### # # Change log # Letzte Änderung 12.Februar 2002 # ############################################### - 53 - Projektbericht Single Instruction Computer ############################################### # # Kommentare: # # Es war geplant den Assemble-Vorgang in ein # Unterprogramm zu fassen. Es gelang mir jedoch # nicht beim Unterprogramm-Aufruf Arrays zu # uebergeben. # # Es ist nur wenig Code enthalten, der Eingaben, # Werte, etc. überprüfen. Hier besteht Nach# besserungsbedarf! # # Der Code wurde inkrementell auf seine Funk# tionalitaet getestet. Teilweise sind auch # Grenzwerte getestet worden. Hier besteht # ebenfalls Nachbesserungsbedarf! # ############################################### # Alle Fehlermeldungen und Warnungen use strict; use warnings; # Deklarationen Parameter my $datenwortbreite = 16; my $anz_datenworte = 8; my $anz_befehlsworte = 9; my $arg1 = 5; my $arg2 = 5; my $arg3 = 4; my $nr_reserv_reg = 2; # Deklarationen Skript my $anz_par = 7; my $temp_adr = undef; my $eingabe = undef; my $temp = undef; my $pline = undef; my $line = undef; my @programm = undef; my @parameter = undef; my @target = undef; my @daten = undef; - 54 - Projektbericht Single Instruction Computer # Deklarationen Dateinamen my $vhdl_code = "stat_sic.vhd"; my $target_code = "sic_ass.vhd"; my $source_code = undef; # Begrüßung print "\n\nHallo !\nDies ist der SIC Assembler.\n\n\n"; print "Bitte geben sie den Namen der Quelldatei ein: "; chomp($source_code = <STDIN>); # SIC-Parameter jetzt eingeben oder in Pseudo-Code enthalten? print "Sind die Parameter des SIC in der Assembler-Datei enthalten? (j/n/a)"; do { $eingabe = <STDIN>; chomp $eingabe; if ($eingabe =~ /^(a|A)$/) { print "Abbruch!\n\n"; die; } } until ($eingabe =~ /^(j|J|y|Y|n|N)$/); if ($eingabe =~ /^(j|J|y|Y)$/) { print "\nDie SIC Parameter werden zusammen mit dem Pseudo-Code eingelesen.\n"; # Unterprogrammaufruf zum Auslesen der Parameter aus der Quelldatei @parameter = setpar ($source_code); # Unterprogrammaufruf zum Auslesen der temporaeren Adresse $temp_adr = settemp ($source_code); # Unterprogrammaufruf zum Auslesen Daten-Initialisierung @daten = setdat ($source_code); # Unterprogrammaufruf zum Auslesen und Uebersetzens des Pseudo-Codes @programm = translate ($source_code); # "Programm" und Parameter in den VHDL-Code einbauen - 55 - Projektbericht Single Instruction Computer open (FILE, "<$vhdl_code") or die "VHDL-Code kann nicht geoeffnet werden"; while ($line = <FILE>) { if ($line =~ /^ *-- Parameterliste/) { push (@target, "\-\- Parameterliste\n"); foreach $pline (@parameter) { print $pline; push (@target, "$pline"); } } elsif ($line =~ /^ *-- Programm in Programmspeicher laden/) { push (@target, "\-\- Programm in Programmspeicher laden\n"); foreach $pline (@programm) { print $pline; push (@target, "$pline"); } } elsif ($line =~ /^ *-- Programmdaten initialisieren/) { push (@target, "\-\- Programmdaten initialisieren\n"); foreach $pline (@daten) { print $pline; push (@target, "$pline"); } } else { push (@target, "$line"); } } close (FILE); print "\nStatischer und dynamischer VHDL-Code zusammengefuegt.\n\n"; # Zurueckschreiben der Ergebnisdatei open (FILE, ">$target_code") or die "Zieldatei kann nicht erstellt werden"; print FILE @target; close (FILE); print "\nZieldatei ($target_code) geschrieben.\n\n"; } else { # SIC-Parameter eingeben print "\nDie SIC Parameter koennen jetzt eingegeben werden.\n"; print "Datenwortbreite ($datenwortbreite Bit beibehalten = 'enter'): "; chomp($temp = <STDIN>); if ($temp ne "") { $datenwortbreite = $temp; } print "\nDatenwortbreite sind $datenwortbreite Bits.\n\n"; print "\nBreite des 1. Befehlsarguments ($arg1 Bit): "; - 56 - Projektbericht Single Instruction Computer chomp($temp = <STDIN>); if ($temp ne "") { $arg1 = $temp; } print "\n1. Argument hat $arg1 Bit.\n\n"; print "\nBreite des 2. Befehlsarguments ($arg2 Bit): "; chomp($temp = <STDIN>); if ($temp ne "") { $arg2 = $temp; } print "\n2. Argument hat $arg2 Bit.\n\n"; print "\nBreite des 3. Befehlsarguments ($arg3 Bit): "; chomp($temp = <STDIN>); if ($temp ne "") { $arg3 = $temp; } print "\n3. Argument hat $arg3 Bit.\n\n"; print "\nAnzahl der Datenworte (Groesse des Datenspeichers $anz_datenworte Worte): "; chomp($temp = <STDIN>); if ($temp ne "") { $anz_datenworte = $temp; } print "\nDatenspeicher soll $anz_datenworte Datenworte umfassen.\n\n"; print "\nAnzahl der Befehlsworte im Programmspeicher ($anz_befehlsworte Befehle): "; chomp($temp = <STDIN>); if ($temp ne "") { $anz_befehlsworte = $temp; } print "\nProgrammspeicher umfasst $anz_befehlsworte SBNAnweisungen.\n\n"; print "\nReservierte Adressen im Datenspeicher (bis Register Nr. $nr_reserv_reg): "; chomp($temp = <STDIN>); if ($temp ne "") { $nr_reserv_reg = $temp; } print "\nRegister 0 bis $nr_reserv_reg sind reserviert.\n\n"; # Uebersetzung in VHDL und Zusammenfassung der Parameter push (@parameter, "CONSTANT data_wl_c : INTEGER := $datenwortbreite;\n"); - 57 - Projektbericht Single Instruction Computer push (@parameter, "CONSTANT reg_nr_c : INTEGER := $anz_datenworte;\n"); push (@parameter, "CONSTANT line_nr_c : INTEGER := $anz_befehlsworte;\n"); push (@parameter, "CONSTANT instr_argl1_c : INTEGER := $arg1;\n"); push (@parameter, "CONSTANT instr_argl2_c : INTEGER := $arg2;\n"); push (@parameter, "CONSTANT instr_argl3_c : INTEGER := $arg3;\n"); push (@parameter, "CONSTANT data_reg_reserv_nr_c : INTEGER := $nr_reserv_reg;\n"); # Unterprogrammaufruf zum feststellen der temporaeren Adresse $temp_adr = settemp ($source_code); # Unterprogrammaufruf zum Auslesen Daten-Initialisierung @daten = setdat ($source_code); # Unterprogrammaufruf zum Auslesen und Uebersetzens des Pseudo-Codes @programm = translate ($source_code); # "Programm" und Parameter in den VHDL-Code einbauen open (FILE, "<$vhdl_code") or die "VHDL-Code kann nicht geoeffnet werden"; while ($line = <FILE>) { if ($line =~ /^ *-- Parameterliste/) { push (@target, "\-\- Parameterliste\n"); foreach $pline (@parameter) { print $pline; push (@target, "$pline"); } } elsif ($line =~ /^ *-- Programm in Programmspeicher laden/) { push (@target, "\-\- Programm in Programmspeicher laden\n"); foreach $pline (@programm) { print $pline; push (@target, "$pline"); } } elsif ($line =~ /^ *-- Programmdaten initialisieren/) { push (@target, "\-\- Programmdaten initialisieren\n"); foreach $pline (@daten) { print $pline; push (@target, "$pline"); } } else { push (@target, "$line"); } - 58 - Projektbericht Single Instruction Computer } close (FILE); print "\nStatischer und dynamischer VHDL-Code zusammengefuegt.\n\n"; # Zurueckschreiben der Ergebnisdatei open (FILE, ">$target_code") or die "Zieldatei kann nicht erstellt werden"; print FILE @target; close (FILE); print "\nZieldatei ($target_code) geschrieben.\n\n"; } #Ende des Hauptprogramms ############################################################### # Temporaere Adresse fuer SBN-Sequenzen ermitteln sub settemp { my $source = shift; my $temp_reg = undef; open (FILE, "<$source") or die "Quelltext kann nicht geoeffnet werden"; while ($line = <FILE>) { if ($line =~ /^TMP +([0-9]+) */) { print "TMP\n"; print "Temporaere Adresse : $1\n\n"; $temp_reg = dez2bin ($arg1, $1); } } if ($temp_reg eq "") { die "Keine Temporaere Adresse angegeben\n"; } print "Temporaere Adresse festgelegt.\n\n"; return $temp_reg; } # Programm-Daten für die Initialisierung auslesen sub setdat { my $source = shift; my @dat = undef; open (FILE, "<$source") or die "Quelltext kann nicht geoeffnet werden"; - 59 - Projektbericht Single Instruction Computer while ($line = <FILE>) { if ($line =~ /^DAT +([0-9]+) *, *(\-[0-9]+|[0-9]+) */) { print "DAT\n"; print "Adresse : $1\n"; print "Wert : $2\n\n"; if ($2 > (2**($datenwortbreite-1)-1) or $2 < (2**($datenwortbreite-1)*(-1))) { print "Wertebereich : "; print (2**($datenwortbreite-1)-1); print " bis "; print (2**($datenwortbreite-1)*(-1)); die "\nDatum ausserhalb des gueltigen Wertebereichs\n"; } my $val = dez2bin ($datenwortbreite, $2); if ($1 <= $nr_reserv_reg) { die "Es wird versucht eine der reservierten Adressen ($1) zu beschreiben\n"; } elsif ($1 > ($anz_datenworte-1)) { die "Adresse $1 liegt ausserhalb des gueltigen Bereichs (0 bis $anz_datenworte-1)\n"; } push (@dat, "data_reg_s ($1) <= \"$val\";\n"); } } close (FILE); print "Daten-Initialisierung erstellt.\n\n"; return @dat; } # Pseudo-Code uebersetzen # Dieses Unterprogramm fuer weitere Pseudo-Befehle erweitern !!! sub translate { my $source = shift; my @prog = undef; my $line_cnt = 1; my $line = undef; open (FILE, "<$source") or die "Quelltext kann nicht geoeffnet werden"; while ($line = <FILE>) { print "Zeile: $line"; # MOV Befehl auswerten if ($line =~ /^(MOV|CP|MOVE|COPY) +([0-9]+) *, *([0-9]+) *(,| *) *([0-9]*) */) { print "MOV\n"; - 60 - Projektbericht Single Instruction Computer print "Adresse 1: $2\n"; print "Adresse 2: $3\n"; print chk_adr($2); my $adr1 = dez2bin ($arg1, $2); my $adr2 = dez2bin ($arg2, $3); my $sprung1 = dez2bin ($arg3, 1); my $sprung = undef; if ($4 ne ",") { $sprung = $sprung1; print "\n"; } else { $sprung = dez2bin ($arg3, $5); print "Sprung : $5\n\n"; } push (@prog, "program_s($line_cnt) <= \"${temp_adr}${temp_adr}${sprung1}\";\n"); $line_cnt ++; push (@prog, "program_s($line_cnt) <= \"${temp_adr}${adr2}${sprung1}\";\n"); $line_cnt ++; push (@prog, "program_s($line_cnt) <= \"${adr1}${adr1}${sprung1}\";\n"); $line_cnt ++; push (@prog, "program_s($line_cnt) <= \"${adr1}${temp_adr}${sprung}\";\n"); $line_cnt ++; # ADD Befehl auswerten } elsif ($line =~ /^ADD +([0-9]+) *, *([0-9]+) *(,| *) *([0-9]*) */) { print "ADD\n"; print "Adresse 1: $1\n"; print "Adresse 2: $2\n"; print chk_adr($1); my $adr1 = dez2bin ($arg1, $1); my $adr2 = dez2bin ($arg2, $2); my $sprung1 = dez2bin ($arg3, 1); my $sprung = undef; if ($3 ne ",") { $sprung = $sprung1; print "\n"; } else { $sprung = dez2bin ($arg3, $4); print "Sprung : $4\n\n"; } push (@prog, "program_s($line_cnt) <= \"${temp_adr}${temp_adr}${sprung1}\";\n"); $line_cnt ++; - 61 - Projektbericht Single Instruction Computer push (@prog, "program_s($line_cnt) <= \"${temp_adr}${adr2}${sprung1}\";\n"); $line_cnt ++; push (@prog, "program_s($line_cnt) <= \"${adr1}${temp_adr}${sprung}\";\n"); $line_cnt ++; # SUB Befehl auswerten } elsif ($line =~ /^SUB +([0-9]+) *, *([0-9]+) *(,| *) *([0-9]*) */) { print "SUB\n"; print "Adresse 1: $1\n"; print "Adresse 2: $2\n"; print chk_adr($1); my $adr1 = dez2bin ($arg1, $1); my $adr2 = dez2bin ($arg2, $2); my $sprung = undef; if ($3 ne ",") { $sprung = dez2bin ($arg3, 1); print "\n"; } else { $sprung = dez2bin ($arg3, $4); print "Sprung : $4\n\n"; } push (@prog, "program_s($line_cnt) <= \"${adr1}${adr2}${sprung}\";\n"); $line_cnt ++; # HLT Befehl auswerten } elsif ($line =~ /^(HLT|HALT|HOLD) */) { print "HLT\n\n"; my $adr1 = dez2bin ($arg1, 1); my $adr2 = dez2bin ($arg2, 2); my $sprung = dez2bin ($arg3, 0); push (@prog, "program_s($line_cnt) <= \"${adr1}${adr2}${sprung}\";\n"); $line_cnt ++; # NOP Befehl auswerten } elsif ($line =~ /^NOP */) { print "NOP\n\n"; my $adr1 = dez2bin ($arg1, 2); my $adr2 = dez2bin ($arg2, 1); my $sprung = dez2bin ($arg3, 1); push (@prog, "program_s($line_cnt) <= \"${adr1}${adr2}${sprung}\";\n"); $line_cnt ++; - 62 - Projektbericht Single Instruction Computer # DEC Befehl auswerten } elsif ($line =~ /^(DEC|DECR) +([0-9]+) *(,| *) *([0-9]*) */) { print "DEC\n\n"; print chk_adr($2); my $adr1 = dez2bin ($arg1, $2); my $adr2 = dez2bin ($arg2, 2); my $sprung = undef; if ($3 ne ",") { $sprung = dez2bin ($arg3, 1); print "\n"; } else { $sprung = dez2bin ($arg3, $4); print "Sprung : $4\n\n"; } push (@prog, "program_s($line_cnt) <= \"${adr1}${adr2}${sprung}\";\n"); $line_cnt ++; # INC Befehl auswerten } elsif ($line =~ /^(INC|INCR) +([0-9]+) *(,| *) *([0-9]*) */) { print "INC\n\n"; print chk_adr($2); my $adr1 = dez2bin ($arg1, $2); my $adr2 = dez2bin ($arg2, 2); my $sprung1 = dez2bin ($arg3, 1); my $sprung = undef; if ($3 ne ",") { $sprung = $sprung1; print "\n"; } else { $sprung = dez2bin ($arg3, $4); print "Sprung : $4\n\n"; } push (@prog, "program_s($line_cnt) <= \"${temp_adr}${temp_adr}${sprung1}\";\n"); $line_cnt ++; push (@prog, "program_s($line_cnt) <= \"${temp_adr}${adr2}${sprung1}\";\n"); $line_cnt ++; push (@prog, "program_s($line_cnt) <= \"${adr1}${temp_adr}${sprung}\";\n"); $line_cnt ++; } } close (FILE); if ($line_cnt > ($anz_befehlsworte + 1)) { $line_cnt --; - 63 - Projektbericht Single Instruction Computer die "Nicht genuegend Programmspeicher:\nEs stehen $anz_befehlsworte Zeilen zur Verfuegung, $line_cnt Zeilen werden benoetigt\n"; } print "\nSBN-Programm erstellt.\n\n"; return @prog; } # Parameter auslesen und setzen sub setpar { my $source = shift; my @par = undef; my $par_cnt = 0; my $line = undef; open (FILE, "<$source") or die "Quelltext kann nicht geoeffnet werden"; while ($line = <FILE>) { # Parameter einlesen if ($line =~ /^PAR +/) { print "$line"; if ($line =~ /^PAR +data_wl_c := *([0-9]+) *;/) { print "PAR\n"; print "Datenwortbreite: $1\n\n"; $datenwortbreite = $1; push (@par, "CONSTANT data_wl_c : INTEGER := $1;\n"); $par_cnt ++; } elsif ($line =~ /^PAR +reg_nr_c := *([0-9]+) *;/) { print "PAR\n"; print "Anzahl Datenworte: $1\n\n"; $anz_datenworte = $1; push (@par, "CONSTANT reg_nr_c : INTEGER := $1;\n"); $par_cnt ++; } elsif ($line =~ /^PAR +line_nr_c := *([0-9]+) *;/) { print "PAR\n"; print "Anzahl Befehlsworte: $1\n\n"; $anz_befehlsworte = $1; push (@par, "CONSTANT line_nr_c : INTEGER := $1;\n"); $par_cnt ++; } elsif ($line =~ /^PAR +instr_argl1_c := *([0-9]+) *;/) { print "PAR\n"; print "Breite Argument 1: $1\n\n"; $arg1 = $1; push (@par, "CONSTANT instr_argl1_c : INTEGER := $1;\n"); - 64 - Projektbericht Single Instruction Computer $par_cnt ++; } elsif ($line =~ /^PAR +instr_argl2_c := *([0-9]+) *;/) { print "PAR\n"; print "Breite Argument 2: $1\n\n"; $arg2 = $1; push (@par, "CONSTANT instr_argl2_c : INTEGER := $1;\n"); $par_cnt ++; } elsif ($line =~ /^PAR +instr_argl3_c := *([0-9]+) *;/) { print "PAR\n"; print "Breite Argument 3: $1\n\n"; $arg3 = $1; push (@par, "CONSTANT instr_argl3_c : INTEGER := $1;\n"); $par_cnt ++; } elsif ($line =~ /^PAR +data_reg_reserv_nr_c := *([0-9]+) *;/) { print "PAR\n"; print "Nummer des hoehsten reservierten Registers: $1\n\n"; $nr_reserv_reg = $1; push (@par, "CONSTANT data_reg_reserv_nr_c : INTEGER := $1;\n"); $par_cnt ++; } } } if ($par_cnt != $anz_par) { die "Anzahl der gefundenen Parameterstimmt nicht\n"; } print"\nParameter-Liste erstellt.\n\n"; return @par; } # Zieladresse gegen reservierte Adressen pruefen sub chk_adr{ my $target_adr = shift; my $msg = undef; if ($target_adr <= $nr_reserv_reg) { $msg = "WARNUNG : Es wird versucht Adresse $target_adr zu modifizieren !\nDies kann einen Fehler im Programmablauf verursachen.\n\n"; } else { $msg = ""; } return $msg; } - 65 - Projektbericht Single Instruction Computer # Umwandeln von Dezimalzahlen in Dualzahlen # dez2bin (anzahlbits, zahl); sub dez2bin { my $bits = shift; my $zahl = shift; my $cur_bit = 2**$bits; my $bin = ""; do { $cur_bit /= 2; if (($zahl & $cur_bit) != 0) { $bin .= "1"; } else { $bin .= "0"; } } while ($cur_bit > 1); return $bin; } - 66 -