From 0f3fc813f915a7ff9ad22115074cc83cc7aa08b8 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 30 Jul 2024 19:26:43 -0400 Subject: [PATCH] Continue work on testbench; cider2 stubs in place --- Cargo.lock | 11 ++-- tools/calyx-ffi-macro/src/lib.rs | 44 +++++++++----- tools/calyx-ffi/Cargo.toml | 3 + tools/calyx-ffi/src/backend.rs | 34 +---------- tools/calyx-ffi/src/backend/cider.rs | 80 +++++++++++++++++++++++++ tools/calyx-ffi/src/backend/useless.rs | 35 +++++++++++ tools/calyx-ffi/src/lib.rs | 81 ++++++++++++++------------ tools/calyx-ffi/src/prelude.rs | 4 +- tools/calyx-ffi/tests/test.rs | 3 + tools/tb/examples/calyx/test.rs | 4 +- 10 files changed, 206 insertions(+), 93 deletions(-) create mode 100644 tools/calyx-ffi/src/backend/cider.rs create mode 100644 tools/calyx-ffi/src/backend/useless.rs diff --git a/Cargo.lock b/Cargo.lock index 019194c694..6888d4fbf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,6 +409,9 @@ name = "calyx-ffi" version = "0.7.1" dependencies = [ "calyx-ffi-macro", + "calyx-frontend", + "calyx-ir", + "interp", ] [[package]] @@ -2990,9 +2993,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -3011,9 +3014,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index 2efac22f9b..c0ee70c702 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -12,6 +12,7 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let args = parse_macro_input!(attrs as CalyxFFIMacroArgs); let item_struct = parse_macro_input!(item as syn::ItemStruct); let name = item_struct.ident; + let path = args.src.to_string_lossy().to_string(); // let comp = calyx::parse_calyx_file(&args); @@ -22,11 +23,11 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let comp = comp.get(); // - let comp_name = syn::parse_str::(&format!( - "\"{}\"", - comp.name.to_string() - )) - .expect("failed to turn quoted name into string"); + let comp_name = + syn::parse_str::(&format!("\"{}\"", comp.name)) + .expect("failed to turn quoted name into string"); + let comp_path = syn::parse_str::(&format!("\"{}\"", path)) + .expect("failed to turn quoted path into string"); let backend_macro = args.backend; let mut field_names = vec![]; @@ -65,7 +66,8 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let struct_def = quote! { struct #name { - #(#fields),* + #(#fields,)* + user_data: std::mem::MaybeUninit<#backend_macro!(@user_data_type)> } }; @@ -74,35 +76,47 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { #(#getters)* } + impl std::default::Default for #name { + fn default() -> Self { + Self { + #(#field_names: std::default::Default::default(),)* + user_data: unsafe { std::mem::MaybeUninit::zeroed() } + } + } + } + impl CalyxFFIComponent for #name { + fn path(&self) -> &'static str { + #comp_path + } + fn name(&self) -> &'static str { #comp_name } - fn init(&mut self) { - #backend_macro!(init self; #(#field_names),*); + fn init(&mut self, context: &calyx_ir::Context) { + #backend_macro!(@init self, context; #(#field_names),*); } - fn deinit(&mut self) { - #backend_macro!(deinit self; #(#field_names),*); + fn reset(&mut self) { + #backend_macro!(@reset self; #(#field_names),*); } - fn reset(&mut self) { - #backend_macro!(reset self; #(#field_names),*); + fn can_tick(&self) -> bool { + #backend_macro!(@can_tick self; #(#field_names),*) } fn tick(&mut self) { - #backend_macro!(tick self; #(#field_names),*); + #backend_macro!(@tick self; #(#field_names),*); } fn go(&mut self) { - #backend_macro!(go self; #(#field_names),*); + #backend_macro!(@go self; #(#field_names),*); } } }; quote! { - #[derive(Default)] #struct_def #impl_block } diff --git a/tools/calyx-ffi/Cargo.toml b/tools/calyx-ffi/Cargo.toml index 12ed31c33f..862e34cbce 100644 --- a/tools/calyx-ffi/Cargo.toml +++ b/tools/calyx-ffi/Cargo.toml @@ -14,3 +14,6 @@ rust-version.workspace = true [dependencies] calyx-ffi-macro = { path = "../calyx-ffi-macro" } +calyx-frontend.workspace = true +calyx-ir.workspace = true +interp = { path = "../../interp" } diff --git a/tools/calyx-ffi/src/backend.rs b/tools/calyx-ffi/src/backend.rs index 414a632881..759f64d89b 100644 --- a/tools/calyx-ffi/src/backend.rs +++ b/tools/calyx-ffi/src/backend.rs @@ -1,32 +1,2 @@ -/// Example FFI backend. -#[macro_export] -macro_rules! useless_ffi_backend { - (init $dut:ident; $($port:ident),*) => { - println!("useless_ffi_backend init"); - }; - (deinit $dut:ident; $($port:ident),*) => { - println!("useless_ffi_backend deinit"); - }; - (reset $dut:ident; $($port:ident),*) => { - println!("useless_ffi_backend reset"); - $dut.done = 0; - $dut.reset = 1; - for i in 0..5 { - $dut.tick(); - } - $dut.reset = 0; - }; - (tick $dut:ident; $($port:ident),*) => { - println!("useless_ffi_backend tick"); - if $dut.done == 1 { - $dut.done = 0; - } - }; - (go $dut:ident; $($port:ident),*) => { - println!("useless_ffi_backend go"); - $dut.go = 1; - $dut.go = 0; - $dut.done = 1; - $dut.tick(); - }; -} +pub mod cider; +pub mod useless; diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs new file mode 100644 index 0000000000..f67fecab4e --- /dev/null +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -0,0 +1,80 @@ +use calyx_ir::Context; +use interp::flatten::structures::{ + context::Context as CiderContext, + environment::{Environment, Simulator}, +}; +use std::{mem::MaybeUninit, rc::Rc}; + +pub struct CiderFFIBackend { + simulator: Simulator>, +} + +impl CiderFFIBackend { + pub fn from(context: &Context, name: &'static str) -> Self { + let cider_context = CiderContext::new(); + let environment = Environment::new(Rc::new(cider_context), None); + let simulator = Simulator::new(environment); + Self { simulator } + } + + pub fn write_port(&mut self, name: &'static str, value: u64) { + todo!("no way to set port on a component yet I think") + // self.simulator. + } + + pub fn read_port(&self, name: &'static str) -> u64 { + todo!("no way to get port on a component yet I think") + } + + pub fn step(&mut self) { + self.simulator.step().expect( + "this function isn't documented so don't know what went wrong", + ); + } +} + +/// Runs the component using cider2. +#[macro_export] +macro_rules! cider_ffi_backend { + (@user_data_type) => { + $crate::backend::cider::CiderFFIBackend + }; + (@init $dut:ident, $ctx:expr; $($port:ident),*) => { + $dut.user_data + .write($crate::backend::cider::CiderFFIBackend::from( + $ctx, + $dut.name(), + )); + }; + (@reset $dut:ident; $($port:ident),*) => { + println!("cider_ffi_backend reset"); + $dut.done = 0; + $dut.reset = 1; + for i in 0..5 { + $dut.tick(); + } + $dut.reset = 0; + }; + (@can_tick $dut:ident; $($port:ident),*) => { + true + }; + (@tick $dut:ident; $($port:ident),*) => { + println!("cider_ffi_backend tick"); + let cider = unsafe { $dut.user_data.assume_init_mut() }; + $( + cider.write_port(stringify!($port), $dut.$port); + )* + cider.step(); + $( + $dut.$port = cider.read_port(stringify!($port)); + )* + }; + (@go $dut:ident; $($port:ident),*) => { + println!("cider_ffi_backend go"); + $dut.go = 1; + while ($dut.done != 1) { + $dut.tick(); + } + $dut.go = 0; + }; +} diff --git a/tools/calyx-ffi/src/backend/useless.rs b/tools/calyx-ffi/src/backend/useless.rs new file mode 100644 index 0000000000..b6b472b5bd --- /dev/null +++ b/tools/calyx-ffi/src/backend/useless.rs @@ -0,0 +1,35 @@ +/// Example FFI backend. +#[macro_export] +macro_rules! useless_ffi_backend { + (@user_data_type) => { + () // unit type + }; + (@init $dut:ident, $ctx:expr; $($port:ident),*) => { + println!("useless_ffi_backend init"); + }; + (@reset $dut:ident; $($port:ident),*) => { + println!("useless_ffi_backend reset"); + $dut.done = 0; + $dut.reset = 1; + for i in 0..5 { + $dut.tick(); + } + $dut.reset = 0; + }; + (@can_tick $dut:ident; $($port:ident),*) => { + true + }; + (@tick $dut:ident; $($port:ident),*) => { + println!("useless_ffi_backend tick"); + if $dut.done == 1 { + $dut.done = 0; + } + }; + (@go $dut:ident; $($port:ident),*) => { + println!("useless_ffi_backend go"); + $dut.go = 1; + $dut.go = 0; + $dut.done = 1; + $dut.tick(); + }; +} diff --git a/tools/calyx-ffi/src/lib.rs b/tools/calyx-ffi/src/lib.rs index 7c38bbf81e..ccecc28b48 100644 --- a/tools/calyx-ffi/src/lib.rs +++ b/tools/calyx-ffi/src/lib.rs @@ -1,35 +1,46 @@ -use std::{any, cell::RefCell, collections::HashMap, rc::Rc}; +use calyx_ir::Context; +use std::{ + any, cell::RefCell, collections::HashMap, env, path::PathBuf, rc::Rc, +}; pub mod backend; pub mod prelude; /// A non-combinational calyx component. pub trait CalyxFFIComponent: any::Any { - /// The in-source name of this component. + /// The path to the component source file. Must be a constant expression. + fn path(&self) -> &'static str; + + /// The in-source name of this component. Must be a constant expression. fn name(&self) -> &'static str; /// Internal initialization routine. Do not call! - fn init(&mut self); - - /// Internal deinitialization routine. Do not call! - fn deinit(&mut self); + fn init(&mut self, context: &Context); - // Resets this component. + /// Resets this component. fn reset(&mut self); - // Advances this component by one clock cycle. May not always be available. + /// Whether this component's backend supports ticking. + fn can_tick(&self) -> bool; + + /// Advances this component by one clock cycle. May not always be available, so check [`has_tick`]([CalyxFFIComponent::has_tick]). fn tick(&mut self); - /// Calls this component. + /// Calls this component, blocking until it is done executing. fn go(&mut self); } pub type CalyxFFIComponentRef = Rc>; +fn box_calyx_ffi_component( + comp: T, +) -> CalyxFFIComponentRef { + Rc::new(RefCell::new(comp)) +} + #[derive(Default)] pub struct CalyxFFI { - reuse: HashMap<&'static str, usize>, - comps: Vec, + contexts: HashMap<&'static str, Context>, } impl CalyxFFI { @@ -37,35 +48,29 @@ impl CalyxFFI { Self::default() } - /// Any component `T`. - pub fn comp( - &mut self, - ) -> CalyxFFIComponentRef { - let name = T::default().name(); - if let Some(index) = self.reuse.get(name) { - self.comps[*index].clone() - } else { - self.new_comp::() - } - } - - /// A new component `T`. + /// Constructs a new calyx component of the given type. + /// + /// The `path` implementation for `CalyxFFIComponent` must be a constant + /// expression and should derived via the `calyx_ffi` procedural macro. pub fn new_comp( &mut self, ) -> CalyxFFIComponentRef { - let comp = Rc::new(RefCell::new(T::default())); - comp.borrow_mut().init(); - self.comps.push(comp.clone()); - self.reuse - .insert(comp.borrow().name(), self.comps.len() - 1); - comp - } -} - -impl Drop for CalyxFFI { - fn drop(&mut self) { - for comp in &self.comps { - comp.borrow_mut().deinit(); - } + let mut comp = unsafe { T::default() }; + let path = comp.path(); + let context = self.contexts.entry(path).or_insert_with_key(|path| { + // there has to be a better way to find lib + let home_dir = env::var("HOME").expect("user home not set"); + let mut lib_path = PathBuf::from(home_dir); + lib_path.push(".calyx"); + let ws = calyx_frontend::Workspace::construct( + &Some(path.into()), + &lib_path, + ) + .expect("couldn't parse calyx"); + calyx_ir::from_ast::ast_to_ir(ws) + .expect("couldn't construct calyx ir") + }); + comp.init(context); + box_calyx_ffi_component(comp) } } diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index 9b2fe48836..80281ce750 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -1,4 +1,2 @@ -pub use super::{ - useless_ffi_backend, CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef, -}; +pub use super::{CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef}; pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests}; diff --git a/tools/calyx-ffi/tests/test.rs b/tools/calyx-ffi/tests/test.rs index 8a223db05c..238310beda 100644 --- a/tools/calyx-ffi/tests/test.rs +++ b/tools/calyx-ffi/tests/test.rs @@ -1,5 +1,7 @@ use calyx_ffi::prelude::*; +use calyx_ffi::useless_ffi_backend; + #[calyx_ffi( src = "/Users/ethan/Documents/GitHub/calyx/tools/calyx-ffi/tests/file.futil", comp = "main", @@ -10,6 +12,7 @@ struct Main; #[test] fn test() { let mut main = Main::default(); + assert!(main.name() == "main"); main.reset(); assert!(main.reset == 0); main.tick(); diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index f869ca9ce8..9e87468fdf 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -1,9 +1,11 @@ use calyx_ffi::prelude::*; +use calyx_ffi::cider_ffi_backend; + #[calyx_ffi( src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", comp = "main", - backend = useless_ffi_backend + backend = cider_ffi_backend )] struct Adder;