simple extern module simulation works!

This commit is contained in:
Jacob Lifshay 2025-03-21 01:45:18 -07:00
parent f378b9a1d2
commit a5a7284483
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
17 changed files with 650 additions and 167 deletions

View file

@ -20,9 +20,10 @@ use crate::{
},
memory::PortKind,
module::{
transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, Id, InstantiatedModule,
ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration,
StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule,
transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, ExternModuleBody, Id,
InstantiatedModule, ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, StmtConnect,
StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire,
TargetInInstantiatedModule,
},
prelude::*,
reset::{ResetType, ResetTypeDispatch},
@ -1631,12 +1632,21 @@ impl<T> fmt::Debug for DebugOpaque<T> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct CompiledExternModule {
io_ty: Bundle,
module_io_targets: Interned<[Target]>,
module_io: Interned<[CompiledValue<CanonicalType>]>,
simulation: ExternModuleSimulation<Bundle>,
}
#[derive(Debug)]
pub struct Compiler {
insns: Insns<InsnsBuilding>,
original_base_module: Interned<Module<Bundle>>,
base_module: Interned<Module<Bundle>>,
modules: HashMap<InstantiatedModule, CompiledModule>,
extern_modules: Vec<CompiledExternModule>,
compiled_values: HashMap<TargetInInstantiatedModule, CompiledValue<CanonicalType>>,
compiled_exprs: HashMap<Expr<CanonicalType>, CompiledExpr<CanonicalType>>,
compiled_exprs_to_values: HashMap<CompiledExpr<CanonicalType>, CompiledValue<CanonicalType>>,
@ -1665,6 +1675,7 @@ impl Compiler {
original_base_module,
base_module,
modules: HashMap::new(),
extern_modules: Vec::new(),
compiled_values: HashMap::new(),
compiled_exprs: HashMap::new(),
compiled_exprs_to_values: HashMap::new(),
@ -4690,8 +4701,28 @@ impl Compiler {
ModuleBody::Normal(NormalModuleBody { body }) => {
self.compile_block(module, body, Interned::default(), &mut trace_decls);
}
ModuleBody::Extern(_extern_module_body) => {
todo!("simulating extern module: {:?}", module);
ModuleBody::Extern(ExternModuleBody {
verilog_name: _,
parameters: _,
simulation,
}) => {
let Some(simulation) = simulation else {
panic!(
"can't simulate extern module without extern_module_simulation: {}",
module.leaf_module().source_location()
);
};
self.extern_modules.push(CompiledExternModule {
io_ty: module.leaf_module().io_ty(),
module_io_targets: module
.leaf_module()
.module_io()
.iter()
.map(|v| Target::from(v.module_io))
.collect(),
module_io,
simulation,
});
}
}
let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else {
@ -4972,6 +5003,7 @@ impl Compiler {
Compiled {
insns: Insns::from(self.insns).intern_sized(),
base_module,
extern_modules: Intern::intern_owned(self.extern_modules),
io: Instance::new_unchecked(
ScopedNameId(
NameId("<simulator>".intern(), Id::new()),
@ -5004,6 +5036,7 @@ struct CompiledModule {
pub struct Compiled<T: BundleType> {
insns: Interned<Insns<InsnsBuildingDone>>,
base_module: CompiledModule,
extern_modules: Interned<[CompiledExternModule]>,
io: Instance<T>,
traces: SimTraces<Interned<[SimTrace<SimTraceKind, ()>]>>,
trace_memories: Interned<[(StatePartIndex<StatePartKindMemories>, TraceMem)]>,
@ -5018,6 +5051,7 @@ impl<T: BundleType> Compiled<T> {
let Self {
insns,
base_module,
extern_modules,
io,
traces,
trace_memories,
@ -5026,6 +5060,7 @@ impl<T: BundleType> Compiled<T> {
Compiled {
insns,
base_module,
extern_modules,
io: Instance::from_canonical(io.canonical()),
traces,
trace_memories,
@ -5036,6 +5071,7 @@ impl<T: BundleType> Compiled<T> {
let Compiled {
insns,
base_module,
extern_modules,
io,
traces,
trace_memories,
@ -5044,6 +5080,7 @@ impl<T: BundleType> Compiled<T> {
Self {
insns,
base_module,
extern_modules,
io: Instance::from_canonical(io.canonical()),
traces,
trace_memories,
@ -6785,6 +6822,7 @@ impl Ord for WaitTarget {
struct SimulationExternModuleState {
module_state: SimulationModuleState,
io_ty: Bundle,
sim: ExternModuleSimulation<Bundle>,
running_generator: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
wait_target: Option<WaitTarget>,
@ -6794,12 +6832,14 @@ impl fmt::Debug for SimulationExternModuleState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
module_state,
io_ty,
sim,
running_generator,
wait_target,
} = self;
f.debug_struct("SimulationExternModuleState")
.field("module_state", module_state)
.field("io_ty", io_ty)
.field("sim", sim)
.field(
"running_generator",
@ -6894,12 +6934,29 @@ impl std::task::Wake for GeneratorWaker {
}
}
#[derive(Default)]
struct ReadyToRunSet {
state_ready_to_run: bool,
extern_modules_ready_to_run: Vec<usize>,
}
impl ReadyToRunSet {
fn clear(&mut self) {
let Self {
state_ready_to_run,
extern_modules_ready_to_run,
} = self;
*state_ready_to_run = false;
extern_modules_ready_to_run.clear();
}
}
struct SimulationImpl {
state: interpreter::State,
io: Expr<Bundle>,
main_module: SimulationModuleState,
extern_modules: Box<[SimulationExternModuleState]>,
needs_settle: bool,
state_ready_to_run: bool,
trace_decls: TraceModule,
traces: SimTraces<Box<[SimTrace<SimTraceKind, BitVec>]>>,
trace_memories: HashMap<StatePartIndex<StatePartKindMemories>, TraceMem>,
@ -6923,7 +6980,7 @@ impl SimulationImpl {
io: self_io,
main_module,
extern_modules,
needs_settle,
state_ready_to_run,
trace_decls,
traces,
trace_memories,
@ -6938,7 +6995,7 @@ impl SimulationImpl {
.field("io", io.unwrap_or(self_io))
.field("main_module", main_module)
.field("extern_modules", extern_modules)
.field("needs_settle", needs_settle)
.field("state_ready_to_run", state_ready_to_run)
.field("trace_decls", trace_decls)
.field("traces", traces)
.field("trace_memories", trace_memories)
@ -6949,8 +7006,27 @@ impl SimulationImpl {
}
fn new(compiled: Compiled<Bundle>) -> Self {
let io_target = Target::from(compiled.io);
// TODO: add extern_modules
let extern_modules: Box<[SimulationExternModuleState]> = Box::new([]);
let extern_modules = Box::from_iter(compiled.extern_modules.iter().map(
|&CompiledExternModule {
io_ty,
module_io_targets,
module_io,
simulation,
}| {
SimulationExternModuleState {
module_state: SimulationModuleState::new(
module_io_targets
.iter()
.copied()
.zip(module_io.iter().copied()),
),
io_ty,
sim: simulation,
running_generator: None,
wait_target: Some(WaitTarget::Settle),
}
},
));
Self {
state: State::new(compiled.insns),
io: compiled.io.to_expr(),
@ -6972,7 +7048,7 @@ impl SimulationImpl {
}),
),
extern_modules,
needs_settle: true,
state_ready_to_run: true,
trace_decls: compiled.base_module.trace_decls,
traces: SimTraces(Box::from_iter(compiled.traces.0.iter().map(
|&SimTrace {
@ -7129,20 +7205,9 @@ impl SimulationImpl {
}
}
#[track_caller]
fn advance_time(this: &Rc<RefCell<Self>>, duration: SimDuration) {
Self::settle(this);
let mut this = this.borrow_mut();
this.instant += duration;
this.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
match &mut trace_writer_state {
TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(),
TraceWriterState::Running(trace_writer) => {
trace_writer.change_time_to(this.instant)?;
}
TraceWriterState::Errored(_) => {}
}
Ok(trace_writer_state)
});
fn advance_time(this_ref: &Rc<RefCell<Self>>, duration: SimDuration) {
let instant = this_ref.borrow().instant + duration;
Self::run_until(this_ref, WaitTarget::Instant(instant));
}
#[must_use]
fn yield_advance_time_or_settle(
@ -7197,47 +7262,94 @@ impl SimulationImpl {
target,
}
}
fn get_ready_external_modules(&mut self, run_list: &mut Vec<usize>) -> Option<WaitTarget> {
run_list.clear();
let mut iter = self.extern_modules.iter_mut().enumerate().filter_map(
|(module_index, extern_module)| Some((module_index, extern_module.wait_target?)),
);
let (first_module_index, mut wait_target) = iter.next()?;
run_list.push(first_module_index);
for (next_module_index, next_wait_target) in iter {
match next_wait_target.cmp(&wait_target) {
std::cmp::Ordering::Less => run_list.clear(),
/// returns the next `WaitTarget` and the set of things ready to run then.
fn get_ready_to_run_set(&self, ready_to_run_set: &mut ReadyToRunSet) -> Option<WaitTarget> {
ready_to_run_set.clear();
let mut wait_target = None;
if self.state_ready_to_run {
ready_to_run_set.state_ready_to_run = true;
wait_target = Some(WaitTarget::Settle);
}
for (module_index, extern_module) in self.extern_modules.iter().enumerate() {
let Some(extern_module_wait_target) = extern_module.wait_target else {
continue;
};
if let Some(wait_target) = &mut wait_target {
match extern_module_wait_target.cmp(wait_target) {
std::cmp::Ordering::Less => ready_to_run_set.clear(),
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => continue,
}
run_list.push(next_module_index);
wait_target = next_wait_target;
} else {
wait_target = Some(extern_module_wait_target);
}
Some(wait_target)
ready_to_run_set
.extern_modules_ready_to_run
.push(module_index);
}
wait_target
}
fn set_instant_no_sim(&mut self, instant: SimInstant) {
self.instant = instant;
self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
match &mut trace_writer_state {
TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(),
TraceWriterState::Running(trace_writer) => {
trace_writer.change_time_to(this.instant)?;
}
TraceWriterState::Errored(_) => {}
}
Ok(trace_writer_state)
});
}
#[track_caller]
fn settle(this_ref: &Rc<RefCell<Self>>) {
fn run_until(this_ref: &Rc<RefCell<Self>>, run_target: WaitTarget) {
let mut this = this_ref.borrow_mut();
let mut ready_to_run_set = ReadyToRunSet::default();
let generator_waker = this.generator_waker.clone();
assert!(
this.main_module.uninitialized_ios.is_empty(),
"didn't initialize all inputs",
);
let mut run_list = Vec::new();
let generator_waker = this.generator_waker.clone();
for _ in 0..100000 {
if !this.needs_settle {
if let Some(wait_target) = this.get_ready_external_modules(&mut run_list) {
if wait_target > WaitTarget::Instant(this.instant) {
return;
match run_target {
WaitTarget::Settle => {}
WaitTarget::Instant(run_target) => assert!(run_target >= this.instant),
}
for module_index in run_list.drain(..) {
this.extern_modules[module_index].wait_target = None;
let Some(mut generator) =
this.extern_modules[module_index].running_generator.take()
else {
let mut settle_cycle = 0;
let mut run_extern_modules = true;
loop {
assert!(settle_cycle < 100000, "settle(): took too many steps");
settle_cycle += 1;
let next_wait_target = match this.get_ready_to_run_set(&mut ready_to_run_set) {
Some(next_wait_target) if next_wait_target <= run_target => next_wait_target,
_ => break,
};
match next_wait_target {
WaitTarget::Settle => {}
WaitTarget::Instant(instant) => {
settle_cycle = 0;
this.set_instant_no_sim(instant);
}
}
if run_extern_modules {
for module_index in ready_to_run_set.extern_modules_ready_to_run.drain(..) {
let extern_module = &mut this.extern_modules[module_index];
extern_module.wait_target = None;
let mut generator = if !extern_module.module_state.did_initial_settle {
let sim = extern_module.sim;
let io_ty = extern_module.io_ty;
drop(this);
Box::into_pin(sim.run(ExternModuleSimulationState {
sim_impl: this_ref.clone(),
module_index,
io_ty,
}))
} else if let Some(generator) = extern_module.running_generator.take() {
drop(this);
generator
} else {
continue;
};
drop(this);
let generator = match generator
.as_mut()
.poll(&mut std::task::Context::from_waker(&generator_waker))
@ -7246,12 +7358,26 @@ impl SimulationImpl {
Poll::Pending => Some(generator),
};
this = this_ref.borrow_mut();
this.extern_modules[module_index]
.module_state
.did_initial_settle = true;
if !this.extern_modules[module_index]
.module_state
.uninitialized_ios
.is_empty()
{
panic!(
"extern module didn't initialize all outputs before \
waiting, settling, or reading any inputs: {}",
this.extern_modules[module_index].sim.source_location
);
}
this.extern_modules[module_index].running_generator = generator;
}
continue;
}
return;
}
if ready_to_run_set.state_ready_to_run {
this.state_ready_to_run = false;
run_extern_modules = true;
this.state.setup_call(0);
if this.breakpoints.is_some() {
loop {
@ -7278,6 +7404,16 @@ impl SimulationImpl {
} else {
let RunResult::Return(()) = this.state.run(());
}
if this
.clocks_triggered
.iter()
.any(|i| this.state.small_slots[*i] != 0)
{
this.state_ready_to_run = true;
// wait for clocks to settle before running extern modules again
run_extern_modules = false;
}
}
if this.main_module.did_initial_settle {
this.read_traces::<false>();
} else {
@ -7286,10 +7422,6 @@ impl SimulationImpl {
this.state.memory_write_log.sort_unstable();
this.state.memory_write_log.dedup();
this.main_module.did_initial_settle = true;
this.needs_settle = this
.clocks_triggered
.iter()
.any(|i| this.state.small_slots[*i] != 0);
this.for_each_trace_writer_storing_error(|this, trace_writer_state| {
Ok(match trace_writer_state {
TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running(
@ -7310,7 +7442,14 @@ impl SimulationImpl {
});
this.state.memory_write_log.clear();
}
panic!("settle(): took too many steps");
match run_target {
WaitTarget::Settle => {}
WaitTarget::Instant(instant) => this.set_instant_no_sim(instant),
}
}
#[track_caller]
fn settle(this_ref: &Rc<RefCell<Self>>) {
Self::run_until(this_ref, WaitTarget::Settle);
}
fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState {
match which_module {
@ -7342,7 +7481,7 @@ impl SimulationImpl {
let compiled_value = self
.get_module_mut(which_module)
.write_helper(io, which_module);
self.needs_settle = true;
self.state_ready_to_run = true;
match compiled_value.range.len() {
TypeLen::A_SMALL_SLOT => {
self.state.small_slots[compiled_value.range.small_slots.start] = value as _;
@ -7374,7 +7513,7 @@ impl SimulationImpl {
let compiled_value = self
.get_module_mut(which_module)
.write_helper(Expr::canonical(io), which_module);
self.needs_settle = true;
self.state_ready_to_run = true;
let value: BigInt = value.into();
match compiled_value.range.len() {
TypeLen::A_SMALL_SLOT => {
@ -7495,7 +7634,7 @@ impl SimulationImpl {
let compiled_value = self
.get_module_mut(which_module)
.write_helper(io, which_module);
self.needs_settle = true;
self.state_ready_to_run = true;
assert_eq!(Expr::ty(io), value.ty());
Self::read_write_sim_value_helper(
&mut self.state,
@ -7531,7 +7670,6 @@ impl SimulationImpl {
MaybeNeedsSettle::NoSettleNeeded(v) => v,
}
}
#[track_caller]
async fn yield_settle_if_needed<F, O>(
this_ref: &Rc<RefCell<Self>>,
module_index: usize,
@ -7713,18 +7851,22 @@ impl<T: BundleType> fmt::Debug for Simulation<T> {
}
macro_rules! impl_simulation_methods {
(async_await = ($($async:tt, $await:tt)?)) => {
#[track_caller]
pub $($async)? fn read_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>) -> I::Value {
let retval = self
(
async_await = ($($async:tt, $await:tt)?),
track_caller = ($(#[$track_caller:tt])?),
which_module = |$self:ident| $which_module:expr,
) => {
$(#[$track_caller])?
pub $($async)? fn read_bool_or_int<I: BoolOrIntType>(&mut $self, io: Expr<I>) -> I::Value {
let retval = $self
.sim_impl
.borrow_mut()
.read_bool_or_int(io, WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
.read_bool_or_int(io, $which_module);
$self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
$(#[$track_caller])?
pub $($async)? fn write_bool_or_int<I: BoolOrIntType>(
&mut self,
&mut $self,
io: Expr<I>,
value: impl ToExpr<Type = I>,
) {
@ -7733,68 +7875,68 @@ macro_rules! impl_simulation_methods {
let value = value
.to_literal_bits()
.expect("the value that is being written to an input must be a literal");
self.sim_impl.borrow_mut().write_bool_or_int(
$self.sim_impl.borrow_mut().write_bool_or_int(
io,
I::bits_to_value(Cow::Borrowed(&value)),
WhichModule::Main,
$which_module,
);
}
#[track_caller]
pub $($async)? fn write_clock(&mut self, io: Expr<Clock>, value: bool) {
self.sim_impl
$(#[$track_caller])?
pub $($async)? fn write_clock(&mut $self, io: Expr<Clock>, value: bool) {
$self.sim_impl
.borrow_mut()
.write_bit(Expr::canonical(io), value, WhichModule::Main);
.write_bit(Expr::canonical(io), value, $which_module);
}
#[track_caller]
pub $($async)? fn read_clock(&mut self, io: Expr<Clock>) -> bool {
let retval = self
$(#[$track_caller])?
pub $($async)? fn read_clock(&mut $self, io: Expr<Clock>) -> bool {
let retval = $self
.sim_impl
.borrow_mut()
.read_bit(Expr::canonical(io), WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
.read_bit(Expr::canonical(io), $which_module);
$self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn write_bool(&mut self, io: Expr<Bool>, value: bool) {
self.sim_impl
$(#[$track_caller])?
pub $($async)? fn write_bool(&mut $self, io: Expr<Bool>, value: bool) {
$self.sim_impl
.borrow_mut()
.write_bit(Expr::canonical(io), value, WhichModule::Main);
.write_bit(Expr::canonical(io), value, $which_module);
}
#[track_caller]
pub $($async)? fn read_bool(&mut self, io: Expr<Bool>) -> bool {
let retval = self
$(#[$track_caller])?
pub $($async)? fn read_bool(&mut $self, io: Expr<Bool>) -> bool {
let retval = $self
.sim_impl
.borrow_mut()
.read_bit(Expr::canonical(io), WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
.read_bit(Expr::canonical(io), $which_module);
$self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn write_reset<R: ResetType>(&mut self, io: Expr<R>, value: bool) {
self.sim_impl
$(#[$track_caller])?
pub $($async)? fn write_reset<R: ResetType>(&mut $self, io: Expr<R>, value: bool) {
$self.sim_impl
.borrow_mut()
.write_bit(Expr::canonical(io), value, WhichModule::Main);
.write_bit(Expr::canonical(io), value, $which_module);
}
#[track_caller]
pub $($async)? fn read_reset<R: ResetType>(&mut self, io: Expr<R>) -> bool {
let retval = self
$(#[$track_caller])?
pub $($async)? fn read_reset<R: ResetType>(&mut $self, io: Expr<R>) -> bool {
let retval = $self
.sim_impl
.borrow_mut()
.read_bit(Expr::canonical(io), WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
.read_bit(Expr::canonical(io), $which_module);
$self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn read<IO: Type>(&mut self, io: Expr<IO>) -> SimValue<IO> {
let retval = self
$(#[$track_caller])?
pub $($async)? fn read<IO: Type>(&mut $self, io: Expr<IO>) -> SimValue<IO> {
let retval = $self
.sim_impl
.borrow_mut()
.read(Expr::canonical(io), WhichModule::Main);
SimValue::from_canonical(self.settle_if_needed(retval)$(.$await)?)
.read(Expr::canonical(io), $which_module);
SimValue::from_canonical($self.settle_if_needed(retval)$(.$await)?)
}
#[track_caller]
pub $($async)? fn write<IO: Type, V: ToSimValue<IO>>(&mut self, io: Expr<IO>, value: V) {
self.sim_impl.borrow_mut().write(
$(#[$track_caller])?
pub $($async)? fn write<IO: Type, V: ToSimValue<IO>>(&mut $self, io: Expr<IO>, value: V) {
$self.sim_impl.borrow_mut().write(
Expr::canonical(io),
value.into_sim_value(Expr::ty(io)).into_canonical(),
WhichModule::Main,
$which_module,
);
}
};
@ -7855,7 +7997,11 @@ impl<T: BundleType> Simulation<T> {
{
SimulationImpl::settle_if_needed(&self.sim_impl, v)
}
impl_simulation_methods!(async_await = ());
impl_simulation_methods!(
async_await = (),
track_caller = (#[track_caller]),
which_module = |self| WhichModule::Main,
);
#[doc(hidden)]
/// This is explicitly unstable and may be changed/removed at any time
pub fn set_breakpoints_unstable(&mut self, pcs: HashSet<usize>, trace: bool) {
@ -7913,12 +8059,10 @@ impl<T: BundleType> ExternModuleSimulationState<T> {
io_ty: T::from_canonical(io_ty.canonical()),
}
}
#[track_caller]
pub async fn settle(&mut self) {
SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None)
.await
}
#[track_caller]
pub async fn advance_time(&mut self, duration: SimDuration) {
SimulationImpl::yield_advance_time_or_settle(
self.sim_impl.clone(),
@ -7927,14 +8071,17 @@ impl<T: BundleType> ExternModuleSimulationState<T> {
)
.await
}
#[track_caller]
async fn settle_if_needed<F, O>(&mut self, v: MaybeNeedsSettle<F, O>) -> O
where
for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>,
{
SimulationImpl::yield_settle_if_needed(&self.sim_impl, self.module_index, v).await
}
impl_simulation_methods!(async_await = (async, await));
impl_simulation_methods!(
async_await = (async, await),
track_caller = (),
which_module = |self| WhichModule::Extern { module_index: self.module_index },
);
}
pub trait ExternModuleSimGenerator:

View file

@ -5,11 +5,14 @@ use fayalite::{
int::UIntValue,
prelude::*,
reset::ResetType,
sim::{time::SimDuration, vcd::VcdWriterDecls, Simulation, ToSimValue},
sim::{
time::SimDuration, vcd::VcdWriterDecls, ExternModuleSimGenerator,
ExternModuleSimulationState, Simulation, ToSimValue,
},
ty::StaticType,
util::RcWriter,
};
use std::num::NonZeroUsize;
use std::{future::IntoFuture, num::NonZeroUsize};
#[hdl_module(outline_generated)]
pub fn connect_const() {
@ -1443,3 +1446,61 @@ fn test_conditional_assignment_last() {
panic!();
}
}
#[hdl_module(outline_generated, extern)]
pub fn extern_module() {
#[hdl]
let i: Bool = m.input();
#[hdl]
let o: Bool = m.output();
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct Sim {
i: Expr<Bool>,
o: Expr<Bool>,
}
impl ExternModuleSimGenerator for Sim {
type IOType = extern_module;
fn run<'a>(
&'a self,
mut sim: ExternModuleSimulationState<Self::IOType>,
) -> impl IntoFuture<Output = ()> + 'a {
let Self { i, o } = *self;
async move {
sim.write(o, true).await;
sim.advance_time(SimDuration::from_nanos(500)).await;
let mut invert = false;
loop {
sim.advance_time(SimDuration::from_micros(1)).await;
let v = sim.read_bool(i).await;
sim.write(o, v ^ invert).await;
invert = !invert;
}
}
}
}
m.extern_module_simulation(Sim { i, o });
}
#[test]
fn test_extern_module() {
let _n = SourceLocation::normalize_files_for_tests();
let mut sim = Simulation::new(extern_module());
let mut writer = RcWriter::default();
sim.add_trace_writer(VcdWriterDecls::new(writer.clone()));
sim.write(sim.io().i, false);
sim.advance_time(SimDuration::from_micros(10));
sim.write(sim.io().i, true);
sim.advance_time(SimDuration::from_micros(10));
sim.flush_traces().unwrap();
let vcd = String::from_utf8(writer.take()).unwrap();
println!("####### VCD:\n{vcd}\n#######");
if vcd != include_str!("sim/expected/extern_module.vcd") {
panic!();
}
let sim_debug = format!("{sim:#?}");
println!("#######\n{sim_debug}\n#######");
if sim_debug != include_str!("sim/expected/extern_module.txt") {
panic!();
}
}

View file

@ -819,7 +819,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "array_rw",
children: [

View file

@ -115,7 +115,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "conditional_assignment_last",
children: [

View file

@ -91,7 +91,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "connect_const",
children: [

View file

@ -134,7 +134,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "connect_const_reset",
children: [

View file

@ -254,7 +254,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "counter",
children: [

View file

@ -235,7 +235,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "counter",
children: [

View file

@ -95,7 +95,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "duplicate_names",
children: [

View file

@ -1336,7 +1336,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "enums",
children: [

View file

@ -0,0 +1,224 @@
Simulation {
state: State {
insns: Insns {
state_layout: StateLayout {
ty: TypeLayout {
small_slots: StatePartLayout<SmallSlots> {
len: 0,
debug_data: [],
..
},
big_slots: StatePartLayout<BigSlots> {
len: 2,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(extern_module: extern_module).extern_module::i",
ty: Bool,
},
SlotDebugData {
name: "InstantiatedModule(extern_module: extern_module).extern_module::o",
ty: Bool,
},
],
..
},
},
memories: StatePartLayout<Memories> {
len: 0,
debug_data: [],
layout_data: [],
..
},
},
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Return,
],
..
},
pc: 0,
memory_write_log: [],
memories: StatePart {
value: [],
},
small_slots: StatePart {
value: [],
},
big_slots: StatePart {
value: [
1,
1,
],
},
},
io: Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
},
main_module: SimulationModuleState {
base_targets: [
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.i,
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.o,
],
uninitialized_ios: {},
io_targets: {
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.i,
Instance {
name: <simulator>::extern_module,
instantiated: Module {
name: extern_module,
..
},
}.o,
},
did_initial_settle: true,
},
extern_modules: [
SimulationExternModuleState {
module_state: SimulationModuleState {
base_targets: [
ModuleIO {
name: extern_module::i,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module::o,
is_input: false,
ty: Bool,
..
},
],
uninitialized_ios: {},
io_targets: {
ModuleIO {
name: extern_module::i,
is_input: true,
ty: Bool,
..
},
ModuleIO {
name: extern_module::o,
is_input: false,
ty: Bool,
..
},
},
did_initial_settle: true,
},
io_ty: Bundle {
#[hdl(flip)] /* offset = 0 */
i: Bool,
/* offset = 1 */
o: Bool,
},
sim: ExternModuleSimulation {
generator: Sim {
i: ModuleIO {
name: extern_module::i,
is_input: true,
ty: Bool,
..
},
o: ModuleIO {
name: extern_module::o,
is_input: false,
ty: Bool,
..
},
},
source_location: SourceLocation(
module-XXXXXXXXXX.rs:4:1,
),
_phantom: PhantomData<fayalite::bundle::Bundle>,
},
running_generator: Some(
...,
),
wait_target: Some(
Instant(
20.500000000000 μs,
),
),
},
],
state_ready_to_run: false,
trace_decls: TraceModule {
name: "extern_module",
children: [
TraceModuleIO {
name: "i",
child: TraceBool {
location: TraceScalarId(0),
name: "i",
flow: Source,
},
ty: Bool,
flow: Source,
},
TraceModuleIO {
name: "o",
child: TraceBool {
location: TraceScalarId(1),
name: "o",
flow: Sink,
},
ty: Bool,
flow: Sink,
},
],
},
traces: [
SimTrace {
id: TraceScalarId(0),
kind: BigBool {
index: StatePartIndex<BigSlots>(0),
},
state: 0x1,
last_state: 0x1,
},
SimTrace {
id: TraceScalarId(1),
kind: BigBool {
index: StatePartIndex<BigSlots>(1),
},
state: 0x1,
last_state: 0x1,
},
],
trace_memories: {},
trace_writers: [
Running(
VcdWriter {
finished_init: true,
timescale: 1 ps,
..
},
),
],
instant: 20 μs,
clocks_triggered: [],
..
}

View file

@ -0,0 +1,51 @@
$timescale 1 ps $end
$scope module extern_module $end
$var wire 1 ! i $end
$var wire 1 " o $end
$upscope $end
$enddefinitions $end
$dumpvars
0!
1"
$end
#500000
#1500000
0"
#2500000
1"
#3500000
0"
#4500000
1"
#5500000
0"
#6500000
1"
#7500000
0"
#8500000
1"
#9500000
0"
#10000000
1!
#10500000
#11500000
1"
#12500000
0"
#13500000
1"
#14500000
0"
#15500000
1"
#16500000
0"
#17500000
1"
#18500000
0"
#19500000
1"
#20000000

View file

@ -712,7 +712,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "memories",
children: [

View file

@ -670,7 +670,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "memories2",
children: [

View file

@ -1754,7 +1754,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "memories3",
children: [

View file

@ -267,7 +267,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "mod1",
children: [

View file

@ -330,7 +330,7 @@ Simulation {
did_initial_settle: true,
},
extern_modules: [],
needs_settle: false,
state_ready_to_run: false,
trace_decls: TraceModule {
name: "shift_register",
children: [