diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index dd5fc2e..d082187 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -2258,6 +2258,7 @@ impl<'a> Exporter<'a> { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + simulation: _, }) => { let verilog_name = Ident(verilog_name); writeln!(body, "{indent}defname = {verilog_name}").unwrap(); diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index d26dc7b..87f86cc 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -21,6 +21,7 @@ use crate::{ memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, reg::Reg, reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset}, + sim::{ExternModuleSimGenerator, ExternModuleSimulation}, source_location::SourceLocation, ty::{CanonicalType, Type}, util::ScopedRef, @@ -1081,6 +1082,7 @@ pub struct ExternModuleBody< > { pub verilog_name: Interned, pub parameters: P, + pub simulation: Option>, } impl From>> for ExternModuleBody { @@ -1088,11 +1090,13 @@ impl From>> for ExternModuleBody { let ExternModuleBody { verilog_name, parameters, + simulation, } = value; let parameters = Intern::intern_owned(parameters); Self { verilog_name, parameters, + simulation, } } } @@ -1283,10 +1287,12 @@ impl fmt::Debug for DebugModuleBody { ModuleBody::Extern(ExternModuleBody { verilog_name, parameters, + simulation, }) => { debug_struct .field("verilog_name", verilog_name) - .field("parameters", parameters); + .field("parameters", parameters) + .field("simulation", simulation); } } debug_struct.finish_non_exhaustive() @@ -1761,7 +1767,12 @@ impl AssertValidityState { ModuleBody::Extern(ExternModuleBody { verilog_name: _, parameters: _, - }) => {} + simulation, + }) => { + if let Some(simulation) = simulation { + simulation.check_io_ty(self.module.io_ty); + } + } ModuleBody::Normal(NormalModuleBody { body }) => { let body = self.make_block_index(body); assert_eq!(body, 0); @@ -2108,6 +2119,7 @@ impl ModuleBuilder { ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { verilog_name: name.0, parameters: vec![], + simulation: None, }), ModuleKind::Normal => ModuleBody::Normal(NormalModuleBody { body: BuilderModuleBody { @@ -2229,6 +2241,15 @@ impl ModuleBuilder { value: ExternModuleParameterValue::RawVerilog(raw_verilog.intern()), }); } + #[track_caller] + pub fn extern_module_simulation(&self, generator: G) { + let mut impl_ = self.impl_.borrow_mut(); + let simulation = &mut impl_.body.builder_extern_body().simulation; + if simulation.is_some() { + panic!("already added an extern module simulation"); + } + *simulation = Some(ExternModuleSimulation::new(generator)); + } } #[track_caller] diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 662a578..526a62c 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -31,6 +31,7 @@ use crate::{ phantom_const::PhantomConst, reg::Reg, reset::{AsyncReset, Reset, ResetType, SyncReset}, + sim::ExternModuleSimulation, source_location::SourceLocation, ty::{CanonicalType, Type}, wire::Wire, diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 96f6dd9..29a8b67 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -15,7 +15,9 @@ use crate::{ ExprEnum, Flow, ToLiteralBits, }, int::{BoolOrIntType, IntType, SIntValue, UIntValue}, - intern::{Intern, Interned, Memoize}, + intern::{ + Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId, + }, memory::PortKind, module::{ transform::deduce_resets::deduce_resets, AnnotatedModuleIO, Block, Id, InstantiatedModule, @@ -51,7 +53,15 @@ use petgraph::{ }, }; use std::{ - borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut, sync::Arc, + any::Any, + borrow::Cow, + collections::BTreeSet, + fmt, + future::{Future, IntoFuture}, + marker::PhantomData, + mem, + ops::IndexMut, + sync::Arc, }; mod interpreter; @@ -7386,3 +7396,108 @@ impl Simulation { }); } } + +#[derive(Debug)] +pub struct ExternModuleSimulationState { + io_ty: T, +} + +impl ExternModuleSimulationState { + pub fn canonical(self) -> ExternModuleSimulationState { + ExternModuleSimulationState { + io_ty: Bundle::from_canonical(self.io_ty.canonical()), + } + } + pub fn from_canonical(sim: ExternModuleSimulationState) -> Self { + Self { + io_ty: T::from_canonical(sim.io_ty.canonical()), + } + } +} + +pub trait ExternModuleSimGenerator: + Clone + Eq + std::hash::Hash + Any + Send + Sync + fmt::Debug +{ + type IOType: BundleType; + + fn run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> impl IntoFuture + 'a; +} + +trait DynExternModuleSimGenerator: Any + Send + Sync + SupportsPtrEqWithTypeId + fmt::Debug { + fn dyn_run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> Box + 'a>; + #[track_caller] + fn check_io_ty(&self, io_ty: Bundle); +} + +impl DynExternModuleSimGenerator for T { + fn dyn_run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> Box + 'a> { + Box::new( + self.run(ExternModuleSimulationState::from_canonical(sim)) + .into_future(), + ) + } + #[track_caller] + fn check_io_ty(&self, io_ty: Bundle) { + T::IOType::from_canonical(io_ty.canonical()); + } +} + +impl InternedCompare for dyn DynExternModuleSimGenerator { + type InternedCompareKey = PtrEqWithTypeId; + + fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { + this.get_ptr_eq_with_type_id() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct ExternModuleSimulation { + generator: Interned, + _phantom: PhantomData, +} + +impl ExternModuleSimulation { + pub fn new(generator: G) -> Self { + Self { + generator: Interned::cast_unchecked( + generator.intern(), + |v| -> &dyn DynExternModuleSimGenerator { v }, + ), + _phantom: PhantomData, + } + } + pub fn canonical(self) -> ExternModuleSimulation { + ExternModuleSimulation { + generator: self.generator, + _phantom: PhantomData, + } + } + pub fn from_canonical(v: ExternModuleSimulation) -> Self { + Self { + generator: v.generator, + _phantom: PhantomData, + } + } +} + +impl ExternModuleSimulation { + fn run<'a>( + &'a self, + sim: ExternModuleSimulationState, + ) -> Box + 'a> { + self.generator.dyn_run(sim) + } + #[track_caller] + pub fn check_io_ty(self, io_ty: Bundle) { + self.generator.check_io_ty(io_ty); + } +} diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index b284372..451dc90 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -160,7 +160,8 @@ "data": { "$kind": "Struct", "verilog_name": "Visible", - "parameters": "Visible" + "parameters": "Visible", + "simulation": "Visible" } }, "ExternModuleParameter": { @@ -1269,6 +1270,12 @@ "$kind": "Opaque" }, "generics": "" + }, + "ExternModuleSimulation": { + "data": { + "$kind": "Opaque" + }, + "generics": "" } } } \ No newline at end of file