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.