diff --git a/Cargo.toml b/Cargo.toml index 14df370..74ff9e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,13 @@ r5ksc = [] # CONFIG.SE (bit 12 enable/disable), and sized via CONFIG.SS (bits 23:22). # Implies r5ksc (Triton always has secondary cache present). r5ksc_triton = ["r5ksc"] +# Idle detection + in-place CPU park: when the guest sits in its interrupt-enabled +# idle loop, sleep the CPU thread until the next timer tick / device interrupt +# rather than spinning the host core (see docs/idle-pause-work.md). Wallclock- +# anchored timing; the IRIS_NO_IDLE env var disables it at runtime when compiled +# in. OFF by default (the CPU thread spins) โ€” opt in with --features idle-pause; +# off keeps deterministic-clock and raw-benchmark builds simple. +idle-pause = [] default = ["tlbvmap"] # Global Decode Cache: slab-backed hash table memoising decode_into() by raw instruction word. # Both R4K (l2.instrs) and R5K (ic_instrs) store *const DecodedInstr pointers into the slab; diff --git a/src/mips_exec.rs b/src/mips_exec.rs index 7f5bb28..6de4a8a 100644 --- a/src/mips_exec.rs +++ b/src/mips_exec.rs @@ -5210,8 +5210,10 @@ impl Device for MipsCpu< //let mut last_time = std::time::Instant::now(); // --- end perf sampling --- - // Idle detection + park state (see docs/idle-pause-work.md). Set - // IRIS_NO_IDLE to keep spinning the host CPU (for benchmarking/debug). + // Idle detection + park state (see docs/idle-pause-work.md). Compiled + // in only with the `idle-pause` feature (off by default; opt in with + // --features idle-pause). When compiled in, set IRIS_NO_IDLE to keep + // spinning the host CPU at runtime (for benchmarking/debug). // // We only park when the architectural state (PC + all GPRs) REPEATS // across batches. A polling/idle loop (e.g. the kernel idle loop @@ -5220,14 +5222,19 @@ impl Device for MipsCpu< // DELAY(): `bgezl v1,-1; subu v1,v1,v0`) changes a counter every // iteration, so its state never repeats โ€” we must NOT park it or // boot stalls. The state-repeat test distinguishes the two. + #[cfg(feature = "idle-pause")] let idle_enabled = std::env::var_os("IRIS_NO_IDLE").is_none(); // Ring of recent architectural-state hashes (PC folded with all GPRs), // one per idle-suspected batch. A polling/idle loop cycles through a // small set of states, so a hash repeats within ~the loop period; a // delay loop's counter makes every state unique, so it never repeats. + #[cfg(feature = "idle-pause")] const IDLE_RING: usize = 32; + #[cfg(feature = "idle-pause")] let mut idle_ring = [0u64; IDLE_RING]; + #[cfg(feature = "idle-pause")] let mut idle_ring_len = 0usize; + #[cfg(feature = "idle-pause")] let mut idle_ring_pos = 0usize; #[allow(unreachable_code)] @@ -5267,6 +5274,7 @@ impl Device for MipsCpu< // consistent (advancing only count would spike count_step โ€” see // docs/idle-pause-work.md ยง4). We never stop/restart the thread or // peripherals; we just sleep in place, so the kernel is undisturbed. + #[cfg(feature = "idle-pause")] if idle_enabled { let ie = guard.core.interrupts_enabled(); let pending = guard.core.interrupts.load(Ordering::Relaxed) as u32;