Skip to content

Meeting Summary: CS 631-01 Systems Foundations

  • Date: Mar 26, 2026
  • Time: 08:14 AM Pacific Time (US and Canada)
  • Meeting ID: 870 0988 0761

Quick Recap

  • Greg delivered the main lecture on Project 3: implementing a RISC-V emulator in Rust.
  • He explained the emulator’s structure (memory, processor registers, and the PC) and how it mimics a 64-bit RISC-V processor.
  • Topics covered:
  • Emulator state initialization
  • Instruction decoding and execution
  • Instruction formats (I-type, R-type, branch)
  • Bit extraction/manipulation for 32-bit instruction words
  • Use of unsafe Rust for pointer arithmetic and memory access
  • Examples from the starter code repository
  • Guidance:
  • Build support incrementally by examining programs and instructions from the RISC-V specification.

Next Steps

  • Greg:
  • Push the starter code to the in-class repository.
  • Add the full RISC-V specification (reference manual) to the repository.
  • Provide an updated guide for the cache assignment.
  • Cover cache topics in the next class session (Tuesday).
  • Students:
  • Implement the emulator by extending the starter code to support all required instructions and functions, following the provided guidance and specification.

Summary

RISC-V Emulator Project Overview

  • Project 3 focuses on building a RISC-V emulator in Rust to deepen understanding of low-level instruction formats.
  • The emulator mirrors systems used by bytecode interpreters (e.g., Java, Python).
  • Core components to model:
  • Memory
  • 64-bit RISC-V processor state
  • General-purpose registers
  • Program Counter (PC)

32-bit Processor Emulator Design Considerations

  • The lecture also covered a design that emulates a 32-bit instruction stream and processor conventions.
  • Topics included:
  • Managing registers, the PC, and stack space
  • Decoding instructions and dispatching execution
  • Computing target addresses from base addresses and registers
  • Using unsafe Rust to bypass strict memory safety when needed
  • The Rust type usize, which depends on the host architecture’s pointer size

RISC-V RVState Struct Implementation

  • The RVState struct targets a 64-bit RISC-V architecture:
  • Uses u64 for registers
  • Uses pointer-based fields for PC and stack
  • The stack is accessed indirectly via the stack pointer (SP), initialized to the top of a byte array
  • Common register conventions:
  • X0 (zero register)
  • RA (return address)
  • SP (stack pointer)
  • A0–A3 may be used for argument passing depending on the program

Program Emulation Initialization

  • The initialization function sets up registers and memory pointers using an immutable reference to the RV state.
  • Return mechanism:
  • Setting RA to zero indicates program termination when PC becomes zero.
  • Function calls and returns:
  • Follow standard calling conventions with preservation/restoration of registers across nested calls.

Stack Pointer Initialization

  • SP is initialized to one byte past the end of the allocated stack region.
  • Key points:
  • Careful pointer arithmetic is required
  • The stack array is treated as a pointer
  • Emulation proceeds in a loop while PC != 0

Instruction Emulation Function

  • Safeguard: ensure the zero register (X0) remains unchanged.
  • Steps:
  • Fetch the instruction word at PC
  • Decode using helper functions and Rust match on opcode (and related fields)
  • Action item:
  • Verify opcode bit ranges against the reference manual (to be shared)

RISC-V Specification Overview

  • The RISC-V specification (covering 32-bit and 64-bit architectures) details core instructions and opcodes.
  • Instruction types discussed:
  • R-type, I-type, S-type (among others)
  • Emulator responsibilities:
  • Break down 32-bit instruction words using helper functions and bit masks
  • Note:
  • W-suffixed instructions apply to RV64; some programs may use them
  • Be flexible with opcode naming across formats where helpful

Sign Extension Techniques

  • Handling 12-bit immediates in 32-bit encodings:
  • Extract, shift, and sign-extend to 64-bit values correctly
  • Unsafe Rust operations:
  • Pointer arithmetic
  • Unaligned loads/stores to bypass compiler alignment checks
  • Plan:
  • Brief pause to review before continuing with implementation details

Rust vs. C: Practical Differences

  • Rust is stricter and safer than C due to its safety model (e.g., ownership, borrowing).
  • Topics covered:
  • Advancing the PC using pointer ops
  • Loads/stores and integer–pointer conversions
  • A standard module providing generic read/write helpers for addresses

Instruction Classification Methods

  • Two approaches to distinguish instructions:
  • Using opcode + func3 + func7 patterns (preferred for readability)
  • Bit-by-bit extraction (more verbose)
  • Arithmetic behavior:
  • Use wrapping operations; RISC-V ignores overflow carry beyond register width
  • Focus on I-type instructions such as ADDI and JALR:
  • Extract and sign-extend 12-bit immediates
  • Match opcodes to dispatch handlers
  • Initialize emulated state with function pointers and arguments as needed
  • Demonstrations included:
  • Working example for ADDI
  • Clarification that certain JALR forms are specialized variants with fixed fields

JALR, Loads, and Branches

  • JALR:
  • Store PC + 4 in RA
  • Update PC to the computed target
  • Loads:
  • Construct addresses and use unaligned reads for memory access
  • Branches:
  • Offsets are encoded across multiple bit fields and must be reconstructed
  • Only signed comparisons are required for this project
  • Recommendation:
  • Implement incrementally and add support for programs step by step
  • Deadline:
  • Project due next Thursday