Skip to content

Commit

Permalink
Merge pull request #88 from Spooky-Firefox/sequantial_eval_order
Browse files Browse the repository at this point in the history
Sequantial componets evalultion order fix
  • Loading branch information
salon64 authored Oct 3, 2024
2 parents b888888 + 3376df3 commit ff64ab2
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 3 deletions.
63 changes: 63 additions & 0 deletions examples/pass_trough.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::path::PathBuf;
#[cfg(feature = "gui-egui")]
use syncrim::gui_egui::editor::Library;
use syncrim::{
common::{ComponentStore, Input},
components::*,
fern::fern_setup,
};
fn main() {
fern_setup();
let cs = ComponentStore {
store: vec![
Add::rc_new(
"add",
(200.0, 120.0),
Input::new("c", "out"),
Input::new("reg", "out"),
),
Constant::rc_new("c", (100.0, 100.0), 3),
Register::rc_new(
"reg",
(100.0, 140.0),
Input::new("pass", PASS_THROUGH_OUT_ID),
),
Wire::rc_new(
"w1",
vec![(110.0, 100.0), (180.0, 100.0)],
Input::new("c", "out"),
),
Wire::rc_new(
"w2",
vec![(110.0, 140.0), (180.0, 140.0)],
Input::new("reg", "out"),
),
Wire::rc_new(
"w3",
vec![(220.0, 120.0), (260.0, 120.0), (260.0, 180.0)],
Input::new("add", "out"),
),
PassThrough::rc_new("pass", (260.0, 180.0), Input::new("add", "out")),
Wire::rc_new(
"w4",
vec![(260.0, 180.0), (60.0, 180.0), (60.0, 140.0), (90.0, 140.0)],
Input::new("pass", PASS_THROUGH_OUT_ID),
),
Probe::rc_new(
"p_add",
(280.0, 120.0),
Input::new("pass", PASS_THROUGH_OUT_ID),
),
Probe::rc_new("p_reg", (130.0, 120.0), Input::new("reg", "out")),
],
};

let path = PathBuf::from("add_reg_compound.json");
cs.save_file(&path);

#[cfg(feature = "gui-egui")]
syncrim::gui_egui::gui(cs, &path, Library::default()).ok();

#[cfg(feature = "gui-vizia")]
syncrim::gui_vizia::gui(cs, &path);
}
2 changes: 2 additions & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod cross;
mod equals;
mod mem;
mod mux;
mod pass_through;
mod probe;
mod probe_assert;
mod probe_edit;
Expand All @@ -24,6 +25,7 @@ pub use cross::*;
pub use equals::*;
pub use mem::*;
pub use mux::*;
pub use pass_through::*;
pub use probe::*;
pub use probe_assert::*;
pub use probe_edit::*;
Expand Down
81 changes: 81 additions & 0 deletions src/components/pass_through.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#[cfg(feature = "gui-egui")]
use crate::common::EguiComponent;
use crate::common::{Component, Condition, Id, Input, InputPort, OutputType, Ports, Simulator};
use log::*;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::rc::Rc;

pub const PASS_THROUGH_IN_ID: &str = "pass_through_in";

pub const PASS_THROUGH_OUT_ID: &str = "pass_through_out";

#[derive(Serialize, Deserialize, Clone)]
pub struct PassThrough {
pub(crate) id: Id,
pub(crate) pos: (f32, f32),
pub(crate) input: Input,
}

#[typetag::serde]
impl Component for PassThrough {
fn to_(&self) {
trace!("pass_through");
}
#[cfg(feature = "gui-egui")]
fn dummy(&self, id: &str, pos: (f32, f32)) -> Box<Rc<dyn EguiComponent>> {
let dummy_input = Input::new("dummy", "out");
Box::new(Rc::new(PassThrough {
id: id.to_string(),
pos: (pos.0, pos.1),
input: dummy_input.clone(),
}))
}
fn get_id_ports(&self) -> (Id, Ports) {
(
self.id.clone(),
Ports::new(
// Vector of inputs
vec![&InputPort {
port_id: PASS_THROUGH_IN_ID.to_string(),
input: self.input.clone(),
}],
OutputType::Combinatorial,
vec![PASS_THROUGH_OUT_ID],
),
)
}

// propagate input value to output
fn clock(&self, simulator: &mut Simulator) -> Result<(), Condition> {
// get input value
let value = simulator.get_input_value_mut(self.id.clone(), &self.input);
// set output
simulator.set_out_value(&self.id, PASS_THROUGH_OUT_ID, value);
Ok(())
}

fn set_id_port(&mut self, target_port_id: Id, new_input: Input) {
if target_port_id == PASS_THROUGH_IN_ID {
self.input = new_input;
}
}

fn as_any(&self) -> &dyn Any {
self
}
}

impl PassThrough {
pub fn new(id: &str, pos: (f32, f32), input: Input) -> Self {
PassThrough {
id: id.to_string(),
pos,
input,
}
}

pub fn rc_new(id: &str, pos: (f32, f32), input: Input) -> Rc<Self> {
Rc::new(PassThrough::new(id, pos, input))
}
}
1 change: 1 addition & 0 deletions src/gui_egui/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod cross;
mod equal;
mod mem;
mod mux;
mod pass_through;
mod probe;
mod probe_assert;
mod probe_edit;
Expand Down
120 changes: 120 additions & 0 deletions src/gui_egui/components/pass_through.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use crate::common::{Component, EguiComponent, Ports, Simulator};
use crate::components::PassThrough;
use crate::gui_egui::editor::{EditorMode, EditorRenderReturn, GridOptions};
use crate::gui_egui::gui::EguiExtra;
use egui::{
Align2, Area, Context, InnerResponse, Order, Pos2, Rect, Response, TextWrapMode, Ui, Vec2,
};

#[typetag::serde]
impl EguiComponent for PassThrough {
/// TODO this need to be rewritten when newer helper functions becomes available
fn render(
&self,
ui: &mut Ui,
_context: &mut EguiExtra,
_simulator: Option<&mut Simulator>,
offset: Vec2,
scale: f32,
clip_rect: Rect,
_editor_mode: EditorMode,
) -> Option<Vec<Response>> {
fn component_area<R>(
id: String,
ctx: &Context,
pos: impl Into<Pos2>,
content: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
Area::new(egui::Id::from(id))
.order(Order::Middle)
.current_pos(pos)
.movable(false)
.enabled(true)
.interactable(false)
.pivot(Align2::CENTER_CENTER)
.constrain(false)
.show(ctx, content)
}

let offset: Vec2 = offset.into();

Check warning on line 39 in src/gui_egui/components/pass_through.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-pc-windows-gnu)

useless conversion to the same type: `egui::Vec2`

warning: useless conversion to the same type: `egui::Vec2` --> src/gui_egui/components/pass_through.rs:39:28 | 39 | let offset: Vec2 = offset.into(); | ^^^^^^^^^^^^^ help: consider removing `.into()`: `offset` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

Check warning on line 39 in src/gui_egui/components/pass_through.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-unknown-linux-musl)

useless conversion to the same type: `egui::Vec2`

warning: useless conversion to the same type: `egui::Vec2` --> src/gui_egui/components/pass_through.rs:39:28 | 39 | let offset: Vec2 = offset.into(); | ^^^^^^^^^^^^^ help: consider removing `.into()`: `offset` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

Check warning on line 39 in src/gui_egui/components/pass_through.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-apple-darwin)

useless conversion to the same type: `egui::Vec2`

warning: useless conversion to the same type: `egui::Vec2` --> src/gui_egui/components/pass_through.rs:39:28 | 39 | let offset: Vec2 = offset.into(); | ^^^^^^^^^^^^^ help: consider removing `.into()`: `offset` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

Check warning on line 39 in src/gui_egui/components/pass_through.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-pc-windows-gnu)

useless conversion to the same type: `egui::Vec2`

warning: useless conversion to the same type: `egui::Vec2` --> src/gui_egui/components/pass_through.rs:39:28 | 39 | let offset: Vec2 = offset.into(); | ^^^^^^^^^^^^^ help: consider removing `.into()`: `offset` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

Check warning on line 39 in src/gui_egui/components/pass_through.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-unknown-linux-musl)

useless conversion to the same type: `egui::Vec2`

warning: useless conversion to the same type: `egui::Vec2` --> src/gui_egui/components/pass_through.rs:39:28 | 39 | let offset: Vec2 = offset.into(); | ^^^^^^^^^^^^^ help: consider removing `.into()`: `offset` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

Check warning on line 39 in src/gui_egui/components/pass_through.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-apple-darwin)

useless conversion to the same type: `egui::Vec2`

warning: useless conversion to the same type: `egui::Vec2` --> src/gui_egui/components/pass_through.rs:39:28 | 39 | let offset: Vec2 = offset.into(); | ^^^^^^^^^^^^^ help: consider removing `.into()`: `offset` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

let r = component_area(
self.get_id_ports().0,
ui.ctx(),
Pos2::from(self.get_pos()) * scale + offset,
|ui| {
ui.set_clip_rect(clip_rect);

ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);

for (_text_style, font) in ui.style_mut().text_styles.iter_mut() {
font.size *= scale;
}
ui.spacing_mut().button_padding *= scale;
ui.spacing_mut().item_spacing *= scale;
ui.spacing_mut().combo_height *= scale;
ui.spacing_mut().combo_width *= scale;
ui.spacing_mut().icon_width *= scale;
ui.spacing_mut().icon_width_inner *= scale;
ui.spacing_mut().icon_spacing *= scale;
ui.spacing_mut().interact_size *= scale;

let mut group = egui::containers::Frame::group(ui.style());
group.inner_margin *= scale;
group.rounding *= scale;
// group.fill = Color32::LIGHT_RED; // Use this ween component background is implemented, probably when we implement dark mode
group
.show(ui, |ui| {
ui.label("➡️");
})
.response
},
)
.inner;
Some(vec![r])
}

fn render_editor(
&mut self,
_ui: &mut Ui,
_context: &mut EguiExtra,
_simulator: Option<&mut Simulator>,
_offset: Vec2,
_scale: f32,
_clip_rect: Rect,
_id_ports: &[(crate::common::Id, Ports)],
_grid: &GridOptions,
_editor_mode: EditorMode,
) -> EditorRenderReturn {
EditorRenderReturn {
delete: false,
resp: Some(vec![]),
}
}

fn ports_location(&self) -> Vec<(crate::common::Id, Pos2)> {
let own_pos = Vec2::new(self.pos.0, self.pos.1);
vec![
(
crate::components::REGISTER_R_IN_ID.to_string(),
Pos2::new(-10f32, 0f32) + own_pos,
),
(
crate::components::REGISTER_OUT_ID.to_string(),
Pos2::new(10f32, 0f32) + own_pos,
),
]
}

fn top_padding(&self) -> f32 {
20f32
}

fn set_pos(&mut self, pos: (f32, f32)) {
self.pos = pos;
}

fn get_pos(&self) -> (f32, f32) {
self.pos
}
}
40 changes: 37 additions & 3 deletions src/simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ pub struct IdComponent(pub HashMap<String, Box<dyn Component>>);
// The topological order does not enforce any specific order of registers
// Thus registers cannot point to other registers in a cyclic fashion
// This is (likely) not occurring in practice.
//
// A solution is to evaluate register updates separately from other components
// ... but not currently implemented ...
impl Simulator {
pub fn new(component_store: ComponentStore) -> Result<Self, &'static str> {
for component in &component_store.store {
Expand Down Expand Up @@ -138,6 +135,27 @@ impl Simulator {
ordered_components.push(c);
}
}

// check if a sequential components is linking to another sequential component and panic
// this avoids that the order of sequential components matter as they can't affect one other.
for seq_node in &ordered_components {
for seq_node_inputs in seq_node
.get_id_ports()
.1
.inputs
.iter()
.map(|port| &port.input)
{
if ordered_components
.iter()
.find(|node| node.get_id_ports().0 == seq_node_inputs.id)
.is_some()

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-pc-windows-gnu)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-pc-windows-gnu)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-unknown-linux-musl)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-unknown-linux-musl)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-apple-darwin)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-apple-darwin)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-pc-windows-gnu)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-pc-windows-gnu)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-unknown-linux-musl)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-unknown-linux-musl)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-apple-darwin)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default

Check warning on line 152 in src/simulator.rs

View workflow job for this annotation

GitHub Actions / build (x86_64-apple-darwin)

called `is_some()` after searching an `Iterator` with `find`

warning: called `is_some()` after searching an `Iterator` with `find` --> src/simulator.rs:151:22 | 151 | .find(|node| node.get_id_ports().0 == seq_node_inputs.id) | ______________________^ 152 | | .is_some() | |______________________________^ help: consider using: `any(|node| node.get_id_ports().0 == seq_node_inputs.id)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some = note: `#[warn(clippy::search_is_some)]` on by default
{
panic!("Component {} read data from {}. Sequential to sequential is not allowed, consider adding a pass trough component", seq_node.get_id_ports().0, seq_node_inputs.id)
}
}
}

//then the rest...
for node in &top {
#[allow(suspicious_double_ref_op)]
Expand Down Expand Up @@ -569,6 +587,22 @@ mod test {
assert_eq!(simulator.cycle, 1);
}

#[test]
#[should_panic(
expected = "Component r_2 read data from r_1. Sequential to sequential is not allowed, consider adding a pass trough component"
)]
fn test_sequential_to_sequential() {
let cs = ComponentStore {
store: vec![
Register::rc_new("r_1", (0.0, 0.0), Input::new("pass", PASS_THROUGH_OUT_ID)),
Register::rc_new("r_2", (70.0, 0.0), Input::new("r_1", REGISTER_OUT_ID)),
PassThrough::rc_new("pass", (35.0, 35.0), Input::new("r_2", REGISTER_OUT_ID)),
],
};

let _simulator = Simulator::new(cs).unwrap();
}

#[test]
fn test_get_input_val() {
let cs = ComponentStore {
Expand Down

0 comments on commit ff64ab2

Please sign in to comment.