Expand description
This provides an HDL (Hardware Design Language), combinational and temporal logic simulator and optimizer, and general purpose router for FPGAs and more. The HDL is special in that it is written in ordinary Rust code with all the features that Rust provides.
Most of the MVP features of this crate are ready, except for the Router
which is still a WIP and has a lot of todo!()
;
See the documentation of awint
/awint_dag
which is used as the backend
for this. awint
is the base library that operations are modeled off of.
awint_dag
allows for recording a DAG of arbitrary bitwidth integer
operations. starlight
lowers high level operations down into a DAG of
simple lookup tables, and also adds on temporal structs like Loop
s. It can
optimize, evaluate, and retroactively change values in the DAG
for various
purposes.
There are several features on this crate that enable awint
features. The
u32_ptrs
feature reduces the memory consumption of the algorithms
significantly, but limits the number of possible internal references to
about 4 billion, which the largest circuits might not fit in.
use std::num::NonZeroUsize;
use starlight::{awi, dag, Epoch, EvalAwi, LazyAwi};
// in the scope where this is glob imported, all arbitrary width types, some primitives, and
// the mechanisms in the macros will use mimicking types and be lazily evaluated in general.
use dag::*;
// This is just some arbitrary example I coded up, note that you can use
// almost all of Rust's features that you can use on the normal types
struct StateMachine {
data: inlawi_ty!(16),
counter: Awi,
}
impl StateMachine {
pub fn new(w: NonZeroUsize) -> Self {
Self {
data: inlawi!(0u16),
counter: Awi::zero(w),
}
}
pub fn update(&mut self, input: &Bits) -> Option<()> {
self.counter.inc_(true);
let mut s0 = inlawi!(0u4);
let mut s1 = inlawi!(0u4);
let mut s2 = inlawi!(0u4);
let mut s3 = inlawi!(0u4);
cc!(self.data; s3, s2, s1, s0)?;
s2.xor_(&s0)?;
s3.xor_(&s1)?;
s1.xor_(&s2)?;
s0.xor_(&s3)?;
s3.rotl_(1)?;
s2.mux_(&input, input.get(0)?)?;
cc!(s3, s2, s1, s0; self.data)?;
Some(())
}
}
// First, create an epoch, this will live until this struct is dropped. The
// epoch needs to live until all mimicking operations are done and states are
// lowered. Manually drop it with the `drop` function to avoid mistakes.
let epoch = Epoch::new();
let mut m = StateMachine::new(bw(4));
// this is initially an opaque value that cannot be eagerly evaluated
let input = LazyAwi::opaque(bw(4));
// if we later retroactively assign this to an unequal value, the
// `assert_assertions_strict` call will error and show the location of the
// assertion that errored
mimick::assert_eq!(Awi::from(&input), awi!(0101));
// step the state machine forward
m.update(&input).unwrap();
m.update(&awi!(0110)).unwrap();
m.update(&awi!(0110)).unwrap();
// use `EvalAwi`s to evaluate the resulting values
let output_counter = EvalAwi::from(m.counter);
let output_data = EvalAwi::from(m.data);
{
// switch back to normal structs
use awi::*;
// discard all unused mimicking states so the render is cleaner
epoch.prune_unused_states().unwrap();
// See the mimicking state DAG before it is lowered
epoch
.render_to_svgs_in_dir(std::path::PathBuf::from("./".to_owned()))
.unwrap();
// lower into purely static bit movements and lookup tables and optimize
epoch.optimize().unwrap();
// Now the combinational logic is described in a DAG of lookup tables that we
// could use for various purposes
epoch.ensemble(|ensemble| {
for state in ensemble.stator.states.vals() {
assert!(state.lowered_to_lnodes);
}
});
// "retroactively" assign the input with a non-opaque value
input.retro_(&awi!(0101)).unwrap();
// check assertions (all `dag::assert*` functions and dynamic `unwrap`s done
// during the current `Epoch`)
epoch.assert_assertions(true).unwrap();
// evaluate the outputs
assert_eq!(output_counter.eval().unwrap(), awi!(0011));
assert_eq!(output_data.eval().unwrap(), awi!(0xa505_u16));
// reassign and reevaluate
input.retro_(&awi!(1011)).unwrap();
assert!(epoch.assert_assertions(true).is_err());
assert_eq!(output_data.eval().unwrap(), awi!(0x7b0b_u16));
}
drop(epoch);
use starlight::{dag, awi, Epoch, EvalAwi};
use dag::*;
let epoch = Epoch::new();
let mut lhs = inlawi!(zero: ..8);
let rhs = inlawi!(umax: ..8);
let x = inlawi!(10101010);
let y = InlAwi::from_u64(4);
let mut output = inlawi!(0xffu8);
// error: expected `bool`, found struct `bool`
//if lhs.ult(&rhs).unwrap() {
// output.xor_(&x).unwrap();
//} else {
// output.lshr_(y.to_usize()).unwrap();
//};
// A little more cumbersome, but we get to use all the features of
// normal Rust in metaprogramming and don't have to support an entire DSL.
// In the future we will have more macros to help with this.
let lt = lhs.ult(&rhs).unwrap();
let mut tmp0 = output;
tmp0.xor_(&x).unwrap();
output.mux_(&tmp0, lt).unwrap();
let mut tmp1 = output;
tmp1.lshr_(y.to_usize()).unwrap();
output.mux_(&tmp1, !lt).unwrap();
let output_eval = EvalAwi::from(&output);
{
use awi::*;
assert_eq!(output_eval.eval().unwrap(), awi!(01010101));
}
drop(epoch);
Re-exports§
pub use ensemble::Corresponder;
pub use ensemble::Delay;
pub use utils::Error;
pub use awint;
pub use awint::awint_dag;
pub use awint::awint_dag::triple_arena;
Modules§
- awi
- Reexports all the regular arbitrary width integer structs, macros, common
enums, and most of
core::primitive::*
. This is useful for glob importing everything or for when using the regular items in a context with structs imported fromawint_dag
. - dag
- Reexports all the mimicking versions of
awi
items - ensemble
- Data structure internals used by this crate
- epoch
- Internals used for epoch management (most users should just be interacting
with
Epoch
andAssertions
) - lower
- Internal definitions used in lowering
- route
- WIP routing functionality
- utils
- Miscellanious utilities
Structs§
- Assertions
- A list of single bit
EvalAwi
s for assertions - Epoch
- Manages the lifetimes and assertions of
State
s created by mimicking types. - EvalAwi
- When created from a type implementing
AsRef<dag::Bits>
, it can later be used to evaluate its dynamic value. - In
- A wrapper around crate::LazyAwi that has a constant width
- LazyAwi
- When other mimicking types are created from a reference of this,
retro_
can later be called to retroactively change the input values of the DAG. - Loop
- Provides a way to temporally wrap around a combinatorial circuit.
- Net
- A reconfigurable
Net
that is aVec
-like vector of “ports” that are multiplexed to drive an internalLoop
. First, use a trait likeDeref<Target=Bits>
orAsRef<Bits>
to get the temporal value. Second, Net::push and Net::get_mut can be used to write values to each of the ports. Third, Net::drive takes a possibly dynamic index that multiplexes one of the values of the ports to drive the temporal value. - Out
- A wrapper around crate::EvalAwi that has a constant width.
- Suspended
Epoch - Represents a suspended epoch
Traits§
Functions§
- delay
- Delays the temporal value propogation of
bits
bydelay
.