Meeting Summary: CS 631-02 Systems Foundations¶
- Date: Mar 26, 2026
- Time: 02:49 PM Pacific
- Meeting ID: 882 2309 0019
Quick Recap¶
The session introduced the RISC-V emulator project. Greg: - Compared major CPU architectures, noting similarities among RISC-V, MIPS, and ARM, and key differences with x86 (e.g., variable-length instructions). - Presented the structure of a RISC-V emulator in Rust, including register state, the program counter (PC), memory layout, and stack emulation. - Explained decoding for instruction types (R-type, I-type, memory, branches, jumps). - Demonstrated unsafe pointer handling in Rust where needed. - Walked through the emulator’s core execution loop and how to extend the starter code to support more instructions. - Previewed an upcoming cache memory component to be implemented.
Next Steps¶
- Students: Read the cache memory guide before Tuesday’s class to prepare for the cache simulator extension.
- Students: Implement the required code changes for the cache memory simulator as specified in the project assignment.
- Students: Extend the emulator by adding support for additional RISC-V instructions used in the provided assembly programs.
- Students: Bring their own fibrec file to the Project 3 repository.
- Students: Complete as much of the emulator as possible before Tuesday, leaving primarily the cache and analysis components.
Summary¶
Assembly Language Architecture Comparison¶
- RISC-V, MIPS, and ARM share similar, regular instruction formats; x86 differs notably due to variable-length instructions.
- Modern x86 implementations often translate complex instructions into simpler, RISC-like micro-operations internally.
- RISC-V is considered particularly approachable for emulation because of its regularity.
- Greg briefly mentioned industry interest (e.g., a new chip company) in RISC-V.
- Reminder: a project was due that night.
RISC-V Course Updates and Resources¶
- Project 3 introduces a cache memory simulator extension.
- New resources were posted on Campus Wire:
- A RISC-V cheat sheet
- The full specification document
- The cache memory guide
- The Docker image now includes GDB support.
- Suggested workflows include local development with deployment to the Beagle board as needed.
RISC-V Emulator Architecture Overview¶
- The emulator models a 64-bit RISC-V processor state:
- General-purpose registers (with x0 hard-wired to zero)
- Program counter (PC)
- Emulated memory
- Machine instructions are fetched from memory; stack space is allocated for emulated functions (the heap is ignored).
- Clarification on threading: in a multi-core system, each kernel thread has its own stack, while code and address space are shared across threads.
RISC-V Emulator Implementation in Rust¶
- The emulator is implemented in Rust to:
- Represent the machine state (registers, PC, memory)
- Read and decode real RISC-V machine instructions
- The approach is contrasted with virtual machines (e.g., Java, Python), highlighting the project’s goal of understanding real instruction formats.
- Topics covered:
- Data types for representing registers, immediates, and memory
- Use of unsafe Rust for low-level memory access where required
- Interaction with assembly code where helpful
ROP Grant Implementation in Rust¶
- Focus on immutability and clear separation of read-only vs. mutable data.
- Emulation flow for function calls:
- Initialize the state struct
- Set up function arguments
- Configure the stack to match RISC-V calling conventions
- The return address (RA) is used to track control flow and returns.
- The stack is initialized to reflect the expected memory layout for RISC-V functions.
Emulator Execution Loop¶
- The main loop runs until the PC becomes zero.
- Register x0 is enforced to remain zero after each instruction step.
- Single-instruction execution:
- Fetch instruction word from the PC
- Decode using Rust’s match on opcode (with specialization by format)
- Note: Some I-type opcodes (e.g., JALR vs. arithmetic immediate) share a format but are dispatched to different handlers.
Rust Bit Manipulation and Memory Access¶
- Helper functions:
- get_bits: masks and shifts values for field extraction.
- Unsafe Rust is used for memory access beyond normal Rust references.
- Pointer arithmetic supports both positive and negative offsets (e.g., for branches).
- Safe unaligned access patterns are discussed for load/store emulation, allowing the compiler to handle potential alignment issues.
Implementation Approach and Code Organization¶
- Recommended workflow:
- Start with simpler assembly programs from the starter repository.
- Incrementally implement instruction handlers, guided by the spec and cheat sheet.
- Memory allocation:
- Use Box to avoid large stack allocations for emulator state or memory buffers.
- Instruction dispatch:
- Pattern-match on opcode, then on funct3 and funct7 for R-type and I-type decoding.
- Arithmetic:
- Use wrapping_add (and related wrapping operations) on u64 to match RISC-V’s wraparound semantics.
R-Type and I-Type Details¶
- R-type:
- Implement arithmetic and logical operations using rs1 and rs2, with writes to rd.
- Ensure PC updates correctly after each instruction.
- I-type:
- Separate handlers for arithmetic immediates, loads, and JALR.
- JALR updates the PC using RA for returns and writes the link to rd as appropriate.
Instruction Encoding and Semantics¶
- Immediate handling:
- Correct sign extension for immediates and branch offsets.
- Proper masking for shift amounts.
- Branches:
- Construct 13-bit branch offsets and update PC accordingly.
- Use correct signed comparisons (cast between u64 and i64 as needed).
- Loads/Stores:
- Support unaligned reads/writes where necessary.
- Apply the correct width and sign-extension rules per instruction.
- Jumps/Calls:
- Implement JAL by computing offsets, writing the link register, and updating PC.
- Closing guidance:
- Review the cache guide before Tuesday.
- Prioritize finishing emulator functionality so only cache and analysis remain.