diff --git a/CHANGELOG.md b/CHANGELOG.md index b5812c26..35770495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Tracking changes per date: +## 230801 + +- Simulator run/halt is implemented in `vizia` using a simple eventing mechanism. Later we might want to spawn a simulation thread for faster execution (right now its tied to frame rate). + ## 230731 - Return type from `clock` (`fn clock(&self, _simulator: &mut Simulator) -> Result<(), Condition`). diff --git a/examples/probe_stim_assert.rs b/examples/probe_stim_assert.rs index 85c5e1df..194bc8c7 100644 --- a/examples/probe_stim_assert.rs +++ b/examples/probe_stim_assert.rs @@ -9,12 +9,12 @@ fn main() { fern_setup(); let cs = ComponentStore { store: vec![ - ProbeStim::rc_new("stim", (100.0, 100.0), vec![42, 1, 2]), + ProbeStim::rc_new("stim", (100.0, 100.0), vec![0, 1, 2, 3, 1337]), ProbeAssert::rc_new( "assert", (200.0, 100.0), Input::new("stim", "out"), - vec![0, 1, 2], + vec![0, 1, 2, 3, 42], ), ], }; diff --git a/src/common.rs b/src/common.rs index 2f74845f..ae3776c6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -33,6 +33,8 @@ pub struct Simulator { pub history: Vec>, pub component_ids: Vec, pub graph: Graph, + // Running state, (do we need it accessible from other crates?) + pub(crate) running: bool, } #[derive(Serialize, Deserialize)] diff --git a/src/components/probe_assert.rs b/src/components/probe_assert.rs index 404a3f64..925ebfd4 100644 --- a/src/components/probe_assert.rs +++ b/src/components/probe_assert.rs @@ -1,4 +1,7 @@ -use crate::common::{Component, Condition, Id, Input, OutputType, Ports, Signal, Simulator}; +use crate::{ + common::{Component, Condition, Id, Input, OutputType, Ports, Signal, Simulator}, + signal::SignalValue, +}; use log::*; use serde::{Deserialize, Serialize}; use std::rc::Rc; @@ -27,7 +30,10 @@ impl Component for ProbeAssert { fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> { trace!("-- cycle {} --", simulator.cycle); let lhs = simulator.get_input_value(&self.input); - let rhs = self.values[simulator.cycle].get_value(); + let rhs = match self.values.get(simulator.cycle) { + Some(rhs) => rhs.get_value(), + _ => SignalValue::Unknown, + }; // the assertion is checked only in test mode #[cfg(test)] diff --git a/src/gui_vizia/gui.rs b/src/gui_vizia/gui.rs index f8e6cda5..e8e9b245 100644 --- a/src/gui_vizia/gui.rs +++ b/src/gui_vizia/gui.rs @@ -16,7 +16,6 @@ use log::*; pub struct GuiData { pub path: PathBuf, pub simulator: Simulator, - pub pause: bool, pub is_saved: bool, pub show_about: bool, pub selected_id: usize, @@ -72,11 +71,20 @@ impl Model for GuiData { GuiEvent::UnClock => self.simulator.un_clock(), GuiEvent::Reset => { self.simulator.reset(); - self.pause = true; } - GuiEvent::Play => self.pause = false, - GuiEvent::Pause => self.pause = true, - GuiEvent::PlayToggle => self.pause = !self.pause, + GuiEvent::Play => { + self.simulator.running = true; + self.simulator.clock(); + } + GuiEvent::Pause => { + self.simulator.running = false; + } + GuiEvent::PlayToggle => { + self.simulator.running = !self.simulator.running; + if self.simulator.running { + self.simulator.clock(); + } + } GuiEvent::Preferences => trace!("Preferences"), GuiEvent::ShowAbout => self.show_about = true, GuiEvent::HideAbout => self.show_about = false, @@ -130,7 +138,6 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { GuiData { path, simulator, - pause: true, is_saved: false, show_about: false, selected_id: 0, @@ -139,6 +146,19 @@ pub fn gui(cs: &ComponentStore, path: &PathBuf) { } .build(cx); + // bind a clock change to allow free-running mode + Binding::new(cx, GuiData::simulator.then(Simulator::cycle), |cx, lens| { + // Retrieve the data from context + let cycle = lens.get(cx); + trace!("cycle changed {}", cycle); + + let simulator = GuiData::simulator.get(cx); + if simulator.running { + trace!("send clock event"); + cx.emit(GuiEvent::Clock); + }; + }); + VStack::new(cx, |cx| { // Menu Menu::new(cx, |cx| { diff --git a/src/gui_vizia/transport.rs b/src/gui_vizia/transport.rs index f3b4f2fc..f9047bc3 100644 --- a/src/gui_vizia/transport.rs +++ b/src/gui_vizia/transport.rs @@ -1,6 +1,6 @@ +use crate::common::Simulator; use crate::gui_vizia::{GuiData, GuiEvent}; use vizia::{icons, prelude::*}; - pub(crate) struct Transport {} impl View for Transport {} @@ -58,11 +58,11 @@ impl Transport { |cx| { Label::new( cx, - GuiData::pause.map(|pause| { - if *pause { - icons::ICON_PLAYER_PLAY - } else { + GuiData::simulator.then(Simulator::running).map(|running| { + if *running { icons::ICON_PLAYER_PLAY_FILLED + } else { + icons::ICON_PLAYER_PLAY } }), ) @@ -84,11 +84,11 @@ impl Transport { |cx| { Label::new( cx, - GuiData::pause.map(|pause| { - if *pause { - icons::ICON_PLAYER_PAUSE_FILLED - } else { + GuiData::simulator.then(Simulator::running).map(|running| { + if *running { icons::ICON_PLAYER_PAUSE + } else { + icons::ICON_PLAYER_PAUSE_FILLED } }), ) diff --git a/src/simulator.rs b/src/simulator.rs index a9abb3b2..b33d7b76 100644 --- a/src/simulator.rs +++ b/src/simulator.rs @@ -136,6 +136,7 @@ impl Simulator { history: vec![], component_ids, graph, + running: false, }; trace!("sim_state {:?}", simulator.sim_state); @@ -226,16 +227,35 @@ impl Simulator { match component.clock(self) { Ok(_) => {} Err(cond) => match cond { - Condition::Warning(_) => todo!(), + Condition::Warning(warn) => trace!("warning {}", warn), Condition::Error(err) => panic!("err {}", err), - Condition::Assert(assert) => panic!("assert {}", assert), - Condition::Halt(halt) => info!("halt {}", halt), + Condition::Assert(assert) => { + error!("assertion failed {}", assert); + self.running = false; + } + Condition::Halt(halt) => { + self.running = false; + info!("halt {}", halt) + } }, } } self.cycle = self.history.len(); } + /// free running mode until Halt condition + pub fn run(&mut self) { + self.running = true; + while self.running { + self.clock() + } + } + + /// stop the simulator from gui or other external reason + pub fn stop(&mut self) { + self.running = false; + } + /// reverse simulation using history if clock > 1 pub fn un_clock(&mut self) { if self.cycle > 1 { @@ -256,9 +276,14 @@ impl Simulator { self.history = vec![]; self.cycle = 0; self.sim_state.iter_mut().for_each(|val| *val = 0.into()); + self.stop(); self.clock(); } + pub fn get_state(&self) -> bool { + self.running + } + /// save as `dot` file with `.gv` extension pub fn save_dot(&self, path: &PathBuf) { let mut path = path.to_owned();