- DBIS Group
Transcription
- DBIS Group
Westfälische Wilhelms-Universität Münster Institut für Wirtschaftsinformatik Lehrstuhl für Informatik Bachelor – Arbeit zum Thema Mikroprozessorarchitekturkonzepte im Fach: Informatik Themensteller: Prof. Dr. Gottfried Vossen Betreuer: Prof. Dr. Gottfried Vossen Ausgabetermin: 18.02.2002 Abgabetermin: 03.04.2002 vorgelegt von Victor Pankratius [email protected] 2 Abstract Ziel der Arbeit ist eine Darstellung von allgemeinen Architekturkonzepten, die in Mikroprozessoren zu finden sind. Dabei wird von technischen Details abstrahiert und das Zusammenwirken von Teilkomponenten eines Mikroprozessors anhand von Modellen erklärt. Ausgehend von den grundlegenden Prinzipien einer von-NeumannCPU werden Architekturerweiterungen vorgestellt. Nach einer Darstellung von CISCund RISC-Architekturen werden Konzepte der Speicherverwaltung und deren Einfluss auf Mikroprozessorarchitekturen untersucht. Anschließend wird vorgestellt, wie mit Hilfe von Pipelines und Branch Prediction die Performance verbessert werden kann. Die Betrachtung wird bei superskalaren Architekturen fortgesetzt, die Parallelitätskonzepte bei der Verarbeitung von Befehlen nutzen. Die im Zusammenhang mit Pipelines beschriebenen Konzepte werden bei superskalaren Pipelines wieder aufgegriffen und erweitert. Es werden out-of-order-Architekturen vorgestellt, die Befehle außerhalb der Programmreihenfolge verarbeiten können. Anhand von VLIW/EPIC- und Vektorarchitekturen werden weitere Möglichkeiten zur Nutzung von Parallelität beschrieben. Danach wird auf Mikroprozessorarchitekturen eingegangen, die für bestimmte Anwendungsgebiete optimiert sind. Behandelt werden System-on-a-Chip-Ansätze wie Mikrocontroller und speziell für Signalverarbeitung optimierte DSP-Prozessoren. Nach einigen Beispielen von aktuellen Mikroprozessorarchitekturen werden abschließend Schnittstellen zu Softwaresystemen und aktuelle Forschungstrends für zukünftige Architekturen gezeigt. 3 Inhaltsverzeichnis 1 Einführung ...................................................................................5 1.1 Grundbegriffe und Aufbau eines Computers ......................................8 1.2 Aufbau einer CPU und die Prinzipien von Neumanns .......................... 12 1.3 Historische Entwicklung ........................................................... 15 1.4 Designprozess von Mikroprozessoren............................................. 22 1.5 Klassifizierungsschemata .......................................................... 26 1.5.1 Klassifikation nach Flynn ................................................... 26 1.5.2 Erlanger Klassifikationssystem ............................................. 27 1.5.3 Klassifikation nach dem internen Speichermodell....................... 30 1.5.4 Klassifikation nach Befehlsformaten ...................................... 31 1.5.5 Fazit zur Klassifikation...................................................... 32 2 Mikroprozessorarchitektur ............................................................. 35 2.1 ISA-Varianten........................................................................ 37 2.1.1 CISC............................................................................ 37 2.1.2 RISC............................................................................ 42 2.1.3 Zusammenwachsen von CISC und RISC .................................... 46 2.2 Speicher.............................................................................. 47 2.2.1 Cache-Speicher .............................................................. 47 2.2.2 Paging und Segmentierung ................................................. 52 2.2.3 Translation Lookaside Buffer............................................... 54 2.2.4 Virtueller Speicher .......................................................... 54 3 Maßnahmen zur Performancesteigerung............................................. 56 3.1 Pipelines ............................................................................. 56 3.2 Branch Prediction und Spekulation............................................... 58 3.3 Superskalare Architekturen ....................................................... 61 3.3.1 Superskalare Pipelines ...................................................... 64 3.3.2 Out-of-order-Architekturen ................................................ 68 3.3.3 VLIW/EPIC-Architekturen ................................................... 71 3.3.4 Vektor-Architekturen........................................................ 73 4 Spezielle Architekturen ................................................................. 76 4.1 Mikrocontroller...................................................................... 76 4.2 DSP-Prozessoren .................................................................... 78 4 5 Aktuelle Mikroprozessoren ............................................................. 82 6 Softwaresysteme und Forschungstrends ............................................. 87 6.1 Compiler und Betriebssysteme .................................................... 87 6.2 Ausblick .............................................................................. 90 Anhang: Bilder von Mikroprozessoren .................................................. 95 Bibliographie ...............................................................................101 5 1 Einführung Zweifellos hat in der Geschichte der Menschheit keine Maschine die Welt so verändert wie der Computer. Um so erstaunlicher ist es, dass sich ein großer Teil der Entwicklung des Computers in den letzten 60 Jahren vollzogen hat. Das ist sowohl auf die technologische Entwicklung zurückzuführen als auch auf die Entwicklung der zugrundeliegenden theoretischen Konzepte. Ein Computer oder Rechner wird als „universell einsetzbares Gerät zur automatischen Verarbeitung von Daten“ definiert [SDI91]. Realisiert werden Computer heutzutage mit Hilfe binärer Digitaltechnik. Dass es Computer in fast allen Lebensbereichen gibt, kann einerseits durch die geringen Fertigungskosten und andererseits durch die universelle Einsetzbarkeit begründet werden. Universalität bedeutet, dass Computer frei programmierbar sind und ihre spezielle Hardware-Struktur nicht für jede Aufgabe neu angepasst werden muss. Die Untersuchung der allgemeinen Bauprinzipien und der Organisation von Rechnern ist daher ein wichtiger Aspekt. Computer sind modular aufgebaut, wobei der Kern von Mikroprozessoren gebildet wird. Durch die Entwicklung der Halbleitertechnologie und der Fertigungsprozesse ist auch eine Realisierung komplexer Mikroprozessoren möglich geworden. Sie bestehen mittlerweile aus sehr vielen Teilmodulen und Komponenten. Die Art und Weise, wie die Teilmodule organisiert sind, wirkt sich in entscheidendem Maße auf Performance und Fertigungskosten aus. Die vorliegende Arbeit soll den generellen Bebauungsplan, die „Architektur“ von Mikroprozessoren auf konzeptioneller Ebene anhand von Modellen analysieren und allgemeine Prinzipien aufzeigen. Dabei geht es z.B. um die Art und Anzahl der Register, den Befehlssatz, das Speichermodell oder die benutzten Methoden, um Daten parallel zu verarbeiten. Die beschriebene universelle Einsetzbarkeit der Computer ist größtenteils auf die Architektur des Mikroprozessors zurückzuführen. Diese kann für bestimmte Einsatzbereiche optimiert werden, was auch an der heutigen Vielfalt an verschiedenen Mikroprozessortypen zu sehen ist. Mikroprozessoren findet man mittlerweile fast überall: in PCs, Handys, Fernsehern, Bankautomaten, Autos, Waschmaschinen, Uhren, Elektrorasierern, Mikrowellen oder Aufzügen. Im Verlauf 6 der Arbeit soll auch deutlich gemacht werden, welche Mikroprozessor- architekturkonzepte in welchen Anwendungssituationen bevorzugt zum Einsatz kommen. Die Arbeit wird zunächst einige wichtige Begriffe definieren, den groben Aufbau eines Computers skizzieren und die Mikroprozessoren im Gesamtzusammenhang einordnen. Es werden die Prinzipien von Neumanns erläutert und deren Beziehung zur universellen Einsetzbarkeit von Computern. Nach einem kurzen historischen Rückblick folgt eine Darstellung des Designprozesses von Mikroprozessoren. Die Einführung endet mit der Beschreibung einiger wichtiger Klassifikationsmodelle für Mikroprozessoren. Im zweiten Kapitel werden zunächst Basiskonzepte von Mikroprozessorarchitekturen anhand einer Prozessorarchitektur aus den 90er Jahren vorgestellt. In diesem Zusammenhang werden Befehlssatzarchitekturen beschrieben. Danach wird die Auswirkung von Konzepten bei der Speicherverwaltung auf die Prozessorarchitektur untersucht. Das dritte Kapitel befasst sich mit grundlegenden Konzepten, die für Performancesteigerungen benutzt werden. Obwohl im weiteren Verlauf der Arbeit Maßnahmen zur Performancesteigerung untersucht werden, werden die Methoden zur Messung der Performance nicht näher erläutert. Dazu sei auf [HePa96] und [BeiHag01] verwiesen. Im Hinblick auf zeitliche Parallelität wird zunächst auf Pipelining eingegangen. Ergänzend dazu werden Sprungvorhersagetechniken betrachtet, die einige Nachteile von Pipelines ausgleichen sollen. Anschließend wird analysiert, wie superskalare Architekturen räumliche Parallelität zur Ausführung mehrerer Befehle in einem Takt ausnutzen. Hierfür werden die Pipelining-Konzepte bei superskalaren Pipelines wieder aufgegriffen und erweitert. Es werden außerdem out-of-order-Architekturen beschrieben, die Befehle außerhalb der Programmreihenfolge ausführen können. Als weitere Möglichkeiten werden VLIW/EPIC-Architekturen Verarbeitungslogik in präsentiert, Softwaresysteme die ausgliedern. einen großen Schließlich Teil wird der noch Parallelität auf Datenebene behandelt, die von Vektor-Architekturen genutzt wird. Nach der Beschreibung von universell einsetzbaren Mikroprozessor- architekturen, geht das vierte Kapitel auf Special-Purpose-Architekturen ein, die für bestimmte Einsatzgebiete optimiert sind. Dabei werden Mikrocontroller, die ein 7 komplettes System auf einem Chip implementieren und DSP-Prozessoren, die auf Signalverarbeitung optimiert sind, vorgestellt. Das fünfte Kapitel zeigt beispielhaft, wie die besprochenen Konzepte in aktuellen Mikroprozessorarchitekturen verwendet werden. Das letzte Kapitel geht auf Schnittstellen zu Softwaresystemen ein und erläutert Wechselwirkungen zwischen Mikroprozessorarchitektur und Softwaresystemen. Abschließend werden Konzepte vorgestellt, an denen zur Zeit geforscht wird und die möglicherweise einen Einfluss auf zukünftige Mikroprozessorarchitekturen haben werden. 8 1.1 Grundbegriffe und Aufbau eines Computers Programmspeicher Eingabesystem Zentrale Verarbeitungseinheit Ausgabesystem Arbeitsspeicher Abb. 1: Grundstruktur einer programmgesteuerten Datenverarbeitungsanlage (Computer) nach [BeiHag01] In Abb. 1 ist die Grundstruktur eines Computers (=Rechner) dargestellt. Durch das Eingabesystem werden der zentralen Verarbeitungseinheit (Central Processing Unit, CPU) Daten in digitaler Form zugeführt. Die CPU wendet genau definierte Regeln (Algorithmen), die in Form eines Programms im Programmspeicher stehen, auf die Daten an. Der Arbeitsspeicher dient zur Zwischenspeicherung von Ergebnissen, bevor die verarbeiteten Daten an das Ausgabesystem weitergegeben werden. Durch den Austausch des Programms im Programmspeicher kann der Computer ohne Veränderung der Hardware für andere Aufgaben benutzt werden. Häufig werden Daten und Programme in demselben Speicher abgelegt (vgl. vonNeumann-Architektur, Kap. 1.2). In einigen Anwendungsgebieten werden Arbeitsbzw. Datenspeicher und Programmspeicher allerdings getrennt, was als HarvardArchitektur bezeichnet wird (vgl. spezielle Architekturen, Kap. 4). Ein Prozessor wird in [Clausing00] wie folgt definiert: „Jedes Objekt, dass in steuerbarer Weise unterschiedliche Zustände annehmen kann. Das bedeutet, dass es einen wohldefinierten Startzustand gibt, sowie Methoden, davon ausgehend den Prozessor in andere Zustände zu versetzen. Ein Prozessor durchläuft sequenziell (d.h. in diskreten Zeitschritten) eine Folge von Zuständen, bis er einen Endzustand erreicht hat. Ein solcher Ablauf mit definiertem Anfang und Ende ist ein Prozess 9 (=Berechnung). Ein Prozessor heißt universell, wenn er die Operationen einer Turingmaschine [..] emulieren kann.“ (vgl. auch [LewPap98]). Eine auf einem einzigen Chip untergebrachte CPU eines Computers wird Mikroprozessor genannt. Es gibt sowohl für den kompletten Rechner, also auch für Mikroprozessoren allgemeine Bauprinzipien. Eine Rechnerarchitektur beschreibt die Komponenten eines Rechners, deren Struktur und Zusammenwirken, den Aufbau von Befehlen und den Ablauf von Operationen. Die Sicht der Bauprinzipien lässt sich auch auf Mikroprozessoren einschränken. Eine Mikroprozessorarchitektur abstrahiert vom genauen physikalischen Aufbau von Mikroprozessoren und definiert deren wesentlichen Bestandteile und Beziehungen zueinander durch Bildung eines Modells. Ziel ist das Verständnis der Organisation und Funktionsweise der Teileinheiten und des Ganzen. Dabei verzichtet man auf genaue quantitative Aussagen. Ein Programmiermodell ist ein Teil der Prozessorarchitektur und stellt das Prozessormodell aus Sicht des Assemblerprogrammierers dar, wobei von den Einzelheiten der Ausführung abstrahiert wird. Die allgemeinen Ziele von Mikroprozessorarchitekturen werden von [BeiHag01] darin gesehen, dass Programme möglichst performant ausgeführt werden, ohne das Programmierparadigma der sequentiellen Bearbeitung des Befehlsstroms gegenüber Anwendungsprogrammierern wesentlich zu verändern. Die interne (ggf. parallele) Abarbeitung soll gegenüber der Befehlssequenz transparent sein. Bei der Gestaltung von Rechnerarchitekturen unterscheiden [TanGo99] zwischen verschiedenen Schichten, wobei höhere Schichten immer stärker von der untersten Ebene abstrahieren und jede Schicht nur mit der jeweils darunter oder darüber liegenden kommuniziert. Die Abstraktion soll die Komplexität des Gesamtsystems reduzieren und beherrschbar machen. 10 Ebene 5 Problemorientierte Sprache Übersetzung (Compiler) Ebene der Assemblersprache Ebene 4 Übersetzung (Assembler) Ebene 3 Ebene der Betriebssystemmaschine Teilinterpretation (Betriebssystem) ISA (Instruction Set Architecture) Ebene 2 Interpretation (Mikroprogramm) oder direkte Ausführung Mikroarchitektur Ebene 1 Hardware Ebene 0 Digitale Logik Abb. 2: Struktur eines Computers mit sechs Ebenen (nach [TanGo99]) Auf Ebene 0 befindet sich die Hardware der Maschine. Hier werden logische Gatter analysiert, die binäre Eingangssignale in binäre Ausgangssignale transformieren. Mit Hilfe von Gattern kann man auch Speicher realisieren, die 1 Bit speichern können. Mehrere 1-Bit-Speicher werden zu Registern zusammengefasst. Von der Art und Weise, wie Gatter genau mit Hilfe der Elektrotechnik realisiert werden (z.B. mit Transistoren), wird abstrahiert. Auf der Ebene 1 werden unter Benutzung der Ebene 0 Komponenten auf einem höheren Aggregationsniveau betrachtet, wie z.B. Gruppen von Registern als lokale Speicher, Pipelines zur Befehlsdekodierung (vgl. Kap. 3.1) oder eine ALU (Arithmetic Logic Unit). Eine ALU ist mit Registern verbunden und kann arithmetische Operationen auf Daten ausführen. Dabei müssen die Daten über einen Bus aus den Registern in die ALU geholt werden und wieder zurück in die Register geschrieben werden. Dieser als Datenwegzyklus bekannte Prozess ist typisch für jeden Mikroprozessor. Die Steuerung des Datenwegzyklus kann fest in der Hardware verdrahtet sein oder von einem sogenannten Mikroprogramm gesteuert werden. Die Ebene 2 (ISA-Ebene) legt Anzahl und Art der Instruktionen fest, die ein Computer versteht. Diese Ebene stellt einen Teil des Programmiermodells dar, welches zusätzlich zu allen ausführbaren Befehlen auch alle für den Programmierer 11 sichtbaren Register umfasst. Wie man bei CISC und RISC sehen wird (Kap. 2.1.1 und 2.1.2), hat diese Ebene einen entscheidenden Einfluss auf die Mikroprozessorarchitektur. Das auf Ebene 3 vorhandene Betriebssystem ist ein Programm, das die Nutzung des Rechners vereinfacht und komfortabler macht, verglichen mit der direkten Nutzung der Hardware. Es kontrolliert die Hardware-Rressourcen und deren Aufteilung zwischen verschiedenen Benutzern. Das Betriebssystem abstrahiert so stark von der Hardware, dass sich Benutzer nicht um Details bei der Ausführung der Programme kümmern müssen (vgl. auch Kap. 6.1). Ab der Ebene 4 gelangt man von der Systemebene in den Bereich der Anwendungsprogramme. Bis jetzt hat man in den darunter liegenden Ebenen die Programme als Bitfolgen aufgefasst. Die Bitfolgen werden hier zu Symbolen (=Befehle) eines Assemblerprogramms zusammengefasst. Durch Übersetzung kann das Assemblerprogramm wieder in Bitfolgen für die darunter liegende Ebene transformiert werden. Die Ebene 5 betrachtet als letzte Ebene sogenannte Hochsprachen (z.B. C++, Pascal), die für Anwendungsprogrammierer ausgelegt sind. In Hochsprachen gibt es komplexere Sprachkonstrukte und Befehle als in Assemblersprachen. Mit Hilfe eines Compilers (Kap. 6.1) werden Programme von Hochsprachen in Programme für die Ebene 4 übersetzt. Jede einzelne Schicht sieht nur eine einzige darunter liegende Schicht, die die Funktionalität aller weiter unten gelegenen Schichten enthält. Deswegen kann eine Schicht als virtuelle Maschine bezüglich der nächsthöheren Schicht aufgefasst werden. Die Konsequenzen solch einer Abstraktion sind weitreichend: Hardware kann für eine obere Schicht von der darunter liegenden Schicht simuliert werden, so dass der Unterschied zwischen Hardware und Software verschwimmt. Daher ist es unwichtig, ob ein Befehl von Hardware oder Software ausgeführt wird, was man auch an der Mikroprogrammierung auf Ebene 1 sieht. Tanenbaum/Goodman bezeichnen sogar Hardware und Software als logisch äquivalent [TanGo99]. Die vorliegende Arbeit wird sich überwiegend mit Ebene 1 und 2 befassen. Es wird nicht auf die Einzelheiten der Realisierung von Mikroprozessoren mittels Boolescher Logik eingegangen. Die verschiedenen Elemente eines Mikroprozessors wie Register, ALU oder Pipelines werden als gegeben hingenommen und nur hinsichtlich Zusammenwirken und Organisation analysiert. Details zur Realisierung 12 von Mikroprozessoren, Schaltnetzen und Schaltwerken finden sich in [HePa97], [ObVossen00], [TanGo99]. 1.2 Aufbau einer CPU und die Prinzipien von Neumanns Das obige Kapitel hat erläutert, welche Rolle eine CPU für ein Computersystem spielt. Dieser Abschnitt soll den grundsätzlichen Aufbau einer CPU nach von Neumanns Konzepten näher beschreiben. Die Ideen von Neumanns zum Aufbau eines Computers und einer CPU, die schon 1946 vorgestellt wurden, beschreiben einen Universalrechner [BuGoNeu46]. CPU Speicher I/O Datenbus Steuerbus Adressbus Abbildung 3: Aufbau eines von-Neumann-Rechners Der Computer nach von Neumann hat eine CPU, Speicher und Ein-/AusgabeGeräte (I/O). Die obige Abbildung zeigt, dass die CPU über ein Bussystem mit den restlichen Komponenten verbunden ist. Das Bussystem ist in Daten-, Steuer-, und Adressbus unterteilt. Der Datenbus überträgt nur Daten, der Steuerbus nur Steuersignale (z.B. welcher Befehl ausgeführt werden soll) und der Adressbus enthält die Adresse, auf die als nächstes zugegriffen wird. Alle Daten, Adressen und Befehle werden binär kodiert. Die Auswahl eines Ein-/Ausgabe-Geräts erfolgt auch über den Adressbus. Der Speicher besteht aus Speicherzellen fester Größe (1 Speicherwort), die alle über eine eindeutige Adresse angesprochen werden können. Programme und Daten werden im selben Speicher abgelegt. Dadurch, dass Programme wie Daten behandelt werden, können sich Programme zur Laufzeit selbst verändern. Der Inhalt eines Speicherwortes kann Daten, Befehle oder Adressen repräsentieren, wobei die Interpretation des Speicherinhalts vom Kontext abhängt. Der Rechner 13 kann durch Austauschen des Programms zur Lösung anderer Aufgaben verwendet werden. CPU Steuerwerk Rechenwerk Register IR op2 op1 PC ALU Bus-Interface MAR MBR Ergebnis Datenbus Steuerbus Adressbus Abbildung 4: Organisation einer CPU nach von Neumann In Abb. 4 wird die Organisation der CPU nach von Neumann skizziert. Sie besteht aus einem Steuerwerk, einem Rechenwerk und einem Bus-Interface. Das Rechenwerk enthält die schon in Kapitel 1.1 erwähnte Arithmetic-LogicUnit (ALU), die arithmetische Befehle (z.B. Addition, Multiplikation) und logische Befehle (z.B. and, or, not, shift) auf den Daten ausführen kann. Welche Operation die ALU ausführen soll, bestimmt das Steuerwerk über einen internen Steuerbus. Die Register sind schnelle Speicher, die auf dem gleichen Chip untergebracht sind wie die CPU. Das Steuerwerk (Control Unit, CU) steuert den Gesamtablauf des Systems. Dabei wird immer genau ein Befehl zu einem Zeitpunkt ausgeführt. Das Instruktions- bzw. Befehls-Register (IR) enthält die Bitfolge des Befehls, der ausgeführt werden soll. Die Bitfolge besteht aus einem Adressteil und einem Operationsteil (OpCode). Bei diesen sog. Ein-Adress-Befehlen (vgl. Kap. 1.5.4) gibt der Operationsteil an, was zu tun ist und der Adress-Teil sagt, wo die zugehörigen Operanden zu finden sind. Der Dekodierer erkennt den Befehl im Operationsteil und führt ihn aus, indem er entsprechende Signale auf den Steuerbus setzt. Es gibt zumindest arithmetische Befehle, logische Befehle, Transportbefehle zum Speicher 14 oder zur Ein-/Ausgabe, bedingte Sprünge und sonstige Befehle wie unterbrechen oder warten. Für alle Befehle gibt es auch unterschiedliche Adressierungsarten (vgl. Kap. 2.1.1), wie z.B. indirekte Adressierung, bei der man den Inhalt einer referenzierten Speicherzelle als Adresse einer anderen Speicherzelle interpretiert, die den gewünschten Wert enthält. Das Program-Counter-Register (PC) enthält die Adresse des Befehls, der als nächstes ausgeführt werden soll. Da sequenzielle Befehle eines Programms im Speicher nacheinander stehen, erhöht das Steuerwerk nach Ausführung eines Befehls den PC um 1. Sollen Sprünge im Programm ausgeführt werden, dann wird die Zieladresse des Sprungs in den PC geschrieben. Die CPU kommuniziert mit der Außenwelt über das Bus-Interface, welches das Memory-Address-Register (MAR) und Memory-Buffer-Register (MBR) enthält. In das MAR wird die Adresse geschrieben, auf die als nächstes zugegriffen wird. Das hat zur Folge, dass dieser Wert auf den Adressbus gelegt wird. Nachdem das Steuerwerk die Steuersignale auf den Steuerbus gelegt hat, erhält die CPU die Daten im MBR. Von da aus können sie in die internen Register gelangen. Bei der Ausführung eines Befehls wird von der CPU immer derselbe Zyklus durchlaufen: Befehl holen (fetch), Befehl dekodieren (decode), Befehl ausführen (execute). In Pseudocode formuliert passiert dabei im Wesentlichen folgendes: MAR := PC; MBR := Inhalt_von_Adresse(MAR); IR := MBR; dekodiere IR; fetch decode falls kein Sprungbefehl dann {stelle Operanden bereit; PC := PC + 1; } execute sonst{PC := Sprungzieladresse;} Dieser Ablauf ist grob vereinfacht, da z.B. der Operand auch indirekt adressiert werden könnte und dann noch eine Adressberechnung durchzuführen wäre. 15 Die Architektur der CPU nach von Neumann ist eine skalare Architektur, die nicht mehrere Befehle in einem Takt ausführen kann. Die Phasen fetch, decode, execute werden nacheinander ausgeführt, wobei die CPU zu einer bestimmten Zeit immer nur eine der Phasen ausführt. Da die Zugriffszeiten auf den Speicher wesentlich länger sind als die Ausführungszeit eines Befehls, entsteht die Situation, dass die CPU häufig wartet, bis die Befehle und deren Operanden aus dem Speicher gelesen wurden. Die Dauer der Phasen ist dadurch sehr unterschiedlich. Die Kommunikation zwischen CPU und Speicher wird zum Engpass und als der „von Neumannsche Flaschenhals“ bezeichnet. Details zur von-Neumannn-Architektur findet man in [SDI99], [ObVossen00], [TanGo99]. Im Verlauf der Arbeit werden andere CPU-Architekturen vorgestellt, die ausgehend von den Konzepten von Neumanns die verschiedenen Nachteile ausgleichen sollen. Beispielsweise werden schnelle Pufferspeicher (Cache-Speicher) beim Zugriff auf den Hauptspeicher verwendet, um die Zugriffszeit zu reduzieren (vgl. Kap. 2.2.1). Andere Optimierungsmöglichkeiten setzen beim fetch-decodeexecute-Zyklus an, indem mit Hilfe von Pipelines verhindert werden soll, dass Teile der CPU nicht beschäftigt sind (vgl. Kap. 3.1). Durch Sprungvorhersage versucht man, den Kontrollfluß im Programm spekulativ zu berechnen, damit die Bereitstellung der in Zukunft benötigten Daten und Befehle frühzeitig beginnen kann (vgl. Kap. 3.2). Schließlich gibt es noch die Möglichkeit, Befehle parallel mit mehreren ALUs zu bearbeiten (superskalare Architekturen, vgl. Kap. 3.3). Rückblickend kann man sagen, dass die Entwicklung der Mikroprozessorarchitektur evolutionär stattgefunden hat. Das nächste Kapitel erläutert die Entstehungsgeschichte. 1.3 Historische Entwicklung Bevor auf die historische Entwicklung von Mikroprozessoren eingegangen wird, folgt noch eine kurze geschichtliche Zusammenfassung der Zeit bis zum Mikroprozessor, um zu zeigen, dass verschiedene Konzepte nicht erst mit der Erfindung des Mikroprozessors entstanden sind, sondern schon lange vorher bekannt waren. Gleichzeitig soll ein Bild vermittelt werden, welche weitreichenden 16 Auswirkungen der Einsatz neuer Technologien (Halbleitertechnologie und Miniaturisierung) auf die Architektur eines Prozessors hat. Schon lange Zeit vor dem Mikroprozessor verwendeten die Menschen Hilfsmittel, um Berechnungen zu beschleunigen. Die Babylonier benutzten bereits 3000 v. Chr. den Abakus. Im Laufe der Geschichte wurden Rechenmaschinen auf mechanischer Basis entwickelt. Pascal baute 1645 eine einfache mechanische Addiermaschine für den Einsatz in der Finanzverwaltung. Die Rechenmaschine von Leibniz konnte 1672 auch Multiplikationen und Divisionen durchführen. Durch die Entwicklung der Naturwissenschaften entstand im 18. und 19. Jahrhundert ein hoher Bedarf an durchzuführenden Berechnungen für mathematische Tabellenwerke. Diese wurden für die Navigation in der Schifffahrt oder für Zinsberechnungen benutzt. Die hohe Fehlerrate, die durch die manuelle Berechnung der Zahlen zustande kam, veranlasste Charles Babbage im Jahre 1823 die Difference Engine zur Berechnung von Tabellen zu konzipieren, die aber wegen der hohen mechanischen Komplexität nicht gebaut wurde. Im Jahre 1871 hatte Babbage das Konzept der Analytical Engine fertig, die ein Vorläufer des heutigen Universalrechners ist. Babbage hatte Speicher, Rechenwerk (ALU), Eingabewerk und Ausgabewerk (I/O) getrennt und war in der Lage, durch Austausch des Programms seine Maschine für andere Aufgaben zu benutzen. Die Programme konnten Schleifen und bedingte Sprünge durchführen. Letzteres führt zur interessanten Feststellung, dass die Analytical Engine universeller war (was die Berechenbarkeit angeht) als viele Rechner in den 40er Jahren, die keine bedingten Sprünge ausführen konnten. Leider wurde die Analytical Engine aus finanziellen und technischen Gründen nicht gebaut. Die Ära mechanischer Rechner endet etwa 1936 mit Konrad Zuses Z1. Danach wurden Computer auf elektromechanischer Basis mit Relais oder elektrotechnischer Basis mit Hilfe von Vakuumröhren realisiert. Viele davon waren nicht programmierbar und damit nicht universell, da die Programme fest verdrahtet waren. Ein Beispiel dafür war der 1943 von Turing gebaute Colossus für die Entschlüsselung des deutschen Funkverkehrs im zweiten Weltkrieg. Außerdem waren die Computer unglaublich groß und hatten häufig ein Gesamtgewicht im zweistelligen Tonnenbereich. Im Laufe der Zeit erkannte man auch, dass man durch Benutzung des binären statt des dezimalen Zahlensystems die Komplexität des Aufbaus von Computern erheblich reduzieren konnte. Weitere Details zur geschichtlichen Entwicklung finden sich in [Märtin01]. 17 Die nächste Computergeneration sollte durch die Erfindung des Transistors bei den Bell Labs von Bardeen, Brattain, Shockley im Jahre 1947 drastisch verändert werden. Der Transistor, der durch Halbleitertechnik realisiert und für den Bau logischer Gatter verwendet wurde, arbeitete zuverlässiger als Röhren, war kleiner, billiger herzustellen und benötigte weniger Energie. Grundstoffe für die Fertigung von Transistoren sind Silizium und Germanium. Im Jahre 1957 gründeten Moore und Noyce die Firma Fairchild Semiconductor, bei der im Jahre 1959 der erste integrierte Schaltkreis (integrated circuits, IC) entwickelt wurde. Auf einem einzigen IC wird ein kompletter Schaltkreis mit vielen Transistoren mit Hilfe photolithographischer Verfahren „gedruckt“ und in ein kleines Keramikgehäuse „verpackt“ (vgl. [BeiHag01]). An einem IC gibt es Pins, über die er mit der Außenwelt kommunizieren kann. Die Pins leiten elektrische Signale aus dem IC heraus oder in den IC hinein. 1968 gründeten Moore und Noyce die Firma Intel, die zunächst Speicher auf Halbleiterbasis fertigte. Der Mikroprozessor wurde geboren, als die japanische Firma Busicom an Intel den Auftrag erteilte, zehn Spezialchips für Tischrechner zu fertigen. Von Intel wurde stattdessen ein UniversalchipProzessor mit gleicher Funktionalität entwickelt. Die erste CPU der Welt wurde im Jahre 1971 fertiggestellt. Die Intel 4004 CPU hatte eine Verarbeitungsbreite von 4 Bit (d.h. Datenbus und Register waren 4 Bit breit), eine Taktrate von 0,1 MHz und bestand aus 2300 Transistoren. Dazu gab es einen 320-Bit-RAM-Speicher (Intel 4002), 2K-ROM (Intel 4001) und ein 10-Bit-I/OShiftregister (Intel 4003). Für den Bau eines Mikrocomputers (= ein Rechner, bei dem Mikroprozessoren zum Einsatz kommen) waren vier Bausteine von Intel nötig. Die Firma Texas Instruments hatte einen Chip, den TMS1000 entwickelt, der die komplette Funktionalität eines Mikrocomputers auf einem einzigen Chip vereinte und schuf damit den ersten Mikrocontroller (Micro Controller, MC). Dieser wurde anschließend in Taschenrechnern eingesetzt. Mikrocontroller sind vollständige Mikrocomputersysteme auf einem Chip. Sie vereinen neben der CPU den Speicher und die Peripheriekomponenten. Das Ziel ist nicht eine hohe Leistung bei der Verarbeitung, sondern die Integration der einzelnen Komponenten und die geringen Kosten des MC, weswegen sie auch heutzutage überwiegend in Embedded Systems zu finden sind. Auf Mikrokontroller und deren Architektur wird in Kap. 4.1 genauer eingegangen. 18 Die Entwicklung des Mikroprozessors ging rasant weiter. Moore formulierte 1965 einen Zusammenhang für die nächsten 10 Jahre, der anscheinend immer noch anhält und als „Moores Gesetz“ bekannt ist. Danach verdoppelt sich die Anzahl der Transistoren pro Chip etwa alle 2 Jahre. Die immer stärkere Verdichtung von Bauteilen ist heutzutage auf Basis der Very-Large-Scale-Integration-Technologie (VLSI) möglich [Kropf95]. Beispielsweise hat ein Pentium IV-Prozessor 42 Mio. Transistoren. Anz. Transistoren 100.000.000 Pentium IV Pentium III Pentium II 10.000.000 Pentium 486 1.000.000 386 286 100.000 8085 10.000 8080 8008 4004 1970 1000 1975 1980 1985 1990 1995 2000 Quelle: Intel 2002 Abbildung 5: Moores Gesetz Im Jahre 1979 wurde von Intel ein anderer Typ eines spezialisierten Mikroprozessors entwickelt, der Intel 2920. Er ist ein sogenannter digitaler Signalprozessor (DSP) und wird bei der Signalverarbeitung zur sehr schnellen Verarbeitung von mathematischen Befehlen eingesetzt. DSP-Prozessoren werden im Kap. 4.2 ausführlicher beschrieben. Erwähnenswert ist auch ein anderer Typ eines Mikroprozessors, nämlich der 1983 erstmals von Inmos entwickelte Transputer. Er war für den Aufbau von Parallelrechnern gedacht und enthielt auf einem Chip eine CPU, RAM und Kommunikationsschnittstellen zur Kopplung an mehrere Transputer [ObVossen00]). Diese Architektur hat sich allerdings nicht durchgesetzt. (vgl. 19 Die Entwicklung der wesentlichen Typen von Mikroprozessoren geschah größtenteils evolutionär, wobei sich im Laufe der Zeit bestimmte Trends entwickelten. Dabei haben sich zwei wesentliche Prinzipien herauskristallisiert: CISC (Complex Instruction Set Computer) und RISC (Reduced Instruction Set Computer). Ein CISC-Prozessor wird mit umfangreicheren und komplexeren Maschinenbefehlssätzen (Instruction Set Architecture) ausgestattet, die intern noch durch Mikroprogramme interpretiert werden. RISC-Prozessoren verzichten dagegen auf Mikroprogrammierung, reduzieren die Anzahl der Befehle, implementieren ein einheitliches Befehlsformat und verlassen sich bei der Generierung des Codes stark auf Compiler in den darüber liegenden Ebenen (vgl. Abb. 2). Diese Architekturkonzepte werden in den Kapiteln 2.1.1 und 2.1.2 noch ausführlich besprochen. Bei modernen Mikroprozessoren (z.B. Pentium) verschwimmt allerdings die Grenze zwischen RISC und CISC. Neuerdings sind bei CISC-Architekturen auch Architekturelemente zu finden, die sonst nur bei RISC vorhanden waren. Es gibt Prozessoren, die zwar nach außen eine CISC-Schnittstelle besitzen, aber intern die CISC-Befehle in RISC-artige Befehle übersetzen, um die Hardwarekomplexität des Decoders zu reduzieren. Die RISC-Architekturen enthalten mittlerweile auch CISCMerkmale, wie z.B. eine größere Anzahl von Befehlen. Das Kap. 2.1.3 beschreibt die Konvergenz von CISC und RISC ausführlicher. In Abb. 6 sind die allgemeinen Typen von Mikroprozessoren, sowie die Entwicklung von RISC und CISC als Stammbaum dargestellt. 20 MP Einführung der Superskalartechnik 1995 RISC MC CISC MC ~1990 DSP CISC RISC MC ~ 1980 1979 1974 1971 MP MP: Mikroprozessor MC: Mikrocontroller DSP: Digitaler signalprozessor Abbildung 6: Stammbaum der Mikroprozessor-Entwicklung (angelehnt an [SpEl94]) 21 Verarbeitungsbreite + DEC Alpha AXP 21164 64 Bit + MIPS R12000 + SUN UltraSPARC III + ARM Jaguar (MP) + Intel Itanium (MP) +PowerPC620 + MIPS R2000 + PowerPC604 + Intel Pentium IV + PowerPC603 + Intel Pentium III-Xeon + AMD Athlon XP + PowerPC 601 + AMD Athlon + 68020 Motorola (MP) + Intel Pentium III + Intel 486 + ACORN ARM + Intel Pentium II + Intel Pentium Pro + SUN SPARC + Intel Pentium (MP) + IMS T400 Inmos (erster Transputer) + 80386 Intel (MP) + HP PA-RISC 32 Bit + Intel 2920 (erster DSP) + 68000 Motorola (MP) + Z8000 Zilog (MP) + 80286 Intel (MP) + Intel 8088 + 8086 Intel (MP) + TMS9000 Texas Instruments (MP) 16 Bit + 68HC11 Motorola (MC) + Intel 8085 + 8051 Intel (MC) + 6805 Motorola (MC) 8 Bit + Z80 Zilog (MP) + 8748 Intel (MC mit EPROM) + Intel 8080 + 6800 Motorola (MP) + Intel 8008 + TMS1000 Texas Instruments (erster MC) 4 Bit MP: Mikroprozessor MC: Mikrocontroller DSP: Digitaler Signalprozessor + 4004 Intel (erster MP) 1971 1974 1976 Fertigung ab etwa 1979 1983 1985 1990 Taktraten etwa 0,1 Mhz Transistorgröße etwa 10 µm 1995 2001 Taktraten etwa 2 Ghz Transistorgröße etwa 0,1 µm Abbildung 7: Übersicht wichtiger Prozessoren Die obere Grafik stellt abschließend in etwa die Zeitpunkte der Fertigung sowie die Verarbeitungsbreite bekannter Mikroprozessoren dar, um die Gesamtentwicklung zu verdeutlichen. Die Historie der Rechner und Mikroprozessoren wird detaillierter in [Malone96], [TanGo99], [Märtin01] beschrieben. 22 1.4 Designprozess von Mikroprozessoren Die dargestellte Entwicklung aus dem vorherigen Kapitel lässt schon erahnen, welch atemberaubende Komplexität und Miniaturisierung die Mikroprozessoren durch Einsatz von VLSI (vgl. [Kropf95]) mittlerweile erreichen. Ein strukturiertes und systematisches Vorgehen beim Entwurf von Mikroprozessoren ist daher unerlässlich. Allgemein kann man beim Entwurf von VLSI-Schaltungen, zu denen auch Mikroprozessoren gehören, verschiedene Entwurfssichten (Verhalten, Struktur, Geometrie) und Entwurfshierarchien unterscheiden. Gajski-Walker fassen diese Aspekte im sogenannten Y-Diagramm zusammen (Abb. 8), [Gajski83], [Walker85]. Systemebe ne orit hmische Ebene Al g Verhalten Struktur e r- Tr an sf er- Ebe gist ne Re Systemspezifikation Subsysteme, Busse Module, Verbindungen Gatter, Flipflops, Verbindungen rei s- Eb al tk en ch e S Register-Transfer-Spezif. Boolesche Gleichungen CPU, Speicher i k-Eb ene Log Algorithmen Transistoren, Verbindungsstücke Differentialgleichungen Polygone Zellen Makros Block Chip Geometrie Abbildung 8: Y-Diagramm nach Gajski-Walker Die Verhaltenssicht beschreibt, wie sich das System im laufe der Zeit verhält. Die Struktursicht spezifiziert die Subsysteme, aus denen das System zusammengesetzt wird, und deren Verbindung zueinander. Bei der Geometriesicht interessiert man sich für die räumliche Lage der Subsysteme. Letztere nimmt einen besonderen Stellenwert ein, da bei der hohen Integrationsdichte die Platzierung der Subsysteme mit den entsprechenden Komponenten auf einem Chip, sowie deren Verdrahtung, nicht mehr trivial ist. 23 Die Entwurfshierarchien sind im Y-Diagramm durch Kreise dargestellt, wobei der Abstraktionsgrad nach innen hin abnimmt (vgl. [BeiHag01]). Außen auf der Systemebene werden die Hauptelemente des Systems blockartig (z.B. Speicher, Prozessor, Schnittstellen, I/O-Bausteine) mit den zugehörigen Kommunikationsprotokollen definiert. Man trifft Entscheidungen, was in Hardware und was in Software realisiert werden soll (vgl. Kap. 1.1). Die Algorithmische Ebene beschreibt allgemein mit Regeln, wie die Eingaben von Blöcken in Ausgaben transformiert werden. Dabei spielt hier die Implementation noch keine Rolle. Die darunter liegende Register-Transfer-Ebene beschreibt die Operationen und den Datentransfer zwischen Register-Transfer-Modulen (d.h. Register, ALUs, Multiplexer, etc.). Daraus werden Gatter (nand, or, etc.) auf der Logik-Ebene erzeugt. Die Gatter werden selbst wiederum aus Transistoren gebaut. Auf der SchaltkreisEbene erhält man die genaue Verschaltung der einzelnen Transistoren. Daraus kann man Polygonzüge generieren, die mit Hilfe von photolithografischen Verfahren (evtl. in mehreren Schichten) auf Silizium gebracht werden. Die beschriebenen Entwurfsebenen werden in [Goser91] genauer erläutert. Der Entwurfsablauf als Ganzes ist keineswegs streng top-down oder bottomup. Beim Entwurf fängt man auf dem höchsten Abstraktionsgrad mit der Systemspezifikation an und arbeitet sich schrittweise nach innen. Die Verhaltensspezifikation auf jeder Abstraktionsebene ermöglicht es, Simulationen durchzuführen. Entsprechen die Ergebnisse nicht den Anforderungen, so kann man zu einer höheren Ebene zurückkehren und das Design verändern. Aufgrund des ständigen „Auf und Ab“ nennt man diese Entwurfsstrategie auch „Jo-Jo-Design“ (vgl. auch [GlaRau94]). Der Entwurfsprozess legt das Vorgehen bei der Entwicklung von VLSISchaltungen lediglich strategisch fest. Der Prozess, der aus den Verhaltensspezifikationen die Strukturen erzeugt (Synthese), bleibt kreativ. Wenn beispielsweise für ein Addierwerk spezifiziert wird, dass es Zahlen addieren kann, gibt es mehrere Implementierungsmöglichkeiten (z.B. Ripple-Carry-Adder, CarryLookahed-Adder, etc., vgl. [HePa97]), die alle unterschiedliche Vor- und Nachteile haben (Trade-off zwischen Anzahl der Komponenten und Geschwindigkeit). Eine vollständige Automatisierbarkeit der Entwicklung ist daher Prozessordesign bleibt daher sowohl Kunst als auch Wissenschaft. nicht möglich. 24 Für den rechnerunterstützten Entwurf von VLSI-Schaltungen hat man sog. Hardware-Beschreibungssprachen (Hardware Description Languages, HDL) entwickelt. Sie beschreiben in ähnlicher Notation wie eine Programmiersprache in abstrakter und formaler Weise die Struktur und das Verhalten von Systemen. Auf Basis der Hardware-Beschreibungssprachen können auch Simulationen und Validierungen durchgeführt werden. Das folgende Beispiel aus [Kropf95] in VHDL (Very High Speed Integrated Circuits Hardware Description Language, IEEEStandard seit 1987) zeigt, wie ein Hardwarebaustein zur Bestimmung des größten gemeinsamen Teilers zweier Zahlen spezifiziert werden würde. Genauere Details zu VHDL finden sich auch in [Siemers2000]. Spezifikation der Struktur: ENTITY GCD IS PORT ( In1, In2 : IN Integer; Start : IN Std_Logic; Result : OUT Integer; Ready : OUT Std_Logic); END GCD; Spezifikation des Verhaltens: ARCHITECTURE Behavior OF GCD IS BEGIN PROCESS VARIABLE Z1, Z2 : Integer; BEGIN WAIT UNTIL Start = ’1’ AND Start’EVENT; Ready <= ’0’; Z1 := In1; Z2 := In2; WHILE (Z1 /= Z2) LOOP IF (Z1>Z2) THEN Z1 := Z1 – Z2; ELSE Z2 := Z2 – Z1; END IF; END LOOP; 25 Result <= Z1; Ready <= ’1’; END PROCESS; END Behavior; Aufgrund der hohen Komplexität der Hardware ist das Testen der fertigen Bausteine nicht mehr einfach. Es kann sogar sein, dass die Testkosten die Herstellungskosten übersteigen [Runyon99]. Um das zu vermeiden, müssen beim Entwurf der Architektur auch Testmöglichkeiten berücksichtigt werden. Hierbei gibt es verschiedene Ansatzpunkte (vgl. [Märtin01]), von denen die wichtigsten erwähnt werden: • Design Rule Checking soll Fehler schon präventiv während des Entwurfs verhindern [Crouch99]. • Design for Testability: Zusätzliche Hardware in Chips einbauen, die spätere Tests vereinfacht [ABF95]. Dazu gehören beispielsweise Testmustergeneratoren, zusätzliche Register und Pins, oder DebugAusführungsmodi (z.B. in den Intel Pentium CPUs vorhanden). • Beim Built-In Self Test (BIST) wird das Testverfahren komplett in den Chip integriert [ABF95], [Wunderlich98]. BIST wird z.B. in manchen CPUs oder in RAM-Speichern benutzt. Die wesentlichen technischen Herausforderungen beim Designprozess bleiben die Beherrschung der Komplexität, der geringe Energieverbrauch (weil dadurch Taktraten gesteigert werden können) und das Handhaben des memory-latencyProblems (Speicher ist viel langsamer als CPU, so dass diese oft warten muss). Die Entwicklungszeit eines Prozessors sollte so kurz wie möglich sein, um den Herausforderungen des Marktes gerecht zu werden. Es ist oft nicht auszuschließen, dass bis zur Fertigstellung eines Prozessors die Technologie bereits veraltet ist und am Markt kein Bedarf mehr besteht. 26 1.5 Klassifizierungsschemata Für die Vergleichbarkeit und Analyse von Rechnern sind Modelle notwendig, die Rechner nach bestimmten Kriterien, wie z.B. Aufbau und Organisation, klassifizieren. Die Modelle basieren prinzipiell immer darauf, dass erst alle Kriterien identifiziert werden, die für die Unterschiede verantwortlich sind und die Rechner damit in bestimmte Klassen eingeordnet werden. Rechner innerhalb einer Klasse werden als äquivalent angesehen. Es ist allerdings hinzuzufügen, dass solche Klassifikationen nicht immer eindeutig durchzuführen sind und auch die Wahl der Kriterien nicht unumstritten ist. Generell wird bei der Klassifikation auch nicht zwischen Rechner- oder Prozessorarchitektur unterschieden (vgl. [BeiHag01]). 1.5.1 Klassifikation nach Flynn Das wohl berühmteste und am häufigsten gebrauchte Klassifikationsverfahren ist das von Flynn (vgl. [Flynn72]). Es enthält 4 Klassen von Rechnerarchitekturen. Als Kriterien werden das einfache oder mehrfache Auftreten von Befehls- und Datenströmen verwendet. Tabelle 1: Klassifizierung nach Flynn einfacher Befehlsstrom (SI) mehrfacher Befehlsstrom (MI) einfacher SISD MISD Datenstrom (SD) (von-Neumann-Architektur) (-) mehrfacher SIMD MIMD Datenstrom (MD) (Vektorprozessoren) (Mehrprozessorrechner) Flynn geht davon aus, dass • zu einem Zeitpunkt entweder nur ein Befehl (Single Instruction, SI) oder mehrere Befehle (Multiple Instructions, MI) ausgeführt werden können 27 • zu einem Zeitpunkt ein Rechner nur einen Datenwert (Single Data, SD) oder mehrere Datenwerte (Multiple Data, MD) bearbeiten kann. SISD-Rechner entsprechen den klassischen von-Neumann-Rechnern, deren Steuerwek Befehle zeitlich einzeln und nacheinander ausführt. Das Problem dieser Klasse ist, dass alle Rechner, die genau ein Steuerwerk und ein Rechenwerk besitzen in diese Klasse fallen und nicht weiter differenziert werden. Die Klassifizierung ist also recht grob. SIMD-Rechner sind eine Klasse, die ein Steuerwerk und mehrere Rechenwerke besitzen, so dass sie einen Befehl (durch Instruction Broadcasting) gleichzeitig auf mehrere Daten anwenden können. MIMD-Rechner bestehen aus mehreren selbständigen Prozessoren. Auch diese Einteilung ist nicht fein genug. So kann z.B. nicht zwischen eng gekoppelten Systemen (alle Prozessoren haben einen gemeinsamen Adressraum) und lose gekoppelten Systemen (jeder Prozessor hat einen eigenen Adressraum) unterschieden werden (vgl. [Märtin01]). Die aus Symmetriegründen vorhandene Klasse MISD bleibt leer, da man ihr keine bekannten Systeme zuordnen kann. Insgesamt ist also die Klassifikation nach Flynn durch einen sehr hohen Abstraktionsgrad gekennzeichnet. Es wird kritisiert, dass in eine Klasse sehr viele Architekturen fallen und eine Klasse leer bleibt, weshalb die Güte der Klassifikation in Frage gestellt wird. Bei modernen Superskalar-Architekturen oder Mischformen verschiedener Architekturen ist die Klassifikation ebenfalls problematisch. 1.5.2 Erlanger Klassifikationssystem Das Erlanger Klassifikationssystem (Erlanger Classification System, ECS) wurde 1975 von Händler vorgeschlagen [Händler75] und versucht mit einer möglichst einfachen Darstellung auch die Parallelität in Rechnern zu beschreiben. Ein Rechner wird dabei in die Ebenen Prozessorebene, Befehlsausführungsebene, Verarbeitungsebene unterteilt und durch Tripel beschrieben: tRechnertyp = (k * k’, d * d’, w * w’) 28 Dabei ist Die • k die Anzahl der Steuerwerke • d die Zahl der Rechenwerke die einem Steuerwerk zugeordnet sind • w die Wortbreite des Rechners Elemente hinter dem Konkatenationsoperator „*“ sollen die Beschreibungsgenauigkeit steigern und können auch weggelassen werden. Die Zahlen k, d, w sollen als Maß für die Nebenläufigkeit und k’, d’, w’ als Maß für Pipelining (vgl. Kap. 3.1) auf den verschiedenen Ebenen dienen. Unter Nebenläufigkeit versteht man, dass k Prozesse unabhängig voneinander bearbeitet werden können. Das optionale Element k’ stellt eine weitere Spezialisierung des Steuerwerks dar und gibt den Grad des Makropipelinings an. Beim Makropipelining gibt es mehrere Prozessoren, die den gleichen sequentiellen Datenstrom bearbeiten, wobei aber jeder Prozessor eine andere Phase der Ausführung bearbeitet. Das Element d gibt an, dass eine Operation gleichzeitig von d Rechenwerken ausgeführt werden kann. Die Zahl d’ gibt den Grad des Befehlspipelinings in einem Rechenwerk an, d.h. dass maximal d’ sequenzielle Befehle gleichzeitig ausgeführt werden können, wenn es keine Datenabhängigkeiten zwischen ihnen gibt und genügend Funktionseinheiten für die Verarbeitung bereit stehen. Auf der Wortebene (Mikroarchitekturebene) des Rechners gibt die Zahl w’ den Grad des Phasenpipelinings an. Dabei wird die Ausführung eines Befehls zeitlich in w’ Phasen unterteilt, wobei jede Verarbeitungseinheit der Pipeline eine Phase bearbeitet. Die folgende Abbildung fasst die Rechnertypen nach ECS überblicksartig zusammen. 29 Rechnerstrukturen Serienrechner Parallelrechner Rechner mit Nebenläufigkeit Multiprozessor k>1 Feldrechner Parallelwortrechner d>1 w>1 Pipelinerechner Rechner mit Makropipelining k’>1 Rechner mit Befehlspipelining d’>1 Rechner mit Phasenpipelining w’>1 Abbildung 9: ECS-Klassifikation von Prozessoren. Quelle: [BeiHag01] Das ECS enthält auch weitere Operatoren, wie z.B. „+“, um Strukturen darzustellen, die sich während der Verarbeitung gegenseitig ausschließen, oder „v“, um variable, d.h. rekonfigurierbare Elemente darzustellen. Es folgen nun ein paar einfache Beispiele für ECS-Tripel aus [Märtin01]. tBeispielrechner := (9000, 1, 32) „Es handelt sich um einen Multiprozessor, dessen 9000 Prozessoren über je ein Rechenwerk verfügen. Die Prozessoren besitzen die Wortlänge 32 Bit.“ Entspricht etwa dem ASCI-Red Supercomputer. tBeispielrechner2 := (1, 64, 64) Es handelt sich um einen Rechner mit 1 Steuerwerk und 64 Rechenwerken der Wortlänge 64 Bit. tBeispielrechner3 := (1 * 3, 1, 32) „Ein Drei-Prozessor-System, das als Makropipe konfiguriert wurde, bei dem jeder Prozessor ein nahezu identisches Teilprogramm ausführt.“ tPentium III := (1, 1 * 5, 32) „Das bedeutet: Pro Takt können intern 5 bereits dekodierte Befehle an Funktionseinheiten zur Ausführung übergeben werden.“ 30 1.5.3 Klassifikation nach dem internen Speichermodell Eine andere Möglichkeit, Prozessoren zu klassifizieren, ist das interne Speichermodell (vgl. [BeiHag01]). Es spezifiziert den Zugriffsbereich des Prozessors auf die Operanden bei der Ausführung von Operationen. Dabei gibt es folgende Architekturklassen: 1. Stack-Architektur Der Prozessor greift auf einen in Hardware implementierten Stack zu, wo Quell- und Zieloperanden gespeichert werden. Vorteilhaft ist, dass die Zugriffe auch implizit durch eine genau festgelegte Reihenfolge erfolgen können. Damit entfällt die ständige Angabe der Operanden und es ergeben sich kleinere OpCode-Längen. Allerdings sind Austauschoperationen für die Datenübertragung zum und vom Stack nötig sowie Operationen für die Datenumschichtung im Stack. 2. Akkumulator-Architektur Hierbei gibt es ein spezielles Register, den Akkumulator, das implizit als Quelle bzw. Ziel einer Operation benutzt werden kann. Die Architektur ist damit zwar sehr einfach, aber der Akkumulator entwickelt sich zum Engpass des Systems, weil er häufig gebraucht wird. Das erfordert auch, dass sein Inhalt häufig zwischengespeichert wird. 3. (General-Purpose-)Register-Architektur Bei dieser Architektur wird mindestens ein Operand aus einem internen Register des Prozessors geholt, welches auch explizit angegeben werden muss. Dabei gibt es zwei Varianten: 3.1 Register-Speicher-Architektur Das Register und eine Speicheradresse werden explizit angegeben. Der Vorteil ist, dass Daten direkt aus dem Speicher benutzt werden können und nicht vorher alle in die Register geladen werden müssen. Nachteilig ist die sehr unterschiedliche Zugriffszeit von Register und Speicher. 31 3.2 Register-Register-Architektur (Load-Store-Architektur) Operationen durchgeführt. werden Zwischen nur auf Register internen und Prozesor-Registern Speicher findet nur Datenaustausch statt. Das führt dazu, dass Operationen sehr schnell ausgeführt werden können, aber zusätzliche Ladezugriffe nötig werden. Diese Architektur ist bei RISC-Prozessoren zu finden (vgl. Kap. 2.1.2) 1.5.4 Klassifikation nach Befehlsformaten Die letzte hier vorgestellte Klassifikationsmöglichkeit von Prozessoren orientiert sich am Format der Instruktionen ([BeiHag01], [SDI99]). Wie in Kap. 1.2 bereits erläutert, bestehen Instruktionen aus einem Operationsteil, dem sog. OpCode und einem dazugehörigen Adress-Teil, der die benötigten Operanden lokalisiert. Je nach Anzahl der Operanden unterscheidet man zwischen: 1-Adress-Prozessoren • Der Adressteil enthält nur eine Adresse. • Weiter benötigte Operanden befinden sich im Akkumulator-Register (vgl. 1.5.3), das auch die Ergebnisse aufnimmt. Daher sind diese Prozessoren an eine Akkumulator-Architektur gebunden. Alternativ ist auch die Benutzung einer Stack-Architektur denkbar. • Bsp: ADD 10 bedeutet: Akkumulator := Akkumulator + Inhalt der Speicherzelle 10 • Beispiel-Prozessor: Intel 8080 2-Adress-Prozessoren • Der Adressteil enthält 2 Adressen für 2 Operanden. Einer der Operanden wird durch das Ergebnis überschrieben. • Bsp: ADD 10, 20 bedeutet: Inhalt Speicherzelle 10:= Inhalt Speicherzelle 10 + Inhalt Speicherzelle 20 32 • Beispiel-Prozessor: Motorola 68000 3-Adress-Prozessoren • Der Adressteil enthält 3 Adressen, jeweils für das Ziel und für 2 Operanden • Bsp: ADD 10, 20, 30 bedeutet Inhalt Speicherzelle 10 := Inhalt Speicherzelle 20 + Inhalt Speicherzelle 30 • Beispiel-Prozessor: SUN SPARC Allgemein heißt ein Prozessor m-Adress-Prozessor, wenn pro Instruktion m Adressen explizit genannt werden. Bei der obigen Betrachtung wurden immer Operanden fester Länge vorausgesetzt. Es gibt auch Architekturen, deren Operanden nicht auf eine Speicherzelle beschränkt sind. Dabei enthält eine Instruktion neben dem Operations- und Operandenteil noch zusätzliche Angaben über die Länge der einzelnen Operanden. Bei der Betrachtung der Befehlsformate kann eine Klassifikation auch hinsichtlich der Komplexität des Befehlssatzes erfolgen, beispielsweise nach CISC und RISC-Architekturen. Diese werden in den Kapiteln 2.1.1 und 2.1.2 in einem größeren Zusammenhang genauer beschrieben. 1.5.5 Fazit zur Klassifikation Trotz der beschriebenen Ansätze bleibt die Klassifikation von Rechnern und Prozessoren nach wie vor problematisch. Manche Verfahren beschreiben gleiche Sachverhalte aus unterschiedlicher Sicht, während andere disjunkt zueinander sind. Deswegen soll hier der Versuch unternommen werden, die vorgestellten Klassifikationsverfahren zusammenfassend darzustellen und deren Gemeinsamkeiten, Unterschiede und Lücken aufzuzeigen. 33 1) ECS: Nebenläufigkeit, Pipelining, Wortgröße 4) Flynn: Befehls- & Datenströme einfach/mehrfach 2) Speichermodell: Zugriffsbereich des Prozessors auf Operanden 3) Befehlsformate: Anzahl explizit gennanter Adressen in Instruktionen Abbildung 10: Gesamtsicht auf Klassifikationsverfahren Die Gemeinsamkeiten sollen kurz erläutert werden: 1) und 2): Bei Feldrechnern und Multiprozessorsystemen (durch ECS beschrieben) kann sich der Zugriffsbereich für Operanden (durch Speichermodell beschrieben) auf einen gemeinsamen oder mehrere getrennte Speicher erstrecken. 1) und 3): Die von 1) beschriebene Wortgröße nimmt mit der Anzahl der explizit genannten Instruktionen zu, die 3) spezifiziert. 2) und 3): Die Befehlsformate und das Speichermodell beeinflussen sich gegenseitig. Beispielsweise kann beim Stack- oder Akkumulator-Modell die Adressierung eines Operanden implizit erfolgen (vgl. Adressierungsarten in Kap. 2.1.1). 3) und 4): Befehlsformate aus 3) werden von Flynn indirekt durch einfache oder mehrfache Befehls- und Datenströme festgelegt. 4) und 1): Parallelität wird im ECS durch Nebenläufigkeit und Pipelining erfasst. Flynn beschreibt diesen Sachverhalt mit mehrfachen Befehls- und Datenströmen. Die Ansätze 2) und 4) haben keine gemeinsamen Aspekte, da die Befehls- und Datenströme nicht implizieren, ob eine Stack-, Akkumulator- oder RegisterArchitektur vorhanden ist. Schließlich gibt es keinen Aspekt, den 1), 2), 3) und 4) 34 gleichzeitig beschreiben (was auch durch die Disjunktion der Ansätze 2) und 4) begründet werden kann). Die Problematik der Klassifikation kann am Beispiel des Intel Pentium IIIProzessors beschrieben werden: 1) ECS: tPentium III := (1, 1 * 5, 32) Pro Takt können intern 5 bereits dekodierte Befehle an Funktionseinheiten zur Ausführung übergeben werden [Märtin01]. 2) Speichermodell: Der Speicher wird sowohl in Akkumulator- als auch in Stack-Architektur (bei der FPU, vgl. Kap. 2) implementiert (vgl. [Intel99]). Da aber CISC-Befehle intern in einfachere „µOps“ transformiert werden (vgl. Kap. 2.1.3) und RISC-ähnliche Decoder vorhanden sind, wird intern auch eine Load/Store-Architektur implementiert [Gwen95]. Die Klassifikation ist nicht eindeutig. 3) Befehlsformate: Die Anzahl der Operanden ist nicht fest, sondern variiert je nach Befehlstyp und Adressierungsart [Intel99], [TanGo99], [Märtin01]. 4) Flynn: Aus externer Sicht des Programmierers: SISD. Diese Einteilung stimmt aber auch nicht ganz, da intern Phasenpipelines eingesetzt werden und Befehle zeitlich überlappt bearbeitet werden (vgl. Kap. 3.1). Bei MMX-Befehlen ist auch eine SIMD-Verarbeitung möglich (vgl. Kap. 3.3.4). 35 2 Mikroprozessorarchitektur Wie schon in Kapitel 1.2 dargestellt wurde, ist eine CPU nach von Neumann auf viele Arten erweiterbar, um eine noch effizientere und schnellere Verarbeitung der Befehle zu gewährleisten. In diesem Kapitel werden Architekturerweiterungen der von-Neumann-CPU vorgestellt, wie sie auch häufig in heutigen CPUs vorkommen. Es wird auf die im Laufe der Zeit entstandenen CISC und RISC– Philosophien genauer eingegangen. Anschließend werden Konzepte der Speicherverwaltung, Pipelining und Sprungvorhersage bei der Befehlsausführung vorgestellt, die der Performanceverbesserung dienen sollen. Danach sind die Grundlagen vorhanden, um superskalare Architekturen aus einer Gesamtsicht zu verstehen. Zunächst soll anhand eines Blockdiagramms eine Architektur eines Mikroprozessors aus den 90er Jahren erläutert werden (nach [Tabak91]), da viele der hier verwendeten Konzepte in späteren Architekturen zu finden sind. Prefetch Unit und Instruction Queue Systembus Datenbus Adressbus Steuerbus Dekodiereinheit (Decoding Unit) Data Interface Cache Address Interface Control Interface Instruction Cache Steuerwerk (Control Unit, CU) Data Cache Memory Management Unit (MMU) Translation Lookaside Buffer (TLB) Paging Unit Segmentation Unit Integer ALU General Purpose Register Floating Point Unit (FPU) FPU-Register Abbildung 11: Architektur eines Mikroprozessors (angelehnt an [Tabak91]) 36 Ein Mikroprozessor heißt n-Bit-Mikroprozessor, falls die ALU, Register und der interne Bus des Mikroprozessors n-Bit breit sind. Manche Prozessoren haben mehrere interne Busse, deren Breite auch ein Vielfaches von n sein kann. Die Bus Interface Unit (BIU) enthält Puffer, die zwischen Prozessor und der Außenwelt geschaltet sind. Die BIU wird in Daten-, Adress- und Control-Interface unterteilt. Das Dateninterface verbindet den Datenbus des Gesamtsystems mit dem Mikroprozessor, damit Daten gelesen oder geschrieben werden können. Über das Adressinterface werden die von der Memory Management Unit (MMU) generierten Adressen für Befehle oder Daten auf den Adressbus gelegt. Das Control-Interface kann Signale des Steuerbusses lesen oder setzen. Die zu setzenden Signale erhält es vom Steuerwerk (Control Unit, CU). Die Control Unit ist über Steuerleitungen auch mit anderen Einheiten verbunden, von denen sie den Status abfragen oder die Verarbeitung steuern kann. Üblicherweise ist die CU bei CISC-Architekturen mikroprogrammiert, während sie bei RISC-Architekturen fest verdrahtet ist. Die Prefetch Unit holt Befehle aus dem Speicher und legt sie in eine FIFOQueue. Die Befehle werden dann an die Dekodiereinheit geschickt, welche mit der CU gekoppelt ist, so dass die für die Ausführung des Befehls nötigen Steuersignale gesetzt werden können. Der Cache-Speicher (vgl. Kap. 2.2.1) kann Daten und Instruktionen entweder zusammen oder getrennt speichern. Der Instruction-Cache ist zwischen Prefetch Unit und BIU geschaltet. Wenn die Prefetch Unit mehrmals den gleichen Befehl holen will, dann ist er bereits im Cache zu finden. Der Instruction-Cache und der Daten-Cache sind auch noch mit dem internen Mikroprozessorbus verbunden. Bei manchen Mikroprozessoren gibt es noch zusätzliche Busse, die den Datencache mit den Verarbeitungseinheiten (Integer ALU, FPU) einzeln verbinden. Die Integer ALU, manchmal auch Integer Unit (IU) genannt, hat oft spezialisierte Teileinheiten für Addition/Subtraktion und Multiplikation/Division, die auf die Allzweckregister (General Purpose Registers) zugreifen. Auch die Floating Point Unit (FPU) hat auch oft Teileinheiten für Addition/Subtraktion und Multiplikation/Division für Gleitkommazahlen. Außerdem gibt es spezielle Register für Gleitkommazahlen. Die Operationen, welche die FPU durchführt, sind durch den IEEE-754 Standard festgelegt. 37 Die MMU übersetzt logische (vom Prozessor berechnete) Adressen in physikalische Adressen (auf die schließlich zugegriffen wird) und überträgt sie an die BIU. Außerdem wird der Paging-Mechanismus, der Segmentation-Mechanismus und die Implementation von virtuellem Speicher unterstützt. Der Translation Lookaside Buffer (TLB) ist ein Cache-Speicher, der bei der Adressübersetzung eingesetzt wird. Auf die Speicherverwaltung geht das Kapitel 2.2 genauer ein. Der Intel 486-Prozessor (zur zeitlichen Einordnung vgl. Abb. 7) ist beispielsweise ein 32-Bit-Mikroprozessor und hat u.a. eine Integer-ALU, 8 General-PurposeRegister, eine FPU mit 8 FPU-Registern, einen 8 KByte großen Cache-Speicher und einen TLB mit 32 Einträgen. Weitere Details siehe [Tabak91] und [Messmer00]. 2.1 ISA-Varianten Im Verlauf dieses Kapitels werden die aus der historischen Entwicklung (vgl. Kap. 1.3) hervorgegangenen ISA-Varianten genauer erläutert. Eine ISA (vgl. Kap. 1.1) beeinflusst durch die Festlegung der Befehlstypen, Befehlsformate und der für den Assemblerprogrammierer sichtbaren Register in entscheidendem Maße die Mikroprozessorarchitektur und das Zusammenwirken mit Compilern und Betriebssystemen (vgl. Kap. 6.1). Zunächst folgt eine Darstellung der reinen CISC- und RISCPrinzipien. Anschließend wird auf Architekturen eingegangen, die gleichzeitig sowohl CISC-, als auch RISC-Konzepte benutzen. 2.1.1 CISC Nach der Entwicklung des ersten Mikroprozessors (vgl. Abb. 7) wurden Mikroprozessoren mit immer mehr Befehlen ausgestattet. Diese wurden auch zunehmend komplexer, d.h. dass mehr Informationen in einem Befehlswort vorhanden waren. Die Gründe hierfür waren, dass die Zugriffszeiten auf den Speicher, verglichen mit der Verarbeitungsgeschwindigkeit des Prozessors, sehr langsam waren und deswegen zusammenhängende Aktionen auch zusammenhängend kodiert wurden. Das hatte gleichzeitig zur Folge, dass man durch kompakten Code Speicher gespart hatte. Rechner mit solchen ISAs hat man Complex Instruction Set Computer (CISC) genannt. Einige (wenige) Beispiele für 38 CISC-Mikroprozessoren sind: AT&T WE32100, Zilog Z80, Motorola 68000 und die Intel 80x86 Familie. Ein Merkmal von CISC-Rechnern ist, dass sie Mikroprogrammierung verwenden, die 1951 von Wilkes erfunden wurde. Gemäß [Giloi97] ist ein Mikroprogramm „die Darstellung eines Hardware-Algorithmus eines Prozessors, d.h. eines Algorithmus, der von den Funktionseinheiten der Prozessor-Hardware ausgeführt wird und damit eine Maschinenoperation durchführt.“ Die Idee ist, dass die Kontrolle der CPU durch ein Programm erfolgt. Ein Makro-Befehl, wie <load AX,mem[5]> (lade Inhalt der mem-Adresse 5 in Akkumulator) könnte beispielsweise so in Mikrooperationen zerlegt werden: <schreibe 5 in MAR>, <lade den Wert mem[MAR] in MBR>, <schreibe MBR ins Akkumulator-Register>. Ein Mikroprogramm Mikrooperationen. Die besteht aus Mikrooperationen einer sind sequenziellen eine Bitfolge Folge mit von einem Instruktionsteil und einem Adressteil, wobei letzterer die Adresse des nächsten auszuführenden Mikrobefehls enthält. Mikroprogrammierung kann auf mehrere Arten implementiert werden. Das Mikroprogramm kann einerseits auf dem Prozessor fest verdrahtet sein oder in einem Mikroprogrammspeicher liegen. Beim horizontalen Mikrobefehlsformat stellen die Bits im Instruktionsteil Steuersignale für die Hardware (=Picobefehle) dar. Dieses Vorgehen kann noch beim quasihorizontalen Format durch eine Stufe (=Nanobefehlsebene) zwischen Mikrobefehl und Steuersignalen erweitert werden, d.h. Mikrobefehle liegen in kodierter Form vor. Hierbei stellt der Mikrobefehl einen Index dar, der als Einstiegspunkt in eine Look-up-Tabelle dient, die für den Mikrobefehl die entsprechenden Steuersignale enthält. Beim vertikalen Format wird schließlich der kodierte Instruktionsteil wie ein OpCode behandelt, der erst dekodiert werden muss. Für eine ausführlichere Darstellung der Mikroprogrammierung und der Arten der Mikroprogrammierung sei auf [BeiHag01] verwiesen. Durch Mikroprogrammierung ist ein flexibles Prozessordesign möglich, so dass neue ISAs leicht anzupassen sind und Abwärtskompatibilität ohne großen Aufwand gewährleistet werden kann. Der Nachteil sind 39 die hohen Hardware-Kosten (da mehr Transistoren für die komplexe Dekodierlogik benötigt werden) und die Langsamkeit der Ausführung von Befehlen. Die Länge des Befehlswortes ist bei CISC-ISAs variabel (z.B. Pentium III: 1 bis 15 Bytes) und es gibt viele unterschiedliche Befehlstypen (etwa 480 bei der Pentium III IA-32-ISA), was die Dekodierung erschwert (vgl. [Märtin01]). Befehlstypen können sein: Daten-Movement-Befehle, Austauschbefehle, Befehle zur Stack-Manipuation, Arithmetische Befehle, Logische Befehle, Verzweigungen, Ein-/Ausgabe-Befehle, u.v.a.m. Die Dekodierung ist aufwändig, da die Grenzen des Befehls, die über mehrere Bytes verteilten Opcodes und die Operanden erkannt werden müssen. Für die Operanden können zusätzlich noch Adressberechnungen erfolgen. CISC-Rechner verfügen auch über eine große Anzahl an Adressierungsarten, wovon hier einige vorgestellt werden sollen. Adressierungsarten beschreiben, wie die physikalischen Adressen von Daten ermittelt werden. Ausgangspunkt ist dabei die Ermittlung der Operanden einer Instruktion. Dabei gibt es mehrere Möglichkeiten, wo sich Operanden befinden können: • unmittelbar nach der Instruktion • in Registern • im Speicher Bei impliziter Adressierung ist der Ort des Operanden bei Benutzung des Befehls implizit bekannt und wird nicht angegeben (z.B. Operand im Akkumulator bei Akkumulator-Architektur, oder auf oberster Stack-Ebene bei Stack-Architektur, vgl. Kap. 1.5.3) Register Instruktion Daten Abbildung 12: Implizite Adressierung (implicit) Bei der unmittelbaren Adressierung folgt der Operand unmittelbar dem OpCode im Speicher: 40 Instruktion Daten Abbildung 13: Unmittelbare Adressierung (immediate) Die registerdirekte Adressierung enthält im Operandenteil die Adresse eines Registers, welches die Daten enthält: Register Instruktion Adresse Daten Abbildung 14: Registerdirekte Adressierung (register direct) Das kann noch zur registerindirekten Adressierung erweitert werden, so dass im Register wieder eine Speicheradresse steht, die die Daten enthält: Register Speicher Instruktion Adresse Adresse Daten Abbildung 15: Registerindirekte Adressierung (register indirect) Bei der speicherdirekten Adressierung enthält der Adressteil eine Speicheradresse, wo die Daten zu finden sind: Register Speicher Instruktion Adresse Daten Abbildung 16: Speicherdirekte Adressierung (memory direct) Auch das kann zur speicherindirekten Adressierung erweitert werden, so dass der Inhalt der im Adressteil angegebenen Speicherzelle die Adresse des Operanden darstellt: 41 Speicher Speicher Adresse Daten Instruktion Adresse Abbildung 17: Speicherindirekte Adressierung (memory indirect) Schließlich gibt es noch indizierte Adressierungsarten. Bei der speicherdirekten, indizierten Adressierung wird noch ein Index aus einem Indexregister auf die Adresse im Adressteil der Instruktion addiert, was für das Durchlaufen zusammenhängender Speicherbereiche nützlich ist. Register Instruktion Speicher Index Adresse + Daten Abbildung 18: Speicherdirekte, indizierte Adressierung (memory direct, indexed) Von dieser Adressierungsart gibt es noch Varianten mit Postinkrement oder mit Prädekrement, die den Inhalt des Indexregisters nach dem Zugriff um einen konstanten Wert erhöhen bzw. vor dem Zugriff vermindern. Details gibt es in [BeiHag01]. Ein zusätzliches Merkmal von CISC-Befehlssatz-Architekturen ist die geringe Anzahl der Register. Beispielsweise hat die IA-32-Architektur von Intel (vgl. [Intel00a]) acht 32-Bit breite Standardregister (EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP). Die einzelnen Register haben dabei auch spezielle Funktionen, z.B. wird AX als Akkumulator (vgl. Akkumulator-Architektur Kap. 1.5.3) oder Operanden-Register verwendet und ECX für die Steuerung von Schleifen. Zusätzlich gibt es noch sechs 16-Bit breite Segment-Register (CS, SS, DS, ES, FS, GS), welche die Adressierung von Programm-Segmenten unterstützen sollen und ein Flag-Register, dessen StatusBits z.B. einen erfolgten Übertrag oder ein negatives Vorzeichen signalisieren. Weitere Systemregister erleichtern die Implementation von Paging auf Betriebssystemebene (vgl. Kap. 6.1) und enthalten u.a. auch den Befehlszähler (PC). Schließlich existieren noch acht 80-Bit breite Gleitkomma-Register, die als Stack organisiert sind (vgl. Stack-Architektur, Kap. 1.5.3) und acht 128-Bit Register (ab Pentium III) für SIMD-Befehle auf Gleitkommadaten (vgl. Kap. 3.3.4). Es sei hier 42 noch darauf aufmerksam gemacht, dass die IA-32-Architektur dem Programmiermodell des Rechners entspricht, der sie implementiert (z.B. Pentium III). 2.1.2 RISC Nach der rasanten Entwicklung von CISC-Prozessoren kam in den 70er Jahren eine andere Philosophie auf. Man stellte fest, dass in 99% der Programmcodes nur etwa 30 Befehle verwendet wurden [HePa96]. Diese Erkenntnis war die Geburtsstunde von RISC (Reduced Instruction Set Computer) [Pat85]. Merkmale der RISCArchitektur sind oft weniger als 150 Maschinenbefehle mit einheitlicher Länge, wenig Befehlstypen (etwa 40-80) und der völlige Verzicht auf Mikroprogrammierung (vgl. [BeiHag01]). Das hat zur Folge, dass zwar die Programmgröße wächst, aber die Hardware und die Dekodierung der Befehle sehr vereinfacht werden. Da es keine Mikroprogramme und ein einheitliches Befehlsformat gibt, ist die Dekodierung fest verdrahtet und die Ausführungsgeschwindigkeit steigt. Ein weiteres Merkmal von RISC-Prozessoren ist, dass pro Takt 1 Befehl ausgeführt wird (1 CPI, 1 Clock per Instruction). Um das zu realisieren, werden Phasenpipelines eingesetzt (vgl. 3.1). Bei der Reduktion der Befehle werden auch diejenigen abgeschafft, die direkte Manipulationen im Speicher durchführen. Deswegen wird bei RISC-Prozessoren eine Load-Store-Architektur mit vielen Registern (>32) implementiert (vgl. 1.5.3). Für eine Manipulation von Daten im Speicher müssen erst die Daten in die Register übertragen werden, dort verändert und anschließend wieder zurück in den Speicher geschrieben werden. Im Gegensatz zu CISC-Architekturen gibt es oft weniger als 4 Adressierungs-Modi. Der RISC-Ansatz verlässt sich auch darauf, dass Compiler optimierten Code generieren (vgl. Kap. 6.1). Beispiele für bekannte RISC-CPUs sind: HP Precision Architecture, Fairchild Clipper, Berkeley RISC, Stanford MIPS, AMD Am29000, Motorola 88000, Intel 80860, ARM (Advanced RISC Machine), IBM RS/6000 (RS=Risc System), SUN SPARC, IBM POWER, DEC Alpha. Rückblickend kann man die Entwicklung von RISC-Prozessoren in 3 Generationen unterteilen [ObVossen00]. In den 70er Jahren wurden RISC-Prozessoren zum ersten Mal implementiert. Beispielsweise hatten die RISC-II-Prozessoren (Berkeley) über 138 32-Bit-Register und einen Befehlssatz von 39 Instruktionen. Gleichzeitig war die Hardware-Komplexität mit 20.000 bis 100.000 Transistorfunktionen gering. Bei der 2. Generation gab es umfangreichere Befehlssätze (z.B. für Gleitkomma- 43 operationen) und zusätzlich in Hardware integrierte Funktionen, wie beispielsweise eine MMU für die Adressübersetzung. Dabei lag die Komplexität unter 1 Mio. Transistorfunktionen. Ein Prozessor dieser Generation ist beispielsweise der SUN SPARC-Prozessor. Die 3. Generation ist noch zusätzlich um Parallelitätskonzepte wie Superpipelining (vgl. Kapitel 3.1) und Superskalarität (vgl. Kapitel 3.3) erweitert worden. Im folgenden soll nun eine typische RISC-Architektur anhand des PowerPCMikroprozessors vorgestellt werden. Der PowerPC (Performance Optimization With Enhanced RISC Performance Computing) basiert auf der Power-Architektur, die von Apple, IBM und Motorola entwickelt wurde (vgl. [Weiss94]). Von diesem Prozessor gab es mehrere 32-Bit Versionen: PowerPC 601 (1993), PowerPC 603 (Ende 1993, geringerer Energieverbrauch), PowerPC 604 (1994, höhere Leistungsfähigkeit). Der PowerPC 620 (1994) hatte schließlich eine 64-BitArchitektur (vgl. Abb. 7 und [Ungerer95]). Branch Processing Unit instruction fetch Condition-Register Link-Register Count-Register instruction dispatch Floating Point Unit Integer Unit .. 32 doppelt genaue FP-Register 32 Register Adressen IU-Daten FPU-Daten Speicher Abbildung 19: Logische Sicht der POWER-Architektur (angelehnt an [Weiss94]) 44 Die PowerPC-Architektur definiert aus logischer Sicht die Verarbeitungseinheiten Branch Processing Unit (BPU), Integer-Unit (IU) und Floating Point Unit (FPU), welche parallel zueinander arbeiten können. Die BPU ist dafür zuständig, Sprungbefehle zu bearbeiten, während die IU Integer-Befehle und die FPU Gleitkommabefehle bearbeitet. Die BPU enthält ein Link-Register, welches die Adresse eines Sprungziels oder eine Rücksprungadresse aufnehmen kann und von bestimmten Befehlen bei der Ausführung von Unterprogrammen benötigt wird. Für die Implementierung von Schleifen gibt es ein Count-Register. Außerdem enthält die BPU noch ein ConditionRegister mit acht 4-Bit großen Bedingungsfeldern, die von vielen Befehlen gesetzt werden können. Die IU hat 32 allgemeine Register und einen Satz von Operationen für Berechnung, Bitverschiebung und Speicherzugriff. Hier werden auch Adressberechnungen für Speicheroperationen durchgeführt und deren Ausführung gesteuert. In der FPU gibt es 32 doppelt genaue Gleitpunkt-Register. Genauere Spezifikationen für die Realisierung der FPU werden nicht gemacht. Nachdem jetzt die Grundelemente der POWER-Architektur bekannt sind, wird nun überblicksartig die Architektur des PowerPC 601 als 32-Bit Implementierung (2,8 Mio Transistoren, 50 MHz Taktfrequenz) der Power-Architektur genauer beschrieben. Der PowerPC 601 hat über 200 Befehle, die alle 4 Byte lang sind und in die Klassen Integer-Befehle, Prozessor-Kontroll-Befehle Load/Store-Befehle, eingeteilt werden. Pro Floating-Point-Befehle, Takt können durch die „Instruction Queue and Dispatch Logic“ (Befehlszuordnungseinheit) bis zu 3 Befehle den Ausführungseinheiten IU, FPU, BPU zugeordnet werden. Die Befehlszuordnungseinheit kann auf einmal 8 Instruktionen aus dem Cache in eine interne Befehlspuffer-Queue (Instruction Queue) holen. In einem Takt werden die 4 vordersten dieser Instruktionen auf Abhängigkeiten untersucht. Prinzipiell (es gibt einige Einschränkungen) werden dann bis zu 3 Befehle aus der Queue auf BPU, IU und FPU verteilt, je nach dem, ob es sich um Verzweigungs-, Integer-, oder Gleitkommainstruktionen handelt. 45 Instruction Queue and Dispatch Logic 32 32 Sequencer Unit BPU 32 Reservation Station (2) IU 256 FPU 64 Instruction Fetch Unit MMU Tags 32 32 K Cache 32 256 Memory Queue and Bus Interface 32 Adressen 64 Daten Abbildung 20: PowerPC 601 (angelehnt an [Ungerer95]) Die „Instruction Fetch Unit“ (Befehlsbereitstellungseinheit) sorgt für die Bereitstellung der Befehle vom Cache in den Befehlspuffer, wobei sie die Adressen der Befehle von der BPU (bei Verzweigungsbefehlen) oder von der Sequencer-Unit (bei Unterbrechungen/Synchronisationsereignissen) erhält. Dabei können auch Sprungvorhersagetechniken verwendet werden (vgl. Kap. 3.2). Die Sequencer-Unit entlastet andere Einheiten bei der Arbeit und unterstützt beispielsweise die IU bei der Ausführung selten benutzter Befehle oder bei steuerintensiven Aufgaben und überwacht die Ausführungsreihenfolge der Befehle. Die IU hat eine 4-stufige Pipeline (vgl. Kap. 3.1) und implementiert arithmetische Operationen für IntegerBefehle. Außerdem findet hier die Adressberechnung für alle Lade- und Speicherbefehle statt. Die Reservation Station ist ein Befehlspuffer für 2 Befehle, der vor die FPUAusführungseinheit geschaltet ist. Durch diesen zusätzlichen Puffer kann die Dispatch Logic schon Befehle zuordnen, wenn die FPU für die Ausführung eines Befehls länger braucht oder wenn Operanden einer Instruktion noch nicht verfügbar sind. Die FPU verarbeitet 64-Bit Gleitpunktbefehle in einer 4-stufigen Pipeline. Die MMU übersetzt für Befehle und Daten die logischen Adressen in physikalische Adressen. Der Cache ist 32 KByte groß und enthält sowohl Daten als 46 auch Instruktionen. Auf weitere Konzepte der Speicherverwaltung geht das Kapitel 2.2 ein. Die Schnittstelle zur Außenwelt wird durch die Bus-Interface-Einheit gebildet, die eine Memory-Queue enthält. Speicheranforderungen, die in der Memory-Queue stehen, werden in Transaktionen der Busschnittstelle übersetzt. Auch das Programmiermodell des PowerPC601 ist von der Power-Architektur abgeleitet. Es enthält 32 allgemeine Register (GPR0-GPR31) in der IU, 32 Register in der FPU (FPR0-FPR31), die speziellen Register Link-Register (LR), Count-Register (CTR), Condition-Register (CR), Integer Exception Register (XER), Floating Point Status and Control Register (FPSCR), Multiplikationsregister (MQ), Real Time Clock Upper (RTCU), Real Time Clock Lower (RTCL). Diese Register stehen dem Programmierer zur Verfügung und werden auch User-Register genannt. Schließlich sei noch erwähnt, dass es noch etwa 60 weitere Register gibt, die für den Programmierer nicht sichtbar und nur im sog. Supervisor-Modus verwendbar sind (Details siehe [ObVossen00], [Staudt94]). 2.1.3 Zusammenwachsen von CISC und RISC Seit den 90er Jahren (vgl. Abb. 6) verschwimmen die Grenzen zwischen CISC und RISC, da Prozessoren vermehrt Konzepte aus beiden Philosophien verwenden. Die Prozessorhersteller, die früher reine CISC-Architekturen realisiert hatten, wurden von der zunehmenden Komplexität bei der Hardwareimplementierung erschlagen. Die ISAs wurden immer umfangreicher, der Dekodieraufwand immer größer und die Mikroprogrammierung immer aufwändiger, was zu vielen Fehlerquellen führte. Außerdem wurden Rechner nicht mehr in Assembler programmiert. Der Maschinencode wurde zunehmend durch optimierende Compiler erzeugt, die auf die Hardware abgestimmt waren (vgl. Kap. 6.1). Aus diesen Gründen haben sich CISC-Architekturen dahingehend entwickelt, dass nach außen hin CISC-Schnittstellen zur Verfügung stehen, aber intern ein nicht so aufwändiger RISC-Kern existiert. Dazwischen gibt es Stufen, die CISC-Befehle in das interne RISC-Format transformieren (vgl. Konzept der virtuellen Maschine aus Kap. 1.1 und [Märtin01]). RISC-Architekturen sind leichter zu parallelisieren und die Taktraten können stärker erhöht werden. Bei der P6-Architektur der Pentium-CPUs erfolgt beispielsweise eine Vordekodierung der CISC-Befehle in sog. „µOps“ (≠ 47 Mikroinstruktionen !). Dabei werden die Makrobefehle in eine Folge weniger komplexer 3-Adress-Befehle (µOps) umgewandelt. Im Instruktionscache, der dann als Trace-Cache (vgl. auch Kap. 6.2) bezeichnet wird, werden die bereits vordekodierten µOps gespeichert. Viele Compiler sind heutzutage in der Lage, für Pentium-Prozessoren optimierten Code zu erzeugen. Auf der Seite der RISC-Architekturen gibt es auch Veränderungen in CISCRichtung. In den Anfängen von RISC war die Anzahl der Befehle gering (typischerweise weniger als 150). Mit der Zeit hat die Anzahl der Befehle zugenommen (vgl. [BeiHag01]). Der Fokus bleibt aber nach wie vor auf der Einfachheit der Hardware. 2.2 Speicher Dieses Kapitel beschäftigt sich mit den Verbesserungsmöglichkeiten von Architekturen auf der Seite des Speichers, um eine schnellere Befehlsausführung zu unterstützen. Außerdem werden die Konzepte von Paging, Segmentierung und virtuellen Speicher kurz erklärt, damit die Funktion und Notwendigkeit von MMUs in Prozessoren verständlich wird. Im Laufe des Kapitels soll auch verdeutlicht werden, warum man zwischen logischen bzw. virtuellen und physikalischen Adressen unterscheidet. Ausgangspunkt der Betrachtung ist das memory-latency-Problem. Die CPU kann Befehle und Daten viel schneller verarbeiten, als der Speicher sie zur Verfügung stellen kann. Dies ist das Resultat von verschiedenen technischen und physikalischen Gegebenheiten. Man kann zwar kleinere und schnellere Speicher herstellen, jedoch verursachen die derzeitigen technologischen Prozesse sehr hohe Kosten. Aus diesen Gründen versucht man, die Mängel mit Hilfe der Architektur auszugleichen. Die Architekturkonzepte, die nachfolgend vorgestellt werden, sind stark von der gegenwärtigen Technologie beeinflusst (vgl. [Gwen95]). 2.2.1 Cache-Speicher Das sog. Lokalitätsprinzip erlaubt eine Optimierung der Zugriffszeiten durch die Architektur. Die Art und Weise der Programmierung (z.B. Unterprogramme mit 48 lokalen Variablen) wirkt sich auf Art und Häufigkeit der Speicherzugriffe aus. Dadurch ergibt sich eine örtliche Lokalität, bei der die Adresse einer Speicherreferenz oft in der Nähe der Adresse einer alten Speicherreferenz liegt. Es gibt auch eine zeitliche Lokalität, da die Zugriffe innerhalb eines Zeitintervalls in einem eng abgegrenzten Adressbereich stattfinden. Das sog. working set (vgl. [Denning80]) basiert auf dem Lokalitätsprinzip und besteht aus der Menge aller Adressen, die ein Programm innerhalb einer bestimmten Zeit t gebraucht hat. Adressen innerhalb des working sets werden oft referenziert. Basierend auf dieser Erkenntnis versucht man, das working set in einem kleinen, schnelleren aber auch teureren Zwischenspeicher zu halten. Da dieser Pufferspeicher für den Prozessor beim Speicherzugriff transparent ist, nennt man ihn Cache-Speicher (französisch: cacher = verstecken). Der Erwartungswert der Zugriffszeit Tavg aus Sicht des Prozessors ergibt sich aus: Tavg = p * TaccCache + (1-p) * TaccMem TaccCache Zugriffszeit auf Cache TaccMem Zugriffszeit auf Speicher p Wahrscheinlichkeit,dass die gesuchte Adresse im Cache ist Die Wahrscheinlichkeit p ist, wenn die Größe des Cache-Speichers dem working set entspricht, sehr hoch und kann durchaus 80-99% erreichen [HePa97]. Wegen TaccCache < TaccMem senkt der Einsatz eines Cache-Speichers die durchschnittliche Zugriffszeit Tavg. Wenn die Größe des Cache-Speichers wächst, nimmt gezwungenermaßen auch die Zugriffszeit zu. Die Zugriffszeit ist aber immer noch geringer als beim direkten Hauptspeicherzugriff. Deswegen haben sich Speicherhierarchiearchitekturen mit mehreren Stufen („levels“) entwickelt. Auf der untersten Stufe befinden sich die Register der CPU. 49 Prozessor L1 Cache T avg T accL1 L2 Cache Miss L1 T acc L2 Hauptspeicher Miss L2 T accSpeicher Abbildung 21: Darstellung einer Speicherhierarchie (angelehnt an [Dief99]) Speicherhierarchien dienen u.a. auch dafür, den sog. thrashing-Effekt (vgl. [SG99]) zu reduzieren. Dieser Effekt tritt dann auf, wenn das working set größer als der Cache-Speicher ist und deswegen der Aufwand bei der Cache-Verwaltung größer wird als der eigentliche Nutzen. Das führt zur Verlangsamung der Ausführung statt zur Beschleunigung. Im Zusammenhang mit Speicherhierarchien gibt es sog. Dual-IndependentBus-Architekturen [Dief99]. Bei einem Speicherzugriff greift einer der Busse auf den Cache zu, während der andere gleichzeitig auf den Speicher zugreift. Wenn die gesuchten Daten nicht im Cache waren (cache miss), so hat man durch das frühere Anstoßen beim Hauptspeicherzugriff Zeit gespart. Aus Performancegründen teilt man bei manchen Architekturen die CacheSpeicher einer Ebene auf, beispielsweise in Daten- und Instruktions-Cache, die über einzelne Busse angesprochen werden. Die Breite der Busse zwischen Prozessor und Cache ist oft ein Vielfaches des Prozessor-Wortbreite, beispielsweise ist die Busbreite zum L2-Cache oft 2-8 mal breiter als die Prozessor-Wortbreite (vgl. [Märtin01]). Auf Rechnerarchitekturebene hat die Speicherhierarchie aus technologischen Gründen noch mehr Stufen, die nach dem Hauptspeicher folgen. Die Zwischenstufen würden alle entfallen, wenn es billige Hauptspeicher gäbe, die einerseits genau so schnell arbeiten würden wie die CPU und andererseits die Daten dauerhaft speichern könnten. Als nächstes soll kurz gezeigt werden, welche Arten von Cache-Speicher es gibt und wie sie prinzipiell organisiert sind. Die Organisation von Cache-Speichern basiert auf folgendem Modell (Abb. 22 und [TanGo99]): 50 Eintrag StatusBits Tag Daten Cache-Line 0 1 2 n Abbildung 22: Cache-Speicher Der Hauptspeicher wird in Blöcke fester Größer unterteilt (sog. Cache-Lines). Im Cache befinden sich Kopien dieser Cache-Lines. Zu jeder Cache-Line existieren sog. Tags, welche die Ursprungsadresse der Daten enthalten. Außerdem gibt es noch Status-Bits, die Änderungen am Inhalt der Daten signalisieren und der Konsistenzerhaltung im Hauptspeicher dienen. Bei jedem Hauptspeicherzugriff wird überprüft, ob der gesuchte Inhalt bereits im Cache ist. Wenn ja (cache hit) werden die Daten aus dem Cache gelesen. Sind die Daten nicht im Cache (cache miss), dann wird eine Cache-Line aus dem Hauptspeicher gelesen, im Cache abgelegt und anschließend an den Prozessor weitergegeben. Dieser Vorgang kann Ersetzungsstrategien erfordern, falls der Cache komplett belegt ist. Eine typische Ersetzungsstrategie ist die Least Recently Used-Strategie (LRU). Dabei wird derjenige Eintrag ersetzt, auf den am längsten nicht zugegriffen wurde. Diese Strategie ist aber wegen der Suche nach Zeitstempeln aufwändig zu implementieren, weswegen weniger komplexe Varianten gebräuchlich sind, wie z.B. eine zufällige Ersetzung (vgl. [SGG99], [BeiHag01]). Die Abbildung des Hauptspeichers auf den kleineren Cache-Speicher kann auf mehrere Arten erfolgen (vgl. auch [BeiHag01]): • direkt: Die Adressen des Hauptspeichers werden modulo Cache-Größe auf den Cache abgebildet. Einem Hauptspeicherblock ist damit eine feste Cache-Line direkt zugeordnet. Kennt man die Speicheradresse, so gibt es nur eine Stelle im Cache, wo man danach 51 suchen muss. Problematisch ist allerdings, dass zu einer Cache-Line mehrere Hauptspeicherblöcke gehören können und der trashing-Effekt auftreten kann [Märtin01]. • vollassoziativ: Ein Hauptspeicher-Block kann auf einen beliebigen Cache-Eintrag abgebildet werden, so dass die obigen Konflikte vermieden werden. Um zu überprüfen, ob die Daten einer Speicheradresse im Cache vorhanden sind, wird durch die Hardware die Speicheradresse mit den Adressen aus allen Cache-Lines gleichzeitig verglichen (sog. assoziativer Vergleich). Diese Methode ist teuer und aufwändig zu realisieren. • n-fach-assoziativ: Soll als Kompromiss zwischen Flexibilität und Implementierbarkeit dienen. Pro Hauptspeichereintrag gibt es jeweils n Cache-Einträge, in denen ein Eintrag abgelegt werden kann. Dementsprechend müssen bei der Suche nach einem Eintrag im Cache auch maximal n Einträge geprüft werden. Als Beispiel hat die Intel P6-Architektur auf Level 1 jeweils einen getrennten Instruktions- und Daten-Cache. Beide haben eine Cache-Line-Größe von 32 Byte und sind 8 KByte groß. Der Instruktions-Cache ist 4-fach-assoziativ und der Daten-Cache 2-fach-assoziativ. Der Cache auf Level 2 hat eine Cache-Line-Größe von 32 Byte, ist insgesamt 256 KByte groß und 4-fach-assoziativ (Details siehe [Gwen95]). Schreiboperationen auf eine Cache-Line können zu Inkonsistenzen zwischen den Daten im Cache und denen im Hauptspeicher führen. Für die Aktualisierung gibt es folgende Strategien: • Write through: Sowohl die zugehörige Cache-Line Block im im Cache, Speicher als werden auch der aktualisiert („Durchschreiben“ auf die nächste Speicherebene). 52 • Write back: Es wird nur die Cache-Line im Cache aktualisiert. Die modifizierte Cache-Line wird erst dann in den Hauptspeicher zurückgeschrieben, wenn sie ersetzt wird. Wenn bei einem Ladezugriff die gesuchten Daten nicht im Cache sind, können Nicht-Blockierende-Cache-Speicher andere Cache-Zugriffe bedienen, während auf die Daten vom Speicher gewartet wird. Dieses sog. hit-under-miss wird beispielsweise in der Intel-P6-Architektur benutzt (vgl. [Gwen95]). Bei anderen Maßnahmen zur Steigerung der Performance lagert man die Cache-Tags in die darunter liegende Ebene der Speicherhierarchie aus, d.h. auf dem Prozessor befinden sich die L1-Cache-Tags und die L2-Cache-Tags im L1-Cache (vgl. [Dief99]). 2.2.2 Paging und Segmentierung Bei ungeschickter Verwaltung des Hauptspeichers kann es zur sog. externen Fragmentierung (vgl. [SGG99]) kommen, so dass der gesamte Speicher in viele kleine unzusammenhängende Teilbereiche zersplittert wird. Dies ist nicht erwünscht und wird durch folgende Verfahren vermieden. Die logische Sicht ist dabei die Sicht des Prozessors auf den Speicher. Die physikalische Sicht bezieht sich auf die Perspektive des Speichers, der lediglich feststellen kann, dass auf eine bestimmte physikalisch vorhandene Speicherzelle zugegriffen wird. physikalische Adresse logische Adresse CPU d f d Physikalischer Speicher p f Page-Table Abbildung 23: Paging (angelehnt an [SGG99]) 53 Beim Paging wird der logische Adressbereich des Hauptspeichers in Pages fester Größe und der physikalische Adressbereich in Frames gleicher Größe eingeteilt, um externe Fragmentierung zu vermeiden. Die Pages und Frames sind durchnummeriert. Eine sog. Page-Table ordnet jeder Page-Nummer eine bestimmte Frame-Nummer eines physikalischen Frames zu, wobei die Zuordnung nicht kontinuierlich sein muss. Bei der Adressierung einer logischen Speicheradresse muss die Nummer der Page p und der Offset d innerhalb der Page bekannt sein. Über die Page-Table wird die physikalische Anfangsadresse des Frames f ermittelt und der Offset dazuaddiert, was zur gesuchten Speicherzelle führt. Durch geschickte Wahl der Page-Größe kann die Adressübersetzung einer logischen Adresse in Page-Nummer und Page-Offset erleichtert werden. Ist die Größe des Adressraums 2m und die Größe einer Seite 2n, so ergeben die höherwertigen m-n Bits einer logischen Adresse die Nummer der Page und die niederwertigen n Bits den Offset. Das Paging-Verfahren kann zur sog. internen Fragmentierung führen (Speicher wird alloziert, der nicht gebraucht wird), wenn die Page-Größe zu groß gewählt wird. Bei zu vielen kleinen Pages steigt der Verwaltungsaufwand unverhältnismäßig. Die sog. Segmentierung erlaubt eine Aufteilung des Speichers nach inhaltlichen Kriterien. Der logische Adressraum wird in durchnummerierte Segmente mit jeweils einer Anfangs- und Endadresse unterteilt. Der Unterschied zum Paging ist, dass Segmente eine variable Länge haben können. Eine SegmentTabelle ordnet (ähnlich wie beim Paging) die logischen Segmente entsprechenden physikalischen Segmenten zu. Die Adressierung verläuft auch ähnlich. Eine Speicheradresse wird durch Segment-Nummer und Offset identifiziert. Da die Segmentierung zwar die Semantik der Programme berücksichtigt aber wieder zu externer Fragmentierung führt, wird ein Mischverfahren benutzt, welches Paging auf die einzelnen Segmente anwendet. Bei der Adressierung müssen Segment-Nummer, Page-Nummer innerhalb des Segments und Offset innerhalb der Page bekannt sein. Beispielsweise benutzte der Intel 386-Prozessor Segmentierung mit Paging für die Speicherverwaltung [SGG99]. Eine MMU führt die oben beschriebene Adressübersetzung durch und übernimmt die entsprechenden Verwaltungsfunktionen. 54 2.2.3 Translation Lookaside Buffer Die oben beschriebenen Page- bzw. die Segment-Tabellen können sehr groß werden. Da sie bei jedem Speicherzugriff benutzt werden, ist es sehr wichtig, dass man eine effiziente Hardwareimplementierung benutzt. Eine Implementierung als Register kommt bei großen Tabellen mit der derzeitigen Technologie aus Kostengründen nicht in Frage. Lagert man die Tabelle im Hauptspeicher, so muss für einen Hauptspeicherzugriff immer zweimal zugegriffen werden: einmal auf die Tabelle, um die physikalische Adresse zu ermitteln, und einmal beim eigentlichen Zugriff. Glücklicherweise kann das Lokalitätsprinzip der Speicherzugriffe ausgenutzt und Cache-Speicher eingesetzt werden (vgl. Kap. 2.2.1). Diese Cache-Speicher, die als Translation Lookaside Buffer (TLB) bezeichnet werden, merken sich bei jedem Speicherzugriff die zur Page-Nummer ermittelte Frame-Nummer. Wegen der Schnelligkeit des Cache-Speichers und der hohen Trefferwahrscheinlichkeit steigt die Zugriffszeit auf den Hauptspeicher nicht mehr auf das Doppelte an. Obwohl die Zugriffszeit noch größer ist als bei nur einem Zugriff, nimmt man diesen Nachteil wegen einer flexibleren Speicherverwaltung in Kauf. Details zur TLBs gibt es in [SGG99]. Die Translation Lookaside Buffer gehören auch zur MMU. Bei der Intel IA-32Architektur gibt es beispielsweise mehrere TLBs mit Größen zwischen 4KB und 4MB (siehe auch [Märtin01]). 2.2.4 Virtueller Speicher Beim virtuellen Speicher schließt die Betrachtung noch eine Stufe Sneu der Speicherhierarchie ein, die nach dem Hauptspeicher folgt. Die Größe des Hauptspeichers ist dabei kleiner als Sneu. Mit Hilfe von Paging kann man Verfahren implementieren, so dass auch Programme ausgeführt werden können, die größer als der Hauptspeicher sind und nicht am Stück hineinpassen (vgl. [SGG99]). Die zu den Pages aus dem Hauptspeicher gehörenden Frames befinden sich in Sneu. Das sog. Demand Paging verfolgt die Strategie, Frames nur dann aus Sneu in den Hauptspeicher zu laden, wenn sie bei einer Speicherreferenz gebraucht werden. Beim Versuch, auf eine nicht vorhandene Page zuzugreifen wird die 55 Programmausführung suspendiert (page fault), der benötigte Frame in den Speicher geladen und die Programmausführung an genau der gleichen Stelle wieder fortgesetzt. Sollte der Hauptspeicher beim Laden eines Frames komplett belegt sein, so wird mit verschiedenen Ersetzungsstrategien (z.B. LRU, vgl. [SGG99]) eine Page nach Sneu ausgelagert und an deren Stelle der neue Frame eingelagert. Beim Auslagern kann auch erst in einen Cache ausgelagert werden (victim cache). Eine MMU kann die hardwaremäßig unterstützen. beim virtuellen Speicher benötigte Verwaltung 56 3 Maßnahmen zur Performancesteigerung Dieses Kapitel behandelt Architekturkonzepte, die ausgehend von der vonNeumann-Architektur die Performance noch weiter steigern sollen. Die hier vorgestellten Maßnahmen konzentrieren sich im Wesentlichen darauf, Vorgänge bei der Ausführung von Befehlen zu parallelisieren. Bei Pipelines wird zeitliche Parallelität durch eine zeitlich überlappte Ausführung von Befehlen betont. Da in bestimmten Situationen Pipelines nicht voll ausgelastet sein können, versucht man durch Branch Prediction und Spekulation diesen Mangel zu beheben. Superskalare Architekturen sind um räumliche Parallelität durch mehrfachen Einsatz gleicher Komponenten erweitert, wie z.B. mehrere Pipelines. 3.1 Pipelines Der Ansatzpunkt bei der Performancesteigerung mit Pipelines ist der fetchdecode-execute-Zyklus (vgl. Kap. 1.2). Der Nachteil der von-Neumann-Architektur, dass zu einem Zeitpunkt nur eine der Phasen ausgeführt werden kann (ein Befehl muss den Zyklus komplett durchlaufen, bis der nächste dran ist), kann mit Hilfe von Pipelines ausgeglichen werden. Die Idee besteht darin, die Ausführung der Befehle in Phasen einzuteilen, wobei jede Phase eine eigene Verarbeitungseinheit hat. Die Verarbeitungseinheiten aller Phasen können gleichzeitig arbeiten. Man spricht deshalb von Phasenpipelining. Bei der Ausführung wird dabei ein Befehl von einer Verarbeitungsphase zur nächsten gereicht. Eine Pipeline kann als Analogie für ein Fließband bei der Fertigung eines Autos gesehen werden, während bei der Ausführung ohne Pipeline (nach von Neumann) die Fertigung eines neuen Autos erst dann beginnt, wenn ein anderes komplett fertiggestellt ist. S1 S2 S3 S4 S5 Instruktion abrufen Instruktion dekodieren Operanden abrufen Instruktion ausführen write-back Abbildung 24: Fünfstufige Pipeline (angelehnt an [TanGo99]) 57 Abb. 24 zeigt eine Pipeline mit 5 Stufen. Die Stufe S1 ruft einen Befehl aus dem Speicher ab und lagert ihn, bis er gebraucht wird, in einen Puffer. Die Stufe S2 dekodiert im nächsten Takt den Befehl und bestimmt den Befehlstyp und die Operanden, welche S3 aus dem Speicher (oder auch Register bzw. Cache, vgl. Kap. 1.5.3 und Kap. 2.2.1) abruft. Danach folgt im nächsten Takt die eigentliche Ausführung des Befehls durch eine ALU (vgl. Kap. 1.1) in S4, d.h. der Datenwegzyklus wird durchlaufen. Die letzte Stufe der Pipeline schreibt die Ergebnisse an den vorgesehenen Zielort (z.B. Register). Befehl S1: 1 S2: 2 3 4 5 1 2 3 4 1 2 3 1 2 S3: S4: S5: 1 1 2 3 4 5 Zeit Abbildung 25: Status jeder Stufe als Funktion der Zeit (angelehnt an [TanGo99]) Abb. 25 zeigt, warum mit einer Pipeline die Performance gesteigert werden kann. Zum Zeitpunkt 1 wird Befehl 1 abgerufen. Zum Zeitpunkt 2 wird Befehl 1 dekodiert. Gleichzeitig wird aber schon Befehl 2 abgerufen. Zum Zeitpunkt 3 werden für Befehl 1 die Operanden geholt, Befehl 2 dekodiert und Befehl 3 abgerufen. Nach dem Ausführen zum Zeitpunkt 4 werden zum Zeitpunkt 5 die Ergebnisse von Befehl 1 zurückgeschrieben, während die anderen Stufen an den darauf folgenden Befehlen arbeiten. Pipelines werden so konzipiert, dass alle Phasen in etwa die gleiche Zeit bei der Verarbeitung benötigen. Diese Zeit sei mit t bezeichnet. Man versucht dabei, die Länge eines Taktes an t anzugleichen (vgl. [BeiHag01]). Wenn eine Pipeline n Stufen hat, so braucht ein Befehl t*n Zeiteinheiten, bis er die Pipeline durchlaufen hat. Jeder Befehl in Abb. 25 braucht jeweils t*n Zeiteinheiten, bis er fertig verarbeitet ist. Aus Sicht des Rechners steigt aber die Anzahl der verarbeiteten 58 Befehle pro Zeiteinheit, da alle t Zeiteinheiten ein Befehl die Pipeline verlässt (bei voller Pipeline). Beim von-Neumann-Rechner würde nur alle t*n Zeiteinheiten ein Befehl fertig verarbeitet sein. Je nach Befehlsklasse können auch einzelne Phasen entfallen. Beim PowerPC gibt es beispielsweise 4-stufige-Pipelines (vgl. Kap. 2.1.2). Die Firma Intel hatte zum ersten Mal eine 5-stufige-Pipeline beim Intel486-Prozessor verwendet (vgl. [TanGo99], [Messmer00]). Eine besondere Form des Pipelining ist das sog. Superpipelining, bei dem die einzelnen Stufen der Pipeline noch feiner unterteilt werden und intern mit einem schnelleren Takt arbeiten. Die Länge einer Pipeline kann dabei insgesamt groß werden. Beispielsweise hat eine Pentium-IV-Pipeline 20 Stufen (vgl. [Märtin01]). Oft wird von Autoren Superpipelining gleichgesetzt mit dem Vorhandensein von mehr als 5 Stufen in einer Pipeline, wie [Tabak95] bemerkt. Die Möglichkeiten von Superpipelining sind durch exklusive Bus- und Speicherzugriffe, Kontrollfluss- und Datenflussabhängigkeiten (vgl. [BeiHag01]), sowie durch physikalische Aspekte (vgl. [Dief99]) begrenzt. Bei superskalaren Architekturen werden mehrere Pipelines benutzt, die teilweise auch gemeinsame Stufen haben. Die Organisation von superskalaren Pipelines wird in Kapitel 3.3.1 behandelt. 3.2 Branch Prediction und Spekulation Die Auslastung einer Pipeline kann u.U. durch Sprungbefehle sinken. Ein Sprungbefehl wird wie jeder andere Befehl durch die Stufen einer Pipeline weitergereicht. Erst in der letzten Phase (write-back) wird der Program Counter (PC, vgl. Kap. 1.2) aktualisiert, so dass erst dann das tatsächliche Sprungziel bekannt ist. Die Pipeline kann aber bis zum Zeitpunkt des Zurückschreibens des PC keine weiteren Befehle holen und verarbeiten, da nicht bekannt ist, welche Instruktionen als nächste im Programm folgen (control hazard, vgl. 3.3.1). Bei bedingten Sprüngen müssten deswegen bis zum Zeitpunkt der Aktualisierung des PC Wartezyklen eingelegt werden. Das führt zu erheblichen Performanceeinbußen, da in einem Programm häufig jeder fünfte Befehl ein bedingter Sprung ist (vgl. [Dief99]). 59 Andererseits könnte eine Pipeline bereits Befehle verarbeiten, die nach dem Sprungbefehl folgen (spekulative Ausführung). Diese werden aber u.U. nicht gebraucht, falls der Kontrollfluss anderswo hin verzweigt. Sollte das der Fall sein, so muss die Pipeline geleert (Pipeline Flush), die Verarbeitung rückgängig gemacht (Rollback) und die Pipeline mit den richtigen Befehlen wieder gefüllt werden. Die Kosten im Sinne von Verarbeitungsaufwand (mispredict penalties) sind bei einer falschen Sprungvorhersage hoch, insbesondere wenn die Pipeline viele Stufen hat (vgl. Superpipelining, Kap. 3.1). Beispielsweise können sich beim Alpha 21264Prozessor bereits 20 oder mehr Nachfolgebefehle in der Pipeline befinden, bevor klar wird, ob sie überhaupt benötigt werden (vgl. [Märtin01]). Die Performance könnte dadurch gesteigert werden, dass man die Pipeline mit den richtigen Befehlen füllt, noch bevor das Sprungziel bekannt ist. Da man aber nicht in die Zukunft schauen kann, werden Sprünge mit heuristischen Methoden vorausgesagt. Diese Methoden versuchen, die Wahrscheinlichkeit einer richtigen Sprungvorhersage zu maximieren und die einer falschen Sprungvorhersage zu minimieren. Bei statischer Sprungvorhersage enthält ein Befehlswort für Sprungbefehle noch zusätzliche Bits (static predictors), über die ein Compiler der Hardware mitteilen kann, ob ein Sprung voraussichtlich durchgeführt wird oder nicht. Beispielsweise wird bei einer for-Schleife fast immer ein Sprung an den Anfang der Schleife durchgeführt, was vom Compiler erkannt und entsprechend signalisiert wird. Lediglich beim Beenden der Schleife wäre die Sprungvorhersage falsch, was zum Flush der Pipeline führt. Die Performance wäre aber insgesamt gesteigert, weil der Rücksprung an den Anfang viel öfter vorkommt. Mit statischen Sprungvorhersagetechniken werden Trefferquoten von 65-85% erreicht (vgl. [Dief99]). Mit Hilfe dynamischer Sprungvorhersage kann die Trefferwahrscheinlichkeit noch weiter auf 98% erhöht werden [Dief99]. Eine einfache Methode für dynamische Sprungvorhersage ist die Branch History Table (BHT). Dabei handelt es sich um einen Cache-Speicher (vgl. Kap. 2.2.1), der die Adressen von Sprunginstruktionen und zugehörige History-Bits (dynamic predictors) enthält. Während Sprünge stattfinden, werden Cache und History-Bits aktualisiert. In einer ersten Variante gibt es nur ein Bit in der History, das für ein Sprungbefehl angibt, ob beim letzten Mal der Sprung durchgeführt wurde oder 60 nicht. Es wird wird angenommen, dass nun der Sprung in die gleiche Richtung führt wie beim letzten Mal. Bei falscher Vorhersage wird das History-Bit entsprechend geändert. Dieses Verfahren ist bei Schleifen oft erfolgreich, da häufig an deren Anfang gesprungen wird. Bei mehreren verschachtelten Schleifen wird aber beim Beenden einer inneren Schleife eine falsche Voraussage getroffen, was zum unnötigen Flush der Pipeline führt. kein Sprung Sprung Sprung 00 Sprung kein Sprung vorhergesagt kein Sprung kein Sprung 01 10 Zweites Mal kein Sprung vorhergesagt Ein zweites Mal Sprung vorhergesagt 11 Sprung vorhergesagt Sprung kein Sprung Abbildung 26: Sprungvorhersage mit 2 History-Bits. Quelle: [TanGo99] In einer zweiten Variante wird die Effizienz des obigen Verfahrens mit Hilfe eines zusätzlichen History-Bits gesteigert. Bei einem einmaligen Fehler wird die Vorhersage nicht sofort, sondern erst bei zweifach falscher Voraussage geändert (vgl. Abb. 26). Dadurch wird die Vorhersage bei verschachtelten Schleifen verbessert. Dieses Verfahren ist entsprechend auf mehrere History-Bits erweiterbar. Zu den obigen Methoden wurden eine Reihe von verbesserten Verfahren vorgestellt. Die Methode von Yeh/Patt [YehPatt91], erweitert beispielsweise den Automaten aus Abb. 26 und hat eine 4-Bit-Historie. Weitere Verbesserungsvorschläge sind z.B. Korrelations-Prädiktoren [PanRan92], GShare-Prädiktoren [McFarling93] und Zuverlässigkeits-Prädiktoren [Grun98]. Das Problem der BHT ist, dass im Falle einer Verzweigung zusätzliche Informationen besorgt werden müssen, was zu weiteren Verzögerungen führt. Deswegen wird in einem sog. Branch Target Buffer (BTB) zusätzlich zur Sprunghistorie der Verzweigungstyp (bedingt, unbedingt), die zuletzt verwendete 61 Sprungzieladresse (Taken Address) und ggf. die sequenzielle Nachfolgeadresse des Sprungbefehls (Fall-through Address) gespeichert. Wenn nur Sprungzieladressen, aber keine History-Informationen gespeichert werden, so spricht man von einem Branch Target Address Cache (BTAC). An Stelle eines BTAC verwenden machen Prozessoren einen sog. Branch Target Instruction Cache (BTIC), der statt der Sprungzieladressen einige Instruktionen nach dem vorausgesagten Sprungziel enthält, um die Pipeline zu füllen. Für Rücksprungadressen von Unterprogrammaufrufen wird manchmal auch ein Return Address Stack (RAS) implementiert. Bei der Intel P6-Architektur hat beispielsweise der BTB 512 Einträge und ist als 4-fach-assoziativer Cache organisiert [Gwen95]. Für die Sprungvorhersage wird die Methode von [YehPatt91] verwendet. 3.3 Superskalare Architekturen Dieses Kapitel wird näher auf Performancesteigerungen durch superskalare Techniken eingehen, wobei verschiedene Architekturelemente aus den bisherigen Kapiteln aufgegriffen werden. Ein superskalarer Prozessor ist ein Prozessor, der mit Hilfe entsprechender interner Einheiten mehr als einen Befehl pro Takt ausführen kann (CPI < 1) [BeiHag01]. Diese Forderung bedeutet, dass intern zwei oder mehr Befehle in echter Nebenläufigkeit zueinander verarbeitet werden müssen. Die Restriktion der Befehlssequentialität bei von-Neumann-Rechnern wird aufgrund von Performanceforderungen zu Ergebnissequentialität gelockert, d.h. es wird nicht mehr festgelegt, in welcher genauen Reihenfolge etwas berechnet wird, sondern nur wie etwas berechnet wird. Die berechneten Befehle eines Programms müssen dabei denjenigen entsprechen, die von einem Prozessor mit Instruktionssequentialität berechnet werden. Der Aufbau eines superskalaren Prozessors (vgl. [Ungerer01]) ist in Abb. 27 dargestellt. 62 Instruction-Cache BHT RAS Branch Unit MMU BTAC Instruction Fetch Unit Instruction Decode and Register Rename Unit Instruction Buffer Instruction Issue Unit Reorder Buffer Load/ Store Unit(s) FloatingPoint Unit(s) Integer Unit(s) MultimediaUnit(s) Retire Unit FloatingPoint Registers General Purpose Registers MultimediaRegisters Rename Registers Bus Interface Unit MMU Data-Cache Abbildung 27: Komponenten eines superskalaren Prozessors. Quelle: [Ungerer01] Ein superskalarer Prozessor hat eine Instruktionsabrufeinheit (InstructionFetch-Unit), eine Dekodiereinheit (Instruction Decode Unit) mit Registerumbenennung (vgl. Kap. 3.3.2), eine Befehlszuordnungseinheit (Instruction Issue Unit), mehrere Ausführungseinheiten (Floating Point Units, Integer Units, Multimedia Units) und eine Rückordnungseinheit (Retire Unit, vgl. Kap. 3.3.2). Die Zahl der Ausführungseinheiten kann bei verschiedenen Prozessoren variieren, es muss aber wegen der Nebenläufigkeitsforderung mindestens zwei geben, die parallel zueinander arbeiten können [Ungerer95]. Daten- und Instruktions-Cache werden aus Performancegründen getrennt und sind über die Busschnittstelle (Bus Interface Unit) mit dem Hautpspeicher (bzw. einem externen Cache-Speicher) verbunden. Es gibt noch interne Puffer, wie den Instruction Buffer und den Reorder Buffer, dessen Zusammenhang mit out-of-orderexecution noch erläutert wird (vgl. Kap. 3.3.2). 63 Die Load-Store-Unit lädt Werte aus dem Daten-Cache in die Register (Floating Point Register, General Purpose Register, Multimedia Register) oder schreibt berechnete Werte zurück in den Daten-Cache. Die MMU unterstützt die Adressumsetzung von logischen in physikalische Adressen (vgl. Kap. 2.2.2). Bei Cache-Fehlzugriffen wird eine neue Cache-Line (vgl. Kap. 2.2.1) über die Bus Interface Unit aus dem Speicher geladen. Es können eine oder mehrere Integer-Units vorhanden sein, die arithmetische und logische Befehle auf den General-Purpose-Registern ausführen können. Diese Einheiten können einstufig oder pipelineartig implementiert sein. Floating-Point-Units sind meist pipelineartig organisiert und speziell für Gleitkommaoperationen nach dem IEEE754-Standard zuständig. Gleitkomma- operationen werden ausschließlich auf Floating-Point-Registern angewendet. Eine oder mehrere Multimedia-Units führen auf den Multimedia-Registern Befehle nach dem SIMD-Prinzip (vgl. Kap. 1.5.1) aus, die meist maskierender oder konvertierender Art sind. Dabei wird der selbe Befehl auf mehrere Daten simultan angewendet. Beispielsweise kann eine Farbänderung für mehrere Pixel gleichzeitig durchgeführt werden (Details siehe Kap. 3.3.4). Die Befehlszuordnung zu den Ausführungseinheiten geschieht dynamisch zur Laufzeit und wird durch die Befehlszuordnungseinheit (Instruction Issue Unit, manchmal auch als Instruction Dispatch Unit bezeichnet) durchgeführt. Ein Prozessor wird n-fach-superskalar genannt, falls die Befehlszuordnungseinheit pro Takt n Befehle zuordnen kann [Ungerer95]. Die Branch Unit überwacht die Ausführung von Sprungbefehlen, holt mittels einer dynamischen Sprungsvorhersagetechnik die nach einem Sprungbefehl vermuteten Instruktionen aus dem Speicher und führt sie spekulativ aus. Hierfür benutzt sie einen Branch Target Address Cache (BTAC), eine Branch History Table (BHT) und einen Return Address Stack (RAS), deren Funktion im Zusammenhang mit Sprungvorhersagetechniken im Kapitel 3.2 ausführlich erläutert wurde. Die Branch Unit ist außerdem für die Aktualisierung der oben erwähnten Sprungvorhersagetabellen zuständig. Im Falle einer Fehlspekulation muss sie auch die falsch ausgeführten Befehle wieder rückgängig machen. 64 3.3.1 Superskalare Pipelines Bei superskalaren Architekturen wird räumliche Parallelität durch den Einsatz mehrerer Pipelines (Kap. 3.1) bei der Verarbeitung betont. S1 S2 S3 S4 S5 Instruktion dekodieren Operanden abrufen Instruktion ausführen write-back Instruktion dekodieren Operanden abrufen Instruktion ausführen write-back Instruktion abrufen Abbildung 28: Fünfstufige Pipelines mit gemeinsamer Instruktionsabrufeinheit. Quelle: [TanGo99] In Abb. 28 sind 2 Pipelines mit gemeinsamer Instruktionsabrufeinheit dargestellt. Die Instruktionsabrufeinheit ruft jeweils 2 Befehle ab und ordnet jeden Befehl einer Pipeline zu. Da jede Pipeline eine Ausführungseinheit mit eigener ALU hat, können Befehle parallel ausgeführt werden. Bei dieser Architektur können Konflikte (sog. Hazards) bei der Nutzung der Ressourcen, bei Daten- und Kontrollflussabhängigkeiten entstehen. Hazards werden am Ende des Kapitels genauer betrachtet. Der Intel Pentium besitzt 2 fünfstellige Pipelines, die ähnlich wie in Abb. 28 aufgebaut sind. Allerdings kann eine der Pipelines nicht alle Befehle, sondern nur einfache Integer-Instruktionen ausführen. Anhand komplexer Regeln überprüft eine Paarungslogik, ob zwei Instruktionen parallel ausgeführt werden können. Wenn das nicht möglich ist, wird nur eine der Instruktionen ausgeführt und die andere mit der nächsten gepaart. Details sind in [Messmer00] zu finden. Compiler können optimierten Code so generieren, dass besonders viele Befehle gepaart werden können (vgl. Kap. 6.1). 65 S3 Reservation Station (Operanden abrufen) Reservation Station S1 Instruktion abrufen S2.1 S2.2 Instruktion dekodieren Issue (Operanden abrufen) Reservation Station (Operanden abrufen) Reservation Station (Operanden abrufen) Reservation Station (Operanden abrufen) S4 ALU ALU S5 Laden retire and write-back Speichern Fließkomma Abbildung 29: Superskalare Pipeline mit 5 Funktionseinheiten (angelehnt an [TanGo99] und [Ungerer01]) Bei mehr als 2 Pipelines werden in der Praxis nicht mehr alle Stufen der Pipelines einzeln implementiert, da dies zur unnötigen Vervielfachung der Hardware führt (vgl. [TanGo99]). Die Architektur wird dabei so verändert, dass zwar nur mit einer Pipeline gearbeitet wird, diese aber mehrere Funktionseinheiten für die Ausführungsphase zur Verfügung hat (Abb. 29). Eine Instruction Issue Unit ordnet dabei Befehle den Ausführungseinheiten zu. In der Stufe S4 kann es durchaus auch mehrere ALUs geben. Realisiert werden solche Pipelines dann, wenn in der Stufe S4 die Befehle wesentlich langsamer verarbeitet werden als sie von den Stufen S1 bis S2.2 bereitgestellt werden können. Die Stufe S3 wird durch Reservation Stations (vgl. Kap. 2.1.2) realisiert. In diesen Puffern können Instruktionen auf ihre Operanden warten, bevor sie in S4 verarbeitet werden. Die Reservation Stations können einerseits dezentral für jede Funktionseinheit vorhanden sein. Andererseits kann es zentrale Reservation Stations geben, die gleichzeitig mehreren Funktionseinheiten dienen [Dief99]. Ein weiterer Vorteil der Reservation Stations ist, dass S2.2 schon Befehle zuordnen kann, während S4 noch arbeitet (vgl. [Ungerer95]). Eine superskalare Pipeline kann u.U. Befehle außerhalb ihrer Programmreihenfolge ausführen (vgl. Kap. 3.3.2) und gliedert sich dann in 3 Abschnitte [Ungerer01]: 66 • Die Stufen S1 und S2.1 bilden eine in-order-Sektion. Wenn die Befehle von S2.2 in Programmreihenfolge zugewiesen werden, gehört sie auch noch zur in-order-Sektion, ansonsten zur darauf folgenden out-of-order-Sektion. • Die Ausführungsstufen S3 und S4 bilden eine out-of-order-Sektion. Hier werden Resultate ggf. außerhalb der Programmreihenfolge erzeugt. • In der danach folgenden in-order-Sektion S5 werden die Resultate gemäß der Programmreihenfolge zurückgeordnet und zurückgeschrieben. Eine Architektur ähnlich Abb. 26 ist beispielsweise beim Pentium II anzutreffen (vgl. Kap. 3.3.2, [TanGo99]). Oft kann man die erhoffte Performancesteigerung durch Pipelines nicht realisieren, da Datenabhängigkeiten (data hazards), Änderungen im Kontrollfluss (control hazards), Ressourcenkonflikte der Hardware (structural hazards) die unabhängige Ausführung von aufeinanderfolgenden Befehlen verhindern oder die Pipeline zum Stillstand bringen (pipeline stall). Bei der Konzeption von Pipelines halten manche Architekturen deshalb die Zahl der Phasen klein, um diese Abhängigkeiten besser behandeln zu können (vgl. [BeiHag01]). Bei einem data hazard (vgl. auch [HePa97]) geht es um Timingaspekte, wobei folgende Fälle unterschieden werden können: • Read-After-Write-Hazard (RAW): Bei dieser Abhängigkeit versucht ein Befehl B2 Daten aus einem Register zu lesen, bevor sie von einem anderen Befehl B1 geschrieben werden. Deswegen müssen Wartezyklen eingefügt werden, bis B1 fertig ist. Diese Hazards treten häufig auf und müssen bei der Konzeption einer Architektur berücksichtigt werden. Teilweise kann man die Anzahl der Wartezyklen durch data forwarding verringern. Dabei kann B1 nach seiner execute-Phase (und noch vor der write-back-Phase) schon Zwischenergebnisse an die wartende Operanden-Abruf-Phase des Befehls B2 übergeben, der bis zu dem Zeitpunkt schon geholt und dekodiert wurde. Für data forwarding werden im Prozessor zusätzliche Datenpfade 67 benötigt. • Write-After-Write-Hazard (WAW): Hierbei wird gefordert, dass die Reihenfolge der Schreiboperationen entsprechend dem sequenziellen Befehlsstrom eingehalten wird. Diese Abhängigkeit kommt bei Pipelines vor, bei denen die Ausführung verschiedener Befehlstypen unterschiedlich lange dauert (z.B. Integer vs. Floating Point-Befehle) und mehrere write-back-Stufen existieren (vgl. Abb. 25). Diese Hazards können mit Hilfe von Compilern verhindert werden. • Write-After-Read-Hazard (WAR): Ein Schreibzugriff wird auf einem Register durchgeführt, dessen Inhalt zuvor von einem anderen Befehl ausgelesen werden soll. Solche Abhängigkeiten können bei paralleler oder bei Befehlsausführung außerhalb der Programmfolge auftreten. Control hazards treten häufiger auf als data hazards (vgl. [BeiHag01]). Das Problem dieser Hazards ist, dass ein Sprungbefehl den Program Counter erst in der letzten Phase (write-back) der Pipeline aktualisiert. Die Pipeline kann aber bis zum Zeitpunkt des Zurückschreibens keine weiteren Befehle holen und verarbeiten, da nicht bekannt ist, welche Instruktionen als nächste im Programm folgen. Bei bedingten Sprüngen müssen bis zum Zeitpunkt der Aktualisierung Wartezyklen eingelegt werden. Zur Reduktion dieser Hazards gibt es beispielsweise folgende Möglichkeiten: • Sprünge in der Befehlsholstufe vorhersagen (vgl. Kap. 3.2) • Berechnung des Sprungziels bereits in der Dekodierphase durchführen. Dafür wird für diese Phase ein zusätzlicher Addierer bereitgestellt (weitere Details dazu und zu Control Hazards siehe [HePa96] und [BeiHag01]). Struktur-Hazards treten auf, wenn einige der Funktionseinheiten nicht vollständig Pipeline-implementiert sind und nur eine exklusive Nutzung möglich ist. 68 Es entstehen Ressourcenkonflikte aufgrund einer nicht ausreichender Duplizierung der Hardware. Bei Speicherarchitekturen ist deshalb oft eine Harvard-Architektur (vgl. Kap. 1.1) zu finden. Beispielsweise werden Instruktions- und Datencache (vgl. Abb. 11 und Kap. 2.2.1) oft getrennt. Weitere Details sind in [HePa97] und [Ungerer95] zu finden. 3.3.2 Out-of-order-Architekturen Dieser Architekturtyp, bei dem Instruktionen auch außerhalb der sequenziellen Programmreihenfolge ausgeführt werden können, hat sich bei Superskalararchitekturen immer mehr durchgesetzt [Märtin01]. Eine out-of-orderArchitektur basiert auf der vorgestellten Architektur eines superskalaren Prozessors. Sie beinhaltet eine Fetch/Decode–Unit (zusammen auch als InstructionUnit bezeichnet), eine Dispatch-Unit, mehrere nebenläufig arbeitende ExecutionUnits und eine Retirement-Unit (auch als Commit- oder Completion-Unit bezeichnet). Das generelle Prinzip ist in Abb. 30 skizziert. Diese Architektur ist nur dann sinnvoll, wenn der Aufwand bei der Wiederherstellung der Programmreihenfolge geringer ist als der bei der out-of-order-Execution und damit eine Zeitersparnis erreicht wird. Programm instruction fetch/decode &branch prediction instruction dispatch execution reorder & commit Abbildung 30: Skizze zur out-of-order-Ausführung (angelehnt an [SmSohi95]) 69 Die Fetch-Unit holt eine feste oder variable Anzahl von Befehlen vom CodeCache in einen Puffer. Die Decode-Unit, die häufig mehrere parallel arbeitende Dekodierer enthält, greift auf diesen Puffer zu und versucht in einem Takt mehrere Befehle zu dekodieren, wobei aber die Programmreihenfolge eingehalten wird (in-order). Beim Erkennen eines Sprungbefehls während der Dekodierung werden mit Hilfe einer Branch Unit die Nachfolgebefehle des Sprungs bereits in den Code-Cache geladen. Die Befehle gelangen nach ihrer Dekodierung in die Dispatch-Unit. Diese prüft erst auf eventuell vorhandene Datenabhängigkeiten und ob genug Platz im Ergebnispuffer (Reorder Buffer, ROB, machmal auch Instruction Pool genannt) der Retirement-Unit vorhanden ist, um die Ergebnisse aufzunehmen. Erst dann ordnet sie die Befehle ggf. out-of-order einer der Execution-Units zur Verarbeitung zu. Für jeden zugeteilten Befehl wird ein Eintrag in einer Hardware-Tabelle (sog. Scoreboard) erzeugt, die den Bearbeitungszustand eines bereits dekodierten Befehls festhält. Sie dient u.a. dazu, die Ergebnisse später im ROB wieder nach der Programmreihenfolge ordnen zu können. Details siehe [Märtin01]. Bei der Ausführung in einer Execution-Unit können scheinbare Datenabhängigkeiten (vgl. data hazards, Kap. 3.3.1, [Ungerer01]) auftreten, die durch die dynamische Veränderung der Befehlsreihenfolge bei der Ausführung entstehen. Um Pipeline-Stalls (vgl. Kap. 3.1) durch Datenabhängigkeiten zu verhindern, werden die logisch vorhandenen ISA-Register über eine Register-Renaming-Tabelle auf zusätzlich vorhandene physikalische Register (sog. Schattenregister) abgebildet (Register Renaming, vgl. [BeiHag01]). Die Anzahl der Schattenregister übersteigt dabei die der ISA-Register. Durch diese Duplizierung der Hardware sollen die WAR und WAW -Hazards (vgl. Kap. 3.1) vermieden werden. Operationen in der ExecutionPhase werden dann ausschließlich auf den Schattenregistern durchgeführt. Anschließend landen die Ergebnisse ggf. out-of-order im ROB. Die Retirement-Unit schreibt den Inhalt der Ergebnis-Schattenregister nach verschiedenen Gültigkeits-Checks in die ISA-Register. Es wird eine in-ordercompletion sichergestellt, d.h. das Ergebnis der Ausführung wird mit dem Ergebnis einer sequenziellen Ausführung identisch sein. 70 Ergebnis 1 t=4 Ergebnis 2 t=1 Ergbenis 3 Ergebnis 4 t=3 t=2 commit Abbildung 31: Reorder Buffer der Retirement Unit Abb. 31 zeigt, wie eine Retirement Unit die zu unterschiedlichen Zeiten outof-order angekommenen Befehle in einen Reorder Buffer ablegt. Die Retirement Unit erkennt, wann die logisch korrekte Reihenfolge vorliegt und die Befehle weggeschrieben werden können. Als Beispiel für eine out-of-order-Architektur soll die 3-fach superskalare Intel Pentium-III-Architektur dienen (Abb. 32), die auf der Intel-P6-Architektur basiert [Märtin01], [Intel99b]. Bus Interface Unit Instr. Fetch Unit Instr. Decode Unit Instruction Decoder Simple Instruction Decoder Simple Instruction Decoder Next IP Unit Complex Instruction Decoder Memory Reorder Buffer Branch Target Buffer µOp Instruction Sequencer Register Alias Table Data Cache Unit (L1) Retirement Unit Retirement Register File (IA-Registers) Reorder Buffer (Instr. Pool) Reservation Station SSE Unit MMX Unit FPU Unit Integer Unit Integer Unit Memory Interface Unit Internal Data-Results Buses Abbildung 32: Pentium-II-Implementierung der P6-Mikroarchitektur (nach [Intel99b], [Märtin01]) 71 Wie schon in Kap. 2.1.3 erwähnt wurde, übersetzen Pentium-Prozessoren nach der P6-Architektur intern die CISC-Befehle der ISA erst in RISC-ähnliche 3-AdressBefehle (von Intel „µOps“ genannt, ≠ Mikrooperationen), die ein Load/Store-Modell verwenden (vgl. 1.5.3). Nach Holen, Sprungvorhersage und Dekodierung der CISCBefehle in 3 oder mehr µOps werden diese in-order an die Register Alias Table (RAT) weitergegeben, wo sie jeweils freien internen Registern zugeordnet werden. Es stehen 40 interne Register für das Register Renaming zur Verfügung, in die später auch die Ergebnisse geschrieben werden. Nach dieser Zuordnung gelangen die µOps erst in den Reorder Buffer (Instruction Pool), um weiterverarbeitet zu werden. Im Reorder Buffer können bis zu 40 µOps gleichzeitig verwaltet werden. In manchen Fällen können µOps ihre Operanden auch aus dem Reorder Buffer holen. Für alle Funktionseinheiten steht eine zentrale Reservation Station zur Verfügung, über die der out-of-order-Kern des Prozessors die µOps den freien Funktionseinheiten (SSE Unit, MMX Unit, FPU Unit, 2 Integer Units, Memory Interface Unit) zuteilt. Auf SSE und MMX geht das Kapitel 3.3.4 ein. Die von den Funktionseinheiten bearbeiteten µOps werden als bearbeitet markiert und wieder in den Reorder Buffer gespeichert. Die Retirement-Einheit überprüft dann, welche Instruktionen bereits ohne Datenabhängigkeiten fertig ausgeführt sind und stellt die Programmreihenfolge wieder her. In einem Taktzyklus entfernt sie dann maximal 3 µOps, die in der logisch korrekten Reihenfolge vorliegen und schreibt die Ergebnisse in die Register. Der Memory Reorder Buffer sorgt dafür, dass eventuelle Speicheroperationen in der logisch korrekten Reihenfolge stattfinden. Weitere Details sind in [Märtin01] und [Intel99b] zu finden. 3.3.3 VLIW/EPIC-Architekturen Eine weitere Architektur, die hier vorgestellt werden soll, ist die in der Mitte der 80er Jahre entstandene VLIW-Architektur (Very Long Instruction Word) [HePa96]. Diese Architektur basiert auf der Idee, dass ein Compiler eine bestimmte Anzahl voneinander unabhängiger und parallel auszuführender Befehle in ein Instruktionswort fester Länge packt. Der Vorgang der Parallelisierung wird von der Hardware in den Compiler verlagert, was dazu führt, dass die Komplexität der Hardware abnimmt und die des Compilers zunimmt. Wegen der geringen Hardware- 72 flexibilität und den hohen Anforderungen an die Compilertechnologie haben sich reine VLIW -Architekturen nicht durchgesetzt [Märtin01]. Die VLIW -Architektur enthält wie bei superskalaren Architekturen eine bestimmte Anzahl von nebenläufig arbeitenden Funktionseinheiten, wobei aber keine out-of-order-Ausführung stattfindet. Die in einem VLIW -Wort enthaltenen Befehle werden gleichzeitig geholt, dekodiert, ausgeführt und zurückgeschrieben. Sollten Befehle nicht parallel ausführbar sein, so ist im VLIW -Wort nur eine Instruktion enthalten. Die restlichen Stellen sind vom Compiler durch NOPs (no operation, kein Befehl) gefüllt. Obwohl beim Compilieren das gesamte Programm bekannt ist und global optimiert werden kann, ist durch die feste Zuordnung von Instruktionen zu einem VLIW -Wort die Optimierung nur statischer Art. Beispielsweise kann bei eventuellen Fehlzugriffen auf einen Cache-Speicher zur Laufzeit nicht so flexibel wie bei dynamischer Befehlszuordnung reagiert werden. VLIW -Architekturen sind manchmal bei Signalprozessoren (vgl. Kap. 4.2) zu finden [Ungerer01]. Einige der VLIW -Prinzipien sind in der von Intel und HP gemeinsam entwickelten IA-64-Architektur zu finden [CrHuck97]. Das Befehlsformat des IA-64Befehlssatzes wurde als EPIC (Explicit Parallel Instruction Computing) bezeichnet und ähnelt einem Dreibefehls-VLIW -Format. Ein EPIC-Wort ist flexibler als ein VLIW -Wort und enthält zusätzliche Informationen in Form sogenannter TemplateBits, die voneinander unabhängige Instruktionen markieren. Die Template-Bits beziehen sich sowohl auf Instruktionen innerhalb eines EPIC-Worts, als auch auf benachbarte EPIC-Wörter. Mit dem EPIC-Format können auch VLIW -Formate mit variabler Wortlänge realisiert werden. Details sind in [Ungerer01] und [Märtin01] zu finden. Zusätzlich zu den in Kapitel 3.2 behandelten Sprungvorhersagemethoden mit spekulativer Ausführung verwendet die IA-64-Architektur die sog. Prädikation (Predication). Darunter versteht man die Ausführung eines Befehls in Abhängigkeit eines zugeordneten Prädikats [Intel00b]. Die Ergebnisse eines Befehls werden verworfen oder der Befehl nicht ausgeführt, falls das zugehörige Prädikat false ist, ansonsten werden sie als gültig anerkannt. Der Sinn besteht darin, etwa beim bedingten Verzweigen Verzweigungen des auszuführen Kontrollflusses obwohl die (z.B. Bedingung switch-Anweisung) noch alle nicht vollständig ausgewertet ist. Nach der Auswertung der Bedingung werden die Prädikate so 73 geändert, dass nur die richtigen Ergebnisse beibehalten werden (vgl. Beispiel in Kap. 6.1). 3.3.4 Vektor-Architekturen Dieses Kapitel erläutert die Möglichkeiten einer Performancesteigerung mittels Vektorarchitekturen. Das grundlegende Prinzip von Vektorarchitekturen basiert auf Parallelismus auf Datenebene. Ein Befehl wird in SIMD-ähnlicher Anwendung (vgl. Kap. 1.5.1) auf mehrere linear angeordnete Zahlenfelder gleichzeitig angewendet. Eine Menge solcher Zahlenfelder bildet dabei einen Vektor. Eingabevektoren Vektor-ALU Abbildung 33: Vektor-ALU (nach [TanGo99]) Die Abb. 33 zeigt eine mögliche Architektur einer Vektor-Maschine. Zwei Vektoren, die jeweils aus n Elementen bestehen und sich in Vektor-Registern befinden, werden mit Hilfe einer Vektor-ALU verarbeitet. Die Vektor-ALU besteht aus n ALUs (manchmal auch Verarbeitungseinheiten oder Processing Elements genannt), die die einzelnen Elemente eines Vektors parallel manipulieren. Es gibt allerdings nur ein Steuerwerk, welches den aktuell durchzuführenden Befehl durch 74 Instruction Broadcasting an die einzelnen ALUs weiterleitet. Beispielsweise könnten bei einer Vektoraddition die i-ten Elemente von zwei Eingabevektoren für alle i gleichzeitig addiert und in das i-te Element eines Ausgabevektors abgelegt werden. Das Ergebnis, welches wiederum ein Vektor ist, kann in den Speicher oder zurück in ein Vektor-Register geschrieben werden. Vektor-ALUs müssen nicht immer 2 Vektoren verrechnen. Sie können auch skalare Operationen gleichzeitig auf alle Elemente eines Vektors anwenden. Solche Architekturen sind insbesondere für Anwendungen geeignet, wo viele Zahlen verarbeitet werden („number crunching“). Häufig wird die Organisation aus Abb. 33 um Pipelining (vgl. Kap. 3.1) erweitert. Die Verarbeitung von Befehlen auf Gleitkomma-Zahlen Exponenten einstellen, lässt sich Operation in Phasen ausführen, einteilen (Operanden Ergebnis normalisieren, holen, vgl. [TanGo99]). Um die Performance durch zeitliche Überlappung der Phasen zu steigern, wird sog. arithmetisches Pipelining auf einer höheren Ebene als der Maschinenbefehlsebene eingesetzt. Als Beispiel sei der Rechner Cray-1 der Firma Cray Research (1976) erwähnt, der eine Vektorarchitektur mit parallel arbeitenden, pipelineartig organisierten Funktionseinheiten implementiert. Weitere Details zu Vektorarchitekturen und zu Cray-1 sind in [ObVossen00] ausführlich nachzulesen. Vektor-Architekturen haben sich bei Funktionseinheiten von Mikroprozessoren durchgesetzt, welche die Verarbeitung von Multimedia-Daten (Bild-, Audio-, Video-Daten) unterstützen (auch Grafikkarten). In diesen Anwendungsgebieten muss häufig der gleiche Befehl auf feldartige Datenstrukturen angewendet werden. Häufig auftretende Befehle sind arithmetischer, maskierender, auswählender, umordnender oder konvertierender Art. Beispielsweise kann eine Farbänderung auf mehrere Pixel simultan durchgeführt werden (siehe auch Abb. 34). Durch hardwaremäßige erzielt. SIMD-Unterstützung werden erhebliche Performancesteigerungen 75 x3 x2 x1 x0 y3 y2 y1 y0 f f f f f(x3,y3) f(x2,y2) f(x1,y1) f(x0,y0) Abbildung 34: SIMD-Betrieb bei MMX (nach [Märtin01]) Verschiedene Firmen haben für die SIMD-Verarbeitung von Multimedia-Daten Synonyme entwickelt: MMX (Multi Media eXtension, Intel), 3DNOW! (AMD), AltiVec (Motorola). Dabei gibt es in den jeweiligen Mikroprozessoren Funktionseinheiten, die solche Erweiterungen implementieren. Die Firma Intel hat ab dem Pentium III mit den Streaming SIMD Extensions (SSE) sogar noch eine zusätzliche Erweiterung für SIMD-Gleitkommaverarbeitung entwickelt [Intel99b]. Weitere Details zu Vektorarchitekturen aktueller Prozessoren finden sich in [Bleul00] und [Märtin01]. 76 4 Spezielle Architekturen In den vorherigen Kapiteln wurden General-Purpose-Architekturen von Prozessoren dargestellt, die möglichst universell einsetzbar waren. Aus verschieden Gründen (z.B. Kosten, Performance) sind Special-Purpose-Architekturen für spezifische Anwendungsbereiche entstanden. Zwei wichtige Special-PurposeArchitekturen sollen kurz vorgestellt werden. Mikrocontroller konzentrieren sich auf die Integration möglichst vieler Komponenten auf einem Chip (CPU mit der gesamten Peripherie), wobei geringe Kosten und weniger eine hohe Performance im Vordergrund stehen. Im Gegensatz dazu sind DSP-Architekturen auf die Steigerung der Performance speziell bei der Signalverarbeitung ausgerichtet. 4.1 Mikrocontroller Bei Mikrocontrollern handelt es sich um komplette Computersysteme auf einem Chip. Ziel ist nicht eine hohe Verarbeitungsleistung, sondern die größtmögliche funktionelle Integration von Mikrocontrollerkern (Core), Speicher, Peripheriekomponenten und Interruptsteuerung zu geringen Kosten [BeiHag01]. Wegen ihrer Programmierbarkeit sind Mikrocontroller universell einsetzbar. Häufig sind sie in Systemen wie PDAs (Personal Digital Assistant), industriellen Steuerungen, Taschenrechnern, Fernsteuerungen oder digitalen Uhren zu finden. Aufgrund einer Massenproduktion sind die Kosten pro Chip auch entsprechend gering. Die Abb. 35 zeigt die grundsätzliche Architektur eines Mikrocontrollers. 77 Core (CPU) InterruptSystem interner Bus ProgrammSpeicher DatenSpeicher PeripherieKomponenten Abbildung 35: Architektur eines Mikrocontrollers (nach [BeiHag01]) Der Mikrocontrollerkern beinhaltet eine CPU mit Rechenwerk, Steuerwerk, Registern und einer Bus-Interface-Unit (vgl. Kap. 1.2). Die Bus-Interface-Unit ist programmierbar, damit externe Komponenten leicht hinzugefügt werden können. Häufig werden für den Mikrocontrollerkern bereits entwickelte Mikroprozessor-CPUs genommen und geringfügig angepasst, so dass die CPU-Architektur fast gar nicht verändert wird. Mikrocontrollerkerne können CISC, RISC oder auch superskalare Architekturen besitzen. In speziellen Fällen werden auch DSP-Architekturen (vgl. Kap. 4.2) integriert. Beim Speicher werden Programm- und Datenspeicher getrennt (vgl. Kap. 1.1). Auf dem gleichen Chip können Taktgeber und Peripheriekomponenten wie I/OPorts, Analog-Digital-Umsetzer, Digital-Analog-Umsetzer, serielle und parallele Interfaces o.ä. integriert sein. Hierbei ist noch erwähnenswert, dass die Architektur Komponenten enthält, die die Systemverfügbarkeit mittels in Hardware realisierten Zählern (Timer) überprüfen sollen. Ein Watchdog-Timer ist ein laufender Zähler, der kurz vor dem Überlauf durch die Software zurückgesetzt wird. Fehlfunktionen der Software können dadurch erkannt werden, dass der Timer nicht zurückgesetzt wurde. Beispiele für Mikrocontroller sind Texas Instruments TMS1000, Philips 80X51, Siemens Tricore, Motorola HC12, NEC V850, AMD Elan, Intel MCS251. Weitere Details sind in [BeiHag01] zu finden. 78 4.2 DSP-Prozessoren Bei der digitalen Signalverarbeitung (Digital Signal Processing) werden analoge Signale abgetastet, in digitale Daten umgewandelt, numerisch verarbeitet und wieder zurück in analoge Signale transformiert. Durch dieses Vorgehen können Signaleigenschaften verändert oder analysiert werden. Die Anwendungen sind vielfältig, wie z.B. Filterung, Rauschunterdrückung, Frequenzanalyse (auch für Spracherkennung oder Sprachausgabe). Digitale Signalprozessoren (Digital Signal Processor, DSP) sind spezielle Mikroprozessoren, deren Architektur für die Signalverarbeitung optimiert ist (vgl. [BeiHag01]). Sie werden beispielsweise eingesetzt in: HiFi-Anlagen, Telefonen, Handys, Spielzeugen, Modems, Autos, Ultraschallgeräten, Radargeräten (siehe auch [BDT98]). Da hohe Datenmengen an Quell- und Ergebnisdaten anfallen, können sie aus Performancegründen nicht wie bei General-Purpose-Architekturen lokal in Prozessorregistern gehalten werden, so dass Busse mit sehr hohen Bandbreiten zwischen DSP-Prozessor und Speicher erforderlich sind. Auch die ISA-Architektur unterscheidet sich deswegen bei DSPProzessoren erheblich von General-Purpose-Prozessoren [BeiHag01]. Selbst zwischen verschiedenen DSP-Prozessoren variieren die Befehlssätze aufgrund der spezialisierten Architekturen sehr stark. DSP-Prozessoren haben eine Harvard-Architektur (vgl. Kap. 1.1) und besitzen physikalisch getrennte Programm- und Datenspeicher, wobei auch mehrere Datenspeicher vorhanden sein können (Abb. 36). Durch ein umfangreiches Bussystem kann der Prozessorkern parallel auf Programm- und Datenspeicher zugreifen, so dass pro auszuführendem Befehl Daten aus jedem Daten-Speicher gleichzeitig ausgelesen werden können. 79 Prozessorkern Code-Speicher Daten-Speicher 1 Daten-Speicher 2 Adressbus 1 Datenbus 1 Adressbus 2 Datenbus 2 Adressbus 3 Datenbus 3 Abbildung 36: Harvard-Architektur von DSP-Prozessoren (angelehnt an [BeiHag01]) Die ALU von DSP-Prozessoren kann arithmetische und logische Befehle ausführen. Es gibt aber auch weiter eingeschränkte Varianten, die nur mit IntegerWerten rechnen oder keine Divisionen durchführen können [BeiHag01]. Die Architektur eines DSP-Prozessors ist hauptsächlich darauf ausgerichtet, sog. multiply-accumulate-Operationen (MAC) durchzuführen [EyBier98]. Hierbei handelt es sich um Berechnungen der Art Ergebnis(n) = Ergebnis(n-1) + Xn * Yn, die bei der Bildung von Vektor-Skalarprodukten benötigt werden. MAC-Operationen werden von Algorithmen zur Filterung von Signalen oft ausgeführt [EyBier98]. Die folgende Abbildung zeigt die dafür speziell angepasste Architektur. 80 X Datenbus Y Datenbus Y0 OperandenRegister Y1 X0 X1 Multiplizierer ALU AkkumulatorRegister A B Abbildung 37: Datenpfad bei einem DSP-Prozessor (angelehnt an [BDT98]) Die Multiplikation zweier Werte und deren Addition zum vorherigen Zwischenergebnis ist gleichzeitig in einem Takt möglich. Die ALU und der Multiplizierer sind in Hardware realisiert und benutzen keine Mikroprogrammierung. Manche DSPProzessoren besitzen sogar mehrere unabhängige MAC-Einheiten. Um Speicherzugriffe zu beschleunigen, werden in DSPs für die Adressberechnung eigene ALUs implementiert, die parallel zu den MAC-Einheiten arbeiten. Die Adressierungsarten werden auch an spezifische Algorithmen der Signalverarbeitung, wie die Fourier-Transformation (vgl. [BeiHag01]), angepasst. Anzutreffen sind u.a. Register-indirekte Adressierung mit post-Inkrement (vgl. Kap. 2.1.1), Bitreverse-Adressierung (nur für Fourier-Transformation brauchbar, vgl. [Brig95]). Die Modulo-Adressierung basiert auf der Register-indirekten Adressierung mit post-Inkrement, wobei immer modulo n inkrementiert wird (vgl. [BeiHag01]). 81 Die Modulo-Adressierung ist bei Zugriffen auf einen Ringspeicher nützlich, wo gleichzeitig eine Bereichsbegrenzung sichergestellt werden muss. In manchen DSP-Prozessoren werden für weitere Performancesteigerungen auch kurze, meist 3-stufige Pipelines mit weniger komplexen Sprungvorhersagemethoden (vgl. Kap. 3.2) eingesetzt [BeiHag01], [Märtin01]. Die Softwareentwicklung für DSP-Prozessoren ist nicht einfach. Aufgrund der unterschiedlichen Architekturen von verschiedenen DSP-Prozessoren wird der Code häufig in Assembler geschrieben, um alle Möglichkeiten auszureizen. Der von Compilern generierte Code liefert im Vergleich zur manuellen Codierung eine viel schlechtere Performance. Hinzu kommt die Komplexität des Befehlssatzes, die eine intuitive Programmierung erschwert (Details vgl. [EyBier98]). DSP-Prozessoren haben sich trotzdem in Anwendungsbereichen durchgesetzt, wo der Einsatz von General-Purpose-Prozessoren hohe Kosten verursacht. Durch die extreme Spezialisierung können DSP-Prozessoren zu niedrigen Preisen gefertigt werden [BDT00]. Mittlerweile wird aber auch in General-Purpose-Prozessoren DSPFunktionalität integriert (vgl. Kap. 5). Beispiele für DSP-Prozessoren sind Analog Devices ADSP-2100, Texas Instruments TMS320C5000, NEC µPD77100, Lucent DSP16000, Motorola DSP56000, Motorola MSC8102. Details zu DSP-Prozessoren sind in [BeiHag01] nachzulesen. 82 5 Aktuelle Mikroprozessoren In diesem Kapitel sollen abschließend einige Beispiele aktueller Mikroprozessoren vorgestellt werden. Die Darstellung wird überwiegend architekturrelevante Merkmale erwähnen und soll dabei ein Bild vermitteln, inwieweit die Vorgestellten Konzepte tatsächlich in der Praxis eingesetzt werden. Teilweise werden auch schon Ansätze von Konzepten implementiert, an denen zur Zeit noch geforscht wird, wie z.B. Simultaneous Multithreading (SMT) oder Chip- Multiprozessoren (vgl. Kap. 6.2). Bei der folgenden Darstellung ist zu berücksichtigen, dass von Herstellern oft grundlegende Konzepte aufgegriffen, geringfügig modifiziert und mit verkaufsfördernden Schlagworten benannt werden. Ebenso ist zu berücksichtigen, dass aufgrund der starken Konkurrenz auf dem Mikroprozessormarkt viele Hersteller wichtige Details der Mikroprozessorarchitektur nur teilweise oder gar nicht veröffentlichen, so dass die Darstellung an dieser Stelle unvollständig bleibt. Intel Pentium 4 Der Intel Pentium 4 Mikroprozessor (Nov. 2000) arbeitet mit Taktfrequenzen von 1.3 – 2.2 GHz. Bei der 32-Bit Architektur handelt es sich um eine superskalare out-of-order-Architektur. Da die Dekodierung von ISA-Befehlen sehr aufwändig ist, werden sie intern erst in sog. „µOps“ übersetzt, um die restliche Hardware zu vereinfachen. Der Begriff „µOps“ ist ein von Intel geprägtes Schlagwort, wobei es sich um RISC-ähnliche 3-Adress-Befehle handelt. Bei der 20-stufigen, mit Prozessorfrequenz betriebenen superskalaren Pipeline (vgl. Kap. 3.3.1) nutzt Intel eine Superpipelining-Architektur (von Intel „Hyper Pipelined Technology“ genannt). Damit die Pipeline ständig ausgelastet bleibt, wird eine gute Sprungvorhersage mit einem 4000 Einträge großen BTB (vgl. Kap. 3.2) implementiert. Unter den 6 Ausführungseinheiten gibt es 2 Integer-ALUs, die mit doppelter Taktfrequenz arbeiten. Sie stellen arithmetische und logische Befehle in nur einem halben Prozessortakt fertig. Der ROB (vgl. Kap. 3.3.2) hat eine Kapazität von 100 µOps. Auf dem Chip ist ein 8K großer L1-Daten-Cache integriert, jedoch fehlt ein L1-Code-Cache. Stattdessen wurde ein Trace-Cache implementiert (vgl. Kap. 2.1.3 und 6.2), der 83 12.000 µOps enthalten kann und eng mit der Fetch/Decode-Unit zusammenarbeitet. Bei Trace-Cache-Misses wird auf den 256KB-512KB großen OnChip-L2-Cache (von Intel „Advanced Transfer Cache“ genannt) zugegriffen. Der L2Cache ist ein nicht-blockierender, 8-fach-assoziativer Cache (vgl. 2.2.1). Die FPUFunktionseinheiten unterstützen Multimediaanwendungen durch MMX/SSE- Funktionalität (vgl. Kap. 3.3.4). Der Registersatz ist mit dem der IA-32-Architektur (vgl. Kap. 2.1.1) kompatibel. Erwähnenswert ist auch die zusätzlich im Chip implementierte Hardware, die das Testen der Funktionsfähigkeit des Prozessors unterstützen soll (vgl. Kap. 1.4). Weitere Details gibt es in [Märtin01], [Intel02a]. Intel Pentium Xeon Dieser Prozessor (2000) basiert auf dem Pentium III und imlementiert die schon in Kap. 3.3.2 besprochene P6-Architektur. Im Gegensatz zum Pentium 4 wurde er für den Einsatz in Servern konzipiert. Er arbeitet mit Taktfrequenzen bis zu 1.6 GHz und hat eine 3-stufige Cache-Hierarchie. Auf Level 1 befindet sich ein 8 KB großer Daten-Cache und ein 12.000 Einträge umfassender Trace Cache, auf Level 2 ein 256KB großer Cache und auf Level 3 ein 512KB-1MB großer Cache. Der Intel Xeon unterstützt Thread-Level-Parallelism auf Hardwareebene durch eine SMT-Architektur (vgl. Kap. 6.2), die von Intel „Hyper-Threading“ genannt wird. Die übrigen Merkmale sind dem Pentium 4 ähnlich. Details zum Xeon-Prozessor sind in [Intel02b] und zu Hyper-Threading in [Marr02] zu finden. Intel Itanium Der Intel Itanium-Prozessor (2001) implementiert die in Kap. 3.3.3 behandelte IA-64-Architektur. Es handelt sich um einen mit 800MHz getakteten 64-bitProzessor mit 9 nebenläufigen Funktionseinheiten. Das Prozessordesign zielt nicht nur auf eine weitere Verbesserung bzw. Duplizierung der Hardware ab, sondern versucht die Performance auf Mikroarchitekturebene durch Kooperation mit Compilern zu steigern. Dieser Ansatz wurde als Explicit Parallel Instruction Computing (EPIC, vgl. Kap. 3.3.3) bezeichnet und soll eine Alternative zu SMTArchitekturen (vgl. Kap. 6.2) darstellen [Märtin01]. Der Itanium besitzt 3 BranchUnits für die Sprungvorhersage (vgl. Kap. 3.2), 4 Integer- und Multimedia-Units und 84 2 Floating-Point-Units, die auch SIMD-Multimedia-Befehle (vgl. Kap. 3.3.4) und DSPähnliche MAC-Befehle (vgl. Kap. 4.2) unterstützen. Die Cache-Hierarchie ist 3stufig, wobei die ersten 2 Stufen auf dem Prozessorchip implementiert sind. Der L3-Cache kann bis zu 4 MB groß sein. Details sind in [Intel00b], [Intel00c], [Märtin01], [Intel02c] spezifiziert. AMD Athlon XP Der Athlon XP (2001, 1.67 GHz Taktfrequenz) besitzt eine 9-fach-superskalare out-of-order Mikroarchitektur (vgl. Kap. 3.3.2). Darin enthalten sind 3 parallel arbeitende Decoder. Insgesamt sind 9 Ausführungseinheiten vorhanden: 3 IntegerUnits, 3 Floating-Point-Units mit SIMD-Unterstützung für Multimedia-Anwendungen (vgl. Kap. 3.3.4) und 3 Address-Calculation Units. Die Ausführungseinheiten sind intern pipelineartig aufgebaut. Zusätzlich integriert dieser Prozessor DSPFähigkeiten (vgl. Kap. 4.2) für die Aufbereitung von Dolby-Surround-Sound und für MP3-Anwendungen. Der L1-Cache besteht aus einem 64KB Daten- und 64KB Instuktions-Cache. Ebenfalls auf dem Prozessorchip ist auch ein 256KB großer L2Cache integriert. Mehr Informationen sind in [AMD01], [AMD02] zu finden. SUN MAJC 5200 Der Ende 1999 vorgestellte MAJC-Prozessor (Microprocessor Architecture for Java Computing, MAJC, ausgesprochen „magic“) ist eine Implementierung einer SMT- und Chip-Multiprozessor-Architektur (vgl. Kap. 6.2). Dabei sind 2 mit 500 MHz getaktete Prozessoren auf einem Chip vereinigt. Hierdurch können mehrere Arten von Parallelität genutzt werden: Parallelität auf Datenebene durch SIMD-ähnliche Befehle, Parallelität auf Instruktionsebene durch mehrere Funktionseinheiten auf einem Prozessor und Parallelität auf Thread-Ebene (vgl. Kap. 6.1). Im Vordergrund steht eine schnelle Unterstützung von Java-Compilern. Zusätzlich werden für Multimedia-Anwendungen DSP-ähnliche Befehle bereitgestellt (vgl. Kap. 4.2). Ein 16 KB großer, 4-fach-assoziativer Daten-Cache wird von beiden Prozessoren auf dem Chip benutzt. Zusätzlich hat jeder Prozessor einen eigenen 16 KB großen, 2fach-assoziativen Daten-Cache. Der MAJC-Prozessor ist für den Einsatz in den Bereichen Grafik- und Audioverarbeitung, Internetworking oder Verschlüsselung 85 gedacht. Ausführlichere Informationen findet man in [SUN99a], [Subram99], [Tremb99], [SUN02a]. SUN UltraSPARC III Dieser im Jahr 2001 vorgestellte 4-fach superskalare 64-Bit Mikroprozessor ist eine Implementierung Taktfrequenzen bis zu der SUN 1050 SPARC MHz. Er V9-Architektur hat 6 und pipelineartig arbeitet mit organisierte Ausführungseinheiten: 2 Integer-Units, 2 Floating-Point-Units, 1 Load/Store-Unit, und 1 Addressing-Unit. Auf Level 1 gibt es einen 32KB großen Daten- und 64KB großen Instruktions-Cache, die jeweils 4-fach-assoziativ sind. Der externe L2-Cache kann bis zu 8MB groß sein. Die Cache-Tags des L2-Cache befinden sich auf dem Prozessor-Chip. Für mehr Informationen sei auf [SUN02b] und [SUN02c] verwiesen. Motorola Dragonball MX1 Die Firma Motorola hat speziell für den Embedded-Systeme-Bereich die Dragonball-Serie entwickelt. Haupteinsatzgebiete sind PDAs oder Smartphones. Aus dieser Serie soll beispielhaft der zur Zeit neueste Prozessor (MC9328MX1, vorgestellt im Jahr 2001) beschrieben werden. Obwohl er von Motorola als Mikroprozessor bezeichnet wird, ist er aufgrund des integrierten Prozessorkerns und der integrierten Peripheriekomponenten auf dem gleichen Chip eher als Mikrocontroller zu klassifizieren (vgl. Kap. 4.1). Der Prozessorkern basiert auf der von der Firma ARM entwickelten ARM9-Architektur, die eine 32-bit-RISC-Architektur ist. Die Prozessorkern kann mit bis zu 200 MHz getaktet sein, hat einen 16KB großen Daten-Cache, einen 16KB großen Instruktions-Cache und unterstützt virtuellen Speicher (vgl. Kap. 2.2.4) mittels einer Virtual Memory Management Unit. Auf den gleichen Chip sind ein LCD-Display-Support, ein Touch-Panel-Support, ein Watchdog-Timer, ein Interrupt-Controller, ein Video-Port, ein Power-ControlModul und verschiedene Schnittstellen eingebaut. Der eingebaute MultimediaAccelerator unterstützt DSP-ähnliche Operationen (vgl. Kap. 4.2). Weiterführende Informationen gibt es in [Motorola02]. 86 Motorola MSC8102 Mit diesem Prozessor (2001) soll beispielhaft ein Vertreter der DSPProzessoren (vgl. Kap. 4.2) vorgestellt werden. Es handelt sich um einen mit 300 MHz getakteten Prozessor, der für die Ausführung von MAC-Operationen 16 ALUs und 4 Coprozessoren für spezielle Filteroperationen enthält. Ebenfalls auf dem gleichen Chip sind 1436 KByte RAM-Speicher integriert. Genaueres über diesen Prozessor ist in [Motorola02b] zu finden. ZILOG eZ80 Webserver Dieser spezielle als System-on-a-Chip konzipierte 8-Bit-Mikrocontroller (vgl. Kap. 4.1) ist in der Lage, über ein TCP/IP-Netzwerk Web-Seiten zur Verfügung zu stellen. Der eZ80 ist seit 2001 verfügbar und ist mit 50 MHz getaktet. Er besitzt auf dem gleichen Chip 8K RAM, einen Taktgeber und kann 16MB Speicher direkt adressieren. Zusätzlich ist ähnlich einem DSP-Prozessor eine MAC-Unit (vgl. Kap. 4.2) eingebaut, um Algorithmen für den Aufbau sicherer Verbindungen im Internet zu unterstützen. Haupteinsatzgebiete sind beispielsweise Verkaufsautomaten, Online Information Kiosks, Industrial Control oder Remote Monitoring. Der eZ80 ist ein Musterbeispiel dafür, dass nicht immer höchste Performance benötigt wird und auch Einfachheit gefragt sein kann. Weitere Details sind in [Zilog01] beschrieben. 87 6 Softwaresysteme und Forschungstrends Dieses Kapitel fasst die Abhängigkeiten und Wechselwirkungen zwischen Mikroprozessorarchitektur und Softwaresystemen (Ebenen 3,4,5, vgl. Abb. 2) in einem kurzen Überblick zusammen. Es werden die Schnittstellen zu Compilern bezüglich des generierten Maschinencodes und zu Betriebssystemen, die durch spezielle Hardwarearchitekturen unterstützt werden können, beschrieben. Abschließend werden Architekturkonzepte vorgestellt, an denen zur Zeit noch geforscht wird und die möglicherweise die Entwicklung zukünftiger Mikroprozessorarchitekturen beeinflussen werden. 6.1 Compiler und Betriebssysteme Wie schon in Kap. 1.1 erläutert, können Compiler in Hochsprachen geschriebene Programme in Assemblersprachen übersetzen. Die Wahl der Hochsprachen ist meistens von deren kommerziellen Bedeutung zum Zeitpunkt der Markteinführung eines Prozessors abhängig. Die Assemblersprachen werden dagegen von der ISA- und Mikroarchitektur-Ebene (vgl. Abb. 2) beeinflusst. Compiler sind für den Erfolg einer Prozessorarchitektur in hohem Maße mitverantwortlich. Sie dienen der Softwareerstellung oder der Portierung bereits bestehender Software auf eine bestimmte Prozessorarchitektur. Optimierende Compiler ermöglichen durch Veränderung der Code- und Datenstruktur, jedoch unter Beibehaltung der Programmsemantik, eine bestmögliche Ausnutzung aller Architekturmerkmale. Bei einer Low-Level-Optimierung wird die Optimierung im Compilierprozess erst sehr spät durchgeführt. Eine Mixed-Model-Optimierung transformiert die Hochsprache erst in eine Zwischensprache, die anschließend optimiert und in eine Assemblersprache übersetzt wird [Muchnik97]. Durch das letztere Vorgehen können Hersteller die Compiler leichter an neue Prozessorarchitekturen anpassen. Die Optimierung kann unterschiedliche Aspekte umfassen. Ein Compiler kann möglichst wenig Overhead produzieren oder niemals aufgerufenen Code entfernen (Dead Code Elimination). Schleifenkonstrukte können durch „Loop Unrolling“ 88 optimiert werden, indem der Schleifenkörper mehrmals vervielfältigt und aneinandergereiht wird, wobei die Schleifenparameter entsprechend angepasst werden. Dadurch soll die Zahl der Rückwärtssprünge an den Schleifenanfang vermindert werden. Gleichzeitig wird damit versucht, bei Pipelinearchitekturen (vgl. Kap. 3.1) die Pipelines besser auszulasten und Pipeline-Hazards zu vermeiden [Märtin01]. Des weiteren kann ein Compiler für Superskalararchitekturen ein Befehlsscheduling durchführen und Befehle so anordnen, dass eine Dispatch-Unit (vgl. Kap. 3.3) sie möglichst effizient verteilen kann. Auch die Sprungvorhersage (vgl. Kap. 3.2) kann durch Status-Bits bei Sprungbefehlen unterstützt werden, da ein Compiler das wahrscheinliche Ausführungsverhalten eines Programms im Voraus abschätzen kann [Muchnik97]. Ebenso kann das Lokalitätsverhalten abgeschätzt werden. Bestimmte Befehle und Daten können dann durch Prefetching bereits vor der Ausführung in den Cache geholt werden (vgl. Kap. 2.2.1). Die Compiler bei VLIW/EPIC-Architekturen (vgl. Kap. 3.3.3) haben einen noch größeren Stellenwert, da Hardwarefunktionalität in die Compiler ausgelagert wird. Diese Compiler müssen den Befehlsstrom eines Programms auf unabhängige Befehle untersuchen und sie so in Wörter gruppieren, dass die darin enthaltenen Befehle parallel ausführbar sind. Durch besonders geschickte Gruppierung kann die Performance erheblich gesteigert werden (vgl. [Märtin01]). Bei EPIC-Architekturen können sich in einem EPIC-Wort auch Befehle befinden, die nicht parallel ausführbar sind. Jedes EPIC-Wort enthält zusätzliche Status-Bits für die Angabe, welche der enthaltenen Befehle parallel ausführbar sind [Ungerer01]. Eine weitere Besonderheit bei EPIC-Architekturen ist die Nutzung von Predication (vgl. Kap. 3.3.3). Hierbei werden bestimmten Befehlen Prädikate zugeordnet. Die Ergebnisse der Befehle werden nur dann als gültig anerkannt, wenn das zugehörige Prädikat den Wert true hat. Diese Technik wird eingesetzt, um die Ausführung bedingter Sprünge zu optimieren. Der folgende in Pseudocode formulierte Code-Abschnitt switch(Bedingung) { case 1: A = B + C; break; case 2: A = E + F; break; 89 case 3: A = H – I; break; } würde transformiert werden in: (p1) A = B + C; (p2) A = E + F; (p3) A = H – I; Dabei sind p1, p2, p3 die zugehörigen Prädikate. Die komplette switch-Anweisung kann zusammen mit der Auswertung der Bedingung parallel in einem Takt ausgeführt werden. Eventuelle Konflikte bei der Ausführung (hier ist A jedes Mal Ziel der Operationen) werden durch Register Renaming (vgl. Kap. 3.3.2) gelöst. Danach wird das Prädikat auf true gesetzt, auf das die Bedingung der switchAnweisung zutrifft. Weitere Details zu Compilern findet man in [Muchnik97] und [Märtin01]. Ein Betriebssystem (vgl. Kap. 1.1) kann von einer Mikroprozessorarchitektur bei den Verwaltungsaufgaben unterstützt werden. Viele Betriebssysteme verwenden Paging, Segmentierung und virtuellen Speicher (vgl. [SGG99]). Mikroprozessoren können mit MMUs die Adressumsetzung von logischen in physikalische Adressen hardwaremäßig unterstützen und beschleunigen (vgl. Kap. 2.2). Bei Multi-Tasking-Betriebssystemen kann es mehrere ausführende Prozesse geben, zwischen denen das Betriebssystem zur Laufzeit umschaltet. Hierbei sind ständig Daten zur Zustandsbeschreibung eines Prozesses in einen sog. Process Control Block (PCB) zu protokollieren: Registerinhalte, Program Counter, Stack, verbrauchte CPU-Zeit, Prozess-ID, sowie die Prozess-Umgebung. Zwei Prozesse können pseudoparallel ausgeführt werden, indem ein Prozess angehalten (suspendiert), der PCB gesichert, der PCB des nächsten Prozesses geladen und dessen Ausführung fortgesetzt wird. Nach einer bestimmten Zeit kann das Betriebssystem die Kontrolle in der gleichen Weise an den ersten Prozess zurückgeben. Dieser Vorgang wird als Context-Switch bezeichnet und erfordert 90 wegen Speicherzugriffen verhältnismäßig viel Zeit, was die Performance beeinträchtigt. Manche Prozessorarchitekturen unterstützen einen Context-Switch mit zusätzlicher Hardware, z.B. mit zusätzlichen Registern. Bei der SUN-SPARCArchitektur bestehen beispielsweise die Register aus zirkulär angeordneten Registersätzen. Das Betriebssystem verwaltet einen Zeiger, der auf den aktuell für das System sichtbaren Registersatz zeigt. Für einen ausführenden Prozess ist nur ein Registersatz im sog. Register-Fenster sichtbar. Bei einem Context-Switch kann das Betriebssystem den Zeiger zirkulär weitersetzen und braucht nicht mehr so viele Daten im PCB zu verwalten, was die Ausführung beschleunigt. Weitere Details beschreibt [Märtin01]. Die Zeit für einen Context-Switch kann auch dadurch reduziert werden, dass sich sog. Threads eine Programmumgebung (Code, Daten und Betriebssystemressourcen) teilen. Die Verwaltungsinformationen für einen Thread beschränken sich auf Registerinhalte, Program Counter und Stack. Die Performancesteigerung durch einen schnelleren Context-Switch wird allerdings durch ein zusätzliches Konfliktpotenzial zwischen Threads erkauft. Neuere Mikroprozessorarchitekturen versuchen die Parallelität auf Thread-Ebene in der Architektur auszunutzen (Simultaneous Multithreading, SMT, vgl. Kap. 5 und 6.2). Bei der parallelen Ausführung von Prozessen sind oft Synchronisationsmechanismen zur Sicherung der Determiniertheit nötig. Hierfür können atomare Lese- und Schreiboperationen oder lock-Mechanismen unterstützend von der Architektur vorgesehen werden. In [SGG99] werden Betriebssysteme und zugehörige Konzepte ausführlicher besprochen. 6.2 Ausblick In diesem Kapitel sollen mögliche Entwicklungen von zukünftigen Mikroprozessorarchitekturen vorgestellt werden, an denen zur Zeit noch geforscht wird. Trotz der langsam erreichten Grenzen der Verarbeitungsleistung bei aktuellen Prozessorarchitekturen [Ungerer01], bleibt die Forderung nach noch mehr Performance bestehen. Diese kann einerseits durch die zugrunde liegenden Technologien (z.B. Fortschritte bei VLSI) oder durch die Erweiterung der 91 Architekturkonzepte um mehr Parallelität bei der Ausführung erfüllt werden. Grundsätzlich lassen sich vier Entwicklungsrichtungen identifizieren [SiUnRo00]: • Die Prinzipien der von-Neumann-Architektur, insbesondere die Ergebnissequenzialität, werden beibehalten. Die interne Ausführung der Befehle geschieht allerdings parallel. Aktuelle Mikroprozessoren nutzen überwiegend Parallelität auf Befehlsebene (Instruction Level Parallelism, ILP) und Parallelität durch Spekulation aus (vgl. Kap. 3.2). Neuere Forschungsansätze dieser Klasse sind: Superspekulative [LiShe97], Multiskalare [Frank93], Trace-Prozessoren [RJSS97] und Datenskalare Prozessoren [BuKaGo97]. • Das SISD-Prinzip der von-Neumann-Architektur wird zum SIMD-Prinzip erweitert, so dass ein Befehl mehrere Daten gleichzeitig manipuliert. Hierbei soll Parallelität auf Datenebene ausgenutzt werden. Beispiele für solche Architekturen sind Vektor- oder VLIW/EPIC-Architekturen (vgl. Kap. 3.3.3 und 3.3.4). • Es wird zusätzlich die Parallelität auf Thread-Ebene ausgenutzt. Dabei sollen Prozessoren in der Lage sein, hardwaremäßig Threads (vgl. Kap. 6.1) parallel auszuführen. Bei der Ausführung eines Threads werden die von-Neumann-Prinzipien eingehalten. Zu dieser Klasse gehören SingleChip-Multiprozessoren [PPEFS97] und Simultaneous-MultithreadedProzessoren [TuEgLev95]. • Die von-Neumann-Architektur wird komplett verlassen. Dazu zählt beispielsweise der Processor-In-Memory-Ansatz [PaAnCa97]. Aufgrund der Vielzahl unterschiedlicher Forschungsansätze werden in diesem Kapitel nur die wichtigsten erläutert. Alle anderen Architekturprinzipien sind in [SiRoUn99] ausführlich beschrieben. Die direkten Weiterentwicklungen der superskalar-Architekturen („Advanced Superscalar Microprocessors“) konzentrieren sich noch intensiver auf die Ausnutzung der Parallelität auf Befehlsebene (ILP). Dabei versucht man, die Zahl 92 der ausführenden Funktionseinheiten noch weiter zu erhöhen, die Cache-Speicher zu vergrößern und die Sprungvorhersage durch unterschiedliche Algorithmen für jede Klasse von Programmsprüngen zu verbessern (Hybrid Predictors, [EvCP96], [McFarling93]). Die Instruktions-Cache-Speicher werden zu Trace-Cache-Speichern erweitert, indem sie in einer Cache-Line nicht mehr einzelne Instruktionen zwischenspeichern, sondern zur Laufzeit aufgetretene Befehlssequenzen (Traces). Die Idee ist dabei folgende: Sollte die gleiche Befehlssequenz vom Programm öfter ausgeführt werden (z.B. bei Schleifen), so kann auf eine erneute Sprungvorhersage verzichtet und die frühere Befehlssequenz direkt aus dem Trace-Cache geholt werden. Ein weiterer Ansatzpunkt ist die Verbesserung der Spekulation. Basierend auf der Beobachtung, dass die berechneten Werte in Programmen (z.B. Adressen oder Werte von Operanden) aufgrund des Lokalitätsprinzips vorhersehbar sind, werden weitere Möglichkeiten wie Datenabhängigkeits- und Wertespekulation erforscht. Programme werden in Teile unterteilt, die Werte produzieren („Producer“) und Teile, die Werte benutzen („Consumer“). Superspekulative Prozessoren versuchen die von den Consumern benötigten Werte spekulativ zu schätzen, noch bevor sie von den Producern erzeugt wurden. Dieses Parallelitätspotenzial soll für Performancesteigerungen ausgenutzt werden. Weitere Details gibt es in [SiUnRo00]. Die Fokussierung auf Befehlsebenenparallelität wird bei MultithreadedArchitekturen aufgegeben. Ein Multithreaded-Prozessor ist dabei in der Lage, die Befehle von zwei oder mehr Threads in Hardware gleichzeitig (oder überlappt parallel) auszuführen. Für jeden Thread gibt es eigene Registersätze, in denen Kontextinformationen abgelegt werden können (vgl. Kap. 3.2). Die Kombination von Mehrfädigkeit und Superskalartechnik werden in Simultaneous-MultithreadingArchitekturen (SMT) vereint. Solche Prozessoren können in einem Taktzyklus Befehle mehrerer Threads an Ausführungseinheiten zuordnen. SMT-Architekturen werden in [TuEgLev95] und [SiUn96] genauer beschrieben. Es gibt mehrere Möglichkeiten für die Erzeugung von Threads. Bei parallelisierten Betriebssystemen oder Programmen sind Threads durch die Software bereits spezifiziert, so dass sie nur noch auf die Hardware abgebildet werden müssen. Es wird aber zusätzlich die Möglichkeit untersucht, aus einem sequenziellen Befehlsstrom innerhalb des Prozessors Threads zu erzeugen und 93 spekulativ auszuführen. Die Thread-Erzeugung kann dynamisch zur Laufzeit (Dynamic Multithreading) oder statisch mit Unterstützung eines Compilers erfolgen. Letzterer Ansatz soll ein wenig genauer beschrieben werden. Bei Multiskalaren Prozessoren teilt zuerst ein Compiler ein sequentielles Programm in sog. Tasks auf, die den Ausführungseinheiten (processing elements) des Prozessors zugeteilt und spekulativ ausgeführt werden. Dabei wird so vorgegangen, dass der nichtspekulative Teil eines Programms einer bestimmten Ausführungseinheit zugewiesen wird. Tasks, die im Programm später folgen, werden von einem Sequenzer auf alle anderen Ausführungseinheiten verteilt. Deren Ergebnisse werden verworfen, wenn das Programm einen anderen Verlauf annimmt als erwartet (Fehlspekulation). Am Problem der eventuell auftretenden Datenabhängigkeiten wird noch geforscht. Genaueres zu Multiskalaren Prozessoren findet sich in [SoBrVi95], [SiUnRo00], [Ungerer01]. Erwähnenswert ist auch der Ansatz von Chip-Multiprozessoren [Kahle99]. Die Architektur von Chip-Multiprozessoren wird maßgeblich von der vorhandenen Technologie beeinflusst. Hierbei werden mehrere Prozessoren auf einem Chip vereinigt. Einige der Speicher auf dem Chip (z.B. Cache) werden von allen CPUs gemeinsam genutzt. Dadurch sollen Signallaufzeiten und Speicherlatenzzeiten reduziert und die Kommunikation zwischen den CPUs erleichtert werden. ChipMultiprozessoren können auch mit Multithreading-Architekturen kombiniert werden, was die Firma Sun Microsystems im SUN-MAJC-5200-Prozessor realisiert hat (vgl. Kap. 5 und [Märtin01]). Der Architekturansatz der Prozessor-Speicher-Integration (auch Processor-InMemory, Intelligent RAM, IRAM) entfernt sich stark von der Architektur nach vonNeumann. Im Zentrum der Betrachtung steht nicht mehr die CPU, sondern der Speicher. Man versucht dabei, CPU und kompletten Speicher auf einem Chip zu integrieren, um für Speicherzugriffe die Bandbreite zu erhöhen und die Latenzzeit zu reduzieren. Dieser Ansatz könnte dabei Cache-Speicher überflüssig machen. Beim derzeitigen Stand der VLSI-Technologie ist eine solche Integration allerdings noch mit technischen Problemen verbunden. Weitere Details sind in [Ungerer01] zu finden. Schließlich sei noch erwähnt, dass auch an ganz unterschiedlichen Paradigmen geforscht wird, die eine radikal andere Sicht auf Berechnungen haben. Dabei benutzt man für die Repräsentation von Daten nicht mehr elektrische, digitale 94 Signale, sondern beispielsweise chemische Reaktionen (DNA-Computing) oder quantenmechanische Vorgänge (Quantum Computing). Detailliertere Informationen zu DNA-Computing findet man in [Adleman94], [GBGMP01], zu Quantum-Computing in [Shor94], [GBGMP01]. 95 Anhang: Bilder von Mikroprozessoren 96 Intel 4004 (1971) Quelle: Intel 97 Intel 486 (1991) Quelle: Intel 98 Motorola PowerPC 601 (1992) Quelle: Motorola 99 Intel Pentium (1993) Quelle: Intel 100 Intel Pentium 4 (2001) Quelle: Intel 101 Bibliographie [ABF95] Abramovici, Breuer, Friedman, Digital Systems Testing & Testable Design, IEEE Press, 1995 [Adleman94] Adleman, Molecular computation of solutions to combinatoral problems. Science 266, pp. 1024-1024, 1994 [AMD01] ADVANCED MICRO DEVICES, INC., One AMD Place, Sunnyvale, CA 94088, QuantiSpeed™ Architecture, White Paper, September 7, 2001 [AMD02] http://athlonxp.amd.com/overview/keyFeatures.jsp, 21.03.2002 [BDT98] Berkeley Design Technology Inc., PowerPoint-Folien, 2107 Dwight Way, California, USA, http://www.BDTI.com, 1998 [BDT00] Berkeley Design Technology Inc., Choosing a DSP Processor, 2107 Dwight Way, California, USA, http://www.BDTI.com, 2000 [BeiHag01] Thomas Beierlein, Olaf Hagenbruch, Taschenbuch der Mikroprozessortechnik, Fachbuchverlag Leipzig, 2. Auflage 2001 [Bleul00] Bleul, Jonglierwettbewerb. Vektorarchitekturen aktueller Prozessoren im Vergleich. c’t, Heft 4, 2000, pp. 314-323 [Brig95] Brigham, FFT: Schnelle Fourier-Transformation, München, Oldenbourg-Verlag, 1995 [BuKaGo97] Burger, Kaxiras, Goodman, Datascalar Architectures, in: Proceedings of the ISCA 24, Denver, CO, 1997, pp. 338-349. [BuGoNeu46] Burks, Goldstine, von Neumann, Preliminary discussion of the logical design of an electronic computing instrument. Report to the US Army Ordonance Department, 1946. Reprint in: Aspray, Burks (Eds.), Papers of John von Neumann, MIT Press, Cambridge, MA, 1987, pp. 97-146. [Clausing00] Clausing, Skript zu Informatik IV, Institut für Informatik WWU Münster, 2000 102 [CrHuck97] Crawford, Huck, Motivations and Design Approach for the IA-64 64-Bit Instruction Set Architecture. Microprocessor Forum, San Jose, Calif. Oct 14, 1997 [Crouch99] Crouch, Design for Test for Digital ICs and Embedded Core Systems, Prentice-Hall New Jersey, 1999 [Denning80] Denning, Working Sets Past And Present, IEEE Transactions on Software Engineering, Volume SE-6, Number 1, Jan 1980, pp. 64-84. [Dief99] Diefendorff, PC Processor Microarchitecture, Microdesign Resources, Microprocessor Report, July 12, 1999 [EvCP96] Evers, Chang, Patt, Using hybrid branch predictors to improve branch prediction accuracy in the presence of context switches, in: Proceedings of the ISCA 23, Philadelphia, PA, 1996, pp. 3-11. [EyBier98] Eyre, Bier, DSP Processors Hit the Mainstream, Berkeley Design Technology Inc, 2107 Dwight Way, Berkeley, CA, 1998 [Flynn72] Flynn, Some Computer Organizations and Their Effectiveness, IEEE Trans. on Computers C-21, 9. Sept 1972 [Frank93] Franklin, The multiscalar architecture, Computer science Technical Report No. 1196, University of Wisconsin-Madison, WI, 1993 [Gajski83] Gajski, Kuhn, Guest Editor’s Intruduction: New VLSI Tools. IEEE Computer, 6(12): 11-14, 12 1983 [GBGMP01] Gramß, Bornholdt, Groß, Mitchell, Pellizzari, Non-StandardComputation, Wiley, 2001 [Giloi97] Giloi, Rechnerarchitektur, Springer-Verlag, 3. Auflage 1997 [GlaRau94] Glaser, beim Rauch, Ablaufstrategien Mikrosystementwurf. und Rechnerunterstützung Abschlußbericht des Verbund- projekts „Untersuchungen zum Entwurf von Mikrosystemen“, Reihe: Innovationen in der Mikrosystemtechnik, Band 19, November 1994 [Goser91] Goser, Großintegrationstechnik. Teil 2: von der Grundschaltung zum VLSI-System. Heidelberg: Hüthig Buch Verlag, 1991, 103 erschienen in der Reihe ELTEX Studientexte Elektrotechnik, Hrsg: Reinhold Pregla, Fernuniversität Hagen [Grun98] Grunwald, Klauser, Manne, Pleszkun, Confidence Estimation for Speculation Control. 25th Annual International Symposium on Computer Architecture, Barcelona, Juni/Juli 1998, pp. 122-131 [Gwen95] Gwennap, Microprocessor Report, Intel’s P6 Uses Decoupled Superscalar Design, Vol 9, No.2, Feb. 16, 1995 [Händler75] Händler, On Classification Schemes for Computer Systems in the Post-von-Neumann-Era. GI Jahrestagung 1974, Siefkes, G. (Ed), Lecture Notes in Computer Science, Vol. 26, Springer Verlag, 1975, pp. 439-452. [HePa97] Hennessy, Patterson, Computer Organizatin and Design: the Hardware/Software Interface, Morgan Kaufmann 1997 [HePa96] Hennessy, Patterson, Computer Architecture: A Quantitative Approach. Second Edition, Morgan Kaufmann 1996 [Intel99] Intel Architecture Software Developer’s Manual Vol. 1-4 (Pentium III), Intel Corp. 1999 [Intel99b] Intel Architecture Optimization Reference Manual. Intel Corp., 1999 [Intel00a] IA-32 Intel Architecture Software Developer’s Manual With Preliminary Willamette Architecture Information Vol. 1-2 [Intel00b] Intel IA-64 Architecture Software Developer’s Manual. Vol. 1-5 Intel Corp., 2000 [Intel00c] Itanium Processor Microarchitecture Reference for Software optimization. Intel Corp., March 2000 [Intel02a] http://www.intel.com/design/Pentium4/prodbref/, 21.03.2002 [Intel02b] http://www.intel.com/design/xeon/xeonmp/prodbref/ index.htm, 21.03.2002 [Intel02c] http://www.intel.com/design/itanium/itanium.htm, 21.03.2002 [Kahle99] Kahle, Power4: A Dual-CPU Processor Chip. Microprocessor Forum, Oktober 1999 [Kropf95] Thomas Kropf, VLSI-Entwurf, Thomson Publishing, 1995 104 [LewPap98] Lewis, Papadimitriou, Elements of the Theory of Computation, Prentice-Hall International Inc., 1998 [LiShe97] Lipasti, Shen, Superspeculative microarchitecture for beyond AD 2000, Computer 30 (1997), pp. 59-66. [Malone96] Malone, Der Mikroprozessor: Eine ungewöhnliche Biographie, Springer 1996 [Marr02] Marr, Binns, Hill, Hinton, Koufaty, Miller, Upton, HyperThreading Technology Architecture and Microarchitecture, Intel Technology Journal Q1,2002 [Märtin01] Märtin, Rechnerarchitekturen, Fachbuchverlag Leipzig, 2001 [McFarling93] MyFarling, Combining Branch Predictors. DEC WRL Technical Note TN-36, DEC Western Research Laboratory,1993 [Messmer00] Messmer, PC Hardwarebuch, Addison-Wesley, 2000 [Motorola02] Motorola Product Brief, MC9328MX1P/D, Rev. 2, 2/2002, http://e-www.motorola.com/webapp/sps/site/ prod_summary.jsp, 21.03.2002 [Motorola02b] MSC8102, THE PERFORMANCE DSP, Motorola Fact Sheet, 2002 [Muchnik97] Muchnik, Advanced Compiler Design and Implementation, Morgan Kaufmann,1997 [ObVossen00] Oberschelp, Vossen, Rechneraufbau und Rechnerstrukturen, Oldenburg, 2000 [PanRan92] Pan, Ranneh, Improving the Accuracy of Dynamic Branch Prediction Using Conference on Branch Correlation. Architectural Support 5th International for Programming Languages and Operating Systems (ASPLOS), pp. 76-84, Boston, April 1992 [Pat85] Patterson, Reduced Instruction Set Computers. Comm. ACM, V. 28, Nr. 1, 1985, pp. 8-21. [PaAnCa97] Patterson, Anderson, Cardwell et. al, A Case for Intelligent RAM. IEEE Micro, March/April 1997, pp. 34-43. [PPEFS97] Patt, Patel, Evers, Friendly, Stark, Billion Transistors, One Uniprocessor, One Chip, Computer 30 (1997), pp. 51-57. [Reilly99] Matt Reilly, Designing an Alpha Microprocessor, IEEE 1999 105 [RJSS97] Rotenberg, Jacobson, Sazeides, Smith, Trace processors, in: Proceedings of the MICRO-30, Research Triangle Park, NC, 1997, pp. 138-148. [Runyon99] Runyon, Testing Big Chips Becomes An Internal Affair, IEEESpectrum 36(4), 1999, pp. 49-55. [SDI91] Schüler-Duden-Informatik, Meyers Lexikonverlag, 1991 [SG99] Silberschatz, Galvin, Operating Systems Concepts, Wiley, Fifth Edition 1999 [Shor94] Shor, Algorithms for Quantum Computation: Discrete Log and Factoring. Proc. 35th Annual Symposium on Foundations of Computer Science, Nov. 20-22, 1994, Santa Fe, New Mexico, IEEE Computer Society Press, pp. 124-134. [Siemers2000] Siemers C., Hardwaremodellierung, Hanser-Verlag, 2000 [SiRoUn99] Silc, Robic, Ungerer, Processor Architecture, Springer, Berlin, 1999 [SiUn96] Sigmund, Ungerer, Evaluating a Multithreaded Superscalar Microprocessor Versus a Multiprocessor Chip. 4th PASA Workshop on Parallel Systems and Algorithms. Jülich, April 1996, pp. 147-159. [SiUnRo00] Silc, Ungerer, Robic, A survey of new research directions in microprocessors, Elsevier Microprocessors and Microsystems 24 (2000), pp. 175-190. [SmSohi95] Smith, Sohi, The Microarchitecture of Superscalar Processors, Department of Electrical and Computer Engineering, 1415 Johnson Drive, Madison, WI53706, 1995 [SoBrVi95] Sohi, Breach, Vijaykumar, Multiscalar Processors. 22nd Ann. Int. Symp. on Computer Architecture, Santa Margherita Ligure, Italien, July 22-24, 1995, pp. 414-425. [SpEl94] Spoerle Electronic: Distribution Live – Mikrocontroller Special, 10/94 [Staudt94] von Staudt, Das professionelle PowerPC Buch, Franzis 1994 [Subram99] Subramania Sudharsanan, MAJC5200: A High Performance Microprocessor for Multimedia Computing, Sun Microsystems Inc., Palo Alto, CA, 1999 106 [SUN99a] MAJC Architecture Tutorial. White Paper. Sun Microsystems, 1999 [SUN02a] http://www.sun.com/processors/whitepapers/majcintro.html, 21.03.2002 [SUN02b] http://www.sun.com/sparc/UltraSPARC-III/index.html, 21.03.2002 [SUN02c] http://www.sun.com/processors/UltraSPARCIII/USIIITech.html, 21.03.2002 [Tabak91] Tabak, Advanced Microprocessors, McGraw-Hill 1991 [Tabak95] Tabak, Advanced Microprocessors, McGraw-Hill 1995 [TanGo99] Tanenbaum, Goodman, Computerarchitektur, Prentice Hall, 4. Auflage 1999 [Tremb99] Tremblay, MAJC-5200. AVLIW Convergent MPSOC., Microprocessor Forum, 1999 [TuEgLev95] Tullsen, Eggers, Levy, Simultaneous multithreading: maximizing on-chip parallelism, in: Proceedings of the ISCA 22, Santa Margherita Ligures, Italy, 1995, pp. 392-403 [Ungerer95] Ungerer, Mikroprozessortechnik, Thomson Publishing 1995 [Ungerer01] Ungerer, Mikroprozessoren. Stand der Technik und Forschungstrends, Informatik-Spektrum, 24.02.2001 [Weiss94] Weiss, POWER and PowerPC, Morgan-Kaufmann, 1994 Walker, A Model of Design Representation and Synthesis, 22nd Design Automation Conference, Las Vegas, 1985 [Wunderlich98] Wunderlich, BIST for Systems-on-a-Chip in Integration, The VLSI Journal, 1998 [YehPatt91] Tse-Yu Yeh, Yale Patt, „Two-Level Adaptive Training Branch Prediction“, 24th International Symposium Microarchitecture (Nov. 1991), p. 51-61 [Zilog01] Introduction to Embedded Webservers, Zilog, 2001, http://www.zilog.com/products/partdetails.asp, 21.03.2002 on 107 Erklärung Ich versichere hiermit, dass „Mikroprozessorarchitekturkonzepte“ ich meine selbständig und Bachelor-Abschlußarbeit ohne fremde Hilfe angefertigt habe, und dass ich alle von anderen Autoren wörtlich übernommenen Stellen wie auch die sich an die Gedankengänge anderer Autoren eng anlegenden Ausführungen meiner Arbeit besonders gekennzeichnet und die Quellen zitiert habe. Münster, den (Unterschrift)