Common Terms & Abbreviations
- PC: Program Counter — holds the address of the next instruction.
- R[0..31]: General-purpose registers x0–x31 (as an array
processor->R
). - imm: Immediate value encoded in the instruction.
- funct3 / funct7: Fields in the RISC-V instruction word that further distinguish operations.
- rs1, rs2: Source register indices.
- rd: Destination register index.
- SB-type: “S”tore / “B”ranch format.
- U-type, UJ-type: Upper-immediate formats (LUI, JAL).
- LENGTH_BYTE / HALF_WORD / WORD: Alignment constants for memory accesses.
- sign_extend_number(x, n): Helper that sign‐extends an n-bit immediate to 32 bits.
- get_branch_offset, get_store_offset, get_jump_offset: Helpers that reconstruct PC-relative offsets.
void execute_instruction(uint32_t bits, Processor *proc, Byte *mem) { … }
opcode (7 bits) decides which helper to call.
After a normal instruction, does proc->PC += 4 to fetch the next.
R-Type Instructions
void execute_rtype(Instruction instr, Processor *proc) { … }
Handles R-type (register-to-register) ops:
funct3 = 0x0 + funct7 = 0x00 → ADD
funct3 = 0x0 + funct7 = 0x20 → SUB
funct3 = 0x1 → SLL (Shift Left Logical)
…and others (SLT, XOR, SRL/SRA, OR, AND).
Updates proc->R[rd] with the result, then proc->PC += 4.
I-Type (Except Loads)
void execute_itype_except_load(Instruction instr, Processor *proc) { … }
funct3 selects:
0x0 → ADDI (Add Immediate)
0x1 → SLLI (Shift Left by Immediate)
0x2 → SLTI (Set Less Than Immediate, signed)
0x3 → SLTIU (… unsigned)
0x4 → XORI
0x5 → SRLI/SRAI (logical vs arithmetic right shift)
0x6 → ORI
0x7 → ANDI
Sign-extension: for all except shift-immediates, does
imm = sign_extend_number(instr.itype.imm, 12);
Writes result to R[rd], then PC += 4.
Branches (SB-Type)
void execute_branch(Instruction instr, Processor *proc) { … }
funct3 cases:
0x0 → BEQ (Branch if Equal)
0x1 → BNE (Branch if Not Equal)
(others: BLT, BGE, …)
Uses get_branch_offset(instr) for the signed 13-bit offset.
If condition is true: PC += offset; else: PC += 4.
Loads (I-Type, funct3 = 0x0–0x5)
void execute_load(Instruction instr, Processor *proc, Byte *mem) { … }
funct3:
0x0 → LB (Load Byte, sign-ext 8 bits)
0x1 → LH (Load Half, sign-ext 16 bits)
0x2 → LW (Load Word, 32 bits)
0x4 → LBU (Load Byte Unsigned)
0x5 → LHU (Load Half Unsigned)
Computes address = R[rs1] + sign_extend_number(imm,12).
Uses load(mem, address, LENGTH_…) + optional sign-extension.
Then PC += 4.
Stores (S-Type)
void execute_store(Instruction instr, Processor *proc, Byte *mem) { … }
funct3:
0x0 → SB (Store Byte)
0x1 → SH (Store Half)
0x2 → SW (Store Word)
offset = get_store_offset(instr) builds the 12-bit immediate.
address = R[rs1] + offset, then store(mem, address, LENGTH_…, R[rs2]).
Finally PC += 4.
JAL (UJ-Type)
void execute_jal(Instruction instr, Processor *proc) { … }
UJ-type encodes a 20-bit immediate → 21-bit signed offset.
Writes proc->R[rd] = PC + 4 (return address).
Updates PC += get_jump_offset(instr).
LUI (U-Type)
void execute_lui(Instruction instr, Processor *proc) { … }
U-type: 20 most significant bits of a 32-bit constant.
Does R[rd] = instr.utype.imm << 12.
Then PC += 4.
ECALL (Trap)
void execute_ecall(Processor *proc, Byte *mem) { … }
In this lab, always prints “Exiting simulator” and exit(0).
In a full OS, would dispatch on proc->R[a7] (syscall number).
Memory Helpers
Word load(Byte *mem, Address addr, Alignment a) { … } void store(Byte *mem, Address addr, Alignment a, Word v) { … }
Little-endian layout: low address holds LSB.
LENGTH_BYTE reads/writes 1 byte, LENGTH_HALF_WORD = 2 bytes, LENGTH_WORD = 4 bytes.
Shifts and masks assemble/disassemble multi-byte values.