use std::{borrow::Borrow, num::NonZeroUsize, ops::Deref};
use awint::awint_dag::{Lineage, Op, PState};
use crate::{awi, dag, epoch::get_current_epoch, lower::meta::general_mux, Delay, Error};
pub(crate) const DELAY: &str = "starlight::delay";
pub(crate) const UNDRIVEN_LOOP_SOURCE: &str = "starlight::undriven_loop_source";
pub(crate) const LOOP_SOURCE: &str = "starlight::loop_source";
pub(crate) const DELAYED_LOOP_SOURCE: &str = "starlight::delayed_loop_source";
#[track_caller]
pub fn delay<D: Into<Delay>>(bits: &mut dag::Bits, delay: D) {
let epoch = get_current_epoch().expect("cannot use `starlight::delay` without an active epoch");
let mut delay = awi::Awi::from_u128(delay.into().amount());
delay.shrink_to_msb();
if !delay.is_zero() {
bits.opaque_(DELAY, &[&dag::Awi::arg(&delay)]);
let mut lock = epoch.epoch_data.borrow_mut();
lock.ensemble.stator.states_to_lower.push(bits.state());
}
}
#[derive(Debug)] pub struct Loop {
source: dag::Awi,
}
macro_rules! loop_basic_value {
($($fn:ident)*) => {
$(
pub fn $fn(w: NonZeroUsize) -> Self {
Self::from_state(dag::Awi::$fn(w).state())
}
)*
}
}
macro_rules! loop_from_impl {
($($fn:ident $t:ident);*;) => {
$(
pub fn $fn(x: dag::$t) -> Self {
Self::from_state(x.state())
}
)*
}
}
impl Loop {
loop_from_impl!(
from_bool bool;
from_u8 u8;
from_i8 i8;
from_u16 u16;
from_i16 i16;
from_u32 u32;
from_i32 i32;
from_u64 u64;
from_i64 i64;
from_u128 u128;
from_i128 i128;
from_usize usize;
from_isize isize;
);
}
impl Loop {
loop_basic_value!(opaque zero umax imax imin uone);
pub fn from_state(p_state: PState) -> Self {
let w = p_state.get_nzbw();
let source =
dag::Awi::opaque_with(w, UNDRIVEN_LOOP_SOURCE, &[&dag::Awi::from_state(p_state)]);
Self { source }
}
pub fn from_bits(bits: &dag::Bits) -> Self {
Self::from_state(bits.state())
}
#[must_use]
pub fn nzbw(&self) -> NonZeroUsize {
self.source.nzbw()
}
#[must_use]
pub fn bw(&self) -> usize {
self.source.bw()
}
pub fn drive(self, driver: &dag::Bits) -> Result<(), Error> {
let epoch = get_current_epoch()?;
let lhs_w = self.source.bw();
let rhs_w = driver.bw();
if lhs_w != rhs_w {
Err(Error::BitwidthMismatch(lhs_w, rhs_w))
} else {
let mut lock = epoch.epoch_data.borrow_mut();
let op = &mut lock
.ensemble
.stator
.states
.get_mut(self.source.state())
.unwrap()
.op;
if let Op::Opaque(v, name) = op {
assert_eq!(*name, Some(UNDRIVEN_LOOP_SOURCE));
assert_eq!(v.len(), 1);
v.push(driver.state());
*name = Some(LOOP_SOURCE);
} else {
unreachable!()
}
lock.ensemble
.stator
.states
.get_mut(driver.state())
.unwrap()
.inc_rc();
lock.ensemble
.stator
.states_to_lower
.push(self.source.state());
Ok(())
}
}
pub fn drive_with_delay<D: Into<Delay>>(
self,
driver: &dag::Bits,
delay: D,
) -> Result<(), Error> {
let delay = delay.into();
if delay.is_zero() {
self.drive(driver)
} else {
let epoch = get_current_epoch()?;
let lhs_w = self.source.bw();
let rhs_w = driver.bw();
if lhs_w != rhs_w {
return Err(Error::BitwidthMismatch(lhs_w, rhs_w))
}
let mut delay = awi::Awi::from_u128(delay.amount());
delay.shrink_to_msb();
let delay = dag::Awi::arg(&delay).state();
let mut lock = epoch.epoch_data.borrow_mut();
let op = &mut lock
.ensemble
.stator
.states
.get_mut(self.source.state())
.unwrap()
.op;
if let Op::Opaque(v, name) = op {
assert_eq!(*name, Some(UNDRIVEN_LOOP_SOURCE));
assert_eq!(v.len(), 1);
v.push(driver.state());
v.push(delay);
*name = Some(DELAYED_LOOP_SOURCE);
} else {
unreachable!()
}
lock.ensemble
.stator
.states
.get_mut(driver.state())
.unwrap()
.inc_rc();
lock.ensemble.stator.states.get_mut(delay).unwrap().inc_rc();
lock.ensemble
.stator
.states_to_lower
.push(self.source.state());
Ok(())
}
}
}
impl Deref for Loop {
type Target = dag::Bits;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl Borrow<dag::Bits> for Loop {
fn borrow(&self) -> &dag::Bits {
&self.source
}
}
impl AsRef<dag::Bits> for Loop {
fn as_ref(&self) -> &dag::Bits {
&self.source
}
}
#[derive(Debug)]
pub struct Net {
source: Loop,
ports: Vec<dag::Awi>,
}
macro_rules! net_basic_value {
($($fn:ident)*) => {
$(
pub fn $fn(w: NonZeroUsize) -> Self {
Self::from_state(dag::Awi::$fn(w).state())
}
)*
}
}
macro_rules! net_from_impl {
($($fn:ident $t:ident);*;) => {
$(
pub fn $fn(x: dag::$t) -> Self {
Self::from_state(x.state())
}
)*
}
}
macro_rules! net_push_impl {
($($fn:ident $t:ident);*;) => {
$(
#[must_use]
pub fn $fn(&mut self, port: dag::$t) -> Option<()> {
self.push_state(port.state())
}
)*
}
}
impl Net {
net_from_impl!(
from_bool bool;
from_u8 u8;
from_i8 i8;
from_u16 u16;
from_i16 i16;
from_u32 u32;
from_i32 i32;
from_u64 u64;
from_i64 i64;
from_u128 u128;
from_i128 i128;
from_usize usize;
from_isize isize;
);
}
impl Net {
net_push_impl!(
push_bool bool;
push_u8 u8;
push_i8 i8;
push_u16 u16;
push_i16 i16;
push_u32 u32;
push_i32 i32;
push_u64 u64;
push_i64 i64;
push_u128 u128;
push_i128 i128;
push_usize usize;
push_isize isize;
);
}
impl Net {
net_basic_value!(opaque zero umax imax imin uone);
pub fn from_state(p_state: PState) -> Self {
Self {
source: Loop::from_state(p_state),
ports: vec![],
}
}
pub fn from_bits(bits: &dag::Bits) -> Self {
Self::from_state(bits.state())
}
#[must_use]
pub fn len(&self) -> usize {
self.ports.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.ports.is_empty()
}
#[must_use]
pub fn nzbw(&self) -> NonZeroUsize {
self.source.nzbw()
}
#[must_use]
pub fn bw(&self) -> usize {
self.source.bw()
}
#[must_use]
pub fn push_state(&mut self, port: PState) -> Option<()> {
if port.get_nzbw() != self.nzbw() {
None
} else {
self.ports.push(dag::Awi::from_state(port));
Some(())
}
}
#[must_use]
pub fn push(&mut self, port: &dag::Bits) -> Option<()> {
self.push_state(port.state())
}
#[must_use]
pub fn get_mut(&mut self, i: usize) -> Option<&mut dag::Bits> {
self.ports.get_mut(i).map(|x| x.as_mut())
}
#[must_use]
pub fn exchange(&mut self, rhs: &mut Self) -> Option<()> {
if self.bw() != rhs.bw() {
None
} else {
self.ports.push(dag::Awi::from(rhs.as_ref()));
rhs.ports.push(dag::Awi::from(self.as_ref()));
Some(())
}
}
#[must_use]
pub fn drive(self, inx: &dag::Bits) -> dag::Option<()> {
use dag::*;
if self.is_empty() {
return dag::Option::None;
}
if self.len() == 1 {
self.source.drive(&self.ports[0]).unwrap();
return dag::Option::some_at_dagtime((), inx.is_zero());
}
let max_inx = self.len() - 1;
let max_inx_bits = dag::Bits::nontrivial_bits(max_inx).unwrap().get();
let should_stay_zero = if max_inx_bits < inx.bw() {
awi!(inx[max_inx_bits..]).unwrap()
} else {
awi!(0)
};
let mut in_range = should_stay_zero.is_zero();
if (!self.len().is_power_of_two()) && (inx.bw() >= max_inx_bits) {
let mut max = dag::Awi::zero(inx.nzbw());
max.usize_(max_inx);
let le = inx.ule(&max).unwrap();
in_range &= le;
}
let small_inx = if max_inx_bits < inx.bw() {
awi!(inx[..max_inx_bits]).unwrap()
} else if max_inx_bits > inx.bw() {
awi!(zero: .., inx; ..max_inx_bits).unwrap()
} else {
Awi::from(inx)
};
let tmp = general_mux(&self.ports, &small_inx);
self.source.drive(&tmp).unwrap();
dag::Option::some_at_dagtime((), in_range)
}
}
impl Deref for Net {
type Target = dag::Bits;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl Borrow<dag::Bits> for Net {
fn borrow(&self) -> &dag::Bits {
&self.source
}
}
impl AsRef<dag::Bits> for Net {
fn as_ref(&self) -> &dag::Bits {
&self.source
}
}