Rechnerstrukturen: VO, WS 2012/13 → Teil 1 - VoWi
Rechnerstrukturen: VO, WS 2012/13 → Teil 1 - VoWi
Rechnerstrukturen: VO, WS 2012/13 → Teil 1 - VoWi
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>Rechnerstrukturen</strong>: <strong>VO</strong>, <strong>WS</strong> <strong>2012</strong>/<strong>13</strong> <strong>Teil</strong> 1<br />
(Kapitel 1 – Kapitel 4)<br />
Diese Ausarbeitung habe ich mithilfe der Vorlesungsfolien sowie der deutschen Fassung des<br />
Buches „Computer Organization And Design“ von David A. Patterson und John L. Hennessy<br />
gemacht. Sie ist zwar sehr umfangreich, sollte aber meiner Meinung nach alle wichtigen<br />
Themen der Vorlesung behandeln. Trotzdem sind Fehler und Unvollständigkeiten möglich.<br />
Da ich die meisten Informationen aus dem Buch habe, bin ich aber zuversichtlich, dass das<br />
meiste stimmen sollte ;-)<br />
Allerdings habe ich nicht alles von den Folien ausgearbeitet. Vernachlässigt werden in dieser<br />
Ausarbeitung unter anderem<br />
• Graphiken bzw. deren Erklärung (vgl. dazu besonders die Folien oder auch das Buch)<br />
• Rechenbeispiele (vgl. dazu besonders die Folien oder auch das Buch)<br />
• Kapitel 4 habe ich nicht vollständig ausgearbeitet (in diesem Kapitel befinden sich in<br />
den Folien auch einige Graphiken dazu also auch Folien + Buch vergleichen)<br />
Weitere Unvollständigkeiten sind möglich.<br />
Ich habe mit dieser Ausarbeitung jedenfalls für die Prüfung bei Prof. Grünbacher gelernt und<br />
eine Zwei bekommen :-)<br />
Autorin:<br />
Quellen:<br />
Michaela<br />
e1026306@student.tuwien.ac.at<br />
„Rechnerorganisation und Rechnerentwurf:<br />
Die Hardware/Software-Schnittstelle“<br />
von David A. Patterson und John L. Hennessy<br />
Folien von Prof. Herbert Grünbacher<br />
1
Chapter 1: Computer Abstractions and Technology<br />
Im Bereich der Computer-Technologie wurden bereits viele Fortschritte gemacht:<br />
• Computer in Fahrzeugen<br />
o senken Schadstoffausstoß<br />
o sorgen mittel Motorsteuerung für einen kraftstoffarmen Betrieb<br />
o automatische Brems- und Stabilitätskontrollsysteme erhöhte Fahrsicherheit<br />
o automatisches Auslösen eines Airbags<br />
• Handys<br />
• Forschung am menschlichen Genom<br />
o die Kosten für eine solche Erforschung sind gesunken (Hunderte von<br />
Millionen Dollar), daher ist überhaupt möglich, es zu erforschen<br />
• World Wide Web<br />
• Suchmaschinen<br />
Die Fortschritte in der Technologie haben großen Einfluss auf nahezu alle Bereiche unserer<br />
Gesellschaft.<br />
Es werden drei verschiedene Klassen von Computer unterschieden:<br />
• Desktop Computers (Arbeitsplatzrechner)<br />
o Wichtigster Vertreter dieser Klasse ist der Personal Computer (PC)<br />
o bietet Benutzern gute Leistungen zu akzeptablen Preisen (cost/performance tradeoff)<br />
o dient für gewöhnlich der Ausführung von Software von Drittanbietern<br />
• Server<br />
o müssen große Lasten bewältigen (entweder komplexe Anwendung oder Verarbeitung<br />
vieler kleiner Jobs)<br />
o basieren auf einem Netzwerk ( über ein Netzwerk wird zugegriffen)<br />
o häufig gleichzeitig von mehreren Benutzern verwendet<br />
o hohe Kapazität, Performance und Verlässlichkeit (reliability)<br />
o große Variationsbreite bezüglich Kosten und Funktionsfähigkeit (range from small<br />
servers to building sized)<br />
o Supercomputer: Computer der höchsten Leistungs- und Preisklasse; sind als Server<br />
konfiguriert und kosten in der Regel mehrere Millionen Euro; für anspruchsvolle<br />
Aufgaben aus dem technisch-wissenschaftlichen Bereich verwendet<br />
• Embedded Computers (processors, eingebettete Rechner)<br />
o finden sich in vielen Alltagsgeräten<br />
o sind normalerweise in ein System integriert, das als ein Gerät ausgeliefert wird<br />
o Benutzer merken oft nicht, dass sie einen Computer benutzen<br />
o strenge Leistungs-/Performance-/Kosten-Einschränkungen (power / performanc / cost<br />
= Kosten, Aufwand)<br />
Charakteristiken von Embedded Processors:<br />
- größte Klasse von Rechnersystemen<br />
- größte Bandbreite an Anwendungen und Leistungsfähigkeit<br />
2
- haben oft minimum performance requirements (Mindest-Leistungsanforderungen)<br />
- haben oft strenge Limitierungen bei Kosten/Aufwand<br />
- haben oft strenge Limitierungen bei Energieverbrauch<br />
- haben oft eine geringe Fehlertoleranz<br />
Um Performance zu verstehen, muss man die einzelnen Einflussfaktoren kennen, die dabei<br />
zusammenspielen:<br />
• Der Algorithmus<br />
o dieser bestimmt die Anzahl der Operationen, die ausgeführt werden<br />
• Die Programmiersprache, der Compiler und die Architektur<br />
o diese bestimmen die Anzahl der Maschineninstruktionen, die pro Operation<br />
ausgeführt werden<br />
• Der Prozessor und das Speichersystem (memory system)<br />
o diese bestimmen, wie schnell Instruktionen ausgeführt werden<br />
• I/O System (inkludiert OS = Operation System)<br />
o dieses bestimmt, wie schnell I/O Operationen ausgeführt werden<br />
Was verbirgt sich hinter einem<br />
Programm?<br />
• Die Application Software<br />
o wird in high-level language<br />
geschrieben<br />
• Die System Software<br />
Software, die allgemein nützliche<br />
Dienste bereit stellt<br />
o Compiler: übersetzt den high-level language Code in Maschinencode<br />
o Operation System: service code<br />
Das Betriebssystem ist die Schnittstelle zwischen einem Benutzerprogramm<br />
und der Hardware; es ist ein Programm mit Überwachungsfunktion<br />
<br />
<br />
<br />
• Die Hardware<br />
verarbeitet Input und Output (handling)<br />
verwaltet Massen- und Hauptspeicher (Storage und Memory)<br />
plant tasks und sharing resources (scheduling)<br />
o dazu gehören Prozessor, Memory, I/O Controllers<br />
3
Es gibt unterschiedliche Level von Programmcode:<br />
• High-level language<br />
o nicht so abstrakt<br />
o für Programmierer/innen leichter zu<br />
erlernen (der natürlichen Sprache<br />
ähnlicher, erleichtert auch das<br />
Verstehen fremden Codes), daher<br />
mehr Produktivität!<br />
bessere Portabilität (Übertragbarkeit)<br />
• Assembly language<br />
o textuelle Repräsentation von<br />
Instruktionen<br />
• Hardware representation<br />
o in Binärzahlen (bits)<br />
o kodierte Instruktionen und Daten<br />
Der Compiler übersetzt Anweisungen in einer höheren Programmiersprache in Anweisungen<br />
der Assemblersprache.<br />
Der Assembler wiederum ist ein Programm, das eine symbolische Form von Befehlen in eine<br />
binäre Form übersetzt. Die Assemblersprache ist dabei eine symbolische Darstellung von<br />
Maschinenbefehlen.<br />
Die Komponenten eines Computers<br />
Alle Arten von Computern haben die gleichen Komponenten.<br />
Wichtige Komponenten für Input/Output sind dabei:<br />
• User Interface Devices<br />
o Display, Tastatur, Maus<br />
• Storage Devices<br />
o Hard disk, CD/DVD, flash<br />
• Network adapters<br />
o für die Kommunikation mit anderen Computern<br />
Die fünf klassischen Komponenten eines Computers umfassen die Ein- und Ausgabe, den<br />
Hauptspeicher, das Rechenwerk und das Steuer- oder Leitwerk, wobei die letzten zwei Werke<br />
zusammengefasst und als Prozessor bezeichnet werden.<br />
4
Motherboard<br />
Eine Platine, auf der die wichtigsten Komponenten des Rechners miteinander verschaltet sind<br />
wie Prozessor, Speicher und Schnittstellen für zusätzliche Komponenten (z.B. Laufwerke<br />
oder Graphikkarten).<br />
Integrated Circuit (IC, Integrierte Schaltkreise)<br />
Auch als Chip bezeichnet. Eine Einheit mit bis zu vielen Millionen von Transistoren.<br />
Hauptspeicher (memory)<br />
Der Speicher, in dem sich Programme befinden, wenn sie ausgeführt werden. Daneben<br />
befinden sich im Hauptspeicher die Daten, die von diesen Programmen benötigt werden.<br />
Auch als Arbeitsspeicher bezeichnet.<br />
DRAM (Dynamic Random Access Memory)<br />
Hauptspeicher, der in Form eines Chips gebaut ist; er bietet wahlfreien Zugriff auf jeden<br />
Speicherplatz.<br />
CPU (Central Processing Unit)<br />
Auch als Prozessor bezeichnet. Der aktive <strong>Teil</strong> des Computers, der das Rechenwerk und das<br />
Leitwerk b zw. Steuerwerk enthält und Zahlen addiert, Zahlen vergleicht, Signale zum<br />
Aktivieren der Ein-/Ausgabegeräte sendet usw.<br />
5
Datapath (Rechenwerk)<br />
Die Komponente des Prozessors, die arithmetische Operationen ausführt.<br />
Control (Leitwerk)<br />
Die Komponente des Prozessors, die den Datenpfad, den Hauptspeicher und die Ein-<br />
/Ausgabegeräte entsprechend der Programmbefehle ansteuert.<br />
Cache-Memory<br />
Kurz Cache genannt, ist ein kleiner, schneller Speicher, der als Puffer für einen langsamen,<br />
größeren Speicher dient.<br />
Auch: Small fast SRAM memory for immediate access to data.<br />
SRAM (Static Random Access Memory)<br />
Ebenfalls als Chip gebauter Hauptspeicher, aber schneller und weniger dicht als DRAM.<br />
BIG PICTURE – Abstractions<br />
Sowohl die Hardware als auch die Software bestehen aus hierarchischen Ebenen, wobei die<br />
unteren Ebenen jeweils mehr Details enthalten als die oberen Ebenen. Dieses Prinzip der<br />
Abstraktion hilft Hardware- und Softwareentwicklern im Umgang mit komplexen<br />
Computersystemen. Eine wichtige Schnittstelle zwischen den Abstraktionsebenen stellt die<br />
Instruction Set Architecture (ISA = Befehlssatzarchitektur) dar. Hierbei handelt es sich um<br />
die Schnittstellte zwischen der Hardware und der Software auf Maschinenebene. Diese<br />
abstrakte Schnittstelle ermöglicht viele Implementierungen, die sich in den Kosten und der<br />
Leistung unterscheiden, aber die gleiche Software ausführen können.<br />
6
Instruction Set Architecture (ISA)<br />
Auch als Architektur bezeichnet. Eine abstrakte Schnittstelle zwischen der Hardware und der<br />
untersten Softwareebene einer Maschine.<br />
(Instruction Set: Der Wortschatz mit den Befehlen, die eine bestimmte Architektur versteht.)<br />
Application Binary Interface (ABI)<br />
Der Benutzerteil des instruction sets (Befehlssatzes) und die Betriebssystemschnittstellen, die<br />
von Anwendungsprogrammierern verwendet werden. Definiert einen Standard für binäre<br />
Portierbarkeit zwischen Computern.<br />
Wie werden Daten gespeichert?<br />
Es gibt flüchtige Speicher (volatile memories) und nichtflüchtige Speicher (non-volatile<br />
memories). Zu den flüchtigen Speichern zählt z.B. der Hauptspeicher (main memory): dort<br />
gehen alle Instruktionen und Daten verloren, wenn der Strom abgeschaltet wird.<br />
Bei nichtflüchtigen Speichern bleiben Daten jedoch auch nach abschalten des Stroms<br />
gespeichert. Zu den nichtflüchtigen Speichern zählen:<br />
- Magnetic disk ( ↑ 1 Terrabyte)<br />
- Flash memory ( ↑ 256 GB)<br />
- Optical disk (CDROM, DVD, Blue Ray)<br />
Primärspeicher<br />
Auch Hauptspeicher genannt. Flüchtiger Speicher von Programmen während der Ausführung.<br />
Sekundärspeicher<br />
Nichtflüchtiger Speicher, der zum Speichern von Programmen und Daten zwischen<br />
Ausführungsvorgängen verwendet wird.<br />
Festplatte (magnetic disk)<br />
Eine Form des nichtflüchtigen Sekundärspeichers, bestehend aus rotierenden Platten, die zum<br />
Speichern von Daten mit einem magnetisierbaren Material beschichtet sind.<br />
Flash Memory<br />
Ein nichtflüchtiger Halbleiterspeicher. Billiger und schneller als DRAM, aber teurer und<br />
schneller als Magnetplatten.<br />
Netzwerke<br />
Über Netzwerke kann Kommunikation betrieben werden und Ressourcen geteilt werden.<br />
Dabei gibt es verschiedene Arten von Netzwerken.<br />
Local Area Network (LAN)<br />
Ein Netzwerk zum Übertragen von Daten innerhalb eines geographisch begrenzten Bereiches,<br />
in der Regel innerhalb eines Gebäudes; Stichwort: Ethernet.<br />
7
Wide Area Network (WAN)<br />
Ein Netzwerk, das sich über Hunderte von Kilometern erstreckt oder einen ganzen Kontinent<br />
umspannen kann; Stichwort: Internet<br />
Wireless Networks<br />
WiFi, Bluetooth<br />
Was besagt Moore’s Law?<br />
Die Anzahl an Transistoren, die auf einem einzigen Chip integriert werden können, wird sich<br />
ca. alle 2 Jahre verdoppeln.<br />
Wie lässt sich Performance definieren?<br />
Das Beispiel der Flugzeuge zeigt, dass man nicht eindeutig sagen kann, was gute Performance<br />
ist. Jedes dieser Flugzeuge hat seine Vorteile und seine Nachteile. Je nachdem, auf welche<br />
Aspekte man besonders Wert legt, hat dann das eine oder das andere eine bessere<br />
Performance. Es gilt also bei der Beurteilung von Performance, unterschiedliche Aspekte zu<br />
berückrichtigen.<br />
• Response Time<br />
o Wie lange braucht die Ausführung eines Tasks?<br />
• Troughput<br />
o Totale Arbeit, die in einer bestimmten Zeiteinheit geleistet wird<br />
o Übersetzt: Durchsatz; wird auch als Bandbreite bezeichnet<br />
8
Performance = 1 / Execution Time<br />
Wenn man Performance miteinander vergleichen möchte, kann man z.B. sagen:<br />
„X ist n-mal schneller als Y.“<br />
Performance x / Performance Y = Execution Time Y / Execution Time X<br />
Elapsed Time (verstrichene Zeit)<br />
Totale Response Time, die alle Aspekte inkludiert: Processing, I/O, OS overhead, idle time<br />
CPU Time<br />
Die tatsächliche Zeit, die die CPU braucht, um einen bestimmte Aufgabe abzuwickeln. Die<br />
Zeit, die dabei für das Warten auf ein-/Ausgaben oder für die Ausführung anderer Programme<br />
benötigt wird, wird dabei nicht miteinbezogen.<br />
Somit ist die Antwortzeit, die der Benutzer wahrnimmt, nicht die CPU-Zeit!<br />
Die CPU-Time lässt sich unterteilen in User CPU Time und in System CPU Time.<br />
User CPU Time ist dabei die Zeit, die innerhalb des Programms aufgewendet wird und<br />
System CPU Time die Zeit, die im Betriebssystem verbracht wird, wobei Aufgaben für das<br />
Programm ausgeführt werden.<br />
CPU Clocking<br />
Ereignisse innerhalb der Hardware werden von einem konstanten Taktzyklus (Clock)<br />
beherrscht. Diese Taktzyklen sind diskrete Zeitintervalle. Dabei wird mit clock period die<br />
Dauer eines Taktzykluses (clock cycles) bezeichnet und mit clock frequency (rate) die<br />
Zyklen pro Sekunde.<br />
CPU Time<br />
9
Die Performance kann verbessert werden durch:<br />
• Reduzieren der Anzahl der Clock Cycles (Taktzyklen)<br />
• Erhöhen der Clock Rate (d.h. mehr Zyklen pro Sekunde)<br />
Hardware Designer müssen oft Clock Rate und Cycle Count aufeinander abstimmen.<br />
Instruction Count und CPI (Cylces per instruction)<br />
Instruction Count ist die Anzahl der Instruktionen, die ausgeführt werden sollen (z.B. eines<br />
Programms).<br />
CPI (cycles per instruction) ist die durchschnittliche Anzahl der Taktzyklen, die pro<br />
Instruktion durchlaufen werden.<br />
Der Instruction Count wird vom Programm bestimmt sowie von ISA (Instruction Set<br />
Architecture) und Compiler.<br />
Die durchschnittlichen cycles per instruction werden durch die CPU Hardware bestimmt.<br />
Wenn unterschiedliche Befehle unterschiedliche CPIs haben, bestimmt der Mix an Befehlen<br />
die durchschnittlichen CPI.<br />
Wenn unterschiedliche Instruction-Klassen eine unterschiedliche Anzahl an Zyklen brauchen:<br />
Die CPI sind dann gewichtet:<br />
10
Zusammenfassung: Performance<br />
Die Performance hängt von folgenden Faktoren ab:<br />
• Algorithmus<br />
o beeinflusst den Instruction Count (IC), möglicherweise auch CPI<br />
• Programmiersprache<br />
o beeinflusst den IC sowie CPI<br />
• Compiler<br />
o beeinflusst IC und CPI<br />
• Instruction set architecture<br />
o beeinflusst IC, CPI, T C ( Taktgeschwindigkeit)<br />
Was ist die Power Wall?<br />
• Wir können die elektrische Spannung (voltage) nicht weiter reduzieren.<br />
• Wir können nicht mehr Hitze verschwinden lassen.<br />
Wie kann man also noch Performance verbessern?<br />
11
Multiprocessors<br />
Unter multicore microprocessors versteht man, dass sich mehr als ein Prozessor auf einem<br />
Chip befinden. Dabei ist explizit paralleles Programmieren erforderlich.<br />
Die Hardware muss mehrere Instructions gleichzeitig ausführen. Was und wie es geschieht,<br />
soll jedoch vor dem Benutzer verborgen bleiben.<br />
12
Chapter 2: Instructions – Language of the Computer<br />
Instruction Set<br />
Unter einem Instruction Set versteht man den Wortschatz mit den Befehle, die eine bestimmte<br />
Architektur versteht.<br />
Unterschiedliche Computer können auch unterschiedliche Instruction Sets haben. Allerdings<br />
haben sie stets viele Aspekte gemeinsam.<br />
In frühen Computern waren die Instruction Sets sehr simple. Aber auch heute gibt es moderne<br />
Computer, deren Instruction Sets sehr simple sind.<br />
Eines der bekannten Instruction Sets ist das MIPS Instruction Set.<br />
MIPS-32 ISA ist in unterschiedliche Instruction-Kategorien aufgeteilt, die hier aufgelistet<br />
werden:<br />
<br />
<br />
<br />
<br />
<br />
<br />
Computational (rechenbetont)<br />
Load/Store (laden/speichern)<br />
Jump and Branch (Sprung und Verzweigung)<br />
Floating Point (Gleitkomma)<br />
wichtig dabei: Coprocessor (= <strong>Teil</strong> eines<br />
Rechnersystems, das einen peripheren Prozessor<br />
gegenüber dem zentralen Prozessor (CPU) darstellt, weil<br />
er unterstützende Funktionen, insbesondere bei<br />
mathematischen Aufgaben, erfüllt)<br />
Memory Management (Speicherverwaltung)<br />
Special<br />
Arithmetische Operationen in der MIPS Architektur<br />
Die Addition, die Subtraktion, die Multiplikation und die Division sind die vier grundlegenden<br />
Rechenarten in der Arithmetik.<br />
Arithmetische Operationen sind in der MIPS Architektur stets gleich aufgebaut:<br />
• drei Operanden<br />
o zwei Quellen<br />
o ein Ziel<br />
<strong>13</strong>
Beispiel für die Addition:<br />
add a, b, c<br />
# a gets b + c<br />
Design Prinzip 1:<br />
Simplicity favours regularity (Einfachheit begünstigt Regelmäßigkeit)<br />
Regelmäßigkeit macht die Implementierung einfacher<br />
Einfachheit ermöglicht höhere Performance und niedrigere Kosten/Aufwand<br />
Beispiel aus der Arithmetik:<br />
C code:<br />
f = (g + h) – (i + j)<br />
Compiled MIPS code:<br />
add t0, g, h # temp t0 = g + h<br />
add t1, i, j # temp t1 = i + j<br />
sub f, t0, t1 # f = t0 – t1<br />
Für Operanden arithmetischer Befehle gelten bestimmte Einschränkungen. Sie müssen an<br />
speziellen Stellen im Rechner bereitstehe, die jedoch nur in einer beschränkten Anzahl zur<br />
Verfügung stehen: den Registern.<br />
Register sind elementare Komponenten beim Hardware-Entwurf und bilden die<br />
Grundbausteine für den Aufbau von Rechnern. Nach der Fertigstellung des Rechners sind sie<br />
auch für den Programmierer sichtbar.<br />
Die Größe eines Registers bei der MIPS Architektur beträgt 32 Bit.<br />
Die Zusammenfassung von 32 Bit zu einer Einheit geschieht sehr häufig und erhält deshalb<br />
bei der MIPS Architektur eine eigene Bezeichnung, nämlich Word.<br />
Die übliche Anzahl von Registern in einem Rechner ist 32. Der Unterschied zu Variablen ist,<br />
dass diese unbegrenzt sind, während Register nur in begrenzter Anzahl zur Verfügung stehen.<br />
Die Register in der MIPS Architektur sind von 0 bis 31 nummeriert.<br />
Wie spricht man Register an?<br />
Die Konvention bei MIPS für die Bezeichnung von Registern ist das Dollarzeichen gefolgt<br />
von zwei Zeichen. Zurzeit werden folgende Bezeichnungen verwendet:<br />
$t0, $t1, …, $t9 für temporärere Werte<br />
(zum Kompilieren des Programms in MIPS Befehle benötigt)<br />
$s0, $s1, …, $s7 für gespeicherte Variablen<br />
(entsprechen einer Variablen in C und Java-Programmen)<br />
14
Design Prinzip 2:<br />
Smaller is faster (Kleiner ist schneller)<br />
<br />
Bsp. MIPS: große Anzahl von Registern kann zu einer längeren Taktzykluszeit<br />
führen, da die elektronischen Signale für den weiteren Weg mehr Zeit benötigen.<br />
Beispiel für Registeroperanden:<br />
C code:<br />
f = (g + h) – (i + j);<br />
f, …, j in $s0, …, $s4<br />
Compiled MIPS code:<br />
add $t0, §s1, $s2<br />
add $t1, $s3, $s4<br />
sub $s0, $t0, $t1<br />
# temp t0 = g + h<br />
# temp t1 = i + j<br />
# f = t0 – t1<br />
Register sind:<br />
<br />
<br />
<br />
schneller als der Hauptspeicher<br />
o aber Registerfiles mit mehr Locations sind langsamer<br />
o Read/write port erhöht die impacts speed (Aufprallgeschwindigkeit)<br />
quadratisch<br />
einfacher für den Compiler zu verwenden<br />
o z.B. (A*B) – (C*D) – (E*F) kann in einer beliebigen Reihenfolge<br />
multiplizieren (vs. Stack)<br />
können Variablen so speichern, dass sich die Codedichte verbessert<br />
o Registernamen benötigen weniger Bits als eine Memory Location<br />
15
Speicheroperanden (Memory Operands)<br />
Datenstrukturen wie Arrays, Strukturen und dynamische Daten werden im Hauptspeicher<br />
abgelegt. Register können nur eine kleine Menge von Daten halten, der Hauptspeicher kann<br />
hingegen Millionen von Datenelementen speichern.<br />
Da bei arithmetischen Operationen im MIPS-Befehlssatz nur Register verwendet werden,<br />
muss es auch eine Möglichkeit geben, Daten zwischen Hauptspeicher und Register zu<br />
transportieren. Diese Befehle werden data transfer instructions (Datentransfer-Befehle)<br />
genannt. Wichtig ist dabei:<br />
das Laden von Werten aus dem Hauptspeicher in die Register<br />
das Speichern von Ergebnissen aus den Registern im Hauptspeicher<br />
Um auf ein Wort im Hauptspeicher zugreifen zu können, benötigt man in dem entsprechenden<br />
Befehl die Speicheradresse (address). Jede Adresse identifiziert ein 8-bit Byte.<br />
Words sind im Hauptspeicher aneinander gereiht. Die Adresse muss also ein Vielfaches von 4<br />
sein. Grund: ein Word hat 32-Bit, also 4 Byte. Die Adresse 0 beispielsweise adressiert den<br />
Beginn der ersten 32-Bit. Die nächsten 32-Bit liegen dann also nicht unter der Adresse 1,<br />
sondern unter der Adresse 4 (4 Bytes weiter).<br />
MIPS verwendet Big Endian. Im Gegensatz<br />
dazu gibt es noch Little Endian.<br />
Der Unterschied ist folgender:<br />
Bei Big Endian ist das Byte ganz links die Word-Adresse.<br />
Bei Little Endian ist das Byte ganz rechts die Word-Adresse.<br />
Beispiel:<br />
Die Ganzzahl 439.041.101 (439 Millionen...) wird als 32-Bit-Integer-Wert gespeichert (Binär:<br />
00011010 00101011 00111100 01001101, hexadezimal: 1A 2B 3C 4D). Die Speicherung<br />
erfolgt in vier Bytes ab der hypothetischen Speicheradresse 10000.<br />
Wenn die Speicherung in der Reihenfolge 1A 2B 3C 4D erfolgt, entspricht dies Big Endian.<br />
Die Speicherung in der umgekehrten Reihenfolge (4D 3C 2B 1A), also das am wenigsten<br />
signifikante Byte an der niedrigsten Speicheradresse, entspricht dagegen Little Endian.<br />
Einige ältere Systeme (z. B. PDP-11) speichern die Daten auch in der Reihenfolge 3C 4D 1A<br />
2B oder auch 2B 1A 4D 3C. Dies wird als Middle Endian bezeichnet.<br />
(Quelle: Wikipedia)<br />
16
Register vs. Memory<br />
• Auf Register kann schneller zugegriffen werden als auf den Hauptspeicher<br />
• Wenn man mit Daten aus dem Hauptspeicher arbeitet, sind loads und stores<br />
erforderlich<br />
o mehr Instruktionen müssen ausgeführt werden<br />
• Der Compiler muss so oft wie möglicher Register für Variablen verwenden<br />
o nur Variablen, die weniger oft verwendet werden, sollen an den Hauptspeicher<br />
gehen<br />
o die Optimierung bezüglich Register ist wichtig!<br />
Beispiele für Speicheroperanden:<br />
C code:<br />
g = h + A[8]<br />
g in $s1, h in $s2, base address von A in $s3<br />
Compiled MIPS code:<br />
Index 8 erfordert ein Offset von 32 (4 Byte pro Word)<br />
lw $t0, 32($s3) # load word<br />
add $s1, $s2, $t0<br />
32 ist das Offset, $s3 das base register<br />
C code:<br />
A[12] = h + A[8]<br />
h in $s2, base address von A in $s3<br />
Compiled MIPS code:<br />
Index 8 erfordert ein Offset von 32<br />
lw $t0, 32($s3) # load word<br />
add $t0, $s2, $t0<br />
sw $t0, 48($s3) # store word<br />
Konstanten oder Direktoperationen<br />
In Programmen werden in Operationen häufig Konstanten verwendet, z.B. beim<br />
Inkrementieren eines Index, damit dieser auf das nächste Element eines Felds zeigt. Wenn wir<br />
nur die bisher bekannten Befehle verwenden würden müssten wir eine Konstante aus dem<br />
Hauptspeicher laden, um sie zu verwenden (diese müsste beim Laden des Programms im<br />
Speicher abgelegt werden).<br />
17
Eine Alternative, die keinen Ladebefehl erfordert, besteht darin, Versionen der arithmetischen<br />
Befehle bereitzustellen, bei denen ein Operand eine Konstante ist. Dieser schnelle Add-Befehl<br />
wird als add immediate („addiert direkt“) oder addi bezeichnet.<br />
Um beispielsweise die Konstante 4 zum Inhalt des Registers $s3 zu addieren, schreiben wir<br />
einfach:<br />
addi $s3, $s3, 4 # $s3 = $s3 + 4<br />
anstelle von<br />
lw $t0, AddrConstant4($s1) # §t0 = 4<br />
add $s3, $s3, $t0<br />
Für die Subtraktion gibt es keine immediate instruction. Allerdings kann man einfach eine<br />
negative Konstante verwenden.<br />
addi $s2, $s1, -1<br />
Design Prinzip 3:<br />
Make the common case fast (Optimiere den häufig vorkommenden Fall)<br />
Kleine Konstanten sind üblich<br />
der Immediat Operand vermeidet eine Load Instruction<br />
Die Konstante Null<br />
Im MIPS Register 0 ist die Konstante 0 gespeichert ($zero). Diese kann nicht überschrieben<br />
werden! Sie ist bei häufig vorkommenden Operationen nützlich, z.B. beim Verschieben vom<br />
Inhalt eines Registers zu einem anderen Register.<br />
add $t2, $s2, $zero<br />
Vorzeichenbehaftete und nicht-vorzeichenbehaftete Zahlen<br />
Da Computer sowohl positive als auch negative Zahlen berechnen, wird eine Darstellung<br />
benötigt, um diese zu unterscheiden. Eine Lösung wer, ein gesondertes Zeichen einzufügen,<br />
das sich in geeigneter Weise mit einem einzigen Bit darstellen lässt. Diese Darstellung wird<br />
als Vorzeichen-Betrag-Darstellung bezeichnet. Nachteile dabei sind allerdings, dass nicht<br />
genau klar ist, wo das Vorzeichen eingefügt werden soll (rechts oder links?). Außerdem muss<br />
18
das Vorzeichen stets extra mitbeachtet werden (beim Berechnen einer Zahl muss es<br />
anschließend richtig gesetzt werden; es macht eine positive und eine negative 0 möglich, was<br />
zu Fehlern führen kann, etc.). Aufgrund der Nachteile wurde diese Idee nicht weiter verfolgt.<br />
Alternative: Subtrahiert man eine große Zahl von einer kleinen, wird wegen der führenden<br />
Nullen jeweils eine 1 weitergegeben, so dass das Ergebnis eine Folge aus führenden Einsen<br />
enthält. Deshalb kam man auf die Lösung, dass führende 0en für eine positive Zahl stehen<br />
und führende 1en für eine negative Zahl. Diese Darstellung wird als Zweier-Komplement-<br />
Darstellung bezeichnet. Bei 32 Bit wäre also Bit 31 das sogenannte sign bit.<br />
Vorzeichenlose Zahlen haben dabei dieselbe vorzeichenlose und Zweier-Komplement-<br />
Darstellung.<br />
Ein einfacher Weg, um eine vorzeichenbehaftete Zahl zu negieren?<br />
Der einfachste Weg ist, das Komplement der Zahl zu bilden (d.h. alle 1 werden zu 0 und alle<br />
0 werden zu 1) und anschließend 1 zu addieren.<br />
Beispiel:<br />
+2 = 0000 0000 … 0010 2<br />
–2 = 1111 1111 … 1101 2 + 1<br />
= 1111 1111 … 1110 2<br />
Sign Extension (Vorzeichenerweiterung)<br />
Beispiel: 8-bit zu 16-bit<br />
+2: 0000 0010 0000 0000 0000 0010<br />
–2: 1111 1110 1111 1111 1111 1110<br />
________________<br />
Können die beiden Summanden beliebige Werte annehmen, ist für eine korrekte Addition in<br />
Zweierkomplementdarstellung eine Vorzeichenerweiterung nötig. Dabei wird von beiden<br />
Summanden zunächst die oberste Stelle dupliziert und somit die Stellenanzahl um eins<br />
vergrößert. In diesen Beispielen die 8. Stelle, welche auf die 9. Stelle kopiert wird.<br />
Anschließend wird die Addition wie oben, aber mit 9 Stellen, durchgeführt. Das Addierwerk<br />
muss dazu immer eine Stelle mehr umfassen.<br />
Unterscheiden sich in der berechneten Summe dann die höchstwertige und die Stelle darunter<br />
voneinander, ist das Ergebnis nicht mehr im Wertebereich der Summanden darstellbar – es ist<br />
ein Überlauf aufgetreten. Je nach Anwendungsfall wird dann mit dem um ein Bit breiteren<br />
und korrekten Ergebnis weitergerechnet oder ein Fehlerabbruch ist die Folge.<br />
Beispiel: Die Addition der beiden positiven Zahlen 50 und 80 ergibt <strong>13</strong>0 und überschreitet<br />
damit den Wertebereich. Die Zahl passt zwar noch in eine 8-Bit-Variable, aber das 8. Bit ist<br />
jetzt gesetzt, so dass die Zahl fälschlicherweise negativ erscheint. Manche Mikroprozessoren<br />
wie der 6502 melden ein solches Ereignis mit einem eigenen Statusbit, hier dem Overflow-Bit<br />
O, das der Programmierer nach vorzeichenbehafteten Rechenoperationen abfragt und<br />
entsprechend reagieren kann.<br />
19
Beispiel für Vorzeichenerweiterung, die 9. Stelle der Vorzeichenerweiterung ist zur<br />
Verdeutlichung in Klammern geschrieben:<br />
+4 + 127 = +<strong>13</strong>1 führt zu −4 − 127 = −<strong>13</strong>1 führt zu<br />
(0)00000100 (1)11111100<br />
+ (0)01111111 + (1)10000001<br />
----------- -----------<br />
Ü (0)11111000 Ü (1)00000000<br />
----------- -----------<br />
= (0)10000011 = (1)01111101<br />
In beiden Fällen unterscheiden sich die 8. und 9. Stelle voneinander, eine Reduktion auf 8 Bit<br />
würde zu einem Fehler führen. Zur Verdeutlichung und Vergleich die obigen beiden Beispiele<br />
mit Vorzeichenerweiterung:<br />
+4 − 3 = +1 führt zu −4 − 3 = −7 führt zu<br />
(0)00000100 (1)11111100<br />
+ (1)11111101 + (1)11111101<br />
----------- -----------<br />
Ü (1)11111000 Ü (1)11111000<br />
----------- -----------<br />
= (0)00000001 = (1)11111001<br />
in beiden Fällen unterscheiden sich die 8. und 9. Stelle der Summe nicht, die beiden<br />
Ergebnisse können somit korrekt wieder auf 8 Stellen reduziert werden. Generell kann die<br />
Stellenanzahl in der Zweierkomplementdarstellung, von oben beginnend, so lange und ohne<br />
Verfälschung des Wertes reduziert werden, bis sich die beiden obersten Stellen im Wert<br />
voneinander unterscheiden. Das verdeutlicht den Umstand, dass bei der Zweierkomplementdarstellung<br />
von Zahlen keine fixe Stelle für die Codierung des Vorzeichens existiert.<br />
Quelle: Wikipedia<br />
Darstellung von Instructions (Befehlen im Rechner)<br />
Instructions sind binär codiert. Die Bezeichnung dafür lautet Maschinencode.<br />
Befehle werden im Rechner als Folge elektronischer Signale mit jeweils hohem und<br />
niedrigem Potenzial betrachtet und können somit als Zahlen interpretiert werden. Die<br />
einzelnen Zahlen aneinander gereiht ergeben den Befehl. In MIPS ist genau festgelegt, welche<br />
Register welchen Stellen zuordnet werden.<br />
20
Damit es leichter ist, MIPS Felder anzusprechen, haben diese bestimmte Namen:<br />
op 6-bits opcode; spezifiziert die Operation<br />
rs 5-bits register file address des ersten Source-Operanden<br />
rt 5-bits register file address des zweiten Source-Operanden<br />
rd 5-bits register file address des Ziels für das Ergebnis<br />
shamt 5-bits shift amount (für shift instructions)<br />
funct 6-bits function code; erweitert den opcode<br />
R-Typ (für Register) oder auch R-Format<br />
Hexadezimal<br />
Hexadezimal-Darstellung ist eine kompakte Repräsentation von Bit Strings. Die Basis ist 16.<br />
Jede hex digit (hex Ziffer) besteht aus 4 bits.<br />
Da 16 eine Potenz von 2 ist, kann einfach jede Gruppe mit vier Binärziffern durch eine<br />
hexadezimale Ziffer ersetzt werden.<br />
21
MIPS I-format Instructions<br />
Immediat arithmetic instructions und load/store instructions sind so aufgebaut wie es die<br />
obere Graphik zeigt.<br />
rt ist dabei die Ziel- oder Quell-Register-Nummer<br />
Die Konstante bewegt sich im Bereich –2 15 bis +2 15 – 1<br />
Die Adresse ist ein Offset, dass zur base address in rs addiert wird<br />
Diese Instructions müssen anders aufgebaut werden (als andere MIPS Befehle). Grund dafür<br />
ist, dass sie längere Felder brauchen als sie in der op-rs-rt-rd-shamt-funct-Darstellung<br />
bereitgestellt werden. Beispiel: Im Load-word-Befehl müssen zwei Register und eine<br />
Konstante angegeben werden. Würde man für die Adresse eines der 5-Bit-Felder verwenden,<br />
wäre die Konstante auf nur 2 5 (= 32) begrenzt. Das ist oftmals zu klein. Daher gibt es<br />
unterschiedliche Formate für unterschiedliche Befehlsarten.<br />
Das Format, das in diesem Abschnitt vorgestellt wurde, wird I-Typ (für immediate = direkt)<br />
oder auch I-Format genannt und für Immediate- und Datentransfer-Befehle verwendet.<br />
Die unterschiedlichen Formate unterscheiden sich durch die Werte im ersten Feld. Jedem<br />
Format ist eine Reihe von Werten im ersten Feld (op) zugewiesen, do dass die Hardware<br />
weiß, ob die zweite Hälfte des Befehls als drei Felder (R-Typ) oder als ein Feld (I-Typ)<br />
behandelt werden muss.<br />
Design Prinzip 4:<br />
Good design demands good compromises (Ein guter Entwurf erfordert gute Kompromisse)<br />
<br />
<br />
Unterschiedliche Formate machen das Entschlüsseln komplizierter, aber erlauben im<br />
oben beschriebenen Fall ein größeres Feld z.B. für die Konstante<br />
die Formate sollen so ähnlich wie möglich gehalten werden<br />
Überblick: MIPS (RISC) Design Principles<br />
1. Simplicity favours regularity<br />
Instructions mit fixer Länge<br />
kleine Anzahl an unterschiedlichen Instruction Formaten<br />
die ersten 6 bits machen immer den opcode aus<br />
2. Smaller is faster<br />
limitiertes instruction set<br />
limitierte Anzahl an Registern im Register File<br />
limitiere Anzahl an addressing modes (Adressverfahren/-art)<br />
22
3. Make the common case fast<br />
arithmetische Operanden werden dem Register File entnommen (load-storemachine)<br />
es ist erlaubt, dass Instructions immediat operands enthalten<br />
4. Good design demands good compromises<br />
drei Instruction Formate<br />
Computer von heute beruhen auf zwei Grundprinzipien:<br />
1. Befehle werden in Form von Zahlen dargestellt<br />
2. Programme werden wie Zahlen im<br />
Hauptspeicher gespeichert, um gelesen oder<br />
geschrieben werden zu können<br />
Die Tatsache, dass Befehle in Form von Zahlen<br />
dargestellt werden können, hat zur folge, dass<br />
Programme häufig als Dateien mit Binärzahlen<br />
ausgeliefert werden. Die kommerzielle Folge hiervon<br />
ist, dass Rechner fertige Programme übernehmen<br />
können, vorausgesetzt sie sind zu einem vorhandenen<br />
Befehlssatz kompatibel. Diese „Binärkompatibilität“<br />
führt dazu, dass sich die Industrie auf wenige<br />
Befehlssatzarchitekturen konzentriert.<br />
Logische Operationen<br />
Logische Operationen dienen der bitweisen Manipulation. Oft ist es wichtig, auf Bitfelder in<br />
einem Wort oder auch auf einzelne Bits zugreifen zu können. Logische Operationen sind z.B.<br />
nützlich für das Extrahieren bzw. Einfügen von Gruppen an Bits aus einem bzw. in ein Word.<br />
Eine Klasse dieser Operationen sind die Schiebeoperationen oder auch Shift Operations.<br />
Sie schieben alle Bits in einem Wort nach links oder nach rechts, wobei die frei werdenden<br />
Bits mit einer Null aufgefüllt werden.<br />
23
Um auf die MIPS Architektur zurückzukommen: das 5-bits-Feld shamt bei einer Instruction<br />
kann speichern, um wie viele Positionen geshiftet werden soll.<br />
sll by i bits multipliziert dabei mit 2 i<br />
srl by i bits dividert durch 2 i<br />
(shift logical left)<br />
(shift logical right)<br />
Beispiel:<br />
sll $t2, $s0, 4<br />
# Reg. $t2 = Reg. $s0
Im vollständigen MIPS-Befehlssatz gibt es weiters auch ein XOR (Exklusives ODER), das<br />
das Bit auf 1 setzt, wenn sich zwei einander entsprechende Bits unterschieden, und auf 0,<br />
wenn sie gleich sind.<br />
Conditional Operations (Befehle zum Treffen von Entscheidungen)<br />
Ein Computer kann Entscheidungen treffen. Abhängig von den Eingabedaten und den<br />
während der Berechnung erhaltenen Werten werden unterschiedliche Befehle ausgeführt. In<br />
Programmiersprachen finden sich Entscheidungen in der Regel in Form von if-Anweisungen,<br />
gelegentlich auch zusammen mit Go-To-Anweisungen und Sprungmarken.<br />
In der MIPS Architektur gibt es für solche Entscheidungen folgende (ähnliche) Befehle:<br />
beq rs, rt, L1<br />
wenn (rs == rt) wird eine Verzweigung zur Instruction, die mit L1 markiert ist, gestartet<br />
bne rs, rt, L1<br />
wenn (rs != rt) wird eine Verzweigung zur Instruction, die mit L1 markiert ist, gestartet<br />
Weiters gibt es noch die Jump-Anweisung, die folgendermaßen aussieht:<br />
j L1<br />
dabei wird zur Instruction L1 gesprungen, ohne dass eine Bestimme Bedingung erfüllt sein<br />
muss.<br />
Was versteht man unter einem Basic Block?<br />
Eine Befehlsfolge ohne Sprünge, außer möglicherweise m Ende der Befehlsfolge, und ohne<br />
Sprungziel oder Sprungmarke, außer möglicherweise am Anfang der Befehlsfolge, werden als<br />
Basisblock (oder Grundblock) bezeichnet.<br />
Einer der ersten Schritte beim Kompilieren besteht darin, das Programm in Grundblöcke zu<br />
zerlegen.<br />
Besonders häufig wird die Gleichheit oder Ungleichheit zweier Werte geprüft. Gelegentlich<br />
ist es aber auch hilfreich, festzustellen, ob eine Variable z.B. kleiner als eine andere Variable<br />
ist. Wichtig ist das z.B. bei einer for-Schleife (abfragen, ob eine Index-Variable kleiner 0 ist).<br />
Dafür gibt es in der MIPS Architektur folgenden Befehl:<br />
slt $t0, $s3, $s4 # $t0 = 1 if $s3 < $s4, sonst 0<br />
slt steht dabei für set less than.<br />
25
Es gibt auch eine immediate Version des slt-Befehls, da Konstanten als Operanden gerne für<br />
einen solchen Vergleich herangezogen werden. Beispiel hierfür:<br />
slti $t0, $s2, 10 # $t0 = 1 if $s2 < 10, sonst 0<br />
Der Befehl slt kann in Kombination mit beq, bne verwendet werden. Beispiel hierfür:<br />
slt $t0, $s1, $s2<br />
bne $t0, $zero, L<br />
# if ($s1 < $s2)<br />
# branch to L<br />
Frage: Warum gibt es die Befehle blt, bge, etc. nicht?<br />
Die Hardware für
Die MIPS Assembler Sprache hat einen speziellen Befehl für Prozeduraufrufe: Dieser Befehl<br />
springt zu einer Adresse und speichert dabei die Adresse des nachfolgenden Befehls im<br />
Register $ra. Dieser Befehl wird jump-and-Link-Befehl (jump-and-link instruction) genannt<br />
und wie folgt geschrieben:<br />
jal ProcedureAddress<br />
Der Link-<strong>Teil</strong> des Namesn bedeutet, dass eine Adresse festgehalten wird bzw. ein Verweis<br />
auf die Stelle des Aufrufs gebildet wird, so dass die Prozedur an die richtige Adresse<br />
zurückkehren kann. Diese Adresse wird im Register $ra gespeichert und als<br />
Rücksprungadresse (return address) bezeichnet.<br />
Ein Rücksprung ist mit folgendem Befehl möglich, dem jump-Register-Befehl.<br />
jr $ra<br />
Die main routine ist die aufrufende Prozedur, genannt caller. Sie speichert die Parameter in<br />
$a0 - $a3 und springt mithilfe des Befehls jal X zur Prozedur x (auch als aufgerufene<br />
Prozedur oder callee bezeichnet).<br />
Der Caller überträgt die Kontrolle auf den Callee.<br />
1. Der Callee erwirbt die benötigten Speicherressourcen<br />
2. Der Callee führt den gewünschten Task aus<br />
3. Der Callee platziert das Ergebnis an einem Ort, an dem der Caller es erreichen kann<br />
<br />
$v0 - $v1 sind zwei value register für Ergebniswerte<br />
4. Der Callee gibt die Kontrolle an den Caller zurück<br />
<br />
$ra ist das Register für die return address, um zum Ausgangspunkt<br />
zurückzukehren<br />
Es ist auch ein Register für die Adresse des gerade auszuführenden Befehls notwendig. Dieses<br />
Register wird als Befehlszähler (programm counter) bezeichnet, abgekürzt PC.<br />
Befehlsadressregister wäre allerdings eine treffendere Bezeichnung.<br />
Wenn die Procedur also mit dem Befehl jr $ra zum Ausgangspunkt zurückspringt, muss<br />
dieses auch zum PC kopiert werden.<br />
Geschachtelte Prozeduren<br />
Prozeduren, die keine anderen Prozeduren aufrufen, werden als Blattprozeduren (Leaf<br />
Procedure) bezeichnet. Non-Leafe Procedures sind dann natürlich Prozeduren, die auch<br />
andere Prozeduren aufrufen. Damit solche verschachtelten Aufrufe möglich sind, muss der<br />
Caller folgendes am Stack abspeichern:<br />
- seine Return-Adresse<br />
- alle Argumente und Temporaries (vorübergehenden Werte), die nach dem Aufruf<br />
benötigt werden<br />
27
All das muss nach dem Aufruf einer anderen Prozedur vom Stack geholt und wiederhergestellt<br />
werden.<br />
Stack<br />
Eine als LIFO (Last in first out) Warteschlange organisierte Datenstruktur zum Auslagern von<br />
Registern.<br />
Stack Pointer<br />
Ein Wert, der die in einem Stack zuletzt reservierte Adresse angibt und anzeigt, von welcher<br />
Position an auszulagernde Register gespeichert werden müssen oder wo alte Registerwerte<br />
gefunden werden können. In MIPS ist dies das Register $sp.<br />
Der Stack wird nicht nur dazu verwendet, bei Non-Leaf-Prozeduren wichtige Werte<br />
abzulegen, sondern auch, um Variablen zu Speichern, die nicht in Register passen, z.B. lokale<br />
Felder und Strukturen.<br />
Das Segment im Stack, das die geretteten Register und lokalen Variablen einer Prozedur<br />
enthält, wird als Procedure Call Frame bezeichnet. Manche MIPS Software verwendet einen<br />
Frame Pointer, der auf das erste Wort in einem Procedure Frame zeigt.<br />
a) vor dem Prozeduraufruf<br />
b) während dem Prozeduraufruf<br />
c) nach dem Prozeduraufruf<br />
28
Memory Layout<br />
Neben den für Prozeduren lokalen Variablen vom Typ automatic benötigen C-Programmierer<br />
Speicherplatz für statische Variablen und für dynamische Datenstrukturen.<br />
Der Stack beginnt am oberen Speicherende und<br />
wächst nach unten.<br />
Der erste <strong>Teil</strong> am unteren Speicherende ist<br />
reserviert.<br />
Diesem <strong>Teil</strong> folgt der Bereich mit dem MIPS-<br />
Maschinencode, der als Testsegment<br />
bezeichnet wird.<br />
Über dem Code befindet sich das statische<br />
Datensegment, in dem Konstanten und andere<br />
statische Variablen abgelegt werden. Felder<br />
haben eine fixe Länge und werden im statischen<br />
Datensegment abgelegt.<br />
Datenstrukturen wie beispielsweise verkettete<br />
Liste, verändern dagegen ihre Länge im Laufe ihrer Lebensdauer. Das für Datenstrukturen<br />
dieser Art servierte Segment wird als Heap (Halde) bezeichnet.<br />
Character Data<br />
• Byte-encoded character sets<br />
o ASCII: 128 characters<br />
o Latin-1: 256 characters<br />
• Unicode: 32-bit character set<br />
o verwendet in Java, C++,...<br />
o ist ein internationaler Standard, in dem langfristig für jedes sinntragende<br />
Schriftzeichen oder Textelement aller bekannten Schriftkulturen und<br />
Zeichensysteme ein digitaler Code festgelegt wird. Ziel ist es, die Verwendung<br />
unterschiedlicher und inkompatibler Kodierungen in verschiedenen Ländern<br />
oder Kulturkreisen zu beseitigen. Unicode wird ständig um Zeichen weiterer<br />
Schriftsysteme ergänzt.<br />
Byte/Halfword Operations<br />
Beim MIPS-Befehlssatz gibt es spezielle Befehle zum Laden und Speichern von 16-Bit-<br />
Größen (bei Unicode wird ein Zeichen standardgemäß mit 16 Bit dargestellt). Diese 16-Bit-<br />
Größen werden als Halbwörter bezeichnet.<br />
Der load-halfword-Befehl ( lh ) lädt ein Halbwort aus dem Hauptspeicher und legt es in den<br />
rechtsbündigen 16 Bit eines Registers ab. Wie load byte behandelt load half das Halbwort als<br />
vorzeichenbehaftete Zahl und führt deshalb eine Vorzeichenerweiterung aus, um die 16<br />
linksbündigen Bit des Registers aufzufüllen, während load halfword unsigned ( lhu ) mit<br />
vorzeichenlosen Integern arbeitet. lhu ist deshalb der gebräuchlichere Befehl.<br />
29
Der store half Befehl ( sh ) nimmt ein halbwort aus den rechtbündigen 16 Bit eines Registers<br />
und schreibt es in den Hauptspeicher.<br />
32-Bit-Konstanten<br />
In der Regel sind Konstanten kurz und passen in das 16-Bit-FEld. Gelegentlich sind sie<br />
jedoch etwas länger. Der MIPS-Befehlssatz enthält den Befehl lui (load upper immediate,<br />
lade höherwertige Hälfte des Direktoperanden), mit dem die höherwertigen 16 Bit einer<br />
Konstante in ein Register geladen werden, so dass in einem nachfolgenden Befehl die<br />
niederwertigen 16 Bit der Konstante spezifiziert werden können.<br />
Branch Adressing<br />
Bei bedingten Verzweigungsbefehlen müssen neben der Sprungadresse zwei Operanden<br />
angegeben werden. Das Format ist: opcode, zwei Register, eine Zieladresse für den Sprung<br />
Bedingte Sprünge findet man z.B. in Schleifen und in if-Anweisungen, d.h. bedingte Sprünge<br />
verweisen auf nahe gelegene Befehle. Da der PC die Adresse des aktuellen Befehls enthält,<br />
können wir in einen Bereich von +/– 2 15 Wörtern vom aktuellen Befehl aus verzweigen, wenn<br />
wir den PC als Register verwenden, das zur Adresse addiert wird. Fast alle Schleifen und If-<br />
Anweisungen sind wesentlich kleiner als 2 16 Wörter, sodass der PC hierfür die richtige Wahl<br />
darstellt. Diese Art der Adressierung bei Sprüngen wir als PC-relative addressing<br />
bezeichnet.<br />
Target address = PC + offset x 4<br />
(der PC ist zu dieser Zeit bereits um 4 erhöht, da er während dem fetch cycle bereits um 4 für<br />
den nächstfolgenden Befehl erhöht wird)<br />
30
Jump Adressing<br />
Die MIPS-Sprungbefehle verwenden die einfachste Adressierungsart. Für sie gibt es ein<br />
weiteres MIPS-Befehlsformat, das sogenannte J-Typ-Format. Es setzt sich aus dem 6 Bit<br />
breiten Operationsfeld und dem Adressfeld usammen, das die restlichen Bits umfasst.<br />
Branching Far Away<br />
Die meisten bedingten Sprünge verzweigen innerhalb eines beschränkten Adressbereichs. Es<br />
gibt jedoch Situationen, in denen weiter verzweigt werden muss, als dies in den 16 Bit des<br />
bedingten Sprungbefehles dargestellt werden kann. Der Assembler löst dieses Problem auf<br />
ähnliche Weise wie das Problem mit den langen Adressen bzw. Konstanten: Er fügt einen<br />
unbedingten Sprung mit dem Sprungziel nach der Verzweigung ein und invertiert die<br />
Bedingung, so dass die Verzweigung entscheidet, ob der Sprung genommen wird.<br />
Beispiel:<br />
Erklärung: Die erste Zeile vergleicht, ob $s0 und $s1 gleich sind und springt, falls es der Fall<br />
ist, zu L1. Die zweite Lösung ist gleich darunter. Es wird verglichen, ob $s0 und $s1 nicht<br />
gleich sind. Sind sie nicht gleicht, wird zu L2 gesprungen und somit der jump zu L1<br />
übersprungen. Sind sie jedoch gleich, wird der Jump-Befehl (der mehr Platz bietet 26 bits)<br />
zu L1 ausgeführt.<br />
Synchonisation<br />
Parallele Ausführung ist dann einfach, wenn die betreffenden Tasks voneinander unabhängig<br />
sind, aber häufig müssten Tasks zusammenarbeiten. Zusammenarbeiten bedeutet in der Regel,<br />
dass einige Tasks neue Werte schreiben, die die anderen lesen müssen. Um zu erkennen,<br />
wann ein Task mit dem Schreiben fertig ist, so dass eine andere sicher lesen kann, müssen<br />
sich die Tasks synchronisieren. Wenn sie sich nicht synchronisieren, besteht die Gefahr eines<br />
Data Race, wobei das Programmergebnis davon abhängig sein kann, welche Ereignisse<br />
zuerst auftreten.<br />
Deshalb ist hier Hardware Support erforderlich! Read und write memory Operationen müssen<br />
atomar sein (unteilbar), d.h. es ist kein anderer Zugriff zwischen read und write erlaubt.<br />
31
Erst wird ein load linked ( ll ) ausgeführt und danach ein store conditional ( sc ).<br />
Erfolgreich ist die Ausführung dann, wenn sich die Location seit dem load linked nicht<br />
geändert hat (gibt 1 in rt zurück). Die Ausführung schlägt fehl, wenn sich die Location<br />
geändert hat (gibt 0 in rt zurück).<br />
Assembler Pseudoinstructions<br />
Die meisten Assembler Instructions repräsentieren die Maschinenbefehle eins zu eins.<br />
Allerdings gibt es auch sogenannte Pseudoinstructions. Diese sind Erfindungen der<br />
Assembler-Vorstellung. Beispiele dafür sind:<br />
move $t0, $t1 add $t0, $zero, $t1<br />
blt $t0, $t1, L slt $at, $t0, $t1<br />
bne $at, $zero, L<br />
($at ist Register 1: assembler temporary)<br />
Performance Vergleich für Bubble (exchange) Sort:<br />
Der unoptimierte Code hat den besten CPI, die O1 Version hat den niedrigsten IC, aber O3 ist<br />
am schnellsten.<br />
Gelerntes Wissen:<br />
- IC und CPI sind keine guten Performance Indikatoren (in Isolation)<br />
- Compiler Optimierungen sind empfindlich auf den Algorithmus<br />
- Java/JIT compiled Code ist deutlich schneller als JVM (Java Virtual Machine)<br />
interpreted<br />
- nichts kann einen schlechten Algorithmus wettmachen<br />
32
Chapter 3: Arithmetic for Computers<br />
Arithmetic Logic Unit (ALU)<br />
Hardware, die die Addition und die Subtraktion ausführt,<br />
ebenso wie üblicherweise logische Operationen wie UND<br />
und ODER, kurzum: die ALU ist für arithmetische<br />
Operationen sowie logische Operationen zuständig.<br />
Bei der Addition werden die Ziffern Bit für Bit von rechts nach links addiert, wobei die<br />
Überträge jeweils auf die nächste Stelle links weitergegeben werden. Auf für die Subtraktion<br />
wird die Addition verwendet: Der entsprechende Operand wird lediglich vor der Addition<br />
negiert.<br />
Overflow bei einer Addition?<br />
Ein Overflow bedeutet, dass ein <strong>Teil</strong> der errechneten Zahl außerhalb des zulässigen Bereichs<br />
ist, d.h. dass das Ergebnis nicht korrekt dargestellt werden kann.<br />
<br />
<br />
<br />
Addiert man einen positiven und einen negativen Operand, kommt es zu keinem<br />
Overflow<br />
Addiert man zwei positive Operanden<br />
o es ist ein Overflow, wenn das Vorzeichenbit des Ergebnisses 1 ist<br />
Addiert man zwei negative Operanden<br />
o es ist ein Overflow, wenn das Vorzeichenbit des Ergebnisses 0 ist<br />
Ebenso ist es bei der Subtraktion:<br />
<br />
<br />
<br />
Subtrahiert man zwei negative Operanden oder zwei positive Operanden, tritt kein<br />
Overflow auf<br />
Subtrahiert man einen positiven von einem negativen Operanden<br />
o es ist ein Overflow, wenn das Vorzeichenbit des Ergebnisses 0 ist<br />
Subtrahiert man einen negativen von einem positiven Operanden<br />
o es ist ein Overflow, wenn das Vorzeichenbit des Ergebnisses 1 ist<br />
33
Ein Overflow tritt auf, wenn das Ergebnis eines Operation nicht in 32-bits repräsentiert<br />
werden kann, z.B. wenn ein sign bit (Vorzeichenbit) eine Werte-Bit des Ergebnisses<br />
beinhaltet und nicht das eigentliche sign bit.<br />
wenn Operanden unterschiedlicher Vorzeichen addiert werden oder Operanden gleicher<br />
Vorzeichen subtrahiert, kann es niemals zu einem Overflow kommen<br />
MIPS signalisiert einen Overflow mit einer Exception (auch genannt: Interrupt). Das ist eine<br />
ungeplante Prozedur, wobei der EPC (exceptioin programm counter) die Adresse der<br />
Instruction enthält, die die Exception verursacht hat.<br />
Exception<br />
Wird bei manchen Rechnern auch als Unterbrechung bezeichnet. Ein im ursprünglichen<br />
Programm an dieser Stelle nicht vorgesehenes Ereignis, das die Programmausführung<br />
unterbricht. Dient beispielsweise zum Erkennen von Überläufen.<br />
Interrupt<br />
Eine Ausnahmesituation, die außerhalb des Prozessors verursacht wird. (Bei manchen<br />
Architekturen wird der Ausdruck Unterbrechung (interrupt) für alle Arten von<br />
Ausnahmeverarbeitungen verwendet).<br />
Manche Sprachen (z.B. C) ignorieren Overflow. Andere wiederum erwarten eine Exception.<br />
Arithmetic for Multimedia<br />
Graphik- und Mediasysteme arbeiten mit Vektoren von 8-bit und 16-bit Daten. Im<br />
Zusammenhang mit Medien ist der Begriff Sättigung (saturation) wichtig, des besagt, dass<br />
bei einem Overflow der Wert entweder auf die höchste positive oder die niedrigste negative<br />
Zahl gesetzt wird. Bei gewöhnlichen Mikroprozessoren findet man das normalerweise nicht.<br />
Sättigung wird aber eben bei Medien-Operationen gebraucht (z.B. Lautstärkereglern).<br />
Multiplikation<br />
Bei der binären Multiplikation wird der Multiplikand immer nach links verschoben, der<br />
Multiplikator nach rechts und die „Zwischenergebnisse“ addiert.<br />
34
Die einzelnen Schritte der Multiplikation (Multiplikand x Multiplikator) sind:<br />
1. Wenn an der Stelle des Muliplikators eine 1 steht, wird an der entsprechenden Stelle<br />
eine Kopie des Multiplikanden gesetzt (1 x Multiplikand), und<br />
2. wenn an der Stelle des Multiplikators eine 0 steht, wird an der entsprechende Stelle<br />
eine 0 (0 x Multiplikand) gesetzt<br />
Um die nächste Stelle des Multiplikators zu bekommen, erfolgt ein right shift.<br />
Damit die Addition korrekt erfolgt, muss beim Multiplikand ein left shift erfolgen.<br />
Ein Multiplier kann optimiert werden, indem für jeden Schritt nur ein Taktzyklus benötigt<br />
wird. Die Beschleunigung wird durch die parallele Ausführung der Operationen erzielt: Der<br />
Multiplikator und der Multiplikand werden verschoben, während der Multiplikand zum<br />
Produkt addiert wird, falls das Multiplikatorbit 1 ist.<br />
Eine Multiplikation kann schneller gemacht werden, indem mehrere Addierer verwendet<br />
werden. Anstatt z.B. einen 32-Bit-Addierer 32-mal zu verwenden, werden 31 Addierer<br />
verwendet. Jeder Addierer generiert eine 32-Bit-Summe und einen Übertrag. Das<br />
niedrigstwertige Bit ist ein Bit des Produkts, und der Übertrag und die obereren 31 Bit der<br />
Summe werden an den nächsten Addierer weitergeleitet.<br />
Für das Ganze kann eine Pipeline verwendet werden.<br />
Multiplikation bei MIPS<br />
Bei der MIPS-Architektur gibt es zwei getrennte 32-Bit-Register zum Speichern des 64-Bit-<br />
Produkts. Diese Register heißen Hi und Lo. Um ein Produkt mit oder ohne Vorzeichen<br />
generieren zu können, enthält der MIPS Befehlssatz zwei Befehle: multiply ( mult ) und<br />
multiply unsigned ( multu ). Um das ganzzahlige 32-Bit-Produkt zu holen, verwendet der<br />
Programmierer den Befehl move from lo ( mflo ). Der MIPS-Assembler generiert einen<br />
Pseudobefehl für die Multiplikation, in dem drei Allzweckregister angegeben sind, und<br />
speichert das Produkt mithilfe des Befehles mflo und mfhi in Registern.<br />
Division<br />
Dividend = Quotient x Divisor + Remainder (Rest)<br />
(Dividend / Divisor)<br />
35
Der Computer kann nicht wie der Mensch sofort<br />
erkennen, ob der Divisor kleiner als der Dividend ist.<br />
Er muss zunächst den Divisor vom Dividenden<br />
subtrahieren.<br />
Ist das Ergebnis positiv, ist der Divisor kleiner<br />
oder gleich groß wie der Dividend und wir generieren<br />
eine 1 im Quotienten.<br />
Wenn das Ergebnis negativ ist, besteht der<br />
nächste Schritt darin, den Anfangswert<br />
wiederherzustellen (Divisor und Rest werden<br />
addiert). Dann wird im Quotienten eine 0 generiert.<br />
Nun wird der Divisor nach rechts verschoben, und<br />
der Vorgang wird wiederholt.<br />
Nach Abschluss aller Wiederholungen befinden sich der Rest im Restregister und der<br />
Quotient im Quotientenregister.<br />
Der Algorithmus und die Hardware können verfeinert und damit schneller und billiger<br />
gemacht werden. Die Beschleunigung wird dadurch erzielt, dass das Verschieben der<br />
Operanden und des Quotienten gleichzeitig mit der Subtraktion erfolgt. Durch das<br />
Beobachten, wo <strong>Teil</strong>e der Register und des Addierers ungenutzt bleiben, kann durch diese<br />
Verfeinerung die Bereite der Register und des Addierers halbiert werden.<br />
Den Trick der Multiplikation (31 Addierer) können wir hier nicht verwenden. Grund ist, dass<br />
wir das Vorzeichen der Differenz kennen müssen, bevor wir den nächsten Schritt des<br />
Algorithmus ausführen können. Eine andere Lösung ist die SRT-Division. Hierbei wird pro<br />
Schritt mit Hilfe der oberen Bits des Dividenden und des Rest in einer Tabelle<br />
nachgeschlagen und mit dem so gefunden Eintrag versucht, auf mehrere Bits des Quotienten<br />
zu schließen. Typischerweise werden heute pro Schritt 4 Bits betrachtet. Falsche Schlüsse<br />
müssen in nachfolgenden Schritten korrigiert werden. Es geht darum, auf den zu<br />
subtrahierenden Wert zu schließen.<br />
MIPS Division<br />
Hier werden wieder HI/LO Register für das Ergebnis verwendet.<br />
- HI: 32-bit Rest<br />
- LO: 32-bit Quotient<br />
Bei MIPS wird nicht auf Overflow oder Divide-By-0 überprüft!<br />
Floating Point<br />
Die Gleitkommadarstellung wird für nicht-ganzzahlige Zahlen verwendet (damit können also<br />
alle möglichen reellen Zahlen dargestellt werden).<br />
Es gibt die normalisierte Darstellung (eine Stelle vor dem Komma, mehr stellen nach dem<br />
Komma) und die nicht-normalisierte Darstellung.<br />
36
Bei Binärzahlen ist die Darstellung folgendermaßen:<br />
Die Typen für Gleitkommazahlen in C sind float und double.<br />
Es gibt zwei Arten der Repräsentation:<br />
• Single precision (Einfache Genauigkeit):<br />
o Ein Gleitkommawert, der in einem 32-Bit-Wort dargestellt wird<br />
• Double precision (Doppelte Genauigkeit):<br />
o Ein Gleitkommawert, der in zwei 32-Bit-Wörtern dargestellt wird<br />
(Fraction = Mantisse)<br />
S ist das Vorzeigen Bit (0 positiv, 1 negativ)<br />
Significand normalisieren: 1.0 ≤ | significand | ≤ 2.0<br />
o vor dem Kommapunkt steht immer ein 1 bit (normalisierte Darstellung), es muss<br />
daher nicht explizit repräsentiert werden<br />
o Significand ist Fraction zusammen mit der wiederhergestellten 1<br />
Exponent<br />
o der Exponent darf nicht negativ sein<br />
o Darstellung ist meist der eigentliche Exponent + Bias<br />
o Single precision: Bias = 127<br />
Double precision: Bias = 1023<br />
Single Precision-Range<br />
Die Exponenten 00000000 und 11111111 sind reserviert.<br />
Der kleinste Wert, der dargestellt werden kann, ist folgender:<br />
Exponent: 00000001<br />
der eigentliche Exponent ist = 1 – 127 = –126<br />
Fraction: 000…00 significand = 1.0<br />
+/– 1.0 x 2 -126 ~ +/– 1.2 x 10 -38<br />
37
Der größte Wert, der dargestellt werden kann, ist folgender:<br />
Exponent: 11111110<br />
eigentlicher Exponent = 254 – 127 = +127<br />
Fraction: 111…11 significand ~ 2.0<br />
+/– 2.0 x 2 +127 ~ +/– 3.4 x 10 +38<br />
Double Precision-Range<br />
Die Exponenten 0000…00 and 1111..11 sind reserviert.<br />
Der kleinste Wert, der dargestellt werden kann, ist folgender:<br />
Exponent: 000000000001<br />
der eigentliche Exponent ist = 1 – 1023 = –1022<br />
Fraction: 000…00 significand = 1.0<br />
+/– 1.0 x 2 -1022 ~ +/– 2.2 x 10 -308<br />
Der größte Wert, der dargestellt werden kann, ist folgender:<br />
Exponent: 111111111110<br />
eigentlicher Exponent = 254 – 1023 = +1023<br />
Fraction: 111…11 significand ~ 2.0<br />
+/– 2.0 x 2 +1023 ~ +/– 1.8 x 10 +308<br />
IEEE 754<br />
Die Norm IEEE 754 definiert Standarddarstellungen für binäre Gleitkommazahlen in<br />
Computern und legt genaue Verfahren für die Durchführung mathematischer Operationen,<br />
insbesondere für Rundungen, fest.<br />
Die IEEE 754 ist nicht einfach nur ein simples Format, sondern regelt auch anderes bzw. stellt<br />
die Funktionalität bereit:<br />
<br />
<br />
<br />
<br />
Rundungsalgorithmen<br />
Arithmetische Operationen<br />
Konvertierungen (zwischen Formaten, zu und von Strings, etc.)<br />
Exception Handling (nicht korrekte Zahlen wie z.B. die Wurzel einer negativen Zahl,<br />
Division durch 0, over/unter flow das Symbol NaN steht dabei für Not a Number)<br />
38
Floating Point Addition<br />
Anhand folgenden Beispiels soll die Addition von Gleitkommazahlen veranschaulicht<br />
werden:<br />
9.999 x 10 1 + 1.610 x 10 -1<br />
1. Die Dezimalpunkte müssen aneinander angepasst werden<br />
Die Zahl mit dem kleineren Exponenten muss geshiftet werden<br />
9.999 x 10 1 + 0.016 x 10 1<br />
2. Die Significands werden addiert<br />
9.999 x 10 1 + 0.016 x 10 1 = 10.015 x 10 1<br />
3. Das Ergebnis muss normalisiert werden und auf over/unterflow gecheckt werden<br />
1.0015 x 10 2<br />
4. Das Ergebnis wird gerundet und nochmals normalisieret, falls nötig<br />
1.002 x 10 2<br />
Nun erfolgt ein Beispiel mit einer 4-stelligen Binärzahl:<br />
1.000 2 x 2 -1 + –1.110 2 x 2 -2 (0.5 + –0.4375)<br />
1. Die Dezimalpunkte müssen aneinander angepasst werden<br />
Die Zahl mit dem kleineren Exponenten muss geshiftet werden<br />
1.000 2 x 2 -1 + –0.111 2 x 2 -1<br />
2. Die Significands werden addiert<br />
1.000 2 x 2 -1 + –0.111 2 x 2 -1 = –0.001 2 x 2 -1<br />
3. Das Ergebnis muss normalisiert werden und auf over/unterflow gecheckt werden<br />
1.000 2 x 2 -4<br />
4. Das Ergebnis wird gerundet und nochmals normalisieret, falls nötig<br />
1.000 2 x 2 -4 (no change) = 0.0625<br />
Ein Floating Point Adder ist viel komplexer als ein Integer Adder. Sollte er seine Arbeit in nur<br />
einem Clock Cylce ausführen, würde dieser zu lange dauern. Deshalb braucht ein FP Adder<br />
normalerweise mehrere Clock Cycles. Es ist aber pipelining möglich.<br />
THE BIG PICTURE<br />
- Bits haben keine angehaftete Bedeutung, ihre Interpretation hängt von der<br />
angewandten Instruction ab<br />
39
Right-Shift und Division?<br />
Ein left shift um i entspricht einer Multiplikation eines Integers mit 2 i<br />
Ein right shift hingegen dividiert durch 2 i , allerdings nur bei vorzeichenlosen Integern.<br />
40
Chapter 4: The Processor<br />
Es gibt verschiedene Faktoren, die die CPU Performance beeinflussen:<br />
<br />
<br />
der Instruction Count (IC)<br />
o wird von der Instruction Set Architecture (ISA) und dem Compiler bestimmt<br />
CPI (Cycles per Instruction) und Cycle Time<br />
o werden von der CPU Hardware bestimmt<br />
Übersicht über die Implementierung<br />
Bei allen Befehlen sind die ersten beiden Schritte dieselben:<br />
1. Senden des PC (Programm Counter, Befehlszähler) an den Speicher, der den Code<br />
enthält, und Holen des Befehls aus diesem Speicher.<br />
2. Lesen eines oder zweier Register, wobei die Auswahl des zu lesenden Registers<br />
mithilfe von Feldern des Befehls erfolgt. Beim load word-Befehl muss nur ein<br />
Register gelesen werden, bei den meisten anderen Befehlen dagegen zwei.<br />
Welche Schritte nach diesen beiden Schritten zum Durchführen des Befehls erforderlicher<br />
sind, hängt von der Befehlsklasse ab. Für die drei Befehlsklassen (Speicherzugriff,<br />
arithmetisch-logische Befehle und Sprünge) sind das unabhängig vom exakten Opcode im<br />
Großen und Ganzen dieselben Schritte.<br />
Alle Befehlsklassen außer jump verwenden die ALU (Arithmetic Logical Unit), nachdem<br />
sie die Register gelesen haben:<br />
Speicherzugriffsbefehle: verwenden die ALU für die Adressberechnung<br />
die arithmetisch-logischen Befehle: verwenden die ALU für die Ausführung von<br />
Operationen<br />
die Verzweigungen: verwenden die ALU für Vergleiche<br />
Nach dem Einsatz der ALU sind unterschiedliche Schritte zur Beendigung des Befehls<br />
erforderlich.<br />
ein Speicherzugriff muss entweder im Rahmen eines store-Befehls zum Schreiben<br />
von Daten oder im Rahmen eines load-Befehls zum Lesen von Daten auf den<br />
Speicher zugreifen.<br />
Ein arithmetisch-logischer Befehl muss die Daten von der ALU zurück in ein<br />
Register schreiben.<br />
Bei einem Sprungbefehl müssen wir die nachfolgende Befehlsadresse je nach dem<br />
Ergebnis des Vergleichs möglicherweise ändern. Anderenfalls muss der PC um 4<br />
erhöht werden, um so die Adresse des nachfolgenden Befehls zu erhalten.<br />
41
Übersicht über die CPU:<br />
Logic Design Basics<br />
Information wird binär darstellt. Dabei bedeutet 0 = low voltage (Niederspannung) und 1 =<br />
high voltage (Hochspannung). Pro Bit gibt es eine Leitung (one wire per bit), Multi-Bit Daten<br />
werden also auf multi-wire buses codiert (Busse mit mehreren Leitungen).<br />
Es gibt sogenannte Combinational Elements (Verknüpfungsglieder), die mit Daten operieren<br />
und deren Output immer eine Funktion ihres Inputs ist.<br />
Weiters gibt es State (sequential) Elements (Schaltwerke), die Information speichern. Sie<br />
sind also Speicherelemente. Der Rechner kann nach einer Unterbrechnung der<br />
Stromversorgung erneut gestartet werden, indem die Schaltwerke mit den Werten geladen<br />
werden, die zum Zeitpunkt der Unterbrechung der Stromversorgung des Computers<br />
gespeichert waren. Die Befehls- und Datenspeicher sowie die Register sind Beispiele für<br />
Schaltwerke.<br />
Combinational Elements<br />
42
Sequential Elements<br />
Zu den Sequential Elements bzw. State Elements zählen die Register. Sie speichern Daten in<br />
einem Schaltkreis (elektrischer Bauteil, circuit) und verwenden ein Clock Signal, das<br />
bestimmt, wann der gespeicherte Wert upgedated werden muss. Der Takt ist edge-triggered,<br />
das heißt flankengesteuert. Das bedeutet, dass Zustandsänderungen während der Taktflanke<br />
erfolgen, also wenn der Clock sich von 0 zu 1 ändert.<br />
Weiters gibt es Register mit Write Control. Bei diesen erfolgt nur dann ein Update bei der<br />
Clock-Flanke (clock edge), wenn der Write Control Input = 1 ist. Das wird verwendet, wenn<br />
der gespeicherte Wert später benötigt wird.<br />
43
Taktverfahren (Clocking Methodology)<br />
Das Taktverfahren bestimmt, wann Signale<br />
gelesen und wann sie geschrieben werden<br />
können. Es ist wichtig, den zeitlichen<br />
Ablauf von Lese- und Schreibvorgängen<br />
festzulegen. Denn wenn das Signal gleichzeitig<br />
geschrieben und gelesen wird, kann es vorkommen,<br />
dass der Wert des Lesevorgangs dem alten Wert, dem<br />
neugeschriebenen Wert oder sogar einer Mischung<br />
aus beiden Werten entspricht!<br />
Beim flankengesteuerten (edge-triggered)<br />
Taktverfahren werden alle gespeicherten Werte nur während einer Taktflanke aktualisiert. Da<br />
nur Schaltwerke (state elements) einen Datenwert speichern können, müssen die Eingänge<br />
sämtlicher Schaltnetze aus Schaltwerken kommen und die ausgaben wieder in Schaltwerke<br />
geschrieben werden. An den Eingängen liegen die Werte an, die in einem vorhergehenden<br />
Taktzyklus geschrieben wurden, während an den Ausgängen die Werte anliegen, die in einem<br />
nachfolgenden Taktzyklus verwendet werden können.<br />
Alle Signale müssen sich innerhalb eines Taktzyklus vom Schaltwerk 1 über das Schaltnetz zum<br />
Schaltwerk 2 fortpflanzen. Die längste Zeit, die ein Signal dafür benötigt, bestimmt die Länge des<br />
Taktzyklus.<br />
Aufbau eines Datenpfads (Datapath)<br />
Der Datenpfad (datapath) ist ein Element, das Daten und Adressen in der CPU verarbeitet<br />
(Registers, ALUs, Mux’s, Memories,...)<br />
Ein Datenpfad besteht aus Data Path Elements (Bausteine im Datenpfad), die eine<br />
Funktionseinheit zur Verarbeitung oder Speicherung von Daten in einem Prozessor darstellen.<br />
Das erste Element, das wir für den Aufbau benötigen, ist eine Speichereinheit zum Speichern der<br />
Befehle eines Programms und zum Bereitstellen von Befehlen für eine gegebene Adresse. Dafür<br />
wird auch das Register Program Counter (Befehlszähler) benötigt, das zum speichern der<br />
Adresse des aktuellen Befehls verwendet wird. Weiters wird ein Addierer benötigt, um den PC zu<br />
inkrementieren.<br />
Um einen Befehl<br />
auszuführen, muss der<br />
Befehl zunächst aus dem<br />
Speicher geholt werden<br />
(Instruction Fetch). Um<br />
die Ausführung des<br />
nächsten Befehls<br />
vorzubereiten, muss<br />
zudem der PC<br />
inkrementiert werden, so<br />
dass er auf den nächsten<br />
Befehl 4 Byte weiter<br />
zeigt.<br />
Die drei genannten Elemente werden zu einem Datenpfad zusammengefügt.<br />
44
R-Format Instructions<br />
Bei den R-Format Instructions werden zwei Register Operanden benötigt. Es wird eine<br />
arithmetische/logische Operation ausgeführt und das Ergebnis anschließend in ein weiteres<br />
Register geschrieben.<br />
Load/Store Instructions<br />
Bei einem Lade-/Speicher-Befehl werden erst die Register-Operanden gelesen. Dann wird die<br />
Adresse berechnet, indem ein 16-bit Offset verwendet wird (dafür wird die ALU verwendet).<br />
Beim Load wird aus dem Memory gelesen und das Register aktualisiert<br />
lw $t1, offset_value($t2)<br />
# t2 = Basisregister, $t1 Zielregister Wert dorthin laden<br />
Beim Store wird der Wert aus dem Register in den Memory geschrieben.<br />
sw $t1, offset_value($t2)<br />
# t2 = Basisregister, $t1 zu speichernder Wert liegt hier<br />
45
Die Befehle load word und store word berechnen eine Speicheradresse, wobei das<br />
Basisregister und das im Befehl enthaltene vorzeichenbehaftet 16-Bit-Offset-Feld addiert<br />
werden. Wenn es sich um einen Speicherbefehl handelt, muss der zu speichernde Wert<br />
ebenfalls aus dem Registersatz gelesen werden. Wenn es sich um einen Ladebefehl handelt,<br />
muss der aus dem Speicher gelesene Wert in das angegebene Register im Registersatz<br />
geschriebne werden.<br />
Branch Instructions<br />
Bei einem Branch-Befehl (einem Befehl zur Verzweigung) werden erst einmal die Register<br />
Operanden gelesen. Dann werden die Operanden verglichen, wofür die ALU verwendet wird<br />
(es wird subtrahiert und der Output überprüft Null?). Nun muss die Zieladresse (target<br />
address) errechnet werden.<br />
Der beq-Befehl (branch equal) enthält drei Operanden, zwei Register, die miteinander auf<br />
Gleichheit verglichen werden, und ein 16-Bit-Offset zum Berechnen der Sprungzieladresse<br />
(branch target address) relativ zur Sprungbefehladresse. Der Befehl hat die Form:<br />
beq $t1, $t2, offset<br />
Das Offset-Feld wird zum PC addiert (Sprungbefehladresse) und so wird die<br />
Sprungzieladresse berechnet.<br />
Wenn beim beq-Befehl die Bedingung wahr ist, wird die Sprungzieladresse zum neuen<br />
Befehlszählwert (PC) und wir sagen, dass der Sprung ausgeführt wird (branch taken). Wenn<br />
die Operanden nicht gleich sind, wird der aktuelle Befehlszähler durch den inkrementierten<br />
Befehlszähler ersetzt (wie bei jedem anderen normalen Befehl). In diesem Fall sagen wir, dass<br />
der Sprung nicht ausgeführt wird (branch not taken).<br />
46
Die einzelnen Elemente des Datenpfads zusammensetzen…<br />
Die einzelnen eben vorgestellten Komponenten können nun zu einem einfachen Datenpfad<br />
zusammengefügt werden. Der einfachste Datenpfad würde versuchen, alle Befehle in einem<br />
Taktzyklus auszuführen. Das bedeutet, dass keine Ressource im Datenpfad mehr als einmal<br />
pro Befehl verwendet werden kann, so dass jedes Element, das mehr als einmal benötigt wird,<br />
mehrfach vorhanden sein muss. Daher müssen wir Befehlsspeicher und Datenspeicher<br />
voneinander trennen (Harvard Architektur). Auch, wenn einige Funktionseinheiten mehrfach<br />
vorhanden sein müssen, so können doch viele der Elemente von unterschiedlichen Befehlen<br />
gemeinsam genutzt werden.<br />
Damit ein Element im Datenpfad von zwei verschiedenen Befehlsklassen gemeinsam genutzt<br />
werden kann, müssen wir mithilfe eines Multiplexers mehrere Verbindungen zum Eingang<br />
eines Elements zulassen und mithilfe eines Steuersignals zwischen den verschiedenen<br />
Eingängen auswählen.<br />
47
Die ALU Control<br />
Die ALU wird für Folgendes verwendet:<br />
Load/Store: F = add<br />
Branch: F = subtract (Vergleich in beq)<br />
R-type: F hängt vom funct Feld ab<br />
Bei Lade-/Speicherbefehlen wird die Addition verwendet, um die Speicheradresse zu<br />
berechnen. Bei Branch-on-equal wird ein Subtraktion durchgeführt, um zu bestimmen, ob<br />
zwei Register gleich sind (sind die gleich, ist das Ergebnis der Subtraktion 0). Bei R-Type-<br />
Befehlen muss die ALU je nach Inhalt des funct Feldes (6 Bit) eine der fünf Aktionen<br />
ausführen: and, or, subract, add oder set on less than.<br />
Bei der ALU ist eine Wahrheitstabelle hilfreich für die betreffenden Kombinationen des<br />
Funktionscodefelds (funct) und der ALU-Op-Bits. Für eine logische Operation werden in<br />
dieser Darstellung alle Werte der Eingänge aufgelistet und für djeden Fall gezeigt, wie die<br />
resultierenden Ausgänge aussehen sollen.<br />
Die Main Control Unit<br />
Definition: Eine Control Unit (CU) ist eine Schaltung, die den Fluss von Informationen durch<br />
den Prozessor kontrolliert und die Aktivitäten von anderen Einheiten im Prozessor<br />
koordiniert. Es kann als „brain within the brain“ (Gehirn im Gehirn) bezeichnet werden, da es<br />
kontrolliert, was im Inneren des Prozessors passiert, was wiederum den Rest des PCs<br />
(Personal Computer) kontrolliert.<br />
48
Implementieren von Jumps<br />
Ein Jump verwendet eine Word Adresse.<br />
Die Zieladresse für einen Sprungbefehl wird durch Konkatenation (Verkettung, Verknüpfung)<br />
der oberen 4 Bits des aktuellen Befehlszählwertes (PC) +4 und dem 26-Bit-Adressfeld im<br />
Sprungbefehl und durch Hinzufügen von 00 als den beiden niederwertigsten Bits erstellt.<br />
Der beq $t1, $t2, offset Befehl wird folgendermaßen ausgeführt:<br />
1. Ein Befehl wird aus dem Befehlsspeicher geholt und der PC wird inkrementiert<br />
2. Die beiden Register $t1 und $t2 werden aus dem Registersatz ausgelesen.<br />
3. Die ALU subtrahiert die aus dem Registersatz ausgelesenen Datenwerte. Der Wert PC<br />
+4 wird zu den um zwei Stellen nach links verschobenen vorzeichenerweiterten,<br />
unteren 16 Bits des Befehls addiert (offset). Daraus ergibt sich die Sprungzieladresse.<br />
4. Mit dem Zero-Ergebnis aus der ALU wird entschieden, welches Addiererergebnis im<br />
Befehlszähler gespeichert wird.<br />
Probleme bei der Performance<br />
Die längste Verzögerung (delay) bestimmt die Clock Periode. Dabei ist der kritische Path das<br />
Laden einer Instruction. Der Ablauf ist folgender:<br />
Instruction Memory Register File ALU Data Memory Register File<br />
Es ist nicht möglich, die Periode für unterschiedliche Instructions zu variieren. Dadurch wird<br />
das Design Prinzip „Make the common case fast“ verletzt.<br />
Eine Möglichkeit zur Verbesserung der Performance ist Pipelining.<br />
49
Pipelining<br />
Ein gutes Analogie-Beispiel aus dem Alltag, um sich Pipelining besser vorstellen zu können,<br />
ist ein Waschvorgang. Auch hier zeigt sich schnell, dass Parallelismus die Performance<br />
deutlich verbessert.<br />
Die MIPS Pipeline<br />
Bei der MIPS-Pipeline gibt es fünf Stufen, wobei pro Stufe ein Schritt ausgeführt wird:<br />
1. IF: Instruction Fetch (Holen der Instruction) vom (Instruction) Memory<br />
2. ID: Instruction Decode (Übersetzen/Entschlüsseln der Instruction)<br />
und Lesen der Register<br />
3. EX: Ausführen der Operation (execute) oder Errechnen der Adresse<br />
4. MEM: Zugriff auf den (Data) Memory Operanden<br />
5. WB: Zurückschreiben des Ergebnisses in ein Register (write back result)<br />
Übersicht über die Dauer von Befehlen<br />
(1 Pikosekunde (ps) = 0,000 000 000 001 Sekunden = 10-12 Sekunden)<br />
50
Pipeline Performance<br />
Pipeline Speedup (Beschleunigung durch Pipelining)<br />
Wenn alle Stufen ausbalanciert sind, das heißt alle Stufen brauchen dieselbe Zeit, lässt sich<br />
folgendermaßen rechnen:<br />
Wenn die Stufen nicht ausbalanciert sind, ist der Speedup natürlich geringer.<br />
Durch Pipelining wird der Throughput erhöht. Die Latency (Latenz, Zeit für jede Instruction)<br />
wird jedoch nicht verringert.<br />
Das MIPS ISA ist speziell für Pipelining designet. Alle Instructions haben dieselbe Länge,<br />
nämlich 32-bits. Das erleichtert fetch (holen) und decode (entschlüsseln, übersetzen) in einem<br />
Cycle. Weiters gibt es nur wenige und dafür regelmäßige Instruction Formate. Diese<br />
ermöglichen das Decodieren und Lesen von Registern in einem Schritt.<br />
51
Load/store addressing: Die Adresse kann auf der 3. Stufe errechnet werden, auf den<br />
Memory wird auf Stufe 4 zugegriffen. Da die Memory-Operanden aneinandergereiht sind,<br />
benötigt der Speicherzugriff nur einen Cycle.<br />
Hazards<br />
Beim Pipelining gibt es Situationen, in denen der nächste Befehl nicht im nachfolgenden<br />
Taktzyklus ausgeführt werden kann. Diese Ereignisse werden als Hemmnisse oder auch<br />
Konflikte bezeichnet (Übersetzung für Hazard). Von diesen gibt es drei verschiedene Typen.<br />
• Structure Hazard (Strukturkonflikt)<br />
o tritt auf, wenn eine benötigte Ressource busy ist (beschäftigt, belegt <br />
kurzum: zur Zeit nicht verfügbar)<br />
• Data Hazard (Datenkonflikt)<br />
o tritt auf, wenn noch darauf gewartet werden muss, dass die Instruction davor<br />
ihren Schreib-/Lesezugriff auf die benötigten Daten beendet<br />
• Control Hazard (Steuerkonflikt)<br />
o tritt auf, wenn die Entscheidung einer Control-Aktion von der vorhergehenden<br />
Instruction abhängt<br />
Structure Hazards<br />
Structure Hazards sind Konflikte bei der Verwendung einer Ressource. Die Hardware kann<br />
die Befehlskombination, die in einem Taktzyklus ausgeführt werden soll, nicht unterstützen.<br />
Hat man anstelle von zwei Speichern nur einen, kann ein solcher Konflikt auftreten, und zwar,<br />
wenn eine Load/Store Operation Zugriff auf Daten benötigt, also auf den Speicher zugreift.<br />
So könnte nicht gleichzeitig ein Befehl aus dem Speicher geholt werden, der Instruction Fetch<br />
hätte einen stall (eine Verzögerung) und müsste warten.<br />
Der MIPS Befehlssatz wurde jedoch so designed, dass es Entwicklern leichter gemacht<br />
wurde, beim Entwerfen einer Pipeline Strukturkonflikte zu vermeiden.<br />
Pipelined Datapaths benötigen separate Instruction- und Daten-Speicher oder separate<br />
Instruction- und Daten-Caches.<br />
Data Hazards<br />
Data Hazards sind Konflikte, die durch Zugriff auf Daten auftreten. Dabei kann ein Befehl<br />
nicht im vorgesehenen Taktzyklus ausgeführt werden, weil Daten zum Ausführen des Befehls<br />
noch nicht verfügbar sind. Es muss gewartet werden, bis die vorhergehende Instruction ihren<br />
Datenzugriff abgeschlossen hat.<br />
Beispiel:<br />
52
Bei einer Pipeline im Rechner treten Datenkonflikte aufgrund der Abhängigkeit eines Befehls<br />
von einem zu einem früheren Zeitpunkt begonnenen Befehl auf, der sich noch in der Pipeline<br />
befindet. Der add-Befehl vom obigen Beispiel schreibt sein Ergebnis erst in der fünften Stufe,<br />
was bedeutet, dass wir drei sogenannte Bubbles (Pipelineleerlauf, Leeroperationen oder<br />
Wartetakte) in die Pipeline einfügen müssen.<br />
Die wichtigste Gegenmaßnahme beruht auf der Beobachtung, dass wir mit dem Beheben des<br />
Datenkonflikts nicht warten müssen, bis der Befehl ausgeführt ist. Bei der obigen<br />
Codesequenz können wir die Somme aus der Addition als Eingangswert für die Subtraktion<br />
bereitstellen, sobald die ALU die Summe berechnet hat. Das Verwenden zusätzlicher<br />
Hardware zum frühzeitigen Abrufen des fehlenden Elements aus den internen Ressourcen<br />
wird als Forwarding oder Bypassing (Daten-Bypassstechnik) bezeichnet.<br />
dabei wird ein fehlendes Datenelement aus internen Pufferspeichern abgerufen und<br />
es wird nicht darauf gewartet, bis dieses aus den für den Programmierer sichtbaren<br />
Registern oder aus dem Speicher kommt.<br />
Forwarding-Pfade sind nur dann zulässig, wenn die Zielstufe zu einem späteren Zeitpunkt<br />
ausgeführt wird als die Quellstufe. In anderen Situationen kann eine Pipelineverzögerung so<br />
nicht verhindert werden.<br />
Nehmen wir als Beispiel den Befehl zum Laden des Registers $s0 aus dem Speicher (anstelle<br />
des add-Befehls aus dem obigen Beispiel). Die gewünschten Daten werden hier erst nach der<br />
vierten Stufe des ersten Befehls bereitgestellt, also zu spät für den Eingang der dritten Stufe<br />
53
des sub-Befehls. Also kann es auch mit Forwarding vorkommen, dass wir eine Stufe<br />
aufgrund eines Load-use-Konflikts (load-use data hazard) anhalten müssen.<br />
ein Load-use-Konflikt ist eine spezielle Form eines Datenkonflikts, bei dem durch<br />
einen Ladebefehl aus dem Speicher gelesene Daten zum Zeitpunkt der Anforderung<br />
noch nicht verfügbar sind.<br />
Code Schuduling (Umordnen von Code) zum Vermeiden von Pipelineleerläufen<br />
Betrachten wir folgendes Codesegment in C:<br />
a = b + e;<br />
c = b + f;<br />
Der MIPS-Code für dieses Segment lautet unter der Voraussetzung, dass sich alle Variablen<br />
im Speicher befinden und als Offsets von Register $t0 adressierbar sind, wie in der Graphik<br />
links angezeigt wird. Die Lösung findet sich in der Graphik rechts der Code wird<br />
umgeordnet.<br />
54
Control Hazards<br />
Steuerkonflikte (Control Hazards) entstehen, wenn aufgrund der Ergebnisse eines Befehls<br />
eine Entscheidung getroffen werden muss, während andere Befehle ausgeführt werden. Der<br />
gewünschte Befehl kann nicht im selben Taktzyklus ausgeführt werden, weil der Befehl, der<br />
geholt wurde, nicht der ist, der benötigt wird. Das bedeutet, dass die Abfolge von<br />
Befehlsadressen anders als von der Pipeline erwartet ist das Holen der nächsten Instruction<br />
(fetching next instruction) hängt vom Ergebnis eines Branchs ab. So können nicht immer die<br />
richtigen Instructions geholt werden.<br />
In der MIPS Pipeline sollten daher der Vergleich der Register und das Errechnen der<br />
Zieladresse möglichst früh passieren. Es wird zusätzliche Hardware hinzugefügt, die diese<br />
Schritte bereits in der ID-Stufe (Instruction Decode) ausführt.<br />
Für Hazards dieser Art gibt es unterschiedliche Lösung.<br />
Stall on Branch (Leerlauf)<br />
Es muss gewartet werden, bis das Ergebnis des Branchs bestimmt ist, bevor man die nächste<br />
Instruction holen kann. Diese Lösung funktioniert zweifelsohne, ist jedoch sehr langsam.<br />
Branch Prediction<br />
Lange Pipelines können das Ergebnis bei einem Branch nicht rechtzeitig bestimmen. Die<br />
Dauer des Stalls, die dadurch entsteht, ist nicht akzeptabel.<br />
Eine Lösung ist es, das Ergebnis des Branchs vorauszusagen. So kommt es nur dann zu einem<br />
Stall, wenn die Voraussage falsch war.<br />
In der MIPS Pipeline kann eine einfache Version der Vorhersage angewendet werden,<br />
nämlich immer vorherzusagen, dass ein Branch nicht ausgeführt wird (untakten branch) und<br />
einfach, ohne Verzögerung, die nächste Instruction zu holen, die auf den Branch folgt. Diese<br />
Methode funktioniert großteils gut, da es oft wahrscheinlicher ist, dass der Branch nicht<br />
genommen wird als dass er genommen wird.<br />
55
MIPS with Predict Not Taken (MIPS mit der Vorhersage: Branch wird nicht genommen)<br />
More-realistic Branch Prediction<br />
Bei einer anspruchsvolleren Version der Branchvorhersage wird angenommen, dass gewisse<br />
Sprünge ausgeführt werden, andere nicht. Beispiel: Bei Schleifen finden sich am Ende<br />
Sprünge, die auf den Anfang der Schleife verzweigen. Dass diese Sprünge ausgeführt werden,<br />
ist sehr wahrscheinlich, solange die Schleife läuft. Da es sich bei Schleifen um Rücksprünge<br />
handelt, kann man davon ausgehen, dass Sprünge auf zurückliegende Adressen immer als<br />
auszuführen anzunehmen sind.<br />
Starre Ansätze wie diese beruhen auf der Annahme stereotypen Verhaltens und<br />
berücksichtigen die Individualität einzelner Sprungbefehle nicht. Man nennt das auch Static<br />
Branch Prediction (statische Branch-Vorhersage). Diese Vorhersage beruht also auf einem<br />
typischen Branchverhalten, wie eben im Fall der Schleife. Einfache Annahmen wären also:<br />
<br />
<br />
Sprünge auf zurückliegende Adressen (Rücksprünge) als taken annehmen<br />
Sprünge auf folgende Adressen (forward, Vorsprünge) als not taken annehmen (z.B.<br />
bei if-Statements)<br />
Im Gegensatz zur Static Branch Prediction steht die Dynamic Branch Prediction<br />
(dynamische Branch-Vorhersage). Dynamische Hardware-Prädiktoren ziehen ihre Schlüsse<br />
aus dem Verhalten jedes einzelnen Sprungs und können die vorhersage für eine Verzweigung<br />
während der Ausführung eines Programms ändern. Ein beliebter Ansatz besteht darin,<br />
ausgeführte und nicht ausgeführte Sprünge zu protokollieren, und dann anhand der letzten<br />
Sprünge die nächsten vorherzusagen. Wenn die Einschätzung falsch ist, muss die<br />
Pipelinesteuerung sicherstellen, dass die Befehle nach dem falsch eingeschätzten Sprung<br />
keine Auswirkung haben, und sie muss die Pipeline von der richtigen Sprungadresse aus neu<br />
starten.<br />
56
Probleme entstehen durch diese Lösung dann, wenn es längere Pipelines sind, wodurch die<br />
Kosten bei falschen Vorhersagen zunehmen.<br />
Ein weiterer möglicher Ansatz ist die verzögerte Entscheidung. Die von der MIPS-<br />
Architektur verwendete Lösung für Rechner wird als verzögerter Sprung bezeichnet (mehr<br />
dazu im Buch, Seite 332 der deutschen Version).<br />
Pipelining des Datenpfads<br />
Eine fünfstufige Pipeline, wie sie beschrieben wurde, erfordert, dass der Datenpfad in fünf<br />
<strong>Teil</strong>e geteilt wird. Jeder <strong>Teil</strong> wird nach der jeweiligen Befehlsausführungsstufe benannt.<br />
Zwischen den einzelnen Stufen werden Register benötigt (Pipeline Register), um die<br />
Informationen zu speichern, die im Cycle davor produziert wurden.<br />
Die Register werden nach den beiden Stufen benannt, die durch sie getrennt werden. So heißt<br />
das Pipelineregister zwischen IF-Stufe und ID-Stufe beispielsweise IF/ID-Register.<br />
Single-Clock-Cycle Diagramme für Load und Store<br />
siehe Beispiele in den Folien (Kapitel 4, Folie 56 – 64)<br />
57
Mehr zu Forwarding im Buch (deutsche Version: Seite 353 – 362) sowie zu Pipeline-<br />
Hazards im Buch (deutsche Version: Seite 362 – 375)<br />
Wie kann man eine Pipeline stallen (anhalten, verzögern)?<br />
Die Control Values im ID/EX Register müssen auf 0 gesetzt werden. Daraufhin führen EX,<br />
MEM und WB nop (= no-operation) aus. Weiters muss der Aktualisieren des PCs und das<br />
Aktualisieren des IF/ID Registers verhindert werden. Die verwendete Instruction muss erneut<br />
übersetzt (decoded) werden, die folgende Instruction wird erneut geholt (gefetched).<br />
Stalls reduzieren die Performance, manchmal sind sie allerdings notwendig, damit korrekte<br />
Resultate erzielt werden. Der Compiler kann den Code so arrangieren, dass Hazards und<br />
Stalls vermieden werden. Dafür werden Kenntnisse über die Pipelinestruktur benötigt.<br />
59
Wann wird jetzt also der Strom von sequentiellen Instructions unterbrochen?<br />
bei Unconditional Branches (unbedingten Verzweigungen: j, jal, jr)<br />
bei Conditional Branches (bedingten Verzweigungen: beq, bne)<br />
bei Exceptions<br />
Was sind mögliche Vorgehensweisen?<br />
<br />
<br />
<br />
<br />
Stall (Verzögerung, hat Auswirkung auf CPI)<br />
den Entscheidungspunkt so früh wie möglich in die Pipeline geben, wodurch die<br />
Anzahl an Stall Cycles verringert wird<br />
die Hardware, die das Ergebnis der Entscheidung liefert, wird in die ID Stufe<br />
gelegt, um die Kosten des taken branch zu reduzieren<br />
die Entscheidung verzögern (delay decision; hier ist Hardware Support nötig)<br />
Vorhersagen der Entscheidung<br />
Data Hazards für Branchs<br />
Wenn ein Vergleichs-Register (comparison register) das Ziel von einer zweiten oder<br />
dritten vorhergehenden ALU Instruction ist<br />
kann durch den Einsatz von Forwarding gelöst werden<br />
60
Wenn ein Vergleichs-Register ein Ziel einer vorhergehenden ALU Instruction oder einer<br />
zweiten vorhergehenden Load Instruction ist<br />
ein Stall Cycle wird benötigt<br />
Wenn ein Vergleichs-Register ein Ziel von einer unmittelbar vorausgehenden Load<br />
Instruction ist<br />
zwei Stall Cycles werden benötigt<br />
Dynamische Branch Prediction<br />
Der Ablauf bei der dynamischen Branch Prediction ist folgender:<br />
- es wird ein Branch Prediction Table verwendet (auch Branch History Table genannt)<br />
- wird über die letzte Branch-Instruction-Adresse indiziert<br />
- speichert das Ergebnis des letzten Branchs (taken oder not taken)<br />
- wenn nun eine Branch Entscheidung getroffen wird:<br />
o Table checken und dasselbe Ergebnis erwarten<br />
o mit dem Fetching der nächsten (vorhergesagten) Instruction beginnen (from<br />
fall-through or target)<br />
o Wenn die Vorhersage falsch war, muss die Pipeline geflutet (flush) werden, die<br />
Vorhersage in der Table wird umgekehrt<br />
61
1-Bit-Predictor<br />
Ein 1-Bit-Predictor wird zweimal falsch liegen, wenn not taken.<br />
Ein Beispiel ist eine Schleife. Ist das predict_bit = 0 und die Schleife wiederholt sich zum<br />
ersten Mal, war die Vorhersage not taken falsch, das Bit muss auf 1 gesetzt werden. Während<br />
sich die Schleife wiederholt, ist aller in Ordnung. Beim Austritt aus der Schleife ist die<br />
Vorhersage taken dann aber falsch, das Bit muss wieder auf 0 gesetzt werden.<br />
2-Bit-Predictor<br />
Ein 2-Bit-Predictor liefert eine Präzision/Richtigkeit von 90%, da hier eine Vorhersage<br />
zweimal falsch sein muss, damit das predict_bit invertiert wird.<br />
62
Exceptions und Interrupts<br />
Exceptions und Interrupts sind unerwartete Events, die eine Änderung im Kontrollfluss<br />
(change in flow control) erfordern.<br />
Eine Exception tritt innerhalb der CPU auf, ist also prozessorintern.<br />
Ein Interrupt kommt von einem externen I/O Controller, ist also ein prozessorexternes<br />
Ereignis.<br />
ILP (Intruction Level Parallelism)<br />
Unter Instruction Level Parallelism (ILP) versteht man die Parallelität innerhalb von<br />
Befehlen. Pipelining führt z.B. mehrere Instructions parallel aus.<br />
Um ILP zu erhöhen, kann eine tiefere Pipeline verwendet werden, das heißt mehr Stufen in<br />
der Pipeline. Dadurch, dass mehr Stufen verwendet werden, gibt es pro Stufe weniger Arbeit<br />
und so wird ein kürzerer Clock Cycle benötigt.<br />
Multiple Issues (Mehrfachzuordnung)<br />
Unter Multiple Issues (Mehrfachzuordnung) versteht man ein Verfahren, bei dem in einem<br />
Taktzyklus mehrere Befehle gestartet werden.<br />
Static Multiple Issue<br />
Hier gruppiert der Compiler (zur Kompilierzeit) Instructions, die gemeinsam gestartet werden<br />
können. Diese werden in sogenannte Issue Slots verpackt. Der Compiler entdeckt Hazards<br />
und kann sie so (durch richtiges Kombinieren der Instructions?) verhindern.<br />
Dynamic Multiple Issue<br />
Hier ist die CPU (während der Ausführungszeit) dafür verantwortlich, die Instrucions<br />
auszuwählen, die gemeinsam bei einem Cycle gestartet werden. Der Compiler kann hier<br />
helfen, indem er Instructions umordnet. Die CPU löst so Hazards, indem sie fortgeschrittene<br />
Techniken dafür zur Laufzeit verwendet.<br />
63