From bd35351087d406bfd9581c08575706ad98b24100 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Tue, 25 Jul 2023 18:49:28 +0200 Subject: [PATCH] ProbeAssert --- .vscode/settings.json | 1 + examples/probe_stim_assert.rs | 30 ++++ src/components/mod.rs | 2 + src/components/probe_assert.rs | 214 +++++++++++++++++++++++ src/components/probe_edit.rs | 29 +-- src/components/probe_stim.rs | 7 +- src/gui_egui/components/mod.rs | 1 + src/gui_egui/components/probe_assert.rs | 5 + src/gui_vizia/components/mod.rs | 3 +- src/gui_vizia/components/probe_assert.rs | 57 ++++++ src/gui_vizia/components/probe_edit.rs | 9 +- src/gui_vizia/components/probe_stim.rs | 43 ++--- 12 files changed, 359 insertions(+), 42 deletions(-) create mode 100644 examples/probe_stim_assert.rs create mode 100644 src/components/probe_assert.rs create mode 100644 src/gui_egui/components/probe_assert.rs create mode 100644 src/gui_vizia/components/probe_assert.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index ebbdb44c..2a1cb9c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,7 @@ "epaint", "graphviz", "hoverable", + "lightcoral", "lightgray", "menubutton", "petgraph", diff --git a/examples/probe_stim_assert.rs b/examples/probe_stim_assert.rs new file mode 100644 index 00000000..52c41596 --- /dev/null +++ b/examples/probe_stim_assert.rs @@ -0,0 +1,30 @@ +use std::path::PathBuf; +use syncrim::{ + common::{ComponentStore, Input}, + components::*, + fern::fern_setup, +}; + +fn main() { + fern_setup(); + let cs = ComponentStore { + store: vec![ + ProbeStim::rc_new("stim", (100.0, 100.0), vec![0, 1, 2]), + ProbeAssert::rc_new( + "assert", + (200.0, 100.0), + Input::new("stim", "out"), + vec![0, 1, 2], + ), + ], + }; + + let path = PathBuf::from("probe_stim_assert.json"); + cs.save_file(&path); + + #[cfg(feature = "gui-egui")] + syncrim::gui_egui::gui(&cs, &path).ok(); + + #[cfg(feature = "gui-vizia")] + syncrim::gui_vizia::gui(&cs, &path); +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 951775fd..6ea2061e 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -3,6 +3,7 @@ mod constant; mod mem; mod mux; mod probe; +mod probe_assert; mod probe_edit; mod probe_out; mod probe_stim; @@ -15,6 +16,7 @@ pub use constant::*; pub use mem::*; pub use mux::*; pub use probe::*; +pub use probe_assert::*; pub use probe_edit::*; pub use probe_out::*; pub use probe_stim::*; diff --git a/src/components/probe_assert.rs b/src/components/probe_assert.rs new file mode 100644 index 00000000..4ffe123a --- /dev/null +++ b/src/components/probe_assert.rs @@ -0,0 +1,214 @@ +use crate::common::{Component, Id, Input, OutputType, Ports, Signal, Simulator}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::rc::Rc; + +#[derive(Serialize, Deserialize)] +pub struct ProbeAssert { + pub id: Id, + pub pos: (f32, f32), + pub input: Input, + pub values: Vec, +} + +#[typetag::serde] +impl Component for ProbeAssert { + fn to_(&self) { + trace!("ProbeAssert {:?}", self.values); + } + + fn get_id_ports(&self) -> (Id, Ports) { + ( + self.id.clone(), + Ports::new(vec![&self.input], OutputType::Combinatorial, vec![]), + ) + } + + fn clock(&self, simulator: &mut Simulator) { + trace!("-- cycle {} --", simulator.cycle); + // the assertion is checked only in test mode + #[cfg(test)] + assert_eq!( + simulator.get_input_val(&self.input), + self.values[simulator.cycle] + ); + } + + // notice we don't implement `un_clock` since the state is already kept in history +} + +impl ProbeAssert { + pub fn new(id: &str, pos: (f32, f32), input: Input, values: Vec>) -> Self { + ProbeAssert { + id: id.to_string(), + pos, + input, + values: values.into_iter().map(|v| v.into()).collect(), + } + } + + pub fn rc_new( + id: &str, + pos: (f32, f32), + input: Input, + values: Vec>, + ) -> Rc { + Rc::new(ProbeAssert::new(id, pos, input, values)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + common::{ComponentStore, Input}, + components::ProbeStim, + }; + + #[test] + fn test_probe_stim() { + let cs = ComponentStore { + store: vec![ + ProbeStim::rc_new("stim", (0.0, 0.0), vec![0, 1, 2]), + ProbeAssert::rc_new( + "assert", + (0.0, 0.0), + Input::new("stim", "out"), + vec![0, 1, 2], + ), + ], + }; + + let mut simulator = Simulator::new(&cs); + // output + let out = &Input::new("stim", "out"); + + // reset + println!(""); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!(simulator.get_input_val(out), 1.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 3); + assert_eq!(simulator.get_input_val(out), 2.into()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!(simulator.get_input_val(out), 1.into()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!(simulator.get_input_val(out), 1.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 3); + assert_eq!(simulator.get_input_val(out), 2.into()); + + println!(""); + simulator.reset(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + } + + #[test] + #[should_panic] + fn test_probe_stim_fail() { + let cs = ComponentStore { + store: vec![ + ProbeStim::rc_new("stim", (0.0, 0.0), vec![0, 1, 2]), + ProbeAssert::rc_new( + "assert", + (0.0, 0.0), + Input::new("stim", "out"), + vec![0, 0, 2], + ), + ], + }; + + let mut simulator = Simulator::new(&cs); + // output + let out = &Input::new("stim", "out"); + + // reset + println!(""); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!(simulator.get_input_val(out), 1.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 3); + assert_eq!(simulator.get_input_val(out), 2.into()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!(simulator.get_input_val(out), 1.into()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + + println!(""); + simulator.un_clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 2); + assert_eq!(simulator.get_input_val(out), 1.into()); + + println!(""); + simulator.clock(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 3); + assert_eq!(simulator.get_input_val(out), 2.into()); + + println!(""); + simulator.reset(); + println!("sim_state {:?}", simulator.sim_state); + assert_eq!(simulator.cycle, 1); + assert_eq!(simulator.get_input_val(out), 0.into()); + } +} diff --git a/src/components/probe_edit.rs b/src/components/probe_edit.rs index b71705e2..5433b172 100644 --- a/src/components/probe_edit.rs +++ b/src/components/probe_edit.rs @@ -1,13 +1,16 @@ use crate::common::{Component, Id, OutputType, Ports, Signal, Simulator}; use log::*; use serde::{Deserialize, Serialize}; -use std::sync::{Arc, RwLock}; +use std::{ + rc::Rc, + sync::{Arc, RwLock}, +}; #[derive(Serialize, Deserialize, Clone)] pub struct ProbeEdit { pub id: Id, pub pos: (f32, f32), - pub history: Arc>>, // will contain the next editable value + pub edit_history: Arc>>, // will contain the next editable value } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -37,7 +40,7 @@ impl Component for ProbeEdit { // propagate editable value fn clock(&self, simulator: &mut Simulator) { - let mut history = self.history.write().unwrap(); + let mut history = self.edit_history.write().unwrap(); trace!("{} history {:?}", self.id, history); let current = history.last().unwrap().clone(); // set output to current value @@ -48,16 +51,16 @@ impl Component for ProbeEdit { // reverse simulation, notice does not touch simulator state, its just internal fn un_clock(&self) { - let mut history = self.history.write().unwrap(); - trace!("{} history {:?}", self.id, history); - let _next = history.pop().unwrap(); // pop the next editable value - let _current = history.pop().unwrap(); // pop current editable value - let prev = history.pop().unwrap(); // pop the prev editable value + let mut edit_history = self.edit_history.write().unwrap(); + trace!("{} history {:?}", self.id, edit_history); + let _next = edit_history.pop().unwrap(); // pop the next editable value + let _current = edit_history.pop().unwrap(); // pop current editable value + let prev = edit_history.pop().unwrap(); // pop the prev editable value trace!("next {:?}", _next); trace!("current {:?}", _current); trace!("prev {:?}", prev); - history.push(prev.clone()); // push as current - history.push(prev); // push as next (to be edited) + edit_history.push(prev.clone()); // push as current + edit_history.push(prev); // push as next (to be edited) } } @@ -67,10 +70,14 @@ impl ProbeEdit { id: id.into(), pos, // initiate internal history - history: Arc::new(RwLock::new(vec![TextSignal { + edit_history: Arc::new(RwLock::new(vec![TextSignal { text: "0".to_string(), signal: Signal::Data(0), }])), } } + + pub fn rc_new(id: &str, pos: (f32, f32)) -> Rc { + Rc::new(ProbeEdit::new(id, pos)) + } } diff --git a/src/components/probe_stim.rs b/src/components/probe_stim.rs index a49d0ee6..26b1e9f2 100644 --- a/src/components/probe_stim.rs +++ b/src/components/probe_stim.rs @@ -30,7 +30,12 @@ impl Component for ProbeStim { fn clock(&self, simulator: &mut Simulator) { trace!("-- cycle {} --", simulator.cycle); - simulator.set_out_val(&self.id, "out", self.values[simulator.cycle]); + let out = if let Some(value) = self.values.get(simulator.cycle) { + *value + } else { + Signal::Unknown + }; + simulator.set_out_val(&self.id, "out", out); } // notice we don't implement `un_clock` since the state is already kept in history diff --git a/src/gui_egui/components/mod.rs b/src/gui_egui/components/mod.rs index f709494b..849105bb 100644 --- a/src/gui_egui/components/mod.rs +++ b/src/gui_egui/components/mod.rs @@ -3,6 +3,7 @@ mod constant; mod mem; mod mux; mod probe; +mod probe_assert; mod probe_out; mod register; mod sext; diff --git a/src/gui_egui/components/probe_assert.rs b/src/gui_egui/components/probe_assert.rs new file mode 100644 index 00000000..c776bf91 --- /dev/null +++ b/src/gui_egui/components/probe_assert.rs @@ -0,0 +1,5 @@ +use crate::common::EguiComponent; +use crate::components::ProbeAssert; + +#[typetag::serde] +impl EguiComponent for ProbeAssert {} diff --git a/src/gui_vizia/components/mod.rs b/src/gui_vizia/components/mod.rs index 46760aa5..11a74478 100644 --- a/src/gui_vizia/components/mod.rs +++ b/src/gui_vizia/components/mod.rs @@ -3,9 +3,10 @@ mod constant; mod mem; mod mux; mod probe; +mod probe_assert; mod probe_edit; mod probe_out; +mod probe_stim; mod register; mod sext; mod wire; -mod probe_stim; diff --git a/src/gui_vizia/components/probe_assert.rs b/src/gui_vizia/components/probe_assert.rs new file mode 100644 index 00000000..5f59bd33 --- /dev/null +++ b/src/gui_vizia/components/probe_assert.rs @@ -0,0 +1,57 @@ +use crate::{ + common::{Component, Signal, Simulator, ViziaComponent}, + components::ProbeAssert, + gui_vizia::{popup::NewPopup, tooltip::new_component_tooltip, GuiData}, +}; +use vizia::prelude::*; + +use log::*; + +#[typetag::serde] +impl ViziaComponent for ProbeAssert { + // create view + fn view(&self, cx: &mut Context) { + trace!("---- Create ProbeAssert View"); + + let values = self.values.clone(); + + View::build(ProbeAssertView {}, cx, |cx| { + let input = self.input.clone(); + Binding::new( + cx, + crate::gui_vizia::GuiData::simulator.then(Simulator::cycle), + move |cx, cycle| { + let cycle = cycle.get(cx); + let rhs = if let Some(value) = values.get(cycle - 1) { + *value + } else { + Signal::Unknown + }; + Label::new(cx, { + let simulator = GuiData::simulator.get(cx); + &format!("{:?} == {:?}", simulator.get_input_val(&input), rhs) + }) + .hoverable(false); + }, + ); + NewPopup::new(cx, self.get_id_ports()).position_type(PositionType::SelfDirected); + }) + .position_type(PositionType::SelfDirected) + .background_color(Color::lightcoral()) + .left(Pixels(self.pos.0 - 10.0)) + .top(Pixels(self.pos.1 - 10.0)) + // .width(Pixels(20.0)) // TODO, maybe some max width + .width(Auto) + .height(Pixels(20.0)) + // TODO: do we want/need tooltip/popup for constants + .on_press(|ex| ex.emit(PopupEvent::Switch)) + .tooltip(|cx| new_component_tooltip(cx, self)); + } +} +pub struct ProbeAssertView {} + +impl View for ProbeAssertView { + fn element(&self) -> Option<&'static str> { + Some("ProbeAssert") + } +} diff --git a/src/gui_vizia/components/probe_edit.rs b/src/gui_vizia/components/probe_edit.rs index 99e0dd10..82d4d961 100644 --- a/src/gui_vizia/components/probe_edit.rs +++ b/src/gui_vizia/components/probe_edit.rs @@ -3,7 +3,6 @@ use crate::{ common::{Signal, SignalSigned, SignalUnsigned, Simulator, ViziaComponent}, components::{ProbeEdit, TextSignal}, }; -// use anyhow::{anyhow, Result}; use vizia::prelude::*; use log::*; @@ -17,15 +16,15 @@ impl ViziaComponent for ProbeEdit { } .build(cx); - let history_bind = self.history.clone(); - let history_submit = self.history.clone(); + let history_bind = self.edit_history.clone(); + let history_submit = self.edit_history.clone(); Textbox::new(cx, ProbeEditView::editable_text) .bind( GuiData::simulator.then(Simulator::cycle), - move |mut handle, clock| { + move |mut handle, cycle| { let cx = handle.context(); - trace!("bind: clock --- {}", clock.get(cx)); + trace!("bind: clock --- {}", cycle.get(cx)); let text = history_bind.read().unwrap().last().unwrap().text.clone(); trace!("last text: {:?}", text); cx.emit(ProbeEditViewSetter::EditableText(text)); diff --git a/src/gui_vizia/components/probe_stim.rs b/src/gui_vizia/components/probe_stim.rs index f702d66c..2cfa7fa4 100644 --- a/src/gui_vizia/components/probe_stim.rs +++ b/src/gui_vizia/components/probe_stim.rs @@ -1,13 +1,10 @@ use crate::{ - common::{Component, ViziaComponent}, + common::{Component, Signal, Simulator, ViziaComponent}, components::ProbeStim, gui_vizia::{popup::NewPopup, tooltip::new_component_tooltip}, }; -use vizia::{ - prelude::*, - vg::{Paint, Path}, -}; +use vizia::prelude::*; use log::*; @@ -16,14 +13,29 @@ impl ViziaComponent for ProbeStim { // create view fn view(&self, cx: &mut Context) { trace!("---- Create ProbeStim View"); + let values = self.values.clone(); View::build(ProbeStimView {}, cx, |cx| { - // Label::new(cx, &format!("{:?}", self.value)).hoverable(false); + Binding::new( + cx, + crate::gui_vizia::GuiData::simulator.then(Simulator::cycle), + move |cx, cycle| { + let cycle = cycle.get(cx); + let rhs = if let Some(value) = values.get(cycle - 1) { + *value + } else { + Signal::Unknown + }; + Label::new(cx, &format!("{:?}", rhs)).hoverable(false); + }, + ); NewPopup::new(cx, self.get_id_ports()).position_type(PositionType::SelfDirected); }) .position_type(PositionType::SelfDirected) + .background_color(Color::lightblue()) .left(Pixels(self.pos.0 - 10.0)) .top(Pixels(self.pos.1 - 10.0)) - .width(Pixels(20.0)) + .width(Auto) + // .width() // TODO, maybe some max width .height(Pixels(20.0)) // TODO: do we want/need tooltip/popup for constants .on_press(|ex| ex.emit(PopupEvent::Switch)) @@ -36,21 +48,4 @@ impl View for ProbeStimView { fn element(&self) -> Option<&'static str> { Some("ProbeStim") } - - fn draw(&self, cx: &mut DrawContext<'_>, canvas: &mut Canvas) { - let bounds = cx.bounds(); - // trace!("Constant draw {:?}", bounds); - - let mut path = Path::new(); - let mut paint = Paint::color(vizia::vg::Color::rgbf(0.0, 1.0, 0.0)); - paint.set_line_width(cx.logical_to_physical(1.0)); - - path.move_to(bounds.left() + 0.5, bounds.top() + 0.5); - path.line_to(bounds.right() + 0.5, bounds.top() + 0.5); - path.line_to(bounds.right() + 0.5, bounds.bottom() + 0.5); - path.line_to(bounds.left() + 0.5, bounds.bottom() + 0.5); - path.line_to(bounds.left() + 0.5, bounds.top() + 0.5); - - canvas.fill_path(&path, &paint); - } }