From 8ac8e6548f523c5c1511a84266727ad900e9b07a Mon Sep 17 00:00:00 2001 From: Brooks McKinley <31021010+brooksmckinley@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:12:19 -0400 Subject: [PATCH 1/3] Throttle update sending when executing as fast as possible --- Cargo.lock | 13 ++++++++ Cargo.toml | 1 + src/agent.rs | 87 ++++++++++++++++++++++++++++++++-------------------- 3 files changed, 68 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 79f07abe3..b449387af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -688,6 +688,18 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1041,6 +1053,7 @@ dependencies = [ "gloo-events 0.1.2", "gloo-utils 0.1.7", "humantime", + "instant", "js-sys", "levenshtein", "log", diff --git a/Cargo.toml b/Cargo.toml index 3ffe346c3..2ef4afd42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,4 @@ cfg-if = "0.1" log = "0.4" console_log = { version = "1", features = ["color"] } humantime = "2.1.0" +instant = { version = "0.1.12" , features = ["wasm-bindgen"] } diff --git a/src/agent.rs b/src/agent.rs index 51c2bc292..fcf0a7e42 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -9,6 +9,7 @@ use crate::emulation_core::mips::datapath::MipsDatapath; use crate::emulation_core::mips::gp_registers::GpRegisterType; use futures::{FutureExt, SinkExt, StreamExt}; use gloo_console::log; +use instant::Instant; use messages::DatapathUpdate; use std::collections::HashSet; use std::time::Duration; @@ -37,6 +38,8 @@ macro_rules! send_update_mips { }; } +const UPDATE_INTERVAL: Duration = Duration::from_millis(250); + /// The main logic for the emulation core agent. All code within this function runs on a worker thread as opposed to /// the UI thread. #[reactor(EmulationCoreAgent)] @@ -77,41 +80,46 @@ pub async fn emulation_core_agent(scope: ReactorScope) state.execute_syscall_stage().await; // Part 4: Processing State/Sending Updates to UI - match state.current_datapath.as_datapath_ref() { - DatapathRef::MIPS(datapath) => { - log!(format!("Updates: {:?}", state.updates)); - // Stage always updates - send_update_mips!(state.scope, true, UpdateStage(datapath.current_stage)); + if state.should_send_datapath_update() { + match state.current_datapath.as_datapath_ref() { + DatapathRef::MIPS(datapath) => { + log!(format!("Updates: {:?}", state.updates)); + // Stage always updates + send_update_mips!(state.scope, true, UpdateStage(datapath.current_stage)); - // Send all other updates based on the state.updates variable. - send_update_mips!( - state.scope, - state.updates.changed_state, - UpdateState(datapath.state.clone()) - ); - send_update_mips!( - state.scope, - state.updates.changed_registers, - UpdateRegisters(datapath.registers) - ); - send_update_mips!( - state.scope, - state.updates.changed_coprocessor_state, - UpdateCoprocessorState(datapath.coprocessor.state.clone()) - ); - send_update_mips!( - state.scope, - state.updates.changed_coprocessor_registers, - UpdateCoprocessorRegisters(datapath.coprocessor.registers) - ); - send_update_mips!( - state.scope, - state.updates.changed_memory, - UpdateMemory(datapath.memory.clone()) - ); + // Send all other updates based on the state.updates variable. + send_update_mips!( + state.scope, + state.updates.changed_state, + UpdateState(datapath.state.clone()) + ); + send_update_mips!( + state.scope, + state.updates.changed_registers, + UpdateRegisters(datapath.registers) + ); + send_update_mips!( + state.scope, + state.updates.changed_coprocessor_state, + UpdateCoprocessorState(datapath.coprocessor.state.clone()) + ); + send_update_mips!( + state.scope, + state.updates.changed_coprocessor_registers, + UpdateCoprocessorRegisters(datapath.coprocessor.registers) + ); + send_update_mips!( + state.scope, + state.updates.changed_memory, + UpdateMemory(datapath.memory.clone()) + ); + } + DatapathRef::RISCV(_) => todo!(), } - DatapathRef::RISCV(_) => todo!(), + state.updates = Default::default(); + state.last_update = Some(Instant::now()); } + // Part 5: Sending Non-Syscall System Updates to UI send_update!( state.scope, @@ -123,7 +131,6 @@ pub async fn emulation_core_agent(scope: ReactorScope) state.initialized != is_initialiized, DatapathUpdate::System(SystemUpdate::UpdateInitialized(state.initialized)) ); - state.updates = Default::default(); } } @@ -142,6 +149,7 @@ struct EmulatorCoreAgentState { pub updates: DatapathUpdateSignal, pub scope: ReactorScope, speed: u32, + last_update: Option, executing: bool, initialized: bool, messages: Vec, @@ -157,6 +165,7 @@ impl EmulatorCoreAgentState { updates: DatapathUpdateSignal::default(), scope, speed: 0, + last_update: None, executing: false, initialized: false, messages: Vec::new(), @@ -410,6 +419,18 @@ impl EmulatorCoreAgentState { } } + /// Determines of datapath updates should be sent. Datapath updates should be sent at most once + /// per second when executing as fast as possible. If the last cycle was executed using the + /// debug buttons or we're going at at a specific speed, always send an update. + pub fn should_send_datapath_update(&self) -> bool { + if self.executing && self.speed == 0 { + self.last_update.is_none() + || Instant::now().duration_since(self.last_update.unwrap()) > UPDATE_INTERVAL + } else { + true + } + } + async fn reset_system(&mut self) { self.scanner = Scanner::new(); self.blocked_on = BlockedOn::Nothing; From 0c6e4f2942b8e7e029e8f2cb3daa6da69b4b50ba Mon Sep 17 00:00:00 2001 From: Brooks McKinley <31021010+brooksmckinley@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:31:41 -0400 Subject: [PATCH 2/3] Fix syscalls triggering multiple times when executing as fast as possible --- src/agent.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/agent.rs b/src/agent.rs index 07a22a45a..1b50b1d01 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -304,8 +304,10 @@ impl EmulatorCoreAgentState { DatapathRef::MIPS(datapath) => datapath.registers.pc, DatapathRef::RISCV(datapath) => datapath.registers.pc, }; - if self.breakpoints.contains(¤t_pc) { + if self.breakpoints.contains(¤t_pc) && self.updates.hit_breakpoint { self.executing = false; + // Unset the hit_breakpoint flag after processing + self.updates.hit_breakpoint = false; } } @@ -491,6 +493,9 @@ impl EmulatorCoreAgentState { } } } + + // Now that the syscall is processed, unset the update signal + self.updates.hit_syscall = false; } /// Determines of datapath updates should be sent. Datapath updates should be sent at most once @@ -521,6 +526,7 @@ impl EmulatorCoreAgentState { } async fn add_message(&mut self, msg: String) { + log!("Pushed message"); self.messages.push(msg); self.scope .send(DatapathUpdate::System(SystemUpdate::UpdateMessages( From 930ec9db559d46e7bc97b3b326088ca077b3faae Mon Sep 17 00:00:00 2001 From: Brooks McKinley <31021010+brooksmckinley@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:33:24 -0400 Subject: [PATCH 3/3] Fix fibonacci example to actually halt at the end --- static/assembly_examples/fibonacci.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/assembly_examples/fibonacci.asm b/static/assembly_examples/fibonacci.asm index 19e62420a..fd01dc889 100644 --- a/static/assembly_examples/fibonacci.asm +++ b/static/assembly_examples/fibonacci.asm @@ -66,8 +66,8 @@ main: # PC starts here lw $ra,36($sp) lw $fp,32($sp) addiu $sp,$sp,40 + li $a0, 0 # Load 0 into $a0 to run the exit syscall syscall # replaced jr $ra with syscall to prevent infinite execution - nop # Here's the demo code in C: #