Programming lesson
Einzyklus-CPU-Implementierung in Verilog: Schritt-für-Schritt-Tutorial für CDA 4205L Lab #8
Lerne in diesem Tutorial, wie du eine Einzyklus-CPU in Verilog implementierst – inklusive Instruktionsspeicher, ALU und Steuerwerk. Perfekt für CDA 4205L Lab #8.
Einleitung: Warum eine Einzyklus-CPU?
Im Jahr 2026, wo KI-Chips und Gaming-Hardware immer komplexer werden, ist das Verständnis grundlegender CPU-Architekturen wichtiger denn je. In diesem Tutorial zeigen wir dir, wie du eine Einzyklus-CPU in Verilog implementierst – genau das, was du für CDA 4205L Lab #8 brauchst. Der Fokus liegt auf dem fehlenden Instruktionsspeicher, aber wir behandeln auch die Integration von ALU und Steuerwerk.
Grundlagen: Was ist eine Einzyklus-CPU?
Eine Einzyklus-CPU führt jeden Befehl in einem einzigen Taktzyklus aus. Das ist zwar nicht besonders schnell, aber ideal zum Verständnis der CPU-Interna. Stell dir vor, du spielst ein Rennspiel: Jede Runde entspricht einem Takt, und in dieser Runde muss das Auto (die CPU) alles erledigen – Gas geben, lenken, bremsen. So ähnlich arbeitet eine Einzyklus-CPU.
Voraussetzungen: Was du aus Lab #7 mitbringst
Dieses Tutorial baut auf deiner ALU und deinem Steuerwerk aus Lab #7 auf. Du solltest bereits wissen, wie man eine ALU mit Operationen wie Addition, Subtraktion und bitweisen Operationen entwirft. Auch das Steuerwerk, das aus dem Opcode die richtigen Steuersignale generiert, sollte dir vertraut sein.
Schritt 1: Den Instruktionsspeicher erstellen
Der Instruktionsspeicher (instruction memory) ist das Herzstück dieses Labs. Er speichert den Maschinencode, den dein Assembler aus Lab #3 und #4 erzeugt hat. In Verilog deklarierst du ihn als Register-Array:
reg [DATA_WIDTH-1:0] inst_mem[0:NUM_MEM_CELLS-1];Dabei sind DATA_WIDTH (z.B. 32 Bit) und NUM_MEM_CELLS (Anzahl der Speicherzellen) Parameter, die du später anpassen kannst. Das macht dein Modul wiederverwendbar – ähnlich wie bei einer App, die für verschiedene Bildschirmgrößen konfigurierbar ist.
Schritt 2: Speicher mit dem Maschinencode initialisieren
In einem initial-Block verwendest du $readmemb oder $readmemh, um die Binär- oder Hex-Datei einzulesen:
initial begin
$readmemb("program.mem", inst_mem);
endBeachte: Der Dateiname wird als Makro definiert (z.B. `PROGRAM_FILE). So kannst du einfach das Programm austauschen, ohne den Code zu ändern. Das ist praktisch, wenn du verschiedene Algorithmen testen willst – ähnlich wie bei einem Spiel, bei dem du verschiedene Levels laden kannst.
Schritt 3: Kontinuierliche Zuweisung für den Ausgang
Da die Einzyklus-CPU keinen Takt für den Speicher verwendet, musst du den Ausgang kontinuierlich zuweisen:
assign data = inst_mem[address];Hier wird address durch den Programmzähler (PC) gespeist. Jede Änderung der Adresse führt sofort zu einem neuen Befehl am Ausgang. Das ist wie bei einer Echtzeit-Datenbank: Sobald du eine Abfrage stellst, bekommst du sofort das Ergebnis.
Schritt 4: Integration in die Gesamt-CPU
Jetzt verbindest du den Instruktionsspeicher mit ALU und Steuerwerk. Der ausgelesene Befehl wird in Opcode, Registeradressen und Immediate-Wert aufgeteilt. Das Steuerwerk interpretiert den Opcode und setzt die Signale für die ALU, den Datenspeicher und die Multiplexer. Ein Beispiel für einen R-Typ-Befehl (z.B. ADD):
// Instruktion: add rd, rs1, rs2
// Opcode: 0110011
// Steuersignale: ALUSrc = 0, MemWrite = 0, RegWrite = 1, etc.Die ALU führt dann die Operation aus und das Ergebnis wird in das Registerfile geschrieben. Alles in einem Takt – das ist die Essenz der Einzyklus-CPU.
Häufige Fehler und Tipps
- Vergiss die Parameter nicht: Wenn du den Instruktionsspeicher instantziierst, überschreibe die Default-Werte mit den tatsächlichen Größen. Sonst passt dein Programm nicht hinein.
- Dateipfad prüfen: Stelle sicher, dass die
.mem-Datei im richtigen Verzeichnis liegt. In Vivado ist das oft das Projektverzeichnis. - Endianness: Achte darauf, dass die Bytereihenfolge in der Speicherdatei mit deiner Erwartung übereinstimmt. RISC-V verwendet Little-Endian.
Beispiel: Ein einfaches Programm
Angenommen, du hast ein Programm, das zwei Zahlen addiert. Der Assembler erzeugt Maschinencode wie:
@00000000
00100000000000000000000000100011
... (Binär)Lade diese Datei mit $readmemb und simuliere die CPU. Überprüfe, ob nach der Ausführung das richtige Ergebnis im Register steht. Das ist wie ein Debugging in der Spieleentwicklung: Du setzt Breakpoints und prüfst die Variablen.
Erweiterungen und Ausblick
Eine Einzyklus-CPU ist langsam, aber lehrreich. In der Praxis werden mehrzyklische oder pipeline-basierte CPUs verwendet, wie sie in modernen GPUs oder KI-Beschleunigern stecken. Wenn du dieses Lab verstanden hast, fällt dir der Einstieg in fortgeschrittene Architekturen leichter. Vielleicht entwickelst du ja eines Tages den Chip für das nächste iPhone oder eine KI für autonome Autos – die Grundlagen legst du heute.
Fazit
Mit diesem Tutorial hast du gelernt, wie man einen Instruktionsspeicher für eine Einzyklus-CPU in Verilog erstellt und in den Gesamtentwurf integriert. Du kennst jetzt die wichtigsten Schritte: Speicher deklarieren, initialisieren und kontinuierlich auslesen. Kombiniert mit deiner ALU und dem Steuerwerk aus Lab #7 bist du bereit, die komplette CPU zu simulieren und zu testen. Viel Erfolg bei Lab #8!