WIP adding simulator
Some checks failed
/ deps (push) Successful in 17s
/ test (push) Failing after 16s

This commit is contained in:
Jacob Lifshay 2024-11-07 01:19:07 -08:00
parent 3d5d8c54b6
commit 56ff69ba52
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
3 changed files with 671 additions and 0 deletions

View file

@ -46,6 +46,7 @@ pub mod module;
pub mod prelude;
pub mod reg;
pub mod reset;
pub mod sim;
pub mod source_location;
pub mod testing;
pub mod ty;

206
crates/fayalite/src/sim.rs Normal file
View file

@ -0,0 +1,206 @@
//! Fayalite Simulation
use crate::{
bundle::{Bundle, BundleType},
enum_::Enum,
expr::Expr,
int::Bool,
intern::{Intern, Interned},
module::{
Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf,
StmtMatch,
},
sim::interpreter::{Insn, Insns, InsnsBuilding},
source_location::SourceLocation,
};
use hashbrown::HashMap;
use std::{cell::RefCell, rc::Rc};
mod interpreter;
#[derive(Debug)]
struct CompilingModule {
compiled_module: Option<CompiledModule>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
enum CondStack {
Always,
IfTrue {
parent: Interned<CondStack>,
cond: Expr<Bool>,
source_location: SourceLocation,
},
IfFalse {
parent: Interned<CondStack>,
cond: Expr<Bool>,
source_location: SourceLocation,
},
MatchArm {
parent: Interned<CondStack>,
enum_expr: Expr<Enum>,
variant_index: usize,
source_location: SourceLocation,
},
}
#[derive(Debug)]
pub struct Compiler {
insns: Insns<InsnsBuilding>,
modules: HashMap<Interned<Module<Bundle>>, Rc<RefCell<CompilingModule>>>,
module_queue: Vec<Interned<Module<Bundle>>>,
}
impl Compiler {
pub fn new() -> Self {
Self {
insns: Insns::new(),
modules: HashMap::new(),
module_queue: Vec::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_block(
&mut self,
module: Interned<Module<Bundle>>,
block: Block,
cond_stack: Interned<CondStack>,
) {
let Block { memories, stmts } = block;
for memory in memories {
todo!("implement memory");
}
for stmt in stmts {
match stmt {
Stmt::Connect(StmtConnect {
lhs,
rhs,
source_location,
}) => todo!(),
Stmt::Formal(StmtFormal {
kind,
clk,
pred,
en,
text,
source_location,
}) => todo!("implement simulating formal statements"),
Stmt::If(StmtIf {
cond,
source_location,
blocks: [then_block, else_block],
}) => {
self.compile_block(
module,
then_block,
CondStack::IfTrue {
parent: cond_stack,
cond,
source_location,
}
.intern_sized(),
);
self.compile_block(
module,
else_block,
CondStack::IfFalse {
parent: cond_stack,
cond,
source_location,
}
.intern_sized(),
);
}
Stmt::Match(StmtMatch {
expr,
source_location,
blocks,
}) => {
for (variant_index, block) in blocks.into_iter().enumerate() {
self.compile_block(
module,
block,
CondStack::MatchArm {
parent: cond_stack,
enum_expr: expr,
variant_index,
source_location,
}
.intern_sized(),
);
}
}
Stmt::Declaration(stmt_declaration) => todo!(),
}
}
}
fn compile_module(&mut self, module: Interned<Module<Bundle>>) -> CompiledModule {
let step_entry_pc = self.insns.next_insn_pc();
match 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());
}
}
self.insns.push(Insn::Return, module.source_location());
CompiledModule { step_entry_pc }
}
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));
}
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(),
}
}
}
#[derive(Debug)]
struct CompiledModule {
step_entry_pc: usize,
}
#[derive(Debug)]
pub struct Compiled {
insns: Interned<Insns>,
modules: HashMap<Interned<Module<Bundle>>, 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()
}
}
pub struct Simulation {
state: interpreter::State,
}

View file

@ -0,0 +1,464 @@
use crate::{
intern::{Intern, Interned},
source_location::SourceLocation,
};
use num_bigint::BigInt;
use std::{convert::Infallible, fmt, hash::Hash};
pub(crate) type SmallUInt = u64;
pub(crate) type SmallSInt = i64;
macro_rules! impl_insns {
(
#[next_macro = $next_macro:ident, branch_macro = $branch_macro:ident]
$vis:vis fn $State:ident::$run:ident(&mut $self:ident, $insns:ident: &[$Insn:ident]) -> $run_ret_ty:ty {
#[pc]
let mut $pc:ident: usize = $pc_init:expr;
setup! {
$($setup:tt)*
}
$(let mut $local:ident = $local_init:expr;)*
main_loop!();
cleanup! {
$($cleanup:tt)*
}
}
$(
$(#[$insn_meta:meta])*
$insn_name:ident $({
$(
$(#[$field_meta:meta])*
$field_name:ident: $field_ty:ty,
)*
})? => $block:block
)+
) => {
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
$vis enum $Insn {
$(
$(#[$insn_meta])*
$insn_name $({
$(
$(#[$field_meta])*
$field_name: $field_ty,
)*
})?,
)+
}
impl $State {
$vis fn $run(&mut $self, $insns: &[$Insn]) -> $run_ret_ty {
let mut $pc: usize = $pc_init;
$($setup)*
let mut insn = $insns[$pc];
let retval = 'main_loop: loop {
macro_rules! $next_macro {
() => {
$pc += 1;
insn = $insns[$pc];
continue 'main_loop;
};
}
macro_rules! $branch_macro {
($next_pc:expr) => {
$pc = $next_pc;
insn = $insns[$pc];
continue 'main_loop;
};
}
let _: Infallible = match insn {
$(
$Insn::$insn_name $({
$(
$field_name,
)*
})? => {
$block
}
)+
};
};
$($cleanup)*
retval
}
}
};
}
pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default {
type BoxedSlice<T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static>: fmt::Debug
+ Clone
+ Eq
+ std::ops::Deref<Target = [T]>;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct InsnsBuildingDone;
impl InsnsBuildingKind for InsnsBuildingDone {
type BoxedSlice<T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static> = Interned<[T]>;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct InsnsBuilding;
impl InsnsBuildingKind for InsnsBuilding {
type BoxedSlice<T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static> = Vec<T>;
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct Insns<BK: InsnsBuildingKind = InsnsBuildingDone> {
insns: BK::BoxedSlice<Insn>,
small_stack_size: usize,
big_stack_size: usize,
small_state_debug_names: BK::BoxedSlice<Interned<str>>,
big_state_debug_names: BK::BoxedSlice<Interned<str>>,
insn_source_locations: BK::BoxedSlice<SourceLocation>,
}
struct InsnsDebug<'a> {
insns: &'a [Insn],
insn_source_locations: &'a [SourceLocation],
}
impl<'a> fmt::Debug for InsnsDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug_list = f.debug_list();
let mut last_source_location = None;
for (insn, source_location) in self.insns.iter().zip(self.insn_source_locations) {
if Some(source_location) != last_source_location {
debug_list.entry(&format_args!("// at: {source_location}\n{insn:?}"));
} else {
debug_list.entry(insn);
}
last_source_location = Some(source_location);
}
debug_list.finish()
}
}
impl<BK: InsnsBuildingKind> fmt::Debug for Insns<BK> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
insns,
small_stack_size,
big_stack_size,
small_state_debug_names,
big_state_debug_names,
insn_source_locations,
} = self;
f.debug_struct("Insns")
.field("small_stack_size", small_stack_size)
.field("big_stack_size", big_stack_size)
.field("small_state_debug_names", small_state_debug_names)
.field("big_state_debug_names", big_state_debug_names)
.field(
"insns",
&InsnsDebug {
insns,
insn_source_locations,
},
)
.finish_non_exhaustive()
}
}
impl Insns<InsnsBuilding> {
pub(crate) fn new() -> Self {
Self {
insns: Vec::new(),
small_stack_size: 0,
big_stack_size: 0,
small_state_debug_names: Vec::new(),
big_state_debug_names: Vec::new(),
insn_source_locations: Vec::new(),
}
}
pub(crate) fn last_insn_pc(&self) -> usize {
self.insns
.len()
.checked_sub(1)
.expect("no last instruction")
}
pub(crate) fn next_insn_pc(&self) -> usize {
self.insns.len()
}
pub(crate) fn push(&mut self, insn: Insn, source_location: SourceLocation) {
self.insns.push(insn);
self.insn_source_locations.push(source_location);
}
}
impl From<Insns<InsnsBuilding>> for Insns {
fn from(input: Insns<InsnsBuilding>) -> Self {
let Insns {
insns,
small_stack_size,
big_stack_size,
small_state_debug_names,
big_state_debug_names,
insn_source_locations,
} = input;
Insns {
insns: Intern::intern_owned(insns),
small_stack_size,
big_stack_size,
small_state_debug_names: Intern::intern_owned(small_state_debug_names),
big_state_debug_names: Intern::intern_owned(big_state_debug_names),
insn_source_locations: Intern::intern_owned(insn_source_locations),
}
}
}
#[derive(Debug)]
pub(crate) struct State {
pub(crate) insns: Interned<Insns>,
pub(crate) pc: usize,
pub(crate) small_stack: Box<[SmallUInt]>,
pub(crate) small_stack_top: usize,
pub(crate) small_states: Box<[SmallUInt]>,
pub(crate) big_stack: Box<[BigInt]>,
pub(crate) big_stack_top: usize,
pub(crate) big_states: Box<[BigInt]>,
}
impl State {
pub(crate) fn setup_call(&mut self, entry_pc: usize) {
let Self {
insns: _,
pc,
small_stack: _,
small_stack_top,
small_states: _,
big_stack: _,
big_stack_top,
big_states: _,
} = self;
*pc = entry_pc;
*small_stack_top = 0;
*big_stack_top = 0;
}
}
impl_insns! {
#[next_macro = next, branch_macro = branch]
pub(crate) fn State::run(&mut self, insns: &[Insn]) -> () {
#[pc]
let mut pc: usize = self.pc;
setup! {
let small_states = &mut *self.small_states;
let small_stack = &mut *self.small_stack;
let mut small_stack_top = self.small_stack_top;
macro_rules! small_stack_push {
($v:expr) => {
{
small_stack[small_stack_top] = $v;
small_stack_top += 1;
}
};
}
macro_rules! small_stack_pop {
() => {
{
small_stack_top -= 1;
small_stack[small_stack_top]
}
};
}
let big_states = &mut *self.big_states;
let big_stack = &mut *self.big_stack;
let mut big_stack_top = self.big_stack_top;
macro_rules! big_stack_push {
($v:expr) => {
{
big_stack[big_stack_top] = $v;
big_stack_top += 1;
}
};
}
macro_rules! big_stack_pop {
() => {
{
big_stack_top -= 1;
big_stack[big_stack_top]
}
};
}
}
main_loop!();
cleanup! {
self.pc = pc;
self.small_stack_top = small_stack_top;
}
}
ReadSmall {
index: u32,
} => {
small_stack_push!(small_states[index as usize]);
next!();
}
SExtSmall {
/// number of bits in a [`SmallSInt`] that aren't used
unused_bit_count: u8,
} => {
let mut value = small_stack_pop!() as SmallSInt;
value <<= unused_bit_count;
value >>= unused_bit_count;
small_stack_push!(value as SmallUInt);
next!();
}
ZExtSmall {
/// number of bits in a [`SmallUInt`] that aren't used
unused_bit_count: u8,
} => {
let mut value = small_stack_pop!();
value <<= unused_bit_count;
value >>= unused_bit_count;
small_stack_push!(value);
next!();
}
AndSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = lhs & rhs;
small_stack_push!(value);
next!();
}
OrSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = lhs | rhs;
small_stack_push!(value);
next!();
}
XorSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = lhs ^ rhs;
small_stack_push!(value);
next!();
}
NotSmall => {
let value = small_stack_pop!();
small_stack_push!(!value);
next!();
}
CmpEqSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = (lhs == rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpNeSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = (lhs != rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpLTSmallUInt => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = (lhs < rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpLESmallUInt => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = (lhs <= rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpGTSmallUInt => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = (lhs > rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpGESmallUInt => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = (lhs >= rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpLTSmallSInt => {
let rhs = small_stack_pop!() as SmallSInt;
let lhs = small_stack_pop!() as SmallSInt;
let value = (lhs < rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpLESmallSInt => {
let rhs = small_stack_pop!() as SmallSInt;
let lhs = small_stack_pop!() as SmallSInt;
let value = (lhs <= rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpGTSmallSInt => {
let rhs = small_stack_pop!() as SmallSInt;
let lhs = small_stack_pop!() as SmallSInt;
let value = (lhs > rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
CmpGESmallSInt => {
let rhs = small_stack_pop!() as SmallSInt;
let lhs = small_stack_pop!() as SmallSInt;
let value = (lhs >= rhs) as SmallUInt;
small_stack_push!(value);
next!();
}
AddSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = lhs.wrapping_add(rhs);
small_stack_push!(value);
next!();
}
SubSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = lhs.wrapping_sub(rhs);
small_stack_push!(value);
next!();
}
NegSmall => {
let value = small_stack_pop!();
let value = value.wrapping_neg();
small_stack_push!(value);
next!();
}
MulSmall => {
let rhs = small_stack_pop!();
let lhs = small_stack_pop!();
let value = lhs.wrapping_mul(rhs);
small_stack_push!(value);
next!();
}
ConstU32Small {
value: u32,
} => {
small_stack_push!(value as SmallUInt);
next!();
}
ConstS32Small {
value: i32,
} => {
small_stack_push!(value as SmallUInt);
next!();
}
WriteSmall {
index: u32,
} => {
small_states[index as usize] = small_stack_pop!();
next!();
}
Return => {
break;
}
}