Skip to main content

Emulator

Struct Emulator 

Source
pub struct Emulator {
    pub cpu: CpuState,
    pub ram: Vec<u8>,
    pub ram_size: u32,
    pub fail_on_all_faults: bool,
    pub symbols: Option<SymbolTable>,
    pub output_buf: Vec<u8>,
    pub wasm_mode: bool,
}
Expand description

A complete RV32IMA emulator instance.

Owns the CPU state, the RAM buffer, and optional debug/WASM state. Peripheral I/O (time, keyboard, sleep) is delegated to a Platform.

Fields§

§cpu: CpuState

The CPU register file and CSR set.

§ram: Vec<u8>

Flat emulated RAM buffer. Indexed as ram[addr - RAM_BASE].

§ram_size: u32

Size of ram in bytes.

§fail_on_all_faults: bool

When true, any fault halts immediately instead of invoking the trap handler. Enabled by the -d CLI flag for debugging.

§symbols: Option<SymbolTable>

Symbol table from the loaded ELF, if available. Populated by load_elf when the image is not stripped.

§output_buf: Vec<u8>

UART output buffer. In WASM mode, bytes written to the UART go here instead of stdout. Drained by drain_output after each batch.

§wasm_mode: bool

When true, UART bytes go to output_buf instead of stdout. Set automatically by load_raw_from_bytes.

Implementations§

Source§

impl Emulator

Source

pub fn new(ram_size: u32) -> Self

Create a new emulator with ram_size bytes of RAM, all zeroed.

Source

pub fn drain_output(&mut self) -> Vec<u8>

Drain and return the accumulated UART output buffer.

After this call, output_buf is empty and ready for the next batch. Only meaningful in WASM mode; always returns an empty vec otherwise.

Source

pub fn load_raw_from_bytes( &mut self, image_data: &[u8], kernel_cmdline: Option<&str>, ) -> Result<(), i32>

Load a raw kernel image from an in-memory byte slice.

Equivalent to load_raw but without disk I/O. Used by the WASM target where the kernel image is embedded with include_bytes!.

Source§

impl Emulator

Source

pub fn load_raw( &mut self, image_file: &str, dtb_file: Option<&str>, kernel_cmdline: Option<&str>, ) -> Result<(), i32>

Load a raw Linux kernel image and a Device Tree Blob into RAM.

  • dtb_file = None — use the embedded 64 MB DTB from crate::dtb.
  • dtb_file = Some("disable") — skip DTB entirely (a1 = 0).
  • dtb_file = Some(path) — load DTB from the given file.

On success the CPU is reset with:

  • pc = RAM_BASE (kernel entry point)
  • a0 = 0 (hart ID)
  • a1 = dtb_ptr + RAM_BASE (DTB physical address)
  • privilege = M-mode
Source

pub fn load_elf(&mut self, elf_file: &str) -> Result<(), i32>

Load an RV32 bare-metal ELF or FreeRTOS image.

Only PT_LOAD segments with vaddr >= RAM_BASE are copied into RAM. Bytes between p_filesz and p_memsz are zero-filled (BSS). The symbol table is loaded automatically if the ELF is not stripped.

On success the CPU is reset with:

  • pc = elf.entry
  • sp = RAM_BASE + ram_size - 16 (top of RAM, 16-byte aligned per ABI)
  • a0 = a1 = 0 (no DTB — FreeRTOS does not use it)
  • privilege = M-mode
Source

pub fn run( &mut self, cfg: &mut RunConfig, plat: &mut dyn Platform, ) -> StepResult

Run the emulator until it stops or the instruction limit is reached.

Calls step in a loop, advancing the time base between calls. Returns the reason the loop exited as a StepResult.

Source

pub fn step( &mut self, elapsed_us: u32, count: i32, plat: &mut dyn Platform, ) -> StepResult

Execute up to count instructions, advancing the timer by elapsed_us µs.

This is the hot path of the emulator. It:

  1. Calls CpuState::tick_timer to update mtime and check MTIP.
  2. Checks for a pending timer interrupt before the instruction loop.
  3. Fetches, decodes, and executes up to count instructions.
  4. Commits any trap that fired via CpuState::commit_trap.
  5. Updates the cycle counter.

Returns StepResult::Ok in the normal case. Returns StepResult::Wfi if the CPU is sleeping. Returns StepResult::Restart or StepResult::Poweroff if the kernel wrote to SYSCON.

Source

pub fn write_trace_line( &self, writer: &mut dyn Write, pc: u32, prev_regs: &[u32; 32], )

Write one line of compact execution trace to writer.

Format: <pc> <ir> <mnem padded to 36 chars> [reg=val ...]

Only registers that changed relative to prev_regs are listed. Instructions with no register writes (branches, stores) produce a line that ends after the mnemonic.

§Example output
80000014  510010ef  jal     ra, <main>                    ra=80000018
80001524  ff010113  addi    sp, sp, -16                   sp=81fffff0
80001528  00112623  sw      ra, 12(sp)
Source

pub fn dump_state(&self, prev_regs: Option<&[u32; 32]>)

Print the PC, the disassembled instruction, and the full register grid.

prev_regs — register state before the last step. Registers that changed are prefixed with *.

Format:

80001234  510010ef  jal     ra, <main>
  zero=00000000 *ra=80000018   sp=83fffff0  gp=00000000
  ...

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.