Die Konzeption des Von-Neumann-Rechners zeigt innerhalb der Central Processing Unit (CPU) eine Zweiteilung der Aufgaben in Control Unit (Leit- oder Steuerwerk) und Arithmetical Logical Unit (Rechenwerk).
Das Leitwerk hat dabei im Wesentlichen die Aufgabe, den gesamten Programmablauf sowie den der einzelnen Instruktionen jeweils in ihrer zeitlichen Sequenz zu steuern. Die arithmetischen und logischen Rechenkapazitäten sind hingegen im Rechenwerk zusammengefasst.
| |
Teil 1 | |
---|---|
Teil 2 | |
Teil 3 |
Diesen Artikel und eine ganze Reihe weiterer Grundlagenthemen zu Prozessoren finden Sie auch in unserem tecCHANNEL-Compact "Prozessor-Technologie". Die Ausgabe können Sie in unserem Online-Shop für 9,90 Euro versandkostenfrei bestellen. Ausführliche Infos zum Inhalt des tecCHANNEL-Compact "Prozessor-Technologie" finden Sie hier.
Register im Leit- und Rechenwerk
Beide Teileinheiten benötigen zur Befehlsbearbeitung Speicherplatz, der ausschließlich vom Prozessor verwaltet wird und außerhalb des "normalen" Code- beziehungsweise Datenspeichers liegt. Diese Speicherstellen bezeichnet man im Allgemeinen als Register. Sie sind in drei Kategorien eingeteilt:
Datenregister zur Aufnahme von Operanden sowie zur Speicherung von Ergebnissen
Adressregister zur Adressierung von Operanden im Speicherbereich des Prozessors
Steuerregister zur Steuerung des Ablaufs der normalen Befehlsbearbeitung und für besondere Situationen (Ausnahmebehandlung)
Die für eine maschinennahe Programmierung direkt ansprechbaren Register sind im Registermodell (dazu später) zusammengefasst. In diesem Fall müssen die Register auch adressiert werden beziehungsweise dienen im Fall der Nutzung als Adressregister zur Adressierung von Speicherzellen.
Die Register in einem Prozessor lassen sich hierbei nicht immer eindeutig einem Bereich (Leitwerk oder Rechenwerk) zuordnen. Am ehesten gelingt dieses für die Datenregister, die im Bereich des Rechenwerks (ALU) liegen. Diese Register besitzen eine zentrale Rolle im Rahmen der Berechnung neuer Werte. In modernen Konzepten wird die Anzahl der Datenregister möglichst auf hohem Niveau gehalten, da die Ausführungszeiten von Befehlen mit internen Daten deutlich unter denjenigen mit Daten aus dem Speicher liegen.
Die erwähnten Adressregister können dem Leitwerk (CU) zugerechnet werden. Diese Register sind für eine Adressierung von Operanden zuständig, die nicht in der sequenziellen Abfolge des Programmzählers liegen. Diese Adressregister werden im Rahmen der Befehlsbearbeitung mit der berechneten Adresse geladen und bestimmen während des Bustransfers die am Adressbus liegenden Signale. Zu den Steuerregistern, die wiederum weitest gehend dem Leitwerk zuzuordnen sind, zählen der Programmzähler, das Statusregister und der Stackpointer des Prozessors. Den Stackpointer kann man auch als Adressregister mit zusätzlicher Funktionalität bezeichnen.
Aufgaben des Leitwerks
Das Leit- oder Steuerwerk des Von-Neumann-Prozessors ist sowohl in älteren als auch aktuell in einfacheren Architekturen als zentraler Automat ausgeführt. Hierin werden folgende Aufgaben erfüllt:
Die Steuerung des Instruction Fetch, das heißt des Zugriffs auf den nächsten, auszuführenden Befehl.
Die Berechnung der nächsten Fetch-Adresse: Im Normalfall eines Befehls, der nicht in den Kontrollfluss eingreift, ist dies das im Adressraum an der nächsten Adresse liegende Kommando. Im Spezialfall der Kontrollflussbefehle (Sprünge, Verzweigungen) bestimmen diese die nächste Programmadresse, die im Allgemeinen außerhalb der Sequenz liegt.
Beide Aufgaben ergeben die Ablaufsteuerung eines Programms.
Die Steuerung der Befehlsbearbeitung der jeweiligen Instruktion im Prozessor: Hierzu zählen die Dekodierung des Befehls, die Anforderung der notwendigen Operanden (Daten), die Ansteuerung des Rechenwerks mit den entsprechenden Informationen zur Operation, die Ergebnisspeicherung sowie gegebenenfalls die Behandlung von Ausnahmen.
Diese Aufgaben lassen sich sehr gut zentralisieren, sofern die Kommunikationslängen im Prozessor eine untergeordnete Rolle spielen. Ein Vorteil der zentralen Lösung ist auch, dass jederzeit Rückmeldungen aus den einzelnen Teilen berücksichtigt werden können. Das Grundmodell des Von-Neumann-Rechners geht davon aus, dass alle Teilaktionen für einen Befehl ausgeführt sind, bevor der nächste beginnt.
Mit Einführung des Phasen-Pipelinings ergibt sich eine verteilte Ausführung der Befehle und so auch eine verteilte Struktur des Leitwerks. Dies ist bei modernen Architekturen auch deshalb notwendig, weil die Signalübermittlung im Prozessor bei großen Entfernungen einen wesentlichen Teil der Verzögerung ausmacht.
Rechenwerk
Das Rechenwerk (Arithmetical Logical Unit, ALU) in einem Von-Neumann-Prozessor ist im eigentlichen Sinn kein Rechenwerk, sondern ein Rechennetz, wenn man es mit den Begriffen Schaltwerk und Schaltnetz [4] vergleicht. Es beinhaltet mehrere Komponenten zur Durchführung aller benötigten Berechnungen. Hierzu zählen mindestens das oder die Datenregister, das eigentliche Berechnungsschaltnetz (die ALU) und die verbindenden (internen) Datenbusse.
Oft kommen weitere Komponenten zum Rechenwerk hinzu. So sind beispielsweise Adressrechnungen für berechnete Sprungadressen durchzuführen. Dies lässt sich innerhalb der zentralen ALU, jedoch auch im Rahmen eigenständiger Einheiten, integrieren. Wir beschränken uns auf die minimalen Komponenten im Rechenwerk. Weiter führende Darstellungen sind etwa in [5] zu finden.
Das Rechenwerk im engeren Sinn integriert alle Berechnungskapazitäten für neue Datenwerte innerhalb des Prozessors. Bei speziellen Einheiten wie Multiplizierern, Multiply-Accumulate-Einheiten (MAC) oder Barrel-Shiftern gibt es Ausnahmen von dieser Regel, diese sind häufig separat angeordnet.
Die ALU besteht aus einem von außen steuerbaren Schaltnetz mit zwei Datenbussen am Eingang und zwei am Ausgang. Das Bild "ALU" gibt einen Überblick. Die Steuereingänge werden von dem Leitwerk mit Signalen belegt, die aus dem Befehlswort extrahiert sind. Sie konfigurieren das Schaltnetz für den Zeitraum der Berechnung auf die gewünschte Verknüpfung. Die Operation zwischen den Daten ist dabei in Zusammenhang mit der Interpretation der jeweiligen Darstellung zu sehen. Folgende Klassen von Verknüpfungen sind in der ALU integriert:
Arithmetische Verknüpfungen, wie Addition, Subtraktion, Vergleiche, Mul-tiplikation und Division: Für die meisten Prozessoren sind diese Verknüpfungen auf Integerwerte der Daten beschränkt, während die Interpretation der Floating-Point-Darstellungen (siehe [3]) entweder durch eine Software-Emulation oder innerhalb spezieller Coprozessoren erfolgt. Wie erwähnt können sehr aufwendige Verknüpfungen in speziellen Einheiten integriert sein.
Logische Verknüpfungen, wie Disjunktion (ODER), Konjunktion (UND), Negation (NOT), Einer-Komplement (XOR): Diese logischen Verknüpfungen sind auf eine binärwertige Interpretation der Daten fixiert.
Verschiebeoperationen, wie Rotation, arithmetischer und logischer Shift: Auch in diesem Fall werden die Daten als Binärwerte interpretiert, obwohl einzelne Operationen auch einen Zusammenhang mit Arithmetik zeigen.
Registermodell
Das Registermodell gehört zu den "sichtbaren" Teilen des Prozessors: Die Software-Entwicklung auf maschinennaher Ebene kann und muss die durch Maschinenbefehle in ihren Werten veränderlichen Register berücksichtigen. Die Variabilität bezieht sich dabei sowohl auf Register, die in Verbindung mit der ALU stehen (Datenregister), als auch auf Register, die zum Leitwerk gehören (Kontrollregister).
Das Bild "Mindestsatz" zeigt ein minimales Registermodell, wie es zum Beispiel in "kleinen" 8-Bit-Mikro-Controllern zu finden ist. Hierbei wird deutlich, dass die daten-bezogenen Register (Statusregister, Akkumulator) in der typischen Datenbreite ausgeführt sind. Die adressbezogenen Register (Stackpointer, Programmzähler) wurden mit der hier angenommenen Breite von 16 Bit für die Adressen gewählt.
Programmzähler
Das zentrale Register im Leitwerk ist der Programmzähler (Program Counter, PC, auch Instruction Pointer, IP). Dieses Register zeigt auf die aktuelle Stelle im Programmspeicher, die sich in Bearbeitung befindet.
Der Programmzähler des Prozessorsc wird regelmäßig dem Programmfluss entsprechend modifiziert:
Nach einem Reset beziehungsweise nach dem Einschalten der Versorgungsspannung ("Kaltstart") und einer Wartezeit zum Einschwingen des Takts muss der Programmzähler mit einer definierten Adresse geladen werden. Damit wird ein deterministisches Verhalten des Prozessors nach einem Neustart ermöglicht. Diese Adresse kann selbst fixiert sein (direktes Setzen der Startadresse), beispielsweise bei der 8051-Familie auf die Adresse 0. Sie kann jedoch auch an einer definierten Adresse im Speicherbereich stehen und von dort in den PC geladen werden (indirektes Setzen der Startadresse).
Nach dem Laden eines Befehlsworts (Fetch) wird der Programmzähler um eine Zugriffseinheit (zum Beispiel zwei Byte-Adressen für 16-Bit-Zugriffe) erhöht. Er zeigt anschließend auf die nachfolgende Adresse. Diese Eigenschaft kennzeichnet den Programmzähler als Binärzähler.
Im Rahmen eines dekodierten Sprungbefehls wird der Programmzähler mit dem Operanden dieses Befehlsworts beziehungsweise der daraus berechneten Sprungzieladresse neu geladen. Er zeigt danach auf eine beliebige Adresse im Speicherbereich des Prozessors.
Der Programmzähler kann auf Grund der vorgenannten drei wesentlichen Eigenschaften als ladbarer Binärzähler aufgefasst werden, gegebenenfalls mit Reset- beziehungsweise Set-Funktion seiner internen Flipflops.
Statusregister
Das Statusregister eines Prozessors ist ein wichtiges Interface zwischen Leit- und Rechenwerk. Hier sind üblicherweise einige Statusbits, so genannte Flags, gespeichert. Deren Werte stammen aus den Berechnungen des Rechenwerks, speziellen Befehlswörtern des Kontrollflusses oder Betriebszuständen des Prozessors.
Einige Flags eines typischen Statusregisters sind im Bild "Statusregister" dargestellt. In dieser oder ähnlicher Form sind sie häufig in Prozessoren integriert:
Das C-Flag (Carry) entsteht bei arithmetischen Operationen wie der Addition als Übertrag, das heißt als berechnetes Bit, das nicht mehr in die Darstellung innerhalb des Zielregisters passt. In Kombination mit dem V-Flag (Overflow, auch als OV bezeichnet) lässt sich eine vorzeichenbehaftete Arithmetik programmieren, die auch Überläufe erkennt. Andere Einsatzbereiche des Carry-Flags sind Shift- und Rotationsbefehle für Registerinhalte. Beide Flags werden meist durch bedingte Sprung- beziehungsweise Verzweigungsbefehle ausgewertet, deren Ausführung mit dem Zustand des jeweiligen Flags gekoppelt ist.
Das Z- (Zero) und das N-Flag (Negative) sind Statusbits, deren Wert oftmals durch Vergleiche oder bei Zähloperationen gesetzt und im weiteren Programmfluss wie C und V durch bedingte Sprungbefehle ausgewertet wird.
Das I-Flag (Interrupt Disable oder Interrupt Enable, je nach Architektur) zeigt verschiedene Betriebszustände des Prozessors an und steht in engem Zusammenhang mit dem Interrupt-Konzept (siehe dazu Kapitel 2.9), das eine Erweiterung des ursprünglichen Von-Neumann-Rechnermodells ist. Im Rahmen des Statusregisters wird dieses Flag genutzt, um Unterbrechungen des normalen Programmflusses durch externe Anforderungen zu unterbinden beziehungsweise zu ermöglichen. Das einfache I-Flag lässt sich auch durch mehrfache Flags ersetzen. In diesem Fall wird der Zustand der Flags als Priorität gedeutet, die mindestens eine Unterbrechung besitzen muss, um bearbeitet zu werden. Das I-Flag wird nicht durch bedingte Sprungbefehle ausgewertet, sondern im Rahmen eines globalen Konzepts für Prozessoren mit Unterbrechungsfähigkeit. Setzen beziehungsweise Löschen dieses Bits erfolgt durch entsprechende Befehlswörter.
Neben den hier genannten Beispielen existieren in den verschiedenen Prozessorarchitekturen weitere Flags, insbesondere zur Erweiterung der arithmetischen Kapazitäten und zur Einführung bedingter Befehle (so genannte Predicate Flags).
Stackpointer
Ein Stack (Stapelspeicher, Kellerspeicher) ist ein wichtiges Hilfsmittel zur Realisierung von Unterprogrammen und Unterbrechungen. Dieser Speicherbereich ist - im Gegensatz zum allgemeinen Speicher - nicht mit einem wahlfreien Zugriffsverfahren zu erreichen, das Zugriffe in beliebiger Adressfolge ermöglicht. Der nächstfolgende Transfer vom oder zum Stack ist von der Adresse des vorhergehenden abhängig.
Es existieren aktuell zwei Varianten der Stack-Realisierung. Die schnellere Variante benutzt einen ausgezeichneten Speicherbereich, der direkt im Prozessor implementiert ist und durch keinen anderen Zugriff zu erreichen ist. Diese Form des Stacks fällt meist recht klein aus, da Siliziumfläche auf dem Prozessor-Die benötigt wird. Andererseits ist der Zugriff sehr schnell, auch für Mehrfachzugriffe, da das (langsame) externe Bussystem umgangen wird.
Die zweite Variante benötigt einen Stackpointer im Prozessor und bildet jeden Stack-Zugriff auf den (externen) Speicher ab. Diese Variante ist die meist genutzte, da sie Ressourcen schonend ist. Nachteilig sind die Geschwindigkeit sowie die immanenten Fehlermöglichkeiten, da der Speicherbereich des Stacks auch durch andere, "normale" Datenzugriffe erreichbar ist.
Der Stack arbeitet in beiden Varianten nach dem LIFO-Prinzip (Last In First Out), das sich mit einem Tellerstapel vergleichen lässt: Lese- und Schreibzugriffe beziehen sich hierbei immer auf den obersten Teil des Stapels (Top of Stack). Schreibzugriffe zielen auf das nächste freie Element, Lesezugriffe auf das oberste besetzte Element, das damit gleichzeitig für neue Schreibzugriffe freigegeben wird.
Drei Aktionen nutzen den Stack während der Bearbeitung eines Programms:
Unterprogrammsprünge (Funktionsaufrufe): Die Rücksprungadresse(= aktueller PC-Wert + "1") wird auf dem Stack gespeichert und beim Rücksprung wieder in den Programmzähler geladen.
Lokale Variablen (zum Beispiel in Unterprogrammen): In imperativen oder funktionalen Sprachen wie C oder Pascal mit intensiver Nutzung von Funktionen werden lokale Variablen, die einen beschränkten Gültigkeitsbereich besitzen, auf dem Stack angelegt und bei Verlassen der Routine wieder verworfen.
Unterbrechungsanforderungen (Interrupt Requests, IRQs): Sowohl die Rücksprungadresse nach Behandlung der IRQs als auch (falls erforderlich) gerettete Registerinhalte und das Prozessor-Statusregister werden auf dem Stack zwischengespeichert.
Beispiel zum Stackpointer
Für einen explizit formulierten Datentransfer zwischen einem Datenregister (das hier mit Akkumulator bezeichnet ist) und dem Stack gibt es bei vielen Prozessoren zumeist die besonderen Transferbefehle PUSH und POP:
PUSH A A (r) (--SP)
POP A (SP++) (r) A
PUSH A schreibt den Inhalt des Akkumulators in die Speicherzelle, auf die der Stackpointer SP zeigt, nachdem er zuvor dekrementiert wurde (Predekrement). POP A kopiert hingegen zunächst den Inhalt der Speicherzelle, auf die SP zeigt, in den Akkumulator und erhöht anschließend SP um 1 (Postinkrement, Bild "Stack-Operation"). In diesem Beispiel "wächst" der Stack nach unten (zu kleineren Adressen hin), wenn Daten abgelegt werden.
Die Adressierung der Speicherzelle erfolgt damit relativ zum Stackpointer. Dieses Register muss bei Start des Rechners nach Reset entweder durch die Hardware oder durch einen kurzen Abschnitt in der Software auf einen definierten Anfangswert gesetzt werden, um einen definierten Zustand zu erhalten und gegebenenfalls sporadisch auftretende Programmfehler zu verhindern. Zudem kann es durch einen zu kleinen Stack zu Abstürzen kommen, indem mehr Speicherplatz genutzt werden soll, als real vorhanden ist. Dies resultiert je nach Architektur in einem "Wrap Around". Der Stackpointer fängt wieder am Anfang an (also zum Beispiel bei hohen Adressen). Man spricht dann von Stack Overflow.
Datenregister
Grundsätzlich unterscheidet man zwei verschiedene Implementierungen von Datenregistern innerhalb des Rechenwerks. In einer Stack-orientierten (Rechenwerk-)Architektur sind die Register ähnlich wie im allgemeinen Stack in einer LIFO-Struktur angeordnet. Auch für die internen Register existiert hier ein Stackpointer, der auf das aktuelle Register zeigt. Diese Architektur, die die umgekehrte polnische Notation (Reverse Polnish Notation, RPN) für die Formulierung von Algorithmen benötigt, wird derzeit kaum noch verwendet, da die Unterstützung durch Hochsprachen (zum Beispiel Forth) sehr gering ist.
Die andere Implementierung organisiert die Datenregister in einem Registerblock, bei einer größeren Anzahl auch Registerfile genannt, mit einer wahlfreien Adressierung innerhalb des Rechenwerks. Die Wahlfreiheit des Zugriffs bedeutet, dass die Befehlswörter aus dem Programmspeicher, die eine Aktion im Rechenwerk beinhalten, in beliebiger Reihenfolge stehen dürfen (abgesehen von den Abhängigkeiten des Datenflusses).
Das Befehlsformat eines Prozessors kann 1-, 2- und 3-Adressbefehle beinhalten, in Ausnahmefällen auch mehr. Mit den Adressen sind die Quellen und das Ziel der Operation bezeichnet, so dass beispielsweise bei einer üblichen Addition zwei Quellen mit einem Ziel über die additive Verknüpfung verbunden sind. Die Varia-bilität in der Anzahl der Adressen begründet letztendlich die konkrete Ausführung der internen Daten- und Adressierungsbusse.
Klassifizierung von Prozessoren gemäß Operandenzugriff
Die Form des Operandenzugriffs für eine Operation ist sehr charakteristisch für Prozessorklassen, da sie das interne Speichermodell bestimmt. Aus diesem Grund wird diese Klassifizierung häufig genutzt.
Dieses interne Speichermodell trifft Aussagen darüber, wie die für Datenopera-tionen, etwa eine Addition, notwendigen Operanden vor und nach der Ausführung im Zugriffsbereich des Prozessors liegen. Hierbei unterscheidet man drei grundsätzliche Methoden:
Im Rahmen einer Stack-Architektur werden die Quell- und Zieloperanden auf dem Stack gespeichert und dort auf sie zugegriffen. Der Zugriff seitens des Prozessors kann hierbei sehr implizit erfolgen, etwa in einer exakt festgelegten Reihenfolge (umgekehrt polnische Notation).
Die Akkumulator-Architektur (siehe Bild 2.9) zeichnet sich hingegen durch einen (gegebenenfalls wenige) Akkumulator(en) als zentrale(s) Register aus. In der Befehlskodierung ist bereits implizit festgelegt, welches Register als Quelle und gegebenenfalls Ziel zur Operation genutzt wird.
Die (General-Purpose-) Register-Architektur wiederum bezieht ebenfalls mindestens einen Operanden aus einem internen Prozessorregister. Dieses Register ist jedoch im Befehl explizit anzugeben. In dieser Prozessorklasse unterscheidet man folgende Unterklassifizierungen im Hinblick auf die RISC-Architekturen:
Load-/Store-Architekturen besitzen ein ausschließliches Interface zwischen internem Prozessorregister und externem Speicher zum Datenaustausch zwischen diesen. Operationen zur Verknüpfung von Daten wirken ausschließlich zwischen Registerinhalten.
Register-Speicher-Architekturen stellen eine Erweiterung der Akkumulator-Architektur dar. Bei Befehlen dieser Prozessorsubklasse wird sowohl das Register als auch die Speicheradresse explizit angegeben.
Vor- und Nachteile der Prozessorklassen
Die genannten Prozessorklassen haben Vor- und Nachteile, die in der folgenden Tabelle zusammengefasst sind. Es bestehen daneben noch einige weitere Möglichkeiten zur Klassifizierung der Prozessoren. Ein Beispiel hierfür ist die Unterscheidung, ob der Zieloperand explizit angegeben werden kann oder implizit mit einem der Quelloperanden übereinstimmt.
Typ | Vorteile | Nachteile |
---|---|---|
| ||
Stack | Keine explizite Angabe von Operanden notwendig, daher minimale Opcodelänge für Arithmetikbefehle | Umfangreiche Austauschoperationen von und zum Stack notwendig, das heißt gegebenenfalls wachsende Codegröße |
Akkumulator | Teilweise implizite Operandenangabe möglich (1. Quelloperand, Zieloperand). Einfachste Architektur, Verzicht auf Stack möglich | Das zentrale Register wird sehr stark genutzt, daher gegebenenfalls Engpass mit Notwendigkeit zur Zwischenspeicherung |
Register-Speicher | Daten können ohne Ladezugriff für Operationen genutzt werden | Ladezugriffe im Speicherbereich benötigen wesentlich mehr Zeit, daher gegebenenfalls Verzögerung gegenüber Registerzugriff |
Register-Register | Schnellstmögliche Operationen durch Registeroperanden | Explizite Ladezugriffe notwendig, Codegröße wächst |
Ausblick
Eine genaue Betrachtung der Phasen der Befehlsbearbeitung und - als Erweiterung des Von-Neumann-Rechners - das Interrupt-Request-Konzept - bilden den Abschluss dieser Artikelserie im dritten Teil. (mec)
| |
Teil 1 | |
---|---|
Teil 2 | |
Teil 3 |
Diesen Artikel und eine ganze Reihe weiterer Grundlagenthemen zu Prozessoren finden Sie auch in unserem tecCHANNEL-Compact "Prozessor-Technologie". Die Ausgabe können Sie in unserem Online-Shop für 9,90 Euro versandkostenfrei bestellen. Ausführliche Infos zum Inhalt des tecCHANNEL-Compact "Prozessor-Technologie" finden Sie hier.
Literatur
[1] Christian Siemers: Prozessorbau - München, Wien: Carl Hanser Verlag, 1999
[2] Oberschelp, W.; Vossen, G.: Rechneraufbau und Rechnerstrukturen - 8. Auflage - München, Wien: R. Oldenbourg-Verlag, 2000
[3] Schiffmann, W.; Schmitz, R. ; Weiland, J.: Technische Informatik 2, Grundlagen der Computertechnik - 2. Auflage - Berlin, Heidelberg, New York: Springer Verlag, 2001
[4] Christian Siemers, Axel Sikora (Hrsg.): "Taschenbuch Digitaltechnik", Fachbuchverlag Leipzig im Carl Hanser Verlag, München, Wien, Januar 2003. ISBN 3-446-21862-9
[5] Oberschelp, W.; Vossen, G.: Rechneraufbau und Rechnerstrukturen- 8. Auflage - München, Wien: R. Oldenbourg-Verlag, 2000