WIP implementing simulator

This commit is contained in:
Jacob Lifshay 2024-11-07 21:46:34 -08:00
parent 6f904148c4
commit 479d59b287
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
3 changed files with 1104 additions and 248 deletions

View file

@ -313,7 +313,7 @@ impl TargetChild {
} }
} }
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum Target { pub enum Target {
Base(Interned<TargetBase>), Base(Interned<TargetBase>),
Child(TargetChild), Child(TargetChild),

View file

@ -1,28 +1,32 @@
//! Fayalite Simulation //! Fayalite Simulation
use crate::{ use crate::{
bundle::{Bundle, BundleType}, bundle::{Bundle, BundleField, BundleType},
enum_::Enum, enum_::Enum,
expr::Expr, expr::{
int::Bool, target::{
intern::{Intern, Interned}, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
module::{ },
Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf, Expr,
StmtMatch, },
int::Bool,
intern::{Intern, Interned, Memoize},
module::{
AnnotatedModuleIO, Block, Instance, Module, ModuleBody, NormalModuleBody, Stmt,
StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch,
},
sim::interpreter::{
Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, StatePartLayout, TypeIndex,
TypeIndexRange, TypeLayout,
}, },
sim::interpreter::{Insn, Insns, InsnsBuilding},
source_location::SourceLocation, source_location::SourceLocation,
ty::CanonicalType,
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{cell::RefCell, rc::Rc}; use std::fmt;
mod interpreter; mod interpreter;
#[derive(Debug)]
struct CompilingModule {
compiled_module: Option<CompiledModule>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
enum CondStack { enum CondStack {
Always, Always,
@ -44,32 +48,279 @@ enum CondStack {
}, },
} }
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
enum InstantiatedModule {
Base(Interned<Module<Bundle>>),
Child {
parent: Interned<InstantiatedModule>,
instance: Interned<Instance<Bundle>>,
},
}
impl InstantiatedModule {
fn leaf_module(self) -> Interned<Module<Bundle>> {
match self {
InstantiatedModule::Base(base) => base,
InstantiatedModule::Child { instance, .. } => instance.instantiated(),
}
}
fn write_path(self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InstantiatedModule::Base(base) => fmt::Debug::fmt(&base.name_id(), f),
InstantiatedModule::Child { parent, instance } => {
parent.write_path(f)?;
write!(f, ".{}", instance.name_id())
}
}
}
}
impl fmt::Debug for InstantiatedModule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "InstantiatedModule(")?;
self.write_path(f)?;
write!(f, ": {})", self.leaf_module().name_id())
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
struct TargetInInstantiatedModule {
instantiated_module: InstantiatedModule,
target: Target,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
struct CompiledBundleField {
offset: TypeIndex,
ty: CompiledTypeLayout,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
enum CompiledTypeLayoutBody {
Scalar,
Array {
/// debug names are ignored, use parent's layout instead
element: Interned<CompiledTypeLayout>,
},
Bundle {
/// debug names are ignored, use parent's layout instead
fields: Interned<[CompiledBundleField]>,
},
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
struct CompiledTypeLayout {
ty: CanonicalType,
layout: TypeLayout<InsnsBuildingDone>,
body: CompiledTypeLayoutBody,
}
impl CompiledTypeLayout {
fn with_prefixed_debug_names(self, prefix: &str) -> Self {
let Self { ty, layout, body } = self;
Self {
ty,
layout: layout.with_prefixed_debug_names(prefix),
body,
}
}
fn get(ty: CanonicalType) -> Self {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
struct MyMemoize;
impl Memoize for MyMemoize {
type Input = CanonicalType;
type InputOwned = CanonicalType;
type Output = CompiledTypeLayout;
fn inner(self, input: &Self::Input) -> Self::Output {
match input {
CanonicalType::UInt(_)
| CanonicalType::SInt(_)
| CanonicalType::Bool(_)
| CanonicalType::Enum(_)
| CanonicalType::AsyncReset(_)
| CanonicalType::SyncReset(_)
| CanonicalType::Reset(_)
| CanonicalType::Clock(_) => {
let mut layout = TypeLayout::empty();
let debug_data = SlotDebugData { name: "".intern() };
if input.bit_width() > interpreter::SmallUInt::BITS as usize {
layout.big_slots = StatePartLayout::scalar(debug_data);
} else {
layout.small_slots = StatePartLayout::scalar(debug_data);
};
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Scalar,
}
}
CanonicalType::Array(array) => {
let mut layout = TypeLayout::empty();
let element = CompiledTypeLayout::get(array.element()).intern_sized();
for index in 0..array.len() {
layout.allocate(
&element
.layout
.with_prefixed_debug_names(&format!("[{index}]")),
);
}
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Array { element },
}
}
CanonicalType::Bundle(bundle) => {
let mut layout = TypeLayout::empty();
let fields = bundle
.fields()
.iter()
.map(
|BundleField {
name,
flipped: _,
ty,
}| {
let ty = CompiledTypeLayout::get(*ty);
let offset = layout
.allocate(
&ty.layout
.with_prefixed_debug_names(&format!(".{name}")),
)
.start();
CompiledBundleField { offset, ty }
},
)
.collect();
CompiledTypeLayout {
ty: *input,
layout: layout.into(),
body: CompiledTypeLayoutBody::Bundle { fields },
}
}
}
}
}
MyMemoize.get_owned(ty)
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
struct CompiledValue {
layout: CompiledTypeLayout,
range: TypeIndexRange,
write: Option<(CompiledTypeLayout, TypeIndexRange)>,
}
impl CompiledValue {
fn map(
self,
mut f: impl FnMut(CompiledTypeLayout, TypeIndexRange) -> (CompiledTypeLayout, TypeIndexRange),
) -> Self {
let (layout, range) = f(self.layout, self.range);
Self {
layout,
range,
write: self.write.map(|(layout, range)| f(layout, range)),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Compiler { pub struct Compiler {
insns: Insns<InsnsBuilding>, insns: Insns<InsnsBuilding>,
modules: HashMap<Interned<Module<Bundle>>, Rc<RefCell<CompilingModule>>>, base_module: Interned<Module<Bundle>>,
module_queue: Vec<Interned<Module<Bundle>>>, modules: HashMap<InstantiatedModule, CompiledModule>,
compiled_values: HashMap<TargetInInstantiatedModule, CompiledValue>,
} }
impl Compiler { impl Compiler {
pub fn new() -> Self { pub fn new(base_module: Interned<Module<Bundle>>) -> Self {
Self { Self {
insns: Insns::new(), insns: Insns::new(),
base_module,
modules: HashMap::new(), modules: HashMap::new(),
module_queue: Vec::new(), compiled_values: HashMap::new(),
} }
} }
pub fn add_module(&mut self, module: Interned<Module<Bundle>>) { fn compile_value(&mut self, target: TargetInInstantiatedModule) -> CompiledValue {
self.modules.entry(module).or_insert_with(|| { if let Some(&retval) = self.compiled_values.get(&target) {
self.module_queue.push(module); return retval;
Rc::new(RefCell::new(CompilingModule { }
compiled_module: None, match target.target {
})) Target::Base(base) => {
}); let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty());
let layout = unprefixed_layout.with_prefixed_debug_names(&format!(
"{:?}.{:?}",
target.instantiated_module,
base.target_name()
));
let range = self.insns.allocate_variable(&layout.layout);
let write = match *base {
TargetBase::ModuleIO(_)
| TargetBase::MemPort(_)
| TargetBase::Wire(_)
| TargetBase::Instance(_) => None,
TargetBase::Reg(_) => {
let write_layout = unprefixed_layout.with_prefixed_debug_names(&format!(
"{:?}.{:?}$next",
target.instantiated_module,
base.target_name()
));
Some((
write_layout,
self.insns.allocate_variable(&write_layout.layout),
))
}
};
CompiledValue {
range,
layout,
write,
}
}
Target::Child(target_child) => {
let parent = self.compile_value(TargetInInstantiatedModule {
instantiated_module: target.instantiated_module,
target: *target_child.parent(),
});
match *target_child.path_element() {
TargetPathElement::BundleField(TargetPathBundleField { name }) => {
parent.map(|layout, range| {
let CompiledTypeLayout {
ty: CanonicalType::Bundle(bundle),
layout: _,
body: CompiledTypeLayoutBody::Bundle { fields },
} = layout
else {
unreachable!();
};
let field_index = bundle.name_indexes()[&name];
(
fields[field_index].ty,
range.slice(TypeIndexRange::new(
fields[field_index].offset,
fields[field_index].ty.layout.len(),
)),
)
})
}
TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => parent
.map(|layout, range| {
let CompiledTypeLayoutBody::Array { element } = layout.body else {
unreachable!();
};
(*element, range.index_array(element.layout.len(), index))
}),
TargetPathElement::DynArrayElement(_) => unreachable!(),
}
}
}
} }
fn compile_block( fn compile_block(
&mut self, &mut self,
module: Interned<Module<Bundle>>, parent_module: Interned<InstantiatedModule>,
block: Block, block: Block,
cond_stack: Interned<CondStack>, cond_stack: Interned<CondStack>,
) { ) {
@ -98,7 +349,7 @@ impl Compiler {
blocks: [then_block, else_block], blocks: [then_block, else_block],
}) => { }) => {
self.compile_block( self.compile_block(
module, parent_module,
then_block, then_block,
CondStack::IfTrue { CondStack::IfTrue {
parent: cond_stack, parent: cond_stack,
@ -108,7 +359,7 @@ impl Compiler {
.intern_sized(), .intern_sized(),
); );
self.compile_block( self.compile_block(
module, parent_module,
else_block, else_block,
CondStack::IfFalse { CondStack::IfFalse {
parent: cond_stack, parent: cond_stack,
@ -125,7 +376,7 @@ impl Compiler {
}) => { }) => {
for (variant_index, block) in blocks.into_iter().enumerate() { for (variant_index, block) in blocks.into_iter().enumerate() {
self.compile_block( self.compile_block(
module, parent_module,
block, block,
CondStack::MatchArm { CondStack::MatchArm {
parent: cond_stack, parent: cond_stack,
@ -137,67 +388,79 @@ impl Compiler {
); );
} }
} }
Stmt::Declaration(stmt_declaration) => todo!(), Stmt::Declaration(declaration) => match declaration {
StmtDeclaration::Wire(wire) => todo!(),
StmtDeclaration::Reg(reg) => todo!(),
StmtDeclaration::Instance(StmtInstance {
annotations,
instance,
}) => {
self.compile_module(
InstantiatedModule::Child {
parent: parent_module,
instance: instance.intern_sized(),
}
.intern_sized(),
);
todo!()
}
},
} }
} }
} }
fn compile_module(&mut self, module: Interned<Module<Bundle>>) -> CompiledModule { fn compile_module(&mut self, module: Interned<InstantiatedModule>) -> &CompiledModule {
let step_entry_pc = self.insns.next_insn_pc(); let module_io = module
match module.body() { .leaf_module()
.module_io()
.iter()
.map(
|&AnnotatedModuleIO {
annotations: _,
module_io,
}| {
self.compile_value(TargetInInstantiatedModule {
instantiated_module: *module,
target: Target::from(module_io),
})
},
)
.collect();
match module.leaf_module().body() {
ModuleBody::Normal(NormalModuleBody { body }) => { ModuleBody::Normal(NormalModuleBody { body }) => {
self.compile_block(module, body, CondStack::Always.intern_sized()) self.compile_block(module, body, CondStack::Always.intern_sized())
} }
ModuleBody::Extern(_extern_module_body) => { ModuleBody::Extern(_extern_module_body) => {
todo!("simulating extern module: {}", module.name_id()); todo!("simulating extern module: {:?}", module);
} }
} }
self.insns.push(Insn::Return, module.source_location()); let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else {
CompiledModule { step_entry_pc } unreachable!("compiled same instantiated module twice");
};
entry.insert(CompiledModule { module_io })
} }
pub fn run(mut self) -> Compiled { pub fn run(mut self) -> Compiled {
while let Some(module) = self.module_queue.pop() { self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized());
let compiling_module = self.modules[&module].clone();
let mut compiling_module = compiling_module.borrow_mut();
let CompilingModule { compiled_module } = &mut *compiling_module;
assert!(compiled_module.is_none());
*compiled_module = Some(self.compile_module(module));
}
Compiled { Compiled {
insns: Insns::from(self.insns).intern_sized(), insns: Insns::from(self.insns).intern_sized(),
modules: self modules: self.modules,
.modules
.into_iter()
.map(|(module, compiling_module)| {
(
module,
Rc::into_inner(compiling_module)
.expect("only reference is Compiler::modules")
.into_inner()
.compiled_module
.expect("module is compiled by Compiler::run"),
)
})
.collect(),
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
struct CompiledModule { struct CompiledModule {
step_entry_pc: usize, module_io: Interned<[CompiledValue]>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Compiled { pub struct Compiled {
insns: Interned<Insns>, insns: Interned<Insns<InsnsBuildingDone>>,
modules: HashMap<Interned<Module<Bundle>>, CompiledModule>, modules: HashMap<InstantiatedModule, CompiledModule>,
} }
impl Compiled { impl Compiled {
pub fn new<T: BundleType>(module: Module<T>) -> Self { pub fn new<T: BundleType>(module: Module<T>) -> Self {
let mut compiler = Compiler::new(); Compiler::new(module.canonical().intern()).run()
compiler.add_module(module.canonical().intern());
compiler.run()
} }
} }

File diff suppressed because it is too large Load diff