Die Komponenten des von Neumann Rechners: Der Prozessor
Die Maschinensprache-Befehle sind noch nicht die elementaren Anweisungen für die Komponenten des Prozessors: einen Maschinensprache-Befehl zu dekodieren heißt, ein Mikro-Programm zu laden, das den Befehl implementiert. Je nach Prozessor-Architektur gibt es eine unterschiedlich große semantische Lücke zwischen den Maschinensprache-Befehlen und den Mikro-Befehlen. Mit der Kenntnis der Mikro-Programme wird leichter verständlich, wie die Komponenten des Prozessors zusammenarbeiten und sie mit Hilfe des Pipelining optimal ausgelastet werden können.
Einordnung des Artikels
- Einführung in die Informatik
- Rechnerarchitektur
- Die Komponenten des von Neumann Rechners: Der Prozessor
- Rechnerarchitektur
Mikroprogramme
Beim groben Überblick über die von Neumann Rechnerarchitektur wurde gesagt, dass die Maschinensprache diejenigen Befehle enthält, die im Prozessor ausgeführt werden können. Nachdem jetzt die Arbeitsweise der elementaren Schaltungen untersucht wurde, ist klar, dass dies so nicht richtig ist: die Maschinensprache-Befehle (wie etwa die Addition von mehrstelligen Dualzahlen) sind viel zu komplex als dass sie wie eine Anweisung behandelt werden könnten. Vielmehr müssen die Maschinensprache-Befehle in mehrere Mikro-Befehle zerlegt werden.
Somit gibt es zwischen der Maschinensprache und den Mikro-Befehlen eine weitere semantische Lücke — ähnlich der semantischen Lücke zwischen höherer Programmiersprache und Maschinensprache. Mit semantischer Lücke ist gemeint, dass auf der höheren Ebene Befehle eingesetzt werden, die auf der unteren Ebene nicht verarbeitet werden können und erst auf diese Ebene heruntergebrochen werden müssen.
Als Beispiel diene der Satz:
Zwei Brüche werden multipliziert, indem man die Zähler und Nenner miteinander multipliziert.
Der Satz hört sich einfach an — ist aber für einen Grundschüler völlig unverständlich, da er die Begriffe Bruch, Zähler, Nenner nicht kennt. Um ihm den Satz verständlich zu machen — was möglich sein muss, da Bruchrechnen nichts anderes als Division ist —, muss man erst diese Begriffe erklären und die Aussage des Satzes in eine Aussage über Divisionen übersetzen. Damit wird die semantische Lücke überwunden.
Die semantische Lücke zwischen Maschinensprache und den Befehlen, die tatsächlich auf der Hardware der CPU ausgeführt werden können, wird durch sogenannte Mikroprogramme (oder Mikrocode) geschlossen. Dazu gibt es zu jedem Maschinensprache-Befehl im Mikroprogramm-Speicher (realisiert als ROM = read only memory) einen Satz von Mikro-Befehlen (sogenannte Micro-Operations, oder Micro-Ops). Dass das Steuerwerk einen Maschinensprache-Befehl dekodiert heißt dann genauer, dass das zum Befehl gehörige Mikroprogramm ausgewählt und ausgeführt wird. Und jeder der darin enthaltenen Mikro-Befehle versendet Steuersignale, die Register und Schaltungen ansprechen und somit einen Teil des Maschinensprache-Befehls erledigen.
So kann man etwa die Addition zweier Dualzahlen in folgende Mikro-Befehle zerlegen:
- Operand 1 in das erste Operanden-Register laden
- Operand 2 in das zweite Operanden-Register laden
- niedrigstes Bit aus den Operanden-Registern in den Halb-Addierer laden
- Halb-Addition ausführen
- Carry-Bit in das dafür vorgesehen Register speichern
- Ergebnis der Halb-Addition in das letzte Bit des Ergebnis-Registers schreiben
- nächste Bits in den Voll-Addierer laden
- und so weiter ...
- Ergebnis der Addition in das Ergebnis-Register schreiben.
Prozessor-Architekturen
Die Kenntnis der Mikroprogramme erlaubt es verschiedene Architekturen für Prozessoren zu entwickeln. Man unterscheidet die CISC-Architektur und die RISC-Architektur:
- CISC = Complex Instruction Set Computer
- RISC = Reduced Instruction Set Computer.
Die Unterscheidung bezieht sich darauf, wie komplex die Maschinensprache-Befehle sind — und wie groß dementsprechend die semantische Lücke zwischen der höheren Programmiersprache und der Maschinensprache ist (siehe folgende Abbildung 1).
Heutige PCs enthalten stets Prozessoren gemäß der komplexen Architektur CISC; für eingebettete System wird dagegen fast ausschließlich die reduzierte Architektur RISC verwendet.
Dass PCs heute der komplexen Prozessor-Architektur genügen, darf man aber nicht als bewusste Entscheidung ansehen: Die Prozessoren sind im Lauf der Zeit immer kleiner und leistungsfähiger geworden, daher war es naheliegend, sie auch immer komplexer zu gestalten.
Erst in den 80er Jahren sind Ideen aufgekommen, wie man Prozessoren mit reduziertem Befehlssatz bauen kann, die in der Leistungsfähigkeit den komplexen Prozessoren nicht nachstehen. Man hat die damals aktuelle Prozessor-Architektur CISC-Architektur genannt, um sie von der neuartigen RISC-Architektur abzugrenzen.
Entstanden sind die Vorschläge, weil die Untersuchung von Compilern und typischen Programmen ein 80-20-Gesetz zu Tage gefördert hat: Mit etwa 20 Prozent der Maschinensprache-Befehle werden etwa 80 Prozent der Aufgaben in einem Programm erledigt. Dagegen wird der Großteil der Maschinensprache-Befehle — und darunter finden sich sehr viele komplexe Befehle — nur für die restlichen 20 verwendet. Warum also nicht die komplexen Befehle auf einfachere Befehle reduzieren und nur diese in der Maschinensprache anbieten?
In der RISC-Architektur versucht man dann tatsächlich für die Gleichheit von Maschinensprache-Befehl und Mikro-Befehl zu sorgen, die zweite semantische Lücke der CISC-Architektur entfällt somit. Insbesondere benötigt damit jeder Maschinensprache-Befehl die gleiche Anzahl von Takten; in der CISC-Architektur streuen die Taktzahlen der Befehle immer mehr je komplexere Befehle man anbietet. Die Vorteile der reduzierten Prozessor-Architektur sind leicht anzugeben:
- Für das sogenannte Pipelining (Fließbandprinzip — es wird unten näher erklärt) hätte dies nur Vorteile. Grob gesagt versucht man Pipelining die Abarbeitung der Befehle innerhalb des Prozessors weitgehend parallel ablaufen zu lassen. Benötigen alle Befehle unterschiedliche Anzahlen von Takten, ist dies sehr schwer zu realisieren.
- Die Fertigung von Prozessoren kämpft mit zunehmender Komplexität mit immer größeren Anteilen an Ausschuss (nahe 50 Prozent) — vermutlich gibt es keinen anderen Industriezweig mit derart hohen Ausschuss-Quoten.
Aufgabe
Warum ist die Arbeit für einen Programmierer leichter, wenn er weiß, dass sein Programm später auf einem Rechner mit CISC-Architektur laufen wird?
Die Komponenten des Prozessors
Bisher wurde der Prozessor lediglich in die Bestandteile Steuerwerk, Rechenwerk (= ALU) und Register aufgeteilt. Mit dem inzwischen besseren Verständnis für Schaltnetze und Schaltwerke sowie der Einsicht, dass Maschinensprache-Befehle noch nicht die unterste Ebene des Computers darstellen (sie werden in der CISC-Architektur in Mikro-Programme übersetzt), kann nochmal versucht werden, die Komponenten des Prozessors und ihre Aufgabe bei der Befehlsverarbeitung zu benennen. Die Aufgabe der Komponenten, die bisher noch nicht behandelt wurden (vor allem Befehlsregister und Befehlszähler), wird sofort klar, wenn man sich vergegenwärtigt, dass der Prozessor immer einen Maschinensprache-Befehl entgegennimmt und abarbeitet. Man darf dies aber nicht damit verwechseln, dass hinter jedem Maschinensprache-Befehl ein komplettes Mikro-Programm steht.
Die Komponenten des Prozessors sind (siehe Abbildung 2):
1. Das Steuerwerk:
A) Das Steuerwerk kann auf den Mikroprogramm-Speicher zugreifen (ROM = read only memory) und es benötigt
B) drei Register, um seine Aufgaben zu erledigen:
- Befehlsregister IR (instruction register): In ihm steht der aktuell auszuführende Maschinensprache-Befehl.
- Befehlszähler PC (program counter): Er enthält die Adresse des nächsten auszuführenden Maschinensprache-Befehls (genauer: die Adresse im Hauptspeicher, da er von dort erst geladen werden muss).
- Statusregister SR: Es nimmt Statusmeldungen des Rechenwerkes entgegen (diese können etwa die weitere Befehlsverarbeitung beeinflussen).
C) Zur Abarbeitung der Mikro-Programme werden weitere Register benötigt, die hier nicht besprochen werden.
2. Das Rechenwerk:
Es enthält mehrere auf spezielle Aufgaben zugeschnittene arithmetische und logische Einheiten; welche das genau sind und wie komplex sie sind, hängt von der Architektur und den Aufgaben ab, die der Prozessor zu erfüllen hat. Beispiele solcher ALUs sind:
- Ganzzahl-Rechenwerk,
- Gleitkommazahl-Rechenwerk,
- Rechenwerk für spezielle mathematische Funktionen (wie Sinus- und Logarithmus-Funktion),
- Zufallsgenerator.
3. Register:
Sie wurden bisher als eigenständige Komponente des Prozessors angesehen. Es sollte inzwischen klar geworden sein, dass es treffender ist, sie nicht als eigenständige Komponenten zu betrachten, sondern sie den Komponenten zuzuordnen, die auf ihre Dienste zurückgreifen.
4. Interner Bus:
Um das Mikro-Programm zu laden und abzuarbeiten, müssen die Komponenten des Prozessors miteinander verbunden sein.
Wie der Prozessor mit dem Hauptspeicher kommuniziert und dass insbesondere das Adressregister (AR) und das Datenregister (DR) die Schnittstelle bilden, über die der Hauptspeicher angesprochen wird, soll hier nicht nochmal erklärt werden (ist bei der Beschreibung des Hauptspeichers geschehen).
Der Prozessor besteht aus den Komponenten Steuerwerk und Rechenwerk, die durch den internen Bus miteinander verbunden sind; die Register sollte man nicht als eigenständige Komponente auffassen, da sie besser den anderen Komponenten zugeordnet werden.
Im Steuerwerk befindet sich, das Befehlsregister (IR), in dem der aktuell zu bearbeitende Maschinensprache-Befehl abgelegt ist. (Dazu muss der Maschinencode im Hauptspeicher vorliegen — die dargestellte .exe-Datei — und die Befehle werden sukzessive daraus geladen.) Dekodieren dieses Befehls heißt, dass das zugehörige Mikro-Programm im Mikroprogramm-Speicher (MP, der als ROM realisiert ist) geladen und ausgeführt wird. Ausführen wiederum heißt hier, dass die Steuersignale erzeugt werden, die dafür sorgen, dass die richtigen Operanden in das Rechenwerk geladen werden und dort die richtigen logischen oder arithmetischen Operationen angesprochen werden. Das Ergebnis der Berechnung wird in einem Register abgelegt oder in den Hauptspeicher zurückgeschrieben.
Bei der Abarbeitung eines Maschinensprache-Befehls können in der ALU Statusmeldungen erzeugt werden, die im Statusregister (SR, hier nicht dargestellt) des Steuerwerks abgelegt werden. Sie können die weitere Befehlsverarbeitung beeinflussen.
Und bei der Abarbeitung eines Maschinensprache-Befehls entscheidet sich auch, welcher Befehl als nächster ausgeführt werden muss (bei einer Alternative wird entweder der eine oder der andere Befehl ausgeführt; bei einem Sprungbefehl wird eine andere Stelle im Programm angesprochen; bei einer Sequenz von Befehlen wird einfach der nächste Befehl der .exe-Datei ausgeführt). Die Adresse des nächsten auszuführenden Befehls wird in den Befehlszähler (PC) geladen.
Wenn die Bearbeitung des Maschinensprache-Befehls abgeschlossen ist, wird der Befehl, der zur Adresse im PC gehört in das Befehlsregister (IR) geladen und die Abarbeitung wiederholt sich wie eben beschrieben.
Die Arithmetic and Logic Unit (ALU)
Das Rechenwerk enthält eine oder mehrere ALU (= Arithmetic and Logic Unit); je nach Einsatzgebiet des Prozessor sind diese möglichst universell oder spezialisiert realisiert. Da in einer ALU meist zwei Eingabewerte (etwa x und y) verknüpft werden und ein Ausgabewert (etwa z) erzeugt wird, wird sie symbolisch wie in Abbildung 3 links (V-förmig) dargestellt. Entsprechend enthält sie ALU zwei Operanden-Register (x und y in Abbildung 3 rechts) und ein Ergebnis-Register (z).
Nach der Diskussion wie ein Maschinensprache-Befehl mit Hilfe von Mikro-Programmen abgearbeitet wird, sollte klar sein, dass es zu den Eingabewerten x und y weitere Steuersignale geben muss, mit deren Hilfe eine logische oder arithmetische Funktion der ALU ausgewählt werden kann (siehe Abbildung 3 rechts: Steuersignal s, das von links kommend der ALU übergeben wird). Und es werden Steuersignale erzeugt (siehe Abbildung 3 rechts: Steuersignal c, das nach rechts ausgegeben wird); dabei kann es sich etwa um:
- einen Übertrag (carry-Bit),
- ein Bit, das angibt, ob die letzte Berechnung 0 ergeben hat (Sie erinnern sich: es gibt den Maschinensprache-Befehl jnz = jump not zero),
- ein Bit, das einen Überlauf anzeigt (overflow), also dass das Ergebnis-Register die berechnete Zahl nicht aufnehmen kann
und dergleichen Informationen handeln. Diese Steuersignale werden an das Steuerwerk zurückgeschickt, da sie die weitere Befehlsabarbeitung beeinflussen.
Maschinenbefehls-Zyklus
In den Prozessor wird immer ein Maschinensprache-Befehl geladen, dieser wird ausgeführt und nach dessen Beendigung muss feststehen, welcher Maschinensprache-Befehl als nächster auszuführen ist. Und nachdem inzwischen diskutiert wurde, wie die Schaltnetze und Schaltwerke im Prozessor arbeiten, sollte klar sein, dass die Abarbeitung eines Maschinensprache-Befehls in mehrere Phasen unterteilt werden muss:
- Holphase
- Dekodierphase
- Ausführungsphase.
Ihre Erklärung im Detail (siehe dazu auch Abbildung 4).
1. Holphase:
Im Befehlszähler (PC = program counter) steht welcher Befehl als nächster auszuführen ist; genauer: im PC steht die Adresse des nächsten auszuführenden Befehls. Dieser Befehl wird aus dem Hauptspeicher in das Befehlsregister (IR = instruction register) des Steuerwerks geladen; wie Prozessor und Hauptspeicher kommunizieren, wurde bereits besprochen.
2. Dekodierphase:
Das zu dem Maschinensprache-Befehl gehörige Mikro-Programm wird aus dem Mikroprogramm-Speicher geladen. Dieses sorgt dafür, dass das Steuerwerk die für die folgenden Befehle nötigen Steuersignale erzeugen kann.
Nebenbei: Hier sollte auch klar geworden sein, dass im Maschinencode nicht Befehle in der Form
add RA RB;
stehen — wozu auch? Stattdessen steht dort einfach eine Zahl, die die den Befehl und die beiden anzusprechenden Adressen eindeutig kodieren, damit das passende Mikro-Programm auffindbar ist. Der Maschinensprache-Befehl (wie oben) wird in der Assembler-Programmierung verwendet, wenn tatsächlich die Befehle von Menschen geschrieben werden; oder sie werden von Editoren erzeugt, die Maschinencode für Menschen lesbar anzeigen.
3. Ausführungsphase:
Die eigentliche Ausführung eines Befehls (also gnauer der Befehle eines Mikro-Programmes) erfolgt in den Schritten:
- Die Operanden werden in die Register der zuständigen ALU im Rechenwerk geladen; die Adressen der Operanden sind im Maschinensprache-Befehl enthalten, sie werden aus dem Hauptspeicher oder Zwischenspeichern des Rechenwerkes geladen.
- Der Signalgenerator des Steuerwerks erzeugt die Signale, die die ALU veranlassen die betreffende Operation auszuführen.
- Die Ergebnisse werden in die vorgesehenen Register (oder in den Hauptspeicher) geschrieben.
- Zuletzt wird der Befehlszähler (PC) neu gesetzt.
Im Steuerwerk ist zusätzlich zum Befehlsregister (IR), zum Befehlszähler (PC) und zum Mikroprogramm-Speicher (MP) der Signalgenerator (SG) gezeigt. Er erzeugt aus den Mikro-Befehlen die Steuersignale (s), die bestimmen, welche logische oder arithmetische Funktion von der ALU ausgeführt wird.
Die Inhalte der Operanden-Register (x und y) werden entweder aus dem Hauptspeicher oder aus weiteren Registern (hier nicht dargestellt) geladen. Durch die ALU wird einerseits das Ergebnis-Register (z) gefüllt; dessen Inhalt wird entweder zum Hauptspeicher übertragen oder in einem Register abgespeichert (wieder nicht dargestellt). Andererseits werden die Steuersignale (c) erzeugt, die an das Steuerwerk geschickt werden, da sie den nächsten abzuarbeitenden Befehl (PC) beeinflussen.
Pipelining
Der Prozessor arbeitet sequentiell, das heißt er kann immer nur einen Maschinensprache-Befehl nach dem anderen abarbeiten, aber nicht mehrere gleichzeitig. Dennoch wird die Arbeit im Inneren des Prozessors weitgehend parallel erledigt. Dass dies kein Widerspruch ist, zeigt das folgende Beispiel:
Ein Arbeitsgang lasse sich in 3 Phasen zerlegen (PH1, PH2, PH3) und er besitze folgende Eigenschaften:
- die 3 Einzelschritte können nur in dieser Reihenfolge ausgeführt werden,
- sie beanspruchen etwa gleich lange Zeiten,
- die Hardware, die in einer Phase benötigt wird, wird in den anderen Phasen nicht benötigt,
- nach dem Ende des Arbeitsganges startet er sofort wieder von vorne.
Naheliegend ist folgender Ablauf:
PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 ...
Die Arbeitsschritte werden nacheinander ausgeführt.
Es gibt aber auch eine parallele Abarbeitung:
PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 || PH1 - PH2 - PH3 ||
Sobald Phase 1 beendet ist und Phase 2 beginnt, ist die Hardware frei, die zuvor Phase 1 abgearbeitet hat. Daher kann Phase 1 sofort wieder aufgenommen werden: jetzt werden Phase 2 (in der 1. Zeile) und Phase 1 (in der 2. Zeile) gleichzeitig bearbeitet. Und sobald Phase 1 aus der 2. Zeile beendet ist, startet Phase 1 erneut in der 3. Zeile.
Diese Bearbeitung wird als Pipelining oder Fließbandprinzip bezeichnet.
Man erkennt sofort die Vorteile der parallelen Bearbeitung:
- Sieht man von den Totzeiten ab (beim Start und beim Ende), können drei Arbeitsgänge gleichzeitig erledigt werden.
- Die Hardware ist bei der sequentiellen Ausführung nur zu einem Drittel ausgelastet, bei der parallelen Abarbeitung ist sie (wieder mit Ausnahme der Totzeiten) vollständig ausgelastet.
Der Nachteil liegt in einem erhöhten Koordinationsaufwand, der umso größer wird je mehr Phasen es gibt und wenn die Dauer der einzelnen Phasen nicht mehr identisch ist.
Der Maschinenbefehls-Zyklus besteht aus drei Phasen (Holphase, Dekodierphase und Ausführungsphase) und wird im realen Prozessor wie hier beschrieben parallel abgearbeitet. Die Koordination ist aber sehr aufwendig, da in der CISC-Architektur Maschinensprache-Befehle sehr unterschiedlicher Taktzahlen vorkommen.