Computerarchitektur · Modul 03 · Kapitel 2 · Folien 29–59

Ein Befehl sind 32 Bit

Instruction Encoding heißt: Wie wird aus add x5, x6, x7 eine 32-Bit-Zahl, die der Prozessor direkt ausführen kann? RISC-V hat dafür sechs feste Schablonen. Das ganze Kapitel erklärt diese Schablonen und warum sie genau so aussehen.

add x5, x6, x7 → 0x007302B3. Jede Farbe ist ein Feld. Die gleichen Farben gelten auf der ganzen Seite.

Vorspann · 5 Minuten

Worum es in dem Kapitel geht

Kernidee 1

Jeder Befehl ist genau 32 Bit lang und in feste Felder aufgeteilt: ganz rechts der opcode, daneben die Register, dazu Zusatzinfos (funct3/funct7) und eine Konstante.

Kernidee 2

Es gibt sechs Formate: R, I, S, B, U und J. Sie unterscheiden sich fast nur darin, wo die Konstante steckt. Die Register und der Opcode bleiben immer an ihrem Platz.

Das ganze Kapitel in einem Bild. Tippe auf rs1 oder opcode, dann siehst du, dass sich nur die gelben Teile (die Konstante) verschieben.

Die sechs Formate

  • R 3 Register · add x5, x6, x7
  • I Register + Konstante · addi x5, x6, -10, außerdem Laden mit lw
  • S Speichern · sw x5, 12(x6), die Konstante ist zweigeteilt
  • B bedingter Sprung · beq x5, x6, +16
  • U obere 20 Bit laden · lui x5, 0x12345
  • J Sprung, der sich die Rücksprungadresse merkt · jal x1, +2048

Die Begriffe

  • opcode: die Befehlsgruppe (Bit 6:0)
  • funct3 / funct7: die genaue Operation in der Gruppe, z. B. add oder sub
  • rd: Zielregister, dort landet das Ergebnis
  • rs1, rs2: Quellregister, die gelesen werden
  • imm: Konstante, die direkt im Befehl steht
Frage zum Einstieg

Warum lohnt es sich, dass rs1, rs2 und opcode in jedem Format an derselben Stelle sitzen? Denk an den Decoder aus Modul 02 (CISC gegen RISC, Iron Law).

Auflösung

Die Registerdatei kann schon lesen, während der Opcode noch dekodiert wird. Die Bits 19:15 und 24:20 hängen direkt an den Leseports, ohne Multiplexer. Dadurch ist der Decode-Schritt schneller und der Takt kürzer. Der Preis dafür sind die zerstückelten Konstanten. Mehr dazu unten bei „Warum das Ganze so aussieht“.

Vertiefung

Was gewollt ist, etwas genauer

Immer 32 Bit

Jeder Basisbefehl ist genau 4 Byte lang. Der nächste Befehl liegt deshalb immer bei PC + 4. Die Hardware muss nie erst herausfinden, wie lang ein Befehl ist (bei x86 muss sie das).

Der Opcode sagt das Format

Die unteren 7 Bit (6:0) sind der Opcode. Daran erkennt die Hardware, welche der sechs Schablonen gilt und wie der Rest zu lesen ist.

Register stehen immer am selben Platz

rs1, rs2 und rd liegen in jedem Format an denselben Bitpositionen. Die Registerdatei kann also schon lesen, bevor der Befehl fertig dekodiert ist.

Dafür werden die Konstanten zerstückelt

Damit die Register fest bleiben, werden die Immediates (Konstanten im Befehl) auf die übrigen Bits verteilt, teils in seltsamer Reihenfolge. Das sortiert der Assembler. Die Hardware kostet es nur Leitungen und keine Logik.

2.1 · Major Opcode Map

Der Opcode: 7 Bit, die alles entscheiden

Bei 32-Bit-Befehlen sind Bit 1:0 immer 11. Die eigentliche Klasse steht in Bit 6:2. Für die Klausur reichen die zehn wichtigen Opcodes:

Opcode [6:0]NameFormatBefehle

Auf Folie 33 steht die volle Tabelle (Floating Point, Atomics, custom, reserved). Die Logik dahinter: Bit 6:5 wählt die Spalte, Bit 4:2 die Zeile. Auswendig lernen musst du die nicht.

2.2 · Instruction Formats

Die sechs Schablonen übereinander

Das Bild aus dem Vorspann als Tabelle. Alles in Gelb ist Immediate. Gleiche Gelbtöne bedeuten gleiche Immediate-Bits.

FormatWofürRegisterImmediateBereich
RRechnen mit zwei Registern: add sub and or xor sll sltrd, rs1, rs2keins–
IRechnen mit Konstante, Laden, jalr: addi lw lb jalrrd, rs112 Bit−2048 … +2047
SSpeichern: sw sh sbrs1 (Basis), rs2 (Daten)12 Bit, geteilt−2048 … +2047
BBedingt springen: beq bne blt bge bltu bgeurs1, rs213 Bit, Bit 0 = 0−4096 … +4094
UObere 20 Bit setzen: lui auipcrd20 Bit (oben)imm ≪ 12
JUnbedingt springen und merken: jalrd21 Bit, Bit 0 = 0± 1 MiB

Merkhilfe: S ist I mit einem zweiten Quellregister statt rd. B ist S mit umsortierten Bits. J ist U mit umsortierten Bits. Eigentlich gibt es also nur R, I, S, U und zwei Varianten davon.

2.2 · Hardware Secrets

Warum das Ganze so aussieht

Guntoro begründet jede Designentscheidung mit der Hardware. Das ist der rote Faden. Wenn eine Prüfungsfrage mit „Why“ anfängt, steht die Antwort meistens hier.

Trick 1

Feste Registerfelder

Bit 19:15 hängt direkt an Leseport 1 der Registerdatei, Bit 24:20 an Leseport 2. Kein Multiplexer heißt keine Verzögerung („zero mux delay“). Bei CISC muss der Decoder erst Länge und Adressierungsart verstehen.

Trick 2

Bit 31 ist immer das Vorzeichen

In I, S, B, U und J steht in Bit 31 das höchste Immediate-Bit. Für die Sign-Extension reicht deshalb ein einziger Draht von Bit 31, ganz ohne Auswahl-Logik.

Trick 3

Gleiche Bits an gleicher Stelle

imm[10:5] steht in I, S, B und J immer in Bit 30:25. Die Leitungen zum Immediate-Generator werden dadurch geteilt (im Bild oben derselbe Gelbton).

Trick 4

Zerstückelte Immediates

B und J sehen chaotisch aus, weil die Register fest bleiben sollen. Das Umsortieren ist reine Verdrahtung und kostet keine Gatterlaufzeit.

Bonus

Bit 0 wird nicht gespeichert

Sprungziele sind immer gerade (Befehle sind mindestens 2-Byte-aligned). Deshalb fehlt Bit 0 bei B und J, die Hardware hängt eine 0 an. Mit denselben Bits erreicht man so den doppelten Bereich.

Ergebnis

Ein einfacher Fetch und Decode

Fetch heißt immer PC + 4. Der Decode liest Opcode und Register parallel. Genau das will RISC: kurzer Takt, CPI ≈ 1 (Iron Law aus Modul 2).

Das Puzzle

Wie B- und J-Immediates zusammengesetzt werden

Oben steht, wo die Bits im Befehl liegen, unten die Konstante, die daraus gebaut wird. Die Farben zeigen, welches Stück wohin wandert.

B-Type · imm[12|10:5] … imm[4:1|11]

J-Type · imm[20|10:1|11|19:12]

2.3 – 2.8 · Beispiele aus den Folien

Jedes Format einmal durchgerechnet

Die Werte stehen direkt in den Feldern. Lies von rechts nach links: Opcode, dann rd, funct3, rs1 und so weiter.

2.7 / 2.8 · Kombis

32-Bit-Werte aus zwei Befehlen

Ein Befehl kann keine 32-Bit-Konstante enthalten, weil der Platz fehlt. Deshalb gibt es feste Paare: U-Type liefert die oberen 20 Bit, ein I-Type die unteren 12.

lui + addi

Beliebige Konstante: lui t0, 0x12345 ergibt t0 = 0x12345000, danach addi t0, t0, 0x678.

auipc + lw/addi

rd ← PC + (imm ≪ 12). Die Adresse wird relativ zum PC berechnet, damit läuft das Programm an jeder Ladeadresse (position-independent code, ±2 GB).

jalr (I-Type!)

rd ← PC+4; PC ← (rs1 + imm) & ~1. Daraus wird ret = jalr x0, x1, 0, außerdem Funktionszeiger und mit lui davor ein absoluter Sprung.

jal (J-Type)

rd ← PC+4; PC ← PC + offset. Mit rd = x1 (ra) ist es ein Funktionsaufruf, mit rd = x0 ein einfacher Sprung.

Stolperfalle aus der Praxis: Weil addi sign-extended, muss man bei lui 1 dazuzählen, wenn Bit 11 der unteren 12 Bit gesetzt ist. Das kommt in Modul 03 Kapitel 7 (Pseudo-Instructions) wieder.

Das Kochrezept

So gehst du an jede Aufgabe ran

Assembler → Maschinencode

  1. Befehl → Format bestimmen (Tabelle oben).
  2. Opcode und funct3 (bei R auch funct7) nachschlagen.
  3. Registernummern in 5 Bit umwandeln (x6 → 00110).
  4. Immediate in Zweierkomplement mit der richtigen Breite (12, 13, 20 oder 21 Bit).
  5. Immediate-Bits nach Schablone verteilen, bei B und J Bit 0 weglassen.
  6. Alles von Bit 31 bis 0 hinschreiben, in 4er-Gruppen teilen und in Hex umrechnen.

Maschinencode → Assembler

  1. Hex → 32 Bit binär.
  2. Die letzten 7 Bit sind der Opcode. Daraus folgt das Format.
  3. Nach Schablone schneiden: rd = 11:7, funct3 = 14:12, rs1 = 19:15, rs2 = 24:20.
  4. funct3 und funct7 sagen den genauen Befehl (z. B. add oder sub).
  5. Immediate zusammensetzen und Vorzeichen beachten (Bit 31).

funct3-Tabellen für den Spickzettel

funct3000001010011100101110111
OP / OP-IMMadd/subsllsltsltuxorsrl/sraorand
BRANCHbeqbne––bltbgebltubgeu
LOADlblhlw–lbulhu––
STOREsbshsw–––––

funct7 0100000 macht aus add ein sub und aus srl ein sra. Sonst ist es 0000000.

Was das Kapitel von dir will

Die vier Übungen (Folie 55–58)

Hier ohne Lösung, damit du sie danach kalt rechnen kannst. Mit dem Kochrezept oben sind alle vier machbar.

2.1 · Decode

0x003102B3: Opcode, Format, alle Felder und der Assembler-Befehl.

2.2 · Decode

0x00C32283: Opcode, Format, Felder, 12-Bit-Immediate und der Assembler-Befehl.

2.3 · Encode

sw x5, 8(x10) nach Binär und Hex.

2.4 · Sprungweite

bne x5, x6, LOOP steht bei 0x00001008, LOOP bei 0x00000FC0. Wie groß ist der Offset und wie sieht das 13-Bit-Immediate aus?

Achtung: zwei Beispiele auf den Folien sind wahrscheinlich falsch

Folie 48 beq x5, x6, +16: Die Binärzahl auf der Folie stimmt, aber das Hex dazu nicht. Richtig ist 0x00628863 statt 0x00620863.

Folie 53 jal x1, +2048: 2048 = 2¹¹, also ist imm[11] = 1 und imm[10:1] = 0. Die Folie setzt imm[10], das wäre +1024. Richtig ist 0x001000EF statt 0x400000EF.

Folie 37 sagt, imm[4:1] liege in I, S, B und J immer in Bit 11:8. Das stimmt nur für S und B. In I und J liegt es in Bit 24:21 (siehe das Bild mit den sechs Schablonen).

Kann auch ein Fehler im PDF-Text sein. In der Vorlesung kurz nachfragen lohnt sich, das sind dankbare Prüfungsfragen.