16.01.2014 Aufrufe

Rechnerstrukturen: VO, WS 2012/13 → Teil 1 - VoWi

Rechnerstrukturen: VO, WS 2012/13 → Teil 1 - VoWi

Rechnerstrukturen: VO, WS 2012/13 → Teil 1 - VoWi

MEHR ANZEIGEN
WENIGER ANZEIGEN

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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!