Skip to content

Commit

Permalink
many things
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronKutch committed Dec 4, 2023
1 parent 28ada02 commit d0df853
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 19 deletions.
2 changes: 1 addition & 1 deletion starlight/src/awi_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ mod temporal;

pub use epoch::{Assertions, Epoch};
pub use eval_awi::EvalAwi;
pub use lazy_awi::LazyAwi;
pub use lazy_awi::{LazyAwi, LazyInlAwi};
pub use temporal::{Loop, LoopHandle, Net};
60 changes: 49 additions & 11 deletions starlight/src/awi_structs/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ pub fn _callback() -> EpochCallback {
}
fn register_assertion_bit(bit: dag::bool, location: Location) {
// need a new bit to attach new location data to
let new_bit = new_pstate(bw(1), Op::Copy([bit.state()]), Some(location));
let new_bit = new_pstate(bw(1), Op::Assert([bit.state()]), Some(location));
no_recursive_current_epoch_mut(|current| {
let mut epoch_data = current.epoch_data.borrow_mut();
// need to manually construct to get around closure issues
Expand Down Expand Up @@ -326,8 +326,8 @@ pub fn _callback() -> EpochCallback {
/// created will be kept until the struct is dropped, in which case the capacity
/// for those states are reclaimed and their `PState`s are invalidated.
///
/// Additionally, assertion bits from [crate::mimick::assert],
/// [crate::mimick::assert_eq], [crate::mimick::Option::unwrap], etc are
/// Additionally, assertion bits from [crate::dag::assert],
/// [crate::dag::assert_eq], [crate::dag::Option::unwrap], etc are
/// associated with the top level `Epoch` alive at the time they are
/// created. Use [Epoch::assertions] to acquire these.
///
Expand Down Expand Up @@ -386,28 +386,66 @@ impl Epoch {
self.shared.assertions()
}

/// If any assertion bit evaluates to false, this returns an error.
// TODO fix the enum situation
pub fn assert_assertions(&self) -> Result<(), EvalError> {
let bits = self.shared.assertions().bits;
for eval_awi in bits {
let val = eval_awi.eval_bit()?;
if let Some(val) = val.known_value() {
if !val {
return Err(EvalError::OtherString(format!(
"an assertion bit evaluated to false, failed on {}",
self.shared
.epoch_data
.borrow()
.ensemble
.get_state_debug(eval_awi.state())
.unwrap()
)))
}
}
}
Ok(())
}

/// If any assertion bit evaluates to false, this returns an error. If there
/// were no known false assertions but some are `Value::Unknown`, this
/// returns a specific error for it.
// TODO fix the enum situation
pub fn assert_assertions(&self) -> Result<(), EvalError> {
pub fn assert_assertions_strict(&self) -> Result<(), EvalError> {
let bits = self.shared.assertions().bits;
let mut unknown = false;
let mut unknown = None;
for eval_awi in bits {
let val = eval_awi.eval_bit()?;
if let Some(val) = val.known_value() {
if !val {
return Err(EvalError::OtherString(format!(
"assertion bits are not all true, failed on bit {eval_awi}"
"assertion bits are not all true, failed on {}",
self.shared
.epoch_data
.borrow()
.ensemble
.get_state_debug(eval_awi.state())
.unwrap()
)))
// TODO also return location
}
} else {
unknown = true;
} else if unknown.is_none() {
// get the earliest failure to evaluate wait for all bits to be checked for
// falsity
unknown = Some(eval_awi.p_state);
}
}
if unknown {
Err(EvalError::OtherStr("could not eval bit to known value"))
if let Some(p_state) = unknown {
Err(EvalError::OtherString(format!(
"an assertion bit could not be evaluated to a known value, failed on {}",
self.shared
.epoch_data
.borrow()
.ensemble
.get_state_debug(p_state)
.unwrap()
)))
} else {
Ok(())
}
Expand Down
11 changes: 10 additions & 1 deletion starlight/src/awi_structs/eval_awi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,16 @@ impl EvalAwi {
if let Some(val) = val.known_value() {
res.set(bit_i, val).unwrap();
} else {
return Err(EvalError::OtherStr("could not eval bit to known value"))
return Err(EvalError::OtherString(format!(
"could not eval bit {bit_i} to known value, the state is {}",
get_current_epoch()
.unwrap()
.epoch_data
.borrow()
.ensemble
.get_state_debug(p_self)
.unwrap()
)))
}
}
Ok(res)
Expand Down
103 changes: 103 additions & 0 deletions starlight/src/awi_structs/lazy_awi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,106 @@ impl fmt::Debug for LazyAwi {
}

forward_debug_fmt!(LazyAwi);

/// The same as [LazyAwi](crate::LazyAwi), except that it allows for checking
/// bitwidths at compile time.
#[derive(Clone, Copy)]
pub struct LazyInlAwi<const BW: usize, const LEN: usize> {
opaque: dag::InlAwi<BW, LEN>,
}

#[macro_export]
macro_rules! lazy_inlawi_ty {
($w:expr) => {
LazyInlAwi::<
{ $w },
{
{
Bits::unstable_raw_digits({ $w })
}
},
>
};
}

impl<const BW: usize, const LEN: usize> Lineage for LazyInlAwi<BW, LEN> {
fn state(&self) -> PState {
self.opaque.state()
}
}

impl<const BW: usize, const LEN: usize> LazyInlAwi<BW, LEN> {
fn internal_as_ref(&self) -> &dag::InlAwi<BW, LEN> {
&self.opaque
}

pub fn nzbw(&self) -> NonZeroUsize {
self.opaque.nzbw()
}

pub fn bw(&self) -> usize {
self.nzbw().get()
}

pub fn opaque() -> Self {
Self {
opaque: dag::InlAwi::opaque(),
}
}

/// Retroactively-assigns by `rhs`. Returns `None` if bitwidths mismatch or
/// if this is being called after the corresponding Epoch is dropped and
/// states have been pruned.
pub fn retro_(&self, rhs: &awi::Bits) -> Result<(), EvalError> {
let p_lhs = self.state();
Evaluator::change_thread_local_state_value(p_lhs, rhs)
}
}

impl<const BW: usize, const LEN: usize> Deref for LazyInlAwi<BW, LEN> {
type Target = dag::InlAwi<BW, LEN>;

fn deref(&self) -> &Self::Target {
self.internal_as_ref()
}
}

impl<const BW: usize, const LEN: usize> Index<RangeFull> for LazyInlAwi<BW, LEN> {
type Output = dag::InlAwi<BW, LEN>;

fn index(&self, _i: RangeFull) -> &dag::InlAwi<BW, LEN> {
self
}
}

impl<const BW: usize, const LEN: usize> Borrow<dag::InlAwi<BW, LEN>> for LazyInlAwi<BW, LEN> {
fn borrow(&self) -> &dag::InlAwi<BW, LEN> {
self
}
}

impl<const BW: usize, const LEN: usize> AsRef<dag::InlAwi<BW, LEN>> for LazyInlAwi<BW, LEN> {
fn as_ref(&self) -> &dag::InlAwi<BW, LEN> {
self
}
}

impl<const BW: usize, const LEN: usize> fmt::Debug for LazyInlAwi<BW, LEN> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "LazyAwi({:?})", self.state())
}
}

macro_rules! forward_lazyinlawi_fmt {
($($name:ident)*) => {
$(
impl<const BW: usize, const LEN: usize> fmt::$name for LazyInlAwi<BW, LEN> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
)*
};
}

forward_lazyinlawi_fmt!(Display LowerHex UpperHex Octal Binary);
5 changes: 2 additions & 3 deletions starlight/src/awi_structs/temporal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use awint::{

/// Returned from `Loop::drive` and other structures like `Net::drive` that use
/// `Loop`s internally, implements [awint::awint_dag::Lineage] so that the whole
/// DAG can be captured. In most cases, you will collect all the handles and add
/// them to the `leaves` argument of [awint::awint_dag::OpDag::from_epoch]
/// DAG can be captured.
#[derive(Debug, Clone)] // TODO make Copy
pub struct LoopHandle {
// just use this for now to have the non-sendability
Expand All @@ -27,7 +26,7 @@ impl Lineage for LoopHandle {
/// `AsRef<Bits>` impls, then consume the `Loop` with [Loop::drive].
///
/// The fundamental reason for temporal asymmetry is that there needs to be a
/// well defined root evaluation node and value.
/// well defined root evaluation state and value.
#[derive(Debug)] // do not implement `Clone`, but maybe implement a `duplicate` function that
// explicitly duplicates drivers and loopbacks?
pub struct Loop {
Expand Down
23 changes: 21 additions & 2 deletions starlight/src/ensemble/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ impl Stator {
}

impl Ensemble {
pub fn get_state_debug(&self, p_state: PState) -> Option<String> {
self.stator
.states
.get(p_state)
.map(|state| format!("{p_state} {state:#?}"))
}

pub fn dec_rc(&mut self, p_state: PState) -> Result<(), EvalError> {
if let Some(state) = self.stator.states.get_mut(p_state) {
state.rc = if let Some(x) = state.rc.checked_sub(1) {
Expand Down Expand Up @@ -343,8 +350,8 @@ impl Ensemble {
}
}
let needs_lower = match lock.ensemble.stator.states[p_state].op {
Opaque(..) | Literal(_) | Copy(_) | StaticGet(..) | StaticSet(..)
| StaticLut(..) => false,
Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..)
| StaticSet(..) | StaticLut(..) => false,
Lut([lut, inx]) => {
if let Literal(ref lit) = lock.ensemble.stator.states[lut].op {
let lit = lit.clone();
Expand Down Expand Up @@ -527,6 +534,18 @@ impl Ensemble {
} else if i >= ops.len() {
// checked all sources
match self.stator.states[p_state].op {
Assert([x]) => {
// this is the only foolproof way of doing this, at least without more
// branches
self.initialize_state_bits_if_needed(p_state).unwrap();
let len = self.stator.states[p_state].p_self_bits.len();
assert_eq!(len, self.stator.states[x].p_self_bits.len());
for i in 0..len {
let p_equiv0 = self.stator.states[p_state].p_self_bits[i].unwrap();
let p_equiv1 = self.stator.states[x].p_self_bits[i].unwrap();
self.union_equiv(p_equiv0, p_equiv1).unwrap();
}
}
Copy([x]) => {
// this is the only foolproof way of doing this, at least without more
// branches
Expand Down
Loading

0 comments on commit d0df853

Please sign in to comment.