Rechnerstrukturen Skript zur Vorlesung an der RWTH-Aachen Lehrstuhl für Informatik IV Prof. Dr. O. Spaniol Dr. Mesut Güneş Ralf Wienzek 2 Inhaltsverzeichnis 1 Grundlegende Konzepte 1.1 Funktionelle Einheiten eines Rechners . . . . . . . . . . . . . . . . 1 1.2 Speicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.3 Programmausführung . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.3.1 Von-Neumann-Prinzip . . . . . . . . . . . . . . . . . . . . 4 1.3.2 Alternative Modelle . . . . . . . . . . . . . . . . . . . . . 5 1.3.3 Pipelining . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.3.4 Ausführungssteuerung . . . . . . . . . . . . . . . . . . . . 6 Datenübertragung . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4.1 Leitungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4.2 Busse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.4 2 1 Informationsdarstellung 9 2.1 9 Allgemeines zur Darstellung ganzer Zahlen . . . . . . . . . . . . . 2.1.1 2.2 2.3 Stellenwertcodierung . . . . . . . . . . . . . . . . . . . . . 10 Codierungen für ganze Zahlen . . . . . . . . . . . . . . . . . . . . 11 2.2.1 Betrag- und Vorzeichen . . . . . . . . . . . . . . . . . . . . 11 2.2.2 2-Komplement . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.3 1-Komplement . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.4 Transformation von negativen Zahlen . . . . . . . . . . . . 14 2.2.5 Addition und Subtraktion . . . . . . . . . . . . . . . . . . . 15 2.2.6 Überlaufproblematik . . . . . . . . . . . . . . . . . . . . . 16 Darstellung rationaler Zahlen . . . . . . . . . . . . . . . . . . . . . 17 2.3.1 Festkommazahlen . . . . . . . . . . . . . . . . . . . . . . 17 2.3.2 Gleitkommazahlen . . . . . . . . . . . . . . . . . . . . . . 18 i ii Inhaltsverzeichnis 2.4 2.5 3 Addition und Subtraktion bei Gleitkommazahlen . . . . . . 20 2.3.4 Multiplikation und Division bei Gleitkommazahlen . . . . . 21 Darstellung von Zeichen . . . . . . . . . . . . . . . . . . . . . . . 22 2.4.1 Codierung mit Umschaltsymbolen . . . . . . . . . . . . . . 22 2.4.2 Codierung durch Bitstrings . . . . . . . . . . . . . . . . . . 23 2.4.3 Darstellung von Dezimalziffern . . . . . . . . . . . . . . . 24 Erkennung und Behebung von Bitfehlern . . . . . . . . . . . . . . . 25 2.5.1 26 Hamming-Distanz . . . . . . . . . . . . . . . . . . . . . . Maschinennahe Programmierung 29 3.1 Darstellung von Befehlen . . . . . . . . . . . . . . . . . . . . . . . 29 3.1.1 Aufbau eines Befehlswortes . . . . . . . . . . . . . . . . . 29 3.1.2 Befehlstypen . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.1.3 Befehlssätze . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.1.4 Verwendung zusätzlicher Register und Registerbefehle . . . 34 Adressierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.1 Direkte Adressierung . . . . . . . . . . . . . . . . . . . . . 35 3.2.2 Indirekte Adressierung durch Basisregister und Relative Distanz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.3 Indexregister zur indirekten Adressierung . . . . . . . . . . 36 3.2.4 Indexregister mit Adressmodifikation . . . . . . . . . . . . 37 3.2.5 Unterschiedliche Adressmodi . . . . . . . . . . . . . . . . 37 3.2.6 Indirekte Adressierung . . . . . . . . . . . . . . . . . . . . 37 3.2.7 Modifizierung von Befehlen . . . . . . . . . . . . . . . . . 38 Leistungsfähigkeit von Adressiertechniken . . . . . . . . . . . . . . 38 3.3.1 Berechnung durch ein Programm . . . . . . . . . . . . . . 38 3.3.2 Endlichkeit der realen Maschine . . . . . . . . . . . . . . . 39 3.3.3 Beliebig große Funktionswerte sollen berechenbar sein . . . 42 Unterprogramme . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.4.1 Einstufige, nichtrekursive Unterprogramme . . . . . . . . . 43 3.4.2 Mehrstufige, nichtrekursive Unterprogramme . . . . . . . . 44 3.4.3 Mehrstufige, eventuell rekursive Unterprogramme . . . . . 44 3.2 3.3 3.4 4 2.3.3 Bausteine und Komponenten von Rechensystemen 47 4.1 47 Schaltfunktionen, Bausteinsysteme und Boolesche Algebra . . . . . Inhaltsverzeichnis 4.2 4.3 4.4 5 iii 4.1.1 Entwurf und Realisierung von Schaltfunktionen . . . . . . . 49 4.1.2 Atom, Minterm und Maxterm . . . . . . . . . . . . . . . . 54 4.1.3 Boolesche Ausdrücke . . . . . . . . . . . . . . . . . . . . . 56 Normalformen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.2.1 Disjunktive Normalform (DNF) . . . . . . . . . . . . . . . 59 4.2.2 Konjunktive Normalform (KNF) . . . . . . . . . . . . . . . 60 4.2.3 Komplementfreie Ringsummenentwicklung (KRE) . . . . . 60 Synthese von Schaltkreisen, Minimierung . . . . . . . . . . . . . . 62 4.3.1 Eingeschränktes Minimierungsproblem . . . . . . . . . . . 63 4.3.2 Quine-McCluskey-Algorithmus . . . . . . . . . . . . . . . 64 4.3.3 Karnaugh-Diagramme . . . . . . . . . . . . . . . . . . . . 70 Schaltwerke und Speicherelemente . . . . . . . . . . . . . . . . . . 71 4.4.1 72 Speicherelemente . . . . . . . . . . . . . . . . . . . . . . . Die Arithmetisch-Logische Einheit 79 5.1 Addition und Subtraktion . . . . . . . . . . . . . . . . . . . . . . . 80 5.1.1 Halbaddierer . . . . . . . . . . . . . . . . . . . . . . . . . 80 5.1.2 Volladdierer . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.1.3 Carry-Ripple-Addierer . . . . . . . . . . . . . . . . . . . . 82 5.1.4 Serieller Addierer . . . . . . . . . . . . . . . . . . . . . . . 84 5.1.5 Von-Neumann-Addierer . . . . . . . . . . . . . . . . . . . 86 5.1.6 Erweiterungen des Von-Neumann-Addierers . . . . . . . . 90 5.1.7 Carry-Look-Ahead-Addierer . . . . . . . . . . . . . . . . . 93 5.1.8 Carry-Skip-Addierer . . . . . . . . . . . . . . . . . . . . . 95 5.1.9 Conditional-Sum-Addierer . . . . . . . . . . . . . . . . . . 99 Multiplikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 5.2.1 Serielle Multiplikation . . . . . . . . . . . . . . . . . . . . 103 5.2.2 Multiplikatorcodierung . . . . . . . . . . . . . . . . . . . . 105 5.2.3 Adder-Tree und Pipelining . . . . . . . . . . . . . . . . . . 108 Division . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 5.3.1 Serielle Division . . . . . . . . . . . . . . . . . . . . . . . 110 5.3.2 Iterative Division . . . . . . . . . . . . . . . . . . . . . . . 115 5.3.3 Iterative Berechnung von Quadratwurzeln . . . . . . . . . . 119 Arithmetik bei redundanter Zahlendarstellung . . . . . . . . . . . . 122 5.2 5.3 5.4 iv Inhaltsverzeichnis 5.4.1 SDNR-Darstellung für die Addition . . . . . . . . . . . . . 122 5.4.2 Gemischtes SDNR-Verfahren . . . . . . . . . . . . . . . . 124 5.4.3 SRT-Verfahren zur seriellen Division . . . . . . . . . . . . 125 Abbildungsverzeichnis 1.1 Komponenten eines Rechners . . . . . . . . . . . . . . . . . . . . . 2 1.2 Pipelining von Befehlen . . . . . . . . . . . . . . . . . . . . . . . 5 1.3 Ausführungssteuerung mit Interruptsignalen . . . . . . . . . . . . . 6 1.4 Zwei-Bus-Architektur . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.5 Unibus-Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.6 Pufferung des Buszugangs . . . . . . . . . . . . . . . . . . . . . . 8 2.1 Kodierung einer Gleitkommazahl . . . . . . . . . . . . . . . . . . . 18 3.1 Befehlsstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.2 Indirekte Adressierung mit Basisregister und relativer Distanz . . . 36 3.3 Algorithmus zur Berechnung von h0 , h1 , . . . . . . . . . . . . . . . . 41 3.4 Aufruf und Beenden von Unterprogrammen . . . . . . . . . . . . . 43 3.5 Einstufiger Unterprogrammaufruf . . . . . . . . . . . . . . . . . . 44 3.6 Mehrstufiger Unterprogrammaufruf . . . . . . . . . . . . . . . . . 44 4.1 Karnaugh-Diagramme . . . . . . . . . . . . . . . . . . . . . . . . 70 4.2 Schaltkreis und Schaltwerk . . . . . . . . . . . . . . . . . . . . . . 72 4.3 Takt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 4.4 RS-Flipflop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 4.5 Schaltung des RS-Flipflops . . . . . . . . . . . . . . . . . . . . . . 74 4.6 Schaltsymbole für das RS-Flipflop . . . . . . . . . . . . . . . . . . 75 4.7 Earle Latch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 4.8 Schaltung des Earle Latch . . . . . . . . . . . . . . . . . . . . . . 76 4.9 Hazards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 4.10 Taktflanke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 v vi Abbildungsverzeichnis 5.1 Addition nach Schulmethode . . . . . . . . . . . . . . . . . . . . . 80 5.2 Symbol für einen Halbaddierer . . . . . . . . . . . . . . . . . . . . 80 5.3 Schaltung für den Halbaddierer . . . . . . . . . . . . . . . . . . . . 81 5.4 Schaltung und Schaltsymbol eines Volladdierers . . . . . . . . . . . 82 5.5 Carry-Ripple-Addierwerk . . . . . . . . . . . . . . . . . . . . . . . 83 5.6 Serielles Addierwerk . . . . . . . . . . . . . . . . . . . . . . . . . 84 5.7 Von-Neumann-Addierwerk . . . . . . . . . . . . . . . . . . . . . . 86 5.8 Ablaufsteuerung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 5.9 Dioden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 5.10 Diodenmatrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.11 Kurznotation des Von-Neumann-Addierers . . . . . . . . . . . . . . 91 5.12 Carry-Save-Addierer . . . . . . . . . . . . . . . . . . . . . . . . . 91 5.13 Lineare Verschaltung von CSAs . . . . . . . . . . . . . . . . . . . 92 5.14 Wallace Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 5.15 Carry-Look-Ahead-Addierer für Gruppengröße 4 . . . . . . . . . . 94 5.16 Gruppe eines Carry-Skip-Addierers . . . . . . . . . . . . . . . . . 96 5.17 Schaltung zum Carry-Skip-Addierer zweiter Ordnung . . . . . . . . 98 5.18 Kombination von Carry-Skip und Carry-Look-Ahead . . . . . . . . 99 5.19 Gruppen bei der Conditional-Sum-Addition . . . . . . . . . . . . . 99 5.20 Schema zur seriellen Multiplikation . . . . . . . . . . . . . . . . . 104 5.21 Multiplikation als Addition im Adder-Tree . . . . . . . . . . . . . . 109 5.22 Pipelining durch Adder-Tree . . . . . . . . . . . . . . . . . . . . . 109 5.23 Adder-Tree für Einzelmultiplikation . . . . . . . . . . . . . . . . . 110 5.24 Serielle Multiplikation und Division . . . . . . . . . . . . . . . . . 111 5.25 Table-Look-Up-Methode . . . . . . . . . . . . . . . . . . . . . . . 115 5.26 Newton-Verfahren . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 KAPITEL 1 Grundlegende Konzepte Wir befassen uns in dieser Vorlesung mit digitalen und programmgesteuerten Rechensystemen. Der Rechner akzeptiert digitale Eingabedaten (Eingangsargumente), verarbeitet sie mit einem Programm, das in seinem Speicher steht, und produziert digitale Ausgabedaten. Nimmt man vereinfachend an, dass die Eingabedaten im Speicher stehen und die Ergebnisse ebenfalls im Speicher abgelegt werden, dann bewirkt ein Programm eine Transformation des alten Speicherinhaltes in einen neuen Speicherinhalt. Rechnersysteme unterscheiden sich grundlegend voneinander bezüglich Größe, Geschwindigkeit, Zahl anschließbarer beziehungsweise bedienbarer Geräte oder Benutzer und ihrer Kosten. Eine mögliche Klassifizierung von Rechnersystemen ist die Folgende, wobei die Grenzen zwischen den Klassen fließend sind: • Supercomputer • Workstation • Personal Computer • Prozessrechner • Personal Digital Assistant (PDA) • Smartphone 1.1 Funktionelle Einheiten eines Rechners Ein Rechnersystem besteht aus folgenden Einheiten, die sich aufgrund ihrer Funktion innerhalb des Systems voneinander unterscheiden. Die Eing- und die Ausgabeeinheit bilden die Schnittstellen zur Außenwelt. Der Prozessor (Central Processing Unit, CPU) hat Aufgaben der Steuerung und Arithmetik. Er vereinigt demnach die Steuereinheit (Control Unit) und die Recheneinheit 1 2 Kapitel 1. Grundlegende Konzepte (Arithmetic Logical Unit, ALU). Eine weitere wichtige Einheit ist der Speicher. Auf ihn wird später eingegangen. Abbildung 1.1 veranschaulicht die Zusammenarbeit der einzelnen Komponenten. Es gibt erweiterte Konzepte, bei denen mehrere Rechner zusammenarbeiten, wobei diese dann durch ein Kommunikationsnetz verbunden sind. Rechner Außenwelt Mensch, elektr. Gerät, Leitungen zu anderen Rechnern Eingabe Datenprozessor RAM Ausgabe Befehlsprozessor ROM CPU Speicher Steuerinformationen Daten Abbildung 1.1: Komponenten eines Rechners Ein Rechensystem verarbeitet Informationen, die im Speicher gehalten werden. Man unterscheidet zwei Arten von Informationen. Zum einen Befehle, die Aktivitäten des Prozessors anstoßen, und auf der anderen Seite Daten, die manipuliert werden. Ein Programm ist dabei eine Folge von Befehlen. Typische Daten sind zum Beispiel Zahlen und Zeichen, die in geeigneter Codierung im Speicher abgelegt werden. Da sich ein Programm meist auch im Speicher befindet, sind die Codierungen der Befehle auch als Daten auffassbar. Der Prozessor verarbeitet Informationen im einfachsten Fall nach folgendem Ablauf: 1. Befehl (aus dem Speicher) holen 2. Befehl interpretieren 3. Aufgrund der Interpretation des Befehls nötige Operanden holen 4. Befehl (Operation) ausführen Anschließend wird dieser Ablauf mit dem nächsten Befehl durchgeführt. Die Bearbeitung erfolgt sequenziell, das heißt, die Befehle werden nacheinander und jeder für sich zu einem Zeitpunkt ausgeführt. Es gibt Befehle, deren Aufgabe es ist, nicht den nachfolgenden, sondern einen an anderer Stelle im Speicher befindlichen Befehl anzustoßen (so genannter Sprungbefehl). Ferner dienen Vergleichsbefehle dazu, Fallunterscheidungen zu bilden, so dass aufgrund eines Speicherzustandes eine bestimmte Aktion ausgeführt wird. 1.2. Speicher 3 Üblicherweise werden ein Programm (oder auch mehrere) und die dazugehörigen Daten im Speicher gehalten. Zu diesem Zweck enthält der Speicher eine Menge von adressierbaren Speicherzellen (Worten). Zur Unterscheidung zwischen Daten und Befehlen muss man entweder die Speicherzelle markieren (zum Beispiel ein gesondertes Bit des Wortes zur Verfügung stellen wie: 1 steht für Datum und 0 steht für Befehl) oder den aktuellen CPU-Zustand interpretieren. Die folgende Abbildung zeigt den Speicher, der in zwei Abschnitte zur Aufnahme von Programmen und Rechendaten aufgeteilt ist. Adresse Inhalt 0 .. .. Programmspeicher P . . N1 − 1 0 .. .. . . Rechenspeicher R N2 − 1 Wir nehmen also ohne wesentliche Einschränkung an, dass wir zwei Speicher P und R haben, die jeweils von Null ab adressiert werden. Den Inhalt der Speicherzellen kennzeichnen wir im Fall eines • Befehls mit π(i): aktueller Inhalt von Zelle i in P • Datums durch ρ( j): aktueller Inhalt der Zelle j in R 1.2 Speicher Man unterscheidet im Wesentlichen drei Arten von Speicher. Der Hauptspeicher (Primary Memory, Main Memory) ist direkt adressierbar mit nahezu gleicher Zugriffszeit pro Zelle (typische Werte liegen bei einigen Nanosekunden). Er wird demnach auch als Random Access Memory (RAM) bezeichnet. Einen Hintergrundspeicher (Secondary Memory) verwendet man zur Speicherung relativ selten benötigter Informationen. Er ist langsamer bezüglich der Zugriffszeit. Da er nicht die teuren und schnellen Speicherbausteine verwendet, ist er wesentlich billiger und daher auch in größerem Umfang verfügbar. Als Beispiel ist Festplattenspeicher zu nennen. Der Pufferspeicher (Cache Memory) zeichnet sich dadurch aus, dass er besonders schnell, dafür aber auch teuer ist. Aus diesem Grund ist er relativ klein. Seine besondere Eignung liegt in der Pufferung (vom Prozessor) besonders häufig benutzter Befehle und Daten. Neben den Speichertypen gibt es die Register. Es handelt sich dabei um Zellen mit besonders kurzen Zugriffszeiten. Den Registern werden bestimmte Sonderaufgaben zugeordnet, wie sie im Folgenden beispielhaft aufgeführt sind. Dabei unterscheiden wir der Einfachheit halber nicht zwischen Registername und Registerinhalt. 4 Kapitel 1. Grundlegende Konzepte Das Akkumulatorregister (der Akkumulator, Bezeichnung durch α) dient der kurzfristigen Speicherung von Informationen. Es wird für den Transfer vom und zum Speicher benutzt. Verwendung findet es zum Beispiel bei der Ausführung arithmetischer Operationen. Eine typische Befehlsfolge unter Verwendung des Akkumulators ist die folgende: 1 2 3 4 α = ρ(0) α = α + ρ(1) α = α + ρ(2) ρ(0) = α Dieses Programm sorgt für eine Addition der Inhalte der ersten drei Rechenspeicherzellen. Die Summe wird in die erste Speicherzelle geschrieben. Das Indexregister (bezeichnet mit γ) wird bei der indizierten Adressierung verwendet, die in Kapitel 3 eingeführt wird. Nicht zu verwechseln sind das Befehlsregister (Instruction Register, IR) und das Befehlszählregister (Program Counter, PC) auch mit β bezeichnet). Das Befehlsregister enthält den Befehl, der momentan ausgeführt wird. Dahingegen steht im Befehlszählregister die Adresse des zur Zeit ausgeführten Befehls. Ist die Ausführung des einen Befehls abgeschlossen und soll der nächste Befehl ausgeführt werden, so wird der Inhalt des Befehlszählregisters inkrementiert (β = β + 1). Das Speicheradressregister (Memory Address Register, MAR) enthält die Adresse einer Speicherzelle, während das Speicherdatenregister (Memory Data Register, MDR) das Datum dieser durch das Speicheradressregister angesprochenen Speicherzelle enthält. Darüber hinaus gibt es noch eine architekturabhängige Vielzahl von Allzweckregistern, die meist mit R0 , R1 , R2 , . . . bezeichnet werden. Sie werden für unterschiedliche Zwecke eingesetzt, zum Beispiel für die Durchführung von Rechenoperationen. 1.3 Programmausführung Es gibt verschiedene Konzepte zur Ausführung von Befehlen und somit zur Manipulation von Daten. 1.3.1 Von-Neumann-Prinzip Die konventionelle Programmausführung erfolgt nach dem Von-Neumann-Prinzip. Es wird zu einem Zeitpunkt ein Befehl auf ein Datum angewandt. Man nennt den zugehörigen Rechner auch SISD-Rechner (Single Instruction Single Data). Die Ausführung eines Befehls geschieht in vier Schritten: 1. Hole nächsten Befehl in das Befehlsregister (IR). Die Adresse des Befehls steht im Befehlszählregister β; demzufolge enthält β zu Beginn der Programmausführung die Startadresse. 1.3. Programmausführung 5 2. Interpretiere den Befehl. Es wird der Typ des Befehls ermittelt und die Anzahl nötiger Operanden bestimmt. 3. Hole den beziehungsweise die benötigten Operanden. 4. Führe den Befehl aus. Der Hole-Befehl (Load) beschreibt einen Datentransfer zwischen Speicher und Prozessor. Er hat die Form α = ρ(i) und bedeutet »Lade Datum von Speicherzelle i in den Akkumulator«. Entsprechend lautet der Befehl für einen Speichere-Befehl (Store) ρ(k) = α. 1.3.2 Alternative Modelle Neben dem SISD-Prinzip gibt es folgende andere Typen, die hier nur kurz genannt werden. Der SIMD-Rechner (Single Instruction Multiple Data) gestattet die Anwendung eines Befehls zu einem Zeitpunkt auf mehrere Daten. Es gibt Rechnertypen, die hierzu mehrere Prozessoren verwenden, welche zum Beispiel einen gemeinsamen Speicher nutzen (Shared Memory Model). Ein weiteres Modell stellt der MIMDRechner dar (Multiple Instruction Multiple Data), selten wird der MISD-Rechner (Multiple Instruction Single Data) verwendet. 1.3.3 Pipelining Die Befehlsausführung nach dem Von-Neumann-Prinzip in vier Schritten kann beschleunigt werden, wenn man Schritt 1 und 2 des nachfolgenden Befehls mit den Schritten 3 und 4 des aktuell auszuführenden Befehls kombiniert. Abbildung 1.2 zeigt, dass der Prozessor nach der Interpretation des Befehls i (Schritt 2) zusätzlich zu dessen weiterer Bearbeitung (Schritte 3 und 4) den Befehl (i + 1) holt und interpretiert. Zeit 0 1 2 3 4 5 .. . Befehl 1 Hole Befehl 1 Interpretiere Befehl 1 Operanden holen Befehl 1 ausführen Befehl 2 Befehl 3 Hole Befehl 2 Interpretiere Befehl 2 Operanden holen Befehl 2 ausführen Hole Befehl 3 Interpretiere Befehl 3 ... Abbildung 1.2: Pipelining von Befehlen Zu beachten ist allerdings, dass die Ausführung von Befehl i (Schritt 4) unter Umständen erst den Befehl (i+1) festlegen kann. In diesem Fall ist irrtümlich der falsche 6 Kapitel 1. Grundlegende Konzepte Befehl (i + 1) ausgeführt worden, was zu ungewünschten Folgen führen kann. Der nun richtige Befehl (i + 1) muss, sofern noch möglich, ausgeführt werden. 1.3.4 Ausführungssteuerung Neben den Befehlen zum Datentransfer zwischen Prozessor und Speicher werden auch die Ein-/Ausgabegeräte (Input/Output, I/O) über E/A-Befehle vom Prozessor angesteuert. Ihre Tätigkeit können solche Endgeräte dann weitgehend unabhängig vom Prozessor ausführen. Eine wichtige Aufgabe hat der Prozessor bei der Koordination dieser Geräte. Hierzu senden die Endgeräte so genannte Interrupt-Signale, die den Prozessor in der Ausführung eines anderen Programms unterbrechen. Auf diese Weise kann von der normalen Befehlsausführung abgewichen werden (siehe Abbildung 1.3). Beispiele für derartige Unterbrechungen des Prozessors sind eine Alarmmeldung, die eine Reaktion innerhalb einer nicht zu überschreitenden Zeitspanne (Deadline) erfordert, oder eine Fertigmeldung eines langsamen Endgerätes. Der vergleichsweise schnelle Prozessor bedient normalerweise viele langsame Endgeräte gleichzeitig, er muss also zwischen gleichzeitig laufenden Programmen umschalten. Prozessorsteuerprogramm (Betriebssystem) Programm 1 Programm 2 Initiiere E/A-Operation 1 für Programm 1 Schalte um zu Programm 2 E/A 1 meldet „fertig“ durch Interruptsignal Unterbreche Programm 2 und rette seine relevanten Informationen Schalte um zu Programm 1 Abbildung 1.3: Ausführungssteuerung mit Interruptsignalen 1.4. Datenübertragung 7 1.4 Datenübertragung 1.4.1 Leitungen Die Kommunikation besteht im Wesentlichen im Transfer von Daten und Steuerinformationen zwischen den funktionellen Einheiten des Rechners, insbesondere zwischen Prozessor, Speicher und E/A-Geräten. Sie erfolgt über Leitungen, welche die Einheiten verbinden. Physikalisch gesehen sind Leitungen meist drahtgebunden. Aber auch andere Möglichkeiten sind gegeben wie etwa Funkstrecken. Informationstransport auf einer Leitung erfolgt in der Regel sequenziell, d.h. ein Bit wird nach dem anderen auf die Leitung gebracht und ebenso empfangen. Der parallele Transport bedeutet, dass mehrere Bits gleichzeitig über ein Bündel von Leitungen gesendet werden. Ein solches Bündel wird auch als Bus bezeichnet. 1.4.2 Busse Ein Rechnerbus besteht aus einer Menge von Leitungen. Ein Bus ist im Rechner als öffentliches Transportmedium für mehrere Einheiten aufzufassen, die über diesen miteinander verknüpft sind. Der Zugriff auf den Bus, also die Busbenutzung, wird daher auch als Multiple Access (MA) bezeichnet und muss durch Konventionen in Form von Protokollen geregelt werden. Busse lassen sich nach den Einheiten charakterisieren, die sie miteinander verbinden. Ebenso gibt es verschiedene Busarchitekturen, welche die Zuordnung von Bussen an Einheiten beschreiben. Abbildung 1.4 zeigt eine Architektur mit zwei Bustypen: Der Datenbus dient der Kommunikation zwischen Prozessoreinheit und dem Speicher. Ein eigener Rechnerbus sorgt für die Kommunikation des Prozessors mit den E/AGeräten. Speicher Datenbus CPU E/A-Gerät E/A-Gerät Rechnerbus Abbildung 1.4: Zwei-Bus-Architektur Einfacher, aber weniger leistungsfähig als diese Zwei-Bus-Architektur ist die Verwendung eines gemeinsamen Busses – so genannter Unibus – für alle Kommunikationsaufgaben zwischen allen Einheiten (siehe Abbildung 1.5). 8 Kapitel 1. Grundlegende Konzepte Speicher CPU E/A-Gerät E/A-Gerät Bus für Daten, Befehle, Steuerung... Abbildung 1.5: Unibus-Architektur Ein grundlegendes Problem ist die unterschiedliche Geschwindigkeit, mit der die Komponenten arbeiten, die an einen gemeinsamen Bus angeschlossen sind, zum Beispiel: schnelle CPU und langsames E/A-Gerät. Benutzt eine langsame Einheit den Bus, so ist er für eventuell längere Zeit für andere Einheiten blockiert. Dies erklärt auch, warum der Unibus nicht sehr leistungsfähig ist. Erforderlich sind also Hilfsmittel, welche die Geschwindigkeitsunterschiede glätten, um weniger Busse verwenden zu können. Ein Lösungsansatz besteht im Einsatz von Pufferregistern zwischen dem Bus und den angeschlossenen Geräten (siehe Abbildung 1.6). Auf diese Weise kann zum Beispiel eine schnelle CPU viele langsame Endgeräte wechselweise bedienen. Der Bus ist allerdings nur solange nicht blockiert, bis der Puffer voll ist. schnell Pufferregister CPU Puffer E/A schnell langsam CPU Drucker Abbildung 1.6: Pufferung des Buszugangs KAPITEL 2 Informationsdarstellung In diesem Kapitel werden Darstellungsformen und Codierungen für Zahlen und Zeichen eingeführt. Wir betrachten sowohl gebräuchliche Formen der Darstellung von ganzen und Gleitkommazahlen als auch Codierungen von Zeichen, denen keine arithmetische Bedeutung zugeordnet ist. Die Darstellung eines Datums findet meist in einem Speicherwort statt. Es sind auch Halb- oder Doppelwortdarstellungen gängig. Ein Speicherwort ist somit als Informationseinheit des Rechners zu verstehen. Die Wortlänge ist dabei abhängig vom Rechner; es handelt sich in der Regel um Potenzen von 2 (16, 32, 64 Bit). Bei Großrechnern findet man auch exotische Werte wie 50- oder 60-Bit-Darstellungen. 2.1 Allgemeines zur Darstellung ganzer Zahlen Es gilt, eine Darstellung für ganze Zahlen zu finden, so dass sich eine vernünftige Zuordnung zwischen Codierung und dem Wert der Zahl ergibt. An eine Codierung werden im Allgemeinen folgende Anforderungen gestellt: • Zunächst sollten alle positiven und negativen Zahlen eines Intervalls [−x; y] dargestellt werden können. • Dabei sollten in etwa gleich viele positive wie negative Zahlen darstellbar sein. • Die Darstellung sollte – abgesehen von wenigen Ausnahmen – eindeutig sein, das heißt, zwei unterschiedliche Codeworte beschreiben auch zwei unterschiedliche Werte. • Eine wichtige Forderung ist die Durchführbarkeit von Rechenoperationen in der Codierung. • Eine eher technische Forderung liegt darin, dass die Unterscheidung zwischen positiver und negativer Zahl möglichst am Anfang des Codeworts getroffen werden kann. Der Rest wird als Codierung des Stellenwerts aufgefasst. 9 10 Kapitel 2. Informationsdarstellung 2.1.1 Stellenwertcodierung Die Stellenwertcodierung ist ein gebräuchliches Beispiel für eine vernünftige Zuordnung. Das bekannteste Beispiel ist das Dezimalsystem. Man ordnet einem Codewort (an−1 , . . . , ai , . . . , a0 ) einen Wert zu, der sich ergibt als: n−1 W(an−1 , . . . , ai , . . . , a0 ) = ∑ ai · d i i=0 Dabei bezeichnet man d als Basis und ai mit 0 ≤ ai ≤ d − 1 als Stellenwert. Gebräuchliche Basen sind d = 2, 8, 10, 16. Das Zahlensystem wird entsprechend der durch die Basis gegebenen Codierung als Binär-, Oktal-, Dezimal- oder Hexadezimal-System bezeichnet. In der folgenden Tabelle sind die vier gebräuchlichsten Zahlensysteme mit ihren Basen und Alphabeten aufgelistet. Zahlensystem Binär Oktal Dezimal Hexadezimal Basis d 2 8 10 16 Zahlenalphabet {0, 1} {0, 1, 2, 3, 4, 5, 6, 7} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B,C, D, E, F} Jede Hexadezimalziffer wird in binärer Darstellung durch vier Bits dargestellt. Im Folgenden betrachten wir jedoch das Binärsystem. Wir verwenden nun die Stellenwertfunktion W, die eine Unterscheidung zwischen negativer und positiver Zahl aufgrund des ersten Bits an−1 zulässt: W : {0, 1}n → Z W(an−1 , . . . , ai , . . . , a0 ) = (an−1 , Sn−2,0 ) mit j S j,i = ∑ ak · 2k (2.1) k=i Man bezeichnet an−1 als Vorzeichenbit und legt fest: ( +Sn−2,0 W(an−1 , an−2 , . . . , ai , . . . , a0 ) = −Sn−2,0 falls an−1 = 0 falls an−1 = 1 Diese Codierung erlaubt eine Darstellung von maximal 2n verschiedenen Zahlen. Da durch das erste Bit aber eine Unterscheidung zwischen positiven und negativen Zahlen getroffen wird und die 0 sowohl als positive als auch negative Zahl interpretiert wird, ist die betragsgrößte Zahl 2n−1 − 1. 2.2. Codierungen für ganze Zahlen 2.2 Codierungen für ganze Zahlen In diesem Abschnitt werden drei Codierungen für ganze Zahlen eingeführt. Die Darstellung positiver ganzer Zahlen ist in allen drei Codierungen gleich. Sie unterscheiden sich in der Codierung negativer Zahlen und der 0, der damit zusammenhängenden Symmetrie und der Größe des Zahlenbereiches. Ferner sind die Rechenoperationen auf diesen Codierungen unterschiedlich zu handhaben. Als Rechenoperationen werden zunächst nur die Addition und Subtraktion vorgestellt. In Abschnitt 2.3 über rationale Zahlen werden auch die Multiplikation und Division eingeführt. Bei der Beschreibung der einzelnen Codierungen wird von einer n-stelligen Binärzahl der Form (bn−1 , . . . , b0 ) ausgegangen und die Summe S j,i aus 2.1 verwendet. 2.2.1 Betrag- und Vorzeichen (B+V-Codierung) Positive und negative Zahlen Eine positive Zahl liegt dann vor, wenn bn−1 = 0 ist. Dann ergibt sich der Wert der Binärzahl als: WBV (bn−1 , . . . , b0 ) = +Sn−2,0 (2.2) Bei negativen Zahlen ist das Vorzeichenbit auf 1 gesetzt bn−1 = 1. Der Wert ist dann: WBV (bn−1 , . . . , b0 ) = −Sn−2,0 Eine negative betragsgleiche Zahl erhält man aus einer positiven, indem man das Vorzeichenbit kippt, d.h. von 0 auf 1 setzt. Beispiel: Zahlen in der B+V-Darstellung Sei n = 6, es ist WBV (001101) = +13 und WBV (101110) = −14. Sei (b2 , b1 , b0 ) = (011) gegeben mit WBV (011) = +3, dann ergibt sich für (111) der Wert WBV (111) = −3. Zahlenbereich und Zahlenverlängerung Der Zahlenbereich in der B+V-Codierung ist symmetrisch und reicht bei einer nstelligen Binärzahl von −(2n−1 − 1) bis +(2n−1 − 1). Deshalb hat die 0 zwei Darstellungen, nämlich (bn−1 , . . . , b0 ) = (0, . . . , 0) und (bn−1 , . . . , b0 ) = (1, 0, . . . , 0). Will man eine n-stellige Binärzahl auf m Stellen (m > n) erweitern, so füllt man die Zahl nach dem Vorzeichenbit an der Stelle (n − 2) mit Nullen auf. Beispiel: Zahlenverlängerung Sei n = 5 mit a = (11010) gegeben. Die auf m = 7 Stellen erweiterte Zahl lautet a′ = (1001010). 11 12 Kapitel 2. Informationsdarstellung 2.2.2 2-Komplement Der Wert W2K (bn−1 , . . . , b0 ) einer ganzen Zahl in der 2-Komplement-Codierung ergibt sich nach folgender Formel W2K (bn−1 , . . . , b0 ) = Sn−2,0 − bn−1 · 2n−1 (2.3) Im Falle, dass bn−1 = 1 ist, wird also durch Subtraktion eine negative Zahl errechnet. Positive und negative Zahlen Positive ganze Zahlen haben die gleiche Binärdarstellung im 2-Komplement wie in der B+V-Codierung. Eine negative Zahl ergibt sich aus der Formel 2.3, wenn bn−1 = 1 ist. Andererseits erhält man den Wert einer negativen Binärzahl in 2-Komplement-Codierung wie folgt. Gegeben sei b = (bn−1 , . . . , b0 ) mit W2K (bn−1 , . . . , b0 ) < 0. 1. Man kippe alle Bits von b, es ergibt sich bneu . 2. Eine 1 addieren: bneu = bneu + 1n mit 1n = (0, . . . 0, 1). 3. Zugehörigen Wert in B+V-Codierung bestimmen: U = WBV (bneu ). 4. Der Dezimalwert von b ist dann −U: W2K (b) = −U. Diese Art der Berechnung versagt nur für (bn−1 , . . . , b0 ) = (1, 0, . . . , 0). Nach der zweiten Methode ergibt sich der Wert −0. Der Dezimalwert nach Formel 2.3 ist jedoch W2K (1, 0, . . . 0) = −2n−1 . Beispiel: Zahlen in der 2-Komplement-Darstellung Sei n = 7 und b = (b6 , . . . , b0 ) = (1010110). Nach Formel 2.3 ergibt sich: W2K (b6 , . . . , b0 ) = 1 · 24 + 1 · 22 + 1 · 21 − (1 · 26 ) = 22 − 64 = −42 Nach der zweiten Methode ergibt sich: 1. Kippen: bneu = 0101001 2. 1 addieren: bneu = 0101010 3. U = WBV (bneu ) = 1 · 25 + 1 · 23 + 1 · 21 = 42 4. Dezimalwert ist W2K (b) = −U = −42 Zahlenbereich und Zahlenverlängerung Der Zahlenbereich im 2-Komplement ist asymmetrisch. Er reicht bei einer n-stelligen Binärzahl von −(2n−1 ) bis +(2n−1 − 1). Die 0 hat also nur eine Darstellung, nämlich (bn−1 , . . . , b0 ) = (0, . . . , 0) 2.2. Codierungen für ganze Zahlen 13 Will man eine n-stellige Binärzahl auf m Stellen (m > n) erweitern, so füllt man die Zahl vorne an der Stelle n mit dem Vorzeichenbit auf. Beispiel: Zahlenverlängerung beim 2-Komplement Sei n = 5 mit a = (11010) gegeben. Die auf m = 7 Stellen erweiterte Zahl lautet a′ = (1111010). 2.2.3 1-Komplement Der Wert W1K (bn−1 , . . . , b0 ) einer ganzen Zahl in der 1-Komplement-Codierung ergibt sich nach folgender Formel: W1K (bn−1 , . . . , b0 ) = Sn−2,0 − bn−1 · (2n−1 − 1) (2.4) Im Falle, dass bn−1 = 1 ist, wird durch Subtraktion eine negative Zahl errechnet. Positive und negative Zahlen Positive ganze Zahlen haben die gleiche Binärdarstellung im 1-Komplement wie in der B+V-Codierung. Eine negative Zahl ergibt sich aus der Formel 2.4, wenn bn−1 = 1 ist. Andererseits erhält man den Wert einer negativen Binärzahl in 1-Komplement-Codierung wie folgt. Gegeben sei b = (bn−1 , . . . , b0 ) mit W1K (bn−1 , . . . , b0 ) < 0. 1. Man kippe alle Bits von b, es ergibt sich bneu . 2. Zugehörigen Wert in B+V-Codierung bestimmen: U = WBV (bneu ). 3. Der Dezimalwert von b ist dann −U : W1K (b) = −U. Beispiel: Darstellung negativer Zahlen in der 1-Komplement-Darstellung Sei n = 7 und b = (b6 , . . . , b0 ) = (1001010). Nach der Formel 2.4 ergibt sich: W1K (b6 , . . . , b0 ) = (1 · 23 + 1 · 21 ) − 1 · (26 − 1) = 10 − 63 = −53 Nach der zweiten Methode ergibt sich: 1. Kippen: bneu = 0110101 2. U = WBV (bneu ) = 1 · 25 + 1 · 24 + 1 · 22 + 1 · 20 = 53 3. Dezimalwert ist W1K (b) = −U = −53 Zahlenbereich und Zahlenverlängerung Die Asymmetrie der 2-Komplement-Codierung ist behoben. Der symmetrische Zahlenbereich reicht nun von −(2n−1 − 1) bis +(2n−1 − 1). 14 Kapitel 2. Informationsdarstellung Die Zahlenverlängerung im 1-Komplement funktioniert genauso wie im 2-Komplement: Vorne das Vorzeichenbit beliebig oft anfügen. 2.2.4 Transformation von negativen Zahlen Wir betrachten in diesem Abschnitt die Transformation von negativen Zahlen von einer Codierung zur anderen. Dabei sei b = (bn−1 , . . . , b0 ) eine Binärzahl in der B+VCodierung. Wenn das Vorzeichenbit bn−1 = 1 ist, dann haben wir eine negative Zahl. Transformation zum 1-Komplement Man kippt alle Bits von b bis auf das Vorzeichenbit. Formal führt man ein ExklusivesOder (Operationszeichen ⊕) zwischen bn−1 und bi für alle Stellen i ∈ {0, . . . , (n−2)} durch. Die Zahl c = (cn−1 , . . . , c0 ) im 1-Komplement ergibt sich durch cn−1 = bn−1 und ci = bn−1 ⊕ bi für alle i ∈ {0, . . . , (n − 2)} Diese Transformation funktioniert auch in umgekehrter Richtung, also vom 1-Komplement zur B+V-Codierung. Transformation zum 2-Komplement Man verfährt zunächst wie bei der Transformation zum 1-Komplement. Nach der Berechnung von c = (cn−1 , . . . , c0 ) wird in einem weiteren Schritt g = c + 1n mit 1n = (0, . . . 0, 1) berechnet. g ist die Darstellung in der 2-Komplement-Codierung. Auch diese Transformation funktioniert in umgekehrter Richtung, also vom 2-Komplement zur B+V-Codierung. Beispiel: Transformation von Zahlen Sei b = (10110) eine negative Zahl mit WBV (10110) = −6. Transformation zum 1-Komplement und zurück. b = 10110 B+V-Codierung Alle Bits bis auf das Vorzeichenbit kippen. c = 11001 1-Komplement-Codierung Alle Bits bis auf das Vorzeichenbit kippen. b = 10110 B+V-Codierung Transformation zum 2-Komplement und zurück. 2.2. Codierungen für ganze Zahlen b= 10110 g= 11001 00001 11010 b= 10101 00001 10110 2.2.5 15 B+V-Codierung Alle Bits bis auf das Vorzeichenbit kippen. 1n drauf addieren 2-Komplement-Codierung Alle Bits bis auf das Vorzeichenbit kippen. 1n drauf addieren B+V-Codierung Addition und Subtraktion Zwei Binärzahlen a = (an−1 , . . . , a0 ) und b = (bn−1 , . . . , b0 ) werden addiert, indem man sie nach Schulmethode komponentenweise addiert. sn an−1 an−2 . . . a2 a1 a0 bn−1 bn−2 . . . b2 b1 b0 sn−1 sn−2 . . . s2 s1 s0 Im Falle, dass die Addition an der (n − 1)-ten Stellen einen Übertrag sn liefert, muss dieser besonders behandelt werden. Die einzelnen Codierungen unterscheiden sich bei der Behandlung des Übertrages. B+V-Codierung Die B+V-Codierung ganzer Zahlen ist ungünstig bezüglich der Addition. Eine Möglichkeit, diese durchzuführen, besteht darin, die Summanden a und b in die Darstellung im 1-Komplement zu transformieren und dort die Addition durchzuführen. Die Summe s liegt dann im 1-Komplement vor und wird auf gleiche Weise zurück in die B+V-Codierung überführt. 2-Komplement Die Addition wird beim 2-Komplement nach Schulmethode durchgeführt, falls ein Übertrag an der (n − 1)-ten Stelle entsteht, wird dieser Gesamtübertrag ignoriert. Beispiel: Addition im 2-Komplement Seien die Summanden a = (1101101) und b = (0011001) gegeben. Die Summe s ergibt sich dann wie folgt: 1101101 0011001 1|0000110 W2K (a) = −19 W2K (b) = +25 W2K (0000110) = +6 Der Übertrag wird ignoriert! 16 Kapitel 2. Informationsdarstellung Die Subtraktion kann auf die Addition zurückgeführt werden, indem man a − b als a + (−b) auffasst. Also muss b in eine negative Zahl umgeformt werden. Anschließend verfährt man man wie bei Addition beschrieben. 1-Komplement Die Addition wird auch hier nach Schulmethode durchgeführt, falls ein Übertrag an der (n − 1)-ten Stelle entsteht, wird ein so genannter End-Around-Carry durchgeführt. Die herausfallende Stelle sn = 1 wird gestrichen, dann allerdings auf die bisherige Summe s addiert s = s + 1n mit 1n = (0, . . . , 0, 1). Wenn im 1-Komplement ein Gesamtübertrag sn = 1 entsteht, dann gilt, dass dieser sowohl den Wert sn · 2n als auch den Wert sn · 20 = sn · 1 hat, weil man modulo (2n − 1) rechnet, denn sn · 2n = sn · (2n − 1) + sn = sn mod (2n − 1) = sn · 20 mod (2n − 1) Beispiel: Addition im 1-Komplement Seien die Summanden a = (1101101) und b = (0011001) gegeben. Die Summe s ergibt sich dann wie folgt: 1101101 0011001 1|0000110 0000001 0000111 W1K (a) = −18 W1K (b) = +25 W1K (0000110) = +6 mit s = (0000110) Da Übertrag 1n drauf addieren W1K (0000111) = 7. Die Subtraktion kann auch hier wieder auf die Addition einer negativen Zahl zurückgeführt werden. 2.2.6 Überlaufproblematik Wie die vorangehenden Abschnitte und das folgende Beispiel zeigen, kann es bei der Addition zu einem Überlauf kommen. Beispiel: Überlaufproblematik Die Summe von a = (01010) und b = (01100) ist s = (10110). Im 2-Komplement ist der Dezimalwert W2K (s) = −10. Das korrekte Ergebnis lautet aber: W2K (a) + W2K (b) = +22. Zwischen beiden Ergebnissen liegt eine Differenz von 32 = 2n = 25 . Dies lässt sich damit begründen, dass im 2-Komplement mod 2n gerechnet wird. Das Ergebnis W2K (s) = −10 ist also bis auf Vielfache von 2n richtig. Die Zahlenverlängerung stellt eine mögliche Abhilfe gegen Überlauf. Bei der 2Komplement- und 1-Komplement-Darstellung verdoppelt man das Vorzeichenbit. Jeder Summand beginnt dann mit zwei gleichen Bits. Sind die ersten beiden Bits der Summe verschieden, ist das Ergebnis sicherheitshalber zu prüfen. 2.3. Darstellung rationaler Zahlen 17 Beispiel: Überlaufabhilfe Verlängere a = (01010) zu a′ = (001010) und b = (01100) zu b′ = (001100). Die Summe ist dann s = (010110). Eine Überprüfung ergibt, dass das Ergebnis korrekt ist. Der Nachteil dieser Vorgehensweise liegt in einer höheren Redundanz; man verschenkt ein Bit und kann somit nur halb so viele Zahlen darstellen, wie die Bitwortlänge erlaubt. Vorteilhaft ist die Einfachheit dieser Methode. Ein Kriterium für die Unmöglichkeit eines Überlaufs ist ein Vergleich der Vorzeichen beider Summanden: Ein Überlauf ist unmöglich, wenn die Vorzeichen unterschiedlich sind, oder formal: Sind a = (an−1 , . . . , a0 ) und b = (bn−1 , . . . , b0 ) die zwei Summanden, dann ist ein Überlauf genau dann unmöglich, wenn an−1 6= bn−1 . Anschaulich lässt sich dies dadurch begründen, dass die Summe einer negativen und einer positiven Zahl nicht den Zahlenbereich überschreiten wird. 2.3 Darstellung rationaler Zahlen Die obigen Abschnitte haben die Darstellung von ganzen Zahlen eingeführt. Die zugehörigen Rechenoperationen sind die Addition und Subtraktion, wobei letztere sich auf die Addition zurückführen lässt. Will man nun Zahlen dividieren, so reichen ganze Zahlen nicht aus. In diesem Abschnitt werden Darstellungsformen rationaler Zahlen eingeführt. 2.3.1 Festkommazahlen Es gibt zwei Arten der Festkommadarstellung. (1) Zu einer Binärzahl b = (bn−1 , . . . , b0 , b−1 , . . . , b−m ) stellt man sich ein Komma zwischen b0 und b−1 vor. Der Wert ergibt sich nach folgender Formel n−1 WFK (bn−1 , . . . , b0 , b−1 , . . . , b−m ) = ∑ i=−m bi · d i (2.5) Ganze Zahlen haben in dieser Darstellung das gedachte Komma ganz rechts, das heißt, nach der Stelle b−m . (2) Eine andere Möglichkeit besteht darin, den Zahlenbereich auf ein offenes Intervall (−1, +1) einzuschränken. Die Binärzahl hat die Form b = (b0 , b1 , . . . , bn ) wobei b0 das Vorzeichenbit ist und der Wert von b sich nach folgender Formel berechnet ( + ∑ni=1 21i falls b0 = 0 (2.6) WFK (b) = − ∑ni=1 21i falls b0 = 1 18 Kapitel 2. Informationsdarstellung Die Werte −1 und 1 sind auf diese Weise nicht darstellbar, denn die größte darstellbare Zahl ist 1 − 21n . Beispiel: Festkommazahlen bei einem offenen Intervall Sei n = 4 und b = (b0 , b1 , b2 , b3 , b4 ). Die kleinste darstellbare Zahl ist WFK (00001) = 1 15 16 und die größte darstellbare Zahl ist WFK (01111) = 16 . 2.3.2 Gleitkommazahlen Abbildung 2.1 zeigt die Darstellung einer Gleitkommazahl. Der Darstellung einer rationalen Zahl durch Mantisse a und Exponent e liegt der folgende Satz für reelle Zahlen zugrunde. Zahlenwert Mantisse Exponent a gedachtes Komma (Mantisse) e gedachtes Komma (Exponent) Abbildung 2.1: Kodierung einer Gleitkommazahl Satz: Darstellung von Gleitkommazahlen 1. Jede Zahl z ist darstellbar in der Form z = a · de mit der Mantisse a, dem Exponenten e und der Basis der Zahlendarstellung d. 2. Eine Zahl z 6= 0 ist eindeutig darstellbar als z = a · de wobei d1 ≤ |a| < 1. Diese Darstellung heißt dann normalisierte Darstellung mit Mantisse a und Exponent e. 2.3. Darstellung rationaler Zahlen Beispiel: Gleitkommazahlen Es soll die Dezimalzahl 5 in die normalisierte Darstellung zur Basis 2 gebracht werden 5 = = = = = 5 · 20 2, 5 · 21 1, 25 · 22 0, 625 · 23 0, 3125 · 24 (1.01000 | 0010) (0.10100 | 0011) normalisierte Darstellung (0.01010 | 0100) . . . Somit hat die Dezimalzahl 5 die normalisierte Darstellung: 5 = (2−1 + 2−3 ) · 23 mit Mantisse a = (0.10100) und Exponent e = (0011) zur Basis Basis 2, denn 1 2 ≤| 0, 625 |< 1. Dieses Beispiel legt die Basis 2 zugrunde. Es sind aber auch andere Basen möglich, wie zum Beispiel d = 16 (hexadezimal). Für d = 2 erhält man folgende größte, kleinste und betragskleinste Zahl, wenn man den Exponenten im 2-Komplement fünfstellig und die Mantisse siebzehnstellig darstellt: Exponent: größter 01111 kleinster 10000 W2K (01111) = +15 W2K (10000) = −16 Seien für α · 2x , α ∈ [−1; +1) und x ∈ [−16; +15]. Binäre Darstellung: größte Zahl kleinste Zahl betragskleinste Zahl (1 − 2116 ) · 215 −1 · 215 1 · 2−16 Die Zahlen liegen mit zunehmender Entfernung von Null weiter auseinander, wohingegen sie um die Null extrem dicht liegen, vorausgesetzt, der Zahlenbereich ist symmetrisch gehalten. Wegen der Binärinterpretation des Exponenten lässt sich dieser dichte Dezimalzahlenbereich verschieben. Denkt man sich zum Beispiel den Exponenten um jeweils 8 größer, also x ∈ [−8; +23], so ist der dichte Bereich ins Positive verschoben. Die Mantissenlänge erweitert nicht den Zahlenbereich, sondern sie erhöht die Genauigkeit der Darstellung. Trotzdem sind irrationale Zahlen immer nur ungenau darstellbar. Das IEEE-754-Format Das IEEE-754 legt ein Format für die Darstellung von Gleitkommazahlen fest, das häufig benutzt wird. Es gibt eine Version mit 32 Bit und eine mit 64 Bit. Im Folgenden wird die Version mit 32 Bit beschrieben. 19 20 Kapitel 2. Informationsdarstellung 1 2 3 0 1234567890123456789012345678901 VZ Exponent e Mantisse a Das erste Bit ist ein Vorzeichenbit, wobei 0 eine positive und 1 eine negative Zahl bedeutet. Die folgenden acht Bits bilden den Exponenten e. Die Basis des Exponenten ist 2. Der Exponent wird in der Excess-127-Darstellung gespeichert. In dieser Darstellung muss vom Exponenten 127 subtrahiert werden, um den eigentlichen Wert des Exponenten zu erhalten. Die letzten 23 Bits bilden die Mantisse a. Die Mantisse ist auf die Darstellung 1, . . . normalisiert, wobei die 1 vor dem Komma steht. Die 23 Bits nehmen die 1 nicht auf, sondern nur die Zahlen nach dem Komma! Definition: Gleitkommazahl im IEEE-754-Format Eine Gleitkommazahl im IEEE-754-Format repräsentiert die Zahl n mit n = V Z 1, a · 2e−127 Beispiel: Gleitkommazahl im IEEE-754-Format Die Zahl n = −0, 75 wird dargestellt als 1 01111110 |{z} | {z } 10000000000000000000000 | {z } − 126−127=−1 2.3.3 (1,5)10 Addition und Subtraktion bei Gleitkommazahlen Bei der Addition zweier Zahlen z1 = a1 · d e1 und z2 = a2 · d e2 ist zu beachten, dass die Exponenten gleich sind. Man geht wie folgt vor: 1. Exponenten anpassen (den kleineren an den größeren) 2. Operation (gemäß der vorangehenden Abschnitte) ausführen 3. Postnormalisieren (Summendarstellung normalisieren) Auf diese Weise kann es zu einem Genauigkeitsverlust und einer Teilauslöschung bei begrenzter Stellenanzahl kommen. Beispiel: Addition und Subtraktion bei normalisierten Gleitkommazahlen 1) Seien z1 = (0, 1101) · 24 und z2 = (0, 1011) · 22 im 2-Komplement gegeben. (a) Exponentenanpassung z2 = (0, 0010)11 · 24 wobei die herausfallenden Einsen auf die letzte Stelle gerundet werden z2 = (0, 0011) (b) Addition s = (0, 1101) · 24 + (0, 0011) · 24 = (1, 0000) · 24 2.3. Darstellung rationaler Zahlen 21 (c) Postnormalisierung s = (0, 1000) · 25 , da |W2K (1, 0000)| = 1 Der sich ergebende Dezimalwert ist ungenau WGK (0, 1000) · 25 = 0, 5 · 25 = 16 Der korrekte Wert ist WGK ((0, 1101) · 24 ) + WGK ((0, 1011) · 22 ) = 1 1 1 1 1 1 + + · 24 + + + · 22 = 13 + 2, 75 2 4 16 2 8 16 = 15,75 2) Die Zahl z2 = 0, 9986 · 1039 soll von z1 = 0, 1002 · 1040 subtrahiert werden. Exakt ergibt sich (1, 002 − 0, 9986) · 1039 = 0, 0034 · 1039 = 0,34 · 1037 . (a) Anpassung von z2 an den größeren Exponenten z2 wird zu 0, 09986 · 1040 , aufgrund der begrenzten Stellenanzahl muss die Zahl gerundet werden z2 = 0, 0999 · 1040 (b) Subtraktion 0, 1002 · 1040 − 0, 0999 · 1040 = 0, 0003 · 1040 (c) Postnormalisierung 0,3000 · 1037 Im Vergleich zum exakten Wert liegt ein großer Fehler vor. Im Extremfall wird die Mantisse durch Rundungsfehler zu Null und ist nicht mehr postnormalisierbar. Gleichzeitig kann der Exponent groß bleiben. In diesem Fall spricht man von einer so genannten Dirty Zero, da die Zahl dann Null ist, obwohl sich der echte Wert von Null unterscheidet. 2.3.4 Multiplikation und Division bei Gleitkommazahlen Für die Multiplikation beziehungsweise Division eignet sich die Darstellung mit Mantisse und Exponent besser. Hier muss keine Exponentenanpassung stattfinden. Man geht wie folgt vor: 1. Mantissen multiplizieren beziehungsweise dividieren 2. Exponenten addieren beziehungsweise subtrahieren 3. Postnormalisierung wie oben. Hierzu reicht maximal ein Linksshift bei der Multiplikation beziehungsweise ein Rechtsshift bei der Division aus. (a1 · d e1 ) · (a2 · d e2 ) = (a1 · a2 ) · d e1 +e2 a1 · d e1 a2 · d e2 = a1 e1 −e2 ·d a2 1 ≤| a1 · a2 |< 1 d2 a1 mit | |< d a2 mit 22 Kapitel 2. Informationsdarstellung Beispiel: Division von Gleitkommazahlen −0, 9 · 105 = −1, 2 · 10−3 = −0, 12 · 10−2 0, 75 · 108 Der letzte Schritt entsteht durch Postnormalisierung, indem die Mantisse nach rechts geshiftet und der Exponent korrigiert wird. 2.4 Darstellung von Zeichen In diesem Abschnitt werden Darstellungsformen von Zeichen eingeführt. Es werden hier keine Forderungen an die Interpretierbarkeit für Rechenoperationen dieser Zeichen gestellt. Zeichen sind zum Beispiel Buchstaben, Ziffern, Sondersymbole und Steuerzeichen. Zeichen werden als Folge von Bits (Bitstring) codiert. Ein Wort (Speicherelement) enthält meist mehrere Zeichen in codierter Form. Es gibt zwei grundsätzlich unterschiedliche Codierungsarten, die in den folgenden Abschnitten vorgestellt werden. Die zugrundeliegende Frage ist hierbei: Wie viele Bits benötigt man, um ein Zeichen eindeutig darzustellen? 2.4.1 Codierung mit Umschaltsymbolen Bei dieser Codierung gibt es keine eindeutige Zuordnung zwischen Zeichen und dem codierenden Bitstring. Man verwendet kurze Bitstrings und kann hiermit wenige Zeichen codieren. Um weitere Zeichen darstellen zu können, fasst man diese als eine weitere Gruppe von Zeichen auf. Ferner verwendet man Umschaltsymbole, die anzeigen, welcher Gruppe die Zeichen angehören, die die nächsten Bits des Bitstrings codieren. Darzustellen seien die 26 Buchstaben des Alphabets (in Form von Großbuchstaben) und die zehn Ziffern sowie weitere Sonderzeichen. Will man jedes Zeichen durch fünf Bits codieren, so kann man 25 = 32 verschiedene Zeichen codieren. Dies reicht offenbar nicht aus, um der Anforderung gerecht zu werden. Nach der obigen Methode führe man zwei Umschaltsymbole (11111) und (11010) ein, die anzeigen, dass von jetzt ab nur Buchstaben beziehungsweise nur Ziffern oder Sonderzeichen im Bitstring folgen. Trifft man also auf ein Umschaltsymbol, so werden die im Bitstring folgenden 5-Bit-Folgen entsprechend dem vorangehenden Umschaltsymbol interpretiert. Diese Codierung erlaubt die Darstellung von 30 Buchstaben sowie 30 Ziffern und Sonderzeichen, wenn man die beiden Umschaltsymbole berücksichtigt. Eine Erweiterung wäre die Unterscheidung zwischen Groß- und Kleinschreibung. Hierzu müssten zwei weitere Umschaltsymbole eingeführt werden: Umschalten Großnach Kleinschreibung und Klein- nach Großschreibung bei Buchstaben. Günstiger ist in diesem Fall die Einführung von insgesamt drei Umschaltsymbolen für Großbuch- 2.4. Darstellung von Zeichen 23 staben, Kleinbuchstaben und Sonderzeichen bzw. Ziffern. Zu beachten ist bei dieser Codierung, dass ohne Kenntnis des im Bitstring zuletzt vorgekommenen Umschaltsymbols einer Bitfolge kein eindeutiges Zeichen zugeordnet werden kann. Dem Vorteil der Darstellbarkeit vieler Zeichen als kurze Bitfolgen steht die Verlängerung des Bitstrings durch die eingestreuten Umschaltsymbole gegenüber. Zudem verkleinern viele Umschaltsymbole die Gruppen der tatsächlich zu codierenden Zeichen. Eine Aufgabe der Informationstheorie ist die Darstellung einer gegebenen Informationsmenge mit möglichst wenigen Bits. Eine Idee liegt darin, häufig vorkommende Zeichen oder Zeichenkombinationen wie zum Beispiel e oder en mit kurzen Bitfolgen zu codieren, für seltene Zeichen wie q verwendet man längere Folgen. Die Huffman-Codierung basiert auf der relativen Häufigkeit des Gebrauchs von Zeichen. Die Häufigkeit deutscher Buchstaben ergibt abnehmend das folgende Kunstwort: ENRISTUDA 2.4.2 Codierung durch Bitstrings Bei dieser Codierung ordnet man jeder n-stelligen Bitfolge genau ein Zeichen zu. Somit sind 2n verschiedene Zeichen codierbar. Beispiel: Codierung durch Bitstrings Die Darstellung der 26 Großbuchstaben, 26 Kleinbuchstaben und 10 Ziffern ist mit sechs Bits möglich. Standardzeichensätze verwenden sieben beziehungsweise acht Bits (ein Byte) pro Zeichen. Zu nennen ist der ASCII-Code (American Standard Code for Information Interchange), welcher sieben Bits verwendet. Der EBCDIC-Code (Extended Binary Coded Decimal Interchange Code) verwendet acht Bits. In Tabellen lassen sich diese Codierungen nachschlagen. Die Darstellung von ASCII-Zeichen auf Bytebasis (Erweiterung auf 8 Bits) ermöglicht die Einrichtung einer Übertragungskontrolle. In der 8-stelligen Bitfolge (b7 , . . . , b0 ) codieren die Bits b6 , . . . , b0 das Zeichen, während das so genannte Parity-Bit b7 die Anzahl der Einsen auf eine gerade Zahl (b0 + . . . + b7 ) mod 2 = 0 ergänzt. Für die Folge (b6 , . . . , b0 ) = (001101) ist b7 = 1. Ein Bitfehler ist definiert als Kippen eines Bits. Ein solcher Fehler ist dadurch erkennbar, dass die Summe der bi mod 2 gleich eins ist. Somit sind Doppelfehler, das heißt Kippen von zwei Bits einer Bitfolge, nicht erkennbar. Erkennbar sind: 1-Bit-, 3-Bit-,. . . (2k + 1)-Bit-Übertragungsfehler. 24 Kapitel 2. Informationsdarstellung 2.4.3 Darstellung von Dezimalziffern Zur Darstellung der zehn Ziffern 0, 1, . . . , 9 benötigt man mindestens vier Bits, da mit drei Bits nur 23 = 8 Zeichen codierbar sind. BCD-Code Die Ziffern werden wie folgt im BCD-Code (Binary Coded Decimal) dargestellt: 0 durch 0000, 1 durch 0001, . . . und 9 durch 1001, wobei sechs Redundanzen 1010, . . ., 1111 auftreten. In ASCII und EBCDIC sind die Ziffern so dargestellt, dass sie bezüglich der Bits b3 b2 b1 b0 gerade den BCD-Code haben. Bei aufeinanderfolgenden Dezimalziffern ist eine Unterdrückung von b7 b6 b5 b4 möglich, womit eine dichtere Packung erreicht werden kann. Überprüfen Sie das ;-) 3-Excess-Code Der redundante Bereich wird hier auf die Bitfolgen (0000), (0001) und (0010) sowie auf (1101), (1110) und (1111) verteilt. Eine Dezimalzahl i wird hier also im BCDCode als Dezimalzahl (3 + i) dargestellt. 0000 0001 0010 0011 codiert die 0 0100 codiert die 1 0101 codiert die 2 .. .. . . 1100 codiert die 9 1101 1110 1111 redundant redundant Ein Vorzug dieser Darstellung ist die Möglichkeit, eine Ziffer a in die komplementäre Ziffer b durch einfaches Kippen der Bits zu überführen, wobei a komplementär zu b ist, wenn a + b = 9. Gray-Code Der Gray-Code ist formal folgendermaßen definiert: Eine Abbildung aller Dezimalzahlen i mit 0 ≤ i ≤ 2n − 1 für n ≥ 1 durch Bitfolgen W n (i) der Länge n sei durch Induktion über n wie folgt erklärt: 1. W 1 (0) = 0 und W 1 (1) = 1 2.5. Erkennung und Behebung von Bitfehlern ( 0W n (i) 2. W n+1 (i) = 1W n (2n+1 − 1 − i) 25 falls 0 ≤ i ≤ 2n − 1 falls 2n ≤ i ≤ 2n+1 − 1 Anschaulich konstruiert man eine Folge von Gray-Code-Ziffern, indem man einen Block von untereinander stehenden Ziffern nach unten spiegelt und vor die Binärzahlen des oberhalb der Spiegelachse befindlichen Blocks 0 und unterhalb der Spiegelachse 1 schreibt: Beispiel: Gray-Code Für n = 2 Bits lange Worte W 2 ergibt sich: 0 0 1 1 0 1 1 0 0 1 2 3 00 01 11 10 10 11 01 00 0 1 2 3 4 5 6 7 Für n = 3 Bits lange Worte W 3 erhält man: 0 0 0 0 1 1 1 1 Das Charakteristikum dieser Codierung besteht darin, dass sich beim Übergang von der Dezimalzahl i nach (i + 1) die zugehörigen Binärzahlen im Gray-Code an genau einer Stelle unterscheiden. 2.5 Erkennung und Behebung von Bitfehlern Die Behandlung von Bitfehlern spielt insbesondere im Bereich der Datenkommunikation eine große Rolle, da die Übertragung von Bits häufig durch äußere Einflüsse gestört wird. Daher hat man Codes eingeführt, durch die man in der Lage ist, auftretende Bitfehler • zu erkennen • zu erkennen und zu beheben Bei einer entsprechenden Codierung verwendet man aus der Menge aller möglichen (binären) Worten der Länge n nur Folgen mit speziellen Eigenschaften, also eine Teilmenge der möglichen Codeworte. Ein einfaches Beispiel für einen fehlererkennenden und behebenden Code ist das Hinzufügen von Paritätsbits. Diese zusätzlichen Bits werden gerade so gewählt, dass 26 Kapitel 2. Informationsdarstellung sie sich mit der Gruppe der Bits, auf die sie sich jeweils beziehen, derart ergänzen, dass sich eine gerade Anzahl von Einsen pro Gruppe ergibt. Analog zu dieser geraden Parität kann man natürlich auch ungerade Parität vereinbaren. Man unterscheidet Längsparität und Querparität, je nachdem, ob es sich bei der Gruppe um eine aufeinanderfolgende Bitreihe handelt oder aber um alle i-ten Positionen innerhalb einer größeren Anzahl von Bitreihen. Gemeinsam angewendet bieten Längs- und Querparität ein Mittel, einzelne Bitfehler zu lokalisieren und zu korrigieren. Bis zu drei Bitfehler werden sicher erkannt. Es kann allerdings in pathologischen Sonderfällen und einer darüber hinausgehenden Häufung von Fehlern dazu kommen, dass diese nicht erkannt werden. Beispiel: Fehlerekennung durch Paritätsbits 10111001 1 00101000 0 10010001 1 Querparität Längsparität Wird nun aus der fettgedruckten Eins aufgrund eines Übertragungsfehlers eine Null, so kann dieser Fehler anhand der zugehörigen Längs- und Querparitätsbits erkannt und behoben werden. Kippen die fettgedruckten Bits des folgenden Beispiels, so wird dies nicht erkannt. 10111001 1 00101000 0 10010001 1 Querparität 2.5.1 Längsparität Hamming-Distanz Die Hamming-Distanz beschreibt ein Abstandsmaß zwischen zwei binären Worten, das durch die Anzahl der Stellen bestimmt ist, an denen sich die Worte unterscheiden. Seien a = (an−1 , . . . , a0 ) und b = (bn−1 , . . . , b0 ) binäre Worte der Länge n. Dann ist n−1 d(a, b) = d((an−1 , . . . , a0 ), (bn−1 , . . . , b0 )) = ∑ | ai − bi | i=0 die Hamming-Distanz. Je größer d(a, b) ist, desto mehr Störungen sind notwendig, um a in b zu verwandeln. Die Hamming-Distanz eines Codes ist definiert als HD = min(d(a, b)) wobei a und b zulässige Codeworte und a 6= b. Bei der Paritätsbitcodierung gilt also HD = 2. Im Extremfall sind die zulässigen Codewörter {(0, . . . , 0); (1, . . . , 1)}, somit gilt hier HD = n, wenn n die Länge der Wörter beschreibt. 2.5. Erkennung und Behebung von Bitfehlern Wenn ein Code eine Hammingdistanz von 2t + 1 hat, dann gibt es um jedes Codewort einen Einzugsbereich der Größe t. Die Wörter innerhalb der t-Kugel entsprechen dabei Störungen des Codeworts von maximal t Bits. Geht man davon aus, dass Störungen weniger wahrscheinlich sind als korrekte Übertragungen, so kann man eine empfangene Folge dem nächstliegenden Codewort zuordnen (Maximum Likelihood Decision). Damit hat man die Möglichkeit, bis zu t Bitfehler zu korrigieren. Treten jedoch mehr als t Bitfehler auf, so wird die Entscheidung fehlerhaft. Bei Codes der Hamming-Distanz 2t sind bis zu t − 1 Bitfehler erkennbar und korrigierbar. t Bitfehler sind erkennbar, aber nicht mehr korrigierbar. Das einfachste Beispiel hierfür ist wieder die Paritätsbitcodierung mit t = 1. Beispiel: Fehlererkennung/-korrektur Es werde der folgende Code definiert: Zeichen Codierung A 00000000 11000011 E 00000111 R S 11100000 00111010 T 11111111 U Dann ist die Hamming-Distanz des Codes 3. Somit gilt hier t = 1, es können also 2Bit-Fehler erkannt und 1-Bit-Fehler korrigiert werden. 27 28 Kapitel 2. Informationsdarstellung KAPITEL 3 Maschinennahe Programmierung In diesem Kapitel wird die maschinennahe Programmierung vorgestellt. Wir betrachten die Darstellung von Befehlen und verschiedene Arten der Speicheradressierung. Schließlich wird erklärt, was es heißt, Programme und Unterprogramme auszuführen. 3.1 Darstellung von Befehlen Die Speicherung eines Befehls findet meist in einem Wort statt, manchmal auch in einem Halbwort oder Doppelwort. Die Interpretation einer Bitfolge setzt in einem Speicherwort eine Strukturierung voraus, die den gewünschten Befehl identifiziert. 3.1.1 Aufbau eines Befehlswortes Der Aufbau eines Befehlswortes ist im Allgemeinen folgendermaßen: Format Opcode Adresse 1 ... Adresse k Format Das Format legt einerseits die Struktur der Adresscodierung fest; es gibt an, an welcher Binärstelle die Adressen beginnen und wie lang sie sind. Andererseits legt es Regeln zur Befehlsausführung fest, das heißt, die Zahl der Operanden, Vorrangregelungen oder etwa implizite Operanden wie zum Beispiel den Akkumulator oder Stack. Oft ist das Format implizit durch den Opcode gegeben. Opcode Der Opcode definiert die auszuführende Operation. Aufgrund seines Codes kann zum Beispiel ein Holebefehl (Code sei 001011) von einem Additionsbefehl (011011) un29 30 Kapitel 3. Maschinennahe Programmierung terschieden werden. Man unterscheidet Operationen nach ihrer Stelligkeit, das heißt, anhand der zu ihrer Ausführung benötigten Operanden. Beispiel: Opcodes Nullstellige Operation Einstellige Operation Zweistellige Operation Mehrstellige Operation HALT (Stop der Programmausführung) a = −b a = b op c Vektoroperation Adresstypen Man unterscheidet zwei Typen von Adressen: 1) Operandenadressen geben die Quell- und Zieladressen der Operanden an. Dabei kann die Adresse zum Beispiel eine Konstante sein. Beispiel: Adressen a = b+c b und c sind Quelladressen und a ist eine Zieladresse. 2) Die Folgebefehlsadresse zeigt an, an welcher Adresse sich die nach diesem Befehl auszuführende Instruktion befindet. Die Quell- und Zieladressen können fehlen, wenn sich die auszuführende Operation auf spezielle Register oder auf den Stack bezieht. Dies wird aus dem Opcode ersichtlich. Die Folgebefehlsadresse fehlt, wenn von der sequenziellen Befehlsausführung nicht abgewichen wird. Anhand der Operanden-Zahl lassen sich Befehlsstrukturen unterscheiden (siehe Abbildung 3.1). Die Bezeichnung (x + y)-Befehl bedeutet, dass der Befehl x Operandenadressen (ohne Register) und y Folgebefehlsadressen verwendet. Beispiel: (1 + 0)-Befehle α = ρ(i) α = α + ρ( j) ρ(k) = α Opcode1 , Operandi Opcode2 , Operand j Opcode3 , Operandk Dabei spricht man von Mnemonischer-Darstellung (Assemblerbefehl) und dem Quellcode, bzw. von Binärdarstellung (Objektform) und Objektcode. Der Unterschied zwischen Quellcode und Objektcode liegt unter anderem in der besseren Lesbarkeit der Programme im Quellcode, während die Darstellung im Objektcode maschinennäher ist. Ein Assembler ist ein Programm, welches Quellcode in Objektcode umwandelt. Mit diesem Begriff identifiziert man auch häufig die Assemblersprache. 3.1. Darstellung von Befehlen Befehlsstruktur 3+1 3+0 2+1 2+0 1+1 1+0 0+1 0+0 Adressen a, b, c, d a, b, c a, b, d a, b a, d a d keine 31 Wirkung (Beispiel) ρ(a) = ρ(b) op ρ(c) ρ(a) = ρ(b) op ρ(c) ρ(a) = ρ(b) op ρ(a) ρ(a) = ρ(b) op ρ(a) α = α op ρ(a) α = α op ρ(a) goto d Stackbefehl Folgebefehl d β+1 d β+1 d β+1 d β+1 α steht für den Akkumulator, β bezeichnet den Befehlsfolgezähler und ρ(i) ist der Wert (Datum) in Speicherzelle i Häufig benutzt und ökonomisch, da nur auf maximal eine Adresse zugegriffen werden muss, sind (1 + 0)-Befehle und (0 + 0)-Adressbefehle. Abbildung 3.1: Befehlsstrukturen (2 + y)-Adressbefehle sind aufwändiger und ungebräuchlich, sie lassen sich außerdem als eine Folge von (1 + y)- und (0 + 1)-Adressbefehlen darstellen. Beispiel: (2 + 1)-Befehlssequenz Der (2 + 1)-Befehl α = ρ(i) − ρ(k); goto h lässt sich durch folgende Sequenz simulieren: α = ρ(i) (1 + 0)-Befehl α = α − ρ(k) (1 + 0)-Befehl goto h (0 + 1)-Befehl Stack Außer den vorgestellten Befehlen gibt es so genannte Stackbefehle, die auf dem Stack arbeiten. Beim Stack handelt sich um eine Datenstruktur, die nach dem LIFOPrinzip (Last In First Out) organisiert ist. Ein Stack ist ein Stapel von Informationseinheiten, in die Informationen (Elemente) abgelegt werden. Dabei kann nur auf das oberste Element zugegriffen werden. Es gibt zwei Operationen zur Veränderung des Stacks. • Push legt ein neues Element auf den Stack, das heißt, an die oberste Position (τ, Top of Stack). • Pop entnimmt das oberste Element, wobei das entnommene Element ausgegeben wird. Es gibt auch eine Variante, bei der das oberste Element nicht ausgegeben wird. In diesem Fall gibt es zusätzlich die Operation Top, welche es erlaubt, das oberste Element des Stacks zu lesen, ohne dass es entfernt wird. 32 Kapitel 3. Maschinennahe Programmierung Ein Stack wird als Folge σ = (σ(0), σ(1), . . . σ(τ)) notiert. τ .. . σ(τ) .. . i .. . σ(i) .. . 2 σ(1) 1 σ(0) Beispiel: Simulation von Stackbefehlen Der Push-Befehl vollzieht folgende Instruktionen: τ = τ+1 σ(τ) = α Pop lässt sich folgendermaßen ausdrücken: α τ = σ(τ) = τ−1 Sei nun stackadd die Operation, welche die obersten Stackelemente addiert, eliminiert und die Summe als oberstes Element ablegt: α = σ(τ) τ = τ−1 σ(τ) = α + σ(τ) Anwendung finden Stacks bei der Auswertung arithmetischer Ausdrücke. Die Ausführung arithmetischer Operationen in Infixnotation (a op b) hängt von Präzedenzregeln und der Assoziativität der Operationen ab, wenn man von einer Klammerung absieht. Die Umgekehrt Polnische Notation (UPN) legt die Auswertung von Ausdrücken eindeutig durch die Reihenfolge der Operanden und Operationen fest. Bei dieser sukzessiven Auswertung im UPN-Ausdruck geht man von links nach rechts in Verbindung mit Push, Pop und Stackφ vor, wobei φ ∈ {add, sub, mult, div, exp} ist. Zur Auswertung eines Infixausdrucks wandelt man diesen zunächst in einen UPNAusdruck um. Hier stehen die Operationen hinter den Operanden. Beispiel: Infixausdruck Sei der Infix-Ausdruck x = a + [(b − c) · d] ↑ (e · f ) − g gegeben. In UPN: x = abc − d · e f · ↑ +g− Stackoperationen: Zu Beginn sei der Stack leer! 1 2 3 4 5 push a push b push c stacksub push d 3.1. Darstellung von Befehlen 6 7 8 9 10 11 12 13 14 stackmult push e push f stackmult stackexp stackadd push g stacksub pop x 3.1.2 Befehlstypen Man unterscheidet folgende drei Befehlstypen nach ihren unterschiedlichen Aufgaben: a) Datentransfer Für den Datentransfer gibt es den Ladebefehl α = ρ(i), der Daten aus dem Speicher in den Akkumulator holt, und den Befehl ρ( j) = α zum Speichern für die umgekehrte Richtung. b) Arithmetische und logische Operationen Beispiel für eine arithmetische Operation ist die Addition α = α + ρ(i). Der Shiftbefehl zum Schieben von Bits in einem Speicherwort kann ebenfalls als arithmetische Operation aufgefasst werden. Ein Shift nach links verdoppelt den Wert (wenn man von Überträgen absieht). Beispiel: Arithmetische und logische Operationen SHR α, i führt einen Rechts-Shift des Akkumulatorinhalts um i Bits durch. shr 2 , (0110111) = (0001101) c) Steuerbefehle • ein unbedingter Sprung goto j • ein bedingter Sprung if <Bedingung> then goto k <Bedingung> ist zum Beispiel α < 0 oder α = γ. • ein Vergleich und Überspringen der nächsten Operation (Skip) bei Gleichheit • ein Unterprogrammaufruf call <Unterprogrammname/-adresse> • ein Unterprogrammrücksprung return 33 34 Kapitel 3. Maschinennahe Programmierung 3.1.3 Befehlssätze Eine Menge von Befehlen eines Rechnermodells bezeichnet man als Befehlssatz. Die meisten Befehle sind durch (eine Folge von) anderen Befehlen ersetzbar, also simulierbar. Es gibt nun die Tendenz zu möglichst großen Befehlssätzen mit komplexen Operationen. Hier lassen sich kurze Programme bei Ausnutzung des vollen Befehlssatzes schreiben, denn es ist unnötig, umfangreichere Befehle durch eine Folge von weniger mächtigen Befehlen zusammenzusetzen. Ein Rechner, der dieser Tendenz entspricht, ist der Complete Instruction Set Computer (CISC) Die RISC-Architektur (Reduced Instruction Set Computer) verfolgt gerade die umgekehrte Tendenz. Kleinere Befehlssätze lassen eine effizientere und schnellere Realisierung des Rechners zu. Ferner geht man davon aus, dass komplexe Befehle selten eingesetzt werden und bei Bedarf simuliert werden können; bei Beachtung des seltenen Eintretens ist der Leistungsverlust gering. Die schnelle Realisierung der häufigen Grundoperationen führt im Allgemeinen zu einer Leistungsverbesserung. Beispiel: Befehlssimulation Ein bedingter Sprung soll durch einen Vergleich-und-Skip-Befehl ersetzt werden. Die Wirkung von skip α+ besteht darin, den nächsten Befehl zu überspringen, wenn α ≥ 0. Zu simulieren ist: if α < 0 then goto r Simulation durch: [skip α+; goto r]; falls also α ≥ 0 ist, wird goto r übersprungen. 3.1.4 Verwendung zusätzlicher Register und Registerbefehle Zusätzlich zum Akkumulator können weitere Register R0 , R1 , . . . zu speziellen Zwecken benutzt werden, zum Beispiel als Indexregister γ. In Registerbefehlen können bis zu drei dieser Register angesprochen werden: • 0-Registerbefehle Hierzu zählen die bisherigen Befehle. Im Falle der Verwendung des Akkumulators (Register) wird dieser nicht explizit genannt, sondern durch den Opcode implizit angesprochen. • 1-Registerbefehle Beispiel eines solchen Befehls ist die Besetzung des Indexregisters zur indizierten Adressierung: γ = ρ(i). Der Opcode hat die Form: Opcode 0 i 3.2. Adressierung 35 • 2-Registerbefehle Ein Beispiel ist die Anwendung von Rechenoperationen auf Registerinhalte: Ri = Ri − R j oder die Zuweisung (Laden) von Registerinhalten: Load Ri , R j , mit der Wirkung: Ri = R j . Der Opcode ist folgendermaßen aufgebaut: Opcode i j • 3-Registerbefehle Analog lässt sich auch mit drei Registern rechnen: Ri = R j + Rk . Die Verwendung von 3-Adressregisterbefehlen ist sinnvoll, weil diese Befehle schnell ausführbar sind und kurze Adressen haben. Beispiel: Registerbefehle Seien 8 Register R0 , . . . , R7 gegeben. Die Registernummern werden mit Adressteilen zu je 3 Bits codiert, also insgesamt durch 3 · 3 = 9 Bits. Dementsprechend ist der Opcode wie folgt aufgebaut: Opcode 010 001 101 3.2 Adressierung In diesem Abschnitt werden Techniken zum Ansprechen von Informationen im Speicher vorgestellt. Das Ansprechen von Speicher bezeichnet man auch als adressieren. 3.2.1 Direkte Adressierung (absolute Adressierung) Bei der direkten Adressierung wird eine Speicherzelle durch Angabe der Adresse adressiert. Hat diese eine Länge von k Bits, so können 2k Adressen angesprochen werden. Man notiert diese direkte Adressierung in der Form α = ρ(100), wobei hier beispielhaft der Akkumulator den Inhalt der Speicherzelle 100 erhält. Der Vorteil dieser Technik liegt im einfachen und übersichtlichen Zugriff auf den Speicher. Ein schwer wiegender Nachteil besteht aber darin, dass ein zu kleiner Adressraum ansprechbar ist. Außerdem ist der Anteil der Adresse am Befehlswort relativ hoch. 3.2.2 Indirekte Adressierung durch Basisregister und Relative Distanz Diese Technik behebt die Nachteile der direkten Adressierung. Ein Befehl ist folgendermaßen aufgebaut: Opcode Nummer des Basisregisters i m Bits Relative Distanz j r Bits 36 Kapitel 3. Maschinennahe Programmierung Dabei stehen für die Nummer des Basisregisters m Bits und für die relative Distanz r Bits zur Verfügung. Ist m = 3, so können die Register R0 , . . . , R7 der Länge h als Basisregister fungieren (siehe Abbildung 3.2). Beispiel zu indirekter Adressierung r=12, m=3, h=4 Opcode Basisadresse R5 m r 5 j=1024 Block für R5=0 Block für R5=1 R0 angesprochene Adresse R5 13 Block für R5=13 Gesamtblock für R5 mit 16 Blöcken und jeweils 212 =4096 Bytes Block für R5=15 R7 h=4 Bits Abbildung 3.2: Indirekte Adressierung mit Basisregister und relativer Distanz Die Größe des adressierbaren Speichers ergibt sich somit aus: Registerzahl Registerinhalt Rel. Verschiebung 2m 2h 2r m=3 h=4 r = 12 → 8 Register → 16 Blöcke pro Register → 0 ≤ j ≤ 4095 Es sind demnach 2m · 2h · 2r Speicherzellen mit einer m + r Bit langen Adresse ansprechbar (im Beispiel also 219 ). Mit direkter Adressierung sind dagegen nur 2m+r Adressen ansprechbar. Die Wortlänge des Befehls wurde eigentlich um h Bits verlängert durch den Zugriff auf das h-Bit-Register Ri . Durch Verwendung vernünftig großer Registerlängen ist ein fast unbeschränkt großer Speicherbereich auf diesem Wege adressierbar. Bisher war die Basisadresse fest. Durch Änderung dieser Adressen ist eine weitere Flexibilisierung möglich. 3.2.3 Indexregister zur indirekten Adressierung Sei γ ein Indexregister. Durch geeignete Belegungen von γ lassen sich praktisch beliebige Adressen angeben. Im Zusammenhang mit diesem Register kann man zum Beispiel folgende Befehlstypen verwenden: • α = ρ(γ) • ρ(γ) = α 3.2. Adressierung 37 • γ = γ−1 Anwendung findet das Indexregister bei der Bearbeitung von linearen Listen, Schleifen und ähnlichem ohne Änderung des Adressteils eines Befehls. Beispiel: Indexregister in einer Schleife 1 2 3 4 5 6 7 8 9 3.2.4 ANFANG : γ = 1000 //Vorbesetzung . . //Andere Operationen denkbar α = ρ(γ) // Nacheinander werden ρ(1000),. . . . // ρ(999), . . . , ρ(1) angesprochen . . γ = γ−1 if γ > 0 then goto ANFANG Indexregister mit Adressmodifikation Um zwischen direkter und indizierter Adressierung wählen zu können, bietet sich diese Technik an. Der Befehl hat die folgende Struktur: Opcode j Adresse j kann dabei zum Beispiel mit 3 Bits codiert sein und gibt folgende Situationen an: j=0 j ∈ {1, . . . 7} 3.2.5 Befehl ohne Adressmodifikation, Befehl α = ρ(Adresse) Adressmodifikation mit Indexregister γ j , Befehl α = ρ(Adresse + γ j ) Unterschiedliche Adressmodi Ferner sind Modifikationen der Adresse denkbar. So wird je nach Modus zum Beispiel das Indexregister automatisch inkrementiert, wenn über dieses eine Adressierung vorgenommen wird. 3.2.6 Indirekte Adressierung Eine weitere Variante ist die indirekte Adressierung, bei der die Adresse einer Speicherzelle als Inhalt einer anderen Speicherzelle ermittelt wird. Ein Holebefehl hat dann die Form α = ρ(ρ(i)) während er bei direkter Adressierung α = ρ(i) lautete. In einem Befehl ist die Kennzeichnung, ob α = ρ(i) oder α = ρ(ρ(i)) gemeint ist, durch das I-Bit möglich; zum Beispiel zeigt I = 0 die direkte und I = 1 die indirekte Adressierung an. 38 Kapitel 3. Maschinennahe Programmierung Beispiel: Indirekte Adressierung Bearbeitung der Zellen 100, 99, . . . , 1 mit indirekter Adressierung: α = 100 1 2 3 4 5 6 7 8 9 10 START : ρ(200) = α // Hilfsspeicherzelle 200 α = ρ(ρ(200)) . . . α = ρ(200) α = α−1 if α > 0 then goto START 3.2.7 Modifizierung von Befehlen Der Inhalt des Adressteils eines Befehls wird hier modifiziert, also etwa inkrementiert oder dekrementiert. Die Wirkung besteht darin, dass das Programm während seines Durchlaufs verändert wird. Dies ist zum Teil undurchschaubar und daher gefährlich. 3.3 Leistungsfähigkeit von Adressiertechniken Satz: Mächtigkeit von Adressiertechniken Die Adresstechniken, welche indirekte Adressierung erlauben, besitzen die volle Funktionalität der absoluten Adressierung, bzw. sind sogar noch stärker. Beweisidee: 1. Jede der Techniken kann durch jede andere simuliert werden. 2. Problem: siehe nachfolgende Vermutung! Ist die direkte Adressierung schwächer als eine indirekte Adressierung? Unter schwächer ist dabei zu verstehen, dass es ein Problem gibt, zu dessen Lösung ein Programm mit indirekter Adressierung existiert, aber keines, welches allein mit direkter Adressierung auskommt. Es stellt sich heraus, dass diese Behauptung nicht allgemein gilt, sondern von der Endlichkeit des Rechnermodells abhängt. 3.3.1 Berechnung durch ein Programm Zur Diskussion der Vermutung müssen wir uns mit der Frage befassen, was ein Programm eigentlich berechnet. 3.3. Leistungsfähigkeit von Adressiertechniken Ein (terminierendes) Programm soll eine Fragestellung universell lösen, das heißt, ein und dasselbe Programm wird auf verschiedene Argumentkombinationen angesetzt (siehe Beispiel: Additionsprogramm). Es bewirkt im Wesentlichen eine Transformation eines alten Rechenspeicherinhalts in einen neuen Rechenspeicherinhalt. Im Wesentlichen soll heißen: Ein- und Ausgabe nur vom oder zum Speicher; Register, Flags und Statusinformationen sind nur für Hilfszwecke vorgesehen, also unerheblich für das Start- beziehungsweise Endergebnis. Damit kann man ein Programm als eine Abbildung vom Rechenspeicher in den Rechenspeicher (RSP) auffassen. f : RSP → RSP Beispiel: Additionsprogramm 1 2 3 4 5 6 7 γ = 100 α=0 //Initialisierung: Anfangssumme α = α + ρ(γ) //Indirekte Adressierung γ = γ−1 if γ > 0 then goto 2 ρ(1) = α HALT In diesem kurzen Programm sei es erlaubt, Zahlen als Label zu verwenden, die an sich keine Aussage über die Programmstruktur zulassen. Daher sollte man bei längeren Assemblerprogrammen aussagekräftige Label verwenden. Die Wirkung des Programms ist in folgender Abbildung dargestellt. RSPalt 0 1 100 M−1 3.3.2 ρ(1) ρ(100) 0 1 RSPneu = neu = 100 = M−1 = ← ρ(1)alt + ρ(2)alt + · · · + ρ(100)alt = heißt unveränderter Inhalt Endlichkeit der realen Maschine Wir betrachten zunächst die Berechenbarkeit, die auf der Endlichkeit einer realen Maschine basiert. Jede Rechenspeicherzelle ist endlich groß, daher ist auch jedes Programmspeicherwort endlich groß. Diese obere Schranke soll nicht vergrößert werden können (etwa durch längere Worte). Die Information einer Rechenspeicherzelle sei o.B.d.A. eine ganze Zahl, die kleinste darstellbare Zahl sei mit MINZAHL und die größte darstellbare Zahl mit MAXZAHL bezeichnet. Ein Programm, das M Speicherzellen verwendet, ist auffassbar als Abbildung f : D → D′ , wobei D, D′ ⊆ [MINZAHL : MAXZAHL]M 39 40 Kapitel 3. Maschinennahe Programmierung Beispiel: Additionsprogramm Das vorige Additionsprogramm hatte 100 Eingangsargumente und 1 Ausgangswert, also gilt hier D = [MINZAHL : MAXZAHL]100 und D’ = [MINZAHL : MAXZAHL]1 . Satz: Realisierbarkeit von Abbildungen durch Programme Jede Abbildung f : D → D′ mit D, D′ ⊆ [MINZAHL : MAXZAHL]M ist realisierbar durch ein Programm, welches (außer Steuerbefehlen wie START, STOP,. . .) auskommt mit: α = ρ(i) für i, j ∈ [0, . . . , M − 1] ρ( j) = α i und j sind absolute Adressen, keine Variablen α=k k ist Konstante α = α±1 In-/Dekrementierung des Akkumulatorinhalts if α < 0 then goto j if α = 0 then goto j if α > 0 then goto j Dieses Programm kommt insbesondere ohne indirekte Adressierung und mit einem sehr kleinen Befehlssatz aus. Beweis: Beweis (für jedes Programm, also für jede Abbildung f ): Die Abbildung f bewirkt: f (ρ(0)alt , . . . , ρ(M − 1)alt ) = (ρ(0)neu , . . . , ρ(M − 1)neu ), wobei: ρ(k)neu = hk (ρ(0)alt , . . . , ρ(M − 1)alt ). Das heißt, der neue Wert ergibt sich aus den alten Werten (im Extremfall aus allen alten Werten). Beispiel: voriges Additionsprogramm k 6= 1: ρ(k)neu = hk (. . .) = ρ(k)alt k = 1: ρ(1)neu = h1 (ρ(1)alt , . . . , ρ(100)alt ) = ρ(1)alt + . . . + ρ(100)alt also: 5050 falls ρ(i)alt = i, 1 ≤ i ≤ 100 h1 (ρ(1)alt , . . . , ρ(100)alt ) = 0 falls ρ(i)alt = 0, 1 ≤ i ≤ 100 ... andere Fälle Die Programmwirkung ist also beschreibbar durch Abbildungen h0 , h1 , . . . hM−1 . Abbildung 3.3 zeigt, wie die Eingangsargumente ρ(i)alt schichtweise im Baum abgefragt werden. Somit ist jede Kombination von Eingabeargumenten durch einen Pfad im Baum gegeben. An den Blättern erhält man aufgrund der Funktionen h1 bis hM−1 und der durch den Pfad gegebenen Eingangswerte die Ausgabeargumente. Das folgende Programm realisiert diese schichtenweise Abfrage der Eingangsargumente im Wesentlichen unter Verwendung bedingter Sprünge. Die Ausgabeargumente werden im letzten Abschnitt des Programms eingetragen. Sei zur Abkürzung: MINZAHL = −Q und MAXZAHL = +Q. 0: 1: 2: 3: α = ρ(0) if α = 0 then goto [ Stufe 2 , Wert 0] Label mit Nummer 4 Q +1 if α < 0 then goto (2 Q +2) α = α−1 3.3. Leistungsfähigkeit von Adressiertechniken Stufe 1: Abfrage von (0)alt 41 (0) =Minzahl =Minzahl (1) =Maxzahl (1) (1) (2) (M-1) =Minzahl Eintragen h0(Min,…,Min,Min) h1(Min,…,Min,Min) . . . hM-1(Min,…,Min,Min) =Maxzahl =Minzahl+1 Stufe M: Abfrage von (M-1)alt Eintragen h0(Min,…,Min,Min,Max) h1(Min,…,Min,Min,Max) . . . hM-1(Min,…,Min,Min,Max) Abbildung 3.3: Algorithmus zur Berechnung von h0 , h1 , . . . 4: if α = 0 then goto [ Stufe 2 , Wert 1] 5: α = α − 1 6: if α = 0 then goto [ Stufe 2 , Wert 2] . 2Q : if α = 0 then goto [ Stufe2 , Wert Q - 1] 2Q +1: goto [ Stufe 2 , Wert Q] 2Q +2: α = α + 1 2Q +3: if α = 0 then goto [ Stufe 2 , Wert - 1] . 4Q -1: if α = 0 then goto [ Stufe 2 , Wert - Q + 1] 4Q : goto [ Stufe 2 , Wert - Q] 4Q +1 [ Baumorganisation Stufe 1 , 2 ,. . ., M] Letzte Stufe zur Eintragung der Ergebnisse : α = h0 (−Q, . . . , −Q) zum Beispiel : α = +17 ρ(0) = α α = h1 (−Q, . . . , −Q) zum Beispiel : α = −3 ρ(1) = α . α = h(M−1) (−Q, . . . , −Q) ρ(M − 1) = α . α = h0 (−Q, −Q, −Q, −Q + 1) ρ(0) = α . 42 Kapitel 3. Maschinennahe Programmierung 3.3.3 Beliebig große Funktionswerte sollen berechenbar sein Ein Beispiel ist die Addition beliebig großer Zahlen. Das Programm ist jetzt eine Abbildung: f : [MINZAHL : MAXZAHL]∗ → [MINZAHL : MAXZAHL]∗ Das * zeigt an, dass es beliebig viele Rechenspeicherzellen gibt. Satz: Indirekte Berechnung von Abbildungen durch Programme Es gibt Abbildungen, die nur durch Programme mit indirekter Adressierung berechenbar sind. Diese Programme enthalten also Befehle der Art ρ(ρ(i)) = α oder ρ(γ) = α. Beweis: Ein Programm ohne indirekte Adressierung spricht nur feste Rechenspeicherzellen durch Befehle der Form ρ(i) = α an. Jedes Programm P hat eine maximale, von ihm angesprochene Rechenspeicheradresse ADMAX(P). Hinter ADMAX(P) kann im Rechenspeicher nichts geändert werden. Daher sind alle Probleme, die hinter ADMAX(P) etwas ändern, von P nicht bearbeitbar. Beispiel: Beispiel für Existenz solcher Probleme Suche die erste 0 im Rechenspeicher und ersetze sie durch 1. Hier kann nämlich die erste 0 hinter ADMAX(P) liegen. Annahme: Es existiere ein Programm P ohne indirekte Adressierung. Man betrachte die Rechenspeicherbelegung, bei der keine 0 in den Speicherzellen mit einer Adresse kleiner/gleich ADMAX(P) steht. Für diese Speicherbelegung versagt das Suchprogramm P. Also ein Widerspruch. Mit indirekter Adressierung geht es sehr einfach: 1 2 3 4 5 6 γ = −1 γ = γ+1 α = ρ(γ) if (α 6= 0) then goto 1 ρ(γ) = 1 HALT 3.4 Unterprogramme Beim Programmaufbau aus Modulen werden oft benutzte Teile als Unterprogramm (UP) zusammengefasst. Diese können auch in Form einer Unterprogrammbibliothek abgerufen werden, was der Wiederverwendbarkeit dient. In diesem Abschnitt werden nun Probleme aufgezeigt, die sich bei der Handhabung solcher Unterprogramme auf maschinennaher Ebene ergeben. Ein Unterprogramm habe die folgende Form: 3.4. Unterprogramme UP 43 ← Aufruf Name, Parameter . . . Ergebnisse → Rücksprung Unterprogramme sind möglicherweise mehrstufig verschachtelt, das heißt, ein Unterprogramm ruft ein anderes oder sich selbst auf. Einen Ablauf einer solchen Aufrufkette zeigt die Abbildung 3.4. Hauptprogramm UP1 UP2 Zeti UP3 UP4 UP5 UP6 Abbildung 3.4: Aufruf und Beenden von Unterprogrammen Die Pfeile → kennzeichnen die Unterbrechung des Unterprogramms der Ebene i und den Aufruf des Unterprogramms der Stufe (i + 1). Die Pfeile in umgekehrter Richtung zeigen die Beendigung des Unterprogramms der Stufe (i + 1) und den Wiedereintritt in das aufrufende Unterprogramm der Stufe i an. Ein Problem ist es nun, die richtige Einsprungstelle für den Wiedereintritt zu verwalten. Die unterschiedlichen Techniken bedingen folgende Kategorien von Unterprogrammen. 3.4.1 Einstufige, nichtrekursive Unterprogramme Die Speicherposition des aufrufenden Programms wird im Register δ gespeichert. Ein Unterprogrammaufruf durch call legt die Adressse des call-Befehls, also des aufrufenden Programms, in δ ab. Ein return aus dem Unterprogramm führt zu einem Sprung an die nächste Adresse nach der geretteten Programmadresse in δ. Weil nur eine Zelle (Register) zur Verwaltung der Adresse des aufrufenden Programms verwendet wird, kann auch nur eine Verzweigung in ein Unterprogramm durchgeführt werden. Daher nennt man diese Programme einstufig. Insbesondere ist Rekursion unmöglich (siehe Abbildung 3.5). 44 Kapitel 3. Maschinennahe Programmierung k: call j . . . Unterprogramm j: . . . . . return Seiteneffekt: =k; goto j Seiteneffekt: goto Abbildung 3.5: Einstufiger Unterprogrammaufruf 3.4.2 Mehrstufige, nichtrekursive Unterprogramme Bei dieser Technik hat jedes Unterprogramm eine eigene Rücksprungadresse. Sie ist an einer speziellen Stelle dessen gespeichert und gibt an, an welcher Stelle in dem aufrufenden (Unter-) Programm fortzufahren ist, wenn das aufgerufene Unterprogramm selbst beendet ist. In Abbildung 3.6 ist diese Rücksprungadresse an der ersten Stelle des aufgerufenen Unterprogramms abgelegt ρ( j). i: j: j+1: call j . . . [Zunächst frei für Rücksprungadresse] Start UP . . . return Wirkung: (i):=i+1; goto j+1; d.h. rette Rücksprungadresse im ersten (dafür frei zu haltenden) UP-Befehl Wirkung: goto (j) Abbildung 3.6: Mehrstufiger Unterprogrammaufruf Eine Voraussetzung für die Funktionsfähigkeit dieser Technik besteht darin, dass das Unterprogramm sich nicht selbst aufruft (reentrent ist); in diesem Falle würde die Rücksprungadresse überschrieben werden. 3.4.3 Mehrstufige, eventuell rekursive Unterprogramme Zunächst wird ein Beispiel eines typischen rekursiven Unterprogramms gegeben. Es handelt sich um die rekursive Berechnung der Fakultätsfunktion (n!). 1 int fakultaet (int n ) { 3.4. Unterprogramme 2 3 4 5 6 7 8 45 if ( n ==0 || n ==1) { return 1; } else { return n* fakultaet (n -1); } } Ein Aufruf von f (n) = f akultaet(n) zieht die folgende Kette von rekursiven Unterprogrammaufrufen nach sich: ruft auf ruft auf ruft auf zurück zurück zurück f (n) −→ f (n − 1) −→ f (n − 2) −→ . . . f (1) f (n) ←− f (n − 1) ←− f (n − 2) ←− . . . f (1) Die Technik des letzten Abschnitts ist nicht anwendbar, da nur eine Speicherzelle für die Rücksprungadresse vorgesehen ist. An dieser Stelle müssten also beliebig viele Stellen für Rücksprungadressen zur Verfügung stehen, anhand derer man ablesen kann, welche die nächste Rücksprungadresse ist. Hierfür eignet sich der Stack, wobei die letzte Rücksprungadresse das Topelement ist. Rekursive Aufrufe werden also über einen Stack mit den Operationen Push und Pop verwaltet, wobei folgende Register verwendet werden. • δ1: Register, dessen Inhalt auf die oberste Stackposition (Topelement) zeigt (für Push und Pop). • δ2: Befehlszählregister (enthält Adresse des aktuellen Befehls). • RL : Das so genannte Linkageregister ist ein beliebiges Register zum Auslesen der Parameter im Unterprogramm und Speichern der Rücksprungadresse. Der call- und return-Befehl haben dann die Wirkung: • call RL ,dst [push RL ; RL = δ2 + 1; goto dst] • return RL [pop RL ;δ2 = RL ] Dabei ist dst die erste Adresse des aufgerufenen Unterprogramms: dst : < Unterprogramm - Anweisungen > Der call-Befehl sichert den aktuellen Wert des Linkageregisters auf dem Stack und setzt RL auf die Adresse des ersten Parameters. Das Unterprogramm liest die Parameter mittels Autoinkrementadressierung über RL , so dass RL anschließend auf den nächsten Befehl zeigt. Dieser ist die Rücksprungadresse. Der return-Befehl setzt das Befehlszählregister auf die Rücksprungadresse und holt den alten Wert von RL vom Stack. 46 Kapitel 3. Maschinennahe Programmierung Eine andere Organisationsform für die Verwaltung der Ein- und Ausgabeparameter verwendet zwei besondere Register: REING und RRESULT sollen die Rechenspeicheradressen enthalten, in denen die Eingangsparameter beginnen beziehungsweise ab wo die Ergebnisse zurückzuschreiben sind. Diese müssen (bei Aufruf des Unterprogramms) geeignet vorbesetzt werden. Die Eingangsparameter werden sofort übernommen (das heißt, es ist hierfür kein Stack notwendig). RRESULT wird über einen Stack verwaltet. Der zugehörige Unterprogrammaufruf sieht schematisch wie folgt aus. Hauptprogramm . . Vorbesetzung RResult PUSH RResult → Resultstack Vorbesetzung REING call dst [ Wirkung α = δ2 + 1; Push α → Rücksprungstack ] . . ------dst : α = REING Übernahme Eingangsargumente . . α = Oberstes Element Resultstack α = ρ(α) Übergabe Ergebnisse return [ Wirkung : α =Oberstes Element des Rücksprungstacks ; goto α] REING und RRESULT verweisen auf den Beginn der Eingangs- bzw. Ergebnisparameterblöcke. Damit man weiß, wie viele Argumente zu übergeben sind, kann man in der ersten Zelle dieser Bereiche jeweils die Anzahl der zu übergebenden Parameter plus die Länge selbst speichern. REING k=Länge Argument 1 Argument 2 .. . Argument k − 1 .. . RRESULT k=2 Ergebnis KAPITEL 4 Bausteine und Komponenten von Rechensystemen In diesem Kapitel wird die rechnerinterne Verarbeitung von Informationen mittels Schaltungen, Schaltkreisen und Schaltwerken betrachtet. 4.1 Schaltfunktionen, Bausteinsysteme und Boolesche Algebra Die Aufgabe einer Schaltung besteht in der Transformation (binär dargestellter) Eingangssignale in (binär dargestellte) Ausgangssignale. Eine Schaltung ist daher mit einem Programm vergleichbar, mit dem Unterschied, dass die Transformation durch die Schaltung fest verdrahtet ist. Eine Schaltfunktion beschreibt die Zuordnung von Ausgangswerten zu Eingangswerten. Beispiel: LED-Dezimalzähler Als erstes Beispiel für eine Schaltung wird ein LED-Zähler betrachtet. Der LED-Zähler besteht aus 7 Strichen, die leuchten oder nicht leuchten. x0 x3 x4 x1 x2 x5 x6 Beispielsweise lässt sich die 7 durch Aufleuchten der Striche x0 , x5 und x6 darstellen. Dies entspricht dem Setzen von Bits (x0 , x1 , . . . , x6 ) = (1, 0, 0, 0, 0, 1, 1). Dem entsprechend ist der 4 (0, 1, 0, 1, 0, 1, 1) zugeordnet. Ein LED-Zähler soll nun von 0 bis 9 mod 10 zählen. Er muss also zur aktuellen Zahl i (Eingangswert) den Nachfolger (i + 1) mod 10 (Ausgangswert) erzeugen, wie folgende Abbildung veranschaulicht. 47 48 Kapitel 4. Bausteine und Komponenten von Rechensystemen x0 x0,Neu x1 x1,Neu x2 x3 x4 Schaltung, Schaltkreis, Schaltznetz x2,Neu x3,Neu x4,Neu x5 x5,Neu x6 x6,Neu Definition: Schaltung Die Wirkung einer Schaltung wird durch eine n-stellige Schaltfunktion mit k Ausgängen beschrieben. f : Bn −→ Bk mit B = {0, 1} x0 y0 n-stellige Schaltfunktion yk-1 xn-1 Möglicherweise kann nur ein Teil aller denkbaren Eingangssignale (Kombinationen der xi in Form von (x0 , x1 , . . . , xn−1 ) vorkommen. In diesem Fall gilt: f : Bn ⊃ D → Bk . Auf dem Eingangsbereich Bn \D ist die Schaltfunktion undefiniert und somit in ihren Ausgangswerten frei wählbar. Diesen Bereich bezeichnet man auch als Don’t CareBereich. Beispiel: Schaltfunktion Beim LED-Zähler werden nur 10 Kombinationen der 27 möglichen benutzt. Es ist zum Beispiel dem Leuchten der Striche drei, vier und sechs keine Zahl sinnvoll zuzuordnen. Eine n-stellige Schaltfunktion kann in Form einer Tabelle angegeben werden, indem man in die linke Spalte die definierten Eingangswerte und in die rechte Spalte die die Funktion definierenden Ausgangswerte schreibt: Ausgänge Eingänge xn−1 , . . . , x0 yk−1 , . . . , y0 0...0 ... .. .. . 2n Zeilen inklusive Don’t Cares . 1...1 ... 4.1. Schaltfunktionen, Bausteinsysteme und Boolesche Algebra Im Falle, dass die Funktion n-stellig und jede Komponente binär ist, sind maximal 2n Werte des Definitionsbereichs und dem entsprechend viele Ausgangswerte denkbar. Beispiel: Rückwärtszähler im 3-Excess-Code Die folgende Tabelle zeigt die Schaltfunktion für einen Rückwärtszähler im 3-ExcessCode, der in Abschnitt 2.4 eingeführt wurde. Die Aufgabe besteht in der Beschreibung der Funktion: f : B4 [i]3-Excess Dezimaler Wert 0 1 2 3 4 5 6 7 8 9 x3 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 1 x2 0 1 1 1 1 0 0 0 0 1 0 0 0 1 1 1 → 7 → B4 [(i − 1) mod 10]3-Excess x1 1 0 0 1 1 0 0 1 1 0 0 0 1 0 1 1 x0 1 0 1 0 1 0 1 0 1 0 0 1 0 1 0 1 y3 1 0 0 0 0 0 1 1 1 1 y2 1 0 1 1 1 1 0 0 0 0 y1 0 1 0 0 1 1 0 0 1 1 y0 0 1 0 1 0 1 0 1 0 1 Dezimaler Wert 9 0 1 2 3 4 5 6 7 8 Don’t Care Werte Eine Schaltfunktion mit k Ausgängen ist darstellbar durch k Schaltfunktionen auf dem gleichen Definitionsbereich mit je einem Ausgang: Bn → B f0 . f : Bn → Bk entspricht .. f n k−1 B → B Aus diesem Grund kann man sich auf Schaltfunktionen mit einem Ausgang beschränken. 4.1.1 Entwurf und Realisierung von Schaltfunktionen In diesem Abschnitt werden Bausteine eingeführt, die es gestatten, binäre Eingangswerte in binäre Ausgangswerte zu überführen. Man kann sie als Operationen auf B auffassen. Es bestehen nun die Fragen • Welche Bausteine sind notwendig, um bestimmte Eigenschaften der Schaltfunktion wiederzugeben? 49 50 Kapitel 4. Bausteine und Komponenten von Rechensystemen • Wie kann man eine Schaltfunktion mit möglichst wenigen Bausteinen darstellen, um den Realisierungsaufwand und damit die Kosten gering zu halten? Dabei soll der Entwurf möglichst systematisch sein. Besonders einfache Schaltfunktionen lassen sich als Grundbausteine (Bausteinfunktionen) zur Zusammensetzung komplexerer Funktionen verwenden. In der folgenden Tabelle sind einige Symbole für solche Grundbausteine dargestellt. Name Konjunktion (AND) Definition ANDn : Bn → B AND(x0 , . . . , xn−1 ) = 1 ⇔ x0 · . . . · xn−1 = 1 Disjunktion (OR) ORn : Bn → B OR(x0 , . . . , xn−1 ) = 0 ⇔ x0 + . . . + xn−1 = 0 Negation (NOT) NAND NOR XOR EQUIV Schwellenelemente Symbol NOT : B → B NOT (x) = 1 − x = x̄ NANDn : Bn → B NAND(x0 , . . . , xn−1 ) = 1 ⇔ x0 · . . . · xn−1 = 0 NORn : Bn → B NOR(x0 , . . . , xn−1 ) = 1 ⇔ x0 + . . . + xn−1 = 0 XORn : Bn → B XOR(x0 , . . . , xn−1 ) = (x0 + . . . + xn−1 ) mod 2 EQUIVn : (x0 , . . . , xn−1 ) = XORn σk : B n → B σk (x0 , . . . , xn−1 ) = 1 ⇔ x0 + . . . + xn−1 ≥ k Die XOR-Funktion (auch EXOR) kann als Parityfunktion verwendet werden, wie zum Beispiel im Abschnitt über Darstellung von Zeichen in Kapitel 2 beschrieben. Die EXOR-, NOT- und EQUIV-Funktion sind keine Schwellenelemente. Für das logische AND schreibt man ∧, ∩ oder ·. Das OR wird als ∨, ∪ oder + geschrieben. AND bindet stärker als OR. Definition: Bausteinsystem Eine Menge von Bausteinfunktionen, aus denen sich beliebige Schaltfunktionen zusammensetzen lassen, heißt Bausteinsystem. 4.1. Schaltfunktionen, Bausteinsysteme und Boolesche Algebra 51 Beispiel: Einige Bausteinsysteme Bausteinsystem {AND2 , OR2 , NOT } {AND2 , NOT } {OR2 , NOT } {NAND2 } {NOR2 } {AND2 , XOR2 , 1} {σk , NOT } Nachweis Disjunktive und konjunktive Normalform OR2 (x, y) = NOT (AND2 (NOT (x), NOT (y))) AND2 (x, y) = NOT (OR2 (NOT (x), NOT (y))) NOT (x) = NAND2 (x, x), AND2 = NOT (NAND2 ) Analog zu {NAND2 } Komplementfreie Ringsummenentwicklung σ1 = ORn , σn = ANDn Schaltfunktionen lassen sich mit diesen Grundbausteinen zu beliebigen neuen Schaltfunktionen zusammensetzen. Zusammensetzung Neue Schaltfunktion Definition x0 xn-1 f xn-1 x0 g fg x0 xn-1 f xn-1 ( f · g) : Bn → B ( f · g)(x0 , . . . , xn−1 ) AND2 ( f (. . .), g(. . .)) = ( f ∪ g) : Bn → B ( f ∪ g)(x0 , . . . , xn−1 ) OR2 ( f (. . .), g(. . .)) = x0 g f+g xn-1 x0 xn-1 x0 f f f¯(x0 , . . . , xn−1 ) = NOT ( f (. . .)) Definition: Gleichheit von Schaltfunktionen Zwei Schaltfunktionen f : D → B und g : D → B heißen genau dann gleich, wenn sie für alle zulässigen Argumentkombinationen das gleiche Ergebnis liefern: f = g ⇔ f (x0 , . . . , xn−1 ) = g(x0 , . . . , xn−1 ) für alle (x0 , . . . , xn−1 ) ∈ D Eigenschaften des Zusammensetzungsprozesses von Schaltfunktionen: f ∧g = g∧ f ( f ∧ g) ∧ h = f ∧ (g ∧ h) f ∧ (g ∨ h) = ( f ∧ g) ∨ ( f ∧ h) f ∧ ( f ∨ g) = f f ∧ (g ∨ ḡ) = f Definition: Boolesche Algebra Eine Menge A mit drei Operationen F1: Kommutativität F2: Assoziativität F3: Distributivität F4: Absorbtion F5: Komplement f ∨g = g∨ f ( f ∨ g) ∨ h = f ∨ (g ∨ h) f ∨ (g ∧ h) = ( f ∨ g) ∧ ( f ∨ h) f ∨ ( f ∧ h) = f f ∨ (g ∧ ḡ) = f 52 Kapitel 4. Bausteine und Komponenten von Rechensystemen • · : A×A → A • + : A×A → A • ¯ :A→A heißt Boolesche Algebra (A, ·, +, ¯ ) genau dann, wenn die Axiome F1 bis F5 erfüllt sind. Hieraus kann man folgern, dass die Menge der Schaltfunktionen mit den Operationen AND2 , OR2 und NOT eine Boolesche Algebra bilden. Es sind auch andere Axiomensysteme denkbar, wie zum Beispiel eines, welches die Kommutativität (K), die Distributivität (D), das neutrale Element (N) und die Komplementfunktion (C) umfasst. Dabei gibt es Entsprechungen zwischen (K) und F1 sowie (D) und F3. Die anderen Axiome lauten: (N) Es gibt neutrale Elemente 0, 1 ∈ A mit f + 0 = f und f · 1 = f (C) Die Komplementfunktion zu f erfüllt: f + f¯ = 1 und f · f¯ = 0 für alle f ∈ A Satz: Äquivalenz zwischen Axiomensystemen Die Axiome F1 bis F5 sind äquivalent zu den Axiomen (K), (D), (N) und (C). Andere Bausteinsysteme verwenden die Schaltfunktionen 0 und 1. 0 : Bn → B mit 0(x0 , . . . , xn−1 ) = 0 1 : Bn → B mit 1(x0 , . . . , xn−1 ) = 1 für alle (x0 , . . . , xn−1 ) ∈ Bn Andere Beispiele für Boolesche Algebren sind: • Aussagenkalkül, wobei eine Aussage den Wahrheitswert wahr oder falsch annimmt. Dabei lassen sich Aussagen wie Schaltfunktionen zusammensetzen. • Mengenalgebra mit den Operationen Durchschnitt, Vereinigung und Komplementmenge. Satz: Satz von Stone Jede endliche Boolesche Algebra (A, op 1 , op 2 , op 3 ) ist isomorph zu einer Mengenalgebra (℘(M), ∪, ∩, ¯ ). Das heißt, es gibt eine Bijektion ϕ : A → ℘(M) mit ϕ(x op 1 y) = ϕ(x) ∪ ϕ(y) ϕ(x op 2 y) = ϕ(x) ∩ ϕ(y) ϕ( op 3 x) = ϕ(x) Beachten Sie, dass ℘(M) die Potenzmenge von M ist. Die folgende Skizze veranschaulicht die Aussage des Satzes, wobei hier beispielhaft op 1 und ∪ aufgeführt werden. 4.1. Schaltfunktionen, Bausteinsysteme und Boolesche Algebra ϕ x, y −−−−→ ϕ(x), ϕ(y) op ∪ y 1 y ϕ x op 1 y −−−−→ ϕ(x) ∪ ϕ(y) Das Diagramm ist kommutativ: man kann in der Mengenalgebra ebenso rechnen wie in der Algebra A. Daher kann man alle Eigenschaften endlicher Boolescher Mengen als Eigenschaften von Mengenalgebren interpretieren. Dies kann dem leichteren Verständnis von Aussagen dienen. Beispiel: Schaltfunktion vs. Boolesche Algebra Die Aussage (F3)DUAL f ∨ (g ∧ h) = ( f ∨ g) ∧ ( f ∨ h) wird in der folgenden Abbildung deutlich. f g h Weitere Eigenschaften endlicher Boolescher Algebren sind somit aus Axiomen oder auch durch mengentheoretische Interpretation herleitbar, wie etwa: a∧0 = 0 a∨1 = 1 a∧a = a a∨a = a a ∧ b = ā ∨ b̄ a ∨ b = ā ∧ b̄ (Gesetz von de Morgan) a∪b = b ⇔ a∩b = a ⇔ ab̄ = 0 ⇔ ā ∪ b = 1 ⇔ b ⊃ a (mengentheoretische Interpretation) ā¯ = a 0̄ = 1 1̄ = 0 Eine weitere Folgerung aus dem Satz von Stone ist der folgende Satz. Satz: Anzahl der Elemente einer Booleschen Algebra Jede endliche Boolesche Algebra hat eine Elementanzahl, die eine Zweierpotenz ist. 53 54 Kapitel 4. Bausteine und Komponenten von Rechensystemen Beispiel: Anzahl der Elemente einer Schaltfunktion Sei S(D) die Menge der n-stelligen Schaltfunktionen mit D Zeilen: S(D) = { f | f : Bn ⊃ D → B} Dann hat S(D) 2|D| Elemente. Diese Elemente stellen alle kombinatorisch denkbaren Wertebereiche der n-stelligen Schaltfunktion dar. Beweis: Jede der D Zeilen ist unabhängig von den anderen mit dem Ausgangswert 0 bezie2 n hungsweise 1 besetzbar. Insbesondere gilt: S(Bn ) = 22 . Sei n = 2, so ergeben sich 22 = 16 Schaltfunktionen, wie in folgender Tabelle aufgeführt: x1 0 0 1 1 x0 0 1 0 1 f0 0 0 0 0 f1 0 0 0 1 f2 0 0 1 0 f3 0 0 1 1 f4 0 1 0 0 f5 0 1 0 1 f6 0 1 1 0 f7 0 1 1 1 f8 1 0 0 0 f9 f10 f11 f12 f13 f14 f15 1 1 1 1 1 1 1 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 1 0 1 0 1 Die Funktionen f0 bis f15 tragen ihrer Bedeutung nach folgende Namen: Funktion f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15 Bezeichnung NULL AND x→y Projektion auf x y→x Projektion auf y XOR OR NOR EQUIV Projektion auf ȳ y→x Projektion auf x̄ x→y NAND EINS Eine 4-stellige Schaltfunktion sei durch die folgende Funktionstabelle gegeben. x3 0 0 0 0 1 x2 0 1 1 1 0 x1 0 0 1 1 1 x0 0 1 0 1 1 f 1 0 0 1 0 Mit | D |= 5 gibt es genau 25 = 32 Schaltfunktionen f0 , . . . , f31 . 4.1.2 Atom, Minterm und Maxterm In diesem Abschnitt betrachten wir kleinste und größte Schaltfunktionen. 4.1. Schaltfunktionen, Bausteinsysteme und Boolesche Algebra Definition: Atom einer Booleschen Algebra a ∈ A mit a 6= 0 heißt Atom einer Booleschen Algebra (A, ·, +, ¯ ) genau dann, wenn a · b = a oder a · b = 0, für alle b ∈ A (a · b 6= 0 ⇒ a · b = a) Atome einer Booleschen Algebra sind die kleinsten Elemente, die multipliziert mit einem anderen Element entweder das Element selbst oder Null ergeben. In Mengenalgebren ℘(A) sind die Atome gerade die einelementigen Mengen mit ∩. Definition: Minterm Ein Minterm ist eine atomare Schaltfunktion, die an genau einer Stelle 1 ist. Hat eine Schaltfunktion in ihrer Funktionstabelle genau eine 1, so ist sie ein Minterm: xn−1 . . . x0 0...0 .. . .. . εn−1 . . . ε0 .. . .. . 1...1 f 0 .. . 0 1 0 .. . 0 Die Schaltfunktion f hat dann die Form: f= x0ε0 · x1ε1 εn−1 , · . . . · xn−1 wobei xiεi ( xi = x̄i wenn εi = 1 wenn εi = 0 55 56 Kapitel 4. Bausteine und Komponenten von Rechensystemen Beispiel: Minterm Sei die Funktion f4 durch folgende Funktionstabelle gegeben. x1 0 0 1 1 x0 0 1 0 1 f4 0 1 0 0 Dann ist x̄1 · x0 ein Minterm. Ein zum Minterm dualer Begriff ist der des Maxterms, also der größten Schaltfunktion ungleich der 1-Funktion. Definition: Maxterm Ein Maxterm ist eine atomare Schaltfunktion, die an genau einer Stelle 0 ist. Eine Schaltfunktion, die in ihrer Funktionstabelle genau eine 0 enthält, ist ein Maxterm: xn−1 . . . x0 0...0 .. . .. . εn−1 . . . ε0 .. . f 1 .. . 1...1 1 1 0 1 .. . Die Schaltfunktion f hat dann die Form: f= x0ε̄0 + x1ε̄1 ε̄n−1 , + . . . + xn−1 wobei ¯ xiεi ( x̄i = xi wenn εi = 1 wenn εi = 0 Minterme und Maxterme ermöglichen standardisierte Darstellungen von Schaltfunktionen, so genannte Normalformen, welche im nächsten Abschnitt vorgestellt werden. 4.1.3 Boolesche Ausdrücke Bisher wurden Schaltfunktionen über Funktionstabellen mit Eingängen (x0 , . . . , xn−1 ) und Ausgang f (x0 , . . . , xn−1 ) definiert. Jetzt werden x0 , . . . , xn−1 und f als Variablen interpretiert. Wir gehen also von Schaltfunktionen mit Eingängen x0 , . . . , xn−1 zu Booleschen Funktionen in x0 , . . . , xn−1 über. Da Schaltfunktionen eine Boolesche Algebra bilden, dürfen wir Boolesche Ausdrücke mit den Rechenregeln der Booleschen Algebra umformen. 4.1. Schaltfunktionen, Bausteinsysteme und Boolesche Algebra Definition: Boolescher Ausdruck Sei X = {0, 1, x0 , . . . , xn−1 } eine Menge von Booleschen Variablen mit den Atomen 0 und 1. Die Boolesche Ausdrücke k-ter Stufe über X sind dann induktiv wie folgt definiert: Stufe 0 : Elemente von X Stufe (k + 1) : (w0 · w1 · . . . · wr−1 ), (w0 + w1 + . . . + wr−1 ) oder w̄, wobei w, wi Boolesche Ausdrücke sind mit maxi (Stufe(wi )) = k und Stufe(w) = k. Hierbei ist die Stufenzahl gleich der Klammertiefe und gleich der Anzahl hintereinander zu durchlaufender AND-, OR- und NOT-Schaltungen. Zusammenhang zwischen Booleschen Ausdrücken und Schaltfunktionen Der Zusammenhang zwischen Booleschen Ausdrücken und Schaltfunktionen wird durch die folgenden Aussagen beschrieben. a) Jedem Booleschen Ausdruck entspricht eindeutig eine Schaltfunktion Sei B(X) = {Boolesche Ausdrücke über X = {x0 , . . . , xn−1 }}, S(Bn ) ={Schaltfunktionen f : Bn → B} und ϕ : B(X) → S(Bn ) eine Abbildung, die wie folgt induktiv definiert ist: ϕ(0) ϕ(1) ϕ(xi ) ϕ((w1 ·w2 )) ϕ((w1 + w2 )) ϕ((w̄)) = = = = = = NULL EINS xi AND(ϕ(w1 ), ϕ(w2 )) OR(ϕ(w1 ), ϕ(w2 )) NOT(ϕ(w)) NULL ist eine Schaltfunktion: 0 EINS ist eine Schaltfunktion: 1 Projektion auf i-te Komponente Die Funktionstabelle eines Booleschen Ausdrucks ist eindeutig. Umgekehrt gilt dies aber nicht. b) Zu jeder Schaltfunktion gibt es unendlich viele Boolesche Ausdrücke Zwei Boolesche Ausdrücke heißen äquivalent, wenn sie dieselbe Schaltfunktion definieren. Seien a, b ∈ B(X) Boolesche Ausdrücke, a ist äquivalent zu b (a ≡ b) genau dann, wenn ϕ(a) = ϕ(b). Man sieht sofort: a äquivalent b, sofern a in b mit Hilfe der Rechenregeln der Booleschen Algebra umwandelbar ist. Beispiel: Umformungen ≡ ≡ ≡ ≡ ≡ ≡ x̄1 · x2 + x1 · x̄2 + x1 · x2 x̄1 · x2 + x1 · x̄2 + x1 · x2 + x1 · x2 x̄1 · x2 + x1 · x2 + x1 · x̄2 + x1 · x2 (x̄1 + x1 ) · x2 + x1 · (x̄2 + x2 ) 1 · x2 + x1 · 1 x2 + x1 x1 + x2 wegen a ≡ a + a wegen (K) wegen (D) wegen a + ā = 1 wegen (K) und a · 1 = a wegen (K) 57 58 Kapitel 4. Bausteine und Komponenten von Rechensystemen Hierbei sind alle Booleschen Ausdrücke äquivalent. Außerdem handelt es sich bei dem letzten Ausdruck offenbar um einen besonders einfachen und billig zu realisierenden Booleschen Ausdruck. c) Aus der Menge der Booleschen Ausdrücke kann man einen suchen, der die Schaltfunktion am billigsten bzw. am schnellsten realisiert. Um einen Booleschen Ausdruck zu finden, der dem ersten Kriterium genügt, ist es erforderlich ein Kostenmaß zu definieren, das eine Entscheidung erlaubt. Um das zweite Kriterium zu benutzen muss man analog ein Zeitmaß einführen. Ergänzung für unvollständig definierte Schaltfunktionen Ist eine n-stellige Schaltfunktion nicht auf dem gesamten Bereich definiert, d.h. f : Bn ⊃ D → B, Bn \D 6= ∅, wenn also Don’t Care-Bedingungen vorliegen, dann heißen zwei Boolesche Ausdrücke äquivalent, wenn sie auf D dieselbe Schaltfunktion realisieren. Don’t Cares sind beliebig besetzbar, das heißt, man darf Minterme, die sich nur auf Bn \D beziehen (also auf D die Nullfunktion liefern), beliebig hinzufügen oder weglassen. Zwei Boolesche Ausdrücke liefern dieselbe Schaltfunktion f : D → B genau dann, wenn sie ineinander überführbar sind. Beispiel: Schaltfunkion vs. Boolescher Ausdruck Sei eine Schaltfunktion durch folgende Funktionstabelle gegeben: x2 0 0 0 0 1 1 1 1 x1 0 0 1 1 0 0 1 1 x0 0 1 0 1 0 1 0 1 f 0 0 0 1 1 1 Don’t Care Don’t Care Ein Boolescher Ausdruck für f ist zum Beispiel: x̄2 x1 x0 + x2 x̄1 x̄0 + x2 x̄1 x0 Durch Umformungen erhält man einen einfacheren Ausdruck: 4.2. Normalformen 59 ≡ x̄2 x1 x0 + x2 x̄1 x̄0 + x2 x̄1 x0 + x2 x1 x̄0 + x2 x1 x0 Don’t Cares hinzufügen, sie werden mit 1 besetzt ≡ x̄2 x1 x0 + x2 (x̄1 x̄0 + x̄1 x0 + x1 x̄0 + x1 x0 ) ≡ x̄2 x1 x0 + x2 ≡ x̄2 x1 x0 + x2 + x2 x1 x0 Don’t Care hinzufügen ≡ (x̄2 + x2 )x1 x0 + x2 ≡ x1 x0 + x2 Im Beispiel werden Don’t Cares hinzugefügt. Es kann auch sinnvoll sein, sie wegzulassen. Trivialbeispiel: NULL : D → B, hier wäre es sinnlos, Don’t Cares auf 1 zu setzen. 4.2 Normalformen In diesem Abschnitt werden standardisierte Darstellungen von Schaltfunktionen durch Boolesche Ausdrücke eingeführt, die aus einer Funktionstabelle systematisch hergeleitet werden können. 4.2.1 Disjunktive Normalform (DNF) Hier wird ein Boolescher Ausdruck zu einer Schaltfunktion als Vereinigung der Minterme dargestellt. Definition: Einschlägiger Index Die Zeilennummer ε einer Funktionstabelle zu einer Schaltfunktion f heißt einschlägiger Index zu f : Bn → B, falls f (ε0 , . . . , εn−1 ) = 1 ist. Definition: ε-ter Minterm Sei ε ein Index von f . Dann heißt die Funktion mε : Bn → B, definiert durch ε ε n−1 mε (x0 , . . . , xn−1 ) = x00 · x1ε1 · . . . · xn−1 ε-ter Minterm von f . Dabei sei ε xjj ( xj = x¯j falls ε j = 1 falls ε j = 0 60 Kapitel 4. Bausteine und Komponenten von Rechensystemen Definition: Disjunktive Normalform (DNF) Sei I die Menge der einschlägigen Indizes zu einer Schaltfunktion f . Die Disjunktive Normalform zu f ist dann definiert als Disjunktion von Mintermen [m ε ε∈I 4.2.2 Konjunktive Normalform (KNF) Definition: ε-ter Maxterm Sei ε Index von f : Bn → B, und sei mε der ε-te Minterm von f . Dann heißt die Funktion Mε : Bn → B definiert durch Mε (x0 , . . . , xn−1 ) = mε (x0 , x1 , . . . , xn−1 ) ε-ter Maxterm von f . Kurz schreibt man Mε = mε Eine Schaltfunktion wird als Durchschnitt der Maxterme dargestellt. Sie lässt sich aus der DNF herleiten. Aus NF( f ) = [ ε {ε| f (ε)=1} folgt NF( f¯) = [ {ε| f (ε)=0} ε n−1 x0ε0 · x1ε1 · . . . · xn−1 = n−1 x0ε0 · x1ε1 · . . . · xn−1 ∏ {ε| f (ε)=0} ε̄ n−1 x0ε̄0 ∪ x1ε̄1 ∪ . . . ∪ xn−1 De Morgan Somit ergibt sich die konjunktive Normalform für f : NF( f ) = ∏ {ε| f (ε)=0} ε̄ n−1 x0ε̄0 ∪ x1ε̄1 ∪ . . . ∪ xn−1 Definition: Konjunktive Normalform (KNF) Sei J = {ε | f (ε) = 0} eine Menge von Indizes zu einer Schaltfunktion f . Die Konjunktive Normalform zu f ist dann definiert als ε̄n−1 ε̄ KNF( f ) = ∏ x00 ∪ x1ε̄1 ∪ . . . ∪ xn−1 {ε∈J} Wenn f nur auf D definiert ist, betrachtet man nur die Produkt- bzw. Vereinigungsbildung bezüglich D. 4.2.3 Komplementfreie Ringsummenentwicklung (KRE) Aus der vollständigen disjunktiven Normalform lässt sich eine weitere Normalform herleiten, die aus ⊕, · und 1 besteht. Dabei stellt ⊕ das Symbol für das Exklusive-Or 4.2. Normalformen 61 dar. 1 ist eine Basisfunktion. Eine Schaltfunktion f hat dann die Darstellung: KRE( f ) = a0 ⊕ a1 · x1 ⊕ . . . ⊕ an · xn ⊕ an+1 · x1 · x2 ⊕ an+2 · x1 · x3 ⊕ . . . .. . a2n −1 · x1 · . . . · xn mit ai ∈ {0, 1} n Es gibt somit 2n Disjunktionsglieder. Ferner gibt es 22 verschiedene Schaltfunktionen, da ai entweder mit 0 oder 1 belegt ist. Liegt die disjunktive Normalform einer Schaltfunktion f vor, so erhält man nach folgender Vorgehensweise die KRE( f ) : a) Ersetze in DNF ∪ durch ⊕. Dies ist erlaubt, da Minterme a, b die Bedingung a · b = 0 für a 6= b erfüllen. b) Ersetze x̄i durch (1 ⊕ xi ) c) Ausmultiplizieren d) Zusammenfassen a ⊕ a = 0; a · a = a Das Ergebnis ist eindeutig bestimmt und von der oben angegebenen Form. Zum Umgang mit dem exklusiven Oder ⊕ seien folgende Rechenregeln angegeben: • x ⊕ 1 = x̄, x ⊕ 0 = x • x ⊕ x = 0, x ⊕ x̄ = 1 • x⊕y = y⊕x • x ⊕ (y ⊕ z) = (x ⊕ y) ⊕ z • x · (y ⊕ z) = x · y ⊕ x · z • 0⊕0⊕...⊕0 = 0 ( 1 wenn n ungerade • 1⊕1⊕...⊕1 = 0 wenn n gerade n ist dabei die Anzahl der Einsen. Beispiel: Normalformen DNF, KNF und KRE Zum Abschluss dieses Abschnitts werden die drei Normalformen durch ein Beispiel illustriert. Sei hierzu die Schaltfunktion durch folgende Funktionstabelle gegeben: 62 Kapitel 4. Bausteine und Komponenten von Rechensystemen x2 0 0 0 0 1 1 1 1 x1 0 0 1 1 0 0 1 1 x0 0 1 0 1 0 1 0 1 f 0 1 0 0 0 1 0 1 Disjunktive Normalform Minterme zu den einschlägigen Indizes sind: m1 (x2 , x1 , x0 ) = x̄2 · x̄1 · x0 m5 (x2 , x1 , x0 ) = x2 · x̄1 · x0 m7 (x2 , x1 , x0 ) = x2 · x1 · x0 ⇒ DNF( f ) = x̄2 · x̄1 · x0 + x2 · x̄1 · x0 + x2 · x1 · x0 Konjunktive Normalform KNF( f ) = (x2 + x1 + x0 ) · (x2 + x̄1 + x0 ) · (x2 + x̄1 + x̄0 ) · (x̄2 + x1 + x0 ) · (x̄2 + x̄1 + x0 ) Komplementfreie Ringsummenentwicklung KRE( f ) = x̄2 · x̄1 · x0 ⊕ x2 · x̄1 · x0 ⊕ x2 · x1 · x0 = (1 ⊕ x2 ) · (1 ⊕ x1 ) · x0 ⊕ x2 · (1 ⊕ x1 ) · x0 ⊕ x2 · x1 · x0 = x2 ⊕ x1 · x0 ⊕ x2 · x0 ⊕ x2 · x1 · x0 ⊕ x2 · x0 ⊕ x2 · x1 · x0 ⊕ x2 · x1 · x0 = x0 ⊕ x1 · x0 ⊕ x2 · x1 · x0 Es gilt: ϕ(DNF( f )) = ϕ(KNF( f )) = ϕ(KRE( f )) 4.3 Synthese von Schaltkreisen, Minimierung Für jede Schaltfunktion gibt es unendlich viele Boolesche Ausdrücke und damit unendlich viele Schaltkreise, welche sie realisieren. Beispiel: Darstellung von Schaltfunktionen x¯1 · x2 + x1 · x¯2 + x1 · x2 und x1 + x2 sind Ausdrücke für dieselbe Schaltfunktion OR. Um den einfachsten und billigsten Booleschen Ausdruck und den ihn realisierenden Schaltkreis (bzgl. AND, OR und NOT ) zu finden, muss man ein Kostenmaß definieren. Wir setzen dazu (sehr vereinfachend): Kosten Boolescher Ausdrücke = Anzahl der Eingänge in AND- beziehungsweise OR-Gatter des entsprechenden Schaltkreises. Die Negationen, Überkreuzungen, Leitungsduplikationen, Leitungslängen, Ausgän- 4.3. Synthese von Schaltkreisen, Minimierung 63 ge, Gatter usw. bleiben unberücksichtigt. Definition: Kostenfunktion Eine Kostenfunktion k : B(X) → N0 sei definiert durch: • k(xi ) = k(0) = k(1) = 0 (Leitungen sind kostenlos) • k(w0 + . . . + wr−1 ) = k(w0 · . . . · wr−1 ) = ∑r−1 i=0 k(wi ) + r r = Anzahl der Eingänge in das Gatter dieser Stufe • k(w̄) = k(w) (Negation ist kostenlos) Nach dieser Definition kostet eine Konjunktion (AND) beziehungsweise Disjunktion (OR) mit r Eingängen r Einheiten zuzüglich der Kosten vorgeschalteter Schaltkreise (Gatter). Beispiel: Kosten von Schaltfunktionen k(x1 · (x2 + x3 ) · x4 + x1 ) = k(x1 · (x2 + x3 ) · x4 ) + k(x1 ) + 2 = k(x1 ) + k((x2 + x3 )) + k(x4 ) + 3 + k(x1 ) + 2 = k(x1 ) + k(x2 ) + k(x3 ) + 2 + k(x4 ) + 3 + k(x1 ) + 2 = 0+0+0+2+0+3+0+2 = 7 Wie oben schon erwähnt gibt es zu einer Schaltfunktion unendlich viele Boolesche Ausdrücke a0 , a1 , . . . mit ϕ(ai ) = f . Das Allgemeine Minimierungsproblem besteht darin einen Ausdruck a∗ ∈ ϕ−1 ( f ) mit den geringsten Kosten bezüglich obiger Definition: k(a∗ ) ≤ k(b) für alle b ∈ ϕ−1 ( f ) zu finden. Dieses Problem ist bisher jedoch ungelöst. Deshalb wird im nächsten Abschnitt eine eingeschränkte Variante mit Lösungsansätzen vorgestellt. 4.3.1 Eingeschränktes Minimierungsproblem Das Ziel beim eingeschränkten Minimierungsproblem ist die Bestimmung einer billigsten zweistufigen Realisierung, das heißt, eine Realisierung, bei der nur maximal zwei AND- bzw. OR-Gatter hintereinander durchlaufen werden. Die konjunktive und die disjunktive Normalform gehören zu diesen zweistufigen Realisierungen. Im Folgenden beschränken wir uns auf zweistufige Realisierungen, welche aus der disjunktiven Normalform ableitbar sind. Hierbei wird erst ein AND- und dann ein OR-Gatter durchlaufen. Solche Realisierungen heißen Polynome. Definition: Minimalpolynom P(X) ist die Menge der polynomialen Booleschen Ausdrücke über den Booleschen Variablen X = {x0 , . . . , xr−1 } : εi εi r−1 } P(X) = {p | p = a0 + . . . + am−1 ; p = 1; p = 0 mit ai = xi00 · . . . · xir−1 64 Kapitel 4. Bausteine und Komponenten von Rechensystemen ai heißt Monom. Ein billigstes Polynom zur Realisierung von f heißt Minimalpolynom von f . Ein Monom a heißt Implikant von f genau dann, wenn ϕ(a) ≤ f (≤-komponentenweise) Beispiel: Minimalpolynom Sei die Schaltfunktion f durch die folgende Funktionstabelle gegeben. x2 0 0 0 0 1 1 1 1 x1 0 0 1 1 0 0 1 1 x0 0 1 0 1 0 1 0 1 f 0 0 1 1 1 0 1 0 ϕ(a1 ) 0 0 0 0 1 0 1 0 ϕ(a2 ) 0 0 0 0 0 1 0 0 Aus der Funktionstabelle folgt: a1 = x2 x̄1 x̄0 + x2 x1 x̄0 = x2 x̄0 und a2 = x2 x̄1 x0 a1 ist Implikant von f ; a2 nicht, da ϕ(a2 (1, 0, 1)) = 1 0 = ϕ( f (1, 0, 1)). Alle Minterme zu den einschlägigen Indizes einer Schaltfunktion sind auch Implikanten derselben. Kürzere Implikanten überdecken mehr Einsen von f und sind billiger als längere. Da ein Polynom von f gerade eine Überdeckung von f durch Implikanten ist, werden für eine billige Realisierung kürzeste Implikanten gesucht. Definition: Primimplikant a heißt Primimplikant von f genau dann, wenn a Implikant von f und kein Teilmonom von a Implikant ist. Beispiel: Implikanten von Schaltfunktionen Seien x · α · y und x · ᾱ · y Implikanten von f . Es gilt also: ϕ(x · α · y) ≤ f und ϕ(x · ᾱ · y) ≤ f Dann erhält man einen minimalen Implikanten von f : ⇒ ϕ(x · α · y + x · ᾱ · y) ⇒ ϕ(x · (α + ᾱ) · y) ⇒ ϕ(x · y) 4.3.2 ≤ ≤ ≤ f f f Kosten: 2 · (k(x) + k(y) + k(α)) + 8 Kosten: k(x) + k(y) + 2 Quine-McCluskey-Algorithmus Es wird nun ein Algorithmus vorgestellt, der eine Lösung des eingeschränkten Minimierungsproblems ist. Der Algorithmus besteht aus zwei Phasen. Phase a) Bestimme alle Primimplikanten von f . 4.3. Synthese von Schaltkreisen, Minimierung 65 Phase b) Setze hieraus eine billigste Überdeckung von f zusammen, das heißt, suche eine billigste Gesamtüberdeckung der Minterme (ohne jene, die durch Don’t Cares entstanden sind) durch geschickte Auswahl einer Menge von Primimplikanten. Phase a): Prinzip zum Auffinden von Primimplikanten Starte mit Mintermen und Don’t Cares von f . Nutze dabei die Nachbarschaftsbeziehung – wie oben gezeigt x · y = x · α · y + x · ᾱ · y – so oft wie möglich aus. Dieses Verfahren liefert alle Primimplikanten. Zur Systematik: 1. Ersetze xiεi durch εi und ergänze fehlende Variablen durch „–“ an der entsprechenden Stelle. x̄5 x3 x̄0 entspricht „0 − −1 − 0“. 2. Nachbarn sind nur in Monomen möglich, die sich bezüglich ihres Gewichts, d.h. der Anzahl der enthaltenen Einsen, um genau Eins unterscheiden. Daher sollte man die Monome nach dem Gewicht vorsortieren. Dazu kann z.B. das HammingGewicht eines binären Vektors vi genutzt werden: n Weight = w(v) = ∑ vi i=1 Phase b): Überdeckung von f aus Primimplikanten 1. Man erstellt eine Matrix mit • Primimplikanten p j als Zeilen und • Mintermen mi der DNF (ohne Don’t Cares) als Spalten. p1 p2 p3 .. . m1 m2 m3 . . . 1 1 1 1 Der Matrixeintrag a jr = 1 genau dann, wenn der Primimplikant p j den Minterm mr überdeckt, d.h. der Minterm stimmt in allen Komponenten mit dem Primimplikanten überein. An Stellen, wo der Primimplikant ein − hat, kann im Minterm an gleicher Stelle beliebiges (1 oder 0) stehen. 66 Kapitel 4. Bausteine und Komponenten von Rechensystemen Beispiel: Überdeckungsmatrix 1 − 1− überdeckt 1110, 1010, 1011 und 1111. 00− überdeckt nicht 1011, denn es muss gelten: ϕ(ā · b̄) ≥ ϕ(a · b̄ · c · d). Mit a = 1, b = 0, c = 1 und d = 1 ist die „rechte Seite“, aber nicht die „linke Seite“ erfüllt. 2. Ein Primimplikant heißt wesentlich genau dann, wenn es einen Minterm mi gibt, der nur von diesem Primimplikanten überdeckt wird. In der entsprechenden Matrixspalte befindet sich nur eine 1. Ein wesentlicher Primimplikant muss zum Minimalpolynom gehören, er ist unverzichtbar. Alle zusätzlich überdeckten Minterme der Matrix sind zu streichen, weil sie schon überdeckt sind. 3. Ist ϕ(mi ) ≤ ϕ(m j ), so streiche m j , denn jede Überdeckung von mi liefert m j mit. p1 p2 p3 p4 .. . . . . mi . . . m j 1 1 1 ... 1 1 4. Ist ϕ(pr ) ≤ ϕ(ps ), wörtlich ps leistet mehr als pr , und k(pr ) ≥ k(ps ), so streiche pr , denn ps ist billiger und leistungsfähiger. m1 m2 m3 . . . .. . pr .. . ps .. . 1 1 1 1 1 Mit den Schritten 2, 3 und 4 (evtl. iteriert) wird die Bestimmung einer Überdeckung oft ein eindeutig bestimmter Vorgang. Manchmal müssen aber mehrere Möglichkeiten geprüft und eine insgesamt billigste Lösung ausgewählt werden. Beispiel: Gray-Code-Addierer In diesem Beispiel sollen Boolesche Ausdrücke für die Realisierung eines Addierers zweistelliger Gray-Code-Zahlen x1 x0 und y1 y0 mittels Quine-McCluskey-Algorithmus bestimmt werden. 4.3. Synthese von Schaltkreisen, Minimierung y1 y0 c 67 x1 z1 x0 z0 Die Schaltfunktion sei durch folgende Funktionstabelle gegeben: x1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 x0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 y1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 y0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 c 0 0 0 0 0 0 0 1 0 0 1 1 0 1 1 1 z1 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 0 z0 0 1 1 0 1 1 0 0 1 0 0 1 0 0 1 1 Schaltfunktion und Minimalpolynom für c: Sei In die Tabelle der Implikanten der Länge n, wobei sich die Länge aus der Anzahl an 0-en und 1-en ergibt. Die Minterme sind in der Tabelle zeilenweise nach Gewicht sortiert. I4 1001 1010 0110 1011 1110 1111 I3 10 − 1 101− 1 − 10 −110 1 − 11 111− I2 1 − 1− Man vergleiche jeden Minterm einer Gewichtsgruppe mit jedem der darunter befindlichen Gruppe. Falls sich diese an genau einer Stelle unterscheiden, so schreibe man an diese Stelle einen −: 1001 und 1011 aus I4 führen zu 10 − 1 in I3 . Befinden sich in den Mintermen −, so müssen diese an den gleichen Stellen auftreten: 1 − 10 und 1 − 11 aus I3 ergibt 1 − 1− in I2 . Als Primimplikanten erhält man diejenigen Implikanten, welche nicht weiter reduziert werden können, hier: 10 − 1, −110 aus I3 und 1 − 1− aus I2 . 68 Kapitel 4. Bausteine und Komponenten von Rechensystemen Die Überdeckungsmatrix lautet: 10 − 1 −110 1 − 1− 1001 1 1010 0110 1011 1 1 1 1 1110 1111 1 1 1 Wie man erkennt, sind alle Primimplikanten wesentlich. Das Minimalpolynom ist also: MPc = x1 · y1 + x1 · x̄0 · y0 + x0 · y1 · ȳ0 Schaltfunktion und Minimalpolynom für z1 : Zunächst die Primimplikanten ermitteln: I4 I3 I2 0010 001− 0 − 1− 1000 0 − 10 1 − 0− 1 − 00 100− 1100 110− 11 − − 0101 11 − 0 −1 − 1 1001 −101 1 − −1 0011 01 − 1 − − 11 0110 1 − 01 −11− 10 − 1 0 − 11 −011 011− −110 1101 11 − 1 0111 −111 1011 1 − 11 1110 111− 1111 Überdeckungsmatrix: 0010 1000 1100 0101 1001 0110 0011 1011 1101 0111 1110 1111 0 − 1− 1 1 1 1 1 − 0− 1 1 1 1 11 − − 1 1 1 1 −1 − 1 1 1 1 1 1 − −1 1 1 1 1 − − 11 1 1 1 1 −11− 1 1 1 1 Wesentliche Primimplikanten sind: 0 − 1−, 1 − 0− und −1 − 1. Sie überdecken alle Minterme bis auf 1011 und 1110. Nach Streichen der bisher überdeckten Minterme bleibt: 4.3. Synthese von Schaltkreisen, Minimierung 1011 11 − − 1 − −1 − − 11 −11− 69 1110 1 1 1 1 Jede Überdeckung benutzt zwei Primimplikanten und ist gleich teuer, so dass die Wahl beliebig ist. Das Minimalpolynom hat die Form: MPz1 = x̄1 · y1 + x1 · ȳ1 + x0 · y0 +Y mit Y ∈ {x1 · x0 + x1 · y0 , x1 · x0 + y1 · y0 , x0 · y1 + x1 · y0 , x0 · y1 + y1 · y0 } Schaltfunktion und Minimalpolynom für z0 : Die Primimplikanten ergeben sich durch folgende Tabelle: I4 0100 0001 0101 0011 1100 1010 1011 1110 I3 010− −100 0 − 01 00 − 1 11 − 0 101− 1 − 10 −011 Alle Implikanten der letzten Spalte sind Primimplikanten. Überdeckungsmatrix: 010− −100 0 − 01 00 − 1 11 − 0 101− 1 − 10 −011 0100 1 1 0001 0101 1 1 1 1 0011 1100 1010 1011 1 1 1 1110 1 1 1 1 1 1 1 In jeder Spalte stehen zwei, d.h. mehr als eine, Einsen, also liegt kein wesentlicher Primimplikant vor. Da kein Primimplikant wesentlich ist (alle gleich teuer), beginnen wir mit 010−: 70 Kapitel 4. Bausteine und Komponenten von Rechensystemen 010− −100 0 − 01 00 − 1 11 − 0 101− 1 − 10 −011 0100 1 1 0001 0101 1 1 1 1 0011 1100 1010 1011 1 1 1 1110 1 1 1 1 1 1 1 Von 101−, 1 − 10 und −011 wählt man 101−, weil dieser Primimplikant 1010 und 1011 überdeckt und diejenigen Minterme, welche 1 − 10 und −011 überdecken, bereits abgedeckt sind. Wesentliche Primimplikanten sind somit: 010−, 00 − 1, 11 − 0 und 101−. Ausgehend von 010− (entspricht x0 ) ergibt sich folgendes eindeutiges Minimalpolynom: MPz0 = x̄1 · x0 · ȳ1 + x̄0 · x̄0 · y0 + x1 · x0 · ȳ0 + x1 · x̄0 · y1 Beginnt man mit −100 statt mit 010−, dann entsteht in eindeutiger Weise: MPz0 = x0 · ȳ1 · ȳ0 + x̄1 · ȳ1 · y0 + x̄0 · y1 · y0 + x1 · y1 · ȳ0 Beide Minimalpolynome sind gleich teuer. 4.3.3 Karnaugh-Diagramme Ein Karnaugh-Diagramm (siehe Abbildung 4.1) einer Booleschen Funktion f : Bn → B mit n ∈ {3, 4} ist eine graphische Darstellung der Funktionstabelle von f durch eine Matrix der Größe 2 × 4 für n = 3 bzw. 4 × 4 für n = 4, deren Spalten mit den möglichen Belegungen der Variablen x0 und x1 und deren Zeilen mit den Belegungen der Variablen x2 (n = 3) bzw. x2 und x3 (n = 4) beschriftet sind. Die Reihenfolge der Beschriftung erfolgt so, dass sich zwei zyklisch benachbarte Zeilen/Spalten nur in genau einer Komponente unterscheiden (Gray-Code). x1x0 00 x2 01 11 10 x1x0 x3x2 00 0 00 1 01 01 11 10 Abbildung 4.1: Karnaugh-Diagramme 11 10 4.4. Schaltwerke und Speicherelemente 71 Vorgehen zur Bestimmung eines Minimalpolynoms • An den Matrixelementen, deren Beschriftung ein einschlägiger Index für die Funktion f ist, eine 1 eintragen. • Maximale 1-er-Blöcke der Form 2s × 2r finden, so dass jede 1 mindestens einmal überdeckt ist. Dabei müssen auch zyklische Überdeckungen beachtet werden. • Summe über den Termen, die den Blöcken entsprechen, bilden. • Falls möglich, zusätzlich Don’t Cares ausnutzen, indem im ersten Schritt in den entsprechenden Feldern auch der Wert 1 zugeordnet wird. Damit sind mehr Einsen vorhanden und evtl. größere Resolutionsblöcke möglich. Don’t Cares brauchen allerdings nicht vollständig überdeckt zu werden. Beispiel: Karnaugh-Diagramm x1x0 00 x2 01 11 1 0 1 1 D 10 x1x0 x3x2 00 00 D 01 1 01 10 10 1 1 11 1 11 x2x0 1 1 1 x1x0 D x2x1x0 1 D x2x1 f1 = x1x0 + x2x1 f2 = x2x0 + x2x1x0 4.4 Schaltwerke und Speicherelemente Die bisherigen Betrachtungen waren weitgehend theoretischer Natur. Für die Realisierung von Schaltfunktionen wurde der Begriff des Booleschen Ausdrucks eingeführt sowie eine Minimierung dessen bezüglich einer einfachen Kostenfunktion. Des Weiteren wurden Schaltkreise betrachtet, wobei die Zeit außerhalb der Betrachtung blieb. In diesem Abschnitt wird eine Taktung eingeführt, um die Schaltfunktion in ihrer Realisierung als Schaltwerk zu diskreten Zeitpunkten auf die Eingangsargumente anzuwenden. 72 Kapitel 4. Bausteine und Komponenten von Rechensystemen Definition: Schaltkreis und Schaltwerk Schaltkreise bestehen aus Eingangsleitungen, Schaltnetz und Ausgangsleitungen. Schaltwerke bestehen aus Schaltkreisen, Speicherelementen und Taktleitungen (siehe Abbildung 4.2) Takt sonstige Eingangsleitungen Speicherelemente Schaltkreise sonstige Ausgangsleitungen Abbildung 4.2: Schaltkreis und Schaltwerk Schaltwerke befinden sich zu gegebener Zeit in einem Zustand Q. Ein solcher Zustand ist etwa durch die Werte der Ausgangsleitungen zu diesem Zeitpunkt charakterisiert. Zustände ändern sich nur zu bestimmten diskreten Zeitpunkten, bei Eintreffen eines Taktes (siehe Abbildung 4.3), das heißt, wenn eine 1 auf der Taktleitung anliegt. T Flanke T=1 1 T=0 Zeit 0 Abbildung 4.3: Takt 4.4.1 Speicherelemente Binäre Speicherelemente sind kleinste logische Bausteine zur Aufbewahrung (Speicherung) einer Information. Es wird hier nur einer der Zustände 0 oder 1 gespeichert. Sie sind als Schaltwerke realisiert. Dabei ist ein Zustand Q(n) durch den gespeicherten Wert und durch die Werte auf den Ausgangsleitungen im n-ten Taktzyklus bestimmt. 4.4. Schaltwerke und Speicherelemente 73 Der gespeicherte Wert steht über eine Ausgangsleitung zur Verfügung; meist gibt es eine zusätzliche Leitung, an der Qn anliegt. RS-Flipflop Dieses Speicherelement besitzt zwei besondere Eingänge zum Setzen (auf 1; Set) und Rücksetzen (auf 0; Reset) des Inhalts (siehe Abbildung 4.4). Der Name Flipflop rührt von der Geräuschentwicklung der ersten Relais-Schaltwerke her, die zur Realisierung von Speicherelementen verwendet wurden. R Q(n) S T Q(n+1) Abbildung 4.4: RS-Flipflop Sei das RS-Flipflop durch folgende Funktionstabelle beschrieben. R 0 0 1 1 S Qn+1 0 Qn 1 1 0 0 1 Don’t Care Sei T der Takt mit den beiden Zuständen: T 0 1 Auswirkung Zustand ändert sich nicht Zustand kann sich ändern Unter Einbeziehung des jeweils aktuellen Zustandes Q(n) ergibt sich folgende Tabelle für die Schaltfunktion von Q(n+1) , wenn ein Takt anliegt (T = 1), sonst (T = 0) ändert sich nichts: 74 Kapitel 4. Bausteine und Komponenten von Rechensystemen S Qn Qn+1 0 0 0 0 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 0 Don’t Care 1 1 Don’t Care R 0 0 0 0 1 1 1 1 Die Schaltfunktion für Q(n+1) ergibt sich zu Q(n+1) = T̄ · Q(n) + T · (S + R̄ · Q(n) ) = T̄ · Q (n) (n) + T · S + T · R̄ · Q = T̄ · Q(n) + T · S + R̄ · Q(n) (Torschaltung, if T = 0 then . . . else . . . ) + T̄ · R̄ · Q(n) = (T̄ + R̄) · Q(n) + T · S = (T · R) · Qn · T · S (NAND − Darstellung) Analog lässt sich für Qn+1 eine Schaltfunktion angeben Qn+1 = (T · S) · Qn · (T · R) R T Q S Q Abbildung 4.5: Schaltung des RS-Flipflops Entscheidend für die Speichereigenschaft des Elements ist die Rückkopplung innerhalb der Schaltung (siehe Abbildung 4.5). Der konstante Wert Q zirkuliert, solange T nicht geändert wird. In Abbildung 4.6 sind zwei unterschiedliche Symbole für das RS-Flipflop dargestellt. 4.4. Schaltwerke und Speicherelemente R T 75 S S T RS-FlipFlop Q Q Q Abbildung 4.6: Schaltsymbole für das RS-Flipflop Earle Latch Beim Earle Larch handelt es sich um ein vereinfachtes Speicherelement (siehe Abbildung 4.7). Es speichert den alten Zustand solange T = 0 und übernimmt den neuen Zustand von der Eingangsleitung D, sobald T = 1. Dabei darf sich D nicht ändern, während T = 1 ist. T D Earle Latch Q Abbildung 4.7: Earle Latch ( Qn Q(n+1) = D falls T = 0 falls T = 1 Die Schaltfunktion wird durch folgenden Booleschen Ausdruck beschrieben: Q(n+1) = T̄ · Q(n) + T · D = T̄ · Q(n) + T · D + D · Q(n) D · Q(n) ist mathematisch gesehen überflüssig, da er in den beiden anderen Termen abgedeckt wird. Er wird jedoch für die technische Realisierung — insbesondere im Hinblick auf den Takt — benötigt (siehe Abbildung 4.8). 76 Kapitel 4. Bausteine und Komponenten von Rechensystemen D T Q Abbildung 4.8: Schaltung des Earle Latch Verwendung des Zusatzterms D · Q(n) : Hierfür sind zwei Gründe zu nennen: 1) Hazards Eine Realisierung wird nie perfekt dem theoretischen Modell genügen. So können bei der Taktung Ungenauigkeiten (Laufzeitunterschiede) auftreten (siehe Abbildung 4.9). T Zeit Es kann kurzzeitig T=T=0 sein! Abbildung 4.9: Hazards 2) Nicht perfekte Taktflanken Ein weiteres Problem besteht darin, dass die Taktflanken, die beim Wechsel des Taktes von T = 0 auf T = 1 entstehen, nicht senkrecht sind. Daher ist die Interpretation des Wertes von T ungenau über eine Zeitspanne nicht vernachlässigbarer Länge (siehe Abbildung 4.10). 4.4. Schaltwerke und Speicherelemente 77 Zeit T=0 T undef. T=1 Abbildung 4.10: Taktflanke Ohne D · Q(n) könnte nach der ersten Taktphase (T = 1) kurzzeitig T = T̄ = 0 gelten und damit Q(n+1) = T̄ · Q + T · D = 0 unabhängig von D, in der zweiten Taktphase (T̄ = 1) gilt: Q(n+1) = T̄ · Q ∪ T · D = 0 (unabhängig von D). 78 Kapitel 4. Bausteine und Komponenten von Rechensystemen KAPITEL 5 Die Arithmetisch-Logische Einheit In diesem Kapitel werden Rechenwerke für die Addition, Multiplikation und Division besprochen. Dabei werden insbesondere für die Addition verschiedene Methoden vorgestellt. Zentraler Bestandteil der Arithmetisch-Logischen Einheit (Arithmetic-Logical Unit, ALU) ist eine schnelle Hardware zur Ausführung der arithmetischen Grundfunktionen • Addition (Subtraktion) • Multiplikation • Division Interessant sind neben der Ausführungsgeschwindigkeit auch die Kosten dieser Rechenwerke. Beide Kriterien lassen sich durch die Wahl geeigneter Methoden zur Addition, Multiplikation und Division entscheidend beeinflussen. Die einfachste Additionsmethode bei Stellenwertcodierung im 2-Komplement ist aus der Schule bekannt. Man addiert zwei ganzzahlige, n-stellige Summanden komponentenweise und reicht den Übertrag zur nächsten Komponente weiter: Summand 1: Summand 2: Summe: an−1 bn−1 sn−1 ... ... ... an−2 bn−2 sn−2 a0 b0 s0 Ein Addierwerk, das diese Methode realisiert, ist in Abbildung 5.1 zu sehen. Beispiel: Addition nach Schulmethode + (1) 1 1 0 0 0 1 79 1 0 0 1 1 0 0 1 1 80 Kapitel 5. Die Arithmetisch-Logische Einheit an-1 bn-1 + a1 b1 a0 b0 + + ... sn=cn-1 sn-1 c1 s1 c0 s0 Abbildung 5.1: Addition nach Schulmethode 5.1 Addition und Subtraktion Im Folgenden werden zunächst mit dem Halbaddierer (Halfadder, HA) und dem Volladdierer (Fulladder, FA) zwei Additionskomponenten eingeführt, aus denen sich viele Rechenwerke zusammensetzen lassen. Einige dieser Rechenwerke werden dann im Anschluss diskutiert. 5.1.1 Halbaddierer a b HA c s Abbildung 5.2: Symbol für einen Halbaddierer Der Halbaddierer wird als Schaltkomponente durch das in Abbildung 5.2 dargestellte Symbol notiert. Er besitzt zwei Eingänge a und b, für welche die Summe s und der Übertrag c (Carry) berechnet werden soll. Um den Halbaddierer durch eine Schaltung zu realisieren, sollen zunächst Boolesche Ausdrücke für c und s entwickelt werden. Deren Funktionstabellen sind gegeben durch: a 0 0 1 1 b 0 1 0 1 s 0 1 1 0 c 0 0 0 1 5.1. Addition und Subtraktion 81 Hieraus ergeben sich die Minimalpolynome als c = a·b s = a·b+a·b ( ≡ a⊕b ) Obwohl es sich um eine Minimalpolynomdarstellung handelt, ist diese Darstellung nicht kostenminimal: Laufzeit (Gatterstufen) Kosten nach Def. aus Kap. 4 (Zahl der Eingänge) Übertrag c 1 2 Summe s 2 6 Eine bessere Realisierung wird erreicht durch: s = a · b + a · b ≡ (a + b) · (a + b) ≡ (a + b) · c, da c = a · b Man beachte, dass es sich nicht um ein Minimalpolynom handelt, weil kein klammerfreier Ausdruck vorliegt. Laufzeit (Gatterstufen) Kosten nach Def. aus Kap. 4 (Zahl der Eingänge) Übertrag c 1 2 Summe s 2 4 Der Halbaddierer lässt sich somit durch die in Abbildung 5.3 dargestellte Schaltung realisieren. a b c s Abbildung 5.3: Schaltung für den Halbaddierer 5.1.2 Volladdierer Der Volladdierer verfügt über einen weiteren Eingang cin . Dieser ist verwendbar, um einen Übertrag aus der Summierung der vorhergehenden Komponenten zu verarbeiten. Wie in Abbildung 5.4 gezeigt, kann ein Volladdierer durch zwei Halbaddierer realisiert werden (Klammerung: (a + b) + cin ). 82 Kapitel 5. Die Arithmetisch-Logische Einheit a b cin HA1 a b cin FA HA2 cout cout s s Abbildung 5.4: Schaltung und Schaltsymbol eines Volladdierers Die Kosten ergeben sich aus den Kosten der Halbaddierer und des zusätzlichen OR-Gatters: KostenFA = 2 · KostenHA + 2 = 14 Die Laufzeit hängt ebenso von der der Halbaddierer ab und beträgt 4 Stufen, wenn alle Eingänge gleichzeitig anliegen, oder 2 Stufen, wenn cin später eintrifft. Logik des Volladdierers Als Boolesche Ausdrücke für den Übertrag und die Summe erhält man: cout = 1 ⇔ a + b + cin ≥ 2 s = 1 ⇔ a + b + cin = 1 mod 2 also: cout = a · b + (a ⊕ b) · cin = a · b + (a + b) · cin s = (a ⊕ b) ⊕ cin = a · b · cin + a · b · cin + a · b · cin + a · b · cin Die Kosten betragen k(s) = 16, das heißt dieser Ausdruck ist für eine praktische Realisierung nicht empfehlenswert. 5.1.3 Carry-Ripple-Addierer Der Carry-Ripple-Addierer ist ein Addierwerk, welches gemäß Abbildung 5.5 aus n in Serie geschalteten Volladdierern aufgebaut ist. Es handelt sich um einen Paralleladdierer. 5.1. Addition und Subtraktion 83 Addiert wird in der 2-Komplement-Darstellung. Eine Subtraktion kann ebenfalls durchgeführt werden, indem alle Bits des zweiten Summanden b gekippt und c−1 auf 1 gesetzt werden. Also: a + b setzt c−1 := 0 und a − b setzt c−1 := 1 voraus. an-1 bn-1 an-2 bn-2 FAn-1 FAn-2 sn-1 a0 b0 ... sn-2 c-1 FA0 s0 Abbildung 5.5: Carry-Ripple-Addierwerk Logik Der Boolesche Ausdruck für den Übertrag ci zum Volladdierer FA(i+1) lautet: ci = ai · bi + (ai ⊕ bi ) · ci−1 = ai · bi + (ai + bi ) · ci−1 Sei zur Abkürzung: ki := ai · bi (Konjunktion) und di := ai + bi (Disjunktion) definiert. Dann hat der Boolesche Ausdruck die Form: ci = ki + di · ci−1 Die verwendete Rekursionsformel hat ihren Start mit c−1 und ist eine Funktion der Form ci = f (ki , di , ci−1 , . . . , c−1 ) für i = 0, . . . , n − 1. Laufzeit Die Rekursionsformel zeigt, dass sich Zwischenergebnisse durch Eintreffen eines Übertrags von rechts ändern können. Dies führt in der Schaltung zu einer Laufzeiterhöhung. Die maximale Laufzeit beträgt etwa 2 · n Gatterstufen. Dies ist der Fall, wenn ein Übertrag ganz rechts entsteht und durch alle Stellen propagiert (fortgeleitet) wird. Beispiel: Subtraktion mit hoher Laufzeit 84 Kapitel 5. Die Arithmetisch-Logische Einheit Sei b = (000101011) von a = (100101011) zu subtrahieren. Also ist c−1 = 1, b wird gekippt und auf a addiert (2-Komplement): + (1) 1 1 1 0 1 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 1 0 0 c−1 = 1 Eine Propagationskette ist ein Abschnitt zwischen zwei Stellen, an denen beide Summanden die gleichen Werte haben. Innerhalb einer solchen Kette wird ein auftretender Übertrag weitergeleitet. Beim Komponentenpaar (0, 0) wird der Übertrag aufgefangen und beim Paar (1, 1) entsteht sowieso ein Übertrag, d.h. ankommender ein Übertrag von rechts muss nicht mehr weitergeleitet werden. Beispiel: Propagationsketten Die Zahlen (0101101101) und (0110100111) haben folgende Propagationsketten: 0 1 0 1 1 0 1 1 0 1 0 1 1 0 1 0 0 1 1 1 2 4 2 3 3 Die Laufzeit beträgt 4 · 2 + 2, da die maximale Propagationskette die Länge 4 hat. Die zusätzliche 2 ergibt sich aus der Tatsache, dass der rechte Volladdierer zwei Gatterstufen mehr braucht. Falls 0 und 1 gleich häufig und unabhängig voneinander auftreten, gilt: Die Länge der längsten Propagationskette ist im Mittel log2 (n) und im schlechtesten Fall n lang. 5.1.4 Serieller Addierer Die serielle Addition entspricht dem handschriftlichen Addieren mit Übertrag. Dieses Addierwerk enthält nur einen Volladdierer, in den die n-stelligen Binärzahlen komponentenweise geleitet werden. In jedem Takt werden Komponenten ai und bi addiert, die berechnete Summenkomponente si gespeichert und ein gegebenenfalls entstehender Übertrag c bei der Addition der Komponenten ai+1 und bi+1 berücksichtigt. Realisiert werden kann ein solches Addierwerk gemäß Abbildung 5.6, also mit einem Volladdierer, zwei n-stelligen Schieberegistern und einem 1-stelligen Register zur Aufnahme des Übertrags. an-1 .....a0 bn-1 .....b0 sn-1 .....s0 FA c Abbildung 5.6: Serielles Addierwerk 5.1. Addition und Subtraktion Mikroprogramm Der Vorgang des seriellen Addierens lässt sich durch ein so genanntes Mikroprogramm beschreiben. Sei hierzu Z eine Zählvariable, welche die Takte zählt. Sie wird mit n initialisiert. Die Steuervariable V gibt an, ob eine Addition (V = 0) oder Subtraktion (V = 1) durchgeführt werden soll, wodurch eine Berechnung in der 2Komplement-Arithmetik realisiert wird. Zu beachten ist, dass die in eckigen Klammern stehenden Befehle eines Mikroprogramms parallel ausgeführt werden, also in einem Takt. Mikroprogramm: Serielle Addition 0 : [ (an−1 . . . a0 ) := Summand1 ; (bn−1 . . . b0 ) := Summand2 ; Z := n; c := V ] 1 : [ bi := bi ⊕V , für i = 0, . . . , n − 1 ] // falls V =1 wird b komponentenweise gekippt // => Subtr. im 2-Komplement 2 : [ ai := ai+1 ; bi := bi+1 ( für i = 0, . . . , n − 2) // => Rechtsshift von a und b c := cFA (a0 , b0 , c); an−1 := sFA (a0 , b0 , c); Z := Z -1; bn−1 := 0 ] 3 : if Z > 0 then goto 2 4 : Stop In Programmzeile 2 werden (2 · n + 2) Operationen parallel ausgeführt. Beispiel: Serielle Addition Seien a = (011) und b = (010) zu addieren: 1.Takt (Z=3) 011 010 c = 0 2.Takt (Z=2) 1 | 01 0 | 01 3.Takt (Z=1) 01 | 0 00 | 0 Ergebnis (Z=0) 101 000 c=0 c=1 c=0 85 86 Kapitel 5. Die Arithmetisch-Logische Einheit 5.1.5 Von-Neumann-Addierer Bei dem Von-Neumann-Addierwerk handelt es sich um ein einfaches getaktetes Paralleladdierwerk (siehe Abbildung 5.7). Es ist aus n parallel arbeitenden Halbaddierern aufgebaut. Jedem Halbaddierer sind die Register ai und bi zugeordnet. Das Register 1 (an−1 . . . a0 ) dient der Speicherung der Zwischensumme. Es enthält auch die Endsumme. Das Register 2 (bn−1 . . . b0 ) speichert die von den Halbaddierern gebildeten Überträge. Summen und Überträge der HA werden solange in die Register 1 beziehungsweise Register 2 zurückgeschrieben, bis Register 2 den Wert Null enthält. an-1 an-2 HA HA bn-1 bn-2 a1 a0 HA HA b1 b0 c ... Abbildung 5.7: Von-Neumann-Addierwerk Beispiel: Von-Neumann-Addition mit Dezimalzahlen Sei dieses Verfahren auf zwei Dezimalzahlen angewandt. Sie befinden sich in den zwei Registern: a = (98725) b = (17282) Es ergeben sich folgende Registerinhalte, wobei die Berechnung einer Stelle mit Übertrag hervorgehoben ist: a b a b a b a b 1 1 0 1 0 9 1 0 1 1 0 1 0 8 7 5 0 5 1 6 0 7 2 9 1 0 0 0 0 2 8 0 0 0 0 0 0 5 2 7 0 7 0 7 0 Seien W(a) und W(b) die Dezimalwerte der Inhalte der Register a und b. Das Verfahren ist korrekt, weil die Invarianzeigenschaft W(a) + W(b) = Summenwert unab- 5.1. Addition und Subtraktion hängig vom aktuellen Takt gilt. Das Verfahren terminiert, weil pro Takt mindestens ein Bit des Registers b zu Null wird. Man kann zeigen, dass wenn Nullen und Einsen in a und b gleichverteilt und unabhängig voneinander sind, das Verfahren im Mittel nach log2 n Takten stoppt. Es benötigt maximal (n + 1) Takte. Bei der Subtraktion im 2-Komplement werden alle Bits von b gekippt und im zweiten Takt wird b0 = 1 gesetzt. Beispiel: Binäre Von-Neumann-Addition Für a = (11110101)2 = (−11)10 und b = (11100101)2 = (−27)10 ist a − b zu berechnen. Hierzu wird b zunächst invertiert: b′ = (00011010)2 . Nach von-Neumann: Takt 1 11110101 00011010 ———— Takt 2 11101111 00100001 c−1 = 1, da Subtraktion im 2-Komplement ———— Takt 3 11001110 01000010 ———— Takt 4 10001100 10000100 Gesamtübertrag ignorieren (2-Komplement) ———— Takt 5 00001000 00001000 ———— Takt 6 00000000 00010000 ———— Takt 7 00010000 Die Summe hat den Wert W(a) = 16. 00000000 Stopp, weil Register 2 den Inhalt (0 . . . 0) hat. Realisierung und Ablaufsteuerung durch ein Mikroprogramm Wie für die serielle Addition wird nun für die Von-Neumann-Addition ein Mikroprogramm angegeben. Anschließend wird dieses in einer Diodenmatrix fest verdrahtet. Das Programm addiert (bzw. subtrahiert) binäre Zahlen in der 2-KomplementDarstellung. Um Überläufe zu erkennen, wird das Vorzeichen verdoppelt. Wiederum werden die in eckigen Klammern stehenden Befehle parallel ausgeführt. Mikroprogramm: Von-Neumann-Addition 0 : [ A=(an−1 . . . a0 ) := Summand1 ; B=(bn−1 . . . b0 ) := Summand2 ; V := 0 , falls Addition ; V := 1 , falls Subtraktion ] 1 : [ bi := bi ⊕ V für i = 0, . . . , n − 1 ] // falls V =1 wird b komponentenweise gekippt 2 : if B ∪ V 6= 0 then [ ai := ai ⊕ bi für i = 0, . . . , n − 1; 87 88 Kapitel 5. Die Arithmetisch-Logische Einheit bi+1 := ai · bi für i = 0, . . . , n − 2; b0 := V ; V := 0; goto 2 ] else [ V := an−1 ⊕ an−2 ; goto 3 ] 3 : if V 6= 0 then goto 5 4 : Stop ohne Überlaufwarnung 5 : Stop mit Überlaufmeldung Ablaufsteuerung Für die Steuerung des Ablaufs eines Programms in hardwarenaher Form sind folgende Schritte umzusetzen: a) Es muss festgestellt werden, welcher Takt vorliegt. b) Es muss erkannt werden, welcher Teiltakt vorliegt (und welche Bedingungen aufgrund der Registerzustände gegeben sind). c) Es ist die Nummer des folgenden Taktes zu berechnen. d) Die Mikrooperationen (Mikrobefehle) sind auszuführen. e) Die durch die Mikrooperationen beeinflussten Register sind zu takten. Folgeadresse Taktmatrix Steuermatrix Adresse Bedingungen Befehlsausführung Decodierung Eingabe Startadresse (zu Beginn) Abbildung 5.8: Ablaufsteuerung Ablaufsteuerungen werden durch Diodenmatrizen oder PLA’s (Programmable Logic Array) realisiert. Dies sind Felder sich kreuzender Leitungen (siehe Abbildung 5.9). Die Ausgangsleitungen stehen orthogonal zu den Eingangsleitungen. Eingangswerte werden mittels spezieller Dioden auf die Ausgangswerte umgesetzt. Der verknüpfende Kreis zwischen Eingangs- und Ausgangsleitungen bildet ein OR, das heißt, eine auf der Eingangsleitung anliegende Eins wird auf die gesamte Ausgangsleitung übertragen. Der Strich hingegen hat den Charakter eines logischen AND: Die Ausgangsleitung hat nur dann den Wert Eins, wenn mindestens alle über den Strich an sie angeschlossenen Eingangsleitungen eine Eins haben. Diesen Schritten sind in einer fest verdrahteten Realisierung eines Mikroprogramms folgende Teilmatrizen zugeordnet (siehe Abbildung 5.8): 5.1. Addition und Subtraktion = Und 89 = Oder (Keine Diode am Kreuzungspunkt => keine Wirkung) a b c Eingangsleitungen d e Abbildung 5.9: Dioden a) Decodiermatrix b) Bedingungsmatrix c) Adressmatrix für die Codierung der nächsten Programmadresse d) Steuermatrix e) Taktmatrix Als Beispiel wird nun das Mikroprogramm der Von-Neumann-Addition in eine Diodenmatrix umgesetzt (siehe Abbildung 5.10). Man trägt Bedingungen im Programm in die Bedingungsmatrix ein und verknüpft sie als Eingänge mit den vertikalen Eingangsleitungen. Zu den Eingängen zählen auch die Leitungen der Decodiermatrix. Sie liefern die Programmadresse des Mikroprogrammbefehls. Auch sie werden über Und-Dioden mit den vertikalen Leitungen verknüpft. Die Verknüpfungspunkte entsprechen dabei der binär codierten Adresse. Zu den Ausgangsleitungen zählen die Taktmatrix, Steuermatrix und die Adressmatrix. Die Leitungen dieser Matrizen werden über Oder-Dioden mit den vertikalen Eingangsleitungen verknüpft. Die Adressmatrix dient der Bestimmung der folgenden Programmadresse. In der Steuermatrix werden die Mikrooperationen der Mikroprogrammzeile angestoßen. Die Taktmatrix liefert den Takt für die in den angestoßenen Befehlen verwendeten Register. Die Ausgangsleitungen der letzten drei Matrizen sind deswegen durch Oder-Dioden mit den vertikalen Leitungen verknüpft, weil nur eine vertikale Leitung eine Eins zu führen braucht, um einen Mikroprogrammbefehl zu realisieren. 90 Kapitel 5. Die Arithmetisch-Logische Einheit Taktmatrix (Takten der angesprochenen Register) Steuermatrix (Anstoß zum Ausführen der angesprochenen Mikrooperationen) TA TB TV Signal “Überlauf” Signal “kein Überlauf” V := an-1 Å an-2 V := 0 b0 := V bi+1 := a i . bi ai := a i Å bi B := Summand 2 A := Summand bi := bi Å V Adressmatrix (Berechnung Folgeadresse) V=0 Bedingungsmatrix BÈV=0 Decodiermatrix: x0 Anfangsbesetzung (hier nicht gezeigt): x1 x2 x1 x0 = 000 x2 d.h. Startadresse = 0 0 1 2,then 3,then 4 2,else 3, else 5 Taktnummer (Adresse) Abbildung 5.10: Diodenmatrix 5.1.6 Erweiterungen des Von-Neumann-Addierers In diesem Abschnitt wird die Von-Neumann-Addition derart erweitert, dass man mehrere Summanden addieren kann. Dies wird durch Pipelining oder Parallelität (Adder-Tree) realisiert. Abbildung 5.11 zeigt eine abkürzende Notation eines VonNeumann-Addierwerks, die im Folgenden verwendet werden soll. Carry-Save-Addierer Eine erste Erweiterung besteht darin, im Von-Neumann-Addierer die Halbaddierer gegen Volladdierer auszutauschen. Aufgrund des somit gewonnenen Eingangs (siehe Kurznotation in Abbildung 5.12, insgesamt hat man nun 3 · n Eingänge und 2 · n Ausgänge) ist es möglich, nach jedem Takt einen neuen Summanden in das Addier- 5.1. Addition und Subtraktion 91 von-Neumann Kurznotation B ... HA HA A HA-Feld C S Abbildung 5.11: Kurznotation des Von-Neumann-Addierers werk einzubringen. neuer Summand (pro Takt je ein neuer Summand) FA-Feld Abbildung 5.12: Carry-Save-Addierer Das erhaltene Volladdiererfeld wird als Carry-Save-Addierer (CSA) bezeichnet (in der Kurznotation werden zur Vereinfachung auch Einfachpfeile verwendet). Die Addition von m Zahlen ist nun in m Takten möglich, wenn man diese wie folgt in den Addierer einbringt: Takt 1: Takt 2: Takt m − 2: Takt m − 1: Takt m: Die ersten drei Summanden werden bearbeitet. Summanden 1 bis 4 werden bearbeitet. .. . Summanden 1 bis m werden bearbeitet. Auslaufen der Addition nach Von-Neumann-Addition Abhängig von der Länge der größten Propagationskette, weiteres Auslaufen der Addition; bei Wortlänge n im Mittel (log2 n) Takte. Eine Anwendung besteht in der konventionellen Multiplikation durch eine Folge von 92 Kapitel 5. Die Arithmetisch-Logische Einheit Additionen (Schulmethode), wie im Abschnitt über die Multiplikation näher noch beschrieben wird. Zur Verdeutlichung dieses Prinzips soll an dieser Stelle die folgende Skizze genügen: Produkt • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • Adder-Tree (Wallace Tree) Um einen Adder-Tree zur parallelen (und somit beschleunigten) Addition aufzubauen, bedarf es der Verwendung mehrerer CSAs, die miteinander verschaltet sind. Hierzu wird die Taktung aufgetrennt und es werden so viele CSAs hintereinandergeschaltet, wie aufgrund der Summandenzahl erforderlich ist (siehe Abbildung 5.13). Summand s1 s2 s3 CSA 1 s4 neuer Summand CSA 2 CSA Takt ... sm CSA m-2 abschließende Addition Abbildung 5.13: Lineare Verschaltung von CSAs Auf diese Weise hat man bezüglich der Laufzeit keinen Gewinn gemacht. Ordnet man 5.1. Addition und Subtraktion 93 jedoch die CSAs in einem Baum an, so ist es möglich, die Addition in jeder Schicht des Baums parallel durchzuführen (siehe Abbildung 5.14). ... ... Adder Tree Beispiel: m=12 CSA c s Abbildung 5.14: Wallace Tree Die Laufzeit des Adder-Trees für m Summanden ergibt sich aus der Tatsache, dass im Wesentlichen durch jeden CSA 3 Eingänge auf 2 Ausgänge abgebildet werden, wobei jedoch die Anzahl der Eingänge nicht immer ein Vielfaches von 3 sein muss. Sei m die Anzahl aller Eingänge (an den Blättern des Baums oben). Dann beträgt die Anzahl der Ausgänge nach der i-ten Stufe ungefähr ( 32 )i · m. Für die Laufzeit ist die Stufenzahl x gesucht, so dass nach der x-ten Stufe zwei Ausgänge vorliegen: ( 23 )x · m = 2. Man erhält: x · log2 ( 23 ) = log2 ( m2 ) ⇒ x = 5.1.7 log2 ( m2 ) log2 ( 23 ) ≈ 1, 72 · log2 m. Carry-Look-Ahead-Addierer Die bereits kennen gelernte Carry-Ripple-Addition ist langsam, weil sie n Volladdierer in Serie verwendet. Hierbei entsteht ein Übertragsengpass, weil jeder Volladdierer auf den Übertrag seines rechten Nachbarn warten muss, um sein korrektes Ergebnis bilden und weiterleiten zu können (siehe Abbildung 5.5). 94 Kapitel 5. Die Arithmetisch-Logische Einheit Seien di := ai + bi und ki := ai · bi wiederum als Abkürzungen der Disjunktion bzw. Konjunktion zweier Stellen definiert. Im Abschnitt 5.1.3 wurde bereits eine Rekursionsformel für den Übertrag an der Stelle i hergeleitet: ci = ai · bi + (ai + bi ) · ci−1 = ki + di · ci−1 Die Rekursionsformel für den Übertrag wird nun aufgelöst, um die Addition durch direkte Berechnung des Übertrags ci aus den (2 · k + 1) Werten ci−k , a j , b j für j = (i − k + 1), . . . , i zu beschleunigen: ci = ki + di · ci−1 = ki + di · (ki−1 + di−1 · ci−2 ) = ki + di · ki−1 + di · di−1 · ci−2 = ... = ki + di · ki−1 + di · di−1 · ki−2 + di · di−1 · di−2 · ki−3 + . . . + di · . . . · d1 · k0 +di · . . . · d0 · c−1 Für große i ist diese Vorgehensweise zu teuer und unrealisierbar. Daher beschränkt man sich auf machbare Gruppen und verknüpft diese nach dem Prinzip der CarryRipple-Addition, wie in Abbildung 5.15 anhand eines 12-stelligen Addierwerks für eine Gruppengröße von 4 gezeigt. a3 b3 Gruppe 2 c11 Gruppe 1 c7 a0 b0 c-1 Gruppe 0 c3 s3 s2 s1 s0 Abbildung 5.15: Carry-Look-Ahead-Addierer für Gruppengröße 4 Ein Carry-Look-Ahead-Addierer arbeitet im Gegensatz zu einem Carry-RippleAddierer also nicht mit einzelnen Bits, sondern mit ganzen Bitgruppen. Der Übertrag c7 beispielsweise hat die Form: c7 = k7 + d7 · k6 + d7 · d6 · k5 + d7 · d6 · d5 · k4 + d7 · d6 · d5 · d4 · c3 Die Laufzeit wird jeweils im Worst-Case von 2 · n bei der Carry-Ripple-Addition auf 2 · gn bei der Carry-Look-Ahead-Addition verringert, wobei g die Gruppengröße und gn die Gruppenanzahl ist. n bezeichnet wie oben die Anzahl der Stellen der zu 5.1. Addition und Subtraktion 95 addierenden Zahlen und somit die Anzahl der Eingänge des Addierers. Sie wird um den Faktor der Gruppengröße verkürzt. Die erzielte Beschleunigung wird bezahlt durch • höhere Kosten innerhalb der Gruppen und • zusätzliche Vorbereitungslaufzeiten, um ki und di bereitzustellen, weil diese sofort benötigt werden. Diese Vorbereitungslaufzeiten wirken sich allerdings erst dann aus, wenn man das Prinzip auf die 2. Ordnung erweitert, das heißt zur Bildung von Supergruppen übergeht, indem man Carry-Look-Ahead-Addierer hintereinanderschaltet. 5.1.8 Carry-Skip-Addierer Die Carry-Skip-Addition ist eine Verbesserung der Addition nach dem Carry-LookAhead-Prinzip. Die aufwändige Schaltlogik der Carry-Look-Ahead-Addition ist nicht in jeder der ng Gruppen nötig. Man berechnet die Überträge einer Gruppe wie bei Carry-Ripple in maximal 2 · g Gatterstufen und leitet die Überträge beschleunigt durch einen einfachen Zusatzschaltkreis über die Gruppen hinweg. Carry-Skip für eine konstante Gruppengröße g Sei j ∈ {0, . . . , ng − 1} der Gruppenindex. Ferner seien folgende Abkürzungen eingeführt: • D j := d( j+1)g−1 · . . . · d jg und • K j := k( j+1)g−1 + d( j+1)g−1 · k( j+1)g−2 + . . . d( j+1)g−1 · . . . · d jg+1 · k jg Hierbei sind di und ki wie üblich definiert: di := ai + bi , ki := ai · bi Ist K j = 1, so ist in Gruppe j ein Übertrag entstanden. Es gilt für den Gesamtübertag c j der Gruppe j: c j = K j + D j · c j−1 Bei Carry-Skip wird ein Übertrag c j einer Gruppe wie folgt berechnet, wobei zwei Fälle unterschieden werden: a) D j · c j−1 = 0 und K j = 1 (Übertrag entsteht erst in Gruppe j): Dann ist c j = K j = 1 und wird wie bei Carry-Ripple in maximal 2 · g Gatterstufen berechnet. 96 Kapitel 5. Die Arithmetisch-Logische Einheit b) D j · c j−1 = 1 (Übertrag wird weitergeleitet): Hier liegt der für die Gesamtlaufzeit ungünstigste Fall vor. Der Übertrag wird mit D j in zwei zusätzlichen Gatterstufen weitergeleitet. Abbildung 5.16 zeigt die Schaltung einer Gruppe des Carry-Skip-Addierers. ajg bjg Dj cj-1 cj FAg Kj ... FAg-1 s(j+1)g-1 s(j+1)g-2 FA1 FA2 sjg+1 sjg Abbildung 5.16: Gruppe eines Carry-Skip-Addierers Betrachten wir nun die Laufzeit und Kosten des Addierers. Der Worst-Case der Laufzeit liegt dann vor, wenn: a0 = b0 = 1, c−1 = 0 und für alle i = 1, . . . , (n − 2) gilt : ai ⊕ bi = 1. Das bedeutet, der Übertrag entsteht nur in Gruppe 0, wird nicht sofort weitergeleitet (da D0 · c−1 = 0) und läuft maximal lange. In diesem Fall ist die Laufzeit: τ = 2·g+1 Berechnung von K0 , c0 + 2 · ( gn − 2) Übertragsweiterleitung über ( gn − 2) innere Gruppen + 2·g Berechnung von sn−1 τmin entsteht als Minimum (nach g ableiten, gleich Null setzen,. . .) für: g = gopt = r n 2 Man erhält: √ √ τmin = 4 · 2 · n − 3 Die Kosten lassen sich direkt aus der Schaltung ablesen (siehe Abbildung 5.16). Zusätzlich zu den im Volladdierer enthaltenen OR-Gattern benötigt man pro Gruppe ein AND-Gatter mit g Eingängen für D j und zwei Gatter (OR, AND) mit jeweils zwei Eingängen für die Ermittlung von c j . Die Kosten kCarry−Skip ergeben sich somit aus den Kosten kCarry−Ripple und den Zusatzkosten der Gatter: n 4·n 4·n kCarry−Skip = kCarry−Ripple + · (g + 2 · 2) = 14 · n + n + = 15 · n + g g g 5.1. Addition und Subtraktion Carry-Skip mit variabler Gruppengröße Aus dem letzten Abschnitt wird ersichtlich, dass folgender ungünstiger Fall auftreten kann: Der Übertrag entsteht in der Gruppe ganz rechts (in Abbildung 5.16), läuft schnell über die inneren Gruppen und langsam in der letzten Gruppe ganz links. Eine Idee zur Behebung der Laufzeitunterschiede zwischen den an den Rändern und in der Mitte liegenden Gruppen besteht nun darin, kleinere Gruppen an den beiden Rändern und höhere Gruppengrößen zur Mitte hin zu wählen. Dabei ist zu beachten, dass der Größenunterschied von Gruppe zu Gruppe nicht beliebig sein darf: Falls die Gruppengröße um mehr als 1 zwischen zwei benachbarten Gruppen differiert, entsteht ein anders gearteter ungünstigster Fall. Beispiel: Laufzeiten bei variabler Gruppengröße Sei i eine Gruppe der Größe i. Unter den Kasten wird die Laufzeit innerhalb der Gruppe notiert. Die Gesamtlaufzeit wird für den Worst-Case angenommen. Fall 1: Drei gleich große Gruppen 7 7 7 14 2 15 Gesamtlaufzeit: 31 Fall 2: Drei Gruppen abnehmender Größe 8 7 6 16 2 13 Gesamtlaufzeit: 31 16 15 Gesamtlaufzeit: 31 Fall 3: Abweichung der Gruppengröße um mehr als 1 9 7 5 18 2 11 Gesamtlaufzeit: 31 18 15 Gesamtlaufzeit: 33 Somit liegt hier ein neuer Worst-Case vor, wenn man in der zweiten Gruppe beginnt. Daher darf nur eine Steigerung der Gruppengröße von beiden Rändern nach innen pro Gruppe um jeweils den Wert 1 stattfinden. Dies ist aber nicht für alle Längen zu addierender Zahlen perfekt möglich: Die Summe der Gruppengrößen muss n ergeben, und gleichzeitig muss der Addierer laufzeitoptimal sein. Daher müssen am Rand gegebenenfalls einzelne Kleingruppen entfernt werden. Einige Beispiele zeigen auch, dass unter Umständen eine Gruppe aus der Mitte wegzunehmen ist. Beispiel: Beispiele verschiedener Gruppengrößen Die Gruppen werden mit ihrer Größe in einem Tupel notiert. 1) Für n = 56 : Hier liegt einer der wenigen optimalen Fälle vor. Gruppengrößen: (1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1) Laufzeit: maximal 29 Gatterstufen. 2) Für n = 50 : In diesem Fall ist die Aufteilung nicht so günstig möglich. Man kann von der Situation im ersten Beispiel ausgehend die Gruppen an den Rändern entfernen: (3, 4, 5, 6, 7, 7, 6, 5, 4, 3) oder die drei Gruppen ganz links streichen: (4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1) oder die rechten drei Gruppen wegnehmen: 97 98 Kapitel 5. Die Arithmetisch-Logische Einheit (1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4). In allen drei Fällen hat man die gleiche Laufzeit von 29 Gatterstufen wie bei n = 56. 3) Für n = 44 : Hier liegt ein Fall vor, bei dem man ausgehend von (1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1) zum einen die Gruppen an den Rändern streichen und die mittlere 7er- gegen eine 6er-Gruppe tauschen sollte: (2, 3, 4, 5, 6, 6, 6, 5, 4, 3) oder an einem Rand Gruppen streicht und in der Mitte zwei Gruppen verkleinert: (3, 4, 5, 6, 6, 5, 5, 4, 3, 2, 1). Die Laufzeit beträgt 27 Gatterstufen wie für n = 49. Die Laufzeit τn eines n-Bit-Carry-Skip-Addierers mit variabler Gruppengröße ist maximal für: ( √ 2 · ⌈ 4 · n + 1⌉ − 1 falls m2 < n ≤ m · (m + 1) , m ∈ N √ τn = 2 · ⌈ 4 · n⌉ − 1 falls (m − 1) · m < n ≤ m2 , m ∈ N √ Insgesamt gilt also: τn ≈ 4 · n. Im Vergleich dazu war die minimale Laufzeit bei konstanter Gruppengröße: √ √ τkonst. min = 4 · 2 · n − 3. Gemessen in unserem Kostenmaß aus Kapitel 4 sind die Kosten fast gleich. Verallgemeinerungen und Kompromisse Man kann Gruppen von Carry-Skip-Addierern hintereinander schalten und erhält eine so genannte Supergruppe. Aus mehreren solcher Supergruppen lässt sich dann ein Carry-Skip-Addierer zweiter Ordnung zusammensetzen (siehe Abbildung 5.17). Abbildung 5.17: Schaltung zum Carry-Skip-Addierer zweiter Ordnung Eine Ordnungserhöhung lohnt sich aber nur für sehr große Längen der zu addierenden Zahlen. Sonst fällt der Gewinn geringer aus als der Aufwand für die zusätzliche 5.1. Addition und Subtraktion Logik, die zur Koordination nötig ist. Ferner lässt sich auch die Technik des Carry-Skip-Addierers mit der eines CarryLook-Ahead-Addierers kombinieren (siehe Abbildung 5.18). Die Anzahl der zusätzlichen Gatter ist hier jedoch größer. Abbildung 5.18: Kombination von Carry-Skip und Carry-Look-Ahead 5.1.9 Conditional-Sum-Addierer Wie bei der Carry-Look-Ahead-Addition werden hier ganze Gruppen bearbeitet, allerdings in verschiedenen Gruppengrößen, nämlich zuerst in 1er, dann in 2er, 4er,. . . Gruppen. Es findet also immer eine Verdopplung der Gruppengröße statt, die durch die Zusammenfassung zweier benachbarter Gruppen erfolgt, wie in Abbildung 5.19 skizziert. Diese Zusammenfassung kann parallel realisiert werden. Abbildung 5.19: Gruppen bei der Conditional-Sum-Addition Die Conditional-Sum-Addition zeichnet sich dadurch aus, dass sie bei der Zusammenfassung zweier Gruppen die Entstehung eines Übertrags explizit berücksichtigt. Eine Gruppe wird durch 4 Einträge repräsentiert: 99 100 Kapitel 5. Die Arithmetisch-Logische Einheit c0 c1 | | S0 S1 ← 0: Kein Carry von rechts ← 1: Carry von rechts mit c0 , c1 ∈ {0, 1} und S0 , S1 ∈ {0, 1}∗ . Die Länge von S0 und S1 variiert je nach Gruppengröße. Bei der Betrachtung einer Gruppe werden zwei Fälle unterschieden: 1. Für den Fall, dass die rechte Nachbargruppe keinen Übertrag liefert, lautet das Ergebnis der betrachteten Gruppe (c0 , S0 ), wobei c0 den Übertrag und S0 die Summenbits darstellen. 2. Für den Fall, dass die rechte Nachbargruppe einen Übertrag weitergibt, lautet das Ergebnis der betrachteten Gruppe (c1 , S1 ). Zusammenfassung zweier Gruppen Die Zusammenfassung zweier Gruppen geht sehr systematisch. Die Gruppen A und B werden zu einer neuen Gruppe AB wie folgt zusammengefasst: Gruppe A c0A | SA0 c1A | SA1 c0AB c1AB Gruppe B c0B | SB0 c1B | SB1 Neue Gruppe AB 0 | SAB 1 | SAB ← 0: Kein Carry von rechts ← 1: Carry von rechts ← 0: Kein Carry von rechts ← 1: Carry von rechts ∗ ist dabei die Summe der Längen von S∗ und S∗ . Die Länge von SAB B A Betrachten wir zunächst die obere Zeile der neuen Gruppe, d.h. den Fall, in dem 0 können dann die rechts kein Übertrag anliegt. Als rechte Hälfte der Summenbits SAB Summenbits SB0 der Gruppe B übernommen werden. Nun ist zu unterscheiden, ob Gruppe B einen Übertrag an Gruppe A weiterleiten würde, was durch c0B angezeigt 0 die Bits S0 und als wird. Ist c0B = 0 werden als linke Hälfte der Summenbits SAB A Übertrag c0AB das Bit c0A übernommen. Ist c0B = 1 muss für die Gruppe A ein einge0 wird S1 und als hender Übertrag berücksichtigt werden, d.h. als linke Hälfte von SAB A Übertrag c0AB wird c1A übernommen. Die untere Zeile der neuen Gruppe ergibt sich analog. 5.1. Addition und Subtraktion 101 Die formalen Regeln für die Zusammenfassung zweier Gruppen lauten somit (hierbei sei ◦ der Konkatenationsoperator): 0 cA , falls c0B = 0 0 cAB = 1 cA , falls c0B = 1 c1AB = 0 cA , falls c1B = 0 0 SAB = 0 0 SA ◦ SB , falls c0B = 0 1 SAB = 0 1 SA ◦ SB , falls c0B = 0 Oder kürzer (für i ∈ {0, 1}): c1A , falls c1B = 1 SA1 ◦ SB0 , falls c0B = 1 SA1 ◦ SB1 , falls c0B = 1 ci ciAB = cAB ci i SAB = SAB ◦ SBi Die Angabe einer Schaltlogik für diese Zusammenfassung ist relativ einfach. In der Stufe 0 ist jede Gruppe ein Bitpaar (ai , bi ). Die Gruppengröße ist 1. Die Logik für die Stufe 0 lautet daher: c0 = ai · bi c1 = ai + bi S0 = S1 S1 = ai ⊕ bi ⊕ 1 = ai · bi + ai · bi = c0 + c1 Die Logik höherer Stufen bedeutet die Umsetzung der Fallunterscheidungen durch eine Torschaltung: x := wird durch die Schaltung α, falls γ = 0 β, falls γ = 1 x := γ · α + γ · β 102 Kapitel 5. Die Arithmetisch-Logische Einheit realisiert. Die Kosten dieser Schaltung nach der Definition aus Kapitel 4 betragen 6. Beispiel: Conditional-Sum-Addition Die Zahlen a = (1101000110) und b = (0101011001) sind zu addieren (cin = 0) bzw. zu subtrahieren (cin = 1). Stufe 1 0 1 1 0 0 1 1 0 0 0 1 0 1 1 0 1 0 0 1 a b 0 0 1 1 0 1 0 1 1 0 0 0 1 1 0 1 1 0 0 0 1 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 cin = 0 cin = 1 1 1 1 00 01 0 0 11 00 cin = 0 cin = 1 2 1 1 00 01 0 0 1111 0000 cin = 0 cin = 1 3 1 1 00 01 0 0 10011111 10100000 cin = 0 cin = 1 4 1 1 0010011111 0010100000 cin = 0 cin = 1 10 11 0 0 01 10 0 1 1001 1010 0 1 11 00 0 1 Laufzeitbetrachtungen Dieser Additionsalgorithmus ist fast optimal, denn es ist beweisbar, dass (log2 n) eine untere Schranke für die Laufzeit der Addition zweier n-stelliger Zahlen ist. • Man benötigt (⌈log2 n⌉ + 2) Stufen für die Addition, wobei die letzte Stufe für die Auswahl des Endergebnisses nötig ist, das heißt, der Summe oder Differenz je nach cin . • Man braucht 2 · (⌈log2 n⌉ + 2) Gatterstufen, denn es sind für jede Stufe je ein AND- und ein OR-Gatter mit jeweils zwei Eingängen nötig. Neben der günstigen Laufzeit besteht ein weiterer Vorteil darin, dass gleichartige und billige Gatter verwendet werden (AND2 , OR2 ). Außerdem ist ein symmetrischer und synchroner Ablauf möglich. 5.2 Multiplikation In diesem Abschnitt werden Verfahren zur Multiplikation zweier Binärzahlen diskutiert. Zunächst wird die Multiplikation nach der Schulmethode betrachtet, anschließend werden beschleunigte Methoden vorgestellt. Die Darstellung der Binärzahlen erfolgt in der Betrag+Vorzeichen-Codierung. Sie eignet sich gut, weil die Multiplikation auf die Addition zurückgeführt wird. Das Vorzeichenbit der Produktbinärzahl ergibt sich durch Anwendung des XOR-Operators auf die Vorzeichenbits des Multiplikanden und Multiplikators. 5.2. Multiplikation 103 Folgende Bezeichnungen werden vereinbart: Multiplikand MD = (MDn−1 . . . MD0 ) Multiplikator MQ = (MQn−1 . . . MQ0 ) Faktor 1: Faktor 2: Beide Faktoren müssen vorzeichenlos sein. Um vorzeichenbehaftete Werte zu multiplizieren oder zu dividieren, geht man daher in drei Schritten vor: 1. Abfrage der Vorzeichen und Werte vorzeichenlos machen. 2. Multiplikation (Division) mit den vorzeichenlosen Werten. 3. Aus den bei Schritt 1 ermittelten Vorzeichen das Vorzeichen des Ergebnisses errechnen (XOR). 5.2.1 Serielle Multiplikation Eine erste Idee besteht darin, die Multiplikation auf eine Folge von Additionen zurückzuführen. Dazu wird die so genannte Multiplikationsmatrix gebildet, deren Zeilen addiert werden: MDi ·MQ j Produkt: • • • • • • • • • • • • • • • • • • • • • i • • • • • • • • • • • • • • • • • • • • j Beispiel: Multiplikationsmatrix Seien MD = 0111 und MQ = 1011. Für j ∈ {0, . . . , 3} schreibt man in die j-te Zeile der Matrix den um j Bits nach links verschobenen Wert von MD, falls MQ j = 1 ist. Sonst wird die Zeile mit entsprechend vielen Nullen besetzt. Anschließend werden die Zeilen komponentenweise addiert (Schulmethode). 0 0 1 0 1 0 0 0 1 0 0 1 0 1 1 1 1 0 1 1 1 1 0 1 Realisierung Für das doppelt so lange Produkt brauchen wir zwei miteinander verbundene Register der Länge n. Die einzelnen Additionen sind jeweils um ein Bit gegeneinander verschoben. Addiert wird: 104 Kapitel 5. Die Arithmetisch-Logische Einheit • Eine 0, falls das betreffende Multiplikatorbit MQ j 0 ist. • MD, falls das betreffende Multiplikatorbit 1 ist. Im Allgemeinen wird also MD · MQi addiert, wobei i die Nummer des aktuellen Multiplikatorbits ist. Um die oben skizzierte relative Verschiebung zu realisieren, kann man entweder das Zwischenergebnis festhalten und MD · MQi um ein Bit nach links verschieben, oder man hält MD · MQi fest und verschiebt das Zwischenergebnis um ein Bit nach rechts. Wir wählen die zweite Möglichkeit und verwenden das Register MQ als zweite Hälfte des Produktregisters. Dadurch wird der Multiplikator im Verlaufe der n Multiplikationsschritte überschrieben. Ferner steht das aktuelle Multiplikationsbit immer in MQ0 (siehe Abbildung 5.20). Zwischenprodukt (Zu Beginn = 0) MP MQ MQ0 + MD Abbildung 5.20: Schema zur seriellen Multiplikation Das Mikroprogramm für die serielle Multiplikation lautet: Mikroprogramm: Serielle Multiplikation 0 : [ MD := Multiplikand ; MQ := Multiplikator ; MP := 0; Z := n ] 1 : [ Z := Z -1; if MQ0 =1 then MP := MP + MD ] 2 : [ shr( MP, MQ ); if Z > 0 then goto 1 ] 3 : Stop Hierbei bezeichnet MP das Partialprodukt. Die Operation shr(MP, MQ) veranlasst einen Rechtsshift des gekoppelten Registers (MP, MQ) um 1 Bit, wobei links Nullen aufgefüllt werden (Betrag+Vorzeichen-Darstellung) und wird rechts rausgeschoben. Falls nicht Betrag+Vorzeichen-, sondern 2-Komplement-Darstellung verwendet wird, dann wird links das Vorzeichen nachgefüllt. Die 1-Komplement-Codierung ist wegen des End-Around-Carries ungeeignet. 5.2. Multiplikation 105 Beispiel: Serielle Multiplikation Seien MD = 0111 und MQ = 1011. Am Anfang ist MP = 0000. Vorbelegung MD : 0111 MP | MQ : 0000 | 1011 Z := 4 1. Durchlauf MP := MP + MD : 0111 | 1011 Z := 3 shr(MP, MQ) : 0011 | 1101 MP := MP + MD : 1010 | 1101 Z := 2 shr(MP, MQ) : 0101 | 0110 Nur shr(MP, MQ) : 0010 | 1011 Z := 1 MP := MP + MD : 1001 | 1011 Z := 0 2. Durchlauf 3. Durchlauf 4. Durchlauf 0100 | 1101 shr(MP, MQ) : Ergebnis: 01001101 5.2.2 Multiplikatorcodierung Die serielle Multiplikation kann dadurch beschleunigt werden, dass die Addition von Nullzeilen nicht ausgeführt wird (wie im obigen Mikroprogramm bereits realisiert). Ein erweiterter Ansatz besteht darin, dass ein Shift über Blöcke von Nullen und Einsen des Multiplikators durchgeführt wird. Hat der Multiplikator die Form MQ = . . . 01k 0 . . . (. . . , 0, k Einsen, 0, . . .), so kann man unter Zuhilfenahme einer aufwändigen Zusatzlogik k Zyklen zusammenfassen. Beispiel: Zusammenfassung von Zyklen Sei MQ = . . . 011110 . . ., wobei die rechte Eins die Nummer u ∈ {0, . . . , n − 1} im Bitwort trägt. Eine herkömmliche Berechnung hat die Form: ... 0 ( 1 | 8 + 1 | 4 + 1 | 2 + 1 | 1 0 ... ) ·2u · MD Insgesamt wird also 15 · 2u · MD addiert. Dies ist darstellbar als Addition von 16 · 2u · MD und anschließender Subtraktion von 1 · 2u · MD. Statt der vier Additionen wird man günstigerweise eine Subtraktion (−1) und eine Addition (+16) durchführen. Angenommen MQ habe die Form MQ = . . . 01k 0 . . . und man betrachtet MQ von 106 Kapitel 5. Die Arithmetisch-Logische Einheit links nach rechts. Trifft man auf die linke 0, so addiert man 2k · MD auf das Partialprodukt. Die ersten k − 1 Einsen werden anschließend überlaufen. Tritt die Situation auf, dass man auf einer 1 stehend als nächstes Bit eine 0 erhält, so subtrahiert man MD vom Partialprodukt. Bei der Realisierung dieses Vorgehens besteht ein Problem jedoch darin, zu erkennen, wie lang der Einser-Block ist. Eine einfache Variante der seriellen Multiplikation ist das Verfahren von Booth. Hierbei handelt es sich um ein serielles Prüfen des Multiplikators, wobei man sich nicht nur das aktuelle Bit von MQ (MQ0 ) ansieht, sondern zusätzlich das vorherige Bit, das hier mit MQ−1 bezeichnet werden soll. Die durchzuführenden Aktionen sind in folgender Tabelle zusammengefasst: MQ0 0 0 1 1 MQ−1 0 1 0 1 Wirkung shr (Shift über Nullen) +1· MD und shr −1· MD und shr shr (Shift über Einsen) Das Verfahren ist nur dann günstig, wenn in MQ längere Blöcke von Nullen oder Einsen auftreten. Es ist ungünstig, wenn Nullen und Einsen häufig wechseln. Das Verfahren von Booth bezeichnet man auch als Multiplikatorcodierung der Gruppengröße 1, da es einen Spezialfall einer allgemeineren Multiplikatorcodierung darstellt. Betrachten wir nun den allgemeinen Fall der Gruppengröße h: MQh−1 . . . MQ0 Für die Multiplikatorcodierung muss zusätzlich das Korrekturbit MQ−1 betrachtet werden: MQh−1 . . . MQ0 | MQ−1 Falls MQ(h−1) = 1, addiert man statt x = 2h−1 · MQh−1 + 2h−2 · MQh−2 + . . . + MQ0 x = 2h − 2h−1 · MQh−1 + 2h−2 · MQh−2 + . . . + MQ0 Dies wird jedoch auf zwei Zyklen verteilt, indem zunächst −2h−1 · MQh−1 + 2h−2 · MQh−2 +. . .+MQ0 addiert, anschließend um h Stellen nach rechts geshiftet und dann 1 · MD addiert wird. Die Information, ob eine Korrektur von 1 · MD aus dem vorherigen Zyklus durchgeführt werden muss, kann anhand von MQ0 abgelesen werden. Im zweiten Zyklus wird 2h also korrigiert durch: x′ = −2h−1 · MQh−1 + 2h−2 · MQh−2 + . . . + 2 · MQ1 + MQ0 + MQ−1 5.2. Multiplikation 107 Aufgrund des Shifts über die Gruppe wird durch MQ−1 die Korrektur vorgenommen. Das Verfahren von Booth arbeitet mit einer Gruppengröße von 1. Somit gilt hier x′ ∈ {−1, 0, 1}. Als ein weiteres Beispiel soll nun die Multiplikatorcodierung mit Gruppengröße 2 betrachtet werden. Hierbei fasst man zwei Multiplikatorbits (MQ1 , MQ0 ) zusammen. Dadurch gibt es nur die vier folgenden Möglichkeiten: (MQ1 , MQ0 ) (0, 0) (0, 1) (1, 0) (1, 1) Operation addiere 0 · MD addiere 1 · MD addiere 2 · MD addiere 3 · MD und schiebe (MP, MQ) anschließend um 2 Bits nach rechts. Die Anzahl der Additionen kann somit halbiert werden. Im Falle, dass 2 · MD addiert werden muss, führt man einen Linksshift von MD um ein Bit und eine anschließende Addition durch. Der kritische Fall ist der letzte, wenn (MQ1 , MQ0 ) = (1, 1) ist. Dann muss das Dreifache des Multiplikators addiert werden. Dieses Vielfache benötigt eine eigene Addition (2 + 1) zu seiner Berechnung. Eine effiziente Lösung besteht nun darin, 3 als 4 − 1 = (+1) · 22 + (−1) · 20 zu interpretieren. Die Wertigkeit von 22 ist im nächsten Zyklus, d.h. nach zweifachem Rechtsshift des Partialprodukts, 20 . Man wird im Fall (MQ1 , MQ0 ) = (1, 1) im Zyklus i fälschlicherweise (−1) · MD addieren. Dies wird im Zyklus (i + 1) korrigiert, indem dann 4 · MD zusätzlich zu der dort anliegenden Addition addiert wird. Aufgrund des Rechtssshifts über die Gruppenlänge (2 Bits) ist dann aber nur MQ−1 · MD zu addieren (MQ−1 war im Zyklus i MQ1 ; +4 entspricht im nächsten Zyklus, der eine um den Faktor 22 = 4 höhere Wertigkeit hat, genau dem Wert +1). Dieses Verfahren lässt sich auch anwenden, falls (MQ1 , MQ0 ) = (1, 0); hier wird im Zyklus i (−2) · MD addiert und im Zyklus (i + 1) durch MQ−1 · MD korrigiert: +2 = 4 Korrektur − 2 addiere (−2) · MD Insgesamt kann man immer dann so vorgehen, wenn MQ1 = 1 und somit im darauf folgenden Zyklus MQ−1 = 1 ist. Dies hat den Vorteil, dass nur ein Bit zu prüfen ist, statt wie im Fall (MQ1 , MQ0 ) = (1, 1) beide. Die folgende Tabelle zeigt die Aktionen, die in Abhängigkeit von (MQ1 , MQ0 , MQ−1 ) zu vollziehen sind. 108 Kapitel 5. Die Arithmetisch-Logische Einheit MQ1 MQ0 MQ−1 0 0 1 0 1 0 0 0 0 1 1 0 0 0 1 0 1 0 1 1 1 1 1 1 Operation: Addiere x · MD mit x = . . . 0 1 -2 (statt 2); spätere Korrektur: 4 -1 (statt 3); spätere Korrektur: 4 1 = 0 + Korrektur: 1 (4) 2 = 1 + Korrektur: 1 (4) -1 = -2 (statt 2) + Korrektur: 1 (4) spätere Korrektur: 4 0 -1 (statt 3) + Korrektur: 1 (4) spätere Korrektur: 4 Es gilt: x = (−2 · MQ1 + MQ0 + MQ−1 ) · MD mit −2 ≤ x ≤ 2. Somit sind alle x-Werte durch Inversion und Shift erhältlich. Beispiel: Multiplikatorcodierung mit Gruppengröße 2 MQ-1 MQ = 0 0 1 0 0 1 1 0 1 1 1 1 0 +1 -2 5.2.3 +2 -1 0 -1 = x Adder-Tree und Pipelining Eine weitere Möglichkeit, um eine Beschleunigung der Multiplikation zu erhalten, ist die Verwendung eines Adder-Trees, mit dem die benötigten Additionsschritte einer Multiplikationsoperation parallelisiert werden können. Hieru werde je drei Zeilen der Multiplikationsmatrix in CSAs eines Adder-Trees gegeben (siehe Abbildung 5.21). Diese Vorgehensweise eignet sich, um mehrere Multiplikationen nacheinander auszuführen. Ein Beispiel ist die Bildung der Summe ∑ki=1 ai · bi . Die Multiplikationen werden gemäß Abbildung 5.22 stufenweise durch den Adder-Tree geführt (Pipelining). Pipeline pro Einzelmultiplikation zusammen mit Multiplikatorcodierung Um für eine einzelne Multiplikation ähnlich vorgehen zu können, wird der Multiplikator (zum Beispiel 60 Bits lang) in Gruppen zerlegt. Die Gruppen werden nacheinander in den Adder-Tree gegeben. Dafür eignet sich besonders eine Gruppengröße von 12 Bits. Auf jede Gruppe wird die Multiplikatorcodierung angewendet, wodurch die Zahl der Vielfachen auf die Hälfte verringert wird, im Beispiel auf 5.2. Multiplikation 109 CSA Sehr schnell! Aber: Teuer; pro Multiplikation ist zu geg. Zeitpunkt nur eine Stufe des Adder-Trees aktiv ... CSA Adder Tree Abschließende Addition z.B. Carry-Look-Ahead Abbildung 5.21: Multiplikation als Addition im Adder-Tree Mult. (i+3) Mult. (i+2) Mult. (i+1) Mult. i Abbildung 5.22: Pipelining durch Adder-Tree 6. Die Gruppen (im Beispiel 5) werden in einen kleinen Adder-Tree (6 Eingänge) gegeben. Dieser Adder-Tree muss Rückkopplungen aufweisen, die den errechneten Wert (mit gleichzeitigem Rechtsshift um die Gruppengröße) mit dem nachfolgenden neuen Wert zusammen verarbeiten (siehe Abbildung 5.23). Von den Rückkopplungen hängt die Laufzeit ab. Ist die Rückkopplungsschleife bis oben zu den Blättern des Adder-Trees gelegt, so ist keine hohe Taktfrequenz möglich. Ein Adder-Tree mit kurzer Rückkopplungsleitung ist dagegen schneller. 110 Kapitel 5. Die Arithmetisch-Logische Einheit Zeit Gr 1 Gr 2 Gr 3 Gr 4 Gr 1 Gr 2 Gr 3 zusätzliche CSAs für Rückkopplungseingänge mit Shift um 12 Stellen Gr 1 Gr 2 + Gr 1 Rückkopplung Abbildung 5.23: Adder-Tree für Einzelmultiplikation 5.3 Division Wie bei der Multiplikation wird zunächst mit der seriellen Division ein einfaches Divisionsverfahren vorgestellt. In einem weiteren Abschnitt wird mit der iterativen Division eine wesentlich elegantere Methode betrachtet, welche für die iterative Berechnung von Quadratwurzeln modifiziert wird. Es seien folgende Abkürzungen eingeführt: DR: DD: DE: 5.3.1 Divisor Dividend Quotient Serielle Division Die serielle Division stellt eine Analogie zur seriellen Multiplikation dar. Sie entsteht durch Umkehrung des entsprechenden Vorgangs bei der Multiplikation (siehe Abbildung 5.24). Es besteht folgende Analogie: Produkt = Multiplikator Multiplikant Dividend = Quotient Divisor Das folgende Mikroprogramm beschreibt die Division nach der Schulmethode für den binären Fall. Seien (DD, DE) = [DDn−1 , . . . , DD0 , DEn−1 , . . . , DE0 ] das Dividendenregister, (DR) = [DRn−1 , . . . , DR0 ] das Divisorregister und (DE) = [DEn−1 , . . . , DE0 ] das Quotientenregister. (DD, DE) ist also doppelt so lang wie DR (ggf. auffüllen). Die Zahlendarstellung erfolgt im 2-Komplement. Ferner wird vereinbart, dass Dividend und Divisor normiert und positiv sind. Es gilt: 0 ≤ W(DD) < W(DR) < 1. 5.3. Division 111 MP MQ MQ0 serielle Multiplikation bedingte Addition MD doppelt langer Dividend DD bedingte Subtraktion DE Quotientenbit q0 serielle Division Divisor DR Abbildung 5.24: Serielle Multiplikation und Division Mikroprogramm: Division gemäß Schulmethode 0 : [ (DD,DE ) := Dividend ; DR := Divisor ; Z := n ] 1 : [ Z := Z - 1; if DD ≥ DR then q := 1; DD := DD - DR else q := 0 ] 2 : [ DE0 := q; if Z > 0 then shl ( DD, DE ); goto 1 else shl ( DE ) ] 3 : Stop Die wesentliche Fallunterscheidung lautet: Ist DD ≥ DR, so subtrahiere DR von DD und setze das Quotientenbit q0 gleich 1, ansonsten führe keine Subtraktion durch und setze q0 auf 0. Am Ende der Programmausführung steht im Register DD der Partialrest der Division, und DE enthält den Quotienten. Der Divisionsrest ist halb so groß wie der Partialrest, daher wendet man den letzten Shift nur noch auf das Quotientenregister DE an. Für die Korrektheit des Verfahrens wurde vorausgesetzt, dass 0 ≤ W(DD, DE) < W(DR) < 1 gilt. Diese Variante wird auch als Non-Performing-Division bezeichnet. 112 Kapitel 5. Die Arithmetisch-Logische Einheit Eine andere Variante ist die Non-Restoring-Division. Sie unterscheidet sich von obiger Variante dadurch, dass in Zeile 1 immer DR von DD subtrahiert wird und diese Subtraktion gegebenenfalls wieder rückgängig gemacht wird. Anstelle der Abfrage, ob DD ≥ DR ist, mit der Konsequenz • falls ja, Subtraktion und q := 1 • falls nein, nur q := 0 kann man auch wie folgt vorgehen (für W(DR) > 0): • falls W(DD, DE) ≥ 0, subtrahiere DR, ansonsten addiere DR • falls das Ergebnis (neuer Wert von (DD, DE)) ≥ 0, q := 1, ansonsten q := 0 • falls am Ende der entstehende Rest negativ ist, korrigiere durch Addition von DR. Mikroprogramm: Non-Restoring-Division 0 : [ (DD,DE ) := Dividend ; DR := Divisor ; Z := n ; DE0 := 1 ] 1 : [ Z := Z - 1; if DE0 = 1 then DD := DD - DR else DD := DD + DR ] 2 : [ DE0 := DDn−1 ; if Z > 0 then shl ( DD, DE ); goto 1 else shl ( DE ); if DDn−1 = 1 then DD := DD + DR ] 3 : Stop In Zeile 1 ist DE0 das umgekehrte Vorzeichen des vorherigen Quotientenbits. Korrektheitsüberlegungen Geht man davon aus, dass die erstgenannte Methode (Non-Performing) korrekt arbeitet, d.h. Zahlen richtig dividiert, so kann man zur Überprüfung der Korrektheit der zweiten Methode (Non-Restoring) diese an der ersten messen. Nach der ersten Methode führt man folgende Operationen aus: Quotientenbits 0 ... 0 ↓ ↓ Vergleich: Vergleich: Keine Keine Operation Operation Der Partialrest ist stets nicht negativ. 1 ↓ Vergleich: Subtraktion 1 ↓ Zuletzt: Subtraktion 5.3. Division 113 Nach der zweiten Methode geht man wie folgt vor: Quotientenbits Partialrest 1 ↓ positiv ↓ Subtraktion ... ↓ ... ↓ Addition 0 ↓ negativ ↓ Subtraktion 0 ↓ negativ ↓ Addition 1 ↓ positiv ↓ Addition Bei jedem Quotientenbit 1 sind die Partialreste beider Methoden gleich groß; an den Stellen, wo die Quotientenbits 0 sind, allerdings nicht: Position Quotientenbits (k − 1) 0 ↓ Keine Operation ... ... ↓ ... 1 0 ↓ Keine Operation Insgesamt ergibt sich: −20 · DR = −DR. Position Quotientenbits (k − 1) 0 ↓ Subtraktion ... ... ↓ Addition ... ... ↓ ... 0 1 ↓ Subtraktion 1 0 ↓ Addition 0 1 ↓ Addition Hier erhält man: (−2k−1 + 2k−2 + . . . + 20 ) · DR = −DR. Beispiel: Non-Restoring-Division (DD, DE) := (0.10001110010), W(DD, DE) = 569/210 ≈ 0, 55566 . . ., DR := 0.11110, W(DR) = 30/25 ≈ 0, 9375. Die Voraussetzung 0 ≤ W(DD, DE) < W(DR) < 1 ist erfüllt. DD DE 010001 110011 -DR 110011 100111 10011|0 +DR 000101 001011 0011|01 -DR 101101 011010 011|010 +DR 111000 110000 11|0100 +DR 001110 011101 1|01001 -DR 111111 010010 Shift von DE, da Rest negativ. +DR 011101 010010 Rest = 29 · 1 , Quotient = 18 · 215 = 0, 5625 25 25 114 Kapitel 5. Die Arithmetisch-Logische Einheit Beschleunigungsversuche Die Multiplikatorcodierung ist in vergleichbar einfacher und leistungsfähiger Form nicht auf die Division übertragbar (niemand hat bisher ein solches Konzept gefunden). Es besteht jedoch die Möglichkeit, spezielle Divisorvielfache zu verwenden. Die grundlegende Idee dabei ist, dass die Division schnell ist, wenn der neue Partialrest möglichst betragsklein ist. Falls sich DD (alter Partialrest) und DR stark unterscheiden, bringt der nächste Zyklus DD − DR oder DD + DR (oder NOOP) noch nicht viel im Sinne eines kleinen Partialrests. Stellt man den Divisor (DR) dem Dividenden (DD) in einer Matrix gegenüber, so kann man drei Fälle unterscheiden: DR DD |DD|≪DR: Bilde DD:=DD-0,5·DR, falls DD ≥ 0 DD+0,5·DR, falls DD < 0 Ist |DD|≈ DR: Operation wie üblich: DD := DD-DR, falls DD ≥ 0, DD := DD+DR, falls DD < 0. |DD|≫DR: Bilde DD:=DD-2·DR, falls DD ≥ 0 DD+2·DR, falls DD < 0 Man versucht also, ein geeignetes Vielfaches von DR einzusetzen. Beispiel: Divisorvielfache Sei 0 < W(DD) < W(DR). Bilde DD := DD − 0, 5 · DR und berechne zwei Quotientenbits (00 und 01) je nachdem, ob das Ergebnis negativ oder positiv ist. Hier ist das { 21 , 1, 2}System verwendet worden. Es gibt auch Ansätze, ein { 34 , 1, 23 }- oder ein { 58 , 1, 45 }-System zu verwenden. Dabei ist der letzte Ansatz besser als der zweite, und dieser ist besser als das { 21 , 1, 2}-System in folgendem Sinn: Der Betrag des neuen Partialrests ist im Mittel möglichst klein. Daher ist die Zahl der im Mittel pro Zyklus berechenbaren Quotientenbits bei dem { 85 , 1, 45 }-System am höchsten. Ein Kompromiss ist die Table-Look-Up-Methode. Sie arbeitet mit einer Matrix über Divisor und Dividend. Es wird ein Näherungsquotient aufgrund der ersten Bits von Dividend und Divisor bestimmt. Dieser Näherungsquotient kann als Startwert in iterativen Verfahren zur Division verwendet werden. Sollen k Bits berücksichtigt werden, müssen in der Tabelle 2k · 2k = 22k Elemente gespeichert werden (siehe Abbildung 5.25). 5.3. Division 115 Divisor Dividend 0. 0 ... 0 ... 0 ... 0.000 ... 0.yyy ... 0.111 0. x x x 0. ... 1 ... 1 ... 1 2 k Spalten, wenn k Bits berücksichtigt Näherungsquotient Abbildung 5.25: Table-Look-Up-Methode 5.3.2 Iterative Division In diesem Abschnitt wird ein iteratives Verfahren zur Division vorgestellt. Man geht dabei von der Rückführung der Division auf eine Folge von Multiplikationen und Additionen aus. Da die Division im Allgemeinen seltener benötigt wird als die Multiplikation, ist diese Rückführung der Konstruktion einer eigenen Divisionshardware vorzuziehen und rechtfertigt ein schnelles, aber teures Multiplizierwerk. Man geht von einem Startwert x0 aus und wendet auf jeden Wert xi eine rekursive Iterationsvorschrift ϕ an, welche den Wert xi+1 liefert: x0 → x1 := ϕ(x0 ) → x2 := ϕ(x1 ) → . . . → xi+1 := ϕ(xi ) → . . . Unter geeigneter Wahl des Startwertes konvergiert ϕ gegen einen Grenzwert. Gilt x̄ = ϕ(x̄), so heißt x̄ Fixpunkt von ϕ. Definition: Konvergenz mit Ordnung m Sei x0 ein geeignet gewählter Startwert und m ∈ N0 . ϕ konvergiert mit Ordnung m gegen einen Grenzwert x, wenn für den Iterationsfehler εi := xi − x gilt: εi+1 = O(εm i ). Ist m = 1, so konvergiert ϕ mit linearer Geschwindigkeit gegen den Grenzwert; ist m = 3, so konvergiert ϕ mit kubischer Geschwindigkeit gegen x̄. Beispiel: Regula Falsi Sei f eine Funktion, die eine Nullstelle besitzt. Man geht von zwei Stellen x0 und x1 aus, für die gilt: f (x0 ) < 0 und f (x1 ) > 0 oder umgekehrt. In jedem Schritt legt man eine Gerade durch die Punkte, welche durch die zwei Stellen und ihre Funktionswerte gebildet werden. Dann betrachtet man den Schnittpunkt der Geraden mit der x-Achse. Seien xi und xi+1 die betrachteten Stellen mit xi < xi+1 . Man wendet folgende Iterationsvorschrift an: if f (Schnittstelle) = 0 then Nullstelle := Schnittstelle; if f (Schnittstelle) < 0 then xi := Schnittstelle; 116 Kapitel 5. Die Arithmetisch-Logische Einheit if f (Schnittstelle) > 0 then xi+1 := Schnittstelle; Das Verfahren konvergiert mit linearer Geschwindigkeit, ist also von der Ordnung m = 1. Beispiel: Newton-Verfahren Dieses Verfahren dient ebenfalls der iterativen Ermittlung einer Nullstelle einer Funktion f . Man geht von einer Startstelle x0 aus und erhält xi+1 durch Anwendung der Iterationsvorschrift ϕ(x) = x − ff′(x) (x) auf xi (siehe Abbildung 5.26). f x x2 x1 x0 Abbildung 5.26: Newton-Verfahren Das Newtonverfahren konvergiert quadratisch, also mit Ordnung m = 2 gegen eine Nullstelle x̄ von f (x), sofern x0 geeignet gewählt wird und f ′ (x̄) 6= 0 ist. Das Newton-Verfahren wird nun verwendet, um ein elegantes Verfahren zur Division zu konstruieren. Dabei wird 1b aus b wie folgt errechnet: • Suche eine Funktion f (x), die • Berechne ϕ(x) = x − f (x) f ′ (x) 1 b als Nullstelle besitzt. auf xi , wobei ϕ(x) keine Division enthalten soll. • Führe die Iteration xi+1 = ϕ(xi ) so oft wie nötig durch, bis Konvergenz im Rahmen der erzielbaren Genauigkeit eintritt. Versuch 1: Geht man von der Funktion f (x) = x − 1b mit Nullstelle brauchbare Iterationsvorschrift: 1 b x − b1 f (x) 1 ϕ(x) = x − ′ = x− = f (x) 1 b Der Fixpunkt 1 b muss vorher bekannt sein. aus, so erhält man keine 5.3. Division 117 Versuch 2: Sei f (x) = − 1x + b. Diese Funktion hat auch die Nullstelle 1b . Die Iterationsvorschrift ϕ lautet: ϕ(x) = x − f (x) f ′ (x) = x− − 1x + b 1 x2 = x − (−x + b · x2 ) = 2 · x − b · x2 = (2 − b · x) · x Mit dieser Iterationsvorschrift hat man eine divisionsfreie Formel erhalten. Die Division ist hiermit auf zwei Multiplikationen und eine Subtraktion zurückgeführt worden, die zum Beispiel im 2-Komplement leicht zu handhaben ist. Für b ∈ [ 12 : 1] liegt beispielsweise die Mantisse einer normalisierten Gleitkommazahl vor. Die Divisionsvorschrift ist also: Startwert: x0 := 1 oder x0 := Wert nach Table-Look-Up (Näherungswert für 1b ) Iterationsvorschrift: ϕDiv (x) = (2 − b · x) · x Beispiel: Iterative Division Seien b = 0, 85; x0 = 1. Dann ergeben sich folgende Näherungswerte, wobei an diesen das quadratische Verhalten des Newton-Verfahrens zu erkennen ist: x1 = (2 − 0, 85 · 1) · 1 = 1, 15 x2 x3 = 1, 175875 = 1, 1764703 x4 = 1, 1764706. . . . 1 x4 stellt einen relativ genauen Wert von 0,85 dar. Ein besserer Startwert ist 0, 8, denn hier erhält man obige Näherungswerte schon einen Schritt früher: x1∗ = 1, 171875 x2∗ x3∗ = 1, 1764526 = 1, 1764705. . . . ϕDiv konvergiert quadratisch gegen 1b . Der Iterationsfehler des (i + 1)-ten Schrittes ist kleiner oder gleich Null (es liegt also quadratische Konvergenz von unten vor): 1 1 2 εi+1 = xi+1 − x̄ = (2 − b · xi ) · xi − = −b · xi − = −b · ε2i ≤ 0, falls 0 < b < 1. b b 118 Kapitel 5. Die Arithmetisch-Logische Einheit Anschaulich bedeutet dies eine Verdopplung der Zahl korrekter Bits (Stellen, Ziffern) bei jedem Übergang von xi zu xi+1 . Der (i + 1)-te Schritt bringt also soviel wie die ersten i Schritte zusammen. Da das Verfahren nur bei hinreichend genauem Ausgangswert konvergiert (dann allerdings schnell!), ist der Start mit einem guten Näherungswert aus der Table-Look-Up-Tabelle sehr zweckmäßig. Bei verkürzter ungenauer Berechnung der Multiplikationen xi+1 = (2 − b · xi ) · xi ist die Zeit pro Iterationsschritt kürzer. Die Konvergenzgeschwindigkeit ist dafür aber etwas schlechter als quadratisch. Für eine kürzere Multiplikation verwendet man die Schreibweise (2 − b · xi )Tr (Truncated). (2 − b · xi )Tr enthält nur die relevantesten Bits von (2 − b · xi ). Ein Vorteil besteht darin, dass (2 − b · xi )Tr mit Multiplikatorcodierung auf wenige, z.B. 6, Vielfache codiert und in einem kleinen Adder-Tree verarbeitet werden kann. Ferner wird die Bildung von (2 − b · xi ) vereinfacht. AEGP (Anderson-Earle-Goldschmidt-Power) Ein Nachteil der bisherigen Iterationsvorschrift ϕDiv (x) = (2 − b · x) · x besteht darin, dass die zwei aufeinanderfolgenden Multiplikationen pro Iterationsschritt eine Parallelisierung verhindern. Ziel ist es, eine gleichschnelle Konvergenz durch zwei parallele Multiplikationen zu erzielen. Eine Idee besteht darin, dass die erste Multiplikation entfällt, wenn b = 1 ist. Das Verfahren konvergiert dann für jeden sinnvollen Startwert zum Grenzwert 1. Das Verfahren lautet: d0 di+1 = = Startwert (2 − di ) · di Beispiel: Konvergenz von di+1 = (2 − di )di d0 d1 = 1, 5 = 0, 5 · 1, 5 = 0, 75 d2 = 1, 25 · 0, 75 = 1 − (0, 25)2 = 1 − 1 16 Ein unbrauchbarer Startwert wäre d0 = 5. Für die Berechnung eines Quotienten a b betrachtet man zwei parallele Folgen: • Folge 1 mit Startwert b und • Folge 2 mit Startwert a. Prinzipiell führt man solange eine Multiplikation der aktuellen Werte von Folge 1 und 2 mit dem gleichen Faktor durch, bis Folge 1 den Grenzwert Eins erreicht hat 5.3. Division 119 (im Rahmen der Genauigkeit). Wenn dies der Fall ist, dann wurde durch Folge 2 der Wert des Quotienten ab approximiert: a · r0 · r1 · . . . ri a a · r0 a · r0 · r1 = = = ... = b b · r0 b · r0 · r1 b · r0 · r1 · . . . ri a · r0 · . . . ri konvergiert also gegen ba , wenn b · r0 · . . . ri gegen 1 konvergiert. Es gilt insgesamt: Startwert Iterationsvorschrift Konvergenz Folge 1 Folge 2 d0 := b x0 := a di+1 := di · (2 − di ) xi+1 := xi · (2 − di ) quadratisch gegen dE = 1 xE = ba In einer Realisierung sind nun zwei Multiplikationen (di · (2 − di ) und xi · (2 − di )) pro Iterationsschritt umzusetzen. Sind zwei Multiplizierwerke vorhanden, so kann man die Multiplikationen parallel ausführen. Stattdessen könnte man auch das Pipelineprinzip verwenden: Pipeline A: Pipeline B: di xi−1 xi di di+1 xi xi+1 di+1 di+2 xi+1 Zeit→ Ferner kann man die verkürzten Formen (di · (2 − di ))Tr und (xi · (2 − di ))Tr in einen Adder-Tree geben, wobei man dann zunächst ungenau aber schnell rechnet und im letzten Schritt die Folge 2 genau berechnet. 5.3.3 Iterative Berechnung von Quadratwurzeln Um die Quadratwurzel einer Zahl N zu berechnen, kann man ähnlich vorgehen wie bei der iterativen Division. Man√verwendet wieder das Newton-Verfahren mit einer geeigneten Funktion f , welche N als Nullstelle besitzt. Wählt man f (x) = x2 − N, so ergibt sich folgende Iterationsvorschrift für das iterative Wurzelziehen: f (xi ) xi2 − N 1 N xi+1 = ϕ(xi ) = xi − ′ = xi − = · xi + f (xi ) 2 · xi 2 xi √ Das Verfahren konvergiert quadratisch gegen N. Beispiel: Iterative Berechnung der Quadratwurzel aus 2 Sei N = 2 und x0 = 100 (ziemlich schlechter Startwert). Es ergibt sich folgende Näherungsfolge: 120 Kapitel 5. Die Arithmetisch-Logische Einheit x0 x1 x2 x3 x4 x5 x6 x7 x8 = 100 1 2 = · (100 + ) ≈ 50 (ungenau) 2 100 ≈ 25 ≈ 12 ≈ 6 ≈ 3 1 2 ≈ · (3 + ) ≈ 1, 83 (ungenau gerechnet ergäbe sich: 1, 5) 2 3 ≈ 1, 46 ≈ 1, 4149 . . . (schon recht genau) Ein großer Nachteil des Verfahrens besteht darin, dass es nicht divisionsfrei ist, da Nxi in jedem Schritt berechnet wird. Daher kommt man wieder auf eine AEGP-verwandte Methode zurück. Man verwendet hier die Folgen (di ) und (xi ) mit: d0 := N, x0 := N, di+1 := di · ri2 xi+1 := xi · ri wobei ri = 1 + 12 · (1 − di ) ist. Die hier vorliegende Division durch 2 kann durch einen 2 Rechtsshift realisiert werden. Es gilt, dass dn = d0 · r02 · r12 · . . . rn−1 quadratisch gegen 1 konvergiert, wobei auch hier wieder ein geeigneter Startwert vorausgesetzt sei. Satz: Die Folge (xi ) konvergiert quadratisch gegen √ N: √ N 1 xn = x0 · r0 · r1 · . . . rn−1 → x0 · √ = √ = N d0 N Beispiel: Berechnung der Quadratwurzel aus 2 Sei N = 2 und somit d0 = x0 = 2. Man erhält die Folgen (di ), (xi ) und (ri ): d0 = 2 x0 = 2 r0 = 1 + 21 · (1 − 2) = 12 1 1 1 x1 = 2 · 2 = 1 r1 = 1 + 21 · (1 − 21 ) = 54 d1 = 2 · 4 = 2 25 5 7 d2 = 32 x2 = 4 r2 = 1 + 64 d3 = 0, 9614943 x3 = 1, 3867187 r3 = 1, 0192528 d4 = 0, 998872 x4 = 1, 4134169 r4 = 1, 0005635 d5 = 0, 999989 x5 = 1, 4142133 r5 = 1, 0000005 5.3. Division 121 Beweis: Es gilt: √ xn = x0 · r0 r1 · . . . · rn−1 = N · r0 r1 · . . . · rn−1 → N 1 ⇔ r0 r1 · . . . · rn−1 → √ N 1 ⇔ (r0 r1 · . . . · rn−1 )2 → N ⇔ N · (r0 r1 · . . . · rn−1 )2 = d0 · (r0 r1 · . . . · rn−1 )2 = dn → 1 Es ist also zu zeigen, dass (di+1 ) gegen 1 konvergiert. Zunächst ist: di+1 = di · (1 + 21 · (1 − di ))2 = di · (2 − di + 41 · (1 − di )2 ). Hilfssatz: Eine Iterationsvorschrift xi+1 := ϕ(xi ) konvergiert genau dann mit Ordnung m ′ gegen x, d.h. für εi = xi − x gilt εi+1 = O(εm i ), wenn ϕ(x) = x und ϕ (x) = . . . = ϕ(m−1) (x) = 0 und ϕ(m) (x) 6= 0. Taylorentwicklung des Fehlers: εi+1 = xi+1 − x̄ = ϕ(xi ) − x̄ = ϕ(x̄ + εi ) − x̄ ∞ ∞ ϕk (x) k ϕk (x) k · εi − x̄ = ∑ εi + ϕ(x̄) − x̄ = ∑ k=1 k! k=0 k! ′ m−1 (x̄) = 0 und ϕ(m) (x̄) 6= 0. Daher: εi+1 = O(εm i ) ⇔ ϕ(x̄) = x̄ und ϕ (x̄) = . . . = ϕ Es bleibt noch zu zeigen, dass di+1 = di · (2 − di + 14 · (1 − di )2 ) quadratisch gegen x̄ = 1 konvergiert. Die Iterationsvorschrift lautet hier: Ableitungen: 1 2 di+1 = ϕ(di ) mit ϕ(x) = x · 2 − x + · (1 − x) , 4 1 1 2 ϕ (x) = 1 · 2 − x + · (1 − x) + x · −1 + · 2 · (1 − x) · (−1) 4 4 ′ 6·x 4 ′ (di ) konvergiert gegen 1, weil ϕ(1) = 1, ϕ (1) = 0 und ϕ′′ (1) 6= 0 ist: ϕ′′ (x) = −2 + ϕ(1) = 1 · (2 − 1 + 0) = 1 = x̄, ϕ (1) = 1 · (2 − 1 + 0) + 1 · (−1 + 0) = 0, 1 ϕ′′ (1) = − . 2 ′ 122 Kapitel 5. Die Arithmetisch-Logische Einheit 5.4 Arithmetik bei redundanter Zahlendarstellung Die letzten Abschnitte haben gezeigt, dass ein Übertrag in ungünstigen Fällen eine lange Laufzeit der Rechenoperation bewirkt. Der Übertrag kann sich zum Beispiel auf die gesamte Addition auswirken. In diesem Abschnitt werden Zahlendarstellungen für die Addition und für die Division besprochen, die auf Kosten einer erhöhten Redundanz eine beschleunigte Durchführung der Operationen ermöglichen. 5.4.1 SDNR-Darstellung für die Addition Die grundlegende Idee besteht darin, eine oder mehrere zusätzliche Redundanzziffern einzuführen, in denen sich der Übertrag fängt, falls er überhaupt entsteht. Man führt zum Beispiel eine zusätzliche 2 zu den binären Ziffern 0 und 1 ein. Die Ausgangszahlen könnte man binär darstellen und das Ergebnis ternär interpretieren. Dies wird im folgenden Abschnitt präzisiert. Die Zahlendarstellung ist in Stellenwertform zu einer Basis d. Dabei sind im codierenden Bitwort nun nicht mehr nur die Ziffern 0, . . . , d − 1 zulässig, sondern positive und negative Ziffern αi ∈ [−r1 : +r2 ], r1 , r2 > 0: WSDNR (αn−1 , αn−2 , . . . , α0 ) = n−1 ∑ αi · d i i=0 Um die Notation im Folgenden übersichtlicher zu gestalten, wird −x als x notiert. Beispiel: Wert einer SDNR-Zahl zur Basis d = 10 WSDNR (23̄14̄) = 2000 − 300 + 10 − 4 = 1706. Definition: SDNR (Signed Digit Number Representation) Eine Zahlendarstellung zu einer Basis d, die folgende Eigenschaften hat, heißt SDNR: 1. Symmetrie: r1 = r2 = r 2. Für αi sollen mindestens d verschiedene Werte möglich sein. Der Übertrag (+1 oder −1) aus der Stelle (i − 1) soll sich in der Stelle i fangen. Dies ist erfüllt, wenn 2 · r + 1 ≥ d + 2 gilt, also ⌊ d2 ⌋ + 1 ≤ r; 2 · r + 1 ist die Anzahl möglicher Ziffern. 3. Für αi sollen höchstens d nicht-negative Werte zulässig sein, also r ≤ d − 1. Das Vorzeichen des Wertes einer SDNR-Zahl ist gleich dem Vorzeichen der von links gesehen ersten Ziffer der Zahl, die nicht 0 ist. Aus der obigen Definition die (Nicht-)Existenz von SDNR-Darstellungen zu bestimmten Basen abgeleitet werden: a) Für die Basis d = 2 gibt es keine SDNR-Darstellung: ⌊ d2 ⌋ + 1 ≤ r ≤ d − 1 (Bedingung 2 und 3) liefert für d = 2 : 5.4. Arithmetik bei redundanter Zahlendarstellung 123 1 + 1 ≤ r ≤ 2 − 1, also 2 ≤ 1, was zu einem Widerspruch führt. b) Für die Basis d = 3 ist eine SDNR-Darstellung möglich: ⌊ 32 ⌋ + 1 ≤ r ≤ 3 − 1, folglich: r = 2 ist hier eindeutig bestimmt. Somit ist die Codierung festgelegt: WSDNR (αn−1 , αn−2 , . . . , α0 ) = n−1 ∑ αi · 3i mit i=0 αi ∈ [−2 : +2]. Beispiel: SDNR-Addition Es werden die Zahlen (21̄2̄2̄012) und (1̄2̄2̄1222) addiert. Dabei wird das jeweilige Summenbit in der oberen und das Übertragsbit in der unteren Zeile notiert, wobei das Übertragsbit einer Stelle eine Stelle weiter links geschrieben wird. Diese Notation wurde schon bei der Von-Neumann-Addition verwendet. Im ersten Schritt der Addition wird darauf geachtet, dass die Summenbits si ∈ [−1 : +1] sind. 0 2 1 1 1 0 1 2 0 1 1 2 2 1 0 1 2 1 1 1 0 0 2 1 1 0 1 2 0 1 1 2 2 1 0 1 Die fettgedruckten Ziffern zeigen, wie man s2 = 2 vermeiden kann: 2 = 3 · 1 − 1. Auf diese Weise kann im zweiten Schritt der Addition kein Übertrag mehr entstehen. Im allgemeinen Fall bildet man die Summenbits si im ersten Schritt so, dass sie den Ziffernbereich nicht bis oben beziehungsweise unten ausfüllen: si ∈ [−(r − 1) : +(r − 1)]. Der Übertrag +1 oder −1 fängt sich dann im zweiten Schritt. Die Addition für die Basis d und ⌊ d2 ⌋ + 1 ≤ r ≤ d − 1 geschieht wie folgt: + + cn−1 sn αn−1 βn−1 σn−1 cn−2 sn−1 ··· ··· ··· ··· ··· c0 α0 β0 σ0 0 s0 Zwischensumme Vorläufiger Übertrag Summe Dabei sind: αi , βi , si ∈ [−r : +r], σi ∈ [−(r − 1) : +(r − 1)] und ci ∈ [−1 : +1]. Für Schritt 1 sind folgende Formeln denkbar: Sei wmax ∈ [(d − r) : (r − 1)], zum Beispiel also wmax = r − 1. +1 falls αi + βi > wmax Setze ci = 0 sonst −1 falls αi + β1 < −wmax und setze σi = αi + βi − ci · d, wegen der Invarianzeigenschaft: αi + βi = ci · d + σi . Dann ist | σi |< r für i = 0, . . . , (n − 1) erfüllt und somit ist Schritt 2 ohne Überträge möglich. 124 Kapitel 5. Die Arithmetisch-Logische Einheit 5.4.2 Gemischtes SDNR-Verfahren Die Addition von Zahlen in der SDNR-Darstellung geht sehr schnell. Zu beachten ist allerdings, dass die Zahlen zunächst in dieser Form codiert sein müssen. Der Aufwand für diese Codierung muss daher berücksichtigt werden. Günstig ist es, das Verfahren zu verallgemeinern, so dass eine Addition von Zahlen durchgeführt werden kann, wobei eine Zahl in SDNR-Darstellung zur Basis d und die andere in normaler Stellenwertcodierung zur Basis d vorliegt: + + cn−1 Sn An−1 bn−1 Tn−1 cn−2 Sn−1 ··· ··· ··· ··· ··· c0 A0 b0 T0 0 S0 SDNR-Zahl d-näre Zahl Zwischensumme Vorläufiger Übertrag Summe Die großen Buchstaben kennzeichnen Ziffern einer SDNR-Zahl, d.h. Ai ∈ [−(d − 1) : +(d − 1)]; die kleinen Buchstaben bezeichnen Ziffern einer d-nären Zahl, also bi ∈ [0 : (d − 1)]. Die Formeln für Schritt 1 und Schritt 2 der Addition lauten: Schritt 1: Setze ci ( 1 wenn Ai + bi ≥ d − 1 = 0 sonst, Ti = Ai + bi − ci · d ∈ [−(d − 1) : +(d − 2)] Schritt 2: Setze Si = Ti + ci−1 ∈ [−(d − 1) : +(d − 1)], ohne weiteren Übertrag. Dieses gemischte Verfahren funktioniert auch bei Codierung zur Basis d = 2. Hier liegt obiges Schema mit entsprechender Wahl der Ziffernbereiche zugrunde: Ai ∈ {−1, 0, +1}, bi ∈ {0, 1}, Ti ∈ {−1, 0}, ci ∈ {0, 1} für i = 0, . . . , (n − 1) und Si ∈ {−1, 0, 1} für i = 0, . . . , n. Ai , Ti und ci werden binär codiert (bi liegt binär vor und Si wird umgewandelt): • Ai auf zwei Binärstellen, wobei sich als Darstellungsform Betrag+Vorzeichen eignet: ai,1 0 0 1 1 ai,2 0 1 0 1 Ai +0 +1 -0 -1 5.4. Arithmetik bei redundanter Zahlendarstellung • ci und Ti können auf einer Binärstelle codiert werden. Für die Realisierung der Berechnung eignen sich folgende Formeln: a) Ai = ai,2 · (1 − 2 · ai,1 ), also ai,2 = Ai mod 2 b) ci = 1 ⇔ Ai + bi ≥ 1 ⇔ Ai = 1 ∨ Ai = 0 ∧ bi = 1 Formel: ci = ai,1 · ai,2 + ai,2 · bi c) Ti = −1 ⇔ Ai + bi = 1 mod 2 ⇔ ai,2 + bi = 1 mod 2 Formel: Ti = −(ai,2 ⊕ bi ); | Ti |= ai,2 ⊕ bi d) Formeln für Si = (si,1 ; si,2 ) : si,2 = 1 ⇔ Si 6= 0 ⇔ Ti + ci−1 = 1 mod 2 ⇔ | Ti | ⊕ci−1 = 1, also: si,2 =| Ti | ⊕ci−1 = ai,2 ⊕ bi ⊕ ci−1 si,1 =| Ti | oder auch si,1 = ci−1 Die zwei unterschiedlichen Möglichkeiten beruhen auf der Redundanz der Zahlendarstellung. 5.4.3 SRT-Verfahren zur seriellen Division Dieses Verfahren ist benannt nach Sweeney, Robertson und Tocher und es verbessert das Prinzip der seriellen Division in der Form, dass der neue Partialrest möglichst betragsklein gewählt wird. Das Prinzip der seriellen Division besteht aus folgenden Schritten: • Subtrahiere vom Dividenden ein geeignetes Vielfaches des Divisors. • Bestimme Quotientenbit(s) in Abhängigkeit von dieser Subtraktion. • Wiederhole diesen Vorgang so lange mit jeweils neuem kleineren Partialrest, bis der Partialrest näherungsweise Null ist (bis also der Quotient vollständig berechnet ist). Die Folge der Partialreste X im Verlaufe einer Divisionsoperation zur Basis d ist gegeben durch: X n := W(DD, DE) Wert von Register (DD, DE) X j := d · (X j+1 − q j · W(DR)) für alle j = (n − 1), . . . , 0 Dabei ist q j ∈ {−(d −1), . . . , 0, . . . , (d −1)} ein Quotientenbit. Dieses Quotientenbit ist nun so zu wählen, dass X j möglichst betragsklein wird, das heißt, für alle q′j gilt: | X j (q j ) |≤| X j (q′j ) |. 125 126 Kapitel 5. Die Arithmetisch-Logische Einheit SRT-Division im binären Fall Man kann folgende zwei Varianten unterscheiden: Das SRT 1-Verfahren ist ziemlich genau, aber aufwändiger als das SRT 2-Verfahren, welches dafür ungenauer arbeitet. SRT1: Es sei vorausgesetzt, dass 1 2 ≤ W(DR) < 1 ist. Dann ist SRT 1 definiert durch: X n := W(DD, DE) X j := 2 · (X j+1 − q j · W(DR)), für j = (n − 1), . . . , 0, mit: j+1 > 1 · W(DR) +1, falls X 2 q j := 0, sonst −1, falls X j+1 < − 21 · W(DR) SRT2: Es ist wieder vorausgesetzt, dass 1 2 ≤ W(DR) < 1 ist. Dann ist SRT 2 definiert durch: X n := W(DD, DE) X j := 2 · (X j+1 − q j · W(DR)), für j = (n − 1), . . . , 0, mit: j+1 ≥ 1 +1, falls X 2 q j := 0, sonst −1, falls X j+1 < − 21 Hier ist also die Bestimmung von q j einfacher, denn es muss kein expliziter Vergleich durchgeführt werden, sondern es reicht, die ersten beiden Bits zu prüfen: j+1 j+1 Seien xn−1 und xn−2 die ersten beiden Bits von X j+1 . Dann wird q j wie folgt bestimmt: j+1 j+1 +1, falls xn−1 = 0 und xn−2 = 1 j+1 j+1 q j := 0, falls xn−1 = xn−2 j+1 j+1 −1, falls xn−1 = 1 und xn−2 = 0 Mikroprogramm zur SRT-Division Es wird nun ein Mikroprogramm für die SRT -Division angegeben. Hierzu seien folgende Bezeichnungen vereinbart: • Register: (DD, DE): Doppelt langer Dividend DR : Divisor 5.4. Arithmetik bei redundanter Zahlendarstellung DE : positive Quotientenbits DH : negative Quotientenbits Falls an der Stelle j kein Quotientenbit aufzunehmen ist, gilt DE j = DH j = 0. • Zähler: Z (wie früher) Das Mikroprogramm für nicht-negative Faktoren 0 ≤ W(DD, DE) < W(DR) < 1 lautet: Mikroprogramm: SRT-Division 0 : [ (DD, DE ) := Dividend ; DR := Divisor ; DH := 0; Z := n ] 1 : [ Z := Z - 1; q := -DDn−1 + DDn−2 ; if DDn−1 6= DDn−2 then if DDn−1 = 1 then DD := DD + DR else DD := DD - DR ] 2 : [ (DE0 , DH0 ) := if q = 1 then (1 ,0) else if q = -1 then (0 ,1) else (0 ,0) if Z > 0 then shl ( DD, DE ); shl ( DH ); goto 1 else shl ( DE ); shl ( DH ) ] 3 : DE := DE - DH 4 : Stop In DH werden die negativen Quotientenbits als Einsen abgelegt. Daher kann im letzten Schritt subtrahiert werden, um das Ergebnis zu dekonvertieren. 127 128 Kapitel 5. Die Arithmetisch-Logische Einheit Index Überdeckung, 65, 66 Übertrag, 16, 81, 84, 122 1-Komplement, 15, 16 2-Komplement, 12, 15, 16, 19, 20, 83 3-Excess-Code, 24, 49 -typen, 33, 36 -wort, 29 Befehlsfolgezähler, 31 Befehlsregister, 4 Befehlssatz, 34 Befehlszählregister, 4, 45 Adder-Tree, 90, 92, 108, 109, 118, 119 Betrag+Vorzeichen, 11, 15, 102 Addierwerk, 82, 84, 91 Binär Addition, 11, 15, 16, 79, 105, 107, 112, -darstellung, 30 122, 124 -system, 10 serielle, 84, 87 -zahl, 11, 14, 84, 102 Additionsmethode, 79 Bitfehler, 27 Adresscodierung, 29 Bitfolge, 29 Adresse, 4, 35, 37 Bitstring, 22 Adressmatrix, 89 Boolesche Algebra, 52, 56 Adressmodifikation, 37 Boolescher Ausdruck, 58, 59, 62, 80, 82, AEGP, 118, 120 83 Akkumulator, 4, 5, 29, 31, 33, 35 Booth ALU, 79 Verfahren von, 106 AND, 50 Bus, 7, 8 ASCII-Code, 23 Assembler, 30 Carry, siehe Übertrag Carry-Look-Ahead-Addierer, 94, 95, 99 Atom, 55 Aussagenkalkül, 52 Carry-Ripple-Addierer, 82, 93, 94 Axiomensystem, 52 Carry-Save-Addierer, 91 Carry-Skip-Addierer, 95, 96, 99 B+V-Codierung, siehe Betrag+Vorzeichen n-Bit-, 98 Basis, 10, 122 zweiter Ordnung, 98 Basisadresse, 36 CISC, 34 Baustein, 49 Codewort, 9 -funktion, 50 Codierung, 9–11, 22, 25, 123 -system, 50 CSA, siehe Carry-Save-Addierer BCD-Code, 24 Decodiermatrix, 89 Bedingungsmatrix, 89 Befehl, 2, 29 Dezimalsystem, 10 -ausführung, 29 Diodenmatrix, 88, 89 -strukturen, 30 Disjunktion, 83 129 130 Index Disjunktive Normalform, 60 Dividend, 110, 114, 125 Division, 21, 79, 103, 111, 114–116, 122 iterative, 110 Non-Performing-, 111 Non-Restoring-, 112 serielle, 110 Divisionsrest, 111 Divisor, 110, 114, 125 DNF, siehe Disjunktive Normalform Don’t Care, 48, 58, 65, 71 Earle Larch, 75 EBCDIC-Code, 23 einschlägiger Index, 59, 71 End-Around-Carry, 104 EQUIV, 50 Ergebnisparameterblöcke, 46 EXOR, 14, 50 FA, siehe Fulladder Festkommadarstellung, 17 Fixpunkt, 115 Flipflop, 73 Fulladder, 80, 81, 83, 84, 90, 96 Gleitkommazahl, 18 Gray-Code, 24, 70 HA, siehe Halfadder Halbaddierer, siehe Halfadder Halfadder, 80, 81, 86, 90 Kosten, 82 Hamming-Distanz, 26 Hamming-Gewicht, 65 Hauptspeicher, 3 Hazards, 76 Hexadezimalsystem, 10 Hintergrundspeicher, 3 Huffman-Codierung, 23 IEEE-754-Format, 19 Implikant, 64 Indexregister, 4, 34 indirekte Adressierung, 37 Interrupt, 6 Iterationsvorschrift, 115–117, 119, 121 KNF, siehe Konjunktive Normalform Kommunikationsnetz, 2 Komplementfreie Ringsummenentwicklung, 61 Komplementfunktion, 52 Konjunktion, 83 Konjunktive Normalform, 60 Korrekturbit, 106 Kostenfunktion, 63 Kostenmaß, 62 KRE, siehe Komplementfreie Ringsummenentwicklung Längsparität, 26 Ladebefehl, 33 Leitung, 7 Linkageregister, 45 Linksshift, 21 Mantisse, 18, 19, 21 Maxterm, 56 Mengenalgebra, 52, 53 Mikro -befehl, 88, 89 -operation, 88 -programm, 85, 87, 110 MIMD-Rechner, 5 Minimalpolynom, 64, 66, 81 Minimierungsproblem, 63 Minterm, 55, 58–60, 64, 65 MISD-Rechner, 5 Mnemonische Darstellung, 30 Monom, 64, 65 Multiplikand, 102, 103 Multiplikation, 21, 79, 102, 103, 108, 110, 115, 118, 119 serielle, 104, 105, 110 Multiplikationsmatrix, 103 Multiplikator, 102–106, 108 Multiplikatorcodierung, 106, 114, 118 Gruppengröße 1, 106 Multiplizierwerk, 115, 119 NAND, 74 Newton-Verfahren, 117 normalisierte Darstellung, 18, 19 normalisierte Gleitkommazahl, 117 Index NOT, 50 Nullstelle, 115, 116 Objektcode, 30 Oktalsystem, 10 Opcode, 29, 30, 34 Operanden, 5, 30, 32 Operationen, 32, 34 Paralleladdierer, 82, 86 Paritätsbit, 25 Partialprodukt, 104, 106 Partialrest, 111, 114, 125 Pipelining, 90, 108, 119 PLA, siehe Programmable Logic Array Postnormalisierung, 20, 21 Primimplikant, 64 wesentlich, 66 Programmable Logic Array, 88 Programmausführung, 30 Propagationskette, 84, 91 Prozessor, 1, 2 Pufferregister, 8 Pufferspeicher, 3 Quadratwurzel, 110, 119 Querparität, 26 Quine-McCluskey, 66 Quotient, 110, 111, 119 Quotientenbit, 111, 112, 125 Quotientenregister, 111 Rückkopplung, 74 Rückkopplungsleitung, 109 rationale Zahlen, 17 Recheneinheit, 1 Rechenoperationen, 11 Rechenwerk, 79 Rechnerbus, siehe Bus Rechtsshift, 21, 120 Redundanz, 17 Redundanzziffern, 122 Register, 3 -befehl, 34 Allzweck-, 4 Befehls-, 4 Rekursion, 43 131 Rekursionsformel, 83, 94 Resolutionsblock, 71 RISC, 34 RS-Flipflop, 73 Schaltfunktion, 48, 49, 54, 58, 60, 62, 71 Schaltkreis, 47, 62, 72 Schaltnetz, 72 Schaltung, 47, 48 Schaltwerk, 47, 71, 72 Schulmethode, 103, 110 SDNR-Darstellung, 123 Shiftbefehl, 33 SIMD-Rechner, 5 SISD-Rechner, 4 Speicher, 2, 35 -adressierung, 29 -adressregister, 4 -datenregister, 4 Speicherelement, 72 Speicherzellen, 3 Stack, 29–32, 45 -befehl, 31 Stellenwert, 10 -codierung, 10, 79 -form, 122 Steuereinheit, 1 Steuermatrix, 89 Stone Satz von, 52 Subtraktion, 11, 16, 83, 87, 105 Supergruppe, 98 Table-Look-Up, 114, 117 Takt, 71, 73, 75, 85, 88, 90, 92 Taktflanke, 76 Taktmatrix, 89 Torschaltung, 74, 101 Umschaltsymbol, 22 Unibus, siehe Bus Unterprogramm, 29, 42, 44 -aufruf, 43 -bibliothek, 42 Vektoroperation, 30 Volladdierer, siehe Fulladder 132 Index Von-Neumann -Addition, 86, 123 -Adierer, 89 -Prinzip, 4, 5 Vorzeichenbit, 10, 11, 13, 16, 17 Wallace Tree, siehe Adder-Tree Zahlenbereich, 11–13, 17 Zahlensystem, 10 Zahlenverlängerung, 14, 16