← Zurück zum BlogCPU-Architektur

Eine CPU von Grund auf in einem Simulator bauen

Eine zusammenhängende, testbare Anleitung für eine 8-Bit-Lehr-CPU mit Registern, ALU, gemeinsamem Bus, bytebreitem RAM, 16-Bit-Befehlen, Flags und einer mikrosequenzierten Steuereinheit.

DennyDenny
12 Min. Lesezeit
Chalkboard feature illustration for “Eine CPU von Grund auf in einem Simulator bauen”

Kurzfassung: Diese Anleitung legt vor dem Verdrahten eine vollständige Lehrarchitektur fest: einen 8-Bit-Datenpfad und Adressraum, einheitliches bytebreites RAM, 16-Bit-Befehle, die als zwei Bytes geholt werden, vier Universalregister, eine ALU und Flags, einen einzelnen gemeinsamen Bus sowie einen mikrosequenzierten Controller. Bauen und überprüfen Sie jedes Teilsystem, erzwingen Sie stets genau einen Bustreiber und führen Sie anschließend ein Programm mit fünf Befehlen aus, das 5 + 3 berechnet und 8 im Speicher ablegt.

„Eine CPU bauen“ kann alles bedeuten, vom Verbinden fertiger Blöcke bis zum Layouten von Transistoren. Dieses Tutorial liegt zwischen diesen Extremen. Sie konstruieren einen Prozessor aus digitalen Bausteinen – Registern, Multiplexern, Decodern, einer ALU, Speicher und einem Kontrollspeicher – und behandeln zugleich die internen Gatter jedes Blocks als separat testbaren Entwurf.

Das Ziel ist nicht, einen modernen kommerziellen Kern nachzuahmen. Es geht darum, den Vertrag offenzulegen, den jede Befehlsimplementierung erfüllen muss:

  1. die richtigen Befehlsbytes holen;
  2. sie beim Decodieren bewahren;
  3. genau die vorgesehenen Operanden bewegen;
  4. die definierte Operation ausführen;
  5. nur den erlaubten Architekturzustand aktualisieren; und
  6. die richtige Adresse des nächsten Befehls wählen.

1. Die Architektur zuerst festschreiben

Viele CPU-Projekte scheitern, weil sich Speicherbreite, Befehlsbreite und PC-Inkrement mitten im Aufbau ändern. Verwenden Sie diese Spezifikation durchgängig:

EigenschaftDefinition der Lehr-CPU
Datenbreite8 Bit
Adressbreite8 Bit
Speicher256 byteadressierte Speicherstellen, gemeinsame Befehle und Daten
Befehlsbreite16 Bit, als zwei aufeinanderfolgende Bytes gespeichert
Byte-Reihenfolge des BefehlsHöherwertiges Byte zuerst an der Startadresse des Befehls
Sequentieller PC-FortschrittZwei Inkremente um je ein Byte während des Holens
Allgemeine RegisterVier 8-Bit-Register: R0–R3
Operanden-LatchesA und B, je 8 Bit
Instruktionsregister16 Bit
FlagsZ (Null), N (negativ), C (Übertrag), V (vorzeichenbehafteter Überlauf)
VerbindungsstrukturEin 8-Bit-Lehrbus
SteuerungMikrostep-Zähler plus Steuer-ROM

Eine 8-Bit-Adresse wählt 256 Speicherstellen; der Speicher umfasst 256 Bytes, weil diese Architektur jede Speicherstelle ausdrücklich als ein Byte definiert. Die Startadressen der Befehle sind normalerweise gerade, da jeder Befehl zwei Bytes belegt.

Blockdiagramm

                         ┌──────────────────────┐
                         │   control ROM +      │
                         │ microstep sequencer  │
                         └──────────┬───────────┘
                                    │ control signals
                                    ▼
 ┌────┐   ┌─────┐   ┌────────────── 8-bit shared bus ───────────────┐
 │ PC │──▶│ MAR │   │       │       │       │       │       │      │
 └────┘   └──┬──┘  ┌▼──┐  ┌▼──┐  ┌▼──┐  ┌▼──┐  ┌▼──┐  ┌▼──┐  ┌▼───┐
             │     │MDR│  │ R0│  │ R1│  │ R2│  │ R3│  │ A │  │ B │
             ▼     └─┬─┘  └───┘  └───┘  └───┘  └───┘  └┬──┘  └┬───┘
        ┌────────┐   │                                    └──┬───┘
        │256 × 8 │   │                                  ┌────▼────┐
        │  RAM   │   │                                  │ 8-bit   │
        └────────┘   │                                  │  ALU    │
                     │                                  └────┬────┘
                     └──────▶ 16-bit IR ◀────────────────────┘
                                      │
                                      ▼
                                opcode / fields

Die Zeichnung ist konzeptionell. Das IR lädt zwei getrennte Bytes vom Bus, und das ALU-Ergebnis erreicht den Bus über seinen eigenen ausgewählten Ausgangspfad.

Öffnen Sie den Sequential Instruction Executor als Referenz, behandeln Sie jedoch diese schriftliche Architektur als maßgeblich für die Übung. Falls die Vorlage eine andere interne Konvention verwendet, dokumentieren Sie den Unterschied, statt beide stillschweigend zu vermischen.

2. Die Befehlsformate definieren

Die eigene ISA verwendet zwei 16-Bit-Formate.

Registerformat

15            12 11             8 7              4 3              0
┌───────────────┬────────────────┬────────────────┬────────────────┐
│ opcode [3:0]  │ RA [3:0]       │ RB [3:0]       │ RD [3:0]       │
└───────────────┴────────────────┴────────────────┴────────────────┘

Immediate-/Adressformat

15            12 11             8 7                               0
┌───────────────┬────────────────┬─────────────────────────────────┐
│ opcode [3:0]  │ RD [3:0]       │ immediate / address [7:0]       │
└───────────────┴────────────────┴─────────────────────────────────┘

In dieser Maschine mit vier Registern sind nur die Registercodes 0–3 zulässig. Der Decoder muss die Werte 4–15 zurückweisen oder sicher abfangen, statt versehentlich eine Steuerleitung auszuwählen.

OpcodeMnemonikFormatDefinierte Wirkung
0NOP–Keine Änderung des Architekturzustands außer dem bereits erhöhten PC
1LDIImmediateRD ← imm8
2LDImmediateRD ← RAM[addr8]
3STImmediateRAM[addr8] ← RD
4ADDRegisterRD ← RA + RB; Flags aktualisieren
5SUBRegisterRD ← RA − RB; Flags aktualisieren
6JMPImmediatePC ← addr8
7JZImmediateFalls Z = 1, PC ← addr8
8HLT–In den angehaltenen Controllerzustand wechseln

Alle anderen Opcodes sind ungültig. Fügen Sie einen ausdrücklichen Pfad illegal_instruction hinzu oder ordnen Sie sie während des Debuggens einer sicheren Trap/einem sicheren Halt zu.

3. Register ohne Takt-Gating aufbauen

Ein 8-Bit-Register besteht aus acht flankengesteuerten D-Flipflops mit gemeinsamem Takt und einem logischen Lade-Enable.

Für jedes Bit:

Dff={Dbus,if load=1Q,if load=0D_{\text{ff}} = \begin{cases} D_{\text{bus}}, & \text{if load}=1 \\ Q, & \text{if load}=0 \end{cases}

Implementieren Sie diese Gleichung mit einem 2:1-Multiplexer vor dem D-Eingang oder verwenden Sie die dedizierte Clock-Enable-Funktion eines Bauteils. Halten Sie jedes Zustandslement am selben globalen Takt.

Erzeugen Sie keinen lokalen Takt, indem Sie den globalen Takt mit load verunden. Unkontrolliertes Clock-Gating kann in Hardware Skew und Glitches verursachen. Integrierte Clock-Gating-Zellen und FPGA-Taktsteuerungsressourcen sind getrennte, timingbewusste Mechanismen.

Jedes mit dem Bus verbundene Register benötigt:

  • einen 8-Bit-Eingang vom Bus;
  • einen gespeicherten 8-Bit-Ausgang;
  • ein Steuersignal load_x; und
  • einen Pfad oe_x, der den Lehrbus treiben darf.

Der PC benötigt zusätzlich einen Inkrementpfad und einen expliziten Ladepfad. Das IR benötigt unabhängige Ladevorgänge für High- und Low-Byte. MAR hält eine Speicheradresse; MDR erfasst oder liefert ein Speicher-Datenbyte.

Ein Register prüfen, bevor Sie es vervielfältigen

Testen Sie diese Fälle:

  1. load = 0: Mehrere Takte lassen Q unverändert.
  2. load = 1: Q übernimmt den Bus an der aktiven Flanke.
  3. Daten ändern sich fern von der Flanke: Q ändert sich erst an der nächsten aktivierten Flanke.
  4. Reset, falls vorhanden: Q erreicht den dokumentierten Zustand.
  5. Output Enable aus: Das Register treibt den Bus nicht.

Ein digitaler Simulator kann die logische Abfolge dieses Modells überprüfen. Er bildet keine analoge Metastabilität nach und beweist kein physisches Setup- und Hold-Timing.

4. Die ALU aufbauen und jedes Flag definieren

Verwenden Sie A und B als stabile Operanden-Latches. Die ALU ist kombinatorisch: Sie darf keinen verborgenen Architekturzustand enthalten.

alu_opOperationErgebnis
000ADDA + B
001SUBA − B
010ANDA & B
011ORA | B
100XORA ^ B
101NOT~A
110SHLA << 1, mit Nullen auffüllen
111SHRlogisches A >> 1, mit Nullen auffüllen

Der ALU-Leitfaden erläutert die Rechenpfade und den Ausgangsmultiplexer ausführlich. Der Kern des arithmetischen Entwurfs besteht aus:

  • einer Kette aus acht Volladdierern;
  • einer Subtraktion, umgesetzt als A + (~B) + 1;
  • parallelen bitweisen Gattern;
  • fest verdrahteten Ein-Bit-Shiftern; und
  • einem durch alu_op ausgewählten Ausgangsmultiplexer.

Flag-Definitionen

Definieren Sie Flags als Teil der ISA, einschließlich der Frage, was Logik- und Shift-Befehle mit C und V tun.

  • Z: 1, wenn jedes Ergebnisbit null ist.
  • N: Ergebnisbit 7, das nur bei vorzeichenbehafteten Operationen als Vorzeichenbit interpretiert wird.
  • C bei ADD: Übertrag aus Bit 7.
  • C bei SUB: Wählen und dokumentieren Sie entweder „kein Borrow“ oder „Borrow“. Diese Anleitung verwendet kein Borrow, passend zur Carry-out-Konvention des Addierers.
  • V bei ADD: Die Operanden haben dasselbe Vorzeichen und das Ergebnis das entgegengesetzte Vorzeichen.
  • V bei SUB: Die Operanden haben unterschiedliche Vorzeichen und das Ergebnisvorzeichen unterscheidet sich von A.

Die äquivalente XOR-Formel aus Carry-in und Carry-out gilt für Zweierkomplement-Addition und -Subtraktion, wenn der Subtraktionspfad mit invertiertem B-Operand und Carry-in umgesetzt wird. Unter CPU-Flags finden Sie durchgerechnete Fälle.

Diese Lehr-ISA aktualisiert Z, N, C und V bei ADD und SUB. Wählen Sie für Logik- und Shift-Operationen eine dokumentierte Regel – C/V beibehalten oder neue Bedeutungen festlegen – bevor Sie Mikrocode schreiben. „Was immer die Leitungen gerade enthalten“ ist keine gültige Regel.

ALU-Prüftabelle

Testen Sie mindestens:

FallErwarteter Schwerpunkt
0x00 + 0x00Z
0xFF + 0x01vorzeichenloser Übertrag und Null
0x7F + 0x01vorzeichenbehafteter Überlauf und N
0x80 + 0x80Übertrag plus vorzeichenbehafteter Überlauf
0x00 − 0x01Borrow-Konvention und N
0x80 − 0x01vorzeichenbehafteter Überlauf
UND-/ODER-/XOR-Identitätenbitweise Rechenpfade
Shifts von 0x81Richtung, Auffüllbit, Regel für herausgeschobenes Bit

5. Den gemeinsamen Bus aufbauen

Der Lehrbus besteht aus acht Leitungen mit mehreren möglichen Quellen. Es gilt eine zentrale Invariante:

∑enabled bus drivers≤1\sum \text{enabled bus drivers} \le 1

Zu den möglichen Treibern gehören PC, MDR, R0–R3, A, B, ein IR-Byte und das ALU-Ergebnis. Die genaue Liste ist länger als „die Anzahl der Register“, zählen Sie daher jede Quelle in der Steuerspezifikation auf.

In DigiSim können Tri-State-Puffer einen gemeinsamen Bus modellieren:

  • aktivieren Sie während einer Übertragung genau eine Quelle;
  • setzen Sie ein oder mehrere Lade-Steuersignale für die nächste aktive Flanke; und
  • erfassen Sie den Bus niemals, wenn keine Quelle ihn treibt.

Buskonflikte sind Entwurfsfehler. Ein physischer Ausgangskonflikt kann falsche Spannung und übermäßigen Strom verursachen; die Folge hängt von Technologie und Dauer ab, daher ist „der Chip brennt immer durch“ keine hilfreiche Regel.

Moderne FPGA-Strukturen implementieren interne gemeinsame Busse im Allgemeinen als Multiplexer statt als echte interne Tri-State-Verdrahtung. Auch ASIC-Stile unterscheiden sich. Der Tri-State-Bus bleibt ein anschauliches Lehrmodell für Besitz und Übertragung.

Bus-Arbitrierungstest

Legen Sie bekannte Werte in R0 und R1 ab:

  • R0 = 0xAA
  • R1 = 0x55

Überprüfen Sie R0 allein, R1 allein, keinen Treiber und beide Treiber. Die letzten beiden Fälle müssen als ungültige Controllerzustände behandelt werden. Fügen Sie anschließend eine Assertion oder sichtbare Anzeige hinzu, damit spätere Mikrocodefehler sofort erkannt werden.

6. Bytebreiten Speicher anschließen

Verwenden Sie die RAM-Komponente des Simulators als architektonisches Speichermodell. Behaupten Sie nicht, dass physisches RAM buchstäblich ein Array aus D-Latches ist: SRAM, DRAM, Block-RAM und synthetisierte Registerarrays verwenden unterschiedliche Speicherzellen und Timings.

Die Schnittstelle dieser CPU lautet:

  • ADDR[7:0], getrieben von MAR;
  • DATA[7:0], übertragen über MDR oder die gesteuerte Busschnittstelle;
  • mem_read;
  • mem_write; und
  • optional ready, falls das Modell variable Latenz unterstützt.

Diese Anleitung nimmt an, dass ein Lesevorgang in einem eigenen Mikrostep angefordert und erfasst werden kann. Ist der gewählte Speicher synchron oder hat sichtbare Latenz, fügen Sie einen Wartezustand ein und erfassen Daten erst, wenn sie gültig sind.

Befehlsspeicher und Daten teilen sich dieselben 256 Bytes. Ein Harvard-Entwurf mit getrennten Speichern ist ebenfalls gültig, wäre jedoch eine andere Architektur mit einem anderen Fetch-Ablauf.

Zur Unterscheidung zwischen beschreibbaren und nichtflüchtigen/konstanten Speicherabstraktionen siehe RAM vs. ROM.

7. Den mikrosequenzierten Controller aufbauen

Der Controller wandelt Opcode, Mikrostep, Flags und Maschinenzustand um in:

  • Bus-Output-Enables;
  • Register-Lade-Enables;
  • ALU-Operation;
  • Speicher-Lese-/Schreibsteuerungen;
  • PC-Inkrement-/Ladesteuerungen;
  • Flag-Laden;
  • Laden des IR-High-/Low-Bytes; und
  • Steuerung des nächsten Mikrosteps oder des Befehlsendes.

Eine Implementierung verwendet ein Steuer-ROM:

control address = {opcode, microstep}
control word    = {oe signals, load signals, alu_op, memory controls, next control}

Flags können ausgewählte Aktionen qualifizieren – beispielsweise load_pc = microcode_load_pc AND Z für JZ – oder an der Adresse des Steuerspeichers beteiligt sein. Explizite Logik für den Folgezustand ist häufig klarer, als anzunehmen, dass der Mikrostep-Zähler immer inkrementiert.

Dies ist ein pädagogischer mikroprogrammierter Controller. Reale Prozessoren verwenden viele Kombinationen aus kombinatorischem Decode, programmierbaren Steuerspeichern, Zustandsautomaten und Sequenzern. Verwenden Sie das Label RISC/CISC nicht als Abkürzung für die Implementierung.

Sichere Standardwerte

Jeder unbenutzte Steuer-ROM-Eintrag sollte:

  • alle Bustreiber deaktivieren;
  • Speicherschreibvorgänge deaktivieren;
  • Schreibvorgänge in Architekturregister deaktivieren; und
  • einen definierten Pfad für ungültige Befehle oder Halt einschlagen.

Ein nicht initialisiertes Steuerwort darf niemals zu „beliebige Daten an beliebige Speicheradresse schreiben“ werden.

8. Zwei Befehlsbytes konsistent holen

Da RAM und Datenpfad acht Bits breit sind, jeder Befehl jedoch 16 Bits umfasst, benötigt das Holen zwei Byte-Lesevorgänge. Das High-Byte wird definitionsgemäß zuerst gespeichert.

Unter der Annahme, dass ein Mikrostep für den gewählten RAM-Lesevorgang ausreicht:

SchrittRegistertransferWichtige Steuerungen
F0MAR ← PCoe_pc, load_mar
F1MDR ← RAM[MAR]; PC ← PC + 1mem_read, load_mdr, inc_pc
F2IR[15:8] ← MDRoe_mdr, load_ir_hi
F3MAR ← PCoe_pc, load_mar
F4MDR ← RAM[MAR]; PC ← PC + 1mem_read, load_mdr, inc_pc
F5IR[7:0] ← MDRoe_mdr, load_ir_lo

Nach F5 enthält das IR einen vollständigen Befehl, und der PC zeigt bereits auf den folgenden Befehl. Das Decode wählt die Ausführungssequenz.

Benötigt das RAM eine Wartezeit von Adresse zu Daten, teilen Sie F1 und F4 in Anforderungs-, Warte- und Erfassungsschritte auf. Verbergen Sie niemals einen 16-Bit-Fetch in einer bytebreiten Übertragung.

9. Ausführungs-Mikrooperationen definieren

ADD RD, RA, RB

SchrittÜbertragung
E0A ← R[RA]
E1B ← R[RB]
E2R[RD] ← A + B; FLAGS ← ALU flags
E3Befehl beenden; zu F0 zurückkehren

RA, RB und RD wählen über decodierte Enables eines von R0–R3. Ungültige Registercodes müssen den Pfad für ungültige Befehle einschlagen.

LDI RD, imm8

SchrittÜbertragung
E0R[RD] ← IR[7:0]
E1Befehl beenden

LD RD, addr8

SchrittÜbertragung
E0MAR ← IR[7:0]
E1MDR ← RAM[MAR]
E2R[RD] ← MDR
E3Befehl beenden

ST RD, addr8

SchrittÜbertragung
E0MAR ← IR[7:0]
E1MDR ← R[RD]
E2RAM[MAR] ← MDR
E3Befehl beenden

JMP und JZ

  • JMP: PC ← IR[7:0], dann beenden.
  • JZ: PC nur laden, wenn Z gleich 1 ist, dann unabhängig davon beenden.

HLT

Wechseln Sie in einen stabilen Controllerzustand, der keine Schreibvorgänge ausgibt. Einen manuell angesteuerten Simulator-Takt anzuhalten ist praktisch, doch der Architekturzustand muss klar definiert bleiben, selbst wenn weitere Taktflanken eintreffen.

10. Das Testprogramm von Hand assemblieren und laden

Das Programm lädt 5 und 3, addiert sie in R2, speichert das Ergebnis an Adresse 0x10 und hält an.

StartadresseBefehl16-Bit-WortBytes im Speicher
0x00LDI R0, #50x10050x10 0x05
0x02LDI R1, #30x11030x11 0x03
0x04ADD R2, R0, R10x40120x40 0x12
0x06ST R2, 0x100x32100x32 0x10
0x08HLT0x80000x80 0x00

Laden Sie diese zehn Bytes an die Adressen 0x00 bis 0x09, dann setzen Sie zurück:

  • PC = 0x00
  • Controller-Mikrostep = F0
  • angehalten = 0
  • alle Bustreiber deaktiviert, außer wenn das Steuerwort einen auswählt

Führen Sie den ersten Fetch im Einzelschritt aus und bestätigen Sie:

  1. nach F2 beträgt das IR-High-Byte 0x10;
  2. nach F5 lautet das IR 0x1005;
  3. der PC ist 0x02;
  4. LDI legt 0x05 in R0 ab; und
  5. kein anderes Register und keine andere Speicherstelle ändert sich.

Beim Halt:

  • R0 = 0x05
  • R1 = 0x03
  • R2 = 0x08
  • RAM[0x10] = 0x08
  • PC = 0x0A

Diese Endwerte sind notwendig, aber nicht hinreichend. Eine fehlerhafte CPU könnte eine andere Speicherstelle beschädigen und sie dennoch erzeugen.

11. Invarianten prüfen, nicht nur das Endergebnis

Fügen Sie Prüfungen hinzu für:

  • nie mehr als ein Bus-Output-Enable gleichzeitig;
  • Speicherschreiben nur während des ST-Schreibschritts;
  • IR-Bytes nur während F2 und F5 geladen;
  • PC bei jedem normalen Fetch genau zweimal inkrementiert;
  • Flags nur von den Befehlen geschrieben, die sie laut Definition aktualisieren;
  • ein ungültiger Opcode oder Registercode verursacht keinen unbeabsichtigten Schreibvorgang;
  • der angehaltene Zustand erzeugt keine Architektur-Nebeneffekte; und
  • Reset erreicht einen bekannten Controllerzustand.

Zeichnen Sie für jeden Befehl einen Trace auf aus:

clock, microstep, PC, IR, bus owner, bus value,
register loads, memory read/write, ALU op, flags

Wenn ein Test fehlschlägt, suchen Sie den ersten Zyklus, der vom erwarteten Trace abweicht. Vom falschen Endwert aus zu debuggen führt Sie gewöhnlich mehrere Schritte zu spät.

Der Leitfaden zum Debuggen mit dem Oszilloskop erklärt, wie Sie modellierte Wellenformen untersuchen, ohne sie mit physischem Timing-Sign-off zu verwechseln.

12. Häufige Fehlermodi

SymptomWahrscheinliche UrsacheZuerst prüfen
IR enthält vertauschte BytesDie Byte-Reihenfolge des Befehls unterscheidet sich zwischen Lader und FetchAdressen PC und PC+1
PC erhöht sich um vierDas Inkrement geschieht zweimal je Byte oder Breitenannahmen wurden vermischtF0–F5-Trace
Bus ist umkämpftZwei Output-Enables teilen sich ein SteuerbitDecodiertes Steuerwort
Bus wird undriven erfasstQuell-Enable fehltAssertion zum Busbesitzer
ADD schreibt in das falsche RegisterRD-Feld-Decodierung oder Registercode-ReihenfolgeIR[3:0] und Ladeleitungen
Store beschädigt BefehlsspeicherAdresse/Immediate oder Timing von mem_writeMAR und ST E2
Branch verwendet veraltetes ZFlags wurden nicht geschrieben oder JZ tastet den falschen Zustand abADD/SUB E2 und JZ
Controller kehrt nie zum Fetch zurückEnd-/Folgezustandseintrag fehltMikrostep-Übergang
Ungültiger Opcode führt eine Operation ausUnbenutztes Steuer-ROM wurde nicht sicher initialisiertStandard-Steuerwort

13. Was diese CPU lehrt – und was nicht

Dieser Entwurf lehrt Registertransfer, Befehlsencodierung, Busbesitz, ALU-Flags, Speichersequenzierung und Controllerzustand.

Er modelliert nicht:

  • RAM oder Logik auf Transistorebene;
  • Cache-Hierarchie oder virtuellen Speicher;
  • spekulative oder Out-of-Order-Ausführung;
  • analoges Taktverhalten oder Metastabilität;
  • physische Ausbreitungs-, Routing-, Leistungs- oder Temperaturgrenzen; oder
  • eine produktive Sicherheits- oder Ausnahme-Architektur.

Sein einzelner gemeinsamer Bus ist absichtlich leicht zu beobachten, keine Behauptung über die interne Topologie einer modernen CPU.

Erweiterungen in einer sicheren Reihenfolge

  1. Automatisierte Befehlstests hinzufügen. Überprüfen Sie jeden Opcode und jede ungültige Codierung, bevor Sie Funktionen ergänzen.
  2. UND, ODER, XOR und Schiebeoperationen hinzufügen. Definieren Sie zunächst das Verhalten der Statusflags und fügen Sie anschließend die Steuersequenzen hinzu.
  3. Einen bedingten Branch mit direktem Vergleich hinzufügen. Das zeigt den Unterschied zwischen flagbasierten und Compare-and-Branch-ISAs.
  4. Einen Stack-Pointer und Call/Return hinzufügen. Legen Sie Stack-Wachstum, Byte-Reihenfolge und Überlaufverhalten fest.
  5. Einen Interrupt-Zustandsautomaten hinzufügen. Sichern Sie Rückkehradresse und Flags vor dem Laden eines Vektors; definieren Sie Priorität und Maskierung.
  6. Befehls- und Datenspeicher trennen. Vergleichen Sie den neuen Fetch-Ablauf mit dem Entwurf mit gemeinsamem Speicher.
  7. Erst pipelinen, nachdem die Mehrzyklus-Referenz besteht. Definieren Sie dann Stage-Gültigkeit, Stalls, Forwarding, Flushes und Ausnahmen.

Nutzen Sie die Fetch-Decode-Execute-Vertiefung und den Leitfaden zu Instruktionsregister/Decode, bevor Sie die Pipeline-Erweiterung versuchen.

Ausprobieren

Nutzen Sie den Sequential Instruction Executor zur Orientierung, das 8-Bit-ALU-System zur separaten Überprüfung der Arithmetik und das CPU-Flags-Register zum Testen des Statusverhaltens. Halten Sie eine schriftliche Architektur und eine Mikrooperationstabelle neben dem Schaltplan bereit.

Der wichtige Meilenstein ist nicht, die 8 einmal zu sehen. Er besteht darin, erklären zu können, warum jede Zustandsänderung autorisiert war, sie in einem Trace zu reproduzieren und zu beweisen, dass sich kein anderer Zustand geändert hat.

Machen Sie aus der Idee eine funktionierende Schaltung.