diff --git a/examples/fulladder.rs b/examples/fulladder.rs index b89e775..c31b741 100644 --- a/examples/fulladder.rs +++ b/examples/fulladder.rs @@ -110,17 +110,27 @@ fn main() { y.replace(triple[1]); c.replace(triple[2]); - for _ in 0..3 { - circuit.tick(); + let mut cycle_count = 1; + while circuit.tick() && cycle_count < 11 { + cycle_count += 1; } + // If the number of required ticks is known, it is possible to use + // a loop with a fixed number of iterations + // ``` + // for _ in 0..3 { + // circuit.tick(); + // } + // ``` + println!( - "{} + {} + {} = {}{}", + "{} + {} + {} = {}{} (used {} cycles to reach a stable circuit)", triple[0], triple[1], triple[2], cout.value(), - s.value() + s.value(), + cycle_count ); assert_eq!(triple[3], s.value()); assert_eq!(triple[4], cout.value()); diff --git a/src/circuit.rs b/src/circuit.rs index 115c50f..d99dbc0 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -1,17 +1,53 @@ use crate::Updateable; +/// A `Circuit` is a combination of connected logic elements +/// +/// A `Circuit` holds references of [`Updateable`](Updateable) structs +/// ([`Port`](crate::port::Port), ...), which are updated on every call of +/// tick. #[derive(Default)] pub struct Circuit { updater: Vec>, } impl Circuit { - pub fn tick(&mut self) { - for u in &mut self.updater { - u.update(); - } + /// The update tick function + /// This function propagates the logic values by one Updateable element. + /// + /// Returns `true` as long as at least one element's output value has changed in the circuit. + /// + /// ``` + /// use logical::{Circuit, Signal}; + /// use logical::models::gates::OrGate; + /// + /// let mut circuit = Circuit::default(); + /// /* Configure updaters */ + /// + /// let mut clock_cycles = 0; + /// while circuit.tick() && clock_cycles < 100 { // 100 is a arbitrary value, to avoid looping if we have an oscillating circuit + /// clock_cycles += 1; + /// } + /// ``` + pub fn tick(&mut self) -> bool { + self.updater.iter_mut().fold(false, |acc, u| acc | u.update()) } + /// Add an [`Updateable`](Updateable) to the `Circuit` + /// + /// ``` + /// use logical::{Circuit, Signal, Ieee1164}; + /// use logical::models::gates::OrGate; + /// + /// let mut sig: logical::Signal = Signal::default(); + /// // Configure signal here + /// + /// let or = OrGate::default(); // Gates do not need to be mutable + /// // Connect gate here + /// + /// let mut circuit = Circuit::default(); + /// circuit.add_updater(&sig); + /// circuit.add_updater(&or); + /// ``` pub fn add_updater(&mut self, updater: &T) { self.updater.push(Box::new(updater.clone())) } diff --git a/src/lib.rs b/src/lib.rs index 22d3fe2..26bf393 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,5 +106,7 @@ pub trait Updateable { /// When this trait function is called you should perform any action necessary to update the /// struct, e.g. reading input values and updating output values. These changes should be /// instant. - fn update(&mut self); + /// + /// Returns `true` if the output value of the updatable object was changed. + fn update(&mut self) -> bool; } diff --git a/src/models/gates/mod.rs b/src/models/gates/mod.rs index 6688623..0f35c7a 100644 --- a/src/models/gates/mod.rs +++ b/src/models/gates/mod.rs @@ -26,8 +26,10 @@ macro_rules! create_simple_1i1o_gate { } impl Updateable for $name { - fn update(&mut self) { - self.z.replace($func(self.a.value())); + fn update(&mut self) -> bool { + let new_value = $func(self.a.value()); + let old_value = self.z.replace(new_value); + old_value != new_value } } @@ -58,8 +60,10 @@ macro_rules! create_simple_2i1o_gate { } impl Updateable for $name { - fn update(&mut self) { - self.z.replace($func(self.a.value(), self.b.value())); + fn update(&mut self) -> bool { + let new_value = $func(self.a.value(), self.b.value()); + let old_value = self.z.replace(new_value); + old_value != new_value } } diff --git a/src/models/gates/mux.rs b/src/models/gates/mux.rs index df35f08..ef76e0a 100644 --- a/src/models/gates/mux.rs +++ b/src/models/gates/mux.rs @@ -67,14 +67,18 @@ pub struct Mux { } impl Updateable for Mux { - fn update(&mut self) { - self.z.replace(if self.s.value().is_1H() { + fn update(&mut self) -> bool { + let new_value = if self.s.value().is_1H() { self.b.value() } else if self.s.value().is_0L() { self.a.value() } else { Ieee1164::_X - }); + }; + + let old_value = self.z.replace(new_value); + + old_value != new_value } } diff --git a/src/models/gates/tri.rs b/src/models/gates/tri.rs index b6b5dbb..34e91b5 100644 --- a/src/models/gates/tri.rs +++ b/src/models/gates/tri.rs @@ -19,14 +19,17 @@ pub struct TriBuffer { } impl Updateable for TriBuffer { - fn update(&mut self) { - self.z.replace(if self.s.value().is_1H() { + fn update(&mut self) -> bool { + let new_value = if self.s.value().is_1H() { self.a.value() } else if self.s.value().is_0L() { Ieee1164::_Z } else { Ieee1164::_X - }); + }; + let old_value = self.z.replace(new_value); + + old_value != new_value } } diff --git a/src/models/rtlib/arithmic/add.rs b/src/models/rtlib/arithmic/add.rs index 6c85a18..b2da060 100644 --- a/src/models/rtlib/arithmic/add.rs +++ b/src/models/rtlib/arithmic/add.rs @@ -1,6 +1,7 @@ use crate::direction::{Input, Output}; use crate::logicbit::mask_from_width; use crate::{Ieee1164, LogicVector, Port, Updateable}; +use crate::port::PortConnector; /// This models an actual adder that will add up both inputs. /// @@ -17,7 +18,8 @@ pub struct Add { } impl Updateable for Add { - fn update(&mut self) { + fn update(&mut self) -> bool { + let old_value = PortConnector::from(self.s.clone()).value(); let a = self.a.value(); let b = self.b.value(); self.s.with_value_mut(|v| match (a.as_u128(), b.as_u128()) { @@ -26,5 +28,7 @@ impl Updateable for Add { .unwrap(), _ => v.set_all_to(Ieee1164::_U), }); + + old_value != PortConnector::from(self.s.clone()).value() } } diff --git a/src/models/rtlib/arithmic/twoscomplement.rs b/src/models/rtlib/arithmic/twoscomplement.rs index dcded09..7ca51c4 100644 --- a/src/models/rtlib/arithmic/twoscomplement.rs +++ b/src/models/rtlib/arithmic/twoscomplement.rs @@ -1,6 +1,8 @@ use crate::direction::{Input, Output}; use crate::{LogicVector, Port, Updateable}; +use crate::port::PortConnector; + /// Computes the two's complement of the applied value. #[derive(Debug)] pub struct TwosComplement { @@ -11,8 +13,10 @@ pub struct TwosComplement { } impl Updateable for TwosComplement { - fn update(&mut self) { + fn update(&mut self) -> bool { + let old_value = PortConnector::from(self.y.clone()).value(); let a = self.a.value(); self.y.with_value_mut(|y| *y = (!a).incr()); + old_value != PortConnector::from(self.y.clone()).value() } } diff --git a/src/models/rtlib/memory/rom.rs b/src/models/rtlib/memory/rom.rs index 44cd81b..532af2e 100644 --- a/src/models/rtlib/memory/rom.rs +++ b/src/models/rtlib/memory/rom.rs @@ -3,6 +3,7 @@ use std::iter::FromIterator; use crate::direction::{Input, Output}; use crate::{Ieee1164, LogicVector, Port, Updateable}; +use crate::port::PortConnector; /// This struct represents a Read-only-memory with a size of 1kB (1024 bytes). /// @@ -83,8 +84,9 @@ impl fmt::Debug for Rom1kx8 { } impl Updateable for Rom1kx8 { - fn update(&mut self) { + fn update(&mut self) -> bool { println!("ROM Update"); + let old_value = PortConnector::from(self.data.clone()).value(); let ncs = self.n_chip_select.value(); let noe = self.n_output_enable.value(); let data = if let Some(addr) = self.addr.value().as_u128() { @@ -106,6 +108,8 @@ impl Updateable for Rom1kx8 { f.set_all_to(Ieee1164::_X); } }); + + old_value != PortConnector::from(self.data.clone()).value() } } diff --git a/src/signal.rs b/src/signal.rs index c8775f1..4683e23 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -200,9 +200,11 @@ where for<'a> &'a T: Resolve<&'a T, Output = T>, T: Clone + std::fmt::Debug, { - fn update(&mut self) { + fn update(&mut self) -> bool { self.remove_expired_portconnector(); + let old_value = self.inner.output_ports.read().unwrap().clone(); + let in_guard = self.inner.input_ports.write().unwrap(); let mut iter = in_guard.iter(); @@ -229,6 +231,8 @@ where .iter_mut() .for_each(|p| p.set_value(r.clone())); } + + old_value != *self.inner.output_ports.read().unwrap() } }