WIP implementing simulator
Some checks failed
/ test (push) Blocked by required conditions
/ deps (push) Has been cancelled

This commit is contained in:
Jacob Lifshay 2024-11-07 21:46:34 -08:00
parent 56ff69ba52
commit 32253bc3f4
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
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 {
Base(Interned<TargetBase>),
Child(TargetChild),

View file

@ -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