diff --git a/Cargo.lock b/Cargo.lock index 2ff9569d25..019194c694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -520,14 +520,6 @@ dependencies = [ name = "calyx-stdlib" version = "0.7.1" -[[package]] -name = "calyx-tb-plugin" -version = "0.0.0" -dependencies = [ - "tb", - "toml", -] - [[package]] name = "calyx-utils" version = "0.7.1" @@ -720,14 +712,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cocotb-tb-plugin" -version = "0.0.0" -dependencies = [ - "makemake", - "tb", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -2910,9 +2894,11 @@ dependencies = [ "fs_extra", "libloading", "log", + "makemake", "semver", "serde", "tempdir", + "toml", ] [[package]] @@ -3370,13 +3356,6 @@ dependencies = [ "pretty", ] -[[package]] -name = "verilator-tb-plugin" -version = "0.0.0" -dependencies = [ - "tb", -] - [[package]] name = "version_check" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index 313502235d..b4cdf32cb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,6 @@ members = [ "tools/yxi", "tools/calyx-writer", "tools/tb", - "tools/tb/plugins/cocotb", - "tools/tb/plugins/verilator", - "tools/tb/plugins/calyx", "tools/calyx-ffi-macro", "tools/calyx-ffi", "tools/tb/examples/calyx", diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index 6d0d9bd15b..2efac22f9b 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -111,6 +111,7 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { #[derive(Default)] struct CalyxFFITestModuleVisitor { + pub wrappers: Vec, pub tests: Vec, } @@ -122,20 +123,27 @@ impl syn::visit::Visit<'_> for CalyxFFITestModuleVisitor { .any(|attr| attr.path().is_ident("calyx_ffi_test")); if has_calyx_ffi_test { let fn_name = &i.sig.ident; - let gen_fn_name = - format_ident!("calyx_ffi_generated_wrapper_for_{}", fn_name); let dut_type = get_ffi_test_dut_type(i) .expect("calyx_ffi_test should enforce this invariant"); - self.tests.push(syn::parse_quote! { - unsafe fn #gen_fn_name(ffi: &mut CalyxFFI) { - let dut = ffi.comp::<#dut_type>(); + self.wrappers.push(syn::parse_quote! { + pub(crate) unsafe fn #fn_name(ffi: &mut CalyxFFI) { + let dut = ffi.new_comp::<#dut_type>(); let dut_ref = &mut *dut.borrow_mut(); let dut_pointer = dut_ref as *mut dyn CalyxFFIComponent as *mut _ as *mut #dut_type; let dut_concrete: &mut #dut_type = &mut *dut_pointer; - #fn_name(dut_concrete); + super::#fn_name(dut_concrete); + } + }); + self.tests.push(syn::parse_quote! { + #[test] + pub(crate) fn #fn_name() { + let mut ffi = CalyxFFI::new(); + unsafe { + super::calyx_ffi_generated_wrappers::#fn_name(&mut ffi); + } } - }) + }); } } } @@ -154,18 +162,34 @@ pub fn calyx_ffi_tests(args: TokenStream, item: TokenStream) -> TokenStream { let mut visitor = CalyxFFITestModuleVisitor::default(); syn::visit::visit_item_mod(&mut visitor, &module); + let wrappers = visitor.wrappers; + let tests = visitor.tests; + + let test_names = wrappers.iter().map(|test| test.sig.ident.clone()); + let generated_wrappers = quote! { + pub(crate) mod calyx_ffi_generated_wrappers { + use super::*; + + pub(crate) const CALYX_FFI_TESTS: &'static [unsafe fn(&mut CalyxFFI) -> ()] = &[ + #(#test_names),* + ]; - let test_names = visitor.tests.iter().map(|test| test.sig.ident.clone()); - let test_array = quote! { - pub const CALYX_FFI_TESTS: &'static [unsafe fn(&mut CalyxFFI) -> ()] = &[ - #(#test_names),* - ]; + #(#wrappers)* + } }; - let test_array_item: syn::Item = syn::parse2(test_array).unwrap(); + let generated_wrappers_item: syn::Item = + syn::parse2(generated_wrappers).unwrap(); + + let generated_tests = quote! { + pub(crate) mod calyx_ffi_generated_tests { + use super::*; - let mut items_to_add = vec![test_array_item]; - items_to_add.extend(visitor.tests.iter().cloned().map(syn::Item::Fn)); + #(#tests)* + } + }; + let generated_tests_item: syn::Item = syn::parse2(generated_tests).unwrap(); + let items_to_add = vec![generated_wrappers_item, generated_tests_item]; if let Some((_, ref mut items)) = module.content { items.extend(items_to_add); } else { @@ -175,9 +199,13 @@ pub fn calyx_ffi_tests(args: TokenStream, item: TokenStream) -> TokenStream { quote! { #module - pub unsafe fn calyx_ffi_test(ffi: &mut CalyxFFI) { - for test in #module_name::CALYX_FFI_TESTS { - test(ffi); + pub mod calyx_ffi_generated_top { + use super::*; + + pub unsafe fn run_tests(ffi: &mut CalyxFFI) { + for test in #module_name::calyx_ffi_generated_wrappers::CALYX_FFI_TESTS { + test(ffi); + } } } } diff --git a/tools/calyx-ffi-macro/src/parse.rs b/tools/calyx-ffi-macro/src/parse.rs index 5796d085ba..91dfa4bb38 100644 --- a/tools/calyx-ffi-macro/src/parse.rs +++ b/tools/calyx-ffi-macro/src/parse.rs @@ -35,7 +35,7 @@ impl Parse for CalyxFFIMacroArgs { if !input.is_empty() { return Err(syn::Error::new_spanned( input.parse::()?, - "Invalid `calyx_ffi` argument syntax: expected 'src = \"...\", comp = \"...\", backend = ...", + "Invalid `calyx_ffi` argument syntax: expected 'src = \"...\", comp = \"...\", extern = ...", )); } diff --git a/tools/calyx-ffi/src/backend.rs b/tools/calyx-ffi/src/backend.rs index 7134483db4..414a632881 100644 --- a/tools/calyx-ffi/src/backend.rs +++ b/tools/calyx-ffi/src/backend.rs @@ -1,3 +1,4 @@ +/// Example FFI backend. #[macro_export] macro_rules! useless_ffi_backend { (init $dut:ident; $($port:ident),*) => { diff --git a/tools/calyx-ffi/src/lib.rs b/tools/calyx-ffi/src/lib.rs index e791d1673b..7c38bbf81e 100644 --- a/tools/calyx-ffi/src/lib.rs +++ b/tools/calyx-ffi/src/lib.rs @@ -28,7 +28,8 @@ pub type CalyxFFIComponentRef = Rc>; #[derive(Default)] pub struct CalyxFFI { - comps: HashMap<&'static str, CalyxFFIComponentRef>, + reuse: HashMap<&'static str, usize>, + comps: Vec, } impl CalyxFFI { @@ -36,22 +37,34 @@ impl CalyxFFI { Self::default() } + /// Any component `T`. pub fn comp( &mut self, ) -> CalyxFFIComponentRef { let name = T::default().name(); - if !self.comps.contains_key(name) { - let comp_ref = Rc::new(RefCell::new(T::default())); - comp_ref.borrow_mut().init(); - self.comps.insert(name, comp_ref); + if let Some(index) = self.reuse.get(name) { + self.comps[*index].clone() + } else { + self.new_comp::() } - self.comps.get(name).unwrap().clone() + } + + /// A new component `T`. + 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 { + for comp in &self.comps { comp.borrow_mut().deinit(); } } diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index cd30286042..fea9e34fc5 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -22,3 +22,5 @@ libloading = "0.8.4" log.workspace = true env_logger.workspace = true fs_extra = "1.3.0" +makemake = "0.1.3" +toml = "0.8.14" diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index b31352a55a..f869ca9ce8 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -7,6 +7,7 @@ use calyx_ffi::prelude::*; )] struct Adder; +#[cfg(test)] #[calyx_ffi_tests] mod tests { use super::*; diff --git a/tools/tb/plugins/.gitkeep b/tools/tb/plugins/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/tb/plugins/calyx/Cargo.toml b/tools/tb/plugins/calyx/Cargo.toml deleted file mode 100644 index 83032095ed..0000000000 --- a/tools/tb/plugins/calyx/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "calyx-tb-plugin" -edition.workspace = true -rust-version.workspace = true - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -tb = { path = "../..", version = "0.0.0" } -toml = "0.8.14" diff --git a/tools/tb/plugins/cocotb/Cargo.toml b/tools/tb/plugins/cocotb/Cargo.toml deleted file mode 100644 index 1ed8cb24e7..0000000000 --- a/tools/tb/plugins/cocotb/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "cocotb-tb-plugin" -edition.workspace = true -rust-version.workspace = true - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -tb = { path = "../..", version = "0.0.0" } -makemake = "0.1.3" diff --git a/tools/tb/plugins/verilator/Cargo.toml b/tools/tb/plugins/verilator/Cargo.toml deleted file mode 100644 index 49af4d6f12..0000000000 --- a/tools/tb/plugins/verilator/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "verilator-tb-plugin" -edition.workspace = true -rust-version.workspace = true - -[lib] -path = "lib.rs" -crate-type = ["cdylib"] - -[dependencies] -tb = { path = "../..", version = "0.0.0" } diff --git a/tools/tb/src/builtin_plugins.rs b/tools/tb/src/builtin_plugins.rs new file mode 100644 index 0000000000..3eb32c3d02 --- /dev/null +++ b/tools/tb/src/builtin_plugins.rs @@ -0,0 +1,3 @@ +pub mod calyx; +pub mod cocotb; +pub mod verilator; diff --git a/tools/tb/plugins/calyx/lib.rs b/tools/tb/src/builtin_plugins/calyx.rs similarity index 87% rename from tools/tb/plugins/calyx/lib.rs rename to tools/tb/src/builtin_plugins/calyx.rs index 2f001fae6a..a4bff0e64b 100644 --- a/tools/tb/plugins/calyx/lib.rs +++ b/tools/tb/src/builtin_plugins/calyx.rs @@ -1,17 +1,18 @@ +use std::fs; use std::io::{self, Write}; use std::path::PathBuf; use std::process::Command; -use std::{fs, path::Path}; -use tb::declare_plugin; -use tb::{config::Config, error::LocalResult, plugin::Plugin, semver, tempdir}; +use crate::{ + config::Config, error::LocalResult, plugin::Plugin, semver, tempdir, +}; #[derive(Default)] pub struct CalyxTB; mod config_keys {} -const DRIVER_CODE: &str = include_str!("driver.rs"); +const DRIVER_CODE: &str = include_str!("resources/driver.rs"); impl Plugin for CalyxTB { fn name(&self) -> &'static str { @@ -22,7 +23,7 @@ impl Plugin for CalyxTB { semver::Version::new(0, 0, 0) } - fn setup(&self, config: &mut Config) -> LocalResult<()> { + fn setup(&self, _config: &mut Config) -> LocalResult<()> { Ok(()) } @@ -31,8 +32,11 @@ impl Plugin for CalyxTB { input: String, tests: &[String], work_dir: tempdir::TempDir, - config: &Config, + _config: &Config, ) -> LocalResult<()> { + println!( + "recommendation: Run the #[calyx_ffi_tests] as Rust tests directly" + ); let mut calyx_ffi_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); calyx_ffi_path.push("../../../calyx-ffi"); @@ -44,7 +48,7 @@ impl Plugin for CalyxTB { manifest_path.push("Cargo.toml"); let mut lib_path = PathBuf::from(work_dir.path()); - lib_path.push("lib.rs"); + lib_path.push(input); for test in tests { let mut test_path = PathBuf::from(work_dir.path()); @@ -99,5 +103,3 @@ impl Plugin for CalyxTB { Ok(()) } } - -declare_plugin!(CalyxTB, CalyxTB::default); diff --git a/tools/tb/plugins/cocotb/lib.rs b/tools/tb/src/builtin_plugins/cocotb.rs similarity index 97% rename from tools/tb/plugins/cocotb/lib.rs rename to tools/tb/src/builtin_plugins/cocotb.rs index 2e1780e124..2cbd27181e 100644 --- a/tools/tb/plugins/cocotb/lib.rs +++ b/tools/tb/src/builtin_plugins/cocotb.rs @@ -2,15 +2,13 @@ use std::io::{self, Write}; use std::process::Command; use std::{fs, path::Path}; -use makemake::{emitter::Emitter, makefile::Makefile}; -use tb::declare_plugin; -use tb::error::LocalError; -use tb::{ +use crate::{ config::{Config, ConfigVarValidator}, - error::LocalResult, + error::{LocalError, LocalResult}, plugin::Plugin, semver, tempdir, }; +use makemake::{emitter::Emitter, makefile::Makefile}; /// v1.8.1 cocotb #[derive(Default)] @@ -145,5 +143,3 @@ impl Plugin for CocoTB { Ok(()) } } - -declare_plugin!(CocoTB, CocoTB::default); diff --git a/tools/tb/plugins/calyx/driver.rs b/tools/tb/src/builtin_plugins/resources/driver.rs similarity index 55% rename from tools/tb/plugins/calyx/driver.rs rename to tools/tb/src/builtin_plugins/resources/driver.rs index d30944f9b7..bce3015661 100644 --- a/tools/tb/plugins/calyx/driver.rs +++ b/tools/tb/src/builtin_plugins/resources/driver.rs @@ -1,9 +1,9 @@ use calyx_ffi::prelude::*; -use test_crate::calyx_ffi_test; +use test_crate::calyx_ffi_generated_top::run_tests; fn main() { let mut ffi = CalyxFFI::default(); unsafe { - calyx_ffi_test(&mut ffi); + run_tests(&mut ffi); } } diff --git a/tools/tb/plugins/verilator/lib.rs b/tools/tb/src/builtin_plugins/verilator.rs similarity index 97% rename from tools/tb/plugins/verilator/lib.rs rename to tools/tb/src/builtin_plugins/verilator.rs index f175c33bc6..8f1a3a3457 100644 --- a/tools/tb/plugins/verilator/lib.rs +++ b/tools/tb/src/builtin_plugins/verilator.rs @@ -3,9 +3,8 @@ use std::{ process::Command, }; -use tb::{ +use crate::{ config::{Config, ConfigVarValidator}, - declare_plugin, error::{LocalError, LocalResult}, plugin::Plugin, semver, tempdir, @@ -135,5 +134,3 @@ impl Plugin for Verilator { Ok(()) } } - -declare_plugin!(Verilator, Verilator::default); diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs index a2a34126f2..4e72be9a0b 100644 --- a/tools/tb/src/driver.rs +++ b/tools/tb/src/driver.rs @@ -5,10 +5,11 @@ use std::{ }; use crate::{ + builtin_plugins::{calyx::CalyxTB, cocotb::CocoTB, verilator::Verilator}, cli::ConfigSet, config::Config, error::{LocalError, LocalResult}, - plugin::{PluginCreate, PluginRef}, + plugin::{Plugin, PluginCreate, PluginRef}, }; use libloading::{Library, Symbol}; use semver::VersionReq; @@ -23,6 +24,14 @@ pub struct Driver { impl Driver { pub fn load(plugin_dirs: &[PathBuf]) -> LocalResult { let mut new_self = Self::default(); + + let cocotb = Box::new(CocoTB); + let verilator = Box::new(Verilator); + let calyx = Box::new(CalyxTB); + new_self.register(cocotb.name(), cocotb); + new_self.register(verilator.name(), verilator); + new_self.register(calyx.name(), calyx); + for plugin_dir in plugin_dirs { match plugin_dir.read_dir().map_err(LocalError::from) { Ok(library_paths) => { diff --git a/tools/tb/src/lib.rs b/tools/tb/src/lib.rs index e3b226d86f..e943f2b7bd 100644 --- a/tools/tb/src/lib.rs +++ b/tools/tb/src/lib.rs @@ -1,9 +1,11 @@ //! Author: Ethan Uppal +pub mod builtin_plugins; pub mod cli; pub mod config; pub mod driver; pub mod error; pub mod plugin; + pub use semver; pub use tempdir;