--- title: Rechnerarchitekturen 2 date: Wintersemester 20/21 author: Robert Jeutter --- # Einführung > Rechnerarchitektur = Programmierschnittstelle + Interner Aufbau\\ > Einheit von Struktur und Funktion - Programmierschnittstelle - Schnittstelle zwischen Rechner und Benutzer bzw. der Hardware und der untersten Softwareschicht - Befehlssatzarchitektur (Instruction Set Architecture) - Interner Aufbau - Hardware-Aufbau von Komponenten, die die Rechnerarchitektur realisieren - Speichereinheiten, Recheneinheiten, Verbindungssysteme, Abstraktionsebenen eines Rechnersystems | Anwendungsprogramm | Java, C,... | | Assemblerprogramm | Betriebssystem-Ebene | | Maschinenprogramm | Betriebssystem-Ebene | | Register Transfer Verhaltensebene | Reg[2]:=Reg[3] | | Register Transfer Strukturebene | Addierer, Multiplexer, Register | | Gatterebene | $f=a\vee bc$ | | Transistorebene | | Grundarchitekturen: - Harvard (Zugriff direkt durch Prozessor) - Princton/von-Neumann (Zugriff über Systembus) | Speicher | Daten und Instruktionen speichern | | Steuerwerk | beinhaltet Programmzähler um Ausführung zu steuern | | Rechenwerk | auch ALU (Arithmetic and Logic Unit) um Berechnung durchzuführen Üblicherweise besitzt eine Recheneinheit (CPU) Daten- oder Rechenregister (Registermaschine). Berechnungen werden ausschließlich mit den Registern vorgenommen. - Daten aus Hauptspeicher in Register laden - Berechnungsaufgaben durchführen - Ergebnisse in Hauptspeicher ablegen Klassifikation von Befehlssatzarchitekturen - 0-Operand (Stack): Add - 1-Operand (Akkumulator): Add R1 - 2-Operand: Add R1, R3 - 3-Operand: Add R1, R2, R3 # Prozessorarchitektur Programmiermodelle, Instruction Set Architectures (ISAs): Klassifikation von Befehlssätzen nach der Gestaltung/Ausprägung der vorhandenen Maschinenbefehle | CISC | RISC | MIPS | | -- | -- | -- | | Complex Instruction Set Computing | Reduced Instruction Set Computing | Microprocessor without interlocked pipeline stages | | Einfache und komplexe Befehle | Nur einfache Befehle | | | Heterogener Befehlssatz | Orthogonaler Befehlssatz | | | Verschiedene Taktzahl pro Befehl | Meist 1 Takt pro Befehl | | | Viele Befehlscode-Formate mit unterschiedlicher Länge | Wenige Befehlscode-Formate mit einheitlicher Länge | | | Mikroprogrammwerk | Direktverdrahtung | | | Vermischung von Verarbeitungs- und Speicherbefehlen | Trennung von Verarbeitungs- und Speicherbefehlen | | | schwierig, unter CPI = 2 zu kommen | CPI möglichst nicht über 1 | | > Unter dem CPI (cycles per instruction) -Wert einer Menge von Maschinenbefehlen versteht man die mittlere Anszahl der Taktzyklen pro Maschinenbefehl ## Einzelzyklusmaschine - Programmzähler (32 bit, PZ, engl. Program Counter, PC) - Speichert und liefert die aktuelle auszuführende Instruktionsadresse - an den Instruktionsspeicher (b) und das Addierwerk (a) - übernimmt die Adresse der Folgeinstruktion (c) - Addierwerk - Fortlaufende Addition mit 4, da 4-Byte Instruktionen - Der neue Wert für PZ wird im Register gespeichert (c) - Die hintersten 2 Bit im PZ sind immer Null - Instruktionsspeicher - Liefert die auszuführende Maschineninstruktion - Instruktionswort (32 bit) - Gelesener Wert erscheint am Ausgang des Speichers - Instruktionsformat bestimmt den weiteren Ablauf - Master-Slave Flip-Flops - Master übernimmt Wert bei steigender Taktflanke - Slave übernimmt Wert bei fallender Taktflanke - Instruktionszyklus beginnt bei fallender Taktflanke - Ansteuerung des Registersatzes - Register immer auslesen (kein Takt) und Transport zur ALU - Schreiben des Zielregisters Register[rd] am Ende der Taktperiode - Zeit für Speicherzugriff und für die primäre ALU muss eingeplant werden - Ausgabe des Instruktionsspeichers wird über die ganze Dauer gehalten - Vorzeichenerweiterung des Direktoperanden von 16 auf 32 Bit - Erleichtert die Unterbringung kleiner Konstanten im Befehlswort - Vom Steuerwerk aus abschaltbar für „unsigned“ Befehle ### Ausführungsphase - ALU-Registeroperationen - Operanden im Register oder als - Direktoperand - Üblicher Satz an ALU-Operationen - Register $0 liefert Wert 0 - Adressierung von Variablen im Speicher - Adressrechnung in der primären ALU - Basisregister plus Direktoperand - Registerinhalt lesen/schreiben - Load/Store-Architektur - Speicheroperationen können keine Arithmetik - also z.B. kein inc 0xaff0($7),0x0004 - ALU schon zur Adressberechnung benötigt - Separater Addierer zur Sprungzielberechnung - Prüfschaltung auf Gleichheit zweier Register in der primären ALU („eql“) - Bedingte Sprünge mit einem 16-bit Direktoperanden (beq $7,$8,loop) - Maximal möglicher Offset von ±17 Bit nach einer 2-bit Verschiebung - Unbedingte Sprünge mit 28-bit Adresse später ### Speicherzugriff - Getrennte Speicher für Code & Daten - Aktuelle Instruktion wird bis zum Ende des Gesamtzyklus gehalten - Kein zweiter Zugriff im gleichen Taktzyklus möglich - Quellregister speichern, falls Store - Speichersteuerung durch besonderes Schreibsignal - Zielregister laden - Falls Ladebefehl aus dem Speicher - Falls Rücksprungadresse (PC-magic) - Falls Resultat aus ALU - ALU-Resultat nutzen - Für „Register Write-Back“ - Als Datenspeicheradresse - Nicht direkt speichern, wg. Load/Store-Architektur! ### Register zurückschreiben - Nummer des Zielregisters (Zielregisterselektor) - Stammt aus IR[15-11] oder IR[20-16], 5-bit Bereich für Werte 0-31 - Steuersignal - Zielregister zum Ende des Instruktionszyklus schreiben - Schreibsignal an Registersatz, falls nötig - Pseudorelative Sprünge (jump xLabel) - Kein separater Addierer erforderlich, nur ein zusätzlicher MUX-Eingang - Oberste 4 Bits unverändert, untere 28 Bits werden ersetzt (4, 26, 2) - Jump-and-Link (jal) sichert alten Programmzähler in $31 (Subroutine) ### erforderliche Steuerleitung - Für Speicher - 2-bit Steuersignal: 0/8/16/32 Bit zum Datenspeicher schreiben - Instruktionsspeicher liest immer - Für Registersatz - 2-bit Steuersignal: 0/8/16/32 Bit zum Registerfile schreiben - Für 4 Multiplexer - 2-bit Steuersignal: Auswahl des Zielregisters (1 aus 3) - 2-bit Steuersignal: Datenquelle für Zielregister - 2-bit Steuersignal: Sprungziel wählen - 1-bit Steuersignal: Direkt- oder Registeroperand für ALU - Für Arithmetik - 1-bit Steuersignal: Vorzeichenerweiterung ja/nein - 6-bit Steuersignal: ALU-Operation Einzyklusmaschine ist unwirtschaftlich - Komponenten arbeiten jeweils nur einen kleinen Teil der Gesamtzeit - Zeitverlust bei potentiell kurzen Instruktionen ## Mehrzyklen CPU - Gesamtzyklus der bisherigen MIPS - Dauer des Instruktionszyklus ist die Summe der Einzelverzögerungen - Unteraktivitäten müssen abwarten, bis die Parameter sicher vorliegen - Anderenfalls können sich „spurious writes“ ergeben - z.B. in Registersatz oder in den Speicher - Mehrzyklen-CPU als Überleitung zum Fließbandprinzip - Aufteilung der Befehlsausführung auf mehrere gleich lange Taktzyklen - Einfügen von Registern für in den Stufen entstandene Zwischenresultate - Noch immer nur eine Instruktion zu einem Zeitpunkt in Ausführung - CPU-Zustand bezieht sich auf eine einzelne aktuelle Instruktion - Pipelined CPU – mit Fließbandprinzip - In jedem Taktzyklus beginnt eine neue Maschineninstruktion - Mehrere Instruktionen gleichzeitig in Ausführung - Aber unterschiedlicher Fertigstellungsgrad - Bessere Auslastung der Hardware - Höherer Durchsatz - Große Pipeline-Tiefe: - Zusätzliche Ressourcen, höherer Energieaufwand (Taktfrequenz!) - Längere Instruktionssequenzen für gleichen oder besseren Speedup (→ Registeroverhead!) - Bei unterschiedlichen Stufenverzögerungen bestimmt die langsamste Stufe die Taktfrequenz - Lange Instruktionssequenzen: - Meist wegen Daten- und Kontrollabhängigkeiten nicht machbar - Hohe Latenz – Füllen und Leeren der Pipeline! - Warum geht die Anzahl der Pipeline-Stufen zurück? - hoher Energieverbrauch - hohe Leistungseinbußen durch Kontroll- und Datenabhängigkeiten (Füllen/Leeren der Pipeline) - mehr Parallelität in den einzelnen Pipeline-Stufen → superskalare Prozessoren - mehr Prozessorkerne mit geringerer Leistungsaufnahme pro Kern - Fließband-Architektur (engl. pipeline architecture): Bearbeitung mehrerer Befehle gleichzeitig, analog zu Fertigungsfließbändern. Aufgaben der einzelnen Phasen - Befehlsholphase - Lesen des aktuellen Befehls; separater Speicher, zur Vermeidung von Konflikten mit Datenzugriffen - Dekodier- und Register-Lese-Phase - Lesen der Register möglich wegen fester Plätze für Nr. im Befehlswort - Ausführungs- und Adressberechnungsphase - Berechnung arithmetischer Funktion bzw. Adresse für Speicherzugriff - Speicherzugriffsphase - Wird nur bei Lade- und Speicherbefehlen benötigt - Abspeicherungsphase - Speichern in Register, bei Speicherbefehlen nicht benötigt Pipeline-Hazards > Structural Hazards (deutsch: strukturelle Abhängigkeiten oder Gefährdungen): Verschiedene Fließbandstufen müssen auf dieselbe Hardware-Komponente zugreifen, weil diese nur sehr aufwändig oder überhaupt nicht zu duplizieren ist. > Definition: Ein Befehl i heißt von einem nachfolgenden Befehl j antidatenabhängig, falls j eine Speicherzelle beschreibt, die von i noch gelesen werden müsste. > Definition: Zwei Befehle i und j heißen voneinander Ausgabeabhängig, falls i und j die selbe Speicherzelle beschreiben. - Gleichheit der Register wird schon in der instruction decode-Stufe geprüft - Sprungziel wird in separatem Adressaddierer ebenfalls bereits in der instruction decode-Stufe berechnet - Sofern weiterhin noch Verzögerungen auftreten: - nächsten Befehl einfach ausführen (delayed branch) - oder weiterhin NOOP(s) einfügen (stall) ### Pipelining – Zusammenfassung - Die Fließbandverarbeitung (engl. pipelining) ermöglicht es, in jedem Takt die Bearbeitung eines Befehls abzuschließen, selbst wenn die Bearbeitung eines Befehls ≥ 1 Takte dauert - Mehrere Pipelines -> pro Takt können mehrere Befehle beendet werden - 3 Typen von Gefährdungen des Fließbandbetriebs: - resource hazards - control hazards - data hazards (RAW, WAR, WAW) - Gegenmaßnahmen - pipeline stall - branch prediction - forwarding / bypassing - delayed branches - out-of-order execution - dynamic sched ## Sprungvorhersage Je mehr die Parallelität ausgenützt werden soll, desto mehr sind Kontrollkonflikte der limitierender Faktor! Dynamische Sprungvorhersage - Zur Laufzeit durch Prozessor-Hardware - Vorhersage, ob ein bedingter Sprung genommen wird oder nicht - Abhängig von der Vorhersage: Füllen der Prozessor-Pipeline mit Befehlen ab der vorhergesagten Programm-Stelle - Reduktion der branch penalty, falls vorhergesagtes Programm-Verhalten mit tatsächlichem übereinstimmt ### Einfache lokale Prädiktoren - Liefern Vorhersage, ob bedingter Sprung genommen wird oder nicht - Prädiktion allein anhand der Historie des betrachteten, aktuellen Sprungs - Historie eines Sprungs wird mit 1, 2 oder n Bits gepuffert - Sprungvorhersage-Puffer - Branch prediction buffer oder branch history table - Kleiner Speicher, der mit (Teil der) Adresse des Sprungbefehls indiziert wird - Verwendet nur wenige untere Bits der Adresse - Enthält 1 Bit: Sprung beim letzten Mal ausgeführt (taken) oder nicht (not taken) - Prädiktion: Sprung verhält sich wie beim letzten Mal - Nachfolgebefehle ab vorhergesagter Adresse holen - Falls Prädiktion fehlerhaft: Prädiktionsbit invertieren - Einfachste Art von Puffer (keine Tags, d.h. keine Überprüfung, ob Adresse tatsächlich im Puffer) - Entspricht sehr einfachem Cache - Hat eine bestimmte Kapazität - Kann nicht für alle Sprünge (aktuelle) Einträge enthalten - Reduziert branch penalty nur, wenn branch delay länger als Berechnung der Zieladresse mit branch prediction buffer dauert - Prädiktion kann fehlerhaft sein - Prädiktion kann von anderem Sprungbefehl stammen (mit gleichen Bits im Indexteil der Adressen) - Nachteile des einfachen 1-Bit Vorhersageschemas - Höhere Fehlerrate als überhaupt möglich, wenn Häufigkeit der Sprungentscheidungen betrachtet wird - D.h. auch wenn Sprung fast immer ausgeführt (taken) wird, entstehen 2 Fehler anstatt 1 2-Bit Branch-Prediction Buffer - Speicherung der Historie, Befehlsadressen als Zugriffsschlüssel: Allgemein: n-Bit Prädiktor (Spezialfall: 2-Bit) - Verwendet n-Bit Zähler - Sättigungsarithmetik (kein wrap around bei Überlauf) - Kann Werte zwischen 0 und 2 n - 1 annehmen - Wenn Zähler größer als Hälfte des Maximums (2 n - 1): Vorhersagen, dass Sprung ausgeführt wird; ansonsten vorhersagen, dass Sprung nicht genommen wird - Zähler wird bei ausgeführtem Sprung inkrementiert und bei nicht ausgeführtem dekrementiert - In der Praxis: 2-Bit Prädiktor ähnlich gut wie n-Bit Prädiktor - In den meisten Prozessoren heute: 2-Bit Prädiktor für (lokale) Vorhersage - Einschränkung des n-Bit (bzw. 2-Bit) Prädiktors: - Betrachtet nur (vergangenes) Verhalten eines Sprungs, um dessen (zukünftiges) Verhalten vorherzusagen. - Arbeitet rein lokal! - Idee: Verbesserung durch Betrachtung des Verhaltens anderer Sprünge - Man erhält so genannten korrelierenden Prädiktor (correlating predictor) oder zweistufigen Prädiktor - Prinzip: Aufgrund globaler Information (anderer Sprünge) wird einer von mehreren lokalen Prädiktoren ausgewählt ### Korrelierende Prädikatoren - Beziehen zur Vorhersage des Verhaltens eines Sprungs Kontext-Information mit ein, d.h. die Historie anderer Sprungbefehle - Prädiktor benutzt globale Kontext-Bits, um einen von mehreren lokalen Prädiktoren auszuwählen - Betrachten wiederholte Ausführung des Codefragments (ignorieren dabei alle anderen Sprünge, inkl. dem für Wiederholung) - Einschränkung: Statt aller möglicher Sequenzen: d wechselt zwischen 2 und 0 Zweistufiger Prädiktor - Verwendet 1 Bit Kontextinformation - Es existieren 2 lokale Prädiktoren, beide je 1-Bit - Kontext: Letzter (i.a. anderer) Sprung wurde ausgeführt/nicht ausgeführt (1 Bit) - Vorhersage des zweistufigen Prädiktors: Anhand des Kontexts wird lokaler Prädiktor für die Vorhersage des aktuell betrachteten Sprungs ausgewählt - Letzter Sprung ist i.a. nicht gleich aktuellem, vorherzusagendem Sprung (nur in einfachen Schleifen) - Notation des Prädiktorstatus: / mit - : Vorhersage, falls letzter Sprung not taken, d.h. Kontext = NT - : Vorhersage, falls letzter Sprung taken, d.h. Kontext = T - und Vorhersagen: jeweils entweder T oder NT (m,n)-Prädiktor - Betrachtet als Kontext das Verhalten der letzten m Sprünge, um aus $2^m$ vielen lokalen Prädiktoren einen n-Bit Prädiktor auszuwählen - Vorteil gegenüber (rein lokalem) 2-Bit Prädiktor - Höhere Vorhersagegenauigkeit - Erfordert kaum Hardwareaufwand - Sprunggeschichte (Kontext, „Ausgang“ vorangegangener Sprünge) kann in m-Bit Schieberegister gespeichert werden (1 Bit für jeden der m vielen letzten Sprünge im Kontext, Bit gleich 1 wenn Sprung taken) - Vorhersagepuffer adressiert via Konkatenation von - Unteren Adressbits der Sprungbefehlsadresse - m Bit globaler Sprunggeschichte ### High Performance Befehlsdekodierung In Hochleistungs-Pipelines ist reine Vorhersage eines Sprungs i.d.R. nicht ausreichend - Insbesondere: Falls mehrere Befehle pro Takt auszugeben sind - Befehlsstrom mit großer Bandbreite erforderlich! - Kontrollflussabhängigkeiten dürfen nicht „wahrnehmbar“ sein - Maßnahmen hierfür - Pufferung von Sprungzielen, und nicht nur Vorhersage des Sprungverhaltens (branch target buffer) - Integrierte Einheit für das Holen der Befehle (d.h. nicht nur [relativ] einfache erste Stufe der Pipeline) - Vorhersage von Rücksprungadressen (bei Prozeduraufruf) ### Branch Target Buffer 5-stufige Pipeline, Auswertung von Sprungbedingungen in EX: - Branch delay von 2 Takten - Mit Sprungvorhersage (branch prediction buffer) - Zugriff erfolgt in ID (Adresse des Sprungbefehls schon in IF bekannt; aber: - evtl. angesprungenes Ziel erst nach Befehlsdecodierung [ID]) - Nächste vorhergesagte Instruktion kann erst nach ID geholt werden - Branch delay = 1, falls Prädiktion korrekt - Mit Pufferung des Sprungziels (branch target buffer) - Zugriff auf branch target buffer erfolgt in IF. Verhalten wie „echter“ Cache, - adressiert mit Sprungbefehlsadresse (überprüft, ob Cache-Hit) - Liefert vorhergesagte Adresse als Ergebnis, d.h. nächsten PC (d.h. nicht nur Vorhersage über Sprungverhalten) - Keine Verzögerung, falls Prädiktion korrekt! Zusätzliche Speicherung auch des Sprungziels, z.B. Kombination mit branch prediction buffer Bei geschickter Organisation kann das Fließband immer gefüllt bleiben; die Sprünge kosten dann effektiv keine Zeit; CPI <1 möglich. Eigenschaften - Verzögerung durch Sprung kann vollständig vermieden werden (sofern Vorhersage korrekt), da bereits in IF Entscheidung über nächsten Befehlszähler (PC) getroffen wird. - Da Entscheidung allein auf Basis des PC getroffen wird, muss überprüft werden, ob Adresse im Puffer (impliziert, dass Sprungbefehl vorliegt) - Speicherung im Prinzip nur für Sprünge notwendig, die als ausgeführt vorhergesagt werden (not taken = normale sequentielle Dekodierung geht weiter) - Achtung – bei falscher Vorhersage - Entsteht ursprüngliche Sprung-Verzögerung, plus - Aufwand zur Aktualisierung des Vorhersagepuffers ### Integrierte Befehls-Hol-Einheit (IF Unit) Insbesondere mit Blick auf multiple-issue Prozessoren eigene (autonome) funktionale Einheit für Befehlsholphase - Führt Befehlscodes in Pipeline ein - Integrierte Funktionalitäten - Sprungvorhersage: Wird Teil der Befehlsholphase - Instruction Pre-fetch: Insbes. um mehrere Befehle pro Takt liefern (und später ausgeben) zu können, läuft Befehlsholen weiterer Dekodierung voraus (= pre-fetch) - Zugriff auf Befehlsspeicher: Bei mehreren Befehlen pro Takt mehrere Zugriffe erforderlich (bei Cache auf ggfs. mehrere cache lines). Werden hier koordiniert/geplant - Befehlspuffer: Befehle können hier (lokal im Prozessor!) von Issue-Stufe nach Bedarf abgerufen werden ### Vorhersage von Rücksprungadressen Allgemeines Ziel: Vorhersage indirekter Sprünge (d.h. bzgl. Basisadresse in Register) - Hauptverwendung: Rückkehr aus Prozeduraufrufen - MIPS: Prozeduraufruf per jal proc, Rückkehr per jr $31 - Vorhersage mit branch target buffer schlecht, da Aufruf aus unterschiedlichen Codeteilen heraus möglich - Methode: (Stack-) Speicher für Rücksprungadressen - Push bei Prozeduraufruf (call), und - Pop bei Rücksprung (return) - Vorhersagequalität „perfekt“, wenn Stack-Puffer größer als maximale Aufruftiefe # Speicherarchitektur # Microcontroller und Digitale Signalprozessoren # Parallele Architekturen # Leistungsbewertung