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
Back to top