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;