WIP implementing simulator
This commit is contained in:
parent
6f904148c4
commit
479d59b287
|
@ -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),
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue