15 Jahre TecChannel - der beliebteste Artikel 1999

So funktioniert ein Prozessor

18.10.1999
Mikroprozessoren sind hochkomplexe Maschinen. Sie basieren jedoch auf einem einfachen Grundprinzip. Wer es kennt, versteht auch die Funktionsweise der modernen CPUs.

Hätten Sie gedacht, dass das Grundprinzip Ihres Computers vor über 50 Jahren erfunden wurde, obwohl der Mikroprozessor nur halb so alt und der PC noch ein Teenager ist?

Das Funktionsprinzip wurde von dem in Budapest geborenen Mathematiker John von Neumann erdacht und in dem US-Militärcomputer EDSAC im Jahr 1949 erstmals realisiert. Die Von-Neumann-Architektur besteht aus vier Funktionseinheiten, die in Bild 1 zu sehen sind: Rechenwerk, Steuerwerk, Speicher (Memory) und Ein-/Ausgabeeinheit (I/O-Unit). Dazu kommen noch die Verbindungen zwischen den Funktionsblöcken - das Bussystem.

Die beiden wichtigsten Einheiten ALU und CU sind heute im Prozessor vereint. Die CPU als Ganzes übernimmt innerhalb des Von-Neumann-Rechners die Ausführung der Befehle und die hierfür notwendige Ablaufsteuerung.

15 Jahre TecChannel Jubiläumspaket -
Zum 15. Geburtstag hat das TecChannel-Team für Sie ein spezielles Jubiläumspaket geschnürt.

Von-Neumann-Rechner

An der Struktur des Von-Neumann-Rechners ändert sich unabhängig von der Aufgabenstellung grundsätzlich nichts. Die Anpassung für jedes zu lösende Problem erfolgt mit im Speicher abgelegten Programmen. Diese Software beinhaltet die Informationen zur Steuerung des Rechners. Bei den Informationen wird auf Prozessorebene zwar zwischen Befehlen und Daten unterschieden, es gibt jedoch nur einen Speicher für beides. Jede Speicherzelle ist mit einer festen Adresse eindeutig identifizierbar.

Der Von-Neumann-Rechner ist eine sequenziell arbeitende Maschine. In ihrer ursprünglichen Form verarbeitet sie mit nur einem Prozessor Schritt für Schritt Befehle und Daten, die aus dem Speicher stammen. Damit ist auch schon der Flaschenhals der Von-Neumann-Architektur beschrieben. Obwohl Befehle und Daten aus dem gleichen Speicher kommen, gibt es nur eine Busverbindung für Beides dorthin.

Im Laufe der Jahre haben die Prozessorhersteller deshalb nach Möglichkeiten gesucht, diesen Engpass zu erweitern. So wurde eine hierarchisch gegliederte Speicherstruktur mit Registern und verschiedenen Cache-Ebenen eingeführt. Die sequenzielle Befehlsausführung wird in der CPU nach Kräften parallelisiert. Dazu stehen mehrere Funktionseinheiten und Ausführungsebenen bereit. Und es sind Befehle hinzugekommen, die pro Ausführungszyklus mehrere Datensätze verarbeiten können (ISSE und 3DNow!).

Erweiterte Architektur

Der Von-Neumann-Rechner holt alle Befehle und Daten direkt aus dem Speicher. Für die heutigen CPUs wäre das viel zu langsam. Das Bild zeigt einen erweiterten tecChannel-Prozessor, der technologisch zwar nicht gegen Pentium III und Co. antreten kann, aber als übersichtliches Beispiel ist er ideal geeignet.

Die tecChannel-CPU orientiert sich hinsichtlich Arbeitsweise stark an den x86-Prozessoren der PCs. Deshalb besitzt sie einen zusätzlichen Registersatz, der den Zugriff auf Befehle und Daten ohne Wartezyklen ermöglicht. Es handelt sich um Mehrzweckregister für Befehle und Daten. Das Adresswerk ist für die Berechnung der effektiven Adresse zuständig. Und der L1-Cache entkoppelt den Prozessorkern zusätzlich vom langsamen externen Speicher. Die BIU arbeitet weitgehend selbstständig vom Rest der CPU und ist für die Kommunikation mit der Außenwelt verantwortlich.

Wie die einzelnen Funktionsblöcke genau arbeiten, klären wir in den folgenden Abschnitten. Bevor wir aber ins Detail gehen, interessiert uns der grundsätzliche Programmablauf im Prozessor:

Steuereinheit (CU)

Die CU ist die Kommandozentrale der CPU. Sie steuert alle Abläufe im Innern des Prozessors sowie seine Kommunikation nach außen.

Der Befehlszähler zeigt auf den nächsten auszuführenden Befehl. Die CU holt sich diesen Befehl aus dem Arbeitsspeicher/Cache und speichert ihn in einem Register zwischen. Damit ist der CPU-Bus frei für weitere Aktionen. Im Befehlsdecoder untersucht die CU die einzelnen Bits des Kommandos dann genauer. Aus einem Teil der Information ergibt sich der weitere logische und zeitliche Ablauf bei der Befehlsausführung. Handelt es sich um einen fest implementierten Befehl, werden sofort die entsprechenden Schritte in der Ablaufsteuerung eingeleitet. Bei komplexeren Kommandos ermittelt der Decoder aus den Befehlsbits die Adresse der zugehörigen Steuerinformation im Microcode-ROM.

Sind alle zur Steuerung notwendigen Informationen gesammelt, beginnt die Ablaufsteuerung damit, das System zu koordinieren. Dazu gehört auch die Steuerung der BIU, um die Operanden für die Rechenbefehle in die Register zu laden.

Rechenwerk (ALU)

Die ALU ist in der CPU für die Rechenarbeit zuständig. Alle aktuellen PC-Prozessoren besitzen neben einem oder mehreren dieser Rechenwerke für Ganzzahlen auch solche für Fließkommaarithmetik. Die FPUs ignorieren wir bei der Erläuterung der prinzipiellen Funktionsweise einer CPU, weil sie prinzipiell mit einer ALU gleichzusetzen ist.

Das Bild zeigt den Datenweg unserer Beispiel-CPU. Die CU steuert die ALU, die auf Anweisung die beiden Operanden aus dem Registersatz holt, mit denen sie rechnen soll. Sie werden zunächst in den beiden Hilfsregistern zwischengepuffert, damit sie während der gesamten Rechenoperation stabil anliegen. Im nächsten Schritt führt die ALU die von der CU geforderte Rechenoperation aus (beispielsweise eine Addition der beiden Operanden). Das Resultat wird schließlich im Ergebnisregister zwischengepuffert, damit sich die ALU sofort der nächsten Aufgabe zuwenden kann. Neben dem eigentlichen Resultat der Information landen im Statusregister noch Zusatzinformationen. Entsteht beispielsweise bei einer Addition ein Übertrag, wird das im Carry Bit des Statusregisters vermerkt.

Adresseinheit (AU) & Busschnittstelle (BIU)

Unsere tecChannel-CPU verfügt über ein einfaches Adresswerk, das mit den wesentlich leistungsfähigeren MMU s der aktuellen PC-Prozessoren nicht mithalten kann. Die AU macht aber die prinzipielle Arbeitsweise dieses Funktionsblocks deutlich.

Der Aufbau der AU mit einem zentralen Addierer in Bild 5 ähnelt dem der ALU. Tatsächlich wurde die Adressberechnung bei den ersten Prozessoren auch noch in dieser erledigt. Die spezialisierte AU erledigt das jedoch schneller und vor allem parallel zur ALU.

Der Decoder ist im einfachsten Fall als Linksschieberegister realisiert. Dieser Barrel Shifter extrahiert die Adressinformation aus dem Befehl durch Verschieben des Befehlscodes um n Bits in nur einem Taktzyklus.

Die extrahierte Grundadresse gelangt dann in Hilfsregister A, wo Sie stabil anliegt bis der Addierer seine Arbeit beendet hat. Hilfsregister B beinhaltet den Inhalt des Programmzählers oder den des BIU-Adresspuffers. Aus der Addition der Hilfsregisterinhalte ergibt sich die neue Adresse.

Die komplexere MMU realer PC-Prozessoren kann darüber hinaus virtuelle Adressen verwalten. Das Betriebssystem lagert hierbei den Speicher blockweise (seitenweise) auf die Festplatte aus. Greift die CPU auf den ausgelagerten Speicher zu, verursacht das einen Seitenfehler, der die MMU zum Handeln veranlasst. Das Betriebssystem blendet dann den gewünschten Speicherbereich in das RAM des PCs ein und lagert einen gerade nicht benötigten dafür aus. Den virtuellen Speicher kann die CPU dank MMU so ansprechen, als wäre er real existierendes RAM.

Busschnittstelle (BIU)

Die Busschnittstelle verbindet die internen Busse des Prozessors mit der Außenwelt. Sie enthält Puffer zur Zwischenspeicherung von Adressen, Daten und Steuersignalen.

Die CPU arbeitet intern mit einer möglichst niedrigen Spannung, damit die Erwärmung bei hohen Taktfrequenzen in erträglichen Grenzen bleibt. Die BIU sorgt deshalb auch für eine Pegelanpassung zwischen dem CPU-Kern und dem externen Bussystem.

Cache-Grundlagen

Zur Steigerung der Arbeitsleistung sitzt in der CPU zwischen den extrem schnellen Funktionseinheiten und dem vergleichsweise sehr langsamen Arbeitsspeicher der L1-Cache. Je nach Prozessor handelt es sich um einen zweigeteilten Cache (Split Cache) oder um einen einheitlichen Speicher (Unified Cache) für Befehle und Daten. Bei der Betrachtung der Funktionsweise spielt das keine Rolle.

Aus Platzgründen kann der L1-Cache in der CPU nicht besonders groß sein. Er bewegt sich in der Regel in Größenordnungen von 16 bis 64 KByte. Die Kunst besteht also darin, den schnellen kleinen Speicher so mit dem langsamen großen Arbeitsspeicher zu kombinieren, dass sich eine möglichst schnelle Gesamtlösung ergibt. Das Grundprinzip: Im Cache müssen die am häufigsten benötigten Informationen enthalten sein, damit möglichst wenige Zugriffe auf den langsamen Arbeitsspeicher erforderlich sind. Der Cache wird deshalb in Zeilen (Cache Lines) oder Sets eingeteilt. Erfolgt ein Lesezugriff auf einen Bereich im Arbeitsspeicher, dann füllt die CPU mittels schneller Burst-Zyklen eine komplette Cache-Zeile mit Informationen aus diesem Speicherblock auf. Die Wahrscheinlichkeit ist hoch, dass im Zuge des weiteren Programmflusses wiederholt auf diesen Speicherbereich zugegriffen werden muss.

Um den Set-Zeilen bestimmte Bereiche im Arbeitsspeicher zuordnen zu können, bedarf es eines Cache-Verzeichnisses. Jeder Eintrag im Cache besteht deshalb aus einem Valid-Bit (V-Bit), Tag- und Data-Feld. Das Bild "Cache Interna" zeigt den prinzipiellen Aufbau eines Caches.

Unsere tecChannel-CPU mit 64 KByte L1-Cache in 1024 Sets zu 64 Byte benötigt die sechs unteren Adressbits, um die 64 Bytes (2^6 = 64, A0 bis A5) eines Sets unterscheiden zu können. Für die 1024 Set-Zeilen muss sie 2^10 Adressbits (A6 bis A18) auswerten. Da die Set-Anzahl und die Cache-Line-Größe nicht verändert werden, muss sich der Cache diese beiden Teile der Adresse nicht merken. Es genügt, wenn sie während eines Zugriffs kurz anliegen. Allerdings benötigt er die höherwertigen Adressbits A19 bis A31 (das Tag), um festzustellen, welcher Block im Arbeitsspeicher gemeint ist. Dieses Tag muss er also unbedingt zusätzlich für jede Cache-Zeile speichern. Bild 6 zeigt die Auswertung einer 32-Bit-Adresse nach diesem Prinzip.

Als einfache Analogie betrachten wir einen Briefträger bei der Arbeit: Er trägt in einer Stadt (Arbeitsspeicher) die Post (Daten) aus. Dafür sortiert er die Post in seiner Tasche nach Straßen (Tag). In einer Straße holt er sich die hierfür bestimmte Post heraus und verteilt sie an die Häuser (Set-Zeile). Um in Mehrfamilienhäusern auch den richtigen Briefkasten zu erwischen, benötigt er noch den Familiennamen (Byte-Nummer).

Cache-Organisation

Der direkt abgebildete (direct mapped) Cache im Bild unten ist die einfachste Form. Jedem Set ist nur ein Cache-Eintrag zugeordnet. Dadurch deckt man im Arbeitsspeicher einen Block von x+n Sets ab. Bei unserer tecChannel-CPU sind das die zuvor definierten 64 KByte. Fordert nun ein Programm außerhalb dieses 64-KByte-Blocks Daten oder Befehle an, führt das zu einem Cache Miss. Wurde der Eintrag dagegen gefunden, spricht man von einem Cache Hit.

Der Direct Mapped Cache hat einen Nachteil: Ist zum Beispiel die Cache Line 0 (ab Adresse 0000) im Cache gespeichert, führt ein Zugriff auf den Speicher ab Adresse 65.537 oberhalb des 64-KByte-Blocks nicht zum Cache Miss. Es muss zudem der gesamte Block neu geladen werden, weil das Tag keine eindeutige Identifizierung der Speicherstelle erlaubt. Da im Tag nur ein Teil der Adresse gespeichert ist, kann er nur innerhalb eines geschlossenen Speicherblocks von 64 KByte einzelne Cache Lines unterscheiden.

Der Briefträger hat dieses Problem ebenfalls: Wenn er einen Abstecher in eine Seitenstraße macht, muss er den nach Straßen vorsortierten Briefstapel zurücklegen und den für die Seitenstraße herausholen.

Die Lösung des Problems sind teilassoziative Mehrweg-Caches. Auch dieser Cache-Typ speichert nur einen Teil der Adresse im Tag ab. Taucht ein Tag aber nochmals auf, erfolgt kein Komplettaustausch des Speicherblocks. Die Hardware merkt sich den Eintrag wieder in der gleichen Set-Zeile. Allerdings erfolgt die Speicherung in einer weiteren Ebene (Weg), sodass die erste Zeile nicht überschrieben werden muss. Die Ebenen- oder Wegauswahl erfolgt über den Set-Adressteil.

Mit einer Mehrwegsortierung arbeitet auch der Briefträger schneller: Er hat sich die Post nicht nur in Straßenzüge (Tag) unterteilt, sondern nochmals mit einem Gummiband hinsichtlich der Straßenseite gebündelt (Set-Weg). Bei einem Abstecher in die Seitenstrasse greift er sich nur das Briefbündel für eine Straßenseite. Dafür legt er nur eines der zwei Bündel der alten Straße weg - und zwar das von der anderen Straßenseite. Wenn er aus der Seitenstrasse zurückkommt, hat er immer noch das Bündel der Seite in der Hand, bei der er abgebogen ist.

Ein Mehrweg-Cache ist ein vervielfachter Direct Mapped Cache. Der Schaltungsaufwand für einen teilassoziativen 2-Wege-Cache ist demnach doppelt so hoch wie bei der einfachen Version. Bei einem 4-Wege-Cache vervierfacht er sich.

Irgendwann sind jedoch auch die Ebenen eines Mehrweg-Caches voll. Mittels zusätzlich gespeicherter LRU-Bits kann der Cache dann feststellen, welcher Eintrag in welcher Ebene am längsten nicht mehr benutzt wurde. Dieser Wegeintrag wird dann überschrieben.

Pipeline-Verfahren

Bis jetzt sind wir davon ausgegangen, dass die CPU die Befehle nach dem klassischen Von-Neumann-Prinzip nacheinander verarbeitet. Jeder Befehl wird innerhalb einer bestimmten Zeit (Taktzyklus) erledigt, dann ist der Nächste dran.

Wenn man der BIU erlaubt, schon Befehle aus dem Speicher zu holen während die CU gerade einen analysiert, hat man zwei Arbeitsschritte parallelisiert. Es sind also zwei Befehle gleichzeitig in Teilbearbeitung. Überträgt man das Prinzip auf alle beteiligten Funktionseinheiten, erhöht sich die Zahl der Teilbearbeitungen weiter. Dieses Pipeline-Prinzip verarbeitet aber die eingehenden Befehle und Daten immer noch Schritt für Schritt.

Im Bild ist zu erkennen, was eine Pipeline bringt. Durch die überlappende Bearbeitung mehrerer Befehle erledigt die Hardware in der gleichen Zeit mehr Kommandos als ohne Pipeline. Die ersten beiden Befehle lassen sich in unserem Beispiel in jeweils nur fünf Takten bearbeiten. Beim dritten Befehl haben wir einfach festgelegt, dass er zwei Taktzyklen in der Ausführungseinheit benötigen soll. Die Wirkung auf alle nachfolgenden Kommandos ist deutlich zu sehen, denn in dieser Zeit stockt der Ablauf in der gesamten Pipeline.

Das gezeigte Beispiel ist immer noch nahe am Idealfall. Sprungbefehle, die den nachfolgenden Programmcode in der Pipeline nicht als Ziel haben, sind nicht eingezeichnet. In diesem Fall müsste die Pipeline im ungünstigsten Fall vollständig (also alle Funktionseinheiten) neu geladen werden. Auch wenn ein nachfolgender Befehl das Ergebnis eines seiner noch in Bearbeitung befindlichen Vorgänger benötigt, stockt das Fließband bis dieser vollständig abgearbeitet ist.

Superskalare Architektur

Wenn schon eine Pipeline die Geschwindigkeit erhöht, geht es mit Zweien noch schneller. In Bild "Parallel ist schneller" ist im oberen Teil ein solcher Ansatz zu sehen. Auf diese Weise arbeitet beispielsweise der Intel Pentium. Um unnötige Probleme mit Abhängigkeiten zwischen den Befehlen zu minimieren, arbeitet die Intel-CPU allerdings nur bei Kommandos gleichzeitig mit beiden Pipelines, die gut zueinander passen.

Im "Pipeline in Funktion" (vorhergehende Seite) ist zu sehen was passiert, wenn ein Befehl mehr als einen Taktzyklus zur Ausführung benötigt. Wenn wir zwei ALUs verwenden würden, könnte die Eine an dem aufwendigen Befehl arbeiten. Selbst wenn ein Taktzyklus später wieder ein solch anspruchsvoller Befehl eintrifft, wäre die zweite ALU dafür noch frei. Die ersten drei Pipeline-Stufen müssten also nicht warten. Damit ist bereits ein superskalarer Prozessor beschrieben. Auch diese Idee ist nicht neu, denn sie wurde schon vor über 30 Jahren in dem Computer CDC 6000 von Control Data realisiert.

Das untere Beispiel in Bild "Parallel ist schneller" zeigt eine superskalare Architektur. Moderne PC-Prozessoren verwenden meistens eine Kombination aus beiden Techniken.

Sprungvorhersage

Die Abhängigkeiten der Befehle untereinander sowie Sprungbefehle machen den Pipelines und superskalaren Architekturen zu schaffen. Je mehr parallel vorweggreifend erledigt wird, desto mehr Arbeit ist beispielsweise bei einem Sprungbefehl in ein anderes Programmsegment nachzuholen. Die ALUs müssen dann warten, bis sich die neuen Befehle durch die lange Pipeline gequält haben.

Eine Möglichkeit das Problem zu mindern, ist das Stoppen der Pipeline (Stalling) bis die Ausführungseinheit weiß, wie es weitergeht. Sinnvoller ist der Versuch einer Sprungvorhersage, bei der die Hardware den Programmcode auf Verzweigungen prüft und das wahrscheinlichste Ziel anvisiert. Geht die Sache schief, kommt es eben trotzdem zur Wartepause.

Dynamische Sprungvorhersage

Die Sprungvorhersage kann auf einfachen Regeln basieren: Beispielsweise ist es bei einem Sprungbefehl gegen den Befehlsstrom sehr wahrscheinlich, dass er mehrheitlich tatsächlich ausgeführt wird. Diese Annahme basiert auf der Feststellung, dass Rückwärtssprünge im Programmcode oft am Ende von Schleifen stehen. Und Programmschleifen werden in der Regel mehr als nur einmal durchlaufen. Bei Vorwärtssprüngen ist eine derart einfache Vorhersage kaum noch zu treffen. Hier kann man nur von der statistischen Erkenntnis ausgehen, dass die meisten bedingten Vorwärtssprünge nicht ausgeführt werden.

Sinnvoller ist deshalb eine dynamische Sprungvorhersage ohne feste Regel. Mittels einer BHT versucht die Hardware, eine begrenzte Zahl von bedingten Sprungbefehlen zu protokollieren. Kommt es zur Sprungausführung, werden dem Befehl in der BHT entsprechende Hinweis-Bits zugeordnet. Diese Bits wertet die Hardware bei späteren bedingten Sprungbefehlen aus, um auf dieser Basis eine Vorhersage zu treffen. Ähnlich wie beim Cache gilt es hier Probleme mit der Assoziativ ität des Inhalts zu berücksichtigen. Deshalb ist auch bei BHTs eine Mehrweg-Struktur üblich. Bei einer ausreichend großen Tabellengröße und Assoziation erzielt man mit BHTs sehr gute Ergebnisse.