← Zurück zum BlogCPU-Architektur

Befehlsregister und die Dekodierphase

Wie eine Lehr-CPU einen Befehl erfasst, Opcode-, Register- und Immediate-Felder extrahiert und daraus validierte Steuersignale erzeugt – mit RISC-V- und x86-Kontext.

DennyDenny
8 Min. Lesezeit
Chalkboard feature illustration for “Befehlsregister und die Dekodierphase”

Kurz gesagt: In einer einfachen CPU erfasst ein Befehlsregister (IR) einen geholten Befehl, damit die Dekodierung stabile Bits sieht. Die Dekodierung erkennt das Befehlsformat, extrahiert Register- und Immediate-Felder, prüft, ob die Kodierung zulässig ist, und erzeugt Steuerinformationen für spätere Phasen. Ein moderner Prozessor kann diese Aufgabe auf Fetch-Puffer und Pipeline-Register verteilen, statt ein einzelnes architektonisches „IR“ zu besitzen.

Der Programmzähler beantwortet die Frage woher holen? Die Dekodierphase beantwortet was die geholten Bits bedeuten. Dazwischen platziert eine Lehr-CPU üblicherweise ein Befehlsregister: eine Bank aus Flipflops, die den aktuellen Befehl bewahrt, während die Steuerlogik ihn untersucht.

Dieses Modell ist nützlich, braucht aber eine klare Grenze. Das Befehlsregister ist in den meisten ISAs kein für Programmierende sichtbares Register, und eine leistungsstarke Implementierung kann viele Befehle gleichzeitig in Fetch-Blöcken, Warteschlangen, Pipeline-Registern oder Caches für dekodierte Operationen halten. „IR“ bezeichnet eine Rolle im Lehrdatenpfad, keine universelle physische Komponente.

Der Lehrdatenpfad

Eine kleine Mehrzyklus-CPU kann so dargestellt werden:

PC → instruction memory → instruction register → field extraction → control

Bei einem Ereignis zum Holen eines Befehls:

  1. liefert der PC eine Befehlsadresse;
  2. gibt der Befehlsspeicher ein Befehlswort zurück;
  3. erfasst das IR dieses Wort an der aktiven Taktflanke; und
  4. interpretiert kombinatorische Dekodierlogik die gespeicherten Bits.

Die Schreibfreigabe des IR wird nur aktiviert, wenn ein neuer Befehl angenommen werden soll. Während eines Stalls hält das IR seinen alten Wert. Bei einem Reset oder Pipeline-Flush markiert die Steuerung den gespeicherten Befehl entweder als ungültig oder ersetzt ihn durch eine harmlose Operation.

Manche Mehrzyklus-Datenpfade platzieren ein Speicher-Datenregister zwischen Speicher und IR. Andere erfassen die Speicherausgabe direkt. Keine der beiden Folgen ist universell; das Timing und der Handshake der gewählten Speicherschnittstelle bestimmen, welche Register erforderlich sind.

Eine kleine Beispielkodierung

Angenommen, eine Acht-Bit-Lehr-ISA definiert dieses Format:

7             5 4             2 1             0
┌──────────────┬───────────────┬───────────────┐
│ opcode [2:0] │ register [2:0]│ mode / small  │
│              │               │ immediate     │
└──────────────┴───────────────┴───────────────┘

Die genauen Felder sind eine ISA-Entscheidung. Acht Bits können nicht gleichzeitig einen großen Opcode-Raum, zwei unabhängige Registeroperanden und ein brauchbares Immediate kodieren; daher verwendet eine echte Lehr-ISA oft mehrere Formate. Zum Beispiel:

  • eine Registeranweisung kann Bits für Quell- und Zieladressen vorsehen;
  • eine Load-Immediate-Anweisung kann die meisten unteren Bits als Konstante verwenden; und
  • eine Sprunganweisung kann dieselben Bits als Offset interpretieren.

Die Dekodierung muss zunächst das Format feststellen und anschließend die übrigen Bits entsprechend interpretieren. Jedem Bit eine dauerhafte einzelne Bezeichnung zu geben, ergibt die falsche Schaltung.

Felder mit fester Position: RISC-V-Beispiel

Die Basis-ISA RISC-V verwendet 32-Bit-Befehlsformate. Für einen Befehl des R-Typs:

31         25 24    20 19    15 14   12 11      7 6        0
┌────────────┬────────┬────────┬───────┬─────────┬──────────┐
│   funct7   │   rs2  │   rs1  │ funct3│    rd   │  opcode  │
└────────────┴────────┴────────┴───────┴─────────┴──────────┘

Die Registerfelder belegen in den Basisformaten konsistente Positionen:

opcode = IR[6:0]
rd     = IR[11:7]
funct3 = IR[14:12]
rs1    = IR[19:15]
rs2    = IR[24:20]
funct7 = IR[31:25]

Diese Slices sind Verdrahtung, keine Arithmetik. rs1 und rs2 speisen die Leseadressen der Registerdatei; rd kennzeichnet ein mögliches Ziel; und Opcode plus Funktionsbits identifizieren die Operationsklasse und -variante.

Bei Immediates ist mehr Sorgfalt nötig. In mehreren RISC-V-Formaten sind die Immediate-Bits absichtlich über den Befehl verteilt. Die Hardware muss sie in der angegebenen Reihenfolge zusammenfügen und vom architektonisch festgelegten Vorzeichenbit aus vorzeichenerweitern. Das bleibt einfache kombinatorische Verdrahtung, ist aber nicht immer ein zusammenhängender Slice.

Dekodierung ist mehr als ein Opcode-Dekoder

Für eine Spielzeug-ISA mit acht vollständigen Operationen kann ein Drei-zu-Acht-Dekoder One-Hot-Signale erzeugen:

OpcodeLehr-OperationOne-Hot-Ergebnis
000NOPis_nop
001LOADis_load
010STOREis_store
011ADDis_add
100SUBis_sub
101JUMPis_jump
110BRANCH-Zis_branch_z
111HALTis_halt

Aus diesen Signalen lassen sich dann Steuergleichungen ableiten:

RegWrite=is_load∨is_add∨is_sub\text{RegWrite} = \text{is\_load} \lor \text{is\_add} \lor \text{is\_sub} MemWrite=is_store\text{MemWrite} = \text{is\_store}

Das ist eine nützliche Einführung in Dekoder und Sum-of-Products-Logik, aber nicht die allgemeine Struktur eines echten Dekoders.

In einer größeren ISA:

  • wählt der primäre Opcode oft ein Format oder eine Befehlsfamilie statt einer einzelnen Operation;
  • unterscheiden sekundäre Funktionsfelder die Mitglieder der Familie;
  • können Modus- oder Präfixzustand Operandenbreite und Interpretation verändern;
  • sind einige Bitmuster reserviert oder unzulässig;
  • können Privilegien und verfügbare Erweiterungen die Zulässigkeit beeinflussen; und
  • sind Steuerausgänge üblicherweise kodiert statt eine eigene Leitung für jeden Befehl zu haben.

Ein korrekter Dekoder muss nicht unterstützte Kodierungen zurückweisen, statt sie versehentlich als einen ähnlichen zulässigen Befehl zu behandeln.

Was die Dekodierung erzeugt

Das Ergebnis sollte man eher als Steuerbündel denn als Dutzende unabhängiger Leitungen verstehen. Ein Lehrbündel könnte enthalten:

FeldBedeutung
alu_opAddition, Subtraktion, logische Operation, Vergleich oder Durchleitung auswählen
src_a / src_bRegister, PC, Immediate oder Konstante als Operanden wählen
reg_writeEinen Schreibvorgang in die Registerdatei in der passenden späteren Phase erlauben
mem_read / mem_writeEine Daten-Speicheroperation anfordern
result_sourceALU, Speicher, PC-relativen Wert oder eine andere Writeback-Quelle auswählen
branch_kindKeinen Sprung, bedingten Sprung, direkten Sprung oder indirekten Sprung auswählen
immediateWieder zusammengesetzte und erweiterte Konstante
legalAnzeigen, ob die Kodierung im aktuellen Maschinenzustand unterstützt wird

Diese Signale müssen durch Pipeline-Gültigkeit und das Timing der Phasen qualifiziert werden. Ein kombinatorisches reg_write = 1 bei der Dekodierung ist keine Erlaubnis, die Registerdatei sofort zu aktualisieren; geschrieben wird erst, wenn der Befehl die vorgesehene Phase erreicht, ohne verworfen worden zu sein oder einen Fehler ausgelöst zu haben.

Feste und variable Befehlslänge

Die Befehlslänge verändert den vorderen Teil des Dekodierproblems.

Beispiel mit fester Länge: Arm A64

Arm-A64-Befehle sind 32 Bit breit. Sobald Fetch ein ausgerichtetes Wort geliefert hat, können die Feldpositionen direkt untersucht werden. Die feste Länge vereinfacht das Erkennen von Befehlsgrenzen, macht aber nicht jede Operation oder Immediate-Kodierung trivial.

RISC-V mit komprimierten Befehlen

RISC-V-Basisbefehle sind 32 Bit breit. Die optionale komprimierte Erweiterung fügt 16-Bit-Befehle hinzu, und die Architektur reserviert ein Längenkodierungsschema für längere Kodierungen. Fetch muss feststellen, ob der nächste Befehl zwei, vier oder eine andere unterstützte Anzahl von Bytes belegt, bevor der PC weitergeschaltet und der nächste Befehl ausgerichtet wird.

Beispiel mit variabler Länge: x86

x86-Befehle sind ein bis 15 Byte lang und können historische oder moderne Präfixkodierungen, Opcode-Bytes, ModR/M- und SIB-Adressierungsbytes, Displacement und Immediate-Daten enthalten. Welche Teile vorhanden sind, hängt von den vorangehenden Bytes ab.

Ein modernes x86-Frontend benötigt daher eine Dekodierung von Befehlsgrenzen und -längen vor oder neben der semantischen Dekodierung. Implementierungen unterscheiden sich: Es ist unsicher zu behaupten, dass jeder Prozessor einen festen Blockumfang holt, dieselbe Warteschlangenstruktur verwendet oder jeden Befehl über denselben Pfad übersetzt. Die Architekturhandbücher von Intel definieren die softwareseitig sichtbare Kodierung; die genaue Pipeline ist mikroarchitekturspezifisch.

Feste und variable Länge sind keine Synonyme für RISC und CISC. Sie beschreiben eine Eigenschaft der Kodierung.

Festverdrahtete und sequenzierte Steuerung

Eine kleine CPU kann die Steuerung direkt aus Boolescher Logik erzeugen. Eine Mehrzyklussteuerung kann stattdessen eine Zustandsmaschine oder einen Steuerungsspeicher nutzen, dessen Einträge mehrere interne Schritte beschreiben. Reale Prozessoren können schnelle Dekodierpfade mit sequenzierter Behandlung komplexerer Operationen kombinieren.

Der hilfreiche Unterschied lautet:

  • Kombinatorische Dekodierung bildet aktuelle Felder auf eine anfängliche Steuerdarstellung ab.
  • Sequenzierte Steuerung gibt im Zeitverlauf mehrere interne Aktionen aus.

Keine Methode ist automatisch „RISC“ oder „CISC“, und eine mikrokodierte Implementierung ist nicht von Natur aus langsam. Die Leistung hängt von der vollständigen Implementierung und der Arbeitslast ab.

Pipelinesteuerung: Stall, Flush und Gültigkeit

Sobald mehrere Befehle überlappen, sind die Befehlsbits nur ein Teil des Zustands. Jedes Pipeline-Register benötigt außerdem Steuermetadaten.

Stall

Wenn die nächste Phase einen Befehl nicht annehmen kann, muss das aktuelle Fetch-/Dekodierregister seine Daten halten oder an einem Ready/Valid-Handshake teilnehmen. Wenn die Bits weiterrücken, während der Gültigkeitszustand angehalten ist, gehen Befehl und Steuerung auseinander.

Flush

Eine fehlvorhergesagte Verzweigung, eine Ausnahme oder ein Reset kann jüngere Befehle ungültig machen. Nur die Befehlsbits zu löschen ist weniger robust, als ein Gültigkeitsbit zu löschen, weil eine reine Null-Kodierung in manchen ISAs selbst zulässig sein kann.

Abhängigkeitsbehandlung

Die Dekodierung kann Quell- und Zielregister für Hazard-Prüfungen identifizieren. Die Reaktion kann ein Stall, eine Forwarding-Auswahl, Registerumbenennung oder eine andere implementierungsspezifische Aktion sein. Die Feldextraktion liefert die Kennungen; spätere Steuerung entscheidet, was damit geschieht.

Häufige Entwurfsfehler

  • Annehmen, dass Speicher immer ein MDR benötigt. Fügen Sie Register anhand des tatsächlichen Speicherzeitverhaltens und der Schnittstelle hinzu, nicht aufgrund eines vermeintlich universellen Diagramms.
  • Das IR verändern, während sein Befehl noch aktiv ist. Verwenden Sie eine Schreibfreigabe oder ein Handshake-Verfahren, damit der Decoder stabile Bits auswertet.
  • Nur den primären Opcode dekodieren. Funktionsfelder, Modi, Erweiterungen und Zulässigkeitsprüfungen können nötig sein.
  • Immediate-Bits in numerischer Zeichenreihenfolge zusammenfügen. Befolgen Sie exakt die Bitzuordnung der ISA und führen Sie anschließend die Vorzeichenerweiterung ausgehend vom festgelegten Vorzeichenbit durch.
  • Seiteneffekte direkt aus unqualifizierter Dekodierung ansteuern. Gatter Schreibvorgänge mit Pipeline-Gültigkeit, Phasen-Timing und Ausnahme-/Flush-Zustand.
  • Auf ein beliebiges Befehlsmuster zurücksetzen. Bevorzugen Sie ein Gültigkeitsbit oder einen definierten Reset-Zustand des Steuerwerks; setzen Sie nicht voraus, dass ein ausschließlich aus Nullen bestehendes Wort zwingend NOP bedeutet.
  • Eine pädagogische Wellenform als Nachweis für physisches Timing behandeln. Das Modell kann logische Abfolgen zeigen, nicht die Einhaltung von Setup-/Hold-Zeiten in Silizium.

Einen Lehrdekoder bauen und überprüfen

Öffnen Sie den sequentiellen Befehlsausführer und identifizieren Sie:

  1. das Befehlsspeicherelement;
  2. die Leitungen, die jedes Feld bilden;
  3. die von jedem Feld beeinflussten Steuerausgänge; und
  4. den Punkt, an dem diese Ausgänge den Architekturzustand verändern dürfen.

Erstellen Sie anschließend eine kompakte Testtabelle:

TestklasseWas variierenErwartetes Ergebnis
Jeder zulässige OpcodePrimäre Opcode-BitsRichtige Operationsfamilie und Freigaben für Seiteneffekte
FunktionsvariantenSekundäre FelderRichtige ALU- oder Adressierungsvariante
Registeradressenrs- / rd-FelderRichtige Registerports, unveränderte Operation
Immediate-GrenzwerteNull, größtes Positiv, kleinstes NegativRichtige Zusammensetzung und Erweiterung
Reservierte KodierungNicht unterstütztes BitmusterIllegal-Instruction-Pfad, kein unbeabsichtigter Schreibvorgang
StallDekodierung mehrere Takte haltenBefehl und Steuerung bleiben ausgerichtet
FlushEinen Befehl in der Pipeline ungültig machenKein Seiteneffekt auf Register oder Speicher

Ein Beispiel manuell umzuschalten ist eine Veranschaulichung, keine ausreichende Überprüfung. Decke jede zulässige Operationsklasse und repräsentative unzulässige Kodierungen ab.

Wo es in die CPU passt

Der Programmzähler liefert die Fetch-Adresse. Die Dekodierung identifiziert Operanden und Steuerabsicht. Das CPU-Flag-Register ist in flagbasierten ISAs eine mögliche Quelle von Sprungbedingungen; andere ISAs vergleichen Register direkt. Das Steuerwerk sequenziert den Lehrdatenpfad und qualifiziert Seiteneffekte.

Zusammen erklären diese Elemente den Steuerfluss eines kleinen Prozessors, doch keines von ihnen ist allein „die gesamte Steuerungsebene“. Caches, Vorhersage, Ausnahmen, Privilegien, Hazards und Pipelinezustand fügen in größeren Maschinen weitere Steuerung hinzu.

Primärquellen

Lesen Sie als Nächstes die ausführliche Betrachtung von Fetch–Decode–Execute und ändern Sie anschließend eine Kodierung in der Lehr-CPU. Aktualisieren Sie Decoder, Tests und Dokumentation gemeinsam.

Machen Sie aus der Idee eine funktionierende Schaltung.