Wir sind ja das letzte Mal stehen geblieben bzw.
wir haben das letzte Mal diskutiert, wie ein Prozessor aufgebaut ist,
wie die Architektur ausschaut oder wie die Architektur auszusehen hat
und haben als Beispiel die Architektur des MIPS Mikroprozessors
besprochen.
Wir haben von allgemeinen Punkten, die zu einer Befehlssatzarchitektur
gehören, also welche Datentypen, Datenformate, wie schaut das
Befehlsformat aus, welche Befehle werden unterstützt durch die
Hardware, welche Adressierungsarten sind notwendig bzw.
werden dann von der Hardware mit unterstützt.
Das sind alles ganz wichtige Punkte, die gerade in den 70er und 80er
Jahren eine wesentliche Rolle gespielt haben, wie ein Prozessor auch
zu implementieren ist.
Und heute möchte ich das ganze Kapitel weiterführen zu der Diskussion,
wie werden jetzt eigentlich Prozessoren implementiert.
Und das ist letztendlich die Diskussion, die in den 80er Jahren
geführt worden ist, nämlich werden komplexe Befehlssätze, komplexe
Architekturen implementiert oder werden reduzierte Architekturen,
Architekturen mit reduzierten Befehlssätzen, eingeschränkten
Adressierungsarten usw.
angeboten.
Reicht es aus für die Unterstützung höherer Programmiersprachen,
können auch damit Programme effizient ausgeführt werden.
Und wir haben ja das letzte Mal gesehen, wie für einen typischen RISC
-Mikroprozessor, also einen Prozessor, der dieser RISC-Philosophie
folgt, Reduced Instruction Set Architectures, die Architektur
ausschaut, das Programmiermodell ausschaut, die Sicht des
Maschinenprogrammierers oder Compilers aussieht.
Und das war dieses feste Maschinenbefehlsformat, die Befehle, die
angeboten werden konnten, wie schauen die Register aus, die eine
solche Architektur anbietet, alle diese Fragen haben wir beispielhaft
an der MIPS-Architektur erklärt.
Sie haben ja auch schon Übungen gemacht zur Assemblerprogrammierung
mit RISC.
Und im Gegensatz, um sozusagen dieses alternative RISC-Konzept mit zu
verstehen, habe ich jetzt einmal ein paar Folien, wie es bei der
klassischen Intel-Architektur aussieht oder wie das Programmiermodell
aussieht, das ein 80x86 Programmierer sieht.
Also 80x86 steht für die Intel-Architektur.
Ab dem 80386 war das die sogenannte IA32-Architektur.
Und 80x86 steht für eine Familie von Prozessoren der Familie der Firma
Intel.
Angefangen vom 80x86, 100x86, 2x86, also hier sind die Nummerierungen
weitergegangen bis zum 80486.
Dann hat man statt einer Zahl eben mit Namen weitergemacht, was
rechtliche Gründe auch hatte bei Intel und ist mit dem Pentium
weitergefahren.
Also nicht mit dem 80586 und so weiter.
Es gab mal nur als Nebenbemerkung vielleicht einen Rechtsstreit
zwischen AMD und Intel, weil Intel ja als Second Source-Hersteller
zunächst in den frühen 70er, späten 70er Jahren auch und Anfang der
80er Jahre auch die Lizenzen von Intel gekauft hat, um Intel
-Prozessoren als sogenannte Second Source-Hersteller mit zu
produzieren.
Der Grund war letztendlich, dass Rechnerhersteller nicht sich allein
auf Intel verlassen wollten, wenn sie Rechner bauen wollten, dass
Intel die Prozessoren liefert, sondern dass eben auch ein anderer
Hersteller da ist, der in der Lage ist, diese Prozessoren, die
binärkompatibel sind und nicht nur binärkompatibel, sondern auch von
der Hardware her kompatibel sind, auch liefern kann.
Der Hintergrund ist der, Intel saß im Silicon Valley damals nur oder
nur mit den Fabriken im Silicon Valley und das ist ja bekanntermaßen
eine ziemlich erdbebenanfällige Gegend, sodass man ja auch damit
rechnen musste, dass so eine Fabrik einmal zerstört würde.
Das würde natürlich dann die Rechnerhersteller auch irgendwo heftig
treffen, wenn dann das Herz ausfällt, wenn es nicht mehr geliefert
werden konnte.
Das war der Hintergrund, weshalb Rechnerhersteller eben verlangten,
dass es auch weitere Anbieter von Prozessoren gab.
Und Mitte der 80er Jahre ist dann Intel hergegangen und hat dieses
Second Source Abkommen aufgekündigt, was AMD natürlich nicht unbedingt
zu Recht gewesen ist, dann gab es gerichtliche Auseinandersetzungen,
die eben dann dazu geführt haben, dass ja Intel nicht mehr die Namen
weitergeführt hat, die Zahlen weitergeführt hat, sondern Namen
eingeführt hat, weil die rechtlich gestützt sind.
Aber wie Sie ja gesehen haben, man hat sich auch entsprechend
geeinigt, AMD liefert ja weiterhin sehr interessante Prozessoren, die
der IA-32-Architektur entsprechen und entsprechend kompatibel sind.
Also nur das zur Bezeichnung Intel 80x86 und Familie.
Und anfangen können wir zunächst mit dem 8086, also einem Prozessor,
der so Mitte der 80er Jahre von Intel auf den Markt gekommen ist,
Nachfolger von den 8008 Mikroprozessoren.
Und Sie sehen jetzt das Programmiermodell, also die Sicht, wie es der
Maschinenprogrammierer dann auch sieht.
Wir haben eine Menge von Allzweckregistern, im Unterschied zu den MIPS
-Prozessoren sind hier 16-Bit-Register zur Verfügung gestellt worden
und relativ wenige Register.
Wir haben als Allzweckregister das Register AX, BX und CX.
Hier können Daten mitgehandelt werden.
Sie sehen, die haben nicht unbedingt Allzweckcharakter, sondern haben
zum Teil auch noch spezielle Bedeutungen.
Das heißt, hier gibt es dann Befehle, die implizit diese Register
verwenden für ihre Aufgaben.
Die Allzweckregister enthalten einen Akkumulator.
Hier wird fortgesetzt, was bei den 8008 Prozessoren eingeführt oder
noch früher beim 4004 eingeführt worden ist, nämlich dass wir ein
ausgezeichnetes Register haben, in dem immer ein Ergebnis gespeichert
wird.
Dazu gibt es auch Befehle, die dieses AX-Register implizit
entsprechend verwenden.
Für weitere Rechner gibt es noch ein weiteres Datenregister, das DX
-Register.
Ein Register, was für Zählerwerte verwendet wird, wenn man Befehle
verwendet, bei denen gezählt werden soll.
Dann haben wir so ein CX-Register, das implizit auch als Counter
-Register genommen wird und zusätzlich noch ein Basisregister.
Diese 16-Bit-Register, die mit dem 8086 eingeführt worden ist, sind
vorher noch als 8-Bit-Register zur Verfügung gewesen und hier gab es
dann sozusagen einen niederwertigen Teil und einen höherwertigen 8-Bit
-Teil, der entsprechend angesprochen werden konnte.
Das ist sozusagen die Folge der Kompatibilität, sodass auch Programme
von 8008 Prozessoren hier noch mitlaufen können.
Man kann also nicht nur das AX-Register im Befehl ansprechen, sondern
eben auch das AL-Register und spricht damit automatisch den
niederwertigen Teil dieses AX-Registers an mit H.
A-H ist dann der höherwertige Teil angesprochen.
Für die Adressrechnung, wir haben eine ganze Reihe von
Adressierungsarten, stehen weitere Register zur Verfügung, S-I und D
-I, was im Wesentlichen auch als Indexregister verwendet wird.
Also beispielsweise, wenn wir Basisrelative oder indizierte
Adressierung verwenden, dann wird dieses Register verwendet, wobei
dann der BP als Zeiger mit als Basisregister verwendet werden kann
oder eben auch eines dieser Allzweckregister.
Dann haben wir einen Kellerzeiger, der auch das oberste Kellerelement
zeigt, um eben auch Unterstützung zu bieten für Prozeduraufrufe und
wir haben den Befehlszähler.
Dann ist bei der 80 oder 86 Architektur sind weitere Register mit
dabei, nämlich die sogenannten Adress- und Segmentregister.
Die Intel-Architektur hat 8086 sogenannte Segmente eingeführt, das
sind Adressräume, virtuelle Adressräume, in denen Daten oder Befehle
stehen können und diese Segmentregister enthalten Informationen,
sodass damit auf den Anfang eines Segments gezeigt werden kann.
Wenn wir diese, und das ist eigentlich ein schönes Beispiel auch, wie
sich eine Architektur von den Anfängen an bis hin zu den heutigen
aktuellen Prozessoren weiterentwickelt hat und dabei immer versucht
worden ist, die Kompatibilität zu bewahren, das heißt, die
Aufwärtskompatibilität, dass also Prozessoren oder Programme, die auf
Prozessoren früherer Generationen liefen, auch auf
Nachfolgerprozessoren gelaufen sind.
Umgekehrt gilt das nicht, also Programme, die heute auf einem Pentium
4 laufen, sind nicht ablaufbar, auf einem Rechner sollte man ihn noch
zur Verfügung haben, der mit einem 8086 Prozessor arbeitet, aber
umgekehrt soll es möglich sein.
Und beim 8080 haben wir 8 Bitregister gehabt, AL-Register, ADL und DH,
CL und CH, BL und BH und die sind dann bei dem 8086 als 16 Bitregister
zusammengeführt worden zu den DX, CX und BX-Registern.
AL wurde dann auch zusammen mit dem 16 Bitregister zusammengefasst,
sodass man dort auch noch das Prozessorstatuswort enthalten hatte,
also der den Zustand festgehalten hat.
Also diese Registerarchitektur, wie sie der 8086 zur Verfügung steht
ist letztendlich, oder wie wir sie auch heute noch zum Teil finden,
hat ihren Ursprung bei den frühen 8080, 8008 Prozessoren, die zunächst
eben als 8 Bit bzw.
16 Bit zur Verfügung standen, beim 8086 eben dann zusammengefasst als
16 Bitregister zur Verfügung gestanden haben.
Also beim 8086 sind dann eben alle Register zu 16 Bitregister
geworden, also entsprechend ansprechbar.
Und wie gesagt, aus Kompatibilitätsgründen konnte man die höhere und
die niederwertige Hälfte dieser Register auch im Befehl explizit
spezifizieren, eben dadurch, dass man AL beispielsweise oder AH
angegeben hat.
Und diese Segmentregister, die dienen eben auch damit, einen
virtuellen Adressraum zur Verfügung zu stellen, sodass man über die 16
Bit Adressierung hinauskommt und eine Erweiterung des Adressraums hat.
Dadurch nämlich, dass ich sage, ich habe 16 Bit zur Verfügung, ich
habe einen 16 Bit Mikroprozessor beim 8086, möchte aber einen größeren
Adressraum haben, ist man dazu übergegangen, eben sogenannte Segmente
bereitzustellen.
Segmente sind dem Programmierer sichtbar und über die zusätzliche
Information, jedes Segment selber ist dann eben adressierbar, oder
jede Stelle in diesem Segment ist adressierbar über die 16 Bit
Adresse.
Wenn dann zusätzlich in den Segmentregistern Informationen
bereitgehalten wird, wo beispielsweise dieses Segment im Adressraum
beginnt und wenn ich entsprechende Schutzmechanismen einbaue, die der
Prozessor unterstützt, dann habe ich sozusagen eine Erweiterung dieses
Adressraums auf 20 Bit, sodass ich mit 1 Megabyte auskomme.
Also diese Segmentregister dienen mit zur Erweiterung des Adressraums.
Ab dem 80386 ist dann die IA32 Architektur von Intel eingeführt
worden, also die 32 Bit Architektur.
Aber auch da wollte man nicht die Kompatibilität zu den
Vorgängerprozessoren verlieren, wollte also unbedingt auch
gewährleisten, dass, wenn auch in bestimmten Arbeitsmodi, auf diesem
80386 Programme, die auf Vorgängerprozessoren liefen, auch hier
ausführbar sind.
Und jetzt sind wir beim 32 Bit Register.
Jetzt haben wir die Allzweckregister, diese allgemeinen Register,
diese acht Register, die jetzt als Allzweckregister dienen, aber eben
schon auch als Kompatibilitätsgründen zu führen und auch aus Gründen,
die in der Befehlssatzarchitektur dann liegen, mit Spezialfunktionen
versehen sind.
Das heißt, dass es bestimmte Befehle gibt, die implizit bestimmte
Register verwenden, für beispielsweise Zähler zu erhöhen oder für als
Akkumulator, um ein Ergebnis zu speichern oder ähnliches.
Diese Register heißen jetzt EAX, das heißt, E steht für die
Erweiterung eben hin zu einer 32 Bit Architektur, eben zu einer
Erweiterung auf ein 32 Bit Register.
Wir haben Datenregister und dieses Datenregister ist insbesondere bei
den Ein-Ausgabe-Befehlen implizit mit zu verwenden als Ein-Ausgabe
-Register.
Wir haben ein Zählerregister, wenn wir Schleifen programmieren wollen.
Wir haben Basisregister und Indexregister für eine Reihe von
Adressierungsarten, wo man eben registerrelativ mit Index arbeiten
muss.
Und eben der Stack ist ebenfalls mit dabei, der Stackregister zur
Abspeicherung des Kellerzeigers, eben um hier Prozeduren mit
unterstützen zu können.
Wir haben dann spezielle Register und mit dem 80386 ist das Konzept
der Segmente weitergeführt worden.
Und zwar ist jetzt ein Segment ein virtueller Adressraum, der vier
Megabyte umfasst.
Und die Segmentregister dienen jetzt dazu, ein Segment, das gerade
aktuell ist, zu spezifizieren.
Man unterscheidet zwischen Daten- und Code-Segmenten sowie Stack
-Segmenten.
Und jedes Segment, das man spezifiziert, ist ein virtueller
Adressraum, in dem hier gearbeitet werden kann.
Und diese Segmentregister, die dienen dazu, auf einen Eintrag in einer
Tabelle zu zeigen.
Und dieser Eintrag enthält jetzt alle Informationen, die notwendig
sind, um ein Segment, also einen virtuellen Adressraum, zu
spezifizieren.
Anfangsadresse des virtuellen Adressraums, die Länge des virtuellen
Adressraums und welche Schutzattribute dieser virtuelle Adressraum
hat.
Wenn ich virtuelle Adressräume habe, brauche ich ja auch
Schutzmechanismen, damit nicht durch Multitaskingbetrieb, also
Mehrbenutzerbetrieb oder Mehrprogrammbetrieb, sich die einzelnen
Segmente gegenseitig behindern oder dass man hier auf verkehrte
Segmente zugreift.
Dies ist ein relativ komplexer und aufwendiger Mechanismus, der weit
über das hinausgeht, was man bei einer virtuellen Speicherverwaltung
vielleicht erwartet, nämlich, dass man mit Seiten arbeitet.
Der Seitenmechanismus, der zur Unterstützung der virtuellen
Speicherverwaltung notwendig ist, wird auch unterstützt, aber erst
nachfolgend nach dieser Segmentierung.
Als weitere Spezialregister, neben dem Befehlsteller, der jetzt auch
32 -Bit ist, hat man eben Statusregister und Steuer und mehrere
Steuerregister.
Das Statusregister enthält dann eben die entsprechenden Statussignale,
die von der arithmetischen und logischen Einheit generiert werden und
darüber hinaus noch weitere Statussignale.
Was dann eingeführt worden ist, sind eine ganze Reihe weiterer
Steuerregister, die jetzt auch dafür sorgen, dass der Prozessor in
bestimmten Modi arbeiten kann.
Wir haben ab dem 80.3.86 eine ganze Reihe von Komponenten auf dem
Baustein, die dafür sorgen, dass die Aufgaben des Betriebssystems
relativ schnell ausgeführt werden können.
Also Allzweck-Mikroprozessoren brauchen solche Funktionen für die
Unterstützung von Betriebssystemen, dass diese Aufgaben auch möglichst
schnell ausgeführt werden.
Und dafür sind Steuerinformationen notwendig, die in verschiedenen
Registern geliefert werden.
Also, da sind zum Beispiel Steuersignale enthalten wie das Paging-Flag
oder Paging-Enable-Flag.
Das wird gesetzt, wenn die Speicherverwaltungseinheit, die auf dem
Prozessor enthalten ist, eben verwendet werden soll.
Oder, wenn hier bestimmte Prozessor-Erweiterungen über eine
Coprocessor -Schnittstelle möglich sind.
Wenn also ein Coprocessor angeschlossen wird, wird es angezeigt.
Wenn ein Prozesswechsel stattfindet, gibt dieses Task-Spitz
entsprechend die Auskunft.
Oder weitere Geschichten.
Ich habe dieses Emulate-Coprocessor genannt, dieses Emulate
-Coprocessor -Bit.
Wenn Sie sich erinnern, bei der Architektur, habe ich gesagt, von der
Architektur her werden bestimmte Befehle oder Datentypen unterstützt.
Beispielsweise bei der Gleitkommaverarbeitung.
Und wenn die Gleitkommaverarbeitung per Software auszuführen ist, weil
auf dem Prozessor keine Gleitkommaneinheit verfügbar ist, oder auch
wenn über die Coprocessor-Schnittstelle kein Gleitkommaprozessor
zusätzlich angeschlossen ist, dann braucht man diese Software
-Emulation von Gleitkommabefehlen.
In der Architektur ist sie vorhanden.
Das heißt, der Programmierer kann Gleitkommabefehle verwenden, aber ob
es die Hardware ausführen kann oder per Software ausgeführt werden
muss, entscheidet sich eben, ob eine entsprechende Komponente
vorhanden ist oder nicht.
Und über dieses Emulate-Coprocessor wird jetzt angezeigt, ob jetzt der
Prozessor auf die Coprocessor-Schnittstelle zugreifen kann und bei
Gleitkommabefehlen den Auftrag an den Coprocessor weitergeben kann,
oder ob über einen entsprechenden Task-Mechanismus, einen Trap
-Mechanismus, dann der Software oder die Software-Emulation
angesprungen werden muss, die dann eben ein Gleitkommabefehl emuliert
und in der Software ausführt.
Also hier sind dann diese Komponenten vorhanden, ab dem 3.86
entsprechend eingeführt worden, weil eben beispielsweise der 80.3.86
keine eigene Gleitkommaneinheit hatte, sondern einen Coprocessor,
einen 80.3.87, der nichts anderes als Gleitkommaverarbeitung konnte,
der aber auf einem extra Chip vorhanden war und über eine spezielle
Prozessor -Schnittstelle, der Coprocessor-Schnittstelle, an den
Prozessor angeschlossen worden ist.
Zusätzlich sind noch Monitor-Möglichkeiten gegeben, die Informationen
aus dem Inneren des Prozessors nach außen geliefert haben, um
beispielsweise Debugging-Komponenten mit unterstützen zu können.
Und dieses Protection-Enable, das ist ein Bit, das jetzt
beispielsweise den Arbeitsmodus des Prozessors vorgibt.
Je nachdem, in welchem Modus jetzt der Prozessor läuft, in dem
Protected - oder in dem Real-Mode, ist dann sozusagen gegeben, ob ein
Prozessor in dem 80.86-Modus arbeitet, also als 80.86-Prozessor
arbeitet und damit entsprechende Programme ablaufen kann, oder in dem
80.386-Modus, das ist der sogenannte Protected-Modus, der dann
erlaubt, dass man diese komplexe, virtuelle, komplekte, virtuelle
Speicherverwaltung dann auch mit ausnützen kann, also die 32-Bit
-Architektur.
Also Sie sehen, da sind im Steuerregister und im Statusregister eine
ganze Reihe von Informationen vorhanden, die mit berücksichtigt werden
müssen.
Wie schaut es jetzt aber beim Befehlssatz aus?
Bei den MIPS-Befehlen haben wir gesehen, wir haben im Prinzip ein
festes Befehlsformat, das 32-Bit-lang ist, jeder Befehl ist in einem
Format eingepackt, der 32-Bit hat.
Wir haben im Prinzip drei verschiedene Formate, je nach Befehlstyp,
den R-Typ, den I-Typ und den G-Typ, für Verzweigungen, für
arithmetische und logische Befehle oder eben für Immediate-Befehle.
Und dann sind die Informationen, Op-Code und so weiter, die
Registeradressen an entsprechenden Feldern fest vorgegeben.
Wir haben also ein einheitliches Maschinenbefehlsformat.
Bei den 80x86-Prozessoren, also bei der IA-32-Architektur, schaut es
schon wesentlich komplizierter aus.
Was Sie hier sehen, ist eine Folge von Bytes und in Abhängigkeit des
Befehls bzw.
des Operanten, der verwendet wird, werden mehrere Bytes notwendig sein
für einen Befehl.
Hier ist nochmal aufgeführt, wir haben für jedes Byte Gebiet 0 bis 7
und wir sehen, wir können mehrere Bytes haben.
Und das Wesentliche bei dieser Architektur ist, dass in Abhängigkeit
des Befehls wir 2-Byte oder 8-Byte, 4-Byte und so weiter bis eben
weitere Bytes möglich sind für einen Befehl.
Wir haben also hier ein variables Maschinenbefehlsformat.
Im ersten beiden Byte kommt der Opcode, der jetzt ein- oder zwei-Byte
lang sein kann.
Das hängt jetzt davon ab, um welchen Befehlstyp es sich handelt.
Und im nächsten Byte kommt dann die Spezifikation der Operanten.
Wo liegen die Operanten?
Habe ich Register-Registeroperanten oder habe ich einen Register- und
einen Speicheroperanten?
Und das wird angegeben in dem sogenannten MOD-RM-Feld.
Dieses MOD steht für Adressierungsart, REG steht für eine
Registeradresse und RM steht eben Register oder Speicher.
Register or Memory.
Und da wird jetzt spezifiziert, welche Adressierungsart für den
Speicheroperanten verwendet wird beziehungsweise für ob man Register
-Register -Operationen hat.
Und falls man komplexere Adressierungsarten hat, kommt als weiteres
Byte hinzu.
Dieses 2B-Byte, wo dann der Skalierungsfaktor, ein Indexregister und
ein Basisregister für den Speicheroperanten mit angegeben werden kann.
Wenn Sie sich erinnern bei den Adressierungsarten, dann haben wir ja
ungefähr 9 oder 12 Adressierungsarten kennengelernt mit
Registerindirekt, mit Index, Skalierungsfaktor und Displacement.
Und alle diese Möglichkeiten müssen entsprechend kodiert werden.
Und das heißt also jetzt im zweiten Byte wird spezifiziert, wie die
Operanten charakterisiert sind, wie auf die Operanten zugegriffen
werden kann.
Und in Abhängigkeit von diesem Feld können jetzt weitere Byte folgen.
Nämlich wenn beispielsweise eine konstante Abstandsgröße mitverwendet
wird, ein Displacement.
Sie erinnern sich, wir können Basisrelativ adressieren.
Also wir haben einen Teil der Adresse oder der Adressinformation in
einem Basisregister stehen und auf diesen Wert in diesem Basisregister
kann eine konstante Abstandsgröße, ein Displacement addiert werden.
Und diese konstante Abstandsgröße und dadurch bekommen wir die
effektive Adresse.
Und diese konstante Abstandsgröße kann jetzt 8 oder 32 Bit lang sein.
Dann können wir aber auch noch mit Konstanten arbeiten.
Eine Konstante kann 8, 16 oder 32 Bit lang sein.
Entsprechend folgen entsprechend viele Bytes weiter.
Wir sehen also, wir haben ein variables Befehlsformat, das jetzt
abhängt von dem Befehlsart plus von der Adressierungsart.
Und in dieser Information und in diesem Op-Code hier und hier steckt
die Information, ob weitere Bytes notwendig sind.
Ob man von denen noch Informationen braucht, sodass wir die noch holen
müssen.
Also variables Maschinenbefehlsformat heißt, wir können nicht sicher
sein, dass im ersten Befehlsteil schon alle Informationen da sind, um
den Befehl auszuführen, sondern dass wir noch weitere Befehle holen
müssen.
Und Sie sehen auch, dass man mit zwei Bytes, die man höchstens oder
die man mitverwenden kann für einen Operationscode, dass man doch
vorgesehen hat, sehr, sehr viele Befehle mitzuimplementieren.
Sehr komplexe Befehle mitzuimplementieren.
Und jetzt sehen Sie auch, im Vergleich zu diesem MIPS-Befehlsformat,
was ein einheitliches, festes Format ist, hat man hier ein sehr
komplexes Format.
Komplex insofern, man kann sehr viele Informationen hier codieren und
zum einen aus dem Hauptspeicher, wenn man den Befehl holt, in den
Prozessor, ins Steuerwerk mitladen.
Aber man braucht diese Informationen, muss dann möglicherweise noch
nachhaken und weitere Daten oder weitere Teile des Befehls nachziehen.
Und dann folgt eine aufwendige Dekodierung, also um dann aus diesen
Informationen dann auch die Steuerinformationen generieren zu können.
Und bei den RISC-Prozessoren, da ist die Kodierung einfach.
Also ich habe ein festes Format und dann weiß ich immer aus den ganz
speziellen Feldern kommt dann die entsprechende Information.
Hier muss ich erst die ersten beiden sozusagen dekodieren,
entschlüsseln, um zu wissen, was brauche ich noch an Informationen, um
den Befehl dann entsprechend ausführen zu können.
Beim MIPS habe ich es sofort vorhanden.
Also das zum Unterschied.
Und das auch, um Ihnen zu zeigen, wie komplex bei so CISC
-Architekturen allein die Kodierung des Maschinenbefehlssatzes sein
kann.
Eben nicht nur der komplexen Maschinenbefehle wegen, sondern eben auch
wegen der vielen Adressierungsarten, die hier implementiert werden.
Hier ist noch einmal kurz zusammengefasst, wie die Befehle sich
zusammensetzen.
Also wir können bis zu 12 Byte Befehle für einen Befehl haben.
Und je nach Befehlstyp haben wir ein oder zwei Byte für den Op-Code.
Und dann wird hier noch mit dekodiert im Op-Code, wie es jetzt mit der
Länge des Befehls ausschaut.
Ob man eine unmittelbare, also eine Konstante hat, ob man Adress
-Offsets hat oder Ähnliches.
Wir haben die Operandenauswahl oder Spezifikation.
Und falls weitere Informationen notwendig sind, können eben noch
weitere Bytes folgen, wie erklärt.
Oder eben, falls wir ein unmittelbar adressiertes Datum, also eine
Konstante, haben.
Oder im Englischen heißt es dann Immediate.
Und dann können in Abhängigkeit der Länge dieser Konstante weitere
Bytes folgen.
Also, ich habe jetzt schon ein bisschen angedeutet, wir haben auf der
einen Seite diese Befehlssätze oder Architekturen, die sehr komplex
sind, die sehr viel mit unterstützen.
Also, was die Implementierung höherer Programmiersprachen angeht, was
die Unterstützung von Betriebssystemfunktionen angeht.
Und wir haben auf der anderen Seite einfachere Geschichten.
Im Prinzip haben wir diese zwei Architekturen und wiederum ist es die
Sicht, die von außen ein Maschinenprogrammierer sehen kann.
Und in den Anfang der 80er Jahre hat man eben begonnen darüber
nachzudenken, ist es wirklich die beste Technik oder ist, wenn man
diesen CISC-Architekturen folgt, ist es sinnvoll, weiter daran zu
arbeiten oder muss man sich grundsätzliche Gedanken machen, wie eine
Architektur zu spezifizieren ist, um dann vielleicht zu wesentlich
effizienteren Implementierungen von Prozessoren zu kommen.
Grundsätzlich haben wir mit RISC und CISC zwei Techniken, wie
Maschinenbefehle in einem Rechner implementiert werden können.
Und das eine ist, dass ich sage, wenn ich einfache Maschinenbefehle
habe, dann bietet es sich vielleicht an, herzugehen und die
Maschinenbefehle direkt in der Hardware als Schaltnetze, Schaltwerke
oder im Wesentlichen als Schaltnetze zu implementieren.
Wenn ich eine sehr große Anzahl von Maschinenbefehlen zur Verfügung
stelle, also wenn man einen typischen 80386 Prozessor anschaut oder
einen Abfolger, dann haben wir hier 200, 300 Befehle mit
entsprechenden Codierungen für die Adressierungsarten.
Und da wird es für die Hardware, wenn man das nur als Schaltnetz
aufbaut, entsprechend komplex.
Oder man sagt, man implementiert jetzt das Leitwerk, das Steuerwerk
als Mikroprogrammwerk.
Und sagt, jeder Befehl, der auszuführen ist oder ausgeführt werden
kann auf meinem Rechner, wird als Mikroprogramm implementiert.
Ein Mikroprogramm, das kennen wir aus der TE1, ist eine Folge von
Mikrobefehlen.
Und diese Mikroprogramme implementieren jetzt einen Maschinenbefehl.
Und ich hole einen Maschinenbefehl und im Op-Code ist dann
spezifiziert, wo dieses Mikroprogramm im Mikroprogrammspeicher steht.
Und dann wird der Mikroprogrammspeicher angesprungen und das
Mikroprogramm an dieser Stelle wird ausgeführt.
Und dann weiß dieses Mikroprogramm aus, folgen noch weitere
Maschinenbefehle, weitere Teile des Maschinenbefehls und hat dann die
entsprechende Adressinformation.
Das heißt, ich habe ein Steuerwerk aufgebaut aus einem Mikroleitwerk,
bestehend eben aus einem Adressgenerator für das Mikroprogrammwerk,
einem sogenannten Sequencer.
Und einem Mikroprogrammspeicher, im Unterschied zum Hauptspeicher,
einem Speicher, der sehr schnell ist, auf dem Prozessor liegt, eben im
Speicher, im Leitwerk.
Und dieser enthält jetzt alle Befehle, alle Mikroprogramme für alle
Befehle, die der Prozessor kennt.
Der Vorteil ist, dass ich, wenn ich für nachfolgende Generationen
beispielsweise zusätzliche Maschinenbefehle einführen möchte, dass ich
dann einfach diese Mikroprogramme zu schreiben habe und damit auch den
Maschinenbefehlssatz erweitern kann.
Wenn ich dazu noch einen schreibbaren Mikroprogrammspeicher habe, was
ich mir beim Prozessor auch vorstellen kann, dass die Mikroprogramme,
der Mikroprogrammspeicher nicht als Festwertspeicher, sondern als
schreibbarer Schreib-Lesenspeicher implementiert ist, dann kann ich
vielleicht sogar überlegen, ob ich es sogar dynamisch machen kann.
Wenn wir den 80386 anschauen beispielsweise, dann ist das Leitwerk
implementiert als ein mikroprogrammiertes Leitwerk mit einem
Festwertspeicher im Leitwerk, in dem die Mikroprogramme enthalten
sind.
Wir werden das Konzept der Mikroprogrammierung dann noch weiter
genauer anschauen.
Und zwar an einer einfachen Architektur.
Diese einfache Architektur ist die WIMA-Architektur und die steht für
mikroprogrammierte oder mikroprogrammierbare Minimalmaschine, die
jetzt dem von Neumann-Prinzip entspricht.
Das werden Sie dann noch in der Übung ausführlich mitbehandeln und
sehen, wie das funktioniert, bzw.
haben Sie es gesehen.
Aber das ist so ein einfacher Rechner, wo man mal sehen kann, wo man
mal ganz genau mitverfolgen kann, was passiert eigentlich bei einem
Prozessor, wenn er einen Maschinenbefehl ausführt.
Wie werden eigentlich aus dem Maschinenbefehl, den der
Maschinenprogrammierer programmiert in seinem Programm, die Signale
generiert, die da notwendig sind für die Ausführung des
Maschinenbefehls, sodass dann die verschiedenen Teile auf dem
Prozessor informiert werden, was zu tun ist.
Und diese MIMA-Architektur, also diese Minimalmaschine, die so
grundsätzlich nach dem von Neumann-Prinzip arbeitet, besteht
typischerweise eben aus einem Steuerwerk, einem Leitwerk und einem
Rechenwerk, sowie einer Schnittstelle zum System, also einer
Systemschnittstelle.
Der Steuerwerk, hier abgekürzt mit SW, hat einen Takteingang, um hier
eine entsprechende Synchronisierung zu erreichen und erhält zunächst
Informationen aus dem Befehlsregister IR.
Dieses Befehlsregister IR enthält den gerade auszuführenden Befehl und
der Inhalt dieses oder Teil dieses Befehlsregisters wird
weitergeleitet an das Steuerregister.
Und was ist das für ein Teil?
Entschuldigung, an das Steuerwerk, was ist das für ein Teil?
Das ist genau der Opcode-Teil zunächst.
Und aus diesem Opcode-Teil werden jetzt die Steuersignale generiert
und die Steuersignale, die sorgen dann dafür, dass die einzelnen Teile
in unserem Steuerwerk, in unserem Rechner angesprochen werden.
Das Steuerwerk ist aufgebaut als ein mikroprogrammierbares Leitwerk,
das heißt wir haben einen Mikroprogrammspeicher und dieser
Mikroprogrammspeicher kann bis zu 256 Mikrobefehle enthalten.
Und jeder Mikrobefehl, wie wir sehen werden, besteht aus einer Folge
von Mikrooperationen und alle Mikrooperationen, die in einem
Mikrobefehl stehen, die werden in einem Takt angestoßen.
Was passiert also bei dieser MIMA?
Wir holen den Befehl aus dem Hauptspeicher und das passiert in der
Lesephase.
Wir adressieren also über das IAR-Register den Hauptspeicher und holen
den Befehl aus dem Hauptspeicher in das Befehlsregister.
Und dann wird der Befehl, der geholt worden ist und im
Maschinenbefehlsregister steht, dekodiert und Dekodierung heißt in
diesem Fall bei einer mikroprogrammierten Maschine, wir springen den
Mikroprogrammspeicher an und zwar nämlich genau das Mikrogramm, das
diesen Maschinenbefehl implementiert, der geholt worden ist.
Es ist die Dekodierphase und in der Dekodierphase werden dann eben
entsprechend des Mikroprogramms die Steuersignale generiert und in der
Ausführungsphase werden dann die entsprechenden Operationen
ausgeführt.
Und wenn wir auf den Speicher zugreifen müssen, dauert das natürlich
eine gewisse Zeit, weil man muss ja zunächst den Speicher adressieren
und wenn der Speicher adressiert ist, muss die entsprechende
Speicherzelle gelesen werden und das Datum muss dann in den Prozessor
geholt bzw.
dann in die Speicherzelle muss das Datum geschrieben werden beim
Lesezugang.
Und das passiert über die entsprechenden Register.
SAR ist ein Speicheradressregister und SDR ist ein
Speicherdatenregister.
Wenn ich also Daten adressiere, gebe ich die Adresse in das SAR
-Register und bei dem Lesen wird das Datum in das Datenregister, SDR
-Register geholt bzw.
beim Schreiben steht im Datenregister ein Wert, der dann in den
Speicher unter die Adresse zu speichern ist.
Wir haben ein Mikroprogrammwerk und haben gesagt, ein Mikroprogramm
ist eine Folge von Mikrobefehlen.
Und Sie sehen jetzt hier das Mikrobefehlsformat, wohlgemerkt, das ist
zu unterscheiden vom Maschinenbefehlsformat, das Mikrobefehlsformat,
das jetzt aus einer Folge von Bits steht.
Wir haben insgesamt 28 Bit, das ist eine einfache Maschine.
Es gab gerade in der Zeit, in der die Mikroprogrammierung als die
Technik gegolten hat, wie Prozessoren zu implementieren sind oder
Steuerwerke zu implementieren sind, Mikrobefehle, die zum Teil bis zu
500 bzw.
1000 Bit auch breit waren oder 256 Bit breit waren.
Jedes Bit in diesem Mikrobefehl entspricht jetzt einem Steuersignal
und man spricht, wenn dies so der Fall ist, von einem sogenannten
horizontalen Maschinenbefehlsformat.
Man hat auch andere Formate sich überlebt, nämlich wenn man
Informationen zusammengefasst hat, beispielsweise eine ALU.
Eine ALU kann beispielsweise acht Operationen ausführen,
beispielsweise drei arithmetische und fünf logische Operationen.
Dann brauche ich acht Bit, um hier zu programmieren, dass ich eine
Addition ausführen möchte oder ich brauche ein Bit eben oder ich
brauche die acht Bit für jede Operation.
Da es natürlich so ist, dass ich eine Addition und eine Subtraktion
nicht gleichzeitig ausführen kann, kann man auch hergehen und
beispielsweise die Steuersignale für die ALU, also die ALU
-Operationen, codieren.
Und dann kommt man zur vertikalen beziehungsweise quasi-horizontalen
Programmierung.
Das sind Feinheiten, aber wenn ich Steuersignale zusammenfasse, weil
sie logischerweise in einem Taktzyklus nicht gleichzeitig ausführbar
sind, wie in allen arithmetischen Operationen, kann ich sie codieren.
Für die acht Operationen in der ALU reichen mir drei Bit in der
Mikroinstruktion aus, brauche allerdings dann eben eine entsprechende
Decodiereinheit im ALU-Werk.
Also, was wir hier haben, ist ein typisches horizontales Format, weil
hier die Steuerbits entsprechend aufgelistet sind und keine Codierung
erfolgt ist.
Wenn man jetzt die Mikroprogrammierung machen möchte oder wenn wir
jetzt noch einmal schauen, Entschuldigung, Dieses Mikroprogrammwerk
enthält jetzt Informationen für die ALU, für den Speicher und für
verschiedene Teile unseres Rechners, für die einzelnen Register.
Beispielsweise ist ein Bit da, damit ein Register geladen werden kann
oder ein Bit, damit ein Register gelesen werden kann oder Ähnliches.
Sie sehen also beispielsweise X und Y.
X und Y, gehen wir zurück, sind zwei Register, die der ALU vorgelagert
sind, also vor den ALU-Eingängen liegen.
Und mit X und Y können diese Register geladen werden, sodass man dann
im nächsten Takt auf die Daten zugreifen kann.
Das ist der Atem, also der Operanten für die ALU.
Und Sie sehen hier weitere Ports oder Steuersignale, S und DR, DW, die
also dann dafür sorgen, dass beispielsweise das Datenregister am
Systembus gelesen oder beschrieben werden kann.
Wir haben hier ein Register mit 1, was als ein Register zählt, das
jetzt entsprechend die 1 festverdreht enthält.
Und wenn ich hier dieses E gebe, dann sorgt es dafür, dass die 1
weitergegeben wird.
Ich habe ein ausgezeichnetes Register, den Akkumulator, und den kann
ich wieder laden oder entsprechend lesen.
Und genauso dieses IAR-Register, das Befehlsadressregister, auch das
kann ich laden oder schreiben oder lesen.
So, und genau diese Signale finden wir jetzt in unserem
Mikrobefehlsformat wieder.
Lesen- und Schreibesignal für den Hauptspeicher.
Ich muss ja dem Hauptspeicher mitteilen, ob bei einem Speicherzugriff,
bei einer Speicherreferenz, ein Wort gelesen oder geschrieben werden
kann.
Ob ich Überträge C0 bis C3 zu verarbeiten habe in der ALU oder
entsprechende Statussignale mit berücksichtigen soll.
Oder ich habe diese angesprochenen Signale für das Laden und Lesen des
Datenregisters oder des Befehlszählers.
Und hier dieses PR und PW ist dann für das Adressregister, also für
den Befehlszähler.
Und wenn man jetzt sehen möchte, wie das funktioniert bei dieser MIMA,
haben wir hier jetzt ein kleines MIEK-Programm.
Das ist jetzt ein MIEK-Programm und hier ist kodiert oder hier ist in
einer lesbaren Form angegeben, was schrittweise erledigt werden muss,
um einen Befehl aus dem Hauptspeicher zu holen.
Wir brauchen hierzu insgesamt bei dieser MIMA-Architektur genau fünf
Takte.
Im ersten Takt sehen wir, wird der Befehlszähler in das
Speicheradressregister geladen.
Das heißt, der Inhalt des Befehlszählers wird in das
Speicheradressregister geladen, damit dann mit dieser Adresse auf den
Speicher zugegriffen werden kann.
Gleichzeitig wird der Inhalt des Befehlszählers in das X-Register vor
dem ALU-Eingang geladen und gleichzeitig wird in diesem Takt das
Lesesignal angegeben, damit dem System, also dem Hauptspeicher
mitgeteilt wird, dass jetzt ein Datum aus dem Hauptspeicher, in dem
Fall ein Befehl aus dem Hauptspeicher, zu lesen ist.
Im zweiten Takt wird dann die 1 nach Y geladen.
Und jetzt kann man agieren und was wir also hier machen, ist nichts
anderes als den Befehlszähler zu inkrementieren.
Der muss ja erhöht werden, damit er im nächsten Befehlszyklus auf den
Befehl zeigt, der zu holen ist.
Und das Signal R, das Lesesignal, wird weiter angegeben, weil wir
greifen auf den Hauptspeicher zu, die Informationen oder die Hardware
im Hauptspeicher braucht diese Information, da sie länger anlegend
ist.
So, im dritten Takt agieren wir dann und das Ergebnis wird im vierten
Takt in den Befehlszähler, also IAR, zurückgeschrieben und damit zeigt
der Befehlszähler auf den nächsten auszuführenden Befehl.
Jetzt ist das Datum, wir haben hier das SAR geladen und hier ist dann
das Datum aus dem Hauptspeicher geholt und wir laden aus dem
Speicherdatenregister den Befehl in das Befehlsregister.
Also, ziemlich lang, bei unserer Minimalmaschine brauchen wir also
hier, um einen Befehl zu holen, fünf Takte, aber wir holen nicht nur
den Befehl, sondern wir inkrementieren auch den Befehlszähler, sodass
er im nächsten Takt auf den Befehl zeigt, der dem gerade geholten
Befehl im Hauptspeicher folgt.
So, und das ist jetzt allerdings eine symbolische Darstellung des
Mikroprogramms, eine Darstellung, die man lesen kann und übersetzt in
ein Mikroprogramm, was dann der Prozessor ausführt, schaut so aus.
Und da tut man sich schon wesentlich schwerer zu lesen.
Was man aber hier sehen kann ist, das ist jetzt ein Mikroprogramm,
eine Zeile ist ein Mikroprogramm, das heißt, die erste Zeile
entspricht dem ersten Takt.
Und was passiert hier?
Wir haben hier eine Reihe von Einsen stehen und diese Einsen, die
sorgen sozusagen dafür, dass dieses Steuersignal aktiviert wird.
Und alle Nullen sorgen dafür, dass eben hier keine Operation
ausgeführt wird oder dass diese Funktion, die aus diesem Bit kodiert
wird, gesteuert wird, nicht ausgeführt wird.
Das heißt, das erste Bit hier, die erste Eins in dieser Stelle ist,
wenn wir nochmal zurückgehen, dieses X-Bit.
Das heißt, wir steuern hier, dass X geladen wird.
Was ist dieses Bit?
Das ist hier, hier, PW.
Erinnern wir uns nochmal, was PW ist?
PW ist, wir lesen dieses Adressregister und sorgen dafür, dass hier
der Inhalt von IAR nach X geladen wird.
Und wir laden das SAR-Register und das X-Register.
Und hier ist das Lesen angegeben.
Beziehungsweise die Folgeadresse, die wir entsprechend benötigen.
So, also, so werden diese Mikroprogramme übersetzt in diese
entsprechenden 01-Muster und damit haben wir das Maschinenbefehl.
Das heißt, wenn man auf dieser Ebene programmiert, wird es natürlich
sehr schwierig, weil man jedes einzelne Steuersignal explizit kodieren
oder programmieren muss, ob es gesetzt wird oder nicht.
Was dann im Einzelnen passiert, noch einmal zusammenfassend ist, wir
haben ein Assemblerprogramm.
Diese Art des Assemblerprogramms kennen wir.
Wir haben eine Folge von Maschinenbefehlen, die stehen im
Hauptspeicher.
Und was jetzt dieses Programm im Einzelnen jetzt macht, ist, dieses
IFetch -Programm, dass es einen Maschinenbefehl holt in Steuerberg.
Aus dem Op-Code wird jetzt ein Mikroprogramm angesprungen und dieses
Mikroprogramm steht im Mikroprogrammspeicher.
Das heißt, es wird ein solches Mikroprogramm angesprungen und dieses
Mikroprogramm wird dann Takt für Takt ausgeführt.
Und anschließend ist der letzte Befehl in so einem Mikroprogramm der,
dass sozusagen der Mikrobefehlszähler angesprochen wird, wiederum den
nächsten Befehl zu holen.
Das heißt, wir haben eine Schleife, die dann dafür sorgt, dass der
Subbefehl als nächstes geholt wird.
Also sozusagen eine Programmabarbeitung im Prozessor selber.
Hier ist es noch einmal genauer aufgeführt.
Ich habe die Befehlszähler im Mikroprogrammspeicher.
Jede Speicherzelle im Mikroprogrammspeicher enthält eine
Mikroinstruktion.
Die wird weitergegeben und aus diesem Decoder werden dann die
einzelnen Steuersignale an die einzelnen Werke geleitet.
In unserem Mikroprogramm-Leitwerk haben wir jetzt auch neben dem
eigentlichen Maschinenbefehlszähler einen weiteren Zähler, nämlich den
Mikrobefehlszähler.
Und dieser Mikrobefehlszähler ist jetzt der Befehlszähler für den
Mikroprogrammspeicher.
Und der muss natürlich geladen werden.
Entweder mit der Adresse des dem aktuellen Mikrobefehl-Folgenbefehls
oder wenn eine Verzweigung ausgeführt wird mit einer Sprungadresse.
Und deshalb hat man hier auch sowas wie einen Sprungspeicher.
Und dort können dann Sprünge auch spezifiziert werden.
Wir haben also auch hier Mikrobefehl holen, dekodieren und dann
ausführen, heißt dann die einzelnen Steuersignale auszuführen.
Ein Vorteil der Mikroprogrammierung, das hat man sehr leicht erkannt,
das ist der, man kann neue Mikroprogramme einführen.
Und damit auch erlauben, dass wenn ich einen Rechner habe, der einen
Schreiblesespeicher hat, dass man mehrere Maschinenbefehlsätze
implementiert.
Oder wenn ich einen Prozessor habe, dass in der nachfolgenden
Generation eben weitere Befehle hinzukommen durch hinzufügen weiterer
Mikrobefehle, Mikroprogramme.
Ein Vorteil, der in den 80er Jahren sehr stark genutzt war, ist der
gewesen, dass man, und da hat AMD diese BitSize-Mikroprozessoren
gehabt, diese AM2900er-Familie, die sie in der T1 kennengelernt haben.
Da hat man Rechner aufbauen können und sie einer bestimmten Anwendung
gezielt hin entwerfen können.
Das waren relativ flexible Bausteine und man hat dann spezielle
Maschinenbefehlssätze implementiert durch entsprechende
Mikroprogramme.
Der Vorteil der Mikroprogrammierung, oder warum man die
Mikroprogrammierung gemacht hat, die Mikroprogrammierung kann man
sagen, geht zurück auf Maurice Wilkes, der Anfang der 60er Jahre in
die IBM-Großrechner, IBM-700 irgendwas, eine strukturierte Weise der
Mikroprogramme eingeführt hat.
Oder der Steuerung, der Implementierung des Befehlssatzes eingeführt
hat.
Und die Idee war sozusagen, diesen schnellen Speicher auf dem
Leitwerk, dass man da sehr viele Informationen kodiert, sodass man
nicht so oft auf den Speicher zugreifen muss, weil der Zugriff vom
Prozessor auf den Speicher aufwendig war.
Das hat sich allerdings komplett geändert.
So Mikroprogramme zu erstellen kann sehr sehr aufwendig sein und weil
ich mehrere Mikroinstruktionen brauche, je nachdem wie ich mein
Mikroinstruktionsformat kodieren kann, je nachdem wie ich meine
Maschine aufbauen kann, kann auch sein, dass ich relativ lange
benötige, um ein solches Mikroprogramm auszuführen.
So, warum hat man diese Geschichten gemacht?
Man wollte also wirklich mit sehr komplexen Befehlen, die man zur
Verfügung stellt, eben beispielsweise durch Mikroprogramme, dafür
sorgen, die Mikroprogramme liegen im schnellen Speicher, im
Mikroprogrammspeicher, der ist auf dem Prozessor.
Wenn so ein Maschinenbefehl sehr viel kann, das heißt das
Mikroprogramm im Mikroprogrammspeicher liegt und das sehr sehr viel
auch erledigen kann, dann wird die Ausführung auch schneller, weil ich
nicht so oft auf den Speicher zugreifen muss, um Befehle, einfachere
Befehle zu holen.
Das war die ursprüngliche Idee für diese Tendenz bei den CIS
-Prozessoren, sehr sehr komplexe Befehle zu implementieren.
Mit der Mikroprogrammierung habe ich eine strukturierte Möglichkeit,
Befehle zu implementieren, weil ich ja ähnlich wie bei der
Programmierung arbeiten kann.
Und das führt dazu, wenn ich komplexe Befehle habe, die sehr sehr viel
können, dass ich nicht so lange Maschinenbefehle bekomme, weil ich ja
in einen Befehl sehr sehr viel hineingekodiert habe, weil ein Befehl
sehr sehr mächtig ist.
Und der dritte Punkt ist, ich kann mit sehr komplexen Befehlen sehr
nahe an die höhere Programmiersprache rankommen.
Dass ich also sozusagen Befehle implementiere, die sehr nah an der
Semantik von Hochsprachenkonstrukten liegen.
Und damit wird vielleicht die Übersetzung relativ einfach.
Und die Idee, gerade warum sich in den 70er, 80er Jahren diese CISC
-Architekturen so entwickelt haben, war wie gesagt, man möchte
möglichst nahe an höhere Programmiersprachen kommen.
Und das erreicht man dadurch, dass ich eben Befehle anbiete, die
höhere Programmiersprachen möglichst unterstützen.
Und damit werden diese Maschinenbefehle sehr komplex.
Komplex, nicht nur das, was sie können, sondern auch was die
Adressierungsarten beispielsweise angeht, die sie eben mit
unterstützen.
Der Unterschied, oder warum sich die Anfang der 80er-Jahre oder sogar
schon in den 70er-Jahren da Gedanken gemacht worden ist, weshalb das
für die Zukunft nicht unbedingt das Sinnvolle ist, haben mehrere
Gründe.
Eine sind natürlich auch in der Technologie begründet.
Wenn man gesagt hat, ich hole sehr komplexe Maschinenbefehle aus dem
Hauptspeicher in den Prozessor und dann lasse ich im Prozessor sehr
schnell die Programme abarbeiten und ich brauche nicht so oft auf den
Hauptspeicher zugreifen, ändert sich natürlich dadurch, wenn die
Technologie sich ändert und ich sehr schnelle Programme bekomme, wenn
ich zusätzlich noch auf meinen Prozessor oder nahe an den Prozessor
Pufferspeicher bekomme, die sehr schnell sind und die dann die
Informationen enthalten, die ich brauche, die Programmteile und die
Daten, also sozusagen Caches mitverwende.
Dann kann ich auch öfters auf den Speicher zugreifen, dann muss ich
vielleicht nicht unbedingt die komplexen Befehle implementieren.
Dann, Mikroprogramme zu entwerfen ist nicht ganz einfach, gerade wenn
es sehr komplexe Mikroprogramme sind, dann hat man ja gesehen, ich
muss diese 01-Geschichten machen und so weiter auf der Mikrobefehls
-Ebene.
Das kann relativ aufwendig werden und solche Steuerwerke, die brauchen
ziemlich viel Platz dann auch auf dem Rechner oder auf einem Chip.
Aber, was noch mehr gegen diese CISC-Architekturen gesprochen hat, war
das, man hat also festgestellt, wenn man wirklich mal Programme
anschaut und analysiert, welche Befehle ausgeführt werden, bei der
Abarbeitung von typischen Programmen, nicht nur von einem, sondern von
einer ganzen Reihe von Programmen, von typischen Programmen,
beispielsweise Übersetzerläufen oder von Betriebssystemfunktionen oder
typischen Anwendungsprogrammen, dann hat man festgestellt, dass
eigentlich diese sehr komplexen Befehle, die man zur Verfügung
gestellt hat, von den Compilern gar nicht verwendet werden.
Dass vielleicht der ein oder andere Maschinenprogrammierer bei der
Implementierung von Betriebssystemfunktionen vielleicht den ein oder
anderen Befehl verwendet.
Das hat man schon gesehen, aber wenn man so typische Compiler
angesehen hat, dann haben die relativ wenig Teile oder nur wenig
Befehle des großen Befehlssatzes benutzt.
Und wie gesagt, der Compilerbau ist auch nicht gerade einfach, wenn
man so komplexe und eine große Anzahl von Maschinenbefehlen hat.
Und das hat man gemacht, diese Analysen auf verschiedenen
Architekturen, beispielsweise auf einer IBM, wo man XBL,
Betriebssystemfunktionen, Betriebssystemprogramme untersucht hat, also
Systemprogramme.
Und da hat man festgestellt, eigentlich sind 90% aller Befehle nur 10
verschiedene Befehle.
Wenn wir ein bisschen weiter gehen, 95%, da haben wir schon 21
verschiedene Befehle und 99% aller ausgeführten Befehle sind 30
verschiedene Befehle.
Und das, obwohl auf der IBM ungefähr der Befehlssatz ungefähr 10 mal
so viel beträgt.
Das heißt, die IBM hat ungefähr 300 Maschinenbefehle gehabt und nur 30
von diesen 300 Befehlen sind zu 99% benutzt worden.
Nur 1% sind dann auch die anderen.
Und genauso, wenn wir COBOL-Programme haben.
COBOL ist eine höhere Programmiersprache, die im wesentlichen im
kaufmännischen Bereich eingesetzt worden ist.
Die hat insbesondere Datenbank, die Implementierung von Datenbanken
und kaufmännischen Programmen mit unterstützt.
Und das war so ein großer, großer Anwendungsbereich, gerade auf den
Großrechenmaschinen von IBM.
Und da, wenn man diese Anwendungsprogramme untersucht hat, dann sind
ungefähr 90% der Befehle gewesen, wenn man die zusammengezählt hat, 26
verschiedene Befehle.
Und 48 Befehle haben immerhin 99% ausgemacht.
Insgesamt sind von den über 300 Befehlen der IBM 370 nur 84
verschiedene Befehle genutzt worden.
Also Sie sehen, wenn man solche Sachen sagt, dann frägt man sich
natürlich, warum stelle ich als Prozessor oder Rechnerhersteller
überhaupt so viele und so komplexe Befehle zur Verfügung.
Und man hat da so eine Art Regel gemacht, ja im Prinzip, wenn man das
mal ein bisschen vereinfacht, dann kann man mal sagen, diese 80, 20
Befehle, in den meisten Fällen werden eigentlich nur 20% der Befehle
benutzt.
Und wenn ich sehr komplexe Befehle habe, dann dauert es relativ lange,
bis ich einen solchen Befehl ausgeführt habe.
Und ein Maß dafür ist der sogenannte CPI-Wert, Cycles Per
Instructions.
Und der besagt, ein Befehl braucht zur Ausführung bestimmte Anzahl von
Taktzyklen.
Und dahinter steht die Idee, je weniger Taktzyklen ein Befehl zur
Ausführung im Mittel braucht, also nicht genau ein Befehl, sondern man
misst, wie lange braucht ein Programm zur Ausführung und setzt das in
Relation, in Beziehung zur Anzahl der ausgeführten Befehle und Mittel
dann entsprechend, dann kommt man zu einem Wert Cycles Per
Instructions.
Man muss das ein bisschen ausführlicher machen, aber es soll eine
Andeutung geben.
Und wenn sozusagen die Ausführung eines Befehls mehr oder sehr viel
mehr als zwei Takte benötigt, dann ist es relativ lang, Takte des
Prozessors.
Und da haben RISC-Prozessoren angesetzt, oder die Ideen, wie man RISC
-Architekturen einsetzt, nämlich, wenn ich hergehe und sage, ich
versuche diesen CPI-Wert möglichst zu drücken, dass ich im Prinzip
dahin komme, dass ich pro Takt, oder dass ich diesen CPI-Wert auf 1
bringe, oder nahe 1 bekomme, dass ich mit jedem Takt einen
Maschinenbefehl fertig bekomme.
Dann habe ich natürlich auch eine sehr gute Beschleunigung möglich.
Wie kann man sowas schaffen?
Ist die zentrale Frage.
Und die kann man dadurch lösen, dass man sich einmal überlegt, ja, wie
wir gesehen haben, bei diesen typischen Systemprogrammen,
Anwendungsprogrammen auf den Großrechnern, auf den Rechnern, die
damals so typisch waren, da hat man nur ganz, ganz wenige Befehle
eigentlich gebraucht.
Also überlege ich mir doch gleich, kann ich grundsätzlich weg von
diesen komplexen Befehlsätzen gehen, hin zu einer Architektur mit
einfachen Befehlen, mit einfach zu implementierenden Befehlen und so
weiter.
Weil, wenn ich sowieso nur einen ganz geringen Teil der ganz komplexen
Befehle überhaupt verwende, dann kann ich den vielleicht dann auch in
Software ausführen, das kostet dann auch nicht mehr viel Zeit.
Wenn ich aber einfache Befehle implementiere, ganz einfache Befehle
und die dann auf einer Prozessorarchitektur habe, auf einem Prozessor
habe, der sehr, sehr schnell arbeitet und mit jedem Takt einen Befehl
fertig bekomme, ja, dann kriege ich vielleicht eine wesentlich
schnellere Prozessoren und die wenigen Fälle, wenigen Ausnahmefälle,
die vielleicht dann noch benötigt werden, die kann man dann per
Software nachbilden.
Das war so dieser Grundgedanke bei den RISK-Protagonisten.
Und dann hat man also überlegt, was werden eigentlich für Befehle in
Programmen verwendet.
Einfache Zuweisungen im Wesentlichen, arithmetische und logische
Befehle.
Dann Abfragen, IF, Verzweigungen, Unterprogrammaufrufe, mit Abstand
Schleifenbefehle und dann noch Go-To-Befehle und schließlich nur 7%
bilden dann den Rest.
Und diese Untersuchungen hat man relativ früh Anfang der 80er Jahre
bei einer ganzen Reihe von Programmen gemacht, bei einer ganzen Reihe
von Maschinen und hat dann eben festgestellt, ja, das sind eigentlich
die Befehle, die wir hauptsächlich brauchen.
Wenn wir diese Befehle brauchen, dann sind das an und für sich relativ
einfache Befehle.
Zuweisungen sind relativ einfach, Addition, Subtraktionsbefehle.
Verzweigungen brauche ich, Unterprogrammaufrufe, dann muss ich die
möglichst unterstützen.
Das heißt, wir gehen weg von der Idee, dass ich sage, ich habe sehr
komplexe Befehle und brauche dadurch nicht mehr oft auf den Speicher
zu greifen, sondern ich mache einfache Befehle.
Einfach in dem Sinn, Befehle, die relativ wenig tun und zwar nur so
viel tun, dass ich sie in einem Takt ausführen kann.
Relativ einfache Befehle, die kann ich dann in Hardware als
Schaltnetze implementieren und brauche keine aufwendigen
Mikroprogramme mehr.
Damit wird dies auch wieder schneller, weil ich direkt die
Steuersignale generieren kann und muss nicht erst auf den
Mikroprogrammspeicher zugreifen, Mikrobefehl lesen und dann die
Steuersignale generieren, sondern ich kann direkt aus dem Opcode, wenn
ich keine Mikroprogrammierung habe, die Steuersignale generieren.
So, wenn ich allerdings relativ einfache Befehle habe, dann können die
ja nicht so viel, also brauche ich mehrere Befehle, die in einem
Programm stehen, im Vergleich zu einem Programm, was dasselbe leistet
auf einem CIS-Prozessor.
Aber wenn ich hergehe und stecke unheimlich viel in die Compiler
-Technologien, Code-Optimierungstechnologien, dann kann ich
möglicherweise meinen Code so optimieren, dass es hier auch wiederum
zu schneller Verarbeitung kommt.
Und dieses einheitliche Befehlsformat, das sorgt eben dafür, dass ich
sehr schnell dekodieren kann.
Ich kann alle Befehle gleichzeitig mit dekodieren und muss nicht erst
die Operandenspezifikation abwarten, wie ich es bereits erklärt habe.
Und das Ziel ist auch noch für sich, man will zu einer fließbaren
Verarbeitung kommen.
Pipelining, das ist der wesentliche Gesichtspunkt.
Wenn ich nämlich hergehen kann und sage, ich habe einfache
Maschinenbefehle und ich kann jetzt die einzelnen Phasen des
Maschinenbefehlszyklus so gestalten, dass im Prinzip für jede Phase
ungefähr gleich viel Zeit verbraucht wird, für das Befehl holen, für
das Dekodieren, für das Ausführen und für ein Ergebnis
zurückschreiben, dann kann ich dafür sorgen, dass ich die einzelnen
Phasen sozusagen überlappt abarbeite, wie es bei einer fließbaren
Verarbeitung funktioniert.
Und das ist das Wesentliche, was zu den Riskargumenten oder zu den
Vorteilen der Riskarchitektur geführt hat, dass ich sagen kann, ich
kann Prozessoren bauen, bei denen ich den Maschinenbefehlszyklus sehr
effizient implementieren kann, weil ich das Pipelining ausnützen kann.
Und man hat dann Entwurfsziele angesetzt bei den Riskarchitekturen,
nämlich dass man gesagt hat, ich möchte einen Befehl in einem
Taktzyklus ausführen.
Das heißt Folgendes, das muss man ausführlicher noch festhalten, für
die Ausführung eines Befehls oder besser gesagt für die Abarbeitung
eines Befehls brauche ich mehrere Phasen.
Ich muss den Befehl holen, muss den Befehl dekodieren, muss die
Operanten holen, ich muss den Befehl ausführen und ich muss das
Ergebnis wieder zurückschreiben.
Also vier beziehungsweise fünf logische Phasen, die ein Befehl
durchlaufen muss.
Das kann er nicht in einem Takt machen, sondern dafür braucht er
mehrere Taktzyklen.
Wenn ich aber wie bei der Riskphilosophie gedacht hergehen kann und
kann diese Phasen für die verschiedenen Befehle überlappen, das heißt,
dass ich sage, ich hole den ersten Befehl, im nächsten Takt wird er
dekodiert, aber während der dekodiert wird, kann ich den nächsten
Befehl schon wiederholen.
Während der Befehl ausgeführt wird, kann der zweite Befehl dekodiert
werden und der dritte Befehl kann schon wieder geholt werden.
Ich stecke also die Befehle in eine Befehlspipeline.
Und damit komme ich zu einer überlappten Aufarbeitung des
Maschinenbefehlszykluses.
Und das Ziel ist bei den Riskarchitekturen, dass wenn ich sozusagen
die Pipeline einmal im eingeschwungenen Zustand habe, dass ich dann
mit jedem Takt einen Befehl fertig bekomme, am Ende der Pipeline
rausbekomme.
Und das versteht man unter den Einzyklusmaschinenbefehlen.
Dazu gehört natürlich, dass ich für die Ausführungsphase selber im
Wesentlichen alle Befehle in einem Takt ausführen kann.
Und das bedingt wiederum, dass ich eine ganze Reihe von
Einschränkungen machen muss.
Es kann nur erreicht werden, wenn ich sage, alle meine arithmetischen
und logischen Befehle setzen voraus, dass ich Registeroperanten habe,
dass die Operanten in den Registern stehen.
Weil dann kann ich gleich diese Register adressieren und die Operanten
bereitstellen, während ich den Opcode dekodiere.
Und ich muss ja irgendwann die Daten aus dem Speicher holen.
Dafür nehme ich dann aber nur zwei Befehle, nämlich Lade- und
Speicherbefehle.
Und da brauche ich dann mehr Takte zur Ausführung, weil ich muss den
Speicher adressieren und das Datum holen.
Das kostet mehr Zeit und das muss man dann entsprechend mit
berücksichtigen.
Aber ich möchte alle Befehle zum großen Teil in einem Takt ausführen
können.
Und dann kann ich keine Adressierungsarten gebrauchen, keine
Speicheroperanten gebrauchen, wo ich über aufwändige Adressrechnung
erst dann die effektive Adresse berechne und dann auf den Speicher
zugreife.
Sondern da greife ich eben auf die Register-Register-Architektur
zurück, wie wir es gesehen haben.
Und ich nehme nur ausgezeichnete Befehle, die Load-Store-Befehle, die
mir auf den Speicher zugreifen.
So, das einfache Pipelining erreiche ich auch, wenn ich eben dieses
einheitliche Maschinenbefehls habe, dieses Fixed-Length-Coding, dass
ich nämlich hergehen kann und kann sagen, ich habe die Decodier-, den
Opcode, den habe ich am bestimmten Teil im Befehl und meine Register
-Adressen.
Und während ich den Opcode decodiere, kann ich gleich mein Register
-File adressieren und gleichzeitig adressieren.
Das heißt, Dekodieren und Operantenbereitstellung fällt zusammen.
Man verzichtet grundsätzlich drauf jetzt beispielsweise mal, dass man
sagt, ich unterstütze jetzt die Gleitkomma-Arithmetik bei der
Philosophie, wogegenmerkt, und unterstütze auch keine
Betriebssystemfunktionen.
Das hat man dann, also als man noch in der akademischen Welt war,
gesagt, das braucht man nicht, das bildet man alles per Software nach.
Allerdings hat man dann sehr schnell gemerkt, wenn man dann raus auf
den Markt geht und Prozessoren verkaufen möchte, dann ist es
vielleicht doch ganz gut, wenn man sowas anbietet.
Und das sieht man dann auch, wenn man die MIPS-Prozessoren anschaut,
beispielsweise wie sie an der Universität in Stanford implementiert
worden sind, und dann anschaut, was dann der MIPS-R2000 als erste
kommerzielle Ausprägung gehabt hat.
Wenn man da die Maschinenbefehle vergleicht, die unterstützt worden
sind, dann war das dann auch schon mit dabei.
Also wenn es dann ums Geldverdienen geht, muss man dann auch
Kompromisse schließen und von der reinen Philosophie abgehen.
Also, Zielvorstellung für Riskrechner, habe ich gesagt, ein Zyklus
Maschinenbefehle, einheitliches Maschinenbefehlsformat ist notwendig,
wie ich es aufgezeigt habe.
Wenige Maschinenbefehle.
Ich implementiere nur die Maschinenbefehle, die einfach sind, die
sozusagen in einem Taktzyklus in der Ausführungsphase auszuführen
sind, mit Ausnahme eben beispielsweise der Load-Store-Befehle.
Die brauche ich aber, die sind grundsätzlich hier auszunehmen.
Ich gehe von der Load-Store-Architektur aus, also Register, Register,
mit den Operanten in den Register.
Nur die ausgezeichneten Befehle Load-Store greifen auf den Speicher
zu.
Ich stelle einen großen Registersatz zur Verfügung.
Großer Registersatz, ich möchte möglichst viele Daten, die ich
verarbeite, in meinen Registern auf dem Prozessor halten.
Und das sollen möglichst Allzweckregister sein, ohne
Spezialfunktionen.
Damit kann ich den Compiler auch unterstützen.
Ich brauche die Mikroprogrammierung nicht mehr, weil ich einfache
Befehle habe, die ich mit einfachen Schaltnetzen realisieren kann.
Und man hat von vornherein gesagt, wenn ich so meine Hardware
vereinfachen kann, weil ich auf einen großen Teil meines Leitwerks
verzichte, dann kann ich gleich auf eine 32-Bit-Architektur eingehen
und stelle alles als 32-Bit dar.
Und wie gesagt, Pipeline-gerechter Maschinenbefehls-Satz heißt, dass
die einzelnen Phasen des Maschinenbefehls-Zykluses gleich lange dauern
und ich nicht für die Befehlsholphase beispielsweise 20 Nanosekunden
brauche fürs Dekodieren, 15 Nanosekunden für die Ausführung, dann
vielleicht 30 Nanosekunden und so weiter.
Also dass ich da unterschiedlich lange Zeiten habe.
Pipeline-gerecht heißt, ich muss die aus einzelnen Phasen möglichst
gleich lange ausrichten.
So, man braucht, wenn man das RISK-System hier anschaut, hauptsächlich
ein Zyklus-Maschinenbefehl.
Und das bildet einen großen Teil.
Ausnahmen hiervon sind die Load-Store-Befehle und vor allen Dingen die
Verzweigungsbefehle.
Bei der ursprünglichen Idee hat man gesagt, man braucht nicht mehr als
128 Befehle.
Wenn man dann schaut von den akademischen Diskussionen Anfang der 80er
Jahre, was dann bei den Ausprägungen der RISK-Prozessoren gekommen
ist, war es ihm dann doch wesentlich mehr als diese 128.
Man hat auch mehr an OPCO zur Verfügung gestellt, also nicht nur 7
-bit, um an OPCO zu spezifizieren, sondern beispielsweise 8-bit, was
einem dann schon 256 Befehle zu decodieren erlaubt.
Ganz wenig Maschinenbefehlsformate oder ein Maschinenbefehlsformat,
beziehungsweise, wie wir es bei der MIPS gesehen haben, drei
Maschinenbefehlsformate, wenig Adressierungsarten, im Wesentlichen
direkte Adressierung, dann die Konstante und Register indirekte
Adressierung und Register indirekte Adressierung mit Displacement.
Laut Steuerarchitektur haben wir gesehen und wir nehmen eine
festvertratete Steuereinheit ohne Mikroprogrammwerk, das heißt die
einzelnen Maschinenbefehle werden als Schaltnetze realisiert.
Und man verändert relativ viele Allspec-Register bei den typischen
CISC -Mikroprozessoren wie dem MIPS oder der SPARC, finden wir 32, wir
haben aber auch RISC-Mikroprozessoren gefunden mit 256 Registern.
Und wie gesagt, man kann hier, wenn man heutige Mikroprozessoren
anschaut, die dieser RISC-Philosophie gefolgt sind, sehen, das
Pipelining ist das zentrale Komponente, die zentrale
Implementierungstechnik für die RISC-Prozessoren, die Laut
Steuerarchitektur ebenfalls, weil die sorgt dafür, dass ich wirklich
zu einer effizienten Pipeline-Realisierung kommen kann.
Man hat einen großen Registersatz, aber man bietet natürlich, wenn man
kommerziell erfolgreich sein will, neben den Allspec-Registern auch
Gleitkommaregister an, also unterstützt auch hier die
Gleitkommaverarbeitung in der Hardware.
Das ist das einheitliche Maschinenbefehlsformat und man ist wirklich
dabei geblieben und hat sehr wenige Adressierungsarten, drei, vier
beispielsweise, mit unterstützt und die Mikroprogrammierung, auf die
wurde generell verzichtet.
Und man kann das dann schön noch mit vergleichen, wie diese CISC und
RISC jetzt gestehen sind.
Hier ist es noch einmal zusammengefasst, in der Vorlesung habe ich es
ja schon ausführlich auch diskutiert.
Hier auf der Folie finden Sie es noch einmal zusammengefasst.
Können Sie sich dann bis nächste Woche anschauen.
Morgen ist dann Übung.
Bis nächste Woche sehen wir uns wieder.
Bis dahin, danke schön für die Aufmerksamkeit.
Wiederschauen.