Dokumentation
Prozessorentwurfspraktikum WS 2002/2003
Gruppe 6
Patrick Röder, Matrikelnummer XXXXXX
Marko Mähner, Matrikelnummer XXXXXX
Eduard Singer, Matrikelnummer XXXXXX
Inhalt
- Einleitung
- CPU
- Steuerwerk
- Register
- Registersatz
- SRAM
- ALU
- Opcode in Alu_Operation umwandeln
- Testumgebung
- Befehlssatz
- Optimierungen
- Weitere Verbesserungen
- Benchmarks
- Feedback zum PeP
- Terminologie
- Abbildungen
- Tabellen
1. Einleitung
Die Aufgabe des diesjährigen Prozessorentwurfs-Praktikumes war es einen 16 Bit RISC-Prozessor mit 8 Registern zu entwerfen.
Ausgehend von der Aufgabenstellung analysierten wir welche Komponenten benötigt werden. Wir versuchten dabei, einen möglichst
modularen Aufbau zu erhalten. Durch das Zusammenfassen von vielen kleineren Bauteilen zu komplexeren Einheiten haben wir dieses Ziel erreicht
und auch zur Übersicht des ganzen Projektes beigetragen. Die Module, die es zu realisieren galt, umfassen eine Einheit, die Lese- und Schreibzugang zu
den 8 Registern bereitstellt, eine ALU, die in der Lage ist zwei Operanden unter Anwendung einer festlegbaren Operation zu einem Ergebnis zu verknüpfen.
Weiterhin wird ein Register benötigt, das den Programmzähler realisiert. Dieses sollte eine Möglichkeit haben, um es direkt (also ohne Berechnung über die ALU)
um eins zu inkrementieren. Schliesslich muss der Prozessor auch über einen Speicherbaustein verfügen. Die bisher genannten Komponenten wurden von uns zu einem
Operationswerk zusammengefasst. Eine kritische Frage war nun, was der geschickteste Weg ist, die genannten Einheiten zu verbinden. Eine einfache Möglichkeit
alle Komponenten an einen gemeinsamen Bus anzuschliessen hat den entscheidenden Nachteil, dass nur ein Datentransfer zwischen zwei Einheiten parallel möglich ist. Diesen Nachteil könnte man durch geziehlte Verwendung mehrerer Busse umgehen. Man müsste sich dabei ansehen, wieviel und welche Transfers parallel möglich sind und dementsprechend die Busse entwerfen. Bei diesem Ansatz geht dann allerdings die Einfachheit und Eleganz der Ein-Bus-Lösung verloren. Wir haben uns dafür entschieden, alle Komponenten, die miteinander kommunizieren müssen, durch direkte Verbindungen zu koppeln. Die Auswahl des zu benutzenden Datenpfades wird über Multiplexer gesteuert. Der Nachteil gegenüber dem Ansatz mit einem Bus ist sicherlich ein höherer Verdrahtungsaufwand und eine höhere Komplexität, aber um ein Maximum an Parallelität zu gewährleisten, haben wir uns für diese Lösung entschieden.
Das eben beschriebene Operationswerk ist selbst nur der passive Teil unseres Prozessors. Es wird vom aktiv handelden Steuerwerk gesteuert. Eine weitere Designfrage war die Art der Kommunikation zwischen diesen beiden Teilen. Ein erster Ansatz von uns war, alle sinnvollen Kombinationen von internen Steuersignalen aufzulisten und zu Mikrocodes zusammenzufassen. Man würde in dieser Lösung nur den Mikrocode vom Steuerwerk aus anlegen und diese OPW-intern wieder in die korrekten Steuersignale zu dekodieren. Da wir im voraus nicht vorhersehen konnten, welche Kombinationen sinvoll sind, haben wir uns vorerst nicht für diese Variante entschieden uns aber den Weg dafür offengehalten. In unserer Lösung sind alle internen Steuersignale und der aktuelle Opcode ans Steuerwerk geführt.
Weiterhin haben wir uns Gedanken gemacht, welche Steueraufgaben wir intern im Steuerwerk schon erledigen können. Dadurch reduziert sich etwas die Komplexität des Steuerwerks und die Anzahl der nötigen Steuerleitungen. Details zu diesen Massnahmen sind unter dem Punkt Optimierungen zu finden.
Die Einzelheiten/Funktionalitäten der einzelnen Komponenten werden in den jeweiligen Abschnitten beschrieben.
2. CPU
Unsere Komponente CPU setzt sich aus den folgenden Komponenten (Verilog-Files) zusammen:
- steuerwerk.v, dem Steuerwerk
- timing.v, den Definitionen des Zeitverhaltens
- defs.v, weiteren allgemeinen Festlegungen
- opw.v, dem Operationswerk
- pc_unit.v, dem Programmzähler
- sram.v, dem Speicher
- register.v, einzelnen Registern
- mux_16bit_2to1.v, den 16 Bit-Multiplexern
- mux_3bit_2to1.v, den 3 Bit-Multiplexern
- mux_16bit_4to1.v, den 16 Bit-Multiplexern
- registersatz.v, dem Registersatz, welcher intern 8 Register beihnhaltet
- alu.v, dem arithmetischen Rechenwerk
- opc_to_alu_op.v, einem Konverter, welcher den Opcode in Alu-Operationen transformiert und damit das Steuerwerk entlastet
Abb. 2.1: Layout des Prozessors
Layout des Prozessors als PDF-Datei
Für eine grössere Darstellung auf das Bild klicken
3. Steuerwerk
Im Steuerwerk (
STEUERWERK.V) findet
sich das "Gehirn" unseres Prozessors wieder.
Das Steuerwerk ist ein Zustandsautomat vom Typ Moore. Als Eingabe erhält es vom Operationswerk den Opcode und die Statusflags der Alu.
Diese sind im Operationswerk in Registern gepuffert. Als Ausgaben produziert es einen ganzen Vektor von Steuersignalen der an das
Operationswerk weitergereicht wird und den nächsten Zustand in den es wechseln wird. Es gibt einen initialen Zustand (S0) der nur einmal nach
dem Reset verwendet. Klassisch folgen dann die Zustände für das Holen eines Befehls (S1) und das Dekodieren (S2). Schon früh in der
Entwicklungsphase des Prozessors erkannten wir, dass es durch die Wahl von parallelen Datenpfaden im Operationswerk möglich ist, auch in der
letzten Phase eines Befehls, durch setzen der richtigen Signale, den nächsten Befehl zu laden. Dadurch haben wir eine Art von "Command
prefetch" implementiert. Normalerweise muss zunächst im Zustand S4 der PC inkrementiert werden und etwas gewartet werden, bis der Speicher auf die neu angelegte Adresse reagiert. Erst dann wird in den Zustand S1 zum eigentlichen Holen des Befehls verzweigt. Die meisten Befehle
inkrementieren den PC, durch setzen des Steuersignals inc_pc schon in ihrer letzten Phase und können so den Zustand S4 überspringen und gehen
gleich zu S1. Dieses Prinzip des Überlappen von Ausführungsphasen eines Befehls haben wir dann kontinuierlich im ganzen Steuerwerk verwendet.
Schon im Zustand S2 (Dekodieren) werden nach dem Dekodieren die passenden Steuersignale für Befehle die den Speicher verwenden gesetzt.
Abhängig vom Opcode werden auch noch einige andere Steuersignale passend gesetzt. Durch die Überlappung von Phasen wurden die einzelnen
Zustände etwas komplexer, da es hierfür nötig war noch einmal Fallunterscheidungen zu machen. Durch die geschickte Realisierung des
Operationswerkes war es möglich alle arithmetischen Befehle und compare durch die gleiche Zustandsfolge abzuarbeiten. Die Quell- und
Zielregister werden vom OPW automatisch am Registersatz ausgewählt.
Weiterhin wird aus dem Opcode durch das Schaltnetz "OPC_to_Alu_OP" der richtige Befehlscode für die Alu erzeugt. Mit diesen Mechanismen war es nicht mehr nötig von Seiten des Steuerwerks zwischen verschiedenen Arithmetischen Befehlen zu unterscheiden. Alle Befehle haben es
gemeinsam, dass zunächst die Operanden an die Alu angelegt werden. Im Falle von monadischen Operationen wird der zweite einfach ignoriert. In
der nächsten Phase rechnet die Alu und darauf wird das Ergebnis der Alu zurückgeschrieben. Dies bereitet uns etwas Probleme beim Befehl comp,
der nur zwei Operanden vergleichen soll und keine Register verändern soll. Als kleine Kunstkniff wurde hierfür die Aluoperation zum ausgeben
von Operand A hinzugefügt und dieser Alu-Opcode automatisch beim Befehl comp durch OPC_to_Alu_OP erzeugt. Dafür dauert comp einen Takt länger als eigentlich nötig, aber es wurden einige Zustände gespart und das Design etwas vereinheitlicht. Nach diesen Prinzipien des Zusammenfassen von Zuständen und des Wiederbenutzen von Zustandsfolgen arbeitet das gesamte Steuerwerk und kommt deswegen mit verhältnismässig wenig Zuständen aus. Beispielsweise verwendet der Befehl JSR (Jump to Subroutine) nachdem er die Rücksprungadresse auf den Stack gerettet hat die gleiche Zustandsfolge wie der Befehl bra (unbedingter Sprung). Ebenso verzweigen alle bedingten Sprungbefehle nach erfolgreichem
Prüfen ihrer Bedingung in die gleiche Zustandsfoge. Mit diesen
Prinzipien haben wir die gebotene mögliche Parallelität des OPW
weitestgehends ausgenutzt. In den meisten Fällen war eine höhere
Parallelität nicht möglich, da auf den Speicher gewartet werden muss.
Durch diese bereits vorhandenen Massnamen erschien uns der Nutzen einer
Pipeline verhältnismässig gering. Einen sicherlich grösseren Vorteil
hätten Cachespeicher mit sich gebracht.
Kurze Zusammenfassung der Signale:
Tab. 3.1: Eingänge des Steuerwerks
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| opc |
6 Bit |
Opcode |
Von außen angelegter Operationscode für die nächste Instruktion |
| alu_flags |
4 Bit |
Steuerung |
Angelegte boolesche Flags der Recheneinheit |
| mode |
4 Bit |
Steuerung |
Der aktuelle Mode, welcher angibt, wie die zu verarbeitenden Werte geladen werden |
Tab. 3.2: Ausgänge des Steuerwerks
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| inc_pc |
1 Bit |
Steuerung |
erhöht den Programmzähler um eins
|
| load_pc |
1 Bit |
Steuerung |
der Programmzähler wird aus dem Memory Data Register geladen |
| ld_mdr |
1 Bit |
Steuerung |
Das Memory Data Register wird aktiviert (enabled) |
| ld_ir |
1 Bit |
Steuerung |
Das Instruction Register wird aktiviert (enabled) |
| ld_reg |
1 Bit |
Steuerung |
Der Registersatz wird aktiviert |
| mem_cs |
1 Bit |
Steuerung |
Chipselect (Aktivierung) des Speichers |
| mem_wr |
1 Bit |
Steuerung |
Der Speicher wird in den Lese- bzw. den Schreibmodus versetzt |
| mem_sflag |
1 Bit |
Steuerung |
Der Speicherinhalt wird in eine Datei geschrieben |
| sel_reg_a_src |
1 Bit |
Steuerung |
Dieses Signal steuert den Multiplexer, welcher für die Auswahl des Dateneinganges des Registersatzes verantwortlich ist. |
| sel_op_a, sel_op_b |
1 Bit |
Steuerung |
Diese beiden Signale steuern die beiden Multiplexer, welche die beiden Operanden (a und b) für die Recheneinheit ALU zur Verfügung stellen. |
| alu_go |
1 Bit |
Steuerung |
Dieses Signal aktiviert die ALU. Sie ist somit nicht getaktet. |
| clock |
1 Bit |
Steuerung |
Angelegter Takt |
| set_sel_a |
1 Bit |
Steuerung |
Mithilfe dieses Signales kann der Wert eines Registers des Registersatzes direkt beschrieben werden (Signal new_sel_a=1). |
| sel_mem_addr |
2 Bit |
Steuerung |
Durch dieses Signal wird der für die Speicher-Adressauswahl (adr des Speichers SRAM) verantwortliche Multiplexer gesteuert. |
| new_sel_a |
3 Bit |
Steuerung |
Dieses Signal dient der Steuerung des Multiplexers, der für die Auswahl des anzusteuernden Registers a im Registersatz zuständig ist. |
4. Register
Die Komponente Register (
REGISTER.V)
beschreibt ein 16 Bit breites Tri-State-Register. Von außen angelegte Daten
werden nach einer Zeitdauer von 10 ns zum Ausgang
do durchgeschaltet.
Der Ausgang selbst befindet sich anfangs in einem hochohmigen Zustand, nach Aktivierung des Registes (
load-Signal) aber
liefert es die zuvor übernommenen Daten.
Der Zustand des Ausgangs richtet sich nach dem LOAD-Signal. Ist es während
einer ansteigenden Flanke auf Eins gesetzt, so wird der Ausgang
do aktiv und
liefert so lange Daten, bis das LOAD-Signal an einer ansteigenden Flanke auf
Null gesetzt ist.
Die Signale des Registers sind im einzelnen:
Tab. 4.1: Eingänge des Registers
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| clock |
1 Bit |
Takt |
Angelegter Takt |
| load |
1 Bit |
Steuerung |
Das LOAD-Signal steuert den Zustand des Ausgangs do. Ist das ENABLE-Signal
während einer ansteigenden Flanke auf Eins, so wird für die Dauer des nächsten
Taktes ein Datum am Register-Ausgang do anliegen. Andernfalls behält der
Ausgang den zuvor geladenen Wert. |
| di |
16 Bit |
Daten |
Angelegte Daten, welche bei einer steigenden Flanke übernommen werden,
sofern das LOAD-Signal auf Eins ist. Die Daten liegen frühestens nach 10 ns
am Ausgang do an. |
Tab. 4.2: Ausgänge des Registers
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| do |
16 Bit |
Daten |
Register-Ausgang, der sich entweder im hochohmigen Zustand befindet oder
aber Daten ausgibt. Der Zustand des Ausgangs richtet sich nach dem ENABLE-Signal,
mit welchem man an jeder ansteigenden Flanke den Zustand ändern kann. |
5. Registersatz
Die Komponente Registersatz (
Registersatz.v) dient als Kapselung der
acht vorgegebenen einzelnen Register. Der Registersatz erhält seine Daten entweder vom Memory Data Register oder
alternativ als Berechnungsergebnis des Rechenwerkes (der ALU). Durch Multiplexer können alle acht internen Register einzeln
ausgelesen bzw. kann in jedes der Register ein Wert geschrieben werden. Der Registersatz liefert die Opernanden für die
Recheneinheit (ALU). Diese beiden zu wählenden Operanden sind ebenfalls durch Multiplexer wählbar.
Tab. 5.1: Eingänge des Registersatzes
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| clock |
1 Bit |
Takt |
Angelegter Takt |
| load |
1 Bit |
Steuerung |
Das LOAD-Signal steuert die Datenspeicherung des Registersatzes bzw. der darin enthaltenen acht Register. Ist das LOAD-Signal
während einer ansteigenden Flanke auf Eins, so wird für die Dauer des nächsten
Taktes das am Registersatz anliegende Datum a_in in das durch sel_a ausgewählte Register geschrieben.
Andernfalls behalten die Register die zuvor geladenen Werte. |
| a_in |
16 Bit |
Daten |
16 Bit-Dateneingang des Registersatzes, welcher bei aktivem load-Signal in ein ausgewähltes Register geschrieben wird. |
| sel_a, sel_b |
3 Bit |
Steuerung |
Diese Signale dienen der Auswahl der im Registersatz enthaltenen Register. Im Falle des Schreibens (load-Signal = 1) des am Registersatz anliegenden Datums in ein Register, wählt man mit sel_a das Register aus, in welches dieses Datum gespeichert werden soll. Möchte man die Werte einzelner Register an die beiden Ausgänge a_out und b_outschalten, so wählt man mit diesen beiden Signalen diejenigen Register für die Durchschaltung aus. |
Tab. 5.2: Ausgänge des Registersatzes
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| a_out, b_out |
16 Bit |
Daten |
Registersatz-Ausgänge, die bei einer ansteigenden Flanke des Taktes die Werte der durch sel_a und sel_b gewählten Register am den jeweiligen Ausgang (sel_a für Ausgang a_out bzw. sel_b für b_out) annehmen. |
6. SRAM
Die Komponente SRAM (
SRAM.V) wurde von uns aus den
Praktikumsvorgaben übernommen.
Tab. 6.1: Eingänge des Speichers
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| CS |
1 Bit |
Steuerung |
Active Hi Chip select |
| WR |
1 Bit |
Steuerung |
Active High write control |
| ABUS |
16 Bit |
Adresse |
16 Bit Adress-Bus |
| SFLAG |
1 Bit |
Steuerung |
Signal, um den Speicher in eine Datei zu schreiben |
Tab. 6.1: Ein- und Ausgang des Speichers
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| DATABUS |
16 Bit |
Daten |
16 Bit Daten-Bus |
7. ALU
Die ALU (
ALU.V) übernimmt, sobald der Befehlscode (
Tab. 7.4) nicht ALUOP_NOP ist, alle anliegenden Daten an
A, B, OP und FLAGMASK in die internen Register.
Anschließend wird die Berechnung gestartet. Diese dauert ???. Je nach
Opcode werden gleichzeitig die Flags OVERFLOW und CARRY berechnet. Im Takt nach
der eigentlichen Berechnung werden noch NEGATIVE und ZERO ermittelt und die
Flags wiederum durch die FLAGMASK ausmaskiert, sodass nur die gewählten Flags
geändert werden.
Tab. 7.1: Eingänge der ALU
| Bezeichnung |
Breite |
Datentyp |
Eigenschaften |
Beschreibung |
| a |
16 bit |
Daten |
Register |
Daten: Operand A für die arithmetische Berechnung |
| b |
16 bit |
Daten |
Register |
Daten: Operand B für die arithmetische Berechnung |
| op |
4 bit |
Steuerung |
Daten |
ALU Befehlscode |
| alu_go |
1 bit |
Steuerung |
Signal |
Mithilfe dieses Signales wird die Recheneinheit aktiviert (enabled) |
Tab. 7.2: Ausgänge der ALU
| Bezeichnung |
Breite |
Datentyp |
Eigenschaften |
Beschreibung |
| result |
16 bit |
Daten |
Tristate |
Ergebnis der Berechnung |
| flags |
4 bit |
Status |
Register |
Flags (Tab. 7.3) für das Steuerwerk |
Tab. 7.3: Statusflags der ALU
| Bezeichnung | Beschreibung |
| Overflow | Meldet einen Überlauf bei einer Berechnung mit vorzeichenbehafteten Integers. |
| Carry | Signalisiert einen Überlauf bei einer Berechnung mit vorzeichenlosen Zahlen. |
| Negativ | Das Ergebnis ist negativ. |
| Zero | Das Ergebnis ist, unabhängig von der Berechnung, Null. |
Tab. 7.4: Befehle der ALU
| Bezeichnung |
Wert |
Parameter |
Flags |
Beschreibung |
| ALUOP_NOP | 0 | - | ---- |
Signalisiert der ALU, dass kein Befehl anliegt. |
| ALUOP_AND | 1 | SU: A | --NZ | Bitweises Und |
| ALUOP_OR | 2 | SU: A | --NZ | Bitweises Oder |
| ALUOP_NOT | 3 | SU: A | --NZ | Bitweises Nicht |
| ALUOP_XOR | 4 | SU: A | --NZ |
Bitweises exklusives Oder |
| ALUOP_ADD | 5 | SU: A, B | OCNZ |
Addition zweier 16 bit Zahlen ohne Carry |
| ALUOP_SUB | 6 | SU: A, B | OCNZ |
Subtraktion ohne Carry |
| ALUOP_MULH | 7 | -U? A, B | O-NZ*) |
Oberer Teil des Ergebnisses der Multiplikation einer vorzeichenlosen 16 bit Zahl |
| ALUOP_MULL | 8 | -U? A, B | O-NZ*) | Unterer Teil |
| ALUOP_DIV | 9 | -U? A, B | O-NZ*) | Division |
| ALUOP_MOD | A | -U? A, B | O-NZ*) |
Modulo, Rest der Division |
| ALUOP_NEG | B | S-: A | O-NZ |
Umkehren des Vorzeichens |
| ALUOP_COMP | C | SU: A, B | OCNZ |
Mithilfe dieses Befehles werden die Flags berechnet, als Ausgabe der ALU wird der Operand A durchgeschaltet |
| ALUOP_INC_A | B | SU: A | --NZ |
Der Wert des Operanden A wird um eins erhöht |
| ALUOP_B | B | SU: B | --NZ |
Der Wert des Operanden B wird unverändert zum Ausgang durchgeschaltet. |
| ALUOP_DEC_A | B | SU: A | --NZ |
Der Wert des Operanden A wird um eins vermindert |
| SU |
Signalisiert, ob die Parameter der Berechnung vorzeichenbehaftet oder -los sein müssen. |
| OCNZ |
Welche Flags können von diesem Befehl auf eins gesetzt werden? |
| *) |
Das Overflow Flag repräsentiert hier nicht den klassischen Überlauf, da der in
diesen Fällen nicht auftreten kann, sondern zeigt an, ob das Ergebnis des
zugehörigen Befehls (MULH zu MULL, bzw. DIV zu MOD und umgekehrt) ungleich
Null ist. |
| ? |
Wir haben in diesen Fällen noch keine expliziten Überlegungen angestellt,
inwieweit die Befehle sowohl mit vorzeichenbehafteten als auch -losen Integers
korrekt rechnen können. Vermutlich funktionieren sie nur mit vorzeichenlosen.
|
8. Opcode in Alu-Operation umwandeln (opc_to_alu_op)
Diese Komponente (
opc_to_alu_op) dient der Entlastung des Steuerwerkes.
Durch die automatische Umsetzung von Opcodes, welche vom Instruction Register kommen und direkt in Alu-Operationen umgesetzt werden können,
erspart man sich den umständlichen und zeitintensiven Weg über das Steuerwerk. Ein weiterer erreichter Optimierungsvorteil ist die Reduzierung
der Steuerleitungen.
Diese Maßnahmen haben dazu geführt, dass alle Befehle, welche von der ALU berechnet werden (arithmetische Befehle), völlig identisch abgearbeitet
werden können, was zu einer deutlichen Vereinfachung und Entlastung führt.
Tab. 8.1: Eingänge von opc_to_alu_op
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| opc |
6 Bit |
Opcode |
Der am Eingang der Komponente anliegende Opcode aus dem Instruction Register |
Tab. 8.2: Ausgänge von opc_to_alu_op
| Bezeichnung |
Breite |
Datentyp |
Beschreibung |
| alu_op |
4 Bit |
Opcode |
Aus dem am Eingang anliegenden Opcode wird ein für die ALU verwendbarer Operationsbefehl erzeugt, welcher dann am Ausgang alu_op
anliegt. |
9. Testumgebung
Die Assembler-Testprogamme in
\ASM\ beschränken sich darauf,
die korrekte Arbeitsweise für einige beispielhafte Fälle zu
überprüfen:
10. Befehlssatz
Tab. 10.1: Befehlsformat Binär
| Bits |
15 14 13 12 11 10 |
9 8 7 6 |
5 4 3 |
2 1 0 |
| Feld |
Opcode | Mode | Register A | Register B |
| Breite |
6 | 4 | 3 | 3 |
Tab. 10.2: Adressierungsarten Assembler
| Adressierung | Format | Beispiel |
| MA | Adress (or identifier) | LDA 55, LDA EMIL |
| MRLIT | Reg # Literal | SHR R1 #3 |
| MRA | Reg Adress (or identifier) | MOV R2 55, MOV R2 EMIL |
| MRR | Reg Reg | AND R1 R2 |
| MRRI | Reg (Reg) | AND R1 (R2) |
Bei unserer Implementierung haben wir die fünf oben aufgeführten, von uns unterstützten, Adressierungsarten
(
Tab. 10.2) gewählt. Ein Assemblerbefehl besteht also immer aus
einem Parameter für die Bedingungen. Darauf folgt eine Adresse, Konstante oder
die Bitmaske, die das Setzen der Flags kontrolliert.
Tab. 10.3: Befehlssatz
| Mnemonic | Klasse | Code | Parameter | Beschreibung |
| nop | Steuerbefehle | 0 | - | Keine Operation |
| stop | Steuerbefehle | 1 | Bedingung | Simulation beenden |
| jsr | Steuerbefehle | 2 | Bedingung | Unterprogrammaufruf |
| ret | Steuerbefehle | 3 | Bedingung | Rückkehr aus Unterprogramm |
| bra | Steuerbefehle | 4 | Bedingung, Sprungadresse | Unbedingter Sprung |
| beq | Steuerbefehle | 5 | Bedingung, Sprungadresse | Branch Equal |
| bgt | Steuerbefehle | 6 | Bedingung, Sprungadresse | Branch Greater Than |
| blt | Steuerbefehle | 7 | Bedingung, Sprungadresse | Branch Less Than |
| boo | Steuerbefehle | 8 | Bedingung, Sprungadresse | Branch On Overflow |
| boc | Steuerbefehle | 9 | Bedingung, Sprungadresse | Branch On Carry |
| add | Arithmetische Befehle | 16 | Bedingung, Flag-Maske | Addition |
| sub | Arithmetische Befehle | 17 | Bedingung, Flag-Maske | Subtraktion |
| multh | Arithmetische Befehle | 18 | Bedingung, Flag-Maske | Untere 16 Bit einer Multiplikation |
| multl | Arithmetische Befehle | 19 | Bedingung, Flag-Maske | Obere 16 Bit einer Multiplikation |
| div | Arithmetische Befehle | 10 | Bedingung, Flag-Maske | Division |
| mod | Arithmetische Befehle | 21 | Bedingung, Flag-Maske | Rechnen mit Rest (modulo) |
| neg | Arithmetische Befehle | 22 | Bedingung, Flag-Maske | Negation |
| comp | Arithmetische Befehle | 23 | Bedingung, Flag-Maske | Vergleich |
| nicht | Logische Befehle | 32 | Bedingung, Flag-Maske | Negation |
| und | Logische Befehle | 33 | Bedingung, Flag-Maske | Und |
| oder | Logische Befehle | 34 | Bedingung, Flag-Maske | Oder |
| xoder | Logische Befehle | 37 | Bedingung, Flag-Maske | Entweder Oder |
| load | Transferbefehle | 48 | Bedingung, Flag-Maske | Laden in Register |
| store | Transferbefehle | 49 | Bedingung, Flag-Maske | Schreiben in Register |
Wir haben eine umfassende Palette von Befehlen erstellt
(
Tab. 10.3), wenn auch noch einige fehlen. Es
existieren viele Stackbefehle, da beispielsweise ein DEL zum Löschen des
obersten Stackdatums etwa 100 mal schneller ist, als ein Workaround mittels POP
in den Speicher. Wir haben nur die nötigsten ALU-Befehle, auch wenn dies
möglicherweise Gweschwindigkeitsverlust bedeutet. ALLE arithmetischen und
logischen Befehle kann man nicht implementieren. Für Sprünge konnten wir aus auf
drei Befehle beschränken, da Bedingungen ja bereits berücksichtigt sind und
relative Sprünge aufgrund des Befehlsformats (eine Adresse ist immer als zweites
16 bit Wort kodiert) nicht sinnvoll sind.
11. Optimierungen
Wie es schon in der Einleitung erwähnt wurde, haben wir teilweise die Steueraufgaben im Operationswerk erledigt.
Als weitere Optimierungsmaßnahme haben wir auf Grund von der Analyse, die wir durchgeführt haben, Zustände, die zusammengefügt
werden können, zusammengebaut. Das hat uns erlaubt, das ganze Struktur zu vereinfachen. Da wir unsere Komponenten direkt miteinander
verbunden haben, haben wir hier einen großen Grad an Parallelität gewonnen. Ferner wird bei uns Prefetch realisiert, indem in manchen
Zustände schon Steuersignale für die nächsten Zustände vorbereitet werden und PC Weiterschaltung erfolgt.
12. Weitere Verbesserungen
Eine sicherlich sehr effektive Massnahme wäre die Verwendung von Caches. Der groesste Engpass in unserer CPU ist der Speicher. Bei der Abarbeitung von Befehlen wird der grossteil der Zeit damit verbracht auf den Speicher zu warten. Deswegen ist dadurch ein hoher Grad an Beschleunigung zu erwarten. Allerdings erfordert die Verwendung von Caches einige Modifikationen des Steuerwerks. Im jetzigen Steuerwerk sind feste Wartezustaende fuer den Speicher vorgesehen. Ein Cache ohne Anpassung des Steuerwerks wuerde hier keine Beschleunigung bringen.
Mit eingebauten Caches wuerde auch eine Pipeline wieder Beschleunigung bringen. Mit der jetzigen Architekur (ohne Cache) ist die Parallelitaet nahezu ausgereizt. Weiterhin koennte man noch den Befehlssatz ausbauen und weitere Adressierungsarten unterstuetzen. Dadurch wuerde sich die Codegroesse signifikant verringern und dadurch zu einem besseren Durchsatz fuehren.
13. Prozessor Benchmarks
Sortieren von 15 vorzeichenbehafteten Zahlen : 408960
rekursive Berechnung der Summe der Zahlen von 1..100 : 291950
Ermittlung des GGT nach Euklid (1482, 646) : 18660
14. Feedback zum PeP
Als Tipp koennen wir nachfolgenden Generationen empfehlen den mitgelieferten Assembler von Anfang an zu benutzen. Kritik moechten wir an dem Tool "Verilogger" ueben. Die 1000 Zeilenbeschraenkung ist sehr stoerend und auch die Pruefung der Syntax ist mangelhaft. Hier waere ein besseres Tool wuenschenswert.
A. Terminologie
In den Tabellen über Signalleitungen verwenden wir diverse Bezeichnungen zur
Charakterisierung der Interface-Eigenschaften. So beschreibt der Datentyp
- Steuerung eine aktive, synchrone Steuerung durch eine eher
übergeordnete Komponente wie das Steuerwerk,
- Status eher eine passive, asynchrone und länger andauernde
Rückmeldung einer untergeordneten Einheit,
- Daten einen meist bidirektionalen Bus, der Daten und Adressen
transportiert,
- Adressen ein Bündel Leitungen allein zum Anlegen von Adressen und
- Takt die spezielle Leitung, die den Takt verteilt.
Für die Eigenschaft eines Interfaces bedeutet
- Register, dass der Wert am Eingang synchron zum Takt übernommen wird
und nicht dauerhaft anliegen muss. Für die Beschreibung eines Ausgangs ist die
Bezeichnung eher ungeeignet. Man kann annehmen, dass der Wert am Ausgang
längere Zeit anliegt.
- Ein Tristate Ausgang treibt im allgemeinen eine bidirektionale
Leitung und ist nur kurzzeitig aktiv.
- Synchron weißt darauf hin, dass die Komponente nur bei einer
Taktflanke den Wert dieses Eingangs überprüft.
B. Abbildungen
C. Tabellen