WIP implementing simulator
This commit is contained in:
parent
56ff69ba52
commit
32253bc3f4
|
@ -313,7 +313,7 @@ impl TargetChild {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Target {
|
||||
Base(Interned<TargetBase>),
|
||||
Child(TargetChild),
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
//! Fayalite Simulation
|
||||
|
||||
use crate::{
|
||||
bundle::{Bundle, BundleType},
|
||||
bundle::{Bundle, BundleField, BundleType},
|
||||
enum_::Enum,
|
||||
expr::Expr,
|
||||
int::Bool,
|
||||
intern::{Intern, Interned},
|
||||
module::{
|
||||
Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf,
|
||||
StmtMatch,
|
||||
expr::{
|
||||
target::{
|
||||
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
|
||||
},
|
||||
Expr,
|
||||
},
|
||||
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,
|
||||
ty::CanonicalType,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::fmt;
|
||||
|
||||
mod interpreter;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CompilingModule {
|
||||
compiled_module: Option<CompiledModule>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
enum CondStack {
|
||||
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)]
|
||||
pub struct Compiler {
|
||||
insns: Insns<InsnsBuilding>,
|
||||
modules: HashMap<Interned<Module<Bundle>>, Rc<RefCell<CompilingModule>>>,
|
||||
module_queue: Vec<Interned<Module<Bundle>>>,
|
||||
base_module: Interned<Module<Bundle>>,
|
||||
modules: HashMap<InstantiatedModule, CompiledModule>,
|
||||
compiled_values: HashMap<TargetInInstantiatedModule, CompiledValue>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(base_module: Interned<Module<Bundle>>) -> Self {
|
||||
Self {
|
||||
insns: Insns::new(),
|
||||
base_module,
|
||||
modules: HashMap::new(),
|
||||
module_queue: Vec::new(),
|
||||
compiled_values: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn add_module(&mut self, module: Interned<Module<Bundle>>) {
|
||||
self.modules.entry(module).or_insert_with(|| {
|
||||
self.module_queue.push(module);
|
||||
Rc::new(RefCell::new(CompilingModule {
|
||||
compiled_module: None,
|
||||
}))
|
||||
});
|
||||
fn compile_value(&mut self, target: TargetInInstantiatedModule) -> CompiledValue {
|
||||
if let Some(&retval) = self.compiled_values.get(&target) {
|
||||
return retval;
|
||||
}
|
||||
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(
|
||||
&mut self,
|
||||
module: Interned<Module<Bundle>>,
|
||||
parent_module: Interned<InstantiatedModule>,
|
||||
block: Block,
|
||||
cond_stack: Interned<CondStack>,
|
||||
) {
|
||||
|
@ -98,7 +349,7 @@ impl Compiler {
|
|||
blocks: [then_block, else_block],
|
||||
}) => {
|
||||
self.compile_block(
|
||||
module,
|
||||
parent_module,
|
||||
then_block,
|
||||
CondStack::IfTrue {
|
||||
parent: cond_stack,
|
||||
|
@ -108,7 +359,7 @@ impl Compiler {
|
|||
.intern_sized(),
|
||||
);
|
||||
self.compile_block(
|
||||
module,
|
||||
parent_module,
|
||||
else_block,
|
||||
CondStack::IfFalse {
|
||||
parent: cond_stack,
|
||||
|
@ -125,7 +376,7 @@ impl Compiler {
|
|||
}) => {
|
||||
for (variant_index, block) in blocks.into_iter().enumerate() {
|
||||
self.compile_block(
|
||||
module,
|
||||
parent_module,
|
||||
block,
|
||||
CondStack::MatchArm {
|
||||
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 {
|
||||
let step_entry_pc = self.insns.next_insn_pc();
|
||||
match module.body() {
|
||||
fn compile_module(&mut self, module: Interned<InstantiatedModule>) -> &CompiledModule {
|
||||
let module_io = module
|
||||
.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 }) => {
|
||||
self.compile_block(module, body, CondStack::Always.intern_sized())
|
||||
}
|
||||
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());
|
||||
CompiledModule { step_entry_pc }
|
||||
let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else {
|
||||
unreachable!("compiled same instantiated module twice");
|
||||
};
|
||||
entry.insert(CompiledModule { module_io })
|
||||
}
|
||||
pub fn run(mut self) -> Compiled {
|
||||
while let Some(module) = self.module_queue.pop() {
|
||||
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));
|
||||
}
|
||||
self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized());
|
||||
Compiled {
|
||||
insns: Insns::from(self.insns).intern_sized(),
|
||||
modules: self
|
||||
.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(),
|
||||
modules: self.modules,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CompiledModule {
|
||||
step_entry_pc: usize,
|
||||
module_io: Interned<[CompiledValue]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Compiled {
|
||||
insns: Interned<Insns>,
|
||||
modules: HashMap<Interned<Module<Bundle>>, CompiledModule>,
|
||||
insns: Interned<Insns<InsnsBuildingDone>>,
|
||||
modules: HashMap<InstantiatedModule, CompiledModule>,
|
||||
}
|
||||
|
||||
impl Compiled {
|
||||
pub fn new<T: BundleType>(module: Module<T>) -> Self {
|
||||
let mut compiler = Compiler::new();
|
||||
compiler.add_module(module.canonical().intern());
|
||||
compiler.run()
|
||||
Compiler::new(module.canonical().intern()).run()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue