From db4db7dbf2988949695eaefa7c534086dba8c113 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 30 Jun 2024 02:03:21 -0400 Subject: [PATCH 01/41] Initial tb --- Cargo.lock | 8 + Cargo.toml | 2 +- fud2/README.md | 11 +- fud2/fud-core/src/exec/driver.rs | 5 + fud2/fud-core/src/run.rs | 6 +- fud2/src/lib.rs | 13 + test.futil | 17 ++ tools/tb/.gitignore | 1 + tools/tb/Cargo.toml | 17 ++ tools/tb/Makefile | 7 + tools/tb/README.md | 6 + .../cocotb/doc_examples_quickstart/README.md | 2 + .../doc_examples_quickstart/my_design.sv | 15 + .../doc_examples_quickstart/test_my_design.py | 50 ++++ tools/tb/src/cli.rs | 21 ++ tools/tb/src/gen_makefile.rs | 261 ++++++++++++++++++ tools/tb/src/lib.rs | 3 + tools/tb/src/main.rs | 23 ++ tools/tb/src/testbench.rs | 64 +++++ tools/tb/src/testbench/cocotb.rs | 57 ++++ tools/tb/src/testbench/verilator.rs | 14 + tools/tb/tb | 1 + 22 files changed, 599 insertions(+), 5 deletions(-) create mode 100644 test.futil create mode 100644 tools/tb/.gitignore create mode 100644 tools/tb/Cargo.toml create mode 100644 tools/tb/Makefile create mode 100644 tools/tb/README.md create mode 100644 tools/tb/examples/cocotb/doc_examples_quickstart/README.md create mode 100644 tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv create mode 100644 tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py create mode 100644 tools/tb/src/cli.rs create mode 100644 tools/tb/src/gen_makefile.rs create mode 100644 tools/tb/src/lib.rs create mode 100644 tools/tb/src/main.rs create mode 100644 tools/tb/src/testbench.rs create mode 100644 tools/tb/src/testbench/cocotb.rs create mode 100644 tools/tb/src/testbench/verilator.rs create mode 100755 tools/tb/tb diff --git a/Cargo.lock b/Cargo.lock index ce5c097edc..bf8be384b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2908,6 +2908,14 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tb" +version = "0.7.1" +dependencies = [ + "argh", + "tempdir", +] + [[package]] name = "tempdir" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index 94ca7bf447..c980048f7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ "tools/cider-data-converter", "tools/calyx-pass-explorer", "tools/yxi", - "tools/calyx-writer", + "tools/calyx-writer", "tools/tb", ] exclude = ["site"] diff --git a/fud2/README.md b/fud2/README.md index a0f0280308..53b47b3a8a 100644 --- a/fud2/README.md +++ b/fud2/README.md @@ -24,4 +24,13 @@ Add the path to the location of the Calyx compiler: base = "" ``` -[fud]: https://docs.calyxir.org/fud/index.html \ No newline at end of file +[fud]: https://docs.calyxir.org/fud/index.html + +### CLI + +- You can pass/override config variables by passing one or more options of the form `--set variable=value`. + +### Writing a new state + +Given a `bld: &mut DriverBuilder`, call `bld.state`, and define appropriate rules via `bld.rule`. +Each rule may require one or more setups; a setup can be obtained through `bld.setup` and may in addition define variables (including those mapped to config file keys) or rules. diff --git a/fud2/fud-core/src/exec/driver.rs b/fud2/fud-core/src/exec/driver.rs index c4805ca7ee..6d69fbadc8 100644 --- a/fud2/fud-core/src/exec/driver.rs +++ b/fud2/fud-core/src/exec/driver.rs @@ -328,6 +328,11 @@ impl DriverBuilder { } } + /// Define a new fud2 state named `name`. Filesnames without an assigned + /// state will be assigned this state if their extension is contained in + /// `extensions`. If two states share an extension, the inferred assignment + /// is undefined. (Assuming this is the behavior; Jeremy said he doesn't + /// remember) pub fn state(&mut self, name: &str, extensions: &[&str]) -> StateRef { self.states.push(State { name: name.to_string(), diff --git a/fud2/fud-core/src/run.rs b/fud2/fud-core/src/run.rs index aa35b8e7d5..504c3d3645 100644 --- a/fud2/fud-core/src/run.rs +++ b/fud2/fud-core/src/run.rs @@ -546,13 +546,13 @@ impl Emitter { } } - /// Emit a Ninja variable declaration for `name` based on the configured value for `key`. + /// Emit a Ninja variable declaration that sets `name` to the value bound by `key` in the config file. pub fn config_var(&mut self, name: &str, key: &str) -> EmitResult { self.var(name, &self.config_val(key)?)?; Ok(()) } - /// Emit a Ninja variable declaration for `name` based on the configured value for `key`, or a + /// Emit a Ninja variable declaration that sets `name` to the value bound by `key` in the config file, or a /// default value if it's missing. pub fn config_var_or( &mut self, @@ -563,7 +563,7 @@ impl Emitter { self.var(name, &self.config_or(key, default)) } - /// Emit a Ninja variable declaration. + /// Emit a Ninja variable declaration `name = value`. pub fn var(&mut self, name: &str, value: &str) -> std::io::Result<()> { writeln!(self.out, "{} = {}", name, value) } diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index dcef0adf96..24747767f2 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -83,6 +83,17 @@ fn setup_mrxl( (mrxl, mrxl_setup) } +fn setup_tb(bld: &mut DriverBuilder, calyx: StateRef) { + let tb = bld.state("tb", &[]); + let tb_setup = bld.setup("Testbench executable", |e| { + e.var("calyx-tb-exe", "calyx-tb")?; + e.config_var("calyx-tb-tests", "tb.tests")?; + e.rule("calyx-to-tb", "$calyx-tb-exe ")?; + Ok(()) + }); + bld.rule(&[tb_setup], calyx, tb, "calyx-to-tb"); +} + pub fn build_driver(bld: &mut DriverBuilder) { // The verilog state let verilog = bld.state("verilog", &["sv", "v"]); @@ -93,6 +104,8 @@ pub fn build_driver(bld: &mut DriverBuilder) { // MrXL. setup_mrxl(bld, calyx); + setup_tb(bld, calyx); + // Shared machinery for RTL simulators. let dat = bld.state("dat", &["json"]); let vcd = bld.state("vcd", &["vcd"]); diff --git a/test.futil b/test.futil new file mode 100644 index 0000000000..4598bac56a --- /dev/null +++ b/test.futil @@ -0,0 +1,17 @@ +component main() -> () { + cells {} + wires { + static<1> group a { + + } + } + control { + seq { + seq { + seq { + a; + } + } + } + } +} diff --git a/tools/tb/.gitignore b/tools/tb/.gitignore new file mode 100644 index 0000000000..ba2906d066 --- /dev/null +++ b/tools/tb/.gitignore @@ -0,0 +1 @@ +main diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml new file mode 100644 index 0000000000..ed15efe2b3 --- /dev/null +++ b/tools/tb/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tb" +authors.workspace = true +license-file.workspace = true +keywords.workspace = true +repository.workspace = true +readme.workspace = true +description.workspace = true +categories.workspace = true +homepage.workspace = true +edition.workspace = true +version.workspace = true +rust-version.workspace = true + +[dependencies] +argh.workspace = true +tempdir = "0.3.7" diff --git a/tools/tb/Makefile b/tools/tb/Makefile new file mode 100644 index 0000000000..e2d46589c9 --- /dev/null +++ b/tools/tb/Makefile @@ -0,0 +1,7 @@ +TARGET = tb + +.PHONY: $(TARGET) +$(TARGET): + cargo build --manifest-path Cargo.toml + printf "../../target/debug/tb \$$@\n" > $@ + chmod u+x $@ diff --git a/tools/tb/README.md b/tools/tb/README.md new file mode 100644 index 0000000000..1b5a183625 --- /dev/null +++ b/tools/tb/README.md @@ -0,0 +1,6 @@ +For example, if you make sure to follow the instructions under [`examples/cocotb/doc_examples_quickstart/`](examples/cocotb/doc_examples_quickstart/), +``` +make +./tb examples/cocotb/doc_examples_quickstart/my_design.sv -t examples/cocotb/doc_examples_quickstart/test_my_design.py --using cocotb +``` +should run `cocotb` on the input file and harness. diff --git a/tools/tb/examples/cocotb/doc_examples_quickstart/README.md b/tools/tb/examples/cocotb/doc_examples_quickstart/README.md new file mode 100644 index 0000000000..b7547d5738 --- /dev/null +++ b/tools/tb/examples/cocotb/doc_examples_quickstart/README.md @@ -0,0 +1,2 @@ +Taken from https://github.com/cocotb/cocotb/tree/master/examples/doc_examples/quickstart. +Make sure to checkout `v1.8.1` for `cocotb`. diff --git a/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv b/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv new file mode 100644 index 0000000000..d79b5d32d1 --- /dev/null +++ b/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv @@ -0,0 +1,15 @@ +// This file is public domain, it can be freely copied without restrictions. +// SPDX-License-Identifier: CC0-1.0 + +module my_design(input logic clk); + +timeunit 1ns; +timeprecision 1ns; + +logic my_signal_1; +logic my_signal_2; + +assign my_signal_1 = 1'bx; +assign my_signal_2 = 0; + +endmodule diff --git a/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py b/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py new file mode 100644 index 0000000000..fa7a210ccd --- /dev/null +++ b/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py @@ -0,0 +1,50 @@ +# This file is public domain, it can be freely copied without restrictions. +# SPDX-License-Identifier: CC0-1.0 + +# test_my_design.py (simple) + +import cocotb +from cocotb.triggers import Timer + + +@cocotb.test() +async def my_first_test(dut): + """Try accessing the design.""" + + for cycle in range(10): + dut.clk.value = 0 + await Timer(1, units="ns") + dut.clk.value = 1 + await Timer(1, units="ns") + + dut._log.info("my_signal_1 is %s", dut.my_signal_1.value) + assert dut.my_signal_2.value[0] == 0, "my_signal_2[0] is not 0!" + + +# test_my_design.py (extended) + +import cocotb +from cocotb.triggers import FallingEdge, Timer + + +async def generate_clock(dut): + """Generate clock pulses.""" + + for cycle in range(10): + dut.clk.value = 0 + await Timer(1, units="ns") + dut.clk.value = 1 + await Timer(1, units="ns") + + +@cocotb.test() +async def my_second_test(dut): + """Try accessing the design.""" + + await cocotb.start(generate_clock(dut)) # run the clock "in the background" + + await Timer(5, units="ns") # wait a bit + await FallingEdge(dut.clk) # wait for falling edge/"negedge" + + dut._log.info("my_signal_1 is %s", dut.my_signal_1.value) + assert dut.my_signal_2.value[0] == 0, "my_signal_2[0] is not 0!" diff --git a/tools/tb/src/cli.rs b/tools/tb/src/cli.rs new file mode 100644 index 0000000000..7f3622752d --- /dev/null +++ b/tools/tb/src/cli.rs @@ -0,0 +1,21 @@ +use argh::FromArgs; + +#[derive(FromArgs)] +/// Test verilog files under various harnesses. +pub struct CLI { + #[argh(positional)] + /// verilog file + pub input: String, + + #[argh(option, short = 't')] + /// test harness + pub tests: Vec, + + #[argh(option, short = 'u')] + /// the testbench to invoke + pub using: String, + + #[argh(switch)] + /// displays version information + pub version: bool, +} diff --git a/tools/tb/src/gen_makefile.rs b/tools/tb/src/gen_makefile.rs new file mode 100644 index 0000000000..8c7cdacb6a --- /dev/null +++ b/tools/tb/src/gen_makefile.rs @@ -0,0 +1,261 @@ +// i tried and couldn't find a crate for this -- if you know of one, lmk + +use std::{cell::RefCell, fmt::Write, rc::Rc}; + +trait MakefileEmittable { + fn emit(&self) -> String; +} + +enum AssignmentKind { + Overwrite, + Underwrite, + Append, +} + +impl MakefileEmittable for AssignmentKind { + fn emit(&self) -> String { + match &self { + Self::Overwrite => "=", + Self::Underwrite => "?=", + Self::Append => "+=", + } + .to_string() + } +} + +type MakefileEmittableRef = Box; + +struct Assignment { + kind: AssignmentKind, + name: String, + value: String, +} + +impl Assignment { + pub fn new, T: AsRef>( + kind: AssignmentKind, + name: S, + value: T, + ) -> Self { + Self { + kind, + name: name.as_ref().to_string(), + value: value.as_ref().to_string(), + } + } +} + +impl MakefileEmittable for Assignment { + fn emit(&self) -> String { + format!("{} {} {}", self.name, self.kind.emit(), self.value) + } +} + +struct Comment { + text: String, +} + +impl Comment { + pub fn new>(text: S) -> Self { + Self { + text: text.as_ref().to_string(), + } + } +} + +impl MakefileEmittable for Comment { + fn emit(&self) -> String { + self.text + .lines() + .map(|line| format!("# {}", line)) + .collect::>() + .join("\n") + } +} + +struct Newline; + +impl MakefileEmittable for Newline { + fn emit(&self) -> String { + String::from("\n") + } +} + +struct Rule { + is_phony: bool, + target: String, + dependencies: Vec, + commands: Vec, +} + +impl Rule { + fn new>(target: S) -> Self { + Self { + is_phony: false, + target: target.as_ref().to_string(), + dependencies: vec![], + commands: vec![], + } + } +} + +impl MakefileEmittable for Rule { + fn emit(&self) -> String { + let mut result = String::new(); + if self.is_phony { + writeln!(&mut result, ".PHONY: {}", self.target).unwrap(); + } + writeln!( + &mut result, + "{}: {}", + self.target, + self.dependencies.join(" ") + ) + .unwrap(); + for (i, command) in self.commands.iter().enumerate() { + if i > 0 { + result.push('\n'); + } + write!(&mut result, "\t{}", command.replace("\n", " ")).unwrap(); + } + result + } +} + +#[derive(Clone)] +pub struct RuleRef { + rule: Rc>, +} + +impl RuleRef { + fn new>(target: S) -> Self { + RuleRef { + rule: Rc::new(RefCell::new(Rule::new(target))), + } + } + + pub fn set_phony(&self) { + self.rule.borrow_mut().is_phony = true; + } + + pub fn add_dep>(&self, dep: S) { + self.rule + .borrow_mut() + .dependencies + .push(dep.as_ref().to_string()); + } + + pub fn add_cmd>(&self, cmd: S) { + self.rule + .borrow_mut() + .commands + .push(cmd.as_ref().to_string()); + } + + pub fn phony(self) -> Self { + self.set_phony(); + self + } + + pub fn dep>(self, dep: S) -> Self { + self.add_dep(dep); + self + } + + pub fn cmd>(self, cmd: S) -> Self { + self.add_cmd(cmd); + self + } +} + +impl MakefileEmittable for RuleRef { + fn emit(&self) -> String { + self.rule.borrow().emit() + } +} + +struct Include { + path_expr: String, +} + +impl Include { + fn new>(path_expr: S) -> Self { + Self { + path_expr: path_expr.as_ref().to_string(), + } + } +} + +impl MakefileEmittable for Include { + fn emit(&self) -> String { + format!("include {}", self.path_expr) + } +} + +pub struct Makefile { + contents: Vec, +} + +impl Makefile { + pub fn new() -> Self { + Self { contents: vec![] } + } + + pub fn comment>(&mut self, text: S) { + self.add(Box::new(Comment::new(text))); + } + + pub fn newline(&mut self) { + self.add(Box::new(Newline)); + } + + pub fn assign, T: AsRef>(&mut self, name: S, value: T) { + self.add(Box::new(Assignment::new( + AssignmentKind::Overwrite, + name, + value, + ))); + } + + pub fn assign_without_overwrite, T: AsRef>( + &mut self, + name: S, + value: T, + ) { + self.add(Box::new(Assignment::new( + AssignmentKind::Underwrite, + name, + value, + ))); + } + + pub fn append, T: AsRef>(&mut self, name: S, value: T) { + self.add(Box::new(Assignment::new( + AssignmentKind::Append, + name, + value, + ))); + } + + pub fn rule>(&mut self, target: S) -> RuleRef { + let rule = RuleRef::new(target); + self.add(Box::new(rule.clone())); + rule + } + + pub fn include>(&mut self, path_expr: S) { + self.add(Box::new(Include::new(path_expr))); + } + + pub fn build(&self) -> String { + self.contents + .iter() + .map(|e| e.emit()) + .collect::>() + .join("\n") + } + + fn add(&mut self, e: Box) { + self.contents.push(e); + } +} diff --git a/tools/tb/src/lib.rs b/tools/tb/src/lib.rs new file mode 100644 index 0000000000..2a131bfb35 --- /dev/null +++ b/tools/tb/src/lib.rs @@ -0,0 +1,3 @@ +pub mod cli; +pub mod gen_makefile; // todo get into own crate or find existing crate +pub mod testbench; diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs new file mode 100644 index 0000000000..00889773c1 --- /dev/null +++ b/tools/tb/src/main.rs @@ -0,0 +1,23 @@ +use tb::{ + cli::CLI, + testbench::{TestbenchManager, TestbenchResult}, +}; + +fn main() -> TestbenchResult { + let args: CLI = argh::from_env(); + + if args.version { + println!( + "{} v{}", + std::env::current_exe() + .expect("how did you call this without argv[0]??") + .to_str() + .expect("argv[0] not valid unicode"), + env!("CARGO_PKG_VERSION") + ); + return Ok(()); + } + + let tbm = TestbenchManager::new(); + tbm.run(args.using, args.input, &args.tests) +} diff --git a/tools/tb/src/testbench.rs b/tools/tb/src/testbench.rs new file mode 100644 index 0000000000..aca86ef1d9 --- /dev/null +++ b/tools/tb/src/testbench.rs @@ -0,0 +1,64 @@ +use std::{collections::HashMap, fs, path::PathBuf}; +use tempdir::TempDir; + +pub mod cocotb; +pub mod verilator; + +pub type TestbenchResult = std::io::Result<()>; + +fn testbench_error_unknown_tb>(tb: S) -> TestbenchResult { + TestbenchResult::Err(std::io::Error::other(format!("Unknown testbench '{}'", tb.as_ref()))) +} + +pub trait Testbench { + // TODO: add a config system here + /// - `input` is a relative path to the input file in `work_dir`. + /// - `tests` are a relative paths to the testing harnesses in `work_dir`. + fn run(&self, input: String, tests: &[String], work_dir: TempDir) -> TestbenchResult; +} + +pub type TestbenchRef = Box; + +#[derive(Default)] +pub struct TestbenchManager { + tbs: HashMap, +} + +impl TestbenchManager { + pub fn new() -> Self { + let mut new_self = Self::default(); + new_self.register("cocotb", Box::new(cocotb::CocoTB)); + new_self.register("verilator", Box::new(verilator::Verilator)); + new_self + } + + pub fn register>(&mut self, name: S, tb: TestbenchRef) { + assert!( + self.tbs.insert(name.as_ref().to_string(), tb).is_none(), + "cannot re-register the same testbench name for a different testbench" + ); + } + + pub fn run>(&self, name: S, input: String, tests: &[String]) -> TestbenchResult { + if let Some(tb) = self.tbs.get(name.as_ref()) { + let work_dir = TempDir::new(".tb")?; + let input = copy_into(&input, &work_dir)?; + let mut test_basenames = vec![]; + for test in tests { + test_basenames.push(copy_into(&test, &work_dir)?); + } + tb.run(input, &test_basenames, work_dir) + } else { + testbench_error_unknown_tb(name) + } + } +} + +fn copy_into>(file: S, work_dir: &TempDir) -> std::io::Result { + let from_path = PathBuf::from(file.as_ref()); + let basename = from_path.file_name().expect("path ended with ..").to_str().expect("invalid unicode").to_string(); + let mut to_path = work_dir.path().to_path_buf(); + to_path.push(&basename); + fs::copy(from_path, to_path)?; + Ok(basename) +} diff --git a/tools/tb/src/testbench/cocotb.rs b/tools/tb/src/testbench/cocotb.rs new file mode 100644 index 0000000000..ba71217c3f --- /dev/null +++ b/tools/tb/src/testbench/cocotb.rs @@ -0,0 +1,57 @@ +use super::Testbench; +use crate::gen_makefile::Makefile; +use std::{fs, io::Write, path::PathBuf}; + +/// v1.8.1 cocotb +pub struct CocoTB; + +fn strip_extension>(path: S) -> String { + let mut path_buf = PathBuf::from(path.as_ref()); + path_buf.set_extension(""); + path_buf.to_str().expect("invalid path").to_string() +} + +impl Testbench for CocoTB { + fn run( + &self, + input: String, + tests: &[String], + work_dir: tempdir::TempDir, + ) -> super::TestbenchResult { + for test in tests { + // copied from https://github.com/cocotb/cocotb/blob/v1.8.1/examples/doc_examples/quickstart/Makefile + let mut makefile = Makefile::new(); + makefile.comment("This file is public domain, it can be freely copied without restrictions."); + makefile.comment("SPDX-License-Identifier: CC0-1.0"); + makefile.newline(); + makefile.comment("Makefile"); + makefile.newline(); + makefile.comment("defaults"); + makefile.assign_without_overwrite("SIM", "icarus"); + makefile.assign_without_overwrite("TOPLEVEL_LANG", "verilog"); + makefile.append("VERILOG_SOURCES", "$(PWD)/my_design.sv"); + makefile.comment("use VHDL_SOURCES for VHDL files"); + makefile.newline(); + makefile.comment("TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file"); + makefile.assign("TOPLEVEL", "my_design"); + makefile.newline(); + makefile.comment("MODULE is the basename of the Python test file"); + makefile.assign("MODULE", "test_my_design"); + makefile.newline(); + makefile.comment("include cocotb's make rules to take care of the simulator setup"); + makefile.include("$(shell cocotb-config --makefiles)/Makefile.sim"); + + let mut makefile_path = work_dir.path().to_path_buf(); + makefile_path.push("Makefile"); + fs::write(makefile_path, makefile.build())?; + + let output = std::process::Command::new("make") + .current_dir(work_dir.path()) + .output()?; + std::io::stdout().write_all(&output.stdout)?; + std::io::stderr().write_all(&output.stderr)?; + } + + Ok(()) + } +} diff --git a/tools/tb/src/testbench/verilator.rs b/tools/tb/src/testbench/verilator.rs new file mode 100644 index 0000000000..15841985ab --- /dev/null +++ b/tools/tb/src/testbench/verilator.rs @@ -0,0 +1,14 @@ +use super::Testbench; + +pub struct Verilator; + +impl Testbench for Verilator { + fn run( + &self, + input: String, + tests: &[String], + work_dir: tempdir::TempDir, + ) -> super::TestbenchResult { + todo!() + } +} diff --git a/tools/tb/tb b/tools/tb/tb new file mode 100755 index 0000000000..e333ad9d76 --- /dev/null +++ b/tools/tb/tb @@ -0,0 +1 @@ +../../target/debug/tb $@ From fae996a78b7975efc95b32a3ac7c580344cd180b Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 30 Jun 2024 02:05:39 -0400 Subject: [PATCH 02/41] fud2 --- fud2/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index 24747767f2..cece66b31b 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -86,9 +86,12 @@ fn setup_mrxl( fn setup_tb(bld: &mut DriverBuilder, calyx: StateRef) { let tb = bld.state("tb", &[]); let tb_setup = bld.setup("Testbench executable", |e| { - e.var("calyx-tb-exe", "calyx-tb")?; - e.config_var("calyx-tb-tests", "tb.tests")?; - e.rule("calyx-to-tb", "$calyx-tb-exe ")?; + e.var("calyx-tb-exe", "tb")?; + e.config_var("calyx-tb-test", "tb.test")?; // todo multi input op + e.rule( + "calyx-to-tb", + "$calyx-tb-exe $in --test $calyx-tb-test --using cocotb", + )?; Ok(()) }); bld.rule(&[tb_setup], calyx, tb, "calyx-to-tb"); From 5f81dc210a4557495329d9be733f96e0dc9c72e5 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 30 Jun 2024 02:09:33 -0400 Subject: [PATCH 03/41] Fix --- fud2/src/lib.rs | 6 +++--- tools/tb/.gitignore | 2 +- tools/tb/src/testbench.rs | 33 +++++++++++++++++++++++++++------ tools/tb/tb | 1 - 4 files changed, 31 insertions(+), 11 deletions(-) delete mode 100755 tools/tb/tb diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index cece66b31b..955df5f16f 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -83,7 +83,7 @@ fn setup_mrxl( (mrxl, mrxl_setup) } -fn setup_tb(bld: &mut DriverBuilder, calyx: StateRef) { +fn setup_tb(bld: &mut DriverBuilder, verilog: StateRef) { let tb = bld.state("tb", &[]); let tb_setup = bld.setup("Testbench executable", |e| { e.var("calyx-tb-exe", "tb")?; @@ -94,7 +94,7 @@ fn setup_tb(bld: &mut DriverBuilder, calyx: StateRef) { )?; Ok(()) }); - bld.rule(&[tb_setup], calyx, tb, "calyx-to-tb"); + bld.rule(&[tb_setup], verilog, tb, "calyx-to-tb"); } pub fn build_driver(bld: &mut DriverBuilder) { @@ -107,7 +107,7 @@ pub fn build_driver(bld: &mut DriverBuilder) { // MrXL. setup_mrxl(bld, calyx); - setup_tb(bld, calyx); + setup_tb(bld, verilog); // Shared machinery for RTL simulators. let dat = bld.state("dat", &["json"]); diff --git a/tools/tb/.gitignore b/tools/tb/.gitignore index ba2906d066..5beb86d02e 100644 --- a/tools/tb/.gitignore +++ b/tools/tb/.gitignore @@ -1 +1 @@ -main +tb diff --git a/tools/tb/src/testbench.rs b/tools/tb/src/testbench.rs index aca86ef1d9..b350dcf946 100644 --- a/tools/tb/src/testbench.rs +++ b/tools/tb/src/testbench.rs @@ -7,14 +7,22 @@ pub mod verilator; pub type TestbenchResult = std::io::Result<()>; fn testbench_error_unknown_tb>(tb: S) -> TestbenchResult { - TestbenchResult::Err(std::io::Error::other(format!("Unknown testbench '{}'", tb.as_ref()))) + TestbenchResult::Err(std::io::Error::other(format!( + "Unknown testbench '{}'", + tb.as_ref() + ))) } pub trait Testbench { // TODO: add a config system here /// - `input` is a relative path to the input file in `work_dir`. /// - `tests` are a relative paths to the testing harnesses in `work_dir`. - fn run(&self, input: String, tests: &[String], work_dir: TempDir) -> TestbenchResult; + fn run( + &self, + input: String, + tests: &[String], + work_dir: TempDir, + ) -> TestbenchResult; } pub type TestbenchRef = Box; @@ -34,12 +42,17 @@ impl TestbenchManager { pub fn register>(&mut self, name: S, tb: TestbenchRef) { assert!( - self.tbs.insert(name.as_ref().to_string(), tb).is_none(), + self.tbs.insert(name.as_ref().to_string(), tb).is_none(), "cannot re-register the same testbench name for a different testbench" ); } - pub fn run>(&self, name: S, input: String, tests: &[String]) -> TestbenchResult { + pub fn run>( + &self, + name: S, + input: String, + tests: &[String], + ) -> TestbenchResult { if let Some(tb) = self.tbs.get(name.as_ref()) { let work_dir = TempDir::new(".tb")?; let input = copy_into(&input, &work_dir)?; @@ -54,9 +67,17 @@ impl TestbenchManager { } } -fn copy_into>(file: S, work_dir: &TempDir) -> std::io::Result { +fn copy_into>( + file: S, + work_dir: &TempDir, +) -> std::io::Result { let from_path = PathBuf::from(file.as_ref()); - let basename = from_path.file_name().expect("path ended with ..").to_str().expect("invalid unicode").to_string(); + let basename = from_path + .file_name() + .expect("path ended with ..") + .to_str() + .expect("invalid unicode") + .to_string(); let mut to_path = work_dir.path().to_path_buf(); to_path.push(&basename); fs::copy(from_path, to_path)?; diff --git a/tools/tb/tb b/tools/tb/tb deleted file mode 100755 index e333ad9d76..0000000000 --- a/tools/tb/tb +++ /dev/null @@ -1 +0,0 @@ -../../target/debug/tb $@ From 7f97267600e4480adb16e196b188c62a5bbbc9e0 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 30 Jun 2024 23:37:59 -0400 Subject: [PATCH 04/41] Use my makefile gen library --- tools/tb/Cargo.toml | 1 + tools/tb/src/gen_makefile.rs | 261 ------------------------------- tools/tb/src/lib.rs | 1 - tools/tb/src/testbench/cocotb.rs | 3 +- 4 files changed, 3 insertions(+), 263 deletions(-) delete mode 100644 tools/tb/src/gen_makefile.rs diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index ed15efe2b3..095c50bcc9 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -15,3 +15,4 @@ rust-version.workspace = true [dependencies] argh.workspace = true tempdir = "0.3.7" +makemake = "0.1.1" diff --git a/tools/tb/src/gen_makefile.rs b/tools/tb/src/gen_makefile.rs deleted file mode 100644 index 8c7cdacb6a..0000000000 --- a/tools/tb/src/gen_makefile.rs +++ /dev/null @@ -1,261 +0,0 @@ -// i tried and couldn't find a crate for this -- if you know of one, lmk - -use std::{cell::RefCell, fmt::Write, rc::Rc}; - -trait MakefileEmittable { - fn emit(&self) -> String; -} - -enum AssignmentKind { - Overwrite, - Underwrite, - Append, -} - -impl MakefileEmittable for AssignmentKind { - fn emit(&self) -> String { - match &self { - Self::Overwrite => "=", - Self::Underwrite => "?=", - Self::Append => "+=", - } - .to_string() - } -} - -type MakefileEmittableRef = Box; - -struct Assignment { - kind: AssignmentKind, - name: String, - value: String, -} - -impl Assignment { - pub fn new, T: AsRef>( - kind: AssignmentKind, - name: S, - value: T, - ) -> Self { - Self { - kind, - name: name.as_ref().to_string(), - value: value.as_ref().to_string(), - } - } -} - -impl MakefileEmittable for Assignment { - fn emit(&self) -> String { - format!("{} {} {}", self.name, self.kind.emit(), self.value) - } -} - -struct Comment { - text: String, -} - -impl Comment { - pub fn new>(text: S) -> Self { - Self { - text: text.as_ref().to_string(), - } - } -} - -impl MakefileEmittable for Comment { - fn emit(&self) -> String { - self.text - .lines() - .map(|line| format!("# {}", line)) - .collect::>() - .join("\n") - } -} - -struct Newline; - -impl MakefileEmittable for Newline { - fn emit(&self) -> String { - String::from("\n") - } -} - -struct Rule { - is_phony: bool, - target: String, - dependencies: Vec, - commands: Vec, -} - -impl Rule { - fn new>(target: S) -> Self { - Self { - is_phony: false, - target: target.as_ref().to_string(), - dependencies: vec![], - commands: vec![], - } - } -} - -impl MakefileEmittable for Rule { - fn emit(&self) -> String { - let mut result = String::new(); - if self.is_phony { - writeln!(&mut result, ".PHONY: {}", self.target).unwrap(); - } - writeln!( - &mut result, - "{}: {}", - self.target, - self.dependencies.join(" ") - ) - .unwrap(); - for (i, command) in self.commands.iter().enumerate() { - if i > 0 { - result.push('\n'); - } - write!(&mut result, "\t{}", command.replace("\n", " ")).unwrap(); - } - result - } -} - -#[derive(Clone)] -pub struct RuleRef { - rule: Rc>, -} - -impl RuleRef { - fn new>(target: S) -> Self { - RuleRef { - rule: Rc::new(RefCell::new(Rule::new(target))), - } - } - - pub fn set_phony(&self) { - self.rule.borrow_mut().is_phony = true; - } - - pub fn add_dep>(&self, dep: S) { - self.rule - .borrow_mut() - .dependencies - .push(dep.as_ref().to_string()); - } - - pub fn add_cmd>(&self, cmd: S) { - self.rule - .borrow_mut() - .commands - .push(cmd.as_ref().to_string()); - } - - pub fn phony(self) -> Self { - self.set_phony(); - self - } - - pub fn dep>(self, dep: S) -> Self { - self.add_dep(dep); - self - } - - pub fn cmd>(self, cmd: S) -> Self { - self.add_cmd(cmd); - self - } -} - -impl MakefileEmittable for RuleRef { - fn emit(&self) -> String { - self.rule.borrow().emit() - } -} - -struct Include { - path_expr: String, -} - -impl Include { - fn new>(path_expr: S) -> Self { - Self { - path_expr: path_expr.as_ref().to_string(), - } - } -} - -impl MakefileEmittable for Include { - fn emit(&self) -> String { - format!("include {}", self.path_expr) - } -} - -pub struct Makefile { - contents: Vec, -} - -impl Makefile { - pub fn new() -> Self { - Self { contents: vec![] } - } - - pub fn comment>(&mut self, text: S) { - self.add(Box::new(Comment::new(text))); - } - - pub fn newline(&mut self) { - self.add(Box::new(Newline)); - } - - pub fn assign, T: AsRef>(&mut self, name: S, value: T) { - self.add(Box::new(Assignment::new( - AssignmentKind::Overwrite, - name, - value, - ))); - } - - pub fn assign_without_overwrite, T: AsRef>( - &mut self, - name: S, - value: T, - ) { - self.add(Box::new(Assignment::new( - AssignmentKind::Underwrite, - name, - value, - ))); - } - - pub fn append, T: AsRef>(&mut self, name: S, value: T) { - self.add(Box::new(Assignment::new( - AssignmentKind::Append, - name, - value, - ))); - } - - pub fn rule>(&mut self, target: S) -> RuleRef { - let rule = RuleRef::new(target); - self.add(Box::new(rule.clone())); - rule - } - - pub fn include>(&mut self, path_expr: S) { - self.add(Box::new(Include::new(path_expr))); - } - - pub fn build(&self) -> String { - self.contents - .iter() - .map(|e| e.emit()) - .collect::>() - .join("\n") - } - - fn add(&mut self, e: Box) { - self.contents.push(e); - } -} diff --git a/tools/tb/src/lib.rs b/tools/tb/src/lib.rs index 2a131bfb35..ab2984f0d9 100644 --- a/tools/tb/src/lib.rs +++ b/tools/tb/src/lib.rs @@ -1,3 +1,2 @@ pub mod cli; -pub mod gen_makefile; // todo get into own crate or find existing crate pub mod testbench; diff --git a/tools/tb/src/testbench/cocotb.rs b/tools/tb/src/testbench/cocotb.rs index ba71217c3f..d41aba0a2c 100644 --- a/tools/tb/src/testbench/cocotb.rs +++ b/tools/tb/src/testbench/cocotb.rs @@ -1,5 +1,6 @@ +use makemake::makefile::Makefile; + use super::Testbench; -use crate::gen_makefile::Makefile; use std::{fs, io::Write, path::PathBuf}; /// v1.8.1 cocotb From 55272109fe9f452e01ca8581a962d0e1bb465b33 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Mon, 1 Jul 2024 00:42:04 -0400 Subject: [PATCH 05/41] Update tb --- Cargo.lock | 31 ++++++++++++++++++----------- tools/tb/Cargo.toml | 2 +- tools/tb/src/testbench.rs | 12 +++++------ tools/tb/src/testbench/cocotb.rs | 24 ++++++++++++---------- tools/tb/src/testbench/verilator.rs | 6 +++--- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf8be384b6..fe68eeac00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1574,15 +1574,14 @@ dependencies = [ [[package]] name = "insta" -version = "1.36.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a508bf83e6f6f2aa438588ae7ceb558a81030c5762cbfe838180a861cf5dc110" +checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" dependencies = [ "console", "lazy_static", "linked-hash-map", "similar", - "yaml-rust", ] [[package]] @@ -1760,6 +1759,16 @@ dependencies = [ "url", ] +[[package]] +name = "makemake" +version = "0.1.2-patch1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb6004ede2d908eb7146261b7a357b2ef31f446bc3b7e5ef1f62f720de0abff" +dependencies = [ + "insta", + "paste", +] + [[package]] name = "manifest-dir-macros" version = "0.1.18" @@ -2004,6 +2013,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.1" @@ -2913,6 +2928,7 @@ name = "tb" version = "0.7.1" dependencies = [ "argh", + "makemake", "tempdir", ] @@ -3676,15 +3692,6 @@ dependencies = [ "tap", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "yansi" version = "1.0.1" diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index 095c50bcc9..8a567a7d29 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -15,4 +15,4 @@ rust-version.workspace = true [dependencies] argh.workspace = true tempdir = "0.3.7" -makemake = "0.1.1" +makemake = "0.1.2-patch1" diff --git a/tools/tb/src/testbench.rs b/tools/tb/src/testbench.rs index b350dcf946..0ecea68039 100644 --- a/tools/tb/src/testbench.rs +++ b/tools/tb/src/testbench.rs @@ -7,10 +7,10 @@ pub mod verilator; pub type TestbenchResult = std::io::Result<()>; fn testbench_error_unknown_tb>(tb: S) -> TestbenchResult { - TestbenchResult::Err(std::io::Error::other(format!( - "Unknown testbench '{}'", - tb.as_ref() - ))) + TestbenchResult::Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Unknown testbench '{}'", tb.as_ref()), + )) } pub trait Testbench { @@ -55,10 +55,10 @@ impl TestbenchManager { ) -> TestbenchResult { if let Some(tb) = self.tbs.get(name.as_ref()) { let work_dir = TempDir::new(".tb")?; - let input = copy_into(&input, &work_dir)?; + let input = copy_into(input, &work_dir)?; let mut test_basenames = vec![]; for test in tests { - test_basenames.push(copy_into(&test, &work_dir)?); + test_basenames.push(copy_into(test, &work_dir)?); } tb.run(input, &test_basenames, work_dir) } else { diff --git a/tools/tb/src/testbench/cocotb.rs b/tools/tb/src/testbench/cocotb.rs index d41aba0a2c..5b1bb37e5e 100644 --- a/tools/tb/src/testbench/cocotb.rs +++ b/tools/tb/src/testbench/cocotb.rs @@ -1,15 +1,17 @@ -use makemake::makefile::Makefile; +use makemake::{emitter::Emitter, makefile::Makefile}; use super::Testbench; -use std::{fs, io::Write, path::PathBuf}; +use std::{fs, io::Write, path::Path}; /// v1.8.1 cocotb pub struct CocoTB; -fn strip_extension>(path: S) -> String { - let mut path_buf = PathBuf::from(path.as_ref()); - path_buf.set_extension(""); - path_buf.to_str().expect("invalid path").to_string() +fn filestem(path_str: &str) -> &str { + let path = Path::new(path_str); + path.file_stem() + .expect("invalid filename") + .to_str() + .expect("invalid unicode") } impl Testbench for CocoTB { @@ -30,16 +32,18 @@ impl Testbench for CocoTB { makefile.comment("defaults"); makefile.assign_without_overwrite("SIM", "icarus"); makefile.assign_without_overwrite("TOPLEVEL_LANG", "verilog"); - makefile.append("VERILOG_SOURCES", "$(PWD)/my_design.sv"); + makefile.append("VERILOG_SOURCES", &input); makefile.comment("use VHDL_SOURCES for VHDL files"); makefile.newline(); makefile.comment("TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file"); - makefile.assign("TOPLEVEL", "my_design"); + makefile.assign("TOPLEVEL", filestem(&input)); makefile.newline(); makefile.comment("MODULE is the basename of the Python test file"); - makefile.assign("MODULE", "test_my_design"); + makefile.assign("MODULE", filestem(test)); makefile.newline(); - makefile.comment("include cocotb's make rules to take care of the simulator setup"); + makefile.comment( + "include cocotb's make rules to take care of the simulator setup", + ); makefile.include("$(shell cocotb-config --makefiles)/Makefile.sim"); let mut makefile_path = work_dir.path().to_path_buf(); diff --git a/tools/tb/src/testbench/verilator.rs b/tools/tb/src/testbench/verilator.rs index 15841985ab..6a05f91c87 100644 --- a/tools/tb/src/testbench/verilator.rs +++ b/tools/tb/src/testbench/verilator.rs @@ -5,9 +5,9 @@ pub struct Verilator; impl Testbench for Verilator { fn run( &self, - input: String, - tests: &[String], - work_dir: tempdir::TempDir, + _input: String, + _tests: &[String], + _work_dir: tempdir::TempDir, ) -> super::TestbenchResult { todo!() } From 2042bb573c85cdc0656b73eecf0e6a3c077f9ec8 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 2 Jul 2024 01:40:39 -0400 Subject: [PATCH 06/41] Add config system --- Cargo.lock | 8 +- Cargo.toml | 4 +- fud2/fud-core/Cargo.toml | 2 +- tools/tb/Cargo.toml | 6 +- tools/tb/src/cli.rs | 6 ++ tools/tb/src/config.rs | 120 ++++++++++++++++++++++++++++ tools/tb/src/driver.rs | 80 +++++++++++++++++++ tools/tb/src/error.rs | 52 ++++++++++++ tools/tb/src/lib.rs | 3 + tools/tb/src/main.rs | 30 ++++++- tools/tb/src/testbench.rs | 74 ++--------------- tools/tb/src/testbench/cocotb.rs | 53 ++++++++++-- tools/tb/src/testbench/verilator.rs | 23 +++++- 13 files changed, 372 insertions(+), 89 deletions(-) create mode 100644 tools/tb/src/config.rs create mode 100644 tools/tb/src/driver.rs create mode 100644 tools/tb/src/error.rs diff --git a/Cargo.lock b/Cargo.lock index fe68eeac00..19fb04de16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1761,9 +1761,9 @@ dependencies = [ [[package]] name = "makemake" -version = "0.1.2-patch1" +version = "0.1.2-patch2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb6004ede2d908eb7146261b7a357b2ef31f446bc3b7e5ef1f62f720de0abff" +checksum = "8adf7ddede5f0086f7b56de8bb01946a5ac6438a92c4ae9c1e7a963e263a01f3" dependencies = [ "insta", "paste", @@ -2925,10 +2925,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tb" -version = "0.7.1" +version = "0.0.0" dependencies = [ "argh", + "figment", "makemake", + "serde", "tempdir", ] diff --git a/Cargo.toml b/Cargo.toml index c980048f7b..57fa5eaf7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ members = [ "tools/cider-data-converter", "tools/calyx-pass-explorer", "tools/yxi", - "tools/calyx-writer", "tools/tb", + "tools/calyx-writer", + "tools/tb", ] exclude = ["site"] @@ -58,6 +59,7 @@ calyx-ir = { path = "calyx-ir", version = "0.7.1" } calyx-frontend = { path = "calyx-frontend", version = "0.7.1" } calyx-opt = { path = "calyx-opt", version = "0.7.1" } calyx-backend = { path = "calyx-backend", version = "0.7.1" } +figment = { version = "0.10.12", features = ["toml"] } [workspace.dependencies.petgraph] version = "0.6" diff --git a/fud2/fud-core/Cargo.toml b/fud2/fud-core/Cargo.toml index 7da316f67a..d983d68fa6 100644 --- a/fud2/fud-core/Cargo.toml +++ b/fud2/fud-core/Cargo.toml @@ -13,7 +13,7 @@ description = "Library for building declarative build tools" argh.workspace = true cranelift-entity = "0.103.0" serde.workspace = true -figment = { version = "0.10.12", features = ["toml"] } +figment.workspace = true pathdiff = { version = "0.2.1", features = ["camino"] } camino = "1.1.6" anyhow.workspace = true diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index 8a567a7d29..6cc728ce38 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -9,10 +9,12 @@ description.workspace = true categories.workspace = true homepage.workspace = true edition.workspace = true -version.workspace = true +version = "0.0.0" rust-version.workspace = true [dependencies] argh.workspace = true tempdir = "0.3.7" -makemake = "0.1.2-patch1" +makemake = "0.1.2-patch2" +figment.workspace = true +serde.workspace = true diff --git a/tools/tb/src/cli.rs b/tools/tb/src/cli.rs index 7f3622752d..af5750d976 100644 --- a/tools/tb/src/cli.rs +++ b/tools/tb/src/cli.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use argh::FromArgs; #[derive(FromArgs)] @@ -15,6 +17,10 @@ pub struct CLI { /// the testbench to invoke pub using: String, + /// path to the config file + #[argh(option, short = 'c')] + pub config: Option, + #[argh(switch)] /// displays version information pub version: bool, diff --git a/tools/tb/src/config.rs b/tools/tb/src/config.rs new file mode 100644 index 0000000000..f9ae46550e --- /dev/null +++ b/tools/tb/src/config.rs @@ -0,0 +1,120 @@ +use crate::error::{LocalError, LocalResult}; +use figment::providers::Format; +use figment::value::Value; +use figment::Figment; +use std::path::Path; + +#[derive(Debug, Clone)] +pub struct ConfigVarValidator(fn(&Value) -> LocalResult<()>); + +impl ConfigVarValidator { + pub fn new(predicate: fn(&Value) -> LocalResult<()>) -> Self { + Self(predicate) + } +} + +impl Default for ConfigVarValidator { + fn default() -> Self { + Self(|_| Ok(())) + } +} + +#[derive(Debug, Clone)] +pub struct ConfigVar { + key: String, + description: String, + validator: ConfigVarValidator, +} + +impl ConfigVar { + pub(crate) fn from, T: AsRef>( + key: S, + description: T, + validator: ConfigVarValidator, + ) -> Self { + Self { + key: key.as_ref().to_string(), + description: description.as_ref().to_string(), + validator, + } + } + + pub fn key(&self) -> &String { + &self.key + } + + pub fn description(&self) -> &String { + &self.description + } + + pub fn validate(&self, value: &Value) -> LocalResult<()> { + self.validator.0(value) + } +} + +pub struct Config { + /// DO NOT USE DIRECTLY. use [`Config::get`] instead. + figment: Figment, + profile: String, // since figment doesn't want to work + required: Vec, +} + +impl Config { + pub fn from, S: AsRef>( + path: P, + profile: S, + ) -> LocalResult { + use figment::providers::Toml; + let toml = Toml::file(path); + Ok(Self { + figment: Figment::from(toml), + profile: profile.as_ref().to_string(), + required: Vec::new(), + }) + } + + pub fn get>(&self, key: S) -> LocalResult { + self.figment + .find_value(&self.fix_key(key)) + .map_err(Into::into) + } + + pub fn require, T: AsRef, V: Into>( + &mut self, + key: S, + default: Option, + description: T, + validator: ConfigVarValidator, + ) { + if let Some(default) = default { + if self.get(key.as_ref()).is_err() { + let new_figment = std::mem::take(&mut self.figment); + self.figment = new_figment + .join((self.fix_key(key.as_ref()), default.into())); + } + } + self.required + .push(ConfigVar::from(key, description, validator)); + } + + pub(crate) fn doctor(&self) -> LocalResult<()> { + let mut errors = vec![]; + for required_key in &self.required { + match self.get(&required_key.key) { + Ok(value) => required_key.validate(&value)?, + Err(error) => { + errors.push((required_key.clone(), Box::new(error))) + } + } + } + if errors.is_empty() { + Ok(()) + } else { + Err(LocalError::MissingConfig(errors)) + } + } + + fn fix_key>(&self, key: S) -> String { + format!("{}.{}", self.profile, key.as_ref()) + } +} diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs new file mode 100644 index 0000000000..146580f8ba --- /dev/null +++ b/tools/tb/src/driver.rs @@ -0,0 +1,80 @@ +use tempdir::TempDir; + +use crate::{ + config::Config, + error::{LocalError, LocalResult}, + testbench::{cocotb, verilator, TestbenchRef}, +}; +use std::{ + collections::HashMap, + fs, + path::{Path, PathBuf}, +}; + +#[derive(Default)] +pub struct Driver { + tbs: HashMap, +} + +impl Driver { + pub fn new() -> Self { + let mut new_self = Self::default(); + new_self.register("cocotb", Box::new(cocotb::CocoTB)); + new_self.register("verilator", Box::new(verilator::Verilator)); + new_self + } + + pub fn register>(&mut self, name: S, tb: TestbenchRef) { + assert!( + self.tbs.insert(name.as_ref().to_string(), tb).is_none(), + "cannot re-register the same testbench name for a different testbench" + ); + } + + pub fn run, P: AsRef>( + &self, + name: S, + path: P, + input: String, + tests: &[String], + ) -> LocalResult<()> { + if let Some(tb) = self.tbs.get(name.as_ref()) { + let work_dir = + TempDir::new(".calyx-tb").map_err(LocalError::from)?; + let mut config = Config::from(path, name)?; + let input = + copy_into(input, &work_dir).map_err(LocalError::from)?; + let mut test_basenames = vec![]; + for test in tests { + test_basenames.push( + copy_into(test, &work_dir).map_err(LocalError::from)?, + ); + } + tb.setup(&mut config)?; + config.doctor()?; + tb.run(input, &test_basenames, work_dir, &config) + } else { + Err(LocalError::Other(format!( + "Unknown testbench '{}'", + name.as_ref() + ))) + } + } +} + +fn copy_into>( + file: S, + work_dir: &TempDir, +) -> std::io::Result { + let from_path = PathBuf::from(file.as_ref()); + let basename = from_path + .file_name() + .expect("path ended with ..") + .to_str() + .expect("invalid unicode") + .to_string(); + let mut to_path = work_dir.path().to_path_buf(); + to_path.push(&basename); + fs::copy(from_path, to_path)?; + Ok(basename) +} diff --git a/tools/tb/src/error.rs b/tools/tb/src/error.rs new file mode 100644 index 0000000000..5ac7edbcd7 --- /dev/null +++ b/tools/tb/src/error.rs @@ -0,0 +1,52 @@ +use crate::config::ConfigVar; +use std::fmt::Display; + +#[derive(Debug)] +pub enum LocalError { + IO(std::io::Error), + Figment(figment::Error), + MissingConfig(Vec<(ConfigVar, Box)>), + Other(String), +} + +impl LocalError { + pub fn other>(msg: S) -> Self { + Self::Other(msg.as_ref().to_string()) + } +} + +impl From for LocalError { + fn from(value: std::io::Error) -> Self { + Self::IO(value) + } +} + +impl From for LocalError { + fn from(value: figment::Error) -> Self { + Self::Figment(value) + } +} + +impl Display for LocalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LocalError::IO(io_err) => io_err.fmt(f), + LocalError::Figment(figment_err) => figment_err.fmt(f), + LocalError::MissingConfig(errors) => { + writeln!(f, "We detected some errors in your config:")?; + for (config_var, _) in errors { + writeln!( + f, + "- missing key '{}': {}", + config_var.key(), + config_var.description() + )?; + } + Ok(()) + } + LocalError::Other(msg) => msg.fmt(f), + } + } +} + +pub type LocalResult = Result; diff --git a/tools/tb/src/lib.rs b/tools/tb/src/lib.rs index ab2984f0d9..75a8a879ca 100644 --- a/tools/tb/src/lib.rs +++ b/tools/tb/src/lib.rs @@ -1,2 +1,5 @@ pub mod cli; +pub mod config; +pub mod driver; +pub mod error; pub mod testbench; diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs index 00889773c1..bb0cbc3c24 100644 --- a/tools/tb/src/main.rs +++ b/tools/tb/src/main.rs @@ -1,9 +1,13 @@ +use std::{env, path::PathBuf}; use tb::{ cli::CLI, - testbench::{TestbenchManager, TestbenchResult}, + driver::Driver, + error::{LocalError, LocalResult}, }; -fn main() -> TestbenchResult { +const CONFIG_FILE_NAME: &str = "calyx-tb.toml"; + +fn main() -> LocalResult<()> { let args: CLI = argh::from_env(); if args.version { @@ -18,6 +22,24 @@ fn main() -> TestbenchResult { return Ok(()); } - let tbm = TestbenchManager::new(); - tbm.run(args.using, args.input, &args.tests) + let config_path = match args.config { + Some(config_path) => config_path, + None => { + let mut config_path = + PathBuf::from(env::var("HOME").expect("user has no $HOME :(")); + config_path.push(".config"); + config_path.push(CONFIG_FILE_NAME); + config_path + } + }; + + if !config_path.exists() { + return Err(LocalError::other(format!( + "missing config file {}", + config_path.to_string_lossy() + ))); + } + + let driver = Driver::new(); + driver.run(args.using, config_path, args.input, &args.tests) } diff --git a/tools/tb/src/testbench.rs b/tools/tb/src/testbench.rs index 0ecea68039..26915c2e2a 100644 --- a/tools/tb/src/testbench.rs +++ b/tools/tb/src/testbench.rs @@ -1,20 +1,12 @@ -use std::{collections::HashMap, fs, path::PathBuf}; +use crate::{config::Config, error::LocalResult}; use tempdir::TempDir; pub mod cocotb; pub mod verilator; -pub type TestbenchResult = std::io::Result<()>; - -fn testbench_error_unknown_tb>(tb: S) -> TestbenchResult { - TestbenchResult::Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Unknown testbench '{}'", tb.as_ref()), - )) -} - pub trait Testbench { - // TODO: add a config system here + fn setup(&self, config: &mut Config) -> LocalResult<()>; + /// - `input` is a relative path to the input file in `work_dir`. /// - `tests` are a relative paths to the testing harnesses in `work_dir`. fn run( @@ -22,64 +14,8 @@ pub trait Testbench { input: String, tests: &[String], work_dir: TempDir, - ) -> TestbenchResult; + config: &Config, + ) -> LocalResult<()>; } pub type TestbenchRef = Box; - -#[derive(Default)] -pub struct TestbenchManager { - tbs: HashMap, -} - -impl TestbenchManager { - pub fn new() -> Self { - let mut new_self = Self::default(); - new_self.register("cocotb", Box::new(cocotb::CocoTB)); - new_self.register("verilator", Box::new(verilator::Verilator)); - new_self - } - - pub fn register>(&mut self, name: S, tb: TestbenchRef) { - assert!( - self.tbs.insert(name.as_ref().to_string(), tb).is_none(), - "cannot re-register the same testbench name for a different testbench" - ); - } - - pub fn run>( - &self, - name: S, - input: String, - tests: &[String], - ) -> TestbenchResult { - if let Some(tb) = self.tbs.get(name.as_ref()) { - let work_dir = TempDir::new(".tb")?; - let input = copy_into(input, &work_dir)?; - let mut test_basenames = vec![]; - for test in tests { - test_basenames.push(copy_into(test, &work_dir)?); - } - tb.run(input, &test_basenames, work_dir) - } else { - testbench_error_unknown_tb(name) - } - } -} - -fn copy_into>( - file: S, - work_dir: &TempDir, -) -> std::io::Result { - let from_path = PathBuf::from(file.as_ref()); - let basename = from_path - .file_name() - .expect("path ended with ..") - .to_str() - .expect("invalid unicode") - .to_string(); - let mut to_path = work_dir.path().to_path_buf(); - to_path.push(&basename); - fs::copy(from_path, to_path)?; - Ok(basename) -} diff --git a/tools/tb/src/testbench/cocotb.rs b/tools/tb/src/testbench/cocotb.rs index 5b1bb37e5e..79a153b3c5 100644 --- a/tools/tb/src/testbench/cocotb.rs +++ b/tools/tb/src/testbench/cocotb.rs @@ -1,11 +1,19 @@ -use makemake::{emitter::Emitter, makefile::Makefile}; - use super::Testbench; +use crate::{ + config::{Config, ConfigVarValidator}, + error::{LocalError, LocalResult}, +}; +use makemake::{emitter::Emitter, makefile::Makefile}; +use std::process::Command; use std::{fs, io::Write, path::Path}; /// v1.8.1 cocotb pub struct CocoTB; +mod config_keys { + pub const EXE: &str = "cocotb-config.exe"; +} + fn filestem(path_str: &str) -> &str { let path = Path::new(path_str); path.file_stem() @@ -15,12 +23,41 @@ fn filestem(path_str: &str) -> &str { } impl Testbench for CocoTB { + fn setup(&self, config: &mut Config) -> LocalResult<()> { + config.require( + config_keys::EXE, + Some("cocotb-config"), + "path to cocotb-config executable", + ConfigVarValidator::new(|value| { + if let Some(cmd) = value.as_str() { + let output = Command::new(cmd) + .arg("--version") + .output() + .map_err(LocalError::from).map_err(|_| LocalError::other(format!("{} is not the cocotb-config executable", cmd)))?; + let version = String::from_utf8(output.stdout) + .map_err(|_| LocalError::other(format!("{} is not the cocotb-config executable", cmd)))?; + if version.trim() != "1.8.1" { + Err(LocalError::other("cocotb-config must be version 1.8.1")) + } else { + Ok(()) + } + } else { + Err(LocalError::other( + "the cocotb-config executable path must be specified as a string", + )) + } + }), + ); + Ok(()) + } + fn run( &self, input: String, tests: &[String], work_dir: tempdir::TempDir, - ) -> super::TestbenchResult { + config: &Config, + ) -> LocalResult<()> { for test in tests { // copied from https://github.com/cocotb/cocotb/blob/v1.8.1/examples/doc_examples/quickstart/Makefile let mut makefile = Makefile::new(); @@ -44,15 +81,17 @@ impl Testbench for CocoTB { makefile.comment( "include cocotb's make rules to take care of the simulator setup", ); - makefile.include("$(shell cocotb-config --makefiles)/Makefile.sim"); + makefile.include(format!( + "$(shell {} --makefiles)/Makefile.sim", + config.get(config_keys::EXE)?.as_str().unwrap() + )); let mut makefile_path = work_dir.path().to_path_buf(); makefile_path.push("Makefile"); fs::write(makefile_path, makefile.build())?; - let output = std::process::Command::new("make") - .current_dir(work_dir.path()) - .output()?; + let output = + Command::new("make").current_dir(work_dir.path()).output()?; std::io::stdout().write_all(&output.stdout)?; std::io::stderr().write_all(&output.stderr)?; } diff --git a/tools/tb/src/testbench/verilator.rs b/tools/tb/src/testbench/verilator.rs index 6a05f91c87..46a036cfce 100644 --- a/tools/tb/src/testbench/verilator.rs +++ b/tools/tb/src/testbench/verilator.rs @@ -1,14 +1,33 @@ use super::Testbench; +use crate::{ + config::{Config, ConfigVarValidator}, + error::LocalResult, +}; + +mod config_keys { + pub const EXE: &str = "verilator-exe"; +} pub struct Verilator; impl Testbench for Verilator { + fn setup(&self, config: &mut Config) -> LocalResult<()> { + config.require( + config_keys::EXE, + Some("verilator"), + "path to verilator executable", + ConfigVarValidator::default(), + ); + Ok(()) + } + fn run( &self, _input: String, _tests: &[String], _work_dir: tempdir::TempDir, - ) -> super::TestbenchResult { - todo!() + _config: &Config, + ) -> LocalResult<()> { + todo!("verilator not yet impl") } } From 68683d4ad25806e1293f4f39ee17f5a07fe1781b Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:54:27 -0400 Subject: [PATCH 07/41] Switch to plugin model --- .gitignore | 5 +- Cargo.lock | 29 ++++++- Cargo.toml | 3 + test.futil | 17 ----- tools/tb/Cargo.toml | 3 +- tools/tb/Makefile | 13 +++- tools/tb/plugins/cocotb/Cargo.toml | 11 +++ .../cocotb.rs => plugins/cocotb/lib.rs} | 26 +++++-- tools/tb/plugins/verilator/Cargo.toml | 11 +++ .../verilator.rs => plugins/verilator/lib.rs} | 19 ++++- tools/tb/src/driver.rs | 75 +++++++++++++++---- tools/tb/src/lib.rs | 4 +- tools/tb/src/main.rs | 7 +- tools/tb/src/plugin.rs | 43 +++++++++++ tools/tb/src/testbench.rs | 21 ------ 15 files changed, 219 insertions(+), 68 deletions(-) delete mode 100644 test.futil create mode 100644 tools/tb/plugins/cocotb/Cargo.toml rename tools/tb/{src/testbench/cocotb.rs => plugins/cocotb/lib.rs} (91%) create mode 100644 tools/tb/plugins/verilator/Cargo.toml rename tools/tb/{src/testbench/verilator.rs => plugins/verilator/lib.rs} (66%) create mode 100644 tools/tb/src/plugin.rs delete mode 100644 tools/tb/src/testbench.rs diff --git a/.gitignore b/.gitignore index d148072ea7..1990cbd0ff 100644 --- a/.gitignore +++ b/.gitignore @@ -61,5 +61,8 @@ tools/profiler/logs temp/ +tools/tb/**/*.so +tools/tb/**/*.dylib + # for running a venv -.venv \ No newline at end of file +.venv diff --git a/Cargo.lock b/Cargo.lock index 19fb04de16..272f19c1c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -710,6 +710,14 @@ dependencies = [ "winapi", ] +[[package]] +name = "cocotb-tb-plugin" +version = "0.0.0" +dependencies = [ + "makemake", + "tb", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -1693,9 +1701,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -2559,6 +2567,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.197" @@ -2929,7 +2943,8 @@ version = "0.0.0" dependencies = [ "argh", "figment", - "makemake", + "libloading", + "semver", "serde", "tempdir", ] @@ -3389,6 +3404,14 @@ dependencies = [ "pretty", ] +[[package]] +name = "verilator-tb-plugin" +version = "0.0.0" +dependencies = [ + "makemake", + "tb", +] + [[package]] name = "version_check" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index 57fa5eaf7e..902a53c1b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ members = [ "tools/yxi", "tools/calyx-writer", "tools/tb", + "tools/tb/plugins/cocotb", + "tools/tb/plugins/verilator", ] exclude = ["site"] @@ -60,6 +62,7 @@ calyx-frontend = { path = "calyx-frontend", version = "0.7.1" } calyx-opt = { path = "calyx-opt", version = "0.7.1" } calyx-backend = { path = "calyx-backend", version = "0.7.1" } figment = { version = "0.10.12", features = ["toml"] } +semver = "1.0.23" [workspace.dependencies.petgraph] version = "0.6" diff --git a/test.futil b/test.futil deleted file mode 100644 index 4598bac56a..0000000000 --- a/test.futil +++ /dev/null @@ -1,17 +0,0 @@ -component main() -> () { - cells {} - wires { - static<1> group a { - - } - } - control { - seq { - seq { - seq { - a; - } - } - } - } -} diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index 6cc728ce38..d6223116d4 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -15,6 +15,7 @@ rust-version.workspace = true [dependencies] argh.workspace = true tempdir = "0.3.7" -makemake = "0.1.2-patch2" figment.workspace = true serde.workspace = true +semver.workspace = true +libloading = "0.8.4" diff --git a/tools/tb/Makefile b/tools/tb/Makefile index e2d46589c9..cc1bf1f7f7 100644 --- a/tools/tb/Makefile +++ b/tools/tb/Makefile @@ -1,7 +1,18 @@ TARGET = tb -.PHONY: $(TARGET) +.PHONY: $(TARGET) | plugins $(TARGET): cargo build --manifest-path Cargo.toml printf "../../target/debug/tb \$$@\n" > $@ chmod u+x $@ + +.PHONY: plugins +plugins: + for dir in plugins/*; do \ + if [ -d "$$dir" ]; then \ + cargo build --manifest-path "$$dir/Cargo.toml" --release --target-dir "$$dir/target"; \ + for file in "$$dir/build/release/"*.{dylib,so}; do \ + [ -f "$$file" ] && mv "$$file" plugins; \ + done \ + fi \ + done diff --git a/tools/tb/plugins/cocotb/Cargo.toml b/tools/tb/plugins/cocotb/Cargo.toml new file mode 100644 index 0000000000..8304654c70 --- /dev/null +++ b/tools/tb/plugins/cocotb/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cocotb-tb-plugin" +edition = "2021" + +[lib] +path = "lib.rs" +crate-type = ["cdylib"] + +[dependencies] +tb = { path = "../..", version = "0.0.0" } +makemake = "0.1.2-patch2" diff --git a/tools/tb/src/testbench/cocotb.rs b/tools/tb/plugins/cocotb/lib.rs similarity index 91% rename from tools/tb/src/testbench/cocotb.rs rename to tools/tb/plugins/cocotb/lib.rs index 79a153b3c5..2abf8a69e5 100644 --- a/tools/tb/src/testbench/cocotb.rs +++ b/tools/tb/plugins/cocotb/lib.rs @@ -1,13 +1,17 @@ -use super::Testbench; -use crate::{ - config::{Config, ConfigVarValidator}, - error::{LocalError, LocalResult}, -}; use makemake::{emitter::Emitter, makefile::Makefile}; use std::process::Command; use std::{fs, io::Write, path::Path}; +use tb::declare_plugin; +use tb::error::LocalError; +use tb::{ + config::{Config, ConfigVarValidator}, + error::LocalResult, + plugin::Plugin, + semver, tempdir, +}; /// v1.8.1 cocotb +#[derive(Default)] pub struct CocoTB; mod config_keys { @@ -22,7 +26,15 @@ fn filestem(path_str: &str) -> &str { .expect("invalid unicode") } -impl Testbench for CocoTB { +impl Plugin for CocoTB { + fn name(&self) -> &'static str { + "cocotb" + } + + fn version(&self) -> semver::Version { + semver::Version::new(0, 0, 0) + } + fn setup(&self, config: &mut Config) -> LocalResult<()> { config.require( config_keys::EXE, @@ -99,3 +111,5 @@ impl Testbench for CocoTB { Ok(()) } } + +declare_plugin!(CocoTB, CocoTB::default); diff --git a/tools/tb/plugins/verilator/Cargo.toml b/tools/tb/plugins/verilator/Cargo.toml new file mode 100644 index 0000000000..89a8bf26f9 --- /dev/null +++ b/tools/tb/plugins/verilator/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "verilator-tb-plugin" +edition = "2021" + +[lib] +path = "lib.rs" +crate-type = ["cdylib"] + +[dependencies] +tb = { path = "../..", version = "0.0.0" } +makemake = "0.1.2-patch2" diff --git a/tools/tb/src/testbench/verilator.rs b/tools/tb/plugins/verilator/lib.rs similarity index 66% rename from tools/tb/src/testbench/verilator.rs rename to tools/tb/plugins/verilator/lib.rs index 46a036cfce..797bf5a2c3 100644 --- a/tools/tb/src/testbench/verilator.rs +++ b/tools/tb/plugins/verilator/lib.rs @@ -1,16 +1,27 @@ -use super::Testbench; -use crate::{ +use tb::{ config::{Config, ConfigVarValidator}, + declare_plugin, error::LocalResult, + plugin::Plugin, + semver, tempdir, }; mod config_keys { pub const EXE: &str = "verilator-exe"; } +#[derive(Default)] pub struct Verilator; -impl Testbench for Verilator { +impl Plugin for Verilator { + fn name(&self) -> &'static str { + "verilator" + } + + fn version(&self) -> semver::Version { + semver::Version::new(0, 0, 0) + } + fn setup(&self, config: &mut Config) -> LocalResult<()> { config.require( config_keys::EXE, @@ -31,3 +42,5 @@ impl Testbench for Verilator { todo!("verilator not yet impl") } } + +declare_plugin!(Verilator, Verilator::default); diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs index 146580f8ba..f0b006a1b7 100644 --- a/tools/tb/src/driver.rs +++ b/tools/tb/src/driver.rs @@ -1,36 +1,76 @@ -use tempdir::TempDir; - use crate::{ config::Config, error::{LocalError, LocalResult}, - testbench::{cocotb, verilator, TestbenchRef}, + plugin::{PluginCreate, PluginRef}, }; +use libloading::{Library, Symbol}; use std::{ collections::HashMap, fs, path::{Path, PathBuf}, }; +use tempdir::TempDir; #[derive(Default)] pub struct Driver { - tbs: HashMap, + plugins: HashMap, + loaded_libraries: Vec, } impl Driver { - pub fn new() -> Self { + pub fn load(library_dirs: &[PathBuf]) -> LocalResult { let mut new_self = Self::default(); - new_self.register("cocotb", Box::new(cocotb::CocoTB)); - new_self.register("verilator", Box::new(verilator::Verilator)); - new_self + for library_dir in library_dirs { + library_dir + .read_dir() + .map_err(LocalError::from) + .and_then(|library_paths| { + for library_path in library_paths { + let library_path = + library_path.map_err(LocalError::from)?.path(); + if library_path + .extension() + .map(|e| e == "so" || e == "dylib") + .unwrap_or_default() + { + let library = + unsafe { Library::new(&library_path).unwrap() }; + new_self.load_plugin(&library_path, library)?; + } + } + Ok(()) + }) + .map_err(LocalError::from)?; + } + Ok(new_self) } - pub fn register>(&mut self, name: S, tb: TestbenchRef) { + pub fn register>(&mut self, name: S, tb: PluginRef) { assert!( - self.tbs.insert(name.as_ref().to_string(), tb).is_none(), + self.plugins.insert(name.as_ref().to_string(), tb).is_none(), "cannot re-register the same testbench name for a different testbench" ); } + fn load_plugin( + &mut self, + path: &Path, + library: Library, + ) -> LocalResult<()> { + let create_plugin: Symbol = + unsafe { library.get(b"_plugin_create") }.map_err(|_| { + LocalError::other(format!( + "Plugin '{}' must `declare_plugin!`.", + extract_plugin_name(path) + )) + })?; + let boxed_raw = unsafe { create_plugin() }; + let plugin = unsafe { Box::from_raw(boxed_raw) }; + self.register(plugin.name(), plugin); + self.loaded_libraries.push(library); + Ok(()) + } + pub fn run, P: AsRef>( &self, name: S, @@ -38,7 +78,7 @@ impl Driver { input: String, tests: &[String], ) -> LocalResult<()> { - if let Some(tb) = self.tbs.get(name.as_ref()) { + if let Some(plugin) = self.plugins.get(name.as_ref()) { let work_dir = TempDir::new(".calyx-tb").map_err(LocalError::from)?; let mut config = Config::from(path, name)?; @@ -50,9 +90,9 @@ impl Driver { copy_into(test, &work_dir).map_err(LocalError::from)?, ); } - tb.setup(&mut config)?; + plugin.setup(&mut config)?; config.doctor()?; - tb.run(input, &test_basenames, work_dir, &config) + plugin.run(input, &test_basenames, work_dir, &config) } else { Err(LocalError::Other(format!( "Unknown testbench '{}'", @@ -78,3 +118,12 @@ fn copy_into>( fs::copy(from_path, to_path)?; Ok(basename) } + +fn extract_plugin_name(path: &Path) -> &str { + let stem = path + .file_stem() + .expect("invalid library path") + .to_str() + .expect("invalid unicode"); + stem.strip_prefix("lib").unwrap_or(stem) +} diff --git a/tools/tb/src/lib.rs b/tools/tb/src/lib.rs index 75a8a879ca..530a6b34b4 100644 --- a/tools/tb/src/lib.rs +++ b/tools/tb/src/lib.rs @@ -2,4 +2,6 @@ pub mod cli; pub mod config; pub mod driver; pub mod error; -pub mod testbench; +pub mod plugin; +pub use semver; +pub use tempdir; diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs index bb0cbc3c24..24d7752398 100644 --- a/tools/tb/src/main.rs +++ b/tools/tb/src/main.rs @@ -40,6 +40,11 @@ fn main() -> LocalResult<()> { ))); } - let driver = Driver::new(); + let default_loc = { + let mut default_loc = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + default_loc.push("plugins"); + default_loc + }; + let driver = Driver::load(&[default_loc])?; driver.run(args.using, config_path, args.input, &args.tests) } diff --git a/tools/tb/src/plugin.rs b/tools/tb/src/plugin.rs new file mode 100644 index 0000000000..2a06b96e17 --- /dev/null +++ b/tools/tb/src/plugin.rs @@ -0,0 +1,43 @@ +use crate::{config::Config, error::LocalResult}; +use semver::Version; +use tempdir::TempDir; + +pub trait Plugin: Send + Sync { + /// A unique name for this plugin. + fn name(&self) -> &'static str; + + /// The version of tb this plugin was built for. + fn version(&self) -> Version; + + /// Declares the configuration for this plugin. + fn setup(&self, config: &mut Config) -> LocalResult<()>; + + /// Runs this plugin's testbench. + /// - `input` is a relative path to the input file in `work_dir`. + /// - `tests` are a relative paths to the testing harnesses in `work_dir`. + fn run( + &self, + input: String, + tests: &[String], + work_dir: TempDir, + config: &Config, + ) -> LocalResult<()>; +} + +pub type PluginRef = Box; + +// https://www.michaelfbryan.com/rust-ffi-guide/dynamic_loading.html +pub type PluginCreate = unsafe fn() -> *mut dyn Plugin; + +/// `declare_plugin!(MyPlugin, MyPlugin::constructor)` exposes `MyPlugin` to the +/// world as constructable by the zero-arity `MyPlugin::constructor`. +#[macro_export] +macro_rules! declare_plugin { + ($plugin_type:ty, $constructor:path) => { + #[no_mangle] + pub extern "C" fn _plugin_create() -> *mut dyn $crate::plugin::Plugin { + let boxed: $crate::plugin::PluginRef = Box::new($constructor()); + Box::into_raw(boxed) + } + }; +} diff --git a/tools/tb/src/testbench.rs b/tools/tb/src/testbench.rs deleted file mode 100644 index 26915c2e2a..0000000000 --- a/tools/tb/src/testbench.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::{config::Config, error::LocalResult}; -use tempdir::TempDir; - -pub mod cocotb; -pub mod verilator; - -pub trait Testbench { - fn setup(&self, config: &mut Config) -> LocalResult<()>; - - /// - `input` is a relative path to the input file in `work_dir`. - /// - `tests` are a relative paths to the testing harnesses in `work_dir`. - fn run( - &self, - input: String, - tests: &[String], - work_dir: TempDir, - config: &Config, - ) -> LocalResult<()>; -} - -pub type TestbenchRef = Box; From 0d2fd7c7f079af4859367266cc7d172e85bf6023 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 2 Jul 2024 13:30:57 -0400 Subject: [PATCH 08/41] fud2 works --- fud2/fud-core/src/run.rs | 5 +++++ fud2/scripts/tb.rhai | 12 ++++++++++++ fud2/src/lib.rs | 5 +++-- tools/tb/README.md | 21 +++++++++++++++++++++ tools/tb/src/cli.rs | 21 +++++++++++++++++++-- tools/tb/src/main.rs | 11 ++--------- tools/tb/src/plugin.rs | 3 +-- 7 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 fud2/scripts/tb.rhai diff --git a/fud2/fud-core/src/run.rs b/fud2/fud-core/src/run.rs index 504c3d3645..8e21228572 100644 --- a/fud2/fud-core/src/run.rs +++ b/fud2/fud-core/src/run.rs @@ -4,6 +4,7 @@ use crate::utils::relative_path; use camino::{Utf8Path, Utf8PathBuf}; use itertools::Itertools; use std::collections::{HashMap, HashSet}; +use std::env; use std::io::Write; use std::process::{Command, ExitStatus}; @@ -405,6 +406,10 @@ impl<'a> Run<'a> { // Emit preamble. emitter.var("build-tool", &self.global_config.exe)?; emitter.rule("get-rsrc", "$build-tool get-rsrc $out")?; + emitter.var( + "caller-dir", + &env::current_dir()?.to_string_lossy().to_string(), + )?; writeln!(emitter.out)?; // Emit the setup for each operation used in the plan, only once. diff --git a/fud2/scripts/tb.rhai b/fud2/scripts/tb.rhai new file mode 100644 index 0000000000..c913817c12 --- /dev/null +++ b/fud2/scripts/tb.rhai @@ -0,0 +1,12 @@ +import "calyx" as calyx; + +let tb_state = state("tb", []); + +fn tb_setup(e) { + e.config_var("calyx-tb-exe", "tb.exe"); + e.config_var("calyx-tb-test", "tb.test"); + e.config_var("calyx-tb-config-file", "tb.config-file"); + e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using cocotb --config $calyx-tb-config-file"); +} + +rule([tb_setup], calyx::verilog_state, tb_state, "verilog-to-tb"); diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index 955df5f16f..2a0fec9303 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -88,13 +88,14 @@ fn setup_tb(bld: &mut DriverBuilder, verilog: StateRef) { let tb_setup = bld.setup("Testbench executable", |e| { e.var("calyx-tb-exe", "tb")?; e.config_var("calyx-tb-test", "tb.test")?; // todo multi input op + e.config_var("calyx-tb-config-file", "tb.config-file")?; e.rule( "calyx-to-tb", - "$calyx-tb-exe $in --test $calyx-tb-test --using cocotb", + "$calyx-tb-exe $in --test $calyx-tb-test --using cocotb --config $calyx-tb-config-file", )?; Ok(()) }); - bld.rule(&[tb_setup], verilog, tb, "calyx-to-tb"); + bld.rule(&[tb_setup], verilog, tb, "test"); } pub fn build_driver(bld: &mut DriverBuilder) { diff --git a/tools/tb/README.md b/tools/tb/README.md index 1b5a183625..f85e568920 100644 --- a/tools/tb/README.md +++ b/tools/tb/README.md @@ -1,6 +1,27 @@ +## Setup + +Run `make plugins` to build the builtin plugins (cocotb, verilator, etc.). + +## Usage + +There are two ways to use `tb`: + +### Directly + For example, if you make sure to follow the instructions under [`examples/cocotb/doc_examples_quickstart/`](examples/cocotb/doc_examples_quickstart/), ``` make ./tb examples/cocotb/doc_examples_quickstart/my_design.sv -t examples/cocotb/doc_examples_quickstart/test_my_design.py --using cocotb ``` should run `cocotb` on the input file and harness. + +### Via `fud2`: + +You can follow the above steps but invoke the following command instead. +``` +fud2 my_design.sv -s tb.test=test_my_design.py -s tb.using=cocotb --to tb +``` + +### Makefile + +I've provided a [Makefile](Makefile) in this directory for local testing. Use `make` to build the `tb` executable locally. diff --git a/tools/tb/src/cli.rs b/tools/tb/src/cli.rs index af5750d976..1251917a78 100644 --- a/tools/tb/src/cli.rs +++ b/tools/tb/src/cli.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{env, path::PathBuf}; use argh::FromArgs; @@ -9,7 +9,7 @@ pub struct CLI { /// verilog file pub input: String, - #[argh(option, short = 't')] + #[argh(option, short = 't', long = "test")] /// test harness pub tests: Vec, @@ -25,3 +25,20 @@ pub struct CLI { /// displays version information pub version: bool, } + +impl CLI { + pub fn from_env() -> Self { + let args: Vec<_> = env::args().collect(); + if args.len() == 2 && matches!(args[1].as_str(), "-v" | "--version") { + Self { + input: String::new(), + tests: Vec::new(), + using: String::new(), + config: None, + version: true, + } + } else { + argh::from_env() + } + } +} diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs index 24d7752398..6fae8d29bf 100644 --- a/tools/tb/src/main.rs +++ b/tools/tb/src/main.rs @@ -8,17 +8,10 @@ use tb::{ const CONFIG_FILE_NAME: &str = "calyx-tb.toml"; fn main() -> LocalResult<()> { - let args: CLI = argh::from_env(); + let args = CLI::from_env(); if args.version { - println!( - "{} v{}", - std::env::current_exe() - .expect("how did you call this without argv[0]??") - .to_str() - .expect("argv[0] not valid unicode"), - env!("CARGO_PKG_VERSION") - ); + println!("{}", env!("CARGO_PKG_VERSION")); return Ok(()); } diff --git a/tools/tb/src/plugin.rs b/tools/tb/src/plugin.rs index 2a06b96e17..a5471102db 100644 --- a/tools/tb/src/plugin.rs +++ b/tools/tb/src/plugin.rs @@ -36,8 +36,7 @@ macro_rules! declare_plugin { ($plugin_type:ty, $constructor:path) => { #[no_mangle] pub extern "C" fn _plugin_create() -> *mut dyn $crate::plugin::Plugin { - let boxed: $crate::plugin::PluginRef = Box::new($constructor()); - Box::into_raw(boxed) + Box::into_raw(Box::new($constructor())) } }; } From 4e479be2c716143bd3b3021b8c7bfb2230172a6f Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:09:59 -0400 Subject: [PATCH 09/41] Add logging, work more on plugin support --- Cargo.lock | 2 ++ tools/tb/Cargo.toml | 2 ++ tools/tb/Makefile | 11 ++---- tools/tb/README.md | 35 +++++++++++++++++++ tools/tb/build_plugins.py | 13 ++++++++ tools/tb/plugins/cocotb/Cargo.toml | 3 +- tools/tb/plugins/cocotb/lib.rs | 2 +- tools/tb/plugins/verilator/Cargo.toml | 3 +- tools/tb/src/config.rs | 48 ++++++++++++++++++++++----- tools/tb/src/driver.rs | 42 +++++++++++++++-------- tools/tb/src/lib.rs | 2 ++ tools/tb/src/main.rs | 17 ++++++++++ 12 files changed, 147 insertions(+), 33 deletions(-) create mode 100644 tools/tb/build_plugins.py diff --git a/Cargo.lock b/Cargo.lock index 272f19c1c3..80f31c5452 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2942,8 +2942,10 @@ name = "tb" version = "0.0.0" dependencies = [ "argh", + "env_logger", "figment", "libloading", + "log", "semver", "serde", "tempdir", diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index d6223116d4..ea3c00bdd3 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -19,3 +19,5 @@ figment.workspace = true serde.workspace = true semver.workspace = true libloading = "0.8.4" +log.workspace = true +env_logger.workspace = true diff --git a/tools/tb/Makefile b/tools/tb/Makefile index cc1bf1f7f7..512957f385 100644 --- a/tools/tb/Makefile +++ b/tools/tb/Makefile @@ -7,12 +7,5 @@ $(TARGET): chmod u+x $@ .PHONY: plugins -plugins: - for dir in plugins/*; do \ - if [ -d "$$dir" ]; then \ - cargo build --manifest-path "$$dir/Cargo.toml" --release --target-dir "$$dir/target"; \ - for file in "$$dir/build/release/"*.{dylib,so}; do \ - [ -f "$$file" ] && mv "$$file" plugins; \ - done \ - fi \ - done +plugins: build_plugins.py + $(shell which python3 || which python || which pypy3 || which pypy) $< diff --git a/tools/tb/README.md b/tools/tb/README.md index f85e568920..19a34aabe5 100644 --- a/tools/tb/README.md +++ b/tools/tb/README.md @@ -1,3 +1,11 @@ +# tb: The Calyx Testbench Tool + +## Contents + +1. Setup +2. Usage +3. Writing a Plugin + ## Setup Run `make plugins` to build the builtin plugins (cocotb, verilator, etc.). @@ -25,3 +33,30 @@ fud2 my_design.sv -s tb.test=test_my_design.py -s tb.using=cocotb --to tb ### Makefile I've provided a [Makefile](Makefile) in this directory for local testing. Use `make` to build the `tb` executable locally. + +## Writing a Plugin + +First, setup a simple rust library as you would any other, but **ensure that `lib.crate-type` is `cdylib`**. +Here, we're writing the plugin in `lib.rs`. +Remember to update the `path` in the `dependencies.tb` dependency! + +```toml +[package] +name = "my-tb-plugin" +edition = "2021" # or `edition.workspace = true` + +[lib] +path = "lib.rs" +crate-type = ["cdylib"] + +[dependencies] +tb = { path = "path/to/tb/crate", version = "0.0.0" } +``` + +In the crate, you can write any auxillary code. +However, you'll need to define at least two things: + +1. A type implementing `tb::plugin::Plugin`. +2. A `declare_plugin!` declaration to expose the plugin and its constructor to the outside world. + +It may be helpful to look at the existing plugins for reference. diff --git a/tools/tb/build_plugins.py b/tools/tb/build_plugins.py new file mode 100644 index 0000000000..6eb46b4769 --- /dev/null +++ b/tools/tb/build_plugins.py @@ -0,0 +1,13 @@ +import os +import subprocess + +for dir_name in os.listdir('plugins'): + dir_path = os.path.join('plugins', dir_name) + if os.path.isdir(dir_path): + subprocess.run(['cargo', 'build', '--manifest-path', os.path.join(dir_path, 'Cargo.toml'), '--release', '--target-dir', os.path.join(dir_path, 'target')], check=True) + release_dir = os.path.join(dir_path, 'target', 'release') + for file_name in os.listdir(release_dir): + file_path = os.path.join(release_dir, file_name) + if os.path.isfile(file_path) and (file_name.endswith('.dylib') or file_name.endswith('.so')): + os.remove(os.path.join('plugins', file_name)) + os.rename(file_path, os.path.join('plugins', file_name)) diff --git a/tools/tb/plugins/cocotb/Cargo.toml b/tools/tb/plugins/cocotb/Cargo.toml index 8304654c70..cee95eb037 100644 --- a/tools/tb/plugins/cocotb/Cargo.toml +++ b/tools/tb/plugins/cocotb/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "cocotb-tb-plugin" -edition = "2021" +edition.workspace = true +rust-version.workspace = true [lib] path = "lib.rs" diff --git a/tools/tb/plugins/cocotb/lib.rs b/tools/tb/plugins/cocotb/lib.rs index 2abf8a69e5..64c0c74069 100644 --- a/tools/tb/plugins/cocotb/lib.rs +++ b/tools/tb/plugins/cocotb/lib.rs @@ -40,7 +40,7 @@ impl Plugin for CocoTB { config_keys::EXE, Some("cocotb-config"), "path to cocotb-config executable", - ConfigVarValidator::new(|value| { + ConfigVarValidator::when(|value| { if let Some(cmd) = value.as_str() { let output = Command::new(cmd) .arg("--version") diff --git a/tools/tb/plugins/verilator/Cargo.toml b/tools/tb/plugins/verilator/Cargo.toml index 89a8bf26f9..1c791ad29a 100644 --- a/tools/tb/plugins/verilator/Cargo.toml +++ b/tools/tb/plugins/verilator/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "verilator-tb-plugin" -edition = "2021" +edition.workspace = true +rust-version.workspace = true [lib] path = "lib.rs" diff --git a/tools/tb/src/config.rs b/tools/tb/src/config.rs index f9ae46550e..08c872f6e5 100644 --- a/tools/tb/src/config.rs +++ b/tools/tb/src/config.rs @@ -2,20 +2,52 @@ use crate::error::{LocalError, LocalResult}; use figment::providers::Format; use figment::value::Value; use figment::Figment; +use std::fmt::Debug; use std::path::Path; +use std::rc::Rc; -#[derive(Debug, Clone)] -pub struct ConfigVarValidator(fn(&Value) -> LocalResult<()>); +pub type ConfigVarValidatorPredicate = fn(&Value) -> LocalResult<()>; + +pub struct ConfigVarValidator { + predicates: Vec, +} impl ConfigVarValidator { - pub fn new(predicate: fn(&Value) -> LocalResult<()>) -> Self { - Self(predicate) + pub fn when(predicate: ConfigVarValidatorPredicate) -> Self { + Self { + predicates: vec![predicate], + } + } + + pub fn and(mut self, predicate: ConfigVarValidatorPredicate) -> Self { + self.predicates.push(predicate); + self + } + + pub(crate) fn run(&self, value: &Value) -> LocalResult<()> { + self.predicates + .iter() + .try_for_each(|predicate| predicate(value)) } } impl Default for ConfigVarValidator { fn default() -> Self { - Self(|_| Ok(())) + Self::when(|_| Ok(())) + } +} + +impl Debug for ConfigVarValidator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ConfigVarValidator {{ predicates: vec![{}] }}", + self.predicates + .iter() + .map(|p| format!("{:p}", p)) + .collect::>() + .join(", ") + ) } } @@ -23,7 +55,7 @@ impl Default for ConfigVarValidator { pub struct ConfigVar { key: String, description: String, - validator: ConfigVarValidator, + validator: Rc, } impl ConfigVar { @@ -35,7 +67,7 @@ impl ConfigVar { Self { key: key.as_ref().to_string(), description: description.as_ref().to_string(), - validator, + validator: Rc::new(validator), } } @@ -48,7 +80,7 @@ impl ConfigVar { } pub fn validate(&self, value: &Value) -> LocalResult<()> { - self.validator.0(value) + self.validator.run(value) } } diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs index f0b006a1b7..cbc8495bba 100644 --- a/tools/tb/src/driver.rs +++ b/tools/tb/src/driver.rs @@ -4,6 +4,7 @@ use crate::{ plugin::{PluginCreate, PluginRef}, }; use libloading::{Library, Symbol}; +use semver::VersionReq; use std::{ collections::HashMap, fs, @@ -18,29 +19,34 @@ pub struct Driver { } impl Driver { - pub fn load(library_dirs: &[PathBuf]) -> LocalResult { + pub fn load(plugin_dirs: &[PathBuf]) -> LocalResult { let mut new_self = Self::default(); - for library_dir in library_dirs { - library_dir - .read_dir() - .map_err(LocalError::from) - .and_then(|library_paths| { + for plugin_dir in plugin_dirs { + match plugin_dir.read_dir().map_err(LocalError::from) { + Ok(library_paths) => { for library_path in library_paths { let library_path = library_path.map_err(LocalError::from)?.path(); - if library_path - .extension() - .map(|e| e == "so" || e == "dylib") - .unwrap_or_default() + if library_path.is_file() + && library_path + .extension() + .map(|e| e == "so" || e == "dylib") + .unwrap_or_default() { let library = unsafe { Library::new(&library_path).unwrap() }; new_self.load_plugin(&library_path, library)?; } } - Ok(()) - }) - .map_err(LocalError::from)?; + } + Err(error) => { + log::warn!( + "Error processing plugin directory {}: {}", + plugin_dir.to_string_lossy(), + error + ) + } + } } Ok(new_self) } @@ -57,6 +63,11 @@ impl Driver { path: &Path, library: Library, ) -> LocalResult<()> { + // todo: better way to do this + let req = + VersionReq::parse(&format!(">={}", env!("CARGO_PKG_VERSION"))) + .unwrap(); + let create_plugin: Symbol = unsafe { library.get(b"_plugin_create") }.map_err(|_| { LocalError::other(format!( @@ -66,6 +77,11 @@ impl Driver { })?; let boxed_raw = unsafe { create_plugin() }; let plugin = unsafe { Box::from_raw(boxed_raw) }; + let plugin_version = plugin.version(); + if !req.matches(&plugin_version) { + log::warn!("Skipping loading {} because its version ({}) is not compatible with {}", plugin.name(), plugin_version, req); + return Ok(()); + } self.register(plugin.name(), plugin); self.loaded_libraries.push(library); Ok(()) diff --git a/tools/tb/src/lib.rs b/tools/tb/src/lib.rs index 530a6b34b4..e3b226d86f 100644 --- a/tools/tb/src/lib.rs +++ b/tools/tb/src/lib.rs @@ -1,3 +1,5 @@ +//! Author: Ethan Uppal + pub mod cli; pub mod config; pub mod driver; diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs index 6fae8d29bf..53d8d53770 100644 --- a/tools/tb/src/main.rs +++ b/tools/tb/src/main.rs @@ -7,7 +7,20 @@ use tb::{ const CONFIG_FILE_NAME: &str = "calyx-tb.toml"; +fn setup_logging() { + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "warn"); + } + if env::var("NO_COLOR").is_err() { + env::set_var("RUST_LOG_STYLE", "always"); + } + + env_logger::builder().format_target(false).init(); +} + fn main() -> LocalResult<()> { + setup_logging(); + let args = CLI::from_env(); if args.version { @@ -18,6 +31,10 @@ fn main() -> LocalResult<()> { let config_path = match args.config { Some(config_path) => config_path, None => { + log::info!( + "No config file specified, using default: {}", + CONFIG_FILE_NAME + ); let mut config_path = PathBuf::from(env::var("HOME").expect("user has no $HOME :(")); config_path.push(".config"); From aecec12cdccb981646515ade98f73ff256ca9126 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:10:00 -0400 Subject: [PATCH 10/41] Update cocotb config - Migrate to makemake 0.3 - Improve error handling in config and main - Fix Makefile/plugin building --- Cargo.lock | 39 ++++++++++++++++---------- tools/tb/Makefile | 8 +++--- tools/tb/build_plugins.py | 29 ++++++++++++++----- tools/tb/plugins/cocotb/Cargo.toml | 2 +- tools/tb/plugins/cocotb/lib.rs | 45 ++++++++++++++++++++++++++---- tools/tb/src/config.rs | 22 ++++++++++++--- tools/tb/src/error.rs | 32 ++++++++++++++------- tools/tb/src/main.rs | 34 ++++++++++++++-------- 8 files changed, 154 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80f31c5452..b4f2b6ee2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,16 @@ version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +[[package]] +name = "cargo_toml" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4895c018bb228aa6b3ba1a0285543fcb4b704734c3fb1f72afaa75aa769500c1" +dependencies = [ + "serde", + "toml", +] + [[package]] name = "cast" version = "0.3.0" @@ -1769,10 +1779,11 @@ dependencies = [ [[package]] name = "makemake" -version = "0.1.2-patch2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf7ddede5f0086f7b56de8bb01946a5ac6438a92c4ae9c1e7a963e263a01f3" +checksum = "fe265024d547dbfdae86fdb24b3ea7b96aff8cc71947d08b61d0db2ce2829e05" dependencies = [ + "cargo_toml", "insta", "paste", ] @@ -2575,9 +2586,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -2594,9 +2605,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -2637,9 +2648,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -3147,9 +3158,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -3159,18 +3170,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap 2.2.4", "serde", diff --git a/tools/tb/Makefile b/tools/tb/Makefile index 512957f385..2dc33d17c0 100644 --- a/tools/tb/Makefile +++ b/tools/tb/Makefile @@ -1,11 +1,11 @@ TARGET = tb -.PHONY: $(TARGET) | plugins -$(TARGET): +.PHONY: $(TARGET) +$(TARGET): plugins cargo build --manifest-path Cargo.toml printf "../../target/debug/tb \$$@\n" > $@ chmod u+x $@ .PHONY: plugins -plugins: build_plugins.py - $(shell which python3 || which python || which pypy3 || which pypy) $< +plugins: + $(shell which python3 || which python || which pypy3 || which pypy) build_plugins.py diff --git a/tools/tb/build_plugins.py b/tools/tb/build_plugins.py index 6eb46b4769..99d9235095 100644 --- a/tools/tb/build_plugins.py +++ b/tools/tb/build_plugins.py @@ -1,13 +1,28 @@ import os import subprocess -for dir_name in os.listdir('plugins'): - dir_path = os.path.join('plugins', dir_name) +for dir_name in os.listdir("plugins"): + dir_path = os.path.join("plugins", dir_name) if os.path.isdir(dir_path): - subprocess.run(['cargo', 'build', '--manifest-path', os.path.join(dir_path, 'Cargo.toml'), '--release', '--target-dir', os.path.join(dir_path, 'target')], check=True) - release_dir = os.path.join(dir_path, 'target', 'release') + subprocess.run( + [ + "cargo", + "build", + "--manifest-path", + os.path.join(dir_path, "Cargo.toml"), + "--release", + "--target-dir", + os.path.join(dir_path, "target"), + ], + check=True, + ) + release_dir = os.path.join(dir_path, "target", "release") for file_name in os.listdir(release_dir): file_path = os.path.join(release_dir, file_name) - if os.path.isfile(file_path) and (file_name.endswith('.dylib') or file_name.endswith('.so')): - os.remove(os.path.join('plugins', file_name)) - os.rename(file_path, os.path.join('plugins', file_name)) + if os.path.isfile(file_path) and ( + file_name.endswith(".dylib") or file_name.endswith(".so") + ): + dest_path = os.path.join("plugins", file_name) + if os.path.isfile(dest_path): + os.remove(dest_path) + os.rename(file_path, dest_path) diff --git a/tools/tb/plugins/cocotb/Cargo.toml b/tools/tb/plugins/cocotb/Cargo.toml index cee95eb037..1ed8cb24e7 100644 --- a/tools/tb/plugins/cocotb/Cargo.toml +++ b/tools/tb/plugins/cocotb/Cargo.toml @@ -9,4 +9,4 @@ crate-type = ["cdylib"] [dependencies] tb = { path = "../..", version = "0.0.0" } -makemake = "0.1.2-patch2" +makemake = "0.1.3" diff --git a/tools/tb/plugins/cocotb/lib.rs b/tools/tb/plugins/cocotb/lib.rs index 64c0c74069..34024356a1 100644 --- a/tools/tb/plugins/cocotb/lib.rs +++ b/tools/tb/plugins/cocotb/lib.rs @@ -1,6 +1,7 @@ use makemake::{emitter::Emitter, makefile::Makefile}; +use std::io::{self, Write}; use std::process::Command; -use std::{fs, io::Write, path::Path}; +use std::{fs, path::Path}; use tb::declare_plugin; use tb::error::LocalError; use tb::{ @@ -16,6 +17,7 @@ pub struct CocoTB; mod config_keys { pub const EXE: &str = "cocotb-config.exe"; + pub const SIM: &str = "sim"; } fn filestem(path_str: &str) -> &str { @@ -60,6 +62,39 @@ impl Plugin for CocoTB { } }), ); + + config.require( + config_keys::SIM, + Some("icarus"), + "cocotb simulator", + ConfigVarValidator::when(|value| { + if let Some(sim) = value.as_str() { + let simulators = [ + "icarus", + "verilator", + "vcs", + "riviera", + "activehdl", + "questa", + "modelsim", + "ius", + "xcelium", + "ghdl", + "cvc", + ]; + if simulators.contains(&sim) { + Ok(()) + } else { + Err(LocalError::other("unsupported simulator: see https://docs.cocotb.org/en/stable/simulator_support.html for details")) + } + } else { + Err(LocalError::other( + "the cocotb simulator must be a string", + )) + } + }), + ); + Ok(()) } @@ -90,9 +125,7 @@ impl Plugin for CocoTB { makefile.comment("MODULE is the basename of the Python test file"); makefile.assign("MODULE", filestem(test)); makefile.newline(); - makefile.comment( - "include cocotb's make rules to take care of the simulator setup", - ); + makefile.comment("include cocotb's make rules to take care of the simulator setup"); makefile.include(format!( "$(shell {} --makefiles)/Makefile.sim", config.get(config_keys::EXE)?.as_str().unwrap() @@ -104,8 +137,8 @@ impl Plugin for CocoTB { let output = Command::new("make").current_dir(work_dir.path()).output()?; - std::io::stdout().write_all(&output.stdout)?; - std::io::stderr().write_all(&output.stderr)?; + io::stdout().write_all(&output.stdout)?; + io::stderr().write_all(&output.stderr)?; } Ok(()) diff --git a/tools/tb/src/config.rs b/tools/tb/src/config.rs index 08c872f6e5..5302de4320 100644 --- a/tools/tb/src/config.rs +++ b/tools/tb/src/config.rs @@ -84,6 +84,12 @@ impl ConfigVar { } } +#[derive(Debug)] +pub enum InvalidConfigVar { + Missing(ConfigVar, Box), + Incorrect(ConfigVar, Box), +} + pub struct Config { /// DO NOT USE DIRECTLY. use [`Config::get`] instead. figment: Figment, @@ -133,16 +139,24 @@ impl Config { let mut errors = vec![]; for required_key in &self.required { match self.get(&required_key.key) { - Ok(value) => required_key.validate(&value)?, - Err(error) => { - errors.push((required_key.clone(), Box::new(error))) + Ok(value) => { + if let Err(error) = required_key.validate(&value) { + errors.push(InvalidConfigVar::Incorrect( + required_key.clone(), + Box::new(error), + )) + } } + Err(error) => errors.push(InvalidConfigVar::Missing( + required_key.clone(), + Box::new(error), + )), } } if errors.is_empty() { Ok(()) } else { - Err(LocalError::MissingConfig(errors)) + Err(LocalError::InvalidConfig(errors)) } } diff --git a/tools/tb/src/error.rs b/tools/tb/src/error.rs index 5ac7edbcd7..1f72025129 100644 --- a/tools/tb/src/error.rs +++ b/tools/tb/src/error.rs @@ -1,11 +1,11 @@ -use crate::config::ConfigVar; +use crate::config::InvalidConfigVar; use std::fmt::Display; #[derive(Debug)] pub enum LocalError { IO(std::io::Error), Figment(figment::Error), - MissingConfig(Vec<(ConfigVar, Box)>), + InvalidConfig(Vec), Other(String), } @@ -32,15 +32,27 @@ impl Display for LocalError { match self { LocalError::IO(io_err) => io_err.fmt(f), LocalError::Figment(figment_err) => figment_err.fmt(f), - LocalError::MissingConfig(errors) => { + LocalError::InvalidConfig(errors) => { writeln!(f, "We detected some errors in your config:")?; - for (config_var, _) in errors { - writeln!( - f, - "- missing key '{}': {}", - config_var.key(), - config_var.description() - )?; + for error in errors { + match error { + InvalidConfigVar::Missing(config_var, _) => { + writeln!( + f, + "- missing key '{}': {}", + config_var.key(), + config_var.description() + )?; + } + InvalidConfigVar::Incorrect(config_var, error) => { + writeln!( + f, + "- incorrect key '{}': {}", + config_var.key(), + error + )?; + } + } } Ok(()) } diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs index 53d8d53770..a2e5b953e7 100644 --- a/tools/tb/src/main.rs +++ b/tools/tb/src/main.rs @@ -1,4 +1,8 @@ -use std::{env, path::PathBuf}; +use std::{ + env, + io::{self, Write}, + path::PathBuf, +}; use tb::{ cli::CLI, driver::Driver, @@ -18,16 +22,7 @@ fn setup_logging() { env_logger::builder().format_target(false).init(); } -fn main() -> LocalResult<()> { - setup_logging(); - - let args = CLI::from_env(); - - if args.version { - println!("{}", env!("CARGO_PKG_VERSION")); - return Ok(()); - } - +fn run_app(args: CLI) -> LocalResult<()> { let config_path = match args.config { Some(config_path) => config_path, None => { @@ -58,3 +53,20 @@ fn main() -> LocalResult<()> { let driver = Driver::load(&[default_loc])?; driver.run(args.using, config_path, args.input, &args.tests) } + +fn main() -> io::Result<()> { + setup_logging(); + + let args = CLI::from_env(); + + if args.version { + println!("{}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + if let Err(error) = run_app(args) { + write!(&mut io::stderr(), "{}", error)?; + } + + Ok(()) +} From a9ebc443584f89f3f280d83542a237846158ded2 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 7 Jul 2024 18:21:53 -0400 Subject: [PATCH 11/41] Verilator works! --- fud2/scripts/tb.rhai | 4 +- .../doc_examples_quickstart/my_design.sv | 2 + .../doc_examples_quickstart/test_my_design.py | 2 + .../tb/examples/verilator/test_our/README.md | 1 + tools/tb/examples/verilator/test_our/our.v | 4 + .../examples/verilator/test_our/sim_main.cpp | 14 +++ tools/tb/plugins/cocotb/lib.rs | 3 +- tools/tb/plugins/verilator/lib.rs | 107 ++++++++++++++++-- tools/tb/src/cli.rs | 37 +++++- tools/tb/src/config.rs | 17 ++- tools/tb/src/driver.rs | 9 +- tools/tb/src/main.rs | 2 +- 12 files changed, 181 insertions(+), 21 deletions(-) create mode 100644 tools/tb/examples/verilator/test_our/README.md create mode 100644 tools/tb/examples/verilator/test_our/our.v create mode 100644 tools/tb/examples/verilator/test_our/sim_main.cpp diff --git a/fud2/scripts/tb.rhai b/fud2/scripts/tb.rhai index c913817c12..99d45a1c41 100644 --- a/fud2/scripts/tb.rhai +++ b/fud2/scripts/tb.rhai @@ -6,7 +6,9 @@ fn tb_setup(e) { e.config_var("calyx-tb-exe", "tb.exe"); e.config_var("calyx-tb-test", "tb.test"); e.config_var("calyx-tb-config-file", "tb.config-file"); - e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using cocotb --config $calyx-tb-config-file"); + e.config_var_or("calyx-tb-flags", "tb.flags", ""); + e.config_var("calyx-tb-using", "tb.using"); + e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using $calyx-tb-using --config $calyx-tb-config-file $calyx-tb-flags"); } rule([tb_setup], calyx::verilog_state, tb_state, "verilog-to-tb"); diff --git a/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv b/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv index d79b5d32d1..bfa912c018 100644 --- a/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv +++ b/tools/tb/examples/cocotb/doc_examples_quickstart/my_design.sv @@ -1,3 +1,5 @@ +// https://github.com/cocotb/cocotb/tree/master/examples/doc_examples/quickstart + // This file is public domain, it can be freely copied without restrictions. // SPDX-License-Identifier: CC0-1.0 diff --git a/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py b/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py index fa7a210ccd..daaa9996db 100644 --- a/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py +++ b/tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py @@ -1,3 +1,5 @@ +# https://github.com/cocotb/cocotb/tree/master/examples/doc_examples/quickstart + # This file is public domain, it can be freely copied without restrictions. # SPDX-License-Identifier: CC0-1.0 diff --git a/tools/tb/examples/verilator/test_our/README.md b/tools/tb/examples/verilator/test_our/README.md new file mode 100644 index 0000000000..43e5c6763d --- /dev/null +++ b/tools/tb/examples/verilator/test_our/README.md @@ -0,0 +1 @@ +Taken from https://veripool.org/guide/latest/example_cc.html#example-c-execution. diff --git a/tools/tb/examples/verilator/test_our/our.v b/tools/tb/examples/verilator/test_our/our.v new file mode 100644 index 0000000000..e124a796f6 --- /dev/null +++ b/tools/tb/examples/verilator/test_our/our.v @@ -0,0 +1,4 @@ +// https://veripool.org/guide/latest/example_cc.html#example-c-execution +module our; + initial begin $display("Hello World"); $finish; end + endmodule diff --git a/tools/tb/examples/verilator/test_our/sim_main.cpp b/tools/tb/examples/verilator/test_our/sim_main.cpp new file mode 100644 index 0000000000..c881991c46 --- /dev/null +++ b/tools/tb/examples/verilator/test_our/sim_main.cpp @@ -0,0 +1,14 @@ +// https://veripool.org/guide/latest/example_cc.html#example-c-execution +#include "Vour.h" +#include "verilated.h" +int main(int argc, char** argv) { + VerilatedContext* contextp = new VerilatedContext; + contextp->commandArgs(argc, argv); + Vour* top = new Vour{contextp}; + while (!contextp->gotFinish()) { + top->eval(); + } + delete top; + delete contextp; + return 0; +} diff --git a/tools/tb/plugins/cocotb/lib.rs b/tools/tb/plugins/cocotb/lib.rs index 34024356a1..2e1780e124 100644 --- a/tools/tb/plugins/cocotb/lib.rs +++ b/tools/tb/plugins/cocotb/lib.rs @@ -1,7 +1,8 @@ -use makemake::{emitter::Emitter, makefile::Makefile}; 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::{ diff --git a/tools/tb/plugins/verilator/lib.rs b/tools/tb/plugins/verilator/lib.rs index 797bf5a2c3..f175c33bc6 100644 --- a/tools/tb/plugins/verilator/lib.rs +++ b/tools/tb/plugins/verilator/lib.rs @@ -1,18 +1,78 @@ +use std::{ + io::{self, Write}, + process::Command, +}; + use tb::{ config::{Config, ConfigVarValidator}, declare_plugin, - error::LocalResult, + error::{LocalError, LocalResult}, plugin::Plugin, semver, tempdir, }; mod config_keys { - pub const EXE: &str = "verilator-exe"; + pub const EXE: &str = "exe"; + pub const CFLAGS: &str = "cflags"; + pub const TOP: &str = "top"; + pub const USE_SV: &str = "use-sv"; } #[derive(Default)] pub struct Verilator; +impl Verilator { + fn create_build_files( + &self, + input: &str, + test: &str, + work_dir: &tempdir::TempDir, + config: &Config, + ) -> LocalResult { + let mut cmd = + Command::new(config.get(config_keys::EXE)?.as_str().unwrap()); + cmd.current_dir(work_dir.path()); + cmd.args([ + "--cc", "--exe", "--build", "--timing", "-j", "0", "-Wall", input, + test, + ]); + cmd.args([ + "--top-module", + config.get(config_keys::TOP)?.as_str().unwrap(), + ]); + + let cflags = config.get(config_keys::CFLAGS)?; + let cflags = cflags.as_str().unwrap(); + cmd.args(["-CFLAGS", if cflags.is_empty() { "\"\"" } else { cflags }]); + if config.get(config_keys::USE_SV)?.as_str().unwrap() == "true" { + cmd.arg("-sv"); + } + + let output = cmd.output()?; + io::stdout().write_all(&output.stdout)?; + io::stderr().write_all(&output.stderr)?; + + Ok(format!( + "obj_dir/V{}", + config.get(config_keys::TOP)?.as_str().unwrap() + )) + } + + fn execute_harness( + &self, + executable: String, + work_dir: &tempdir::TempDir, + ) -> LocalResult<()> { + let output = Command::new(executable) + .current_dir(work_dir.path()) + .output()?; + io::stdout().write_all(&output.stdout)?; + io::stderr().write_all(&output.stderr)?; + + Ok(()) + } +} + impl Plugin for Verilator { fn name(&self) -> &'static str { "verilator" @@ -29,17 +89,50 @@ impl Plugin for Verilator { "path to verilator executable", ConfigVarValidator::default(), ); + + config.require( + config_keys::CFLAGS, + Some(""), + "passed via -CFLAGS", + ConfigVarValidator::default(), + ); + + config.require( + config_keys::TOP, + Some("main"), + "name of top-level module", + ConfigVarValidator::default(), + ); + + config.require( + config_keys::USE_SV, + Some("true"), + "whether the input is SystemVerilog", + ConfigVarValidator::when(|value| { + value + .as_str() + .filter(|value| ["true", "false"].contains(value)) + .ok_or(LocalError::other("must be true or false")) + .map(|_| ()) + }), + ); + Ok(()) } fn run( &self, - _input: String, - _tests: &[String], - _work_dir: tempdir::TempDir, - _config: &Config, + input: String, + tests: &[String], + work_dir: tempdir::TempDir, + config: &Config, ) -> LocalResult<()> { - todo!("verilator not yet impl") + for test in tests { + let exec = + self.create_build_files(&input, test, &work_dir, config)?; + self.execute_harness(exec, &work_dir)?; + } + Ok(()) } } diff --git a/tools/tb/src/cli.rs b/tools/tb/src/cli.rs index 1251917a78..c04aa8de5c 100644 --- a/tools/tb/src/cli.rs +++ b/tools/tb/src/cli.rs @@ -1,8 +1,32 @@ -use std::{env, path::PathBuf}; +use std::{env, path::PathBuf, str::FromStr}; use argh::FromArgs; -#[derive(FromArgs)] +use crate::error::{LocalError, LocalResult}; + +pub struct ConfigSet { + pub key: String, + pub value: String, +} + +impl FromStr for ConfigSet { + type Err = LocalError; + + fn from_str(s: &str) -> LocalResult { + s.find('=') + .map(|index| { + let (key, value) = s.split_at(index); + let value = value.chars().skip(1).collect(); + ConfigSet { + key: key.to_string(), + value, + } + }) + .ok_or(LocalError::other("expected syntax 'key=value'")) + } +} + +#[derive(FromArgs, Default)] /// Test verilog files under various harnesses. pub struct CLI { #[argh(positional)] @@ -13,6 +37,10 @@ pub struct CLI { /// test harness pub tests: Vec, + #[argh(option, short = 's')] + /// set a config option + pub set: Vec, + #[argh(option, short = 'u')] /// the testbench to invoke pub using: String, @@ -31,11 +59,8 @@ impl CLI { let args: Vec<_> = env::args().collect(); if args.len() == 2 && matches!(args[1].as_str(), "-v" | "--version") { Self { - input: String::new(), - tests: Vec::new(), - using: String::new(), - config: None, version: true, + ..Default::default() } } else { argh::from_env() diff --git a/tools/tb/src/config.rs b/tools/tb/src/config.rs index 5302de4320..7bea9ab6bb 100644 --- a/tools/tb/src/config.rs +++ b/tools/tb/src/config.rs @@ -8,6 +8,9 @@ use std::rc::Rc; pub type ConfigVarValidatorPredicate = fn(&Value) -> LocalResult<()>; +/// TODO: make this declarative, allow building complex things in some sort of +/// eDSL fashion, with helpers for like "this must be a string", "this must be a +/// command and running it yields this output", etc. pub struct ConfigVarValidator { predicates: Vec, } @@ -126,9 +129,7 @@ impl Config { ) { if let Some(default) = default { if self.get(key.as_ref()).is_err() { - let new_figment = std::mem::take(&mut self.figment); - self.figment = new_figment - .join((self.fix_key(key.as_ref()), default.into())); + self.set(&key, default); } } self.required @@ -160,6 +161,16 @@ impl Config { } } + pub(crate) fn set, V: Into>( + &mut self, + key: S, + value: V, + ) { + let new_figment = std::mem::take(&mut self.figment); + self.figment = + new_figment.join((self.fix_key(key.as_ref()), value.into())); + } + fn fix_key>(&self, key: S) -> String { format!("{}.{}", self.profile, key.as_ref()) } diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs index cbc8495bba..807a4805cf 100644 --- a/tools/tb/src/driver.rs +++ b/tools/tb/src/driver.rs @@ -1,4 +1,5 @@ use crate::{ + cli::ConfigSet, config::Config, error::{LocalError, LocalResult}, plugin::{PluginCreate, PluginRef}, @@ -90,14 +91,18 @@ impl Driver { pub fn run, P: AsRef>( &self, name: S, - path: P, + config_path: P, + config_sets: Vec, input: String, tests: &[String], ) -> LocalResult<()> { if let Some(plugin) = self.plugins.get(name.as_ref()) { let work_dir = TempDir::new(".calyx-tb").map_err(LocalError::from)?; - let mut config = Config::from(path, name)?; + let mut config = Config::from(config_path, name)?; + for config_set in config_sets { + config.set(config_set.key, config_set.value); + } let input = copy_into(input, &work_dir).map_err(LocalError::from)?; let mut test_basenames = vec![]; diff --git a/tools/tb/src/main.rs b/tools/tb/src/main.rs index a2e5b953e7..2b5e453810 100644 --- a/tools/tb/src/main.rs +++ b/tools/tb/src/main.rs @@ -51,7 +51,7 @@ fn run_app(args: CLI) -> LocalResult<()> { default_loc }; let driver = Driver::load(&[default_loc])?; - driver.run(args.using, config_path, args.input, &args.tests) + driver.run(args.using, config_path, args.set, args.input, &args.tests) } fn main() -> io::Result<()> { From 98f5a9091b233f1f71d9d53302551d598c5be1b2 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:39:28 -0400 Subject: [PATCH 12/41] Fix --- .flake8 | 3 ++- fud2/scripts/tb.rhai | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 1d36346c0d..146ae6054d 100644 --- a/.flake8 +++ b/.flake8 @@ -1,2 +1,3 @@ [flake8] -max-line-length = 88 \ No newline at end of file +max-line-length = 88 +exclude = tools/tb/examples/cocotb/doc_examples_quickstart/test_my_design.py diff --git a/fud2/scripts/tb.rhai b/fud2/scripts/tb.rhai index 99d45a1c41..1f6e041899 100644 --- a/fud2/scripts/tb.rhai +++ b/fud2/scripts/tb.rhai @@ -7,7 +7,7 @@ fn tb_setup(e) { e.config_var("calyx-tb-test", "tb.test"); e.config_var("calyx-tb-config-file", "tb.config-file"); e.config_var_or("calyx-tb-flags", "tb.flags", ""); - e.config_var("calyx-tb-using", "tb.using"); + e.config_var_or("calyx-tb-using", "tb.using"); e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using $calyx-tb-using --config $calyx-tb-config-file $calyx-tb-flags"); } From 432ce205ea4dd7a2ba81744948f67866f34aadd0 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Mon, 8 Jul 2024 02:55:46 -0400 Subject: [PATCH 13/41] Start FFI and custom calyx testbench --- Cargo.lock | 98 +++++++--- Cargo.toml | 4 + tools/calyx-ffi-macro/Cargo.toml | 25 +++ tools/calyx-ffi-macro/src/calyx.rs | 50 ++++++ tools/calyx-ffi-macro/src/lib.rs | 248 ++++++++++++++++++++++++++ tools/calyx-ffi-macro/src/parse.rs | 42 +++++ tools/calyx-ffi-macro/src/util.rs | 6 + tools/calyx-ffi/Cargo.toml | 16 ++ tools/calyx-ffi/src/lib.rs | 38 ++++ tools/calyx-ffi/src/prelude.rs | 2 + tools/calyx-ffi/tests/file.futil | 5 + tools/calyx-ffi/tests/test.rs | 15 ++ tools/tb/Cargo.toml | 1 + tools/tb/examples/calyx/Cargo.toml | 19 ++ tools/tb/examples/calyx/adder.futil | 13 ++ tools/tb/examples/calyx/test.rs | 17 ++ tools/tb/plugins/calyx/Cargo.toml | 12 ++ tools/tb/plugins/calyx/driver.rs | 9 + tools/tb/plugins/calyx/lib.rs | 103 +++++++++++ tools/tb/plugins/verilator/Cargo.toml | 1 - tools/tb/src/driver.rs | 45 +++-- tools/tb/src/error.rs | 8 +- 22 files changed, 729 insertions(+), 48 deletions(-) create mode 100644 tools/calyx-ffi-macro/Cargo.toml create mode 100644 tools/calyx-ffi-macro/src/calyx.rs create mode 100644 tools/calyx-ffi-macro/src/lib.rs create mode 100644 tools/calyx-ffi-macro/src/parse.rs create mode 100644 tools/calyx-ffi-macro/src/util.rs create mode 100644 tools/calyx-ffi/Cargo.toml create mode 100644 tools/calyx-ffi/src/lib.rs create mode 100644 tools/calyx-ffi/src/prelude.rs create mode 100644 tools/calyx-ffi/tests/file.futil create mode 100644 tools/calyx-ffi/tests/test.rs create mode 100644 tools/tb/examples/calyx/Cargo.toml create mode 100644 tools/tb/examples/calyx/adder.futil create mode 100644 tools/tb/examples/calyx/test.rs create mode 100644 tools/tb/plugins/calyx/Cargo.toml create mode 100644 tools/tb/plugins/calyx/driver.rs create mode 100644 tools/tb/plugins/calyx/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b4f2b6ee2f..ac04df75f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,7 +139,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -175,7 +175,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -206,7 +206,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -261,7 +261,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.52", + "syn 2.0.69", "which", ] @@ -410,6 +410,32 @@ dependencies = [ "vast", ] +[[package]] +name = "calyx-ffi" +version = "0.7.1" +dependencies = [ + "calyx-ffi-macro", +] + +[[package]] +name = "calyx-ffi-example" +version = "0.7.1" +dependencies = [ + "calyx-ffi", +] + +[[package]] +name = "calyx-ffi-macro" +version = "0.7.1" +dependencies = [ + "calyx-frontend", + "calyx-ir", + "calyx-utils", + "proc-macro2", + "quote", + "syn 2.0.69", +] + [[package]] name = "calyx-frontend" version = "0.7.1" @@ -498,6 +524,14 @@ 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" @@ -700,7 +734,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -993,7 +1027,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -1015,7 +1049,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -1250,6 +1284,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -1340,7 +1380,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -1797,7 +1837,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2112,7 +2152,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2153,7 +2193,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2227,14 +2267,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2277,9 +2317,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -2488,7 +2528,7 @@ checksum = "59aecf17969c04b9c0c5d21f6bc9da9fec9dd4980e64d1871443a476589d8c86" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2611,7 +2651,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2633,7 +2673,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2704,7 +2744,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2884,7 +2924,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -2927,9 +2967,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" dependencies = [ "proc-macro2", "quote", @@ -2955,6 +2995,7 @@ dependencies = [ "argh", "env_logger", "figment", + "fs_extra", "libloading", "log", "semver", @@ -3036,7 +3077,7 @@ checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -3139,7 +3180,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -3241,7 +3282,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -3269,7 +3310,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] [[package]] @@ -3421,7 +3462,6 @@ dependencies = [ name = "verilator-tb-plugin" version = "0.0.0" dependencies = [ - "makemake", "tb", ] @@ -3766,5 +3806,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.69", ] diff --git a/Cargo.toml b/Cargo.toml index 902a53c1b7..d48509bff6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,10 @@ members = [ "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", ] exclude = ["site"] diff --git a/tools/calyx-ffi-macro/Cargo.toml b/tools/calyx-ffi-macro/Cargo.toml new file mode 100644 index 0000000000..bff0cd63ae --- /dev/null +++ b/tools/calyx-ffi-macro/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "calyx-ffi-macro" +authors.workspace = true +license-file.workspace = true +keywords.workspace = true +repository.workspace = true +readme.workspace = true +description.workspace = true +categories.workspace = true +homepage.workspace = true +edition.workspace = true +version.workspace = true +rust-version.workspace = true + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.86" +quote = "1.0.36" +syn = { version = "2.0.69", features = ["full", "visit"] } +calyx-utils.workspace = true +calyx-frontend.workspace = true +calyx-ir.workspace = true +# bigint = "4.4.3" diff --git a/tools/calyx-ffi-macro/src/calyx.rs b/tools/calyx-ffi-macro/src/calyx.rs new file mode 100644 index 0000000000..4c8654990e --- /dev/null +++ b/tools/calyx-ffi-macro/src/calyx.rs @@ -0,0 +1,50 @@ +use std::{env, path::PathBuf, rc::Rc}; + +use proc_macro::TokenStream; + +use crate::{parse::CalyxFFIMacroArgs, util}; + +pub struct CalyxComponent { + ctx: Rc, + index: usize, +} + +impl CalyxComponent { + pub fn get(&self) -> &calyx_ir::Component { + &self.ctx.components[self.index] + } +} + +pub fn parse_calyx_file( + args: CalyxFFIMacroArgs, +) -> Result { + // 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(args.src.clone()), + &lib_path, + ) + .map_err(|err| util::compile_error(&args.src_attr_span, err.message()))?; + let ctx = calyx_ir::from_ast::ast_to_ir(ws).map_err(|err| { + util::compile_error(&args.src_attr_span, err.message()) + })?; + + let comp_index = ctx + .components + .iter() + .position(|comp| comp.name == args.comp) + .ok_or(util::compile_error( + &args.comp_attr_span, + format!( + "component '{}' does not exist in '{}'", + args.comp, + args.src.to_string_lossy() + ), + ))?; + Ok(CalyxComponent { + ctx: Rc::new(ctx), + index: comp_index, + }) +} diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs new file mode 100644 index 0000000000..48ce367bbe --- /dev/null +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -0,0 +1,248 @@ +use parse::CalyxFFIMacroArgs; +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{parse_macro_input, spanned::Spanned}; + +mod calyx; +mod parse; +mod util; + +#[proc_macro_attribute] +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 comp = calyx::parse_calyx_file(args); + if let Err(error) = comp { + return error; + } + let comp = comp.unwrap(); + let comp = comp.get(); + // + + let comp_name = syn::parse_str::(&format!( + "\"{}\"", + comp.name.to_string() + )) + .expect("failed to turn quoted name into string"); + + let mut fields = Vec::new(); + let mut getters = Vec::new(); + + for port in comp.signature.borrow().ports() { + let port_name_str = port.borrow().name.to_string(); + let port_name = syn::parse_str::(&port_name_str) + .expect("failed to turn port name into identifier"); + // let port_width = port.borrow().width; + + // idk why input output ports are being flipped?? + match port.borrow().direction.reverse() { + calyx_ir::Direction::Input => { + fields.push(quote! { + pub #port_name: u64 + }); + } + calyx_ir::Direction::Output => { + fields.push(quote! { + #port_name: u64 + }); + getters.push(quote! { + pub fn #port_name(&self) -> u64 { + self.#port_name + } + }); + } + calyx_ir::Direction::Inout => { + todo!("inout ports not supported yet") + } + } + } + + let struct_def = quote! { + struct #name { + #(#fields),* + } + }; + + let impl_block = quote! { + impl #name { + #(#getters)* + } + + impl CalyxFFIComponent for #name { + fn name(&self) -> &'static str { + #comp_name + } + + fn reset(&mut self) { + self.reset = 1; + for _ in 0..5 { + self.tick(); + } + self.reset = 0; + } + + fn tick(&mut self) { + self.clk = 1; + + self.clk = 0; + } + } + }; + + quote! { + #[derive(Default)] + #struct_def + #impl_block + } + .into() +} + +#[derive(Default)] +struct CalyxFFITestModuleVisitor { + pub tests: Vec, +} + +impl syn::visit::Visit<'_> for CalyxFFITestModuleVisitor { + fn visit_item_fn(&mut self, i: &syn::ItemFn) { + let has_calyx_ffi_test = i + .attrs + .iter() + .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>(); + 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); + } + }) + } + } +} + +#[proc_macro_attribute] +pub fn calyx_ffi_tests(args: TokenStream, item: TokenStream) -> TokenStream { + if !args.is_empty() { + return util::compile_error( + &args.into_iter().next().unwrap().span().into(), + "#[calyx_ffi_tests] takes no arguments".into(), + ); + } + + let mut module = parse_macro_input!(item as syn::ItemMod); + let module_name = &module.ident; + + let mut visitor = CalyxFFITestModuleVisitor::default(); + syn::visit::visit_item_mod(&mut visitor, &module); + + 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),* + ]; + }; + let test_array_item: syn::Item = syn::parse2(test_array).unwrap(); + + let mut items_to_add = vec![test_array_item]; + items_to_add.extend(visitor.tests.iter().cloned().map(syn::Item::Fn)); + + if let Some((_, ref mut items)) = module.content { + items.extend(items_to_add); + } else { + module.content = Some((syn::token::Brace::default(), items_to_add)); + } + + quote! { + #module + + pub unsafe fn calyx_ffi_test(ffi: &mut CalyxFFI) { + for test in #module_name::CALYX_FFI_TESTS { + test(ffi); + } + } + } + .into() +} + +#[proc_macro_attribute] +pub fn calyx_ffi_test(args: TokenStream, item: TokenStream) -> TokenStream { + if !args.is_empty() { + return util::compile_error( + &args.into_iter().next().unwrap().span().into(), + "#[calyx_ffi_test] takes no arguments".into(), + ); + } + + let mut func = parse_macro_input!(item as syn::ItemFn); + let dut_type = get_ffi_test_dut_type(&func); + let Ok(dut_type) = dut_type else { + return dut_type.err().unwrap(); + }; + + let check_trait_impl = quote! { + { + fn assert_is_calyx_ffi_component() {} + assert_is_calyx_ffi_component::<#dut_type>(); + } + }; + + let check_trait_impl_stmts: syn::Block = syn::parse2(check_trait_impl) + .expect("Failed to parse check_trait_impl as a block"); + + let new_stmts: Vec = check_trait_impl_stmts + .stmts + .iter() + .chain(func.block.stmts.iter()) + .cloned() + .collect(); + + let new_block = syn::Block { + brace_token: func.block.brace_token, + stmts: new_stmts, + }; + func.block = Box::new(new_block); + + quote! { + #func + } + .into() +} + +fn get_ffi_test_dut_type( + func: &syn::ItemFn, +) -> Result<&syn::Type, TokenStream> { + let inputs: Vec<&syn::FnArg> = func.sig.inputs.iter().collect(); + + let bad_sig_msg = "#[calyx_ffi_test] tests must take exactly one argument, namely, a mutable reference to the DUT".into(); + + if inputs.len() != 1 { + return Err(util::compile_error(&func.span(), bad_sig_msg)); + } + let input = inputs.first().unwrap(); + + let syn::FnArg::Typed(pat_ty) = input else { + return Err(util::compile_error(&func.span(), bad_sig_msg)); + }; + + let syn::Type::Reference(syn::TypeReference { + mutability: Some(syn::token::Mut { span: _ }), + ref elem, + .. + }) = *pat_ty.ty + else { + return Err(util::compile_error(&func.span(), bad_sig_msg)); + }; + + Ok(elem) +} diff --git a/tools/calyx-ffi-macro/src/parse.rs b/tools/calyx-ffi-macro/src/parse.rs new file mode 100644 index 0000000000..692ef86555 --- /dev/null +++ b/tools/calyx-ffi-macro/src/parse.rs @@ -0,0 +1,42 @@ +use std::path::PathBuf; + +use proc_macro2::{Span, TokenTree}; +use syn::parse::{Parse, ParseStream}; + +pub struct CalyxFFIMacroArgs { + pub src_attr_span: Span, + pub src: PathBuf, + pub comp_attr_span: Span, + pub comp: String, +} + +impl Parse for CalyxFFIMacroArgs { + fn parse(input: ParseStream) -> syn::Result { + syn::custom_keyword!(src); + syn::custom_keyword!(comp); + + let src_ident = input.parse::()?; + input.parse::()?; + let src_lit = input.parse::()?.value(); + + input.parse::()?; + + let comp_ident = input.parse::()?; + input.parse::()?; + let comp_lit = input.parse::()?.value(); + + if !input.is_empty() { + return Err(syn::Error::new_spanned( + input.parse::()?, + "Invalid `calyx_ffi` attribute syntax: expected 'src = \"...\", comp = \"...\"", + )); + } + + Ok(Self { + src_attr_span: src_ident.span, + src: src_lit.into(), + comp_attr_span: comp_ident.span, + comp: comp_lit, + }) + } +} diff --git a/tools/calyx-ffi-macro/src/util.rs b/tools/calyx-ffi-macro/src/util.rs new file mode 100644 index 0000000000..882a9e0e6b --- /dev/null +++ b/tools/calyx-ffi-macro/src/util.rs @@ -0,0 +1,6 @@ +use proc_macro::TokenStream; +use proc_macro2::Span; + +pub fn compile_error(span: &Span, msg: String) -> TokenStream { + syn::Error::new(*span, msg).to_compile_error().into() +} diff --git a/tools/calyx-ffi/Cargo.toml b/tools/calyx-ffi/Cargo.toml new file mode 100644 index 0000000000..12ed31c33f --- /dev/null +++ b/tools/calyx-ffi/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "calyx-ffi" +authors.workspace = true +license-file.workspace = true +keywords.workspace = true +repository.workspace = true +readme.workspace = true +description.workspace = true +categories.workspace = true +homepage.workspace = true +edition.workspace = true +version.workspace = true +rust-version.workspace = true + +[dependencies] +calyx-ffi-macro = { path = "../calyx-ffi-macro" } diff --git a/tools/calyx-ffi/src/lib.rs b/tools/calyx-ffi/src/lib.rs new file mode 100644 index 0000000000..07e56cd3f4 --- /dev/null +++ b/tools/calyx-ffi/src/lib.rs @@ -0,0 +1,38 @@ +use std::{any, cell::RefCell, collections::HashMap, rc::Rc}; + +pub mod prelude; + +/// A non-combinational calyx component. +pub trait CalyxFFIComponent: any::Any { + /// The in-source name of this component. + fn name(&self) -> &'static str; + + // Resets this component. + fn reset(&mut self); + + // Advances this component by one clock cycle. + fn tick(&mut self); +} + +pub type CalyxFFIComponentRef = Rc>; + +#[derive(Default)] +pub struct CalyxFFI { + comps: HashMap<&'static str, CalyxFFIComponentRef>, +} + +impl CalyxFFI { + pub fn new() -> Self { + Self::default() + } + + pub fn comp( + &mut self, + ) -> CalyxFFIComponentRef { + let name = T::default().name(); + if !self.comps.contains_key(name) { + self.comps.insert(name, Rc::new(RefCell::new(T::default()))); + } + self.comps.get(name).unwrap().clone() + } +} diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs new file mode 100644 index 0000000000..80281ce750 --- /dev/null +++ b/tools/calyx-ffi/src/prelude.rs @@ -0,0 +1,2 @@ +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/file.futil b/tools/calyx-ffi/tests/file.futil new file mode 100644 index 0000000000..f1b20be0ff --- /dev/null +++ b/tools/calyx-ffi/tests/file.futil @@ -0,0 +1,5 @@ +component main(foo: 1) -> () { + cells {} + wires {} + control {} +} diff --git a/tools/calyx-ffi/tests/test.rs b/tools/calyx-ffi/tests/test.rs new file mode 100644 index 0000000000..ba389daf77 --- /dev/null +++ b/tools/calyx-ffi/tests/test.rs @@ -0,0 +1,15 @@ +use calyx_ffi::prelude::*; + +#[calyx_ffi( + src = "/Users/ethan/Documents/GitHub/calyx/tools/calyx-ffi/tests/file.futil", + comp = "main" +)] +struct Main; + +#[test] +fn test() { + let mut main = Main::default(); + main.reset(); + assert!(main.reset == 0); + main.tick(); +} diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index ea3c00bdd3..cd30286042 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -21,3 +21,4 @@ semver.workspace = true libloading = "0.8.4" log.workspace = true env_logger.workspace = true +fs_extra = "1.3.0" diff --git a/tools/tb/examples/calyx/Cargo.toml b/tools/tb/examples/calyx/Cargo.toml new file mode 100644 index 0000000000..a5db5e055b --- /dev/null +++ b/tools/tb/examples/calyx/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "calyx-ffi-example" +authors.workspace = true +license-file.workspace = true +keywords.workspace = true +repository.workspace = true +readme.workspace = true +description.workspace = true +categories.workspace = true +homepage.workspace = true +edition.workspace = true +version.workspace = true +rust-version.workspace = true + +[lib] +path = "test.rs" + +[dependencies] +calyx-ffi = { path = "../../../calyx-ffi" } diff --git a/tools/tb/examples/calyx/adder.futil b/tools/tb/examples/calyx/adder.futil new file mode 100644 index 0000000000..2adfc08f72 --- /dev/null +++ b/tools/tb/examples/calyx/adder.futil @@ -0,0 +1,13 @@ +import "primitives/core.futil"; + +component main(lhs: 64, rhs: 64) -> (result: 64) { + cells { + adder = std_add(64); + } + wires { + adder.left = lhs; + adder.right = rhs; + result = adder.out; + } + control {} +} diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs new file mode 100644 index 0000000000..f9721e170d --- /dev/null +++ b/tools/tb/examples/calyx/test.rs @@ -0,0 +1,17 @@ +use calyx_ffi::prelude::*; + +#[calyx_ffi( + src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", + comp = "main" +)] +struct Adder; + +#[calyx_ffi_tests] +mod tests { + use super::*; + + #[calyx_ffi_test] + fn test(adder: &mut Adder) { + println!("testing adder"); + } +} diff --git a/tools/tb/plugins/calyx/Cargo.toml b/tools/tb/plugins/calyx/Cargo.toml new file mode 100644 index 0000000000..83032095ed --- /dev/null +++ b/tools/tb/plugins/calyx/Cargo.toml @@ -0,0 +1,12 @@ +[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/calyx/driver.rs b/tools/tb/plugins/calyx/driver.rs new file mode 100644 index 0000000000..d30944f9b7 --- /dev/null +++ b/tools/tb/plugins/calyx/driver.rs @@ -0,0 +1,9 @@ +use calyx_ffi::prelude::*; +use test_crate::calyx_ffi_test; + +fn main() { + let mut ffi = CalyxFFI::default(); + unsafe { + calyx_ffi_test(&mut ffi); + } +} diff --git a/tools/tb/plugins/calyx/lib.rs b/tools/tb/plugins/calyx/lib.rs new file mode 100644 index 0000000000..2f001fae6a --- /dev/null +++ b/tools/tb/plugins/calyx/lib.rs @@ -0,0 +1,103 @@ +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}; + +#[derive(Default)] +pub struct CalyxTB; + +mod config_keys {} + +const DRIVER_CODE: &str = include_str!("driver.rs"); + +impl Plugin for CalyxTB { + fn name(&self) -> &'static str { + "calyx" + } + + fn version(&self) -> semver::Version { + semver::Version::new(0, 0, 0) + } + + fn setup(&self, config: &mut Config) -> LocalResult<()> { + Ok(()) + } + + fn run( + &self, + input: String, + tests: &[String], + work_dir: tempdir::TempDir, + config: &Config, + ) -> LocalResult<()> { + let mut calyx_ffi_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + calyx_ffi_path.push("../../../calyx-ffi"); + + let mut main_file = PathBuf::from(work_dir.path()); + main_file.push("main.rs"); + fs::write(&main_file, DRIVER_CODE)?; + + let mut manifest_path = PathBuf::from(work_dir.path()); + manifest_path.push("Cargo.toml"); + + let mut lib_path = PathBuf::from(work_dir.path()); + lib_path.push("lib.rs"); + + for test in tests { + let mut test_path = PathBuf::from(work_dir.path()); + test_path.push(test); + + let mut manifest = toml::Table::new(); + manifest.insert( + "package".into(), + toml::Value::Table(toml::map::Map::from_iter([ + ("name".to_string(), "test_crate".into()), + ("edition".to_string(), "2021".into()), + ])), + ); + manifest.insert( + "lib".into(), + toml::Value::Table(toml::map::Map::from_iter([( + "path".to_string(), + "lib.rs".into(), + )])), + ); + manifest.insert( + "bin".into(), + vec![toml::Value::Table(toml::map::Map::from_iter([ + ("name".to_string(), "test".into()), + ("path".to_string(), "main.rs".into()), + ]))] + .into(), + ); + manifest.insert( + "dependencies".into(), + toml::Value::Table(toml::map::Map::from_iter([( + "calyx-ffi".to_string(), + toml::Value::Table(toml::map::Map::from_iter([( + "path".to_string(), + calyx_ffi_path.to_string_lossy().to_string().into(), + )])), + )])), + ); + + fs::write(&manifest_path, manifest.to_string())?; + fs::rename(&test_path, &lib_path)?; + + let output = Command::new("cargo") + .arg("run") + .arg("--quiet") + .current_dir(work_dir.path()) + .output()?; + io::stderr().write_all(&output.stderr)?; + io::stdout().write_all(&output.stdout)?; + } + + Ok(()) + } +} + +declare_plugin!(CalyxTB, CalyxTB::default); diff --git a/tools/tb/plugins/verilator/Cargo.toml b/tools/tb/plugins/verilator/Cargo.toml index 1c791ad29a..49af4d6f12 100644 --- a/tools/tb/plugins/verilator/Cargo.toml +++ b/tools/tb/plugins/verilator/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] tb = { path = "../..", version = "0.0.0" } -makemake = "0.1.2-patch2" diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs index 807a4805cf..a2a34126f2 100644 --- a/tools/tb/src/driver.rs +++ b/tools/tb/src/driver.rs @@ -1,3 +1,9 @@ +use std::{ + collections::HashMap, + fs, io, + path::{Path, PathBuf}, +}; + use crate::{ cli::ConfigSet, config::Config, @@ -6,11 +12,6 @@ use crate::{ }; use libloading::{Library, Symbol}; use semver::VersionReq; -use std::{ - collections::HashMap, - fs, - path::{Path, PathBuf}, -}; use tempdir::TempDir; #[derive(Default)] @@ -123,20 +124,36 @@ impl Driver { } } -fn copy_into>( - file: S, - work_dir: &TempDir, -) -> std::io::Result { +fn copy_into>(file: S, work_dir: &TempDir) -> io::Result { let from_path = PathBuf::from(file.as_ref()); let basename = from_path .file_name() - .expect("path ended with ..") + .ok_or_else(|| { + io::Error::new(io::ErrorKind::Other, "path ended with '..'") + })? .to_str() - .expect("invalid unicode") + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "invalid unicode") + })? .to_string(); - let mut to_path = work_dir.path().to_path_buf(); - to_path.push(&basename); - fs::copy(from_path, to_path)?; + let to_path = work_dir.path().join(&basename); + + if from_path.is_dir() { + fs_extra::dir::copy( + from_path, + work_dir.path(), + &fs_extra::dir::CopyOptions::new(), + ) + .map_err(io::Error::other)?; + } else { + fs_extra::file::copy( + from_path, + to_path, + &fs_extra::file::CopyOptions::new(), + ) + .map_err(io::Error::other)?; + } + Ok(basename) } diff --git a/tools/tb/src/error.rs b/tools/tb/src/error.rs index 1f72025129..3f34330505 100644 --- a/tools/tb/src/error.rs +++ b/tools/tb/src/error.rs @@ -1,9 +1,9 @@ use crate::config::InvalidConfigVar; -use std::fmt::Display; +use std::{fmt::Display, io}; #[derive(Debug)] pub enum LocalError { - IO(std::io::Error), + IO(io::Error), Figment(figment::Error), InvalidConfig(Vec), Other(String), @@ -15,8 +15,8 @@ impl LocalError { } } -impl From for LocalError { - fn from(value: std::io::Error) -> Self { +impl From for LocalError { + fn from(value: io::Error) -> Self { Self::IO(value) } } From a52391c354891cc693a13aad5104e20297b05661 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:24:08 -0400 Subject: [PATCH 14/41] Work on supporting backends --- Cargo.lock | 2 ++ tools/calyx-ffi-macro/src/calyx.rs | 2 +- tools/calyx-ffi-macro/src/lib.rs | 29 ++++++++++++++++++---------- tools/calyx-ffi-macro/src/parse.rs | 10 +++++++++- tools/calyx-ffi/src/backend.rs | 31 ++++++++++++++++++++++++++++++ tools/calyx-ffi/src/lib.rs | 24 +++++++++++++++++++++-- tools/calyx-ffi/src/prelude.rs | 4 +++- tools/calyx-ffi/tests/test.rs | 3 ++- tools/tb/examples/calyx/Cargo.toml | 2 ++ tools/tb/examples/calyx/test.rs | 16 ++++++++++++++- 10 files changed, 106 insertions(+), 17 deletions(-) create mode 100644 tools/calyx-ffi/src/backend.rs diff --git a/Cargo.lock b/Cargo.lock index ac04df75f2..906546ff22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,8 @@ name = "calyx-ffi-example" version = "0.7.1" dependencies = [ "calyx-ffi", + "calyx-ir", + "interp", ] [[package]] diff --git a/tools/calyx-ffi-macro/src/calyx.rs b/tools/calyx-ffi-macro/src/calyx.rs index 4c8654990e..aee8bbfb52 100644 --- a/tools/calyx-ffi-macro/src/calyx.rs +++ b/tools/calyx-ffi-macro/src/calyx.rs @@ -16,7 +16,7 @@ impl CalyxComponent { } pub fn parse_calyx_file( - args: CalyxFFIMacroArgs, + args: &CalyxFFIMacroArgs, ) -> Result { // there has to be a better way to find lib let home_dir = env::var("HOME").expect("user home not set"); diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index 48ce367bbe..6d0d9bd15b 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -14,7 +14,7 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let name = item_struct.ident; // - let comp = calyx::parse_calyx_file(args); + let comp = calyx::parse_calyx_file(&args); if let Err(error) = comp { return error; } @@ -28,13 +28,16 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { )) .expect("failed to turn quoted name into string"); - let mut fields = Vec::new(); - let mut getters = Vec::new(); + let backend_macro = args.backend; + let mut field_names = vec![]; + let mut fields = vec![]; + let mut getters = vec![]; for port in comp.signature.borrow().ports() { let port_name_str = port.borrow().name.to_string(); let port_name = syn::parse_str::(&port_name_str) .expect("failed to turn port name into identifier"); + field_names.push(port_name.clone()); // let port_width = port.borrow().width; // idk why input output ports are being flipped?? @@ -76,18 +79,24 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { #comp_name } + fn init(&mut self) { + #backend_macro!(init self; #(#field_names),*); + } + + fn deinit(&mut self) { + #backend_macro!(deinit self; #(#field_names),*); + } + fn reset(&mut self) { - self.reset = 1; - for _ in 0..5 { - self.tick(); - } - self.reset = 0; + #backend_macro!(reset self; #(#field_names),*); } fn tick(&mut self) { - self.clk = 1; + #backend_macro!(tick self; #(#field_names),*); + } - self.clk = 0; + fn go(&mut self) { + #backend_macro!(go self; #(#field_names),*); } } }; diff --git a/tools/calyx-ffi-macro/src/parse.rs b/tools/calyx-ffi-macro/src/parse.rs index 692ef86555..5796d085ba 100644 --- a/tools/calyx-ffi-macro/src/parse.rs +++ b/tools/calyx-ffi-macro/src/parse.rs @@ -8,12 +8,14 @@ pub struct CalyxFFIMacroArgs { pub src: PathBuf, pub comp_attr_span: Span, pub comp: String, + pub backend: syn::Path, } impl Parse for CalyxFFIMacroArgs { fn parse(input: ParseStream) -> syn::Result { syn::custom_keyword!(src); syn::custom_keyword!(comp); + syn::custom_keyword!(backend); let src_ident = input.parse::()?; input.parse::()?; @@ -25,10 +27,15 @@ impl Parse for CalyxFFIMacroArgs { input.parse::()?; let comp_lit = input.parse::()?.value(); + input.parse::()?; + input.parse::()?; + input.parse::()?; + let backend_path = input.parse::()?; + if !input.is_empty() { return Err(syn::Error::new_spanned( input.parse::()?, - "Invalid `calyx_ffi` attribute syntax: expected 'src = \"...\", comp = \"...\"", + "Invalid `calyx_ffi` argument syntax: expected 'src = \"...\", comp = \"...\", backend = ...", )); } @@ -37,6 +44,7 @@ impl Parse for CalyxFFIMacroArgs { src: src_lit.into(), comp_attr_span: comp_ident.span, comp: comp_lit, + backend: backend_path, }) } } diff --git a/tools/calyx-ffi/src/backend.rs b/tools/calyx-ffi/src/backend.rs new file mode 100644 index 0000000000..7134483db4 --- /dev/null +++ b/tools/calyx-ffi/src/backend.rs @@ -0,0 +1,31 @@ +#[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(); + }; +} diff --git a/tools/calyx-ffi/src/lib.rs b/tools/calyx-ffi/src/lib.rs index 07e56cd3f4..e791d1673b 100644 --- a/tools/calyx-ffi/src/lib.rs +++ b/tools/calyx-ffi/src/lib.rs @@ -1,5 +1,6 @@ use std::{any, cell::RefCell, collections::HashMap, rc::Rc}; +pub mod backend; pub mod prelude; /// A non-combinational calyx component. @@ -7,11 +8,20 @@ pub trait CalyxFFIComponent: any::Any { /// The in-source name of this component. 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); + // Resets this component. fn reset(&mut self); - // Advances this component by one clock cycle. + // Advances this component by one clock cycle. May not always be available. fn tick(&mut self); + + /// Calls this component. + fn go(&mut self); } pub type CalyxFFIComponentRef = Rc>; @@ -31,8 +41,18 @@ impl CalyxFFI { ) -> CalyxFFIComponentRef { let name = T::default().name(); if !self.comps.contains_key(name) { - self.comps.insert(name, Rc::new(RefCell::new(T::default()))); + let comp_ref = Rc::new(RefCell::new(T::default())); + comp_ref.borrow_mut().init(); + self.comps.insert(name, comp_ref); } self.comps.get(name).unwrap().clone() } } + +impl Drop for CalyxFFI { + fn drop(&mut self) { + for (_, comp) in &self.comps { + comp.borrow_mut().deinit(); + } + } +} diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index 80281ce750..9b2fe48836 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -1,2 +1,4 @@ -pub use super::{CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef}; +pub use super::{ + useless_ffi_backend, 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 ba389daf77..8a223db05c 100644 --- a/tools/calyx-ffi/tests/test.rs +++ b/tools/calyx-ffi/tests/test.rs @@ -2,7 +2,8 @@ use calyx_ffi::prelude::*; #[calyx_ffi( src = "/Users/ethan/Documents/GitHub/calyx/tools/calyx-ffi/tests/file.futil", - comp = "main" + comp = "main", + backend = useless_ffi_backend )] struct Main; diff --git a/tools/tb/examples/calyx/Cargo.toml b/tools/tb/examples/calyx/Cargo.toml index a5db5e055b..a0ac39f852 100644 --- a/tools/tb/examples/calyx/Cargo.toml +++ b/tools/tb/examples/calyx/Cargo.toml @@ -17,3 +17,5 @@ path = "test.rs" [dependencies] calyx-ffi = { path = "../../../calyx-ffi" } +interp = { path = "../../../../interp" } +calyx-ir.workspace = true diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index f9721e170d..b31352a55a 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -2,7 +2,8 @@ use calyx_ffi::prelude::*; #[calyx_ffi( src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", - comp = "main" + comp = "main", + backend = useless_ffi_backend )] struct Adder; @@ -10,8 +11,21 @@ struct Adder; mod tests { use super::*; + fn add(adder: &mut Adder, lhs: u64, rhs: u64) -> u64 { + adder.lhs = lhs; + adder.rhs = rhs; + adder.go(); + adder.result() + } + #[calyx_ffi_test] fn test(adder: &mut Adder) { println!("testing adder"); + adder.reset(); + for i in 0..10 { + for j in 0..10 { + assert!(add(adder, i, j) == i + j); + } + } } } From 0e1f5912d8e78bf5315b49a39079d560992049f2 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 9 Jul 2024 02:15:54 -0400 Subject: [PATCH 15/41] Clean up ffi and testbench --- Cargo.lock | 25 +------- Cargo.toml | 3 - tools/calyx-ffi-macro/src/lib.rs | 64 +++++++++++++------ tools/calyx-ffi-macro/src/parse.rs | 2 +- tools/calyx-ffi/src/backend.rs | 1 + tools/calyx-ffi/src/lib.rs | 27 ++++++-- tools/tb/Cargo.toml | 2 + tools/tb/examples/calyx/test.rs | 1 + tools/tb/plugins/.gitkeep | 0 tools/tb/plugins/calyx/Cargo.toml | 12 ---- tools/tb/plugins/cocotb/Cargo.toml | 12 ---- tools/tb/plugins/verilator/Cargo.toml | 11 ---- tools/tb/src/builtin_plugins.rs | 3 + .../lib.rs => src/builtin_plugins/calyx.rs} | 20 +++--- .../lib.rs => src/builtin_plugins/cocotb.rs} | 10 +-- .../builtin_plugins/resources}/driver.rs | 4 +- .../builtin_plugins/verilator.rs} | 5 +- tools/tb/src/driver.rs | 11 +++- tools/tb/src/lib.rs | 2 + 19 files changed, 105 insertions(+), 110 deletions(-) create mode 100644 tools/tb/plugins/.gitkeep delete mode 100644 tools/tb/plugins/calyx/Cargo.toml delete mode 100644 tools/tb/plugins/cocotb/Cargo.toml delete mode 100644 tools/tb/plugins/verilator/Cargo.toml create mode 100644 tools/tb/src/builtin_plugins.rs rename tools/tb/{plugins/calyx/lib.rs => src/builtin_plugins/calyx.rs} (87%) rename tools/tb/{plugins/cocotb/lib.rs => src/builtin_plugins/cocotb.rs} (97%) rename tools/tb/{plugins/calyx => src/builtin_plugins/resources}/driver.rs (55%) rename tools/tb/{plugins/verilator/lib.rs => src/builtin_plugins/verilator.rs} (97%) diff --git a/Cargo.lock b/Cargo.lock index 906546ff22..7158a75dcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,14 +526,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" @@ -756,14 +748,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cocotb-tb-plugin" -version = "0.0.0" -dependencies = [ - "makemake", - "tb", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -3000,9 +2984,11 @@ dependencies = [ "fs_extra", "libloading", "log", + "makemake", "semver", "serde", "tempdir", + "toml", ] [[package]] @@ -3460,13 +3446,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 d48509bff6..27b717b95d 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; From 2702b32a48f625dddf1ea6fe2499745b2f4731c0 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 16/41] Continue work on testbench; cider2 stubs in place --- Cargo.lock | 3 + 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, 202 insertions(+), 89 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 7158a75dcf..ed2e997307 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,6 +415,9 @@ name = "calyx-ffi" version = "0.7.1" dependencies = [ "calyx-ffi-macro", + "calyx-frontend", + "calyx-ir", + "interp", ] [[package]] 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; From edfa8ade964443d75bbc6417765af01d436b6425 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 30 Jul 2024 20:23:51 -0400 Subject: [PATCH 17/41] Are my commits no longer verified? From d11f2879372d37af8355dd63979497ea6e18ea30 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:49:02 -0400 Subject: [PATCH 18/41] Add weird trait thing (forgot to commit) --- tools/calyx-ffi-macro/src/lib.rs | 34 ++++++++++++++++++- tools/calyx-ffi-macro/src/parse.rs | 52 +++++++++++++++++++++++++++++- tools/calyx-ffi/src/prelude.rs | 14 ++++++++ tools/tb/examples/calyx/test.rs | 16 ++++++--- 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index c0ee70c702..e030a95604 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -1,6 +1,6 @@ use parse::CalyxFFIMacroArgs; use proc_macro::TokenStream; -use quote::{format_ident, quote}; +use quote::quote; use syn::{parse_macro_input, spanned::Spanned}; mod calyx; @@ -116,9 +116,41 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { } }; + let mut derive_impls = Vec::new(); + + for derive in args.derives { + let trait_name = derive.name; + + let mut getters = Vec::new(); + for output in derive.outputs { + getters.push(quote! { + fn #output(&self) -> u64 { + self.#output + } + }) + } + + let mut setters = Vec::new(); + for input in derive.inputs { + setters.push(quote! { + fn #input(&mut self) -> &mut u64 { + &mut self.#input + } + }) + } + + derive_impls.push(quote! { + impl #trait_name for #name { + #(#getters)* + #(#setters)* + } + }); + } + quote! { #struct_def #impl_block + #(#derive_impls)* } .into() } diff --git a/tools/calyx-ffi-macro/src/parse.rs b/tools/calyx-ffi-macro/src/parse.rs index 91dfa4bb38..f0642e5754 100644 --- a/tools/calyx-ffi-macro/src/parse.rs +++ b/tools/calyx-ffi-macro/src/parse.rs @@ -1,7 +1,40 @@ use std::path::PathBuf; use proc_macro2::{Span, TokenTree}; -use syn::parse::{Parse, ParseStream}; +use syn::{ + bracketed, parenthesized, + parse::{Parse, ParseStream}, +}; + +pub struct CalyxInterface { + pub name: syn::Ident, + pub inputs: Vec, + pub outputs: Vec, +} + +impl Parse for CalyxInterface { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse::()?; + let inputs; + let outputs; + parenthesized!(inputs in input); + let inputs = inputs + .parse_terminated(syn::Ident::parse, syn::Token![,])? + .into_iter() + .collect(); + input.parse::]>()?; + parenthesized!(outputs in input); + let outputs = outputs + .parse_terminated(syn::Ident::parse, syn::Token![,])? + .into_iter() + .collect(); + Ok(Self { + name, + inputs, + outputs, + }) + } +} pub struct CalyxFFIMacroArgs { pub src_attr_span: Span, @@ -9,6 +42,7 @@ pub struct CalyxFFIMacroArgs { pub comp_attr_span: Span, pub comp: String, pub backend: syn::Path, + pub derives: Vec, } impl Parse for CalyxFFIMacroArgs { @@ -16,6 +50,7 @@ impl Parse for CalyxFFIMacroArgs { syn::custom_keyword!(src); syn::custom_keyword!(comp); syn::custom_keyword!(backend); + syn::custom_keyword!(derive); let src_ident = input.parse::()?; input.parse::()?; @@ -32,6 +67,20 @@ impl Parse for CalyxFFIMacroArgs { input.parse::()?; let backend_path = input.parse::()?; + let _ = input.parse::(); + + let derives = if input.parse::().is_ok() { + input.parse::()?; + let content; + bracketed!(content in input); + content + .parse_terminated(CalyxInterface::parse, syn::Token![,])? + .into_iter() + .collect() + } else { + vec![] + }; + if !input.is_empty() { return Err(syn::Error::new_spanned( input.parse::()?, @@ -45,6 +94,7 @@ impl Parse for CalyxFFIMacroArgs { comp_attr_span: comp_ident.span, comp: comp_lit, backend: backend_path, + derives, }) } } diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index 80281ce750..93bc88911a 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -1,2 +1,16 @@ pub use super::{CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef}; pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests}; + +#[macro_export] +macro_rules! declare_calyx_interface { + ($name:ident($($input:ident),*) -> ($($output:ident),*)) => { + pub trait $name: CalyxFFIComponent { + $( + fn $input(&mut self) -> &mut u64; + )* + $( + fn $output(&self) -> u64; + )* + } + }; +} diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index 9e87468fdf..66172f65dc 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -1,11 +1,19 @@ +use calyx_ffi::declare_calyx_interface; use calyx_ffi::prelude::*; use calyx_ffi::cider_ffi_backend; +declare_calyx_interface! { + In2Out1(lhs, rhs) -> (result) +} + #[calyx_ffi( src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", comp = "main", - backend = cider_ffi_backend + backend = cider_ffi_backend, + derive = [ + In2Out1(lhs, rhs) -> (result) + ] )] struct Adder; @@ -14,9 +22,9 @@ struct Adder; mod tests { use super::*; - fn add(adder: &mut Adder, lhs: u64, rhs: u64) -> u64 { - adder.lhs = lhs; - adder.rhs = rhs; + fn add(adder: &mut I, lhs: u64, rhs: u64) -> u64 { + *adder.lhs() = lhs; + *adder.rhs() = rhs; adder.go(); adder.result() } From 30da407a8e5ca2387c8d6f0518dbeb5b397ada25 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Mon, 5 Aug 2024 18:40:39 -0400 Subject: [PATCH 19/41] I don't know how to use cider2 --- tools/calyx-ffi-macro/src/lib.rs | 14 +++-- tools/calyx-ffi/src/backend/cider.rs | 80 ++++++++++++++++---------- tools/calyx-ffi/src/backend/useless.rs | 10 ++-- tools/tb/examples/calyx/adder.futil | 12 ++-- tools/tb/examples/calyx/test.rs | 1 + 5 files changed, 73 insertions(+), 44 deletions(-) diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index e030a95604..39c29666b9 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -30,6 +30,8 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { .expect("failed to turn quoted path into string"); let backend_macro = args.backend; + let mut input_names = Vec::new(); + let mut output_names = Vec::new(); let mut field_names = vec![]; let mut fields = vec![]; let mut getters = vec![]; @@ -47,6 +49,7 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { fields.push(quote! { pub #port_name: u64 }); + input_names.push(port_name); } calyx_ir::Direction::Output => { fields.push(quote! { @@ -57,6 +60,7 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { self.#port_name } }); + output_names.push(port_name); } calyx_ir::Direction::Inout => { todo!("inout ports not supported yet") @@ -95,23 +99,23 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { } fn init(&mut self, context: &calyx_ir::Context) { - #backend_macro!(@init self, context; #(#field_names),*); + #backend_macro!(@init self, context; #(#input_names),*; #(#output_names),*); } fn reset(&mut self) { - #backend_macro!(@reset self; #(#field_names),*); + #backend_macro!(@reset self; #(#input_names),*; #(#output_names),*); } fn can_tick(&self) -> bool { - #backend_macro!(@can_tick self; #(#field_names),*) + #backend_macro!(@can_tick self; #(#input_names),*; #(#output_names),*) } fn tick(&mut self) { - #backend_macro!(@tick self; #(#field_names),*); + #backend_macro!(@tick self; #(#input_names),*; #(#output_names),*); } fn go(&mut self) { - #backend_macro!(@go self; #(#field_names),*); + #backend_macro!(@go self; #(#input_names),*; #(#output_names),*); } } }; diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs index f67fecab4e..ab6e274e46 100644 --- a/tools/calyx-ffi/src/backend/cider.rs +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -1,29 +1,40 @@ use calyx_ir::Context; -use interp::flatten::structures::{ - context::Context as CiderContext, - environment::{Environment, Simulator}, +use core::panic; +use interp::{ + flatten::{ + flat_ir, + structures::{ + context::Context as CiderContext, environment::Simulator, + }, + }, + values::Value, }; -use std::{mem::MaybeUninit, rc::Rc}; +use std::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); + pub fn from(ctx: &Context, name: &'static str) -> Self { + let ctx = flat_ir::translate(ctx); + let simulator = Simulator::build_simulator(Rc::new(ctx), &None) + .expect("we live on the edge"); 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. + if name == "go" { + return; + } + self.simulator.pin_value(name, Value::from(value, 64)); } pub fn read_port(&self, name: &'static str) -> u64 { - todo!("no way to get port on a component yet I think") + self.simulator + .lookup_port_from_string(&String::from(name)) + .expect("wrong port name") + .as_u64() } pub fn step(&mut self) { @@ -31,6 +42,12 @@ impl CiderFFIBackend { "this function isn't documented so don't know what went wrong", ); } + + pub fn go(&mut self) { + self.simulator.run_program().expect("failed to run program"); + panic!(); + self.step(); // since griffin said so + } } /// Runs the component using cider2. @@ -39,42 +56,45 @@ macro_rules! cider_ffi_backend { (@user_data_type) => { $crate::backend::cider::CiderFFIBackend }; - (@init $dut:ident, $ctx:expr; $($port:ident),*) => { + (@init $dut:ident, $ctx:expr; $($input:ident),*; $($output: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; + (@reset $dut:ident; $($input:ident),*; $($output:ident),*) => { + println!("cider_ffi_backend reset. doesn't work LOL"); + // $dut.done = 0; + // $dut.reset = 1; + // for i in 0..5 { + // $dut.tick(); + // } + // $dut.reset = 0; }; - (@can_tick $dut:ident; $($port:ident),*) => { + (@can_tick $dut:ident; $($input:ident),*; $($output:ident),*) => { true }; - (@tick $dut:ident; $($port:ident),*) => { + (@tick $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("cider_ffi_backend tick"); let cider = unsafe { $dut.user_data.assume_init_mut() }; $( - cider.write_port(stringify!($port), $dut.$port); + cider.write_port(stringify!($input), $dut.$input); )* cider.step(); $( - $dut.$port = cider.read_port(stringify!($port)); + $dut.$output = cider.read_port(stringify!($output)); )* }; - (@go $dut:ident; $($port:ident),*) => { + (@go $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("cider_ffi_backend go"); - $dut.go = 1; - while ($dut.done != 1) { - $dut.tick(); - } - $dut.go = 0; + let cider = unsafe { $dut.user_data.assume_init_mut() }; + $( + cider.write_port(stringify!($input), $dut.$input); + )* + cider.go(); + $( + $dut.$output = cider.read_port(stringify!($output)); + )* }; } diff --git a/tools/calyx-ffi/src/backend/useless.rs b/tools/calyx-ffi/src/backend/useless.rs index b6b472b5bd..c890fdfce2 100644 --- a/tools/calyx-ffi/src/backend/useless.rs +++ b/tools/calyx-ffi/src/backend/useless.rs @@ -4,10 +4,10 @@ macro_rules! useless_ffi_backend { (@user_data_type) => { () // unit type }; - (@init $dut:ident, $ctx:expr; $($port:ident),*) => { + (@init $dut:ident, $ctx:expr; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend init"); }; - (@reset $dut:ident; $($port:ident),*) => { + (@reset $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend reset"); $dut.done = 0; $dut.reset = 1; @@ -16,16 +16,16 @@ macro_rules! useless_ffi_backend { } $dut.reset = 0; }; - (@can_tick $dut:ident; $($port:ident),*) => { + (@can_tick $dut:ident; $($input:ident),*; $($output:ident),*) => { true }; - (@tick $dut:ident; $($port:ident),*) => { + (@tick $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend tick"); if $dut.done == 1 { $dut.done = 0; } }; - (@go $dut:ident; $($port:ident),*) => { + (@go $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend go"); $dut.go = 1; $dut.go = 0; diff --git a/tools/tb/examples/calyx/adder.futil b/tools/tb/examples/calyx/adder.futil index 2adfc08f72..cb1cfa8b7f 100644 --- a/tools/tb/examples/calyx/adder.futil +++ b/tools/tb/examples/calyx/adder.futil @@ -5,9 +5,13 @@ component main(lhs: 64, rhs: 64) -> (result: 64) { adder = std_add(64); } wires { - adder.left = lhs; - adder.right = rhs; - result = adder.out; + group add { + adder.left = lhs; + adder.right = rhs; + result = adder.out; + } + } + control { + add; } - control {} } diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index 66172f65dc..191cfc08c4 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -3,6 +3,7 @@ use calyx_ffi::prelude::*; use calyx_ffi::cider_ffi_backend; +// not necessary, just to show it off declare_calyx_interface! { In2Out1(lhs, rhs) -> (result) } From 785d2dd2a494597904ed1a63aeb24f5f59643997 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:12:50 -0400 Subject: [PATCH 20/41] It works now! only for main components due to cider difficulties --- Cargo.lock | 1 + .../src/flatten/primitives/combinational.rs | 2 +- tools/calyx-ffi/src/backend/cider.rs | 1 - tools/tb/examples/calyx/Cargo.toml | 1 + tools/tb/examples/calyx/adder.futil | 8 +++- tools/tb/examples/calyx/subber.futil | 21 ++++++++ tools/tb/examples/calyx/test.rs | 48 ++++++++++++++----- 7 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 tools/tb/examples/calyx/subber.futil diff --git a/Cargo.lock b/Cargo.lock index ed2e997307..3136f5bb4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,6 +427,7 @@ dependencies = [ "calyx-ffi", "calyx-ir", "interp", + "rand 0.8.5", ] [[package]] diff --git a/interp/src/flatten/primitives/combinational.rs b/interp/src/flatten/primitives/combinational.rs index e1ef437999..f43f1a943c 100644 --- a/interp/src/flatten/primitives/combinational.rs +++ b/interp/src/flatten/primitives/combinational.rs @@ -123,7 +123,7 @@ comb_primitive!(StdSub(left [0], right [1]) -> (out [2]) { // TODO griffin: the old approach is not possible with the way primitives // work. // this is dubious - let result = Value::from(left.as_signed() - right.as_signed(), left.width()); + let result = Value::from(left.as_unsigned() - right.as_unsigned(), left.width()); Ok(Some(result)) }); diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs index ab6e274e46..53a930858a 100644 --- a/tools/calyx-ffi/src/backend/cider.rs +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -45,7 +45,6 @@ impl CiderFFIBackend { pub fn go(&mut self) { self.simulator.run_program().expect("failed to run program"); - panic!(); self.step(); // since griffin said so } } diff --git a/tools/tb/examples/calyx/Cargo.toml b/tools/tb/examples/calyx/Cargo.toml index a0ac39f852..581eaff62e 100644 --- a/tools/tb/examples/calyx/Cargo.toml +++ b/tools/tb/examples/calyx/Cargo.toml @@ -19,3 +19,4 @@ path = "test.rs" calyx-ffi = { path = "../../../calyx-ffi" } interp = { path = "../../../../interp" } calyx-ir.workspace = true +rand = "0.8.5" diff --git a/tools/tb/examples/calyx/adder.futil b/tools/tb/examples/calyx/adder.futil index cb1cfa8b7f..1730a7c5fd 100644 --- a/tools/tb/examples/calyx/adder.futil +++ b/tools/tb/examples/calyx/adder.futil @@ -3,15 +3,21 @@ import "primitives/core.futil"; component main(lhs: 64, rhs: 64) -> (result: 64) { cells { adder = std_add(64); + temp = std_reg(64); } wires { group add { adder.left = lhs; adder.right = rhs; - result = adder.out; + temp.in = adder.out; + temp.write_en = 1'b1; + add[done] = temp.done; } + result = temp.out; } control { add; } } + + diff --git a/tools/tb/examples/calyx/subber.futil b/tools/tb/examples/calyx/subber.futil new file mode 100644 index 0000000000..260456f938 --- /dev/null +++ b/tools/tb/examples/calyx/subber.futil @@ -0,0 +1,21 @@ +import "primitives/core.futil"; + +component main(lhs: 64, rhs: 64) -> (result: 64) { + cells { + subber = std_sub(64); + temp = std_reg(64); + } + wires { + group sub { + subber.left = lhs; + subber.right = rhs; + temp.in = subber.out; + temp.write_en = 1'b1; + sub[done] = temp.done; + } + result = temp.out; + } + control { + sub; + } +} diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index 191cfc08c4..f70fcdec5c 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -18,26 +18,50 @@ declare_calyx_interface! { )] struct Adder; +#[calyx_ffi( + src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/subber.futil", + comp = "main", + backend = cider_ffi_backend, + derive = [ + In2Out1(lhs, rhs) -> (result) + ] +)] +struct Subber; + #[cfg(test)] #[calyx_ffi_tests] mod tests { use super::*; + use rand::Rng; + use std::mem; - fn add(adder: &mut I, lhs: u64, rhs: u64) -> u64 { - *adder.lhs() = lhs; - *adder.rhs() = rhs; - adder.go(); - adder.result() + // inv: the left argument will always be greater than the right + fn fuzz_in2out1 u64>( + comp: &mut I, + oracle: &F, + ) { + comp.reset(); + let mut rng = rand::thread_rng(); + for (mut x, mut y) in (0..100).map(|_| (rng.gen(), rng.gen())) { + if y > x { + mem::swap(&mut x, &mut y); + } + *comp.lhs() = x; + *comp.rhs() = y; + comp.go(); + assert_eq!(oracle(x, y), comp.result(), "testing f({}, {})", x, y); + } } #[calyx_ffi_test] - fn test(adder: &mut Adder) { + fn test_add(adder: &mut Adder) { println!("testing adder"); - adder.reset(); - for i in 0..10 { - for j in 0..10 { - assert!(add(adder, i, j) == i + j); - } - } + fuzz_in2out1(adder, &|x, y| x.wrapping_add(y)) + } + + #[calyx_ffi_test] + fn test_sub(subber: &mut Subber) { + println!("testing subber"); + fuzz_in2out1(subber, &|x, y| x - y) } } From 78736fe6f3f267e18cb2d49611884569a9b31931 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:56:56 -0400 Subject: [PATCH 21/41] I give up on fud2 --- fud2/fud-core/src/run.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fud2/fud-core/src/run.rs b/fud2/fud-core/src/run.rs index 8e21228572..3e1021473f 100644 --- a/fud2/fud-core/src/run.rs +++ b/fud2/fud-core/src/run.rs @@ -406,10 +406,6 @@ impl<'a> Run<'a> { // Emit preamble. emitter.var("build-tool", &self.global_config.exe)?; emitter.rule("get-rsrc", "$build-tool get-rsrc $out")?; - emitter.var( - "caller-dir", - &env::current_dir()?.to_string_lossy().to_string(), - )?; writeln!(emitter.out)?; // Emit the setup for each operation used in the plan, only once. From e3b57d3a7292b52d82c91db0d44873f49e839d00 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:03:31 -0400 Subject: [PATCH 22/41] Empty From 37a16d656a32cf0ad1396d4729e810e04a2fa93e Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:14:46 -0400 Subject: [PATCH 23/41] Ok --- fud2/fud-core/src/run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/fud2/fud-core/src/run.rs b/fud2/fud-core/src/run.rs index 3e1021473f..504c3d3645 100644 --- a/fud2/fud-core/src/run.rs +++ b/fud2/fud-core/src/run.rs @@ -4,7 +4,6 @@ use crate::utils::relative_path; use camino::{Utf8Path, Utf8PathBuf}; use itertools::Itertools; use std::collections::{HashMap, HashSet}; -use std::env; use std::io::Write; use std::process::{Command, ExitStatus}; From 62060d622078359a5709d83c3971bc5bb5e3a2aa Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:21:56 -0400 Subject: [PATCH 24/41] Finish giving up on fud --- fud2/scripts/tb.rhai | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fud2/scripts/tb.rhai b/fud2/scripts/tb.rhai index 1f6e041899..c53796e551 100644 --- a/fud2/scripts/tb.rhai +++ b/fud2/scripts/tb.rhai @@ -1,14 +1,14 @@ -import "calyx" as calyx; +// import "calyx" as calyx; +// +// let tb_state = state("tb", []); +// +// fn tb_setup(e) { +// e.config_var("calyx-tb-exe", "tb.exe"); +// e.config_var("calyx-tb-test", "tb.test"); +// e.config_var("calyx-tb-config-file", "tb.config-file"); +// e.config_var_or("calyx-tb-flags", "tb.flags", ""); +// e.config_var_or("calyx-tb-using", "tb.using"); +// // e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using $calyx-tb-using --config $calyx-tb-config-file $calyx-tb-flags"); +// } -let tb_state = state("tb", []); - -fn tb_setup(e) { - e.config_var("calyx-tb-exe", "tb.exe"); - e.config_var("calyx-tb-test", "tb.test"); - e.config_var("calyx-tb-config-file", "tb.config-file"); - e.config_var_or("calyx-tb-flags", "tb.flags", ""); - e.config_var_or("calyx-tb-using", "tb.using"); - e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using $calyx-tb-using --config $calyx-tb-config-file $calyx-tb-flags"); -} - -rule([tb_setup], calyx::verilog_state, tb_state, "verilog-to-tb"); +// rule([tb_setup], calyx::verilog_state, tb_state, "verilog-to-tb"); From dc407ad93a122ae213084abbd7e3bea0f537cae1 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:25:51 -0400 Subject: [PATCH 25/41] clippy --- tools/calyx-ffi/src/backend/cider.rs | 4 ++-- tools/calyx-ffi/src/lib.rs | 2 +- tools/calyx-ffi/tests/test.rs | 2 +- tools/tb/examples/calyx/test.rs | 4 ++-- tools/tb/src/driver.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs index 53a930858a..9f7e3438db 100644 --- a/tools/calyx-ffi/src/backend/cider.rs +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -1,5 +1,4 @@ use calyx_ir::Context; -use core::panic; use interp::{ flatten::{ flat_ir, @@ -16,7 +15,8 @@ pub struct CiderFFIBackend { } impl CiderFFIBackend { - pub fn from(ctx: &Context, name: &'static str) -> Self { + pub fn from(ctx: &Context, _name: &'static str) -> Self { + // TODO(ethan, maybe griffin): use _name to select the component somehow let ctx = flat_ir::translate(ctx); let simulator = Simulator::build_simulator(Rc::new(ctx), &None) .expect("we live on the edge"); diff --git a/tools/calyx-ffi/src/lib.rs b/tools/calyx-ffi/src/lib.rs index ccecc28b48..b4644d545a 100644 --- a/tools/calyx-ffi/src/lib.rs +++ b/tools/calyx-ffi/src/lib.rs @@ -55,7 +55,7 @@ impl CalyxFFI { pub fn new_comp( &mut self, ) -> CalyxFFIComponentRef { - let mut comp = unsafe { T::default() }; + let mut comp = 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 diff --git a/tools/calyx-ffi/tests/test.rs b/tools/calyx-ffi/tests/test.rs index 238310beda..1e025264a0 100644 --- a/tools/calyx-ffi/tests/test.rs +++ b/tools/calyx-ffi/tests/test.rs @@ -3,7 +3,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", + src = "/home/runner/work/calyx/calyx/tools/calyx-ffi/tests/file.futil", comp = "main", backend = useless_ffi_backend )] diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index f70fcdec5c..b0a9135ddc 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -9,7 +9,7 @@ declare_calyx_interface! { } #[calyx_ffi( - src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", + src = "/home/runner/work/calyx/calyx/tools/tb/examples/calyx/adder.futil", comp = "main", backend = cider_ffi_backend, derive = [ @@ -19,7 +19,7 @@ declare_calyx_interface! { struct Adder; #[calyx_ffi( - src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/subber.futil", + src = "/home/runner/work/calyx/calyx/tools/tb/examples/calyx/subber.futil", comp = "main", backend = cider_ffi_backend, derive = [ diff --git a/tools/tb/src/driver.rs b/tools/tb/src/driver.rs index 4e72be9a0b..9859003986 100644 --- a/tools/tb/src/driver.rs +++ b/tools/tb/src/driver.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - fs, io, + io, path::{Path, PathBuf}, }; From b6cf3ab3b9d73ba0222804d52d512af332324c57 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:58:41 -0500 Subject: [PATCH 26/41] Fix nightly path stuff --- Cargo.toml | 4 +-- tools/calyx-ffi-macro/Cargo.toml | 28 +++++++-------- tools/calyx-ffi-macro/rust-toolchain.toml | 2 ++ tools/calyx-ffi-macro/src/calyx.rs | 11 +++--- tools/calyx-ffi-macro/src/lib.rs | 42 ++++++++++++++++++++--- tools/calyx-ffi/rust-toolchain.toml | 2 ++ tools/calyx-ffi/src/prelude.rs | 1 + tools/calyx-ffi/tests/test.rs | 4 +-- tools/tb/examples/calyx/test.rs | 4 +-- 9 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 tools/calyx-ffi-macro/rust-toolchain.toml create mode 100644 tools/calyx-ffi/rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 27b717b95d..ab610c6394 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ members = [ "tools/yxi", "tools/calyx-writer", "tools/tb", - "tools/calyx-ffi-macro", - "tools/calyx-ffi", + # "tools/calyx-ffi-macro", + # "tools/calyx-ffi", "tools/tb/examples/calyx", ] exclude = ["site"] diff --git a/tools/calyx-ffi-macro/Cargo.toml b/tools/calyx-ffi-macro/Cargo.toml index bff0cd63ae..10062da404 100644 --- a/tools/calyx-ffi-macro/Cargo.toml +++ b/tools/calyx-ffi-macro/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "calyx-ffi-macro" -authors.workspace = true -license-file.workspace = true -keywords.workspace = true -repository.workspace = true -readme.workspace = true -description.workspace = true -categories.workspace = true -homepage.workspace = true -edition.workspace = true -version.workspace = true -rust-version.workspace = true +authors = ["The Calyx Team"] +license-file = "LICENSE" +keywords = ["ir", "compiler"] +repository = "https://github.com/calyxir/calyx" +readme = "README.md" +description = "Compiler Infrastructure for Hardware Accelerator Generation" +categories = ["compilers"] +homepage = "https://calyxir.org" +edition = "2021" +version = "0.7.1" +rust-version = "1.67" [lib] proc-macro = true @@ -19,7 +19,7 @@ proc-macro = true proc-macro2 = "1.0.86" quote = "1.0.36" syn = { version = "2.0.69", features = ["full", "visit"] } -calyx-utils.workspace = true -calyx-frontend.workspace = true -calyx-ir.workspace = true +calyx-utils = { path = "../../calyx-utils/" } +calyx-frontend = { path = "../../calyx-frontend/" } +calyx-ir = { path = "../../calyx-ir/" } # bigint = "4.4.3" diff --git a/tools/calyx-ffi-macro/rust-toolchain.toml b/tools/calyx-ffi-macro/rust-toolchain.toml new file mode 100644 index 0000000000..5d56faf9ae --- /dev/null +++ b/tools/calyx-ffi-macro/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/tools/calyx-ffi-macro/src/calyx.rs b/tools/calyx-ffi-macro/src/calyx.rs index aee8bbfb52..4039321976 100644 --- a/tools/calyx-ffi-macro/src/calyx.rs +++ b/tools/calyx-ffi-macro/src/calyx.rs @@ -17,16 +17,17 @@ impl CalyxComponent { pub fn parse_calyx_file( args: &CalyxFFIMacroArgs, + file: PathBuf, ) -> Result { // 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(args.src.clone()), - &lib_path, - ) - .map_err(|err| util::compile_error(&args.src_attr_span, err.message()))?; + let ws = + calyx_frontend::Workspace::construct(&Some(file.clone()), &lib_path) + .map_err(|err| { + util::compile_error(&args.src_attr_span, err.message()) + })?; let ctx = calyx_ir::from_ast::ast_to_ir(ws).map_err(|err| { util::compile_error(&args.src_attr_span, err.message()) })?; diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index 39c29666b9..fef181e1c9 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -1,3 +1,7 @@ +#![feature(proc_macro_span)] + +use std::{env, path::PathBuf}; + use parse::CalyxFFIMacroArgs; use proc_macro::TokenStream; use quote::quote; @@ -7,15 +11,42 @@ mod calyx; mod parse; mod util; +fn get_first_token_span(ts: &TokenStream) -> Option { + let mut iter = ts.clone().into_iter(); + iter.next().map(|token| token.span()) +} + #[proc_macro_attribute] pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { + let source_manifest_dir = PathBuf::from( + env::vars() + .find(|(name, _)| name == "CARGO_MANIFEST_DIR") + .expect("caller of calyx_ffi did not use cargo to build project") + .1, + ); + + let source_path = + get_first_token_span(&attrs).unwrap().source_file().path(); + 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 given_path = args.src.to_string_lossy().to_string(); + // panic!( + // "{} {} {}", + // source_manifest_dir.to_string_lossy(), + // source_path.path().to_string_lossy(), + // given_path + // ); + + let mut path = source_manifest_dir; + path.push(source_path); + path.pop(); // remove the file + path.push(given_path); + let path = path; // - let comp = calyx::parse_calyx_file(&args); + let comp = calyx::parse_calyx_file(&args, path.clone()); if let Err(error) = comp { return error; } @@ -26,8 +57,11 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { 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 comp_path = syn::parse_str::(&format!( + "\"{}\"", + path.to_string_lossy() + )) + .expect("failed to turn quoted path into string"); let backend_macro = args.backend; let mut input_names = Vec::new(); diff --git a/tools/calyx-ffi/rust-toolchain.toml b/tools/calyx-ffi/rust-toolchain.toml new file mode 100644 index 0000000000..5d56faf9ae --- /dev/null +++ b/tools/calyx-ffi/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index 93bc88911a..ad56ec06a9 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -1,5 +1,6 @@ pub use super::{CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef}; pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests}; +pub use calyx_ir; #[macro_export] macro_rules! declare_calyx_interface { diff --git a/tools/calyx-ffi/tests/test.rs b/tools/calyx-ffi/tests/test.rs index 1e025264a0..3d9122fd63 100644 --- a/tools/calyx-ffi/tests/test.rs +++ b/tools/calyx-ffi/tests/test.rs @@ -3,9 +3,9 @@ use calyx_ffi::prelude::*; use calyx_ffi::useless_ffi_backend; #[calyx_ffi( - src = "/home/runner/work/calyx/calyx/tools/calyx-ffi/tests/file.futil", + src = "file.futil", comp = "main", - backend = useless_ffi_backend + backend = useless_ffi_backend )] struct Main; diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index b0a9135ddc..f70fcdec5c 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -9,7 +9,7 @@ declare_calyx_interface! { } #[calyx_ffi( - src = "/home/runner/work/calyx/calyx/tools/tb/examples/calyx/adder.futil", + src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", comp = "main", backend = cider_ffi_backend, derive = [ @@ -19,7 +19,7 @@ declare_calyx_interface! { struct Adder; #[calyx_ffi( - src = "/home/runner/work/calyx/calyx/tools/tb/examples/calyx/subber.futil", + src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/subber.futil", comp = "main", backend = cider_ffi_backend, derive = [ From f2de02c20004d76edb4c1fbaa181ef0c9a47f99c Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 7 Nov 2024 00:12:54 -0500 Subject: [PATCH 27/41] Time to start cleanup --- Cargo.lock | 6 +- Cargo.toml | 5 +- tools/calyx-ffi-macro/Cargo.lock | 554 +++++++ tools/calyx-ffi-macro/Cargo.toml | 27 +- tools/calyx-ffi-macro/rust-toolchain.toml | 2 - tools/calyx-ffi-macro/src/lib.rs | 13 - tools/calyx-ffi/Cargo.lock | 1841 +++++++++++++++++++++ tools/calyx-ffi/rust-toolchain.toml | 2 - tools/calyx-ffi/src/prelude.rs | 2 +- tools/calyx-ffi/tests/test.rs | 4 +- tools/tb/Cargo.toml | 1 - tools/tb/examples/calyx/test.rs | 8 +- 12 files changed, 2420 insertions(+), 45 deletions(-) create mode 100644 tools/calyx-ffi-macro/Cargo.lock delete mode 100644 tools/calyx-ffi-macro/rust-toolchain.toml create mode 100644 tools/calyx-ffi/Cargo.lock delete mode 100644 tools/calyx-ffi/rust-toolchain.toml diff --git a/Cargo.lock b/Cargo.lock index 3136f5bb4f..a018cd0d6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,7 +432,7 @@ dependencies = [ [[package]] name = "calyx-ffi-macro" -version = "0.7.1" +version = "0.0.0" dependencies = [ "calyx-frontend", "calyx-ir", @@ -570,9 +570,9 @@ checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" [[package]] name = "cargo_toml" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4895c018bb228aa6b3ba1a0285543fcb4b704734c3fb1f72afaa75aa769500c1" +checksum = "88da5a13c620b4ca0078845707ea9c3faf11edbc3ffd8497d11d686211cd1ac0" dependencies = [ "serde", "toml", diff --git a/Cargo.toml b/Cargo.toml index ab610c6394..ddf6625183 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,10 @@ members = [ "tools/yxi", "tools/calyx-writer", "tools/tb", - # "tools/calyx-ffi-macro", - # "tools/calyx-ffi", + "tools/calyx-ffi-macro", + "tools/calyx-ffi", "tools/tb/examples/calyx", ] -exclude = ["site"] [workspace.package] authors = ["The Calyx Team"] diff --git a/tools/calyx-ffi-macro/Cargo.lock b/tools/calyx-ffi-macro/Cargo.lock new file mode 100644 index 0000000000..4d7d0901f2 --- /dev/null +++ b/tools/calyx-ffi-macro/Cargo.lock @@ -0,0 +1,554 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "calyx-ffi-macro" +version = "0.0.0" +dependencies = [ + "calyx-frontend", + "calyx-ir", + "calyx-utils", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "calyx-frontend" +version = "0.7.1" +dependencies = [ + "atty", + "calyx-utils", + "itertools", + "lazy_static", + "linked-hash-map", + "log", + "pest", + "pest_consume", + "pest_derive", + "smallvec", + "strum", + "strum_macros", +] + +[[package]] +name = "calyx-ir" +version = "0.7.1" +dependencies = [ + "calyx-frontend", + "calyx-utils", + "itertools", + "linked-hash-map", + "log", + "petgraph", + "smallvec", + "string-interner", +] + +[[package]] +name = "calyx-utils" +version = "0.7.1" +dependencies = [ + "atty", + "itertools", + "petgraph", + "serde_json", + "string-interner", + "symbol_table", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.1", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_consume" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79447402d15d18e7142e14c72f2e63fa3d155be1bc5b70b3ccbb610ac55f536b" +dependencies = [ + "pest", + "pest_consume_macros", + "pest_derive", +] + +[[package]] +name = "pest_consume_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8630a7a899cb344ec1c16ba0a6b24240029af34bdc0a21f84e411d7f793f29" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "string-interner" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e2531d8525b29b514d25e275a43581320d587b86db302b9a7e464bac579648" +dependencies = [ + "cfg-if", + "hashbrown 0.11.2", + "serde", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.87", +] + +[[package]] +name = "symbol_table" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "828f672b631c220bf6ea8a1d3b82c7d0fc998e5ba8373383d8604bc1e2a6245a" +dependencies = [ + "ahash", + "hashbrown 0.12.3", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/tools/calyx-ffi-macro/Cargo.toml b/tools/calyx-ffi-macro/Cargo.toml index 10062da404..e9d1eddc15 100644 --- a/tools/calyx-ffi-macro/Cargo.toml +++ b/tools/calyx-ffi-macro/Cargo.toml @@ -1,16 +1,15 @@ [package] name = "calyx-ffi-macro" -authors = ["The Calyx Team"] -license-file = "LICENSE" -keywords = ["ir", "compiler"] -repository = "https://github.com/calyxir/calyx" -readme = "README.md" -description = "Compiler Infrastructure for Hardware Accelerator Generation" -categories = ["compilers"] -homepage = "https://calyxir.org" -edition = "2021" -version = "0.7.1" -rust-version = "1.67" +authors.workspace = true +license-file.workspace = true +keywords.workspace = true +repository.workspace = true +readme.workspace = true +description.workspace = true +categories.workspace = true +homepage.workspace = true +edition.workspace = true +rust-version.workspace = true [lib] proc-macro = true @@ -19,7 +18,7 @@ proc-macro = true proc-macro2 = "1.0.86" quote = "1.0.36" syn = { version = "2.0.69", features = ["full", "visit"] } -calyx-utils = { path = "../../calyx-utils/" } -calyx-frontend = { path = "../../calyx-frontend/" } -calyx-ir = { path = "../../calyx-ir/" } +calyx-utils.workspace = true +calyx-frontend.workspace = true +calyx-ir.workspace = true # bigint = "4.4.3" diff --git a/tools/calyx-ffi-macro/rust-toolchain.toml b/tools/calyx-ffi-macro/rust-toolchain.toml deleted file mode 100644 index 5d56faf9ae..0000000000 --- a/tools/calyx-ffi-macro/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index fef181e1c9..1a2223bc48 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(proc_macro_span)] - use std::{env, path::PathBuf}; use parse::CalyxFFIMacroArgs; @@ -25,23 +23,12 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { .1, ); - let source_path = - get_first_token_span(&attrs).unwrap().source_file().path(); - let args = parse_macro_input!(attrs as CalyxFFIMacroArgs); let item_struct = parse_macro_input!(item as syn::ItemStruct); let name = item_struct.ident; let given_path = args.src.to_string_lossy().to_string(); - // panic!( - // "{} {} {}", - // source_manifest_dir.to_string_lossy(), - // source_path.path().to_string_lossy(), - // given_path - // ); let mut path = source_manifest_dir; - path.push(source_path); - path.pop(); // remove the file path.push(given_path); let path = path; diff --git a/tools/calyx-ffi/Cargo.lock b/tools/calyx-ffi/Cargo.lock new file mode 100644 index 0000000000..b980509297 --- /dev/null +++ b/tools/calyx-ffi/Cargo.lock @@ -0,0 +1,1841 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "argh" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" +dependencies = [ + "argh_shared", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "argh_shared" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.87", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "btor2i" +version = "0.1.0" +dependencies = [ + "bitvec", + "btor2tools", + "clap", + "num-bigint", + "num-integer", + "num-traits", + "tempfile", + "thiserror", +] + +[[package]] +name = "btor2tools" +version = "1.1.0" +source = "git+https://github.com/obhalerao/btor2tools.rs#8d34a0dc96b447fb008ffbb6f15d1ec947eaefb8" +dependencies = [ + "btor2tools-sys", + "thiserror", +] + +[[package]] +name = "btor2tools-sys" +version = "1.1.0" +source = "git+https://github.com/obhalerao/btor2tools-sys#e5d1c44220cb5a02b30b538c5ade70102109904a" +dependencies = [ + "bindgen", + "copy_dir", +] + +[[package]] +name = "calyx-ffi" +version = "0.0.0" +dependencies = [ + "calyx-ffi-macro", + "calyx-frontend", + "calyx-ir", + "interp", +] + +[[package]] +name = "calyx-ffi-macro" +version = "0.0.0" +dependencies = [ + "calyx-frontend", + "calyx-ir", + "calyx-utils", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "calyx-frontend" +version = "0.7.1" +dependencies = [ + "atty", + "calyx-utils", + "itertools", + "lazy_static", + "linked-hash-map", + "log", + "pest", + "pest_consume", + "pest_derive", + "smallvec", + "strum", + "strum_macros", +] + +[[package]] +name = "calyx-ir" +version = "0.7.1" +dependencies = [ + "calyx-frontend", + "calyx-utils", + "itertools", + "linked-hash-map", + "log", + "petgraph", + "smallvec", + "string-interner", +] + +[[package]] +name = "calyx-opt" +version = "0.7.1" +dependencies = [ + "calyx-ir", + "calyx-utils", + "itertools", + "lazy_static", + "linked-hash-map", + "log", + "petgraph", + "serde", + "serde_json", + "smallvec", +] + +[[package]] +name = "calyx-utils" +version = "0.7.1" +dependencies = [ + "atty", + "itertools", + "petgraph", + "serde", + "serde_json", + "string-interner", + "symbol_table", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "copy_dir" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "543d1dd138ef086e2ff05e3a48cf9da045da2033d16f8538fd76b86cd49b2ca3" +dependencies = [ + "walkdir", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fraction" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad18174c73e668720cf9961281ea94a10a8c4003c2b2ad7117252109e5423a3f" +dependencies = [ + "lazy_static", + "num", + "serde", + "serde_derive", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "ibig" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1fcc7f316b2c079dde77564a1360639c1a956a23fa96122732e416cb10717bb" +dependencies = [ + "cfg-if", + "num-traits", + "rand", + "serde", + "static_assertions", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.1", +] + +[[package]] +name = "interp" +version = "0.1.1" +dependencies = [ + "ahash 0.8.11", + "argh", + "base64", + "bitvec", + "btor2i", + "calyx-frontend", + "calyx-ir", + "calyx-opt", + "calyx-utils", + "ciborium", + "fraction", + "ibig", + "itertools", + "lazy_static", + "once_cell", + "owo-colors", + "pest", + "pest_consume", + "pest_derive", + "petgraph", + "rustyline", + "serde", + "serde_json", + "serde_with", + "slog", + "slog-async", + "slog-term", + "smallvec", + "thiserror", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_consume" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79447402d15d18e7142e14c72f2e63fa3d155be1bc5b70b3ccbb610ac55f536b" +dependencies = [ + "pest", + "pest_consume_macros", + "pest_derive", +] + +[[package]] +name = "pest_consume_macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8630a7a899cb344ec1c16ba0a6b24240029af34bdc0a21f84e411d7f793f29" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.87", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "rustyline" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1cd5ae51d3f7bf65d7969d579d502168ef578f289452bd8ccc91de28fda20e" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "clipboard-win", + "dirs-next", + "fd-lock", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + +[[package]] +name = "slog-async" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84" +dependencies = [ + "crossbeam-channel", + "slog", + "take_mut", + "thread_local", +] + +[[package]] +name = "slog-term" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" +dependencies = [ + "is-terminal", + "slog", + "term", + "thread_local", + "time", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + +[[package]] +name = "string-interner" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e2531d8525b29b514d25e275a43581320d587b86db302b9a7e464bac579648" +dependencies = [ + "cfg-if", + "hashbrown 0.11.2", + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.87", +] + +[[package]] +name = "symbol_table" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "828f672b631c220bf6ea8a1d3b82c7d0fc998e5ba8373383d8604bc1e2a6245a" +dependencies = [ + "ahash 0.7.8", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] diff --git a/tools/calyx-ffi/rust-toolchain.toml b/tools/calyx-ffi/rust-toolchain.toml deleted file mode 100644 index 5d56faf9ae..0000000000 --- a/tools/calyx-ffi/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index ad56ec06a9..f56ca89bab 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -3,7 +3,7 @@ pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests}; pub use calyx_ir; #[macro_export] -macro_rules! declare_calyx_interface { +macro_rules! declare_calyx_ffi_interface { ($name:ident($($input:ident),*) -> ($($output:ident),*)) => { pub trait $name: CalyxFFIComponent { $( diff --git a/tools/calyx-ffi/tests/test.rs b/tools/calyx-ffi/tests/test.rs index 3d9122fd63..50e019ec51 100644 --- a/tools/calyx-ffi/tests/test.rs +++ b/tools/calyx-ffi/tests/test.rs @@ -3,9 +3,9 @@ use calyx_ffi::prelude::*; use calyx_ffi::useless_ffi_backend; #[calyx_ffi( - src = "file.futil", + src = "tests/file.futil", comp = "main", - backend = useless_ffi_backend + backend = useless_ffi_backend )] struct Main; diff --git a/tools/tb/Cargo.toml b/tools/tb/Cargo.toml index fea9e34fc5..fea0e11218 100644 --- a/tools/tb/Cargo.toml +++ b/tools/tb/Cargo.toml @@ -9,7 +9,6 @@ description.workspace = true categories.workspace = true homepage.workspace = true edition.workspace = true -version = "0.0.0" rust-version.workspace = true [dependencies] diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx/test.rs index f70fcdec5c..3978511977 100644 --- a/tools/tb/examples/calyx/test.rs +++ b/tools/tb/examples/calyx/test.rs @@ -1,15 +1,15 @@ -use calyx_ffi::declare_calyx_interface; +use calyx_ffi::declare_calyx_ffi_interface; use calyx_ffi::prelude::*; use calyx_ffi::cider_ffi_backend; // not necessary, just to show it off -declare_calyx_interface! { +declare_calyx_ffi_interface! { In2Out1(lhs, rhs) -> (result) } #[calyx_ffi( - src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/adder.futil", + src = "adder.futil", comp = "main", backend = cider_ffi_backend, derive = [ @@ -19,7 +19,7 @@ declare_calyx_interface! { struct Adder; #[calyx_ffi( - src = "/Users/ethan/Documents/GitHub/calyx/tools/tb/examples/calyx/subber.futil", + src = "subber.futil", comp = "main", backend = cider_ffi_backend, derive = [ From e0386dad8c89d08724b79632c6586eadd9d14926 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 7 Nov 2024 00:23:07 -0500 Subject: [PATCH 28/41] Revert more fud2 stuff --- fud2/scripts/tb.rhai | 14 ----- fud2/src/lib.rs | 17 ------- .../tests__test@calyx_cider-debug.snap | 51 ------------------- .../tests__test@plan_verilog-to-tb.snap.new | 13 +++++ 4 files changed, 13 insertions(+), 82 deletions(-) delete mode 100644 fud2/scripts/tb.rhai delete mode 100644 fud2/tests/snapshots/tests__test@calyx_cider-debug.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new diff --git a/fud2/scripts/tb.rhai b/fud2/scripts/tb.rhai deleted file mode 100644 index c53796e551..0000000000 --- a/fud2/scripts/tb.rhai +++ /dev/null @@ -1,14 +0,0 @@ -// import "calyx" as calyx; -// -// let tb_state = state("tb", []); -// -// fn tb_setup(e) { -// e.config_var("calyx-tb-exe", "tb.exe"); -// e.config_var("calyx-tb-test", "tb.test"); -// e.config_var("calyx-tb-config-file", "tb.config-file"); -// e.config_var_or("calyx-tb-flags", "tb.flags", ""); -// e.config_var_or("calyx-tb-using", "tb.using"); -// // e.rule("verilog-to-tb", "$calyx-tb-exe $in --test $caller-dir/$calyx-tb-test --using $calyx-tb-using --config $calyx-tb-config-file $calyx-tb-flags"); -// } - -// rule([tb_setup], calyx::verilog_state, tb_state, "verilog-to-tb"); diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index 2a0fec9303..dcef0adf96 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -83,21 +83,6 @@ fn setup_mrxl( (mrxl, mrxl_setup) } -fn setup_tb(bld: &mut DriverBuilder, verilog: StateRef) { - let tb = bld.state("tb", &[]); - let tb_setup = bld.setup("Testbench executable", |e| { - e.var("calyx-tb-exe", "tb")?; - e.config_var("calyx-tb-test", "tb.test")?; // todo multi input op - e.config_var("calyx-tb-config-file", "tb.config-file")?; - e.rule( - "calyx-to-tb", - "$calyx-tb-exe $in --test $calyx-tb-test --using cocotb --config $calyx-tb-config-file", - )?; - Ok(()) - }); - bld.rule(&[tb_setup], verilog, tb, "test"); -} - pub fn build_driver(bld: &mut DriverBuilder) { // The verilog state let verilog = bld.state("verilog", &["sv", "v"]); @@ -108,8 +93,6 @@ pub fn build_driver(bld: &mut DriverBuilder) { // MrXL. setup_mrxl(bld, calyx); - setup_tb(bld, verilog); - // Shared machinery for RTL simulators. let dat = bld.state("dat", &["json"]); let vcd = bld.state("vcd", &["vcd"]); diff --git a/fud2/tests/snapshots/tests__test@calyx_cider-debug.snap b/fud2/tests/snapshots/tests__test@calyx_cider-debug.snap deleted file mode 100644 index a88b6ace90..0000000000 --- a/fud2/tests/snapshots/tests__test@calyx_cider-debug.snap +++ /dev/null @@ -1,51 +0,0 @@ ---- -source: fud2/tests/tests.rs -description: "emit request: calyx -> cider-debug" ---- -build-tool = fud2 -rule get-rsrc - command = $build-tool get-rsrc $out - -python = python3 -build json-dat.py: get-rsrc -rule hex-data - command = $python json-dat.py --from-json $in $out -rule json-data - command = $python json-dat.py --to-json $out $in -sim_data = /test/data.json -datadir = sim_data -build $datadir: hex-data $sim_data | json-dat.py -rule sim-run - command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out -cycle-limit = 500000000 - -calyx-base = /test/calyx -calyx-exe = $calyx-base/target/debug/calyx -args = -rule calyx - command = $calyx-exe -l $calyx-base -b $backend $args $in > $out -rule calyx-pass - command = $calyx-exe -l $calyx-base -p $pass $args $in > $out -flags = -p none -rule calyx-with-flags - command = $calyx-exe -l $calyx-base $flags $args $in > $out - -build tb.sv: get-rsrc - -cider-exe = $calyx-base/target/debug/cider -cider-converter = $calyx-base/target/debug/cider-data-converter -rule run-cider-debug - command = $cider-exe -l $calyx-base --data data.dump $in debug || true - pool = console -rule run-cider - command = $cider-exe -l $calyx-base --data data.dump $in > $out -rule dump-to-interp - command = $cider-converter --to cider $in > $out -rule interp-to-dump - command = $cider-converter --to json $in > $out -build data.dump: dump-to-interp $sim_data | $cider-converter - -build pseudo_cider: calyx-with-flags _from_stdin_calyx.futil -build _to_stdout_cider-debug: run-cider-debug pseudo_cider | data.dump - -default _to_stdout_cider-debug diff --git a/fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new b/fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new new file mode 100644 index 0000000000..882f318b80 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new @@ -0,0 +1,13 @@ +--- +source: fud2/tests/tests.rs +assertion_line: 66 +description: "emit plan: verilog-to-tb" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + + +build /output.ext: verilog-to-tb /input.ext + +default /output.ext From e8cc729c7c6ebc10331a8b2d9642e075b5027ed2 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 7 Nov 2024 00:25:25 -0500 Subject: [PATCH 29/41] clippy --- tools/calyx-ffi-macro/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index 1a2223bc48..220231cc44 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -9,11 +9,6 @@ mod calyx; mod parse; mod util; -fn get_first_token_span(ts: &TokenStream) -> Option { - let mut iter = ts.clone().into_iter(); - iter.next().map(|token| token.span()) -} - #[proc_macro_attribute] pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let source_manifest_dir = PathBuf::from( From a14dffcafbcc2ea1b0e12b6f89d117a791e0c3d2 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Thu, 7 Nov 2024 01:17:08 -0500 Subject: [PATCH 30/41] The tool is really sus not working for no reason --- Cargo.toml | 2 +- tools/tb/README.md | 17 ++- .../{calyx => calyx-native}/Cargo.toml | 0 .../{calyx => calyx-native}/adder.futil | 0 .../{calyx => calyx-native}/subber.futil | 0 .../examples/{calyx => calyx-native}/test.rs | 0 tools/tb/examples/calyx-tb/adder.futil | 23 ++++ tools/tb/examples/calyx-tb/main.rs | 23 ++++ tools/tb/src/builtin_plugins/calyx.rs | 104 +++++++++++------- tools/tb/src/cli.rs | 4 +- 10 files changed, 129 insertions(+), 44 deletions(-) rename tools/tb/examples/{calyx => calyx-native}/Cargo.toml (100%) rename tools/tb/examples/{calyx => calyx-native}/adder.futil (100%) rename tools/tb/examples/{calyx => calyx-native}/subber.futil (100%) rename tools/tb/examples/{calyx => calyx-native}/test.rs (100%) create mode 100644 tools/tb/examples/calyx-tb/adder.futil create mode 100644 tools/tb/examples/calyx-tb/main.rs diff --git a/Cargo.toml b/Cargo.toml index ddf6625183..eab8e5e7ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ "tools/tb", "tools/calyx-ffi-macro", "tools/calyx-ffi", - "tools/tb/examples/calyx", + "tools/tb/examples/calyx-native", ] [workspace.package] diff --git a/tools/tb/README.md b/tools/tb/README.md index 19a34aabe5..3134dffa44 100644 --- a/tools/tb/README.md +++ b/tools/tb/README.md @@ -17,15 +17,21 @@ There are two ways to use `tb`: ### Directly For example, if you make sure to follow the instructions under [`examples/cocotb/doc_examples_quickstart/`](examples/cocotb/doc_examples_quickstart/), + ``` make ./tb examples/cocotb/doc_examples_quickstart/my_design.sv -t examples/cocotb/doc_examples_quickstart/test_my_design.py --using cocotb ``` + should run `cocotb` on the input file and harness. -### Via `fud2`: +### Via `fud2` + +> THIS SECTION IS INVALIDATED SINCE I HAVE REMOVED FUD2 SUPPORT FOR THE TIME +> BEING. You can follow the above steps but invoke the following command instead. + ``` fud2 my_design.sv -s tb.test=test_my_design.py -s tb.using=cocotb --to tb ``` @@ -34,8 +40,17 @@ fud2 my_design.sv -s tb.test=test_my_design.py -s tb.using=cocotb --to tb I've provided a [Makefile](Makefile) in this directory for local testing. Use `make` to build the `tb` executable locally. +## Testing Calyx Code + +Your input file should be the calyx file you test and each `-t` test should be a +single-file Rust program using calyx-ffi to declare one or more components in +the file and then to write a single `#[calyx_ffi_tests]` module in which you +write your test functions. + ## Writing a Plugin +> WE NO LONGER ALLOW DYNAMICALLY LOADING PLUGINS. + First, setup a simple rust library as you would any other, but **ensure that `lib.crate-type` is `cdylib`**. Here, we're writing the plugin in `lib.rs`. Remember to update the `path` in the `dependencies.tb` dependency! diff --git a/tools/tb/examples/calyx/Cargo.toml b/tools/tb/examples/calyx-native/Cargo.toml similarity index 100% rename from tools/tb/examples/calyx/Cargo.toml rename to tools/tb/examples/calyx-native/Cargo.toml diff --git a/tools/tb/examples/calyx/adder.futil b/tools/tb/examples/calyx-native/adder.futil similarity index 100% rename from tools/tb/examples/calyx/adder.futil rename to tools/tb/examples/calyx-native/adder.futil diff --git a/tools/tb/examples/calyx/subber.futil b/tools/tb/examples/calyx-native/subber.futil similarity index 100% rename from tools/tb/examples/calyx/subber.futil rename to tools/tb/examples/calyx-native/subber.futil diff --git a/tools/tb/examples/calyx/test.rs b/tools/tb/examples/calyx-native/test.rs similarity index 100% rename from tools/tb/examples/calyx/test.rs rename to tools/tb/examples/calyx-native/test.rs diff --git a/tools/tb/examples/calyx-tb/adder.futil b/tools/tb/examples/calyx-tb/adder.futil new file mode 100644 index 0000000000..1730a7c5fd --- /dev/null +++ b/tools/tb/examples/calyx-tb/adder.futil @@ -0,0 +1,23 @@ +import "primitives/core.futil"; + +component main(lhs: 64, rhs: 64) -> (result: 64) { + cells { + adder = std_add(64); + temp = std_reg(64); + } + wires { + group add { + adder.left = lhs; + adder.right = rhs; + temp.in = adder.out; + temp.write_en = 1'b1; + add[done] = temp.done; + } + result = temp.out; + } + control { + add; + } +} + + diff --git a/tools/tb/examples/calyx-tb/main.rs b/tools/tb/examples/calyx-tb/main.rs new file mode 100644 index 0000000000..aaaf82d993 --- /dev/null +++ b/tools/tb/examples/calyx-tb/main.rs @@ -0,0 +1,23 @@ +use calyx_ffi::prelude::*; + +use calyx_ffi::cider_ffi_backend; +#[calyx_ffi( + src = "adder.futil", + comp = "main", + backend = cider_ffi_backend +)] +struct Adder; + +#[cfg(test)] +#[calyx_ffi_tests] +mod tests { + #[calyx_ffi_test] + fn test_add(adder: &mut Adder) { + adder.reset(); + adder.lhs = 4; + adder.rhs = 5; + println!("foo"); + adder.go(); + assert_eq!(9, adder.result()); + } +} diff --git a/tools/tb/src/builtin_plugins/calyx.rs b/tools/tb/src/builtin_plugins/calyx.rs index a4bff0e64b..fe08275762 100644 --- a/tools/tb/src/builtin_plugins/calyx.rs +++ b/tools/tb/src/builtin_plugins/calyx.rs @@ -34,63 +34,87 @@ impl Plugin for CalyxTB { work_dir: tempdir::TempDir, _config: &Config, ) -> LocalResult<()> { - println!( + eprintln!( "recommendation: Run the #[calyx_ffi_tests] as Rust tests directly" ); + + eprintln!("tb: --using {}: setting up dummy crate", self.name()); + + let mut dut_path = PathBuf::from(work_dir.path()); + dut_path.push(&input); + if let Some(parent) = dut_path.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(&input, dut_path)?; + let mut calyx_ffi_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - calyx_ffi_path.push("../../../calyx-ffi"); + calyx_ffi_path.push("../calyx-ffi"); let mut main_file = PathBuf::from(work_dir.path()); main_file.push("main.rs"); - fs::write(&main_file, DRIVER_CODE)?; + fs::write(main_file, DRIVER_CODE)?; let mut manifest_path = PathBuf::from(work_dir.path()); manifest_path.push("Cargo.toml"); let mut lib_path = PathBuf::from(work_dir.path()); - lib_path.push(input); - - for test in tests { - let mut test_path = PathBuf::from(work_dir.path()); - test_path.push(test); - - let mut manifest = toml::Table::new(); - manifest.insert( - "package".into(), - toml::Value::Table(toml::map::Map::from_iter([ - ("name".to_string(), "test_crate".into()), - ("edition".to_string(), "2021".into()), - ])), - ); - manifest.insert( - "lib".into(), + lib_path.push("lib.rs"); + + let mut manifest = toml::Table::new(); + manifest.insert( + "package".into(), + toml::Value::Table(toml::map::Map::from_iter([ + ("name".to_string(), "test_crate".into()), + ("edition".to_string(), "2021".into()), + ])), + ); + manifest.insert( + "lib".into(), + toml::Value::Table(toml::map::Map::from_iter([( + "path".to_string(), + "lib.rs".into(), + )])), + ); + manifest.insert( + "bin".into(), + vec![toml::Value::Table(toml::map::Map::from_iter([ + ("name".to_string(), "test".into()), + ("path".to_string(), "main.rs".into()), + ]))] + .into(), + ); + manifest.insert( + "dependencies".into(), + toml::Value::Table(toml::map::Map::from_iter([( + "calyx-ffi".to_string(), toml::Value::Table(toml::map::Map::from_iter([( "path".to_string(), - "lib.rs".into(), + calyx_ffi_path.to_string_lossy().to_string().into(), )])), - ); - manifest.insert( - "bin".into(), - vec![toml::Value::Table(toml::map::Map::from_iter([ - ("name".to_string(), "test".into()), - ("path".to_string(), "main.rs".into()), - ]))] - .into(), - ); - manifest.insert( - "dependencies".into(), - toml::Value::Table(toml::map::Map::from_iter([( - "calyx-ffi".to_string(), - toml::Value::Table(toml::map::Map::from_iter([( - "path".to_string(), - calyx_ffi_path.to_string_lossy().to_string().into(), - )])), - )])), - ); + )])), + ); + for test in tests { fs::write(&manifest_path, manifest.to_string())?; - fs::rename(&test_path, &lib_path)?; + fs::copy(test, &lib_path)?; + + eprintln!( + "tb: --using {}: building and testing `{}` with `{}`", + self.name(), + input, + test + ); + eprintln!(" (may take a while because `rustc` is slow)"); + + let output = Command::new("cargo") + .arg("expand") + .arg("--lib") + .current_dir(work_dir.path()) + .output()?; + println!("{}", unsafe { + String::from_utf8_unchecked(output.stdout) + }); let output = Command::new("cargo") .arg("run") .arg("--quiet") diff --git a/tools/tb/src/cli.rs b/tools/tb/src/cli.rs index c04aa8de5c..475b5d1699 100644 --- a/tools/tb/src/cli.rs +++ b/tools/tb/src/cli.rs @@ -30,7 +30,7 @@ impl FromStr for ConfigSet { /// Test verilog files under various harnesses. pub struct CLI { #[argh(positional)] - /// verilog file + /// verilog or calyx file pub input: String, #[argh(option, short = 't', long = "test")] @@ -42,7 +42,7 @@ pub struct CLI { pub set: Vec, #[argh(option, short = 'u')] - /// the testbench to invoke + /// the testbench to invoke, e.g., verilator, cocotb, calyx pub using: String, /// path to the config file From ab6229476f8fce101344f0e8c739bdee29c8c2fb Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:13:00 -0500 Subject: [PATCH 31/41] Copy in Cargo.lock? --- Cargo.lock | 226 ++++++++++-------- .../examples/calyx-tb/{main.rs => main.rs_} | 2 + 2 files changed, 125 insertions(+), 103 deletions(-) rename tools/tb/examples/calyx-tb/{main.rs => main.rs_} (85%) diff --git a/Cargo.lock b/Cargo.lock index a018cd0d6d..38e6b44c62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +# This fil# This file is automatically @generated by Cargo. version = 3 [[package]] @@ -68,47 +69,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -139,7 +141,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -175,7 +177,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -206,7 +208,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -261,7 +263,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.69", + "syn 2.0.87", "which", ] @@ -319,7 +321,7 @@ version = "0.1.0" dependencies = [ "bitvec", "btor2tools", - "clap 4.4.18", + "clap 4.5.20", "num-bigint", "num-integer", "num-traits", @@ -330,7 +332,7 @@ dependencies = [ [[package]] name = "btor2tools" version = "1.1.0" -source = "git+https://github.com/obhalerao/btor2tools.rs#a01b2ebc85ee4489860069b196b2ab886d06f43a" +source = "git+https://github.com/obhalerao/btor2tools.rs#8d34a0dc96b447fb008ffbb6f15d1ec947eaefb8" dependencies = [ "btor2tools-sys", "thiserror", @@ -439,7 +441,7 @@ dependencies = [ "calyx-utils", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -681,9 +683,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -703,9 +705,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -713,33 +715,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clipboard-win" @@ -754,9 +756,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "console" @@ -984,12 +986,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.8", - "darling_macro 0.20.8", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -1002,22 +1004,22 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn 2.0.69", + "strsim 0.11.1", + "syn 2.0.87", ] [[package]] @@ -1033,13 +1035,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.8", + "darling_core 0.20.10", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -1370,7 +1372,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -1491,6 +1493,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1611,9 +1619,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "967d6dd42f16dbf0eb8040cb9e477933562684d3918f7d253f2ff9087fb3e7a3" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1622,9 +1630,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.39.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" +checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", @@ -1688,6 +1696,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -1741,9 +1755,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets 0.52.4", @@ -1827,7 +1841,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2142,7 +2156,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2163,7 +2177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.4", + "indexmap 2.5.0", ] [[package]] @@ -2183,7 +2197,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2252,28 +2266,28 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.16" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", @@ -2307,9 +2321,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2399,9 +2413,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -2518,7 +2532,7 @@ checksum = "59aecf17969c04b9c0c5d21f6bc9da9fec9dd4980e64d1871443a476589d8c86" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2616,9 +2630,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -2635,13 +2649,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2663,7 +2677,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2678,9 +2692,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2705,7 +2719,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.4", + "indexmap 2.5.0", "serde", "serde_derive", "serde_json", @@ -2731,10 +2745,10 @@ version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ - "darling 0.20.8", + "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2841,9 +2855,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -2898,6 +2912,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -2910,11 +2930,11 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -2957,9 +2977,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.69" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3054,22 +3074,22 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -3172,7 +3192,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -3191,9 +3211,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -3203,20 +3223,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.2.4", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", @@ -3274,7 +3294,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -3302,7 +3322,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] [[package]] @@ -3739,9 +3759,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.3" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e19b97e00a4d3db3cdb9b53c8c5f87151b5689b82cc86c2848cbdcccb2689b" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -3791,5 +3811,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.87", ] diff --git a/tools/tb/examples/calyx-tb/main.rs b/tools/tb/examples/calyx-tb/main.rs_ similarity index 85% rename from tools/tb/examples/calyx-tb/main.rs rename to tools/tb/examples/calyx-tb/main.rs_ index aaaf82d993..0d1c2075a7 100644 --- a/tools/tb/examples/calyx-tb/main.rs +++ b/tools/tb/examples/calyx-tb/main.rs_ @@ -1,3 +1,5 @@ +// this file is extended with .rs_ instead of .rs to avoid clippy errors + use calyx_ffi::prelude::*; use calyx_ffi::cider_ffi_backend; From 33b9686e7d382c0f253beae956efe5efcb985925 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:43:37 -0500 Subject: [PATCH 32/41] Fix cider backend --- Cargo.lock | 192 ++------------------------- interp/src/lib.rs | 3 + tools/calyx-ffi/src/backend/cider.rs | 24 ++-- 3 files changed, 30 insertions(+), 189 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cce03e950c..b1113d3911 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,55 +67,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "anyhow" version = "1.0.80" @@ -249,29 +200,6 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "bindgen" -version = "0.68.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" -dependencies = [ - "bitflags 2.4.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.87", - "which", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -320,25 +248,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "btor2i" -version = "0.1.0" -dependencies = [ - "bitvec", - "btor2tools", - "clap 4.5.20", - "num-bigint", - "num-integer", - "num-traits", - "tempfile", - "thiserror", -] - -[[package]] -name = "btor2tools" -version = "1.1.0" -source = "git+https://github.com/obhalerao/btor2tools.rs#8d34a0dc96b447fb008ffbb6f15d1ec947eaefb8" - [[package]] name = "bon" version = "2.3.0" @@ -359,7 +268,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.87", ] [[package]] @@ -687,17 +596,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "2.34.0" @@ -709,46 +607,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "clap" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.11.1", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "clap_lex" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" - [[package]] name = "clipboard-win" version = "4.5.0" @@ -760,12 +618,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - [[package]] name = "component_cells" version = "0.7.1" @@ -1512,12 +1364,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -1695,12 +1541,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.10.5" @@ -1814,6 +1654,15 @@ dependencies = [ "url", ] +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + [[package]] name = "makemake" version = "0.1.3" @@ -1825,15 +1674,6 @@ dependencies = [ "paste", ] -[[package]] -name = "lz4_flex" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" -dependencies = [ - "twox-hash", -] - [[package]] name = "manifest-dir-macros" version = "0.1.18" @@ -2244,16 +2084,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "prettyplease" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" -dependencies = [ - "proc-macro2", - "syn 2.0.87", -] - [[package]] name = "proc-macro2" version = "1.0.89" @@ -2898,7 +2728,7 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", diff --git a/interp/src/lib.rs b/interp/src/lib.rs index 34b3d98a9d..6c6afe53be 100644 --- a/interp/src/lib.rs +++ b/interp/src/lib.rs @@ -8,3 +8,6 @@ pub mod serialization; mod tests; pub mod flatten; + +// ethan: griffin ok'd this +pub use baa::{BitVecOps, BitVecValue}; diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs index 9f7e3438db..f2f548742a 100644 --- a/tools/calyx-ffi/src/backend/cider.rs +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -1,12 +1,13 @@ use calyx_ir::Context; use interp::{ + configuration::RuntimeConfig, flatten::{ flat_ir, structures::{ context::Context as CiderContext, environment::Simulator, }, }, - values::Value, + BitVecOps, BitVecValue, }; use std::rc::Rc; @@ -18,23 +19,30 @@ impl CiderFFIBackend { pub fn from(ctx: &Context, _name: &'static str) -> Self { // TODO(ethan, maybe griffin): use _name to select the component somehow let ctx = flat_ir::translate(ctx); - let simulator = Simulator::build_simulator(Rc::new(ctx), &None) - .expect("we live on the edge"); + let simulator = Simulator::build_simulator( + Rc::new(ctx), + &None, + &None, + RuntimeConfig::default(), + ) + .expect("we live on the edge"); Self { simulator } } pub fn write_port(&mut self, name: &'static str, value: u64) { - if name == "go" { + if name == "go" || name == "reset" { return; } - self.simulator.pin_value(name, Value::from(value, 64)); + self.simulator + .pin_value(name, BitVecValue::from_u64(value, 64)); } pub fn read_port(&self, name: &'static str) -> u64 { self.simulator .lookup_port_from_string(&String::from(name)) .expect("wrong port name") - .as_u64() + .to_u64() + .expect("type was not u64") } pub fn step(&mut self) { @@ -75,7 +83,7 @@ macro_rules! cider_ffi_backend { true }; (@tick $dut:ident; $($input:ident),*; $($output:ident),*) => { - println!("cider_ffi_backend tick"); + // println!("cider_ffi_backend tick"); let cider = unsafe { $dut.user_data.assume_init_mut() }; $( cider.write_port(stringify!($input), $dut.$input); @@ -86,7 +94,7 @@ macro_rules! cider_ffi_backend { )* }; (@go $dut:ident; $($input:ident),*; $($output:ident),*) => { - println!("cider_ffi_backend go"); + // println!("cider_ffi_backend go"); let cider = unsafe { $dut.user_data.assume_init_mut() }; $( cider.write_port(stringify!($input), $dut.$input); From a2ba4acbb1ef7ad8948f38eba345b0d12b1550c0 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:00:45 -0500 Subject: [PATCH 33/41] Work on adding compile time port widths --- Cargo.lock | 10 +-- Cargo.toml | 1 - tools/calyx-ffi-macro/src/lib.rs | 75 ++++++++++++++----- tools/calyx-ffi-macro/src/parse.rs | 19 ++++- tools/calyx-ffi/Cargo.toml | 4 + tools/calyx-ffi/src/backend/cider.rs | 15 ++-- tools/calyx-ffi/src/backend/useless.rs | 16 ++-- tools/calyx-ffi/src/lib.rs | 6 ++ tools/calyx-ffi/src/prelude.rs | 29 ++++--- .../tests}/adder.futil | 0 .../test.rs => calyx-ffi/tests/arith_fuzz.rs} | 29 ++++--- tools/calyx-ffi/tests/file.futil | 5 -- .../tests}/subber.futil | 0 tools/calyx-ffi/tests/test.rs | 19 ----- tools/tb/examples/calyx-native/Cargo.toml | 22 ------ 15 files changed, 135 insertions(+), 115 deletions(-) rename tools/{tb/examples/calyx-native => calyx-ffi/tests}/adder.futil (100%) rename tools/{tb/examples/calyx-native/test.rs => calyx-ffi/tests/arith_fuzz.rs} (68%) delete mode 100644 tools/calyx-ffi/tests/file.futil rename tools/{tb/examples/calyx-native => calyx-ffi/tests}/subber.futil (100%) delete mode 100644 tools/calyx-ffi/tests/test.rs delete mode 100644 tools/tb/examples/calyx-native/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index b1113d3911..2e3ad955e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,15 +344,7 @@ dependencies = [ "calyx-frontend", "calyx-ir", "interp", -] - -[[package]] -name = "calyx-ffi-example" -version = "0.7.1" -dependencies = [ - "calyx-ffi", - "calyx-ir", - "interp", + "paste", "rand 0.8.5", ] diff --git a/Cargo.toml b/Cargo.toml index 2f26944b62..8b8fb08203 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ members = [ "tools/tb", "tools/calyx-ffi-macro", "tools/calyx-ffi", - "tools/tb/examples/calyx-native", ] [workspace.package] diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index 220231cc44..ffa13d6f25 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -1,8 +1,8 @@ use std::{env, path::PathBuf}; -use parse::CalyxFFIMacroArgs; +use parse::{CalyxFFIMacroArgs, CalyxPortDeclaration}; use proc_macro::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; use syn::{parse_macro_input, spanned::Spanned}; mod calyx; @@ -48,32 +48,58 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let backend_macro = args.backend; let mut input_names = Vec::new(); let mut output_names = Vec::new(); - let mut field_names = vec![]; let mut fields = vec![]; + let mut default_field_inits = vec![]; let mut getters = vec![]; + let mut setters = vec![]; + let mut width_getters = vec![]; for port in comp.signature.borrow().ports() { let port_name_str = port.borrow().name.to_string(); let port_name = syn::parse_str::(&port_name_str) .expect("failed to turn port name into identifier"); - field_names.push(port_name.clone()); - // let port_width = port.borrow().width; + + let port_width = port.borrow().width; + let width_getter = format_ident!("{}_width", port_name); + width_getters.push(quote! { + pub const fn #width_getter() -> u64 { + #port_width + } + }); + + default_field_inits.push(quote! { + #port_name: calyx_ffi::value_from_u64::<#port_width>(0) + }); // idk why input output ports are being flipped?? match port.borrow().direction.reverse() { calyx_ir::Direction::Input => { + let setter = format_ident!("set_{}", port_name); fields.push(quote! { - pub #port_name: u64 + pub #port_name: calyx_ffi::Value<#port_width> + }); + setters.push(quote! { + pub fn #setter(&mut self, value: u64) { + self.#port_name = calyx_ffi::value_from_u64::<#port_width>(value); + } }); input_names.push(port_name); } calyx_ir::Direction::Output => { fields.push(quote! { - #port_name: u64 + #port_name: calyx_ffi::Value<#port_width> + }); + + let bitvec_getter = format_ident!("{}_bits", port_name); + getters.push(quote! { pub fn #port_name(&self) -> u64 { - self.#port_name + interp::BitVecOps::to_u64(&self.#port_name).expect("port value wider than 64 bits") + } + + pub const fn #bitvec_getter(&self) -> &calyx_ffi::Value<#port_width> { + &self.#port_name } }); output_names.push(port_name); @@ -93,13 +119,15 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let impl_block = quote! { impl #name { + #(#width_getters)* #(#getters)* + #(#setters)* } impl std::default::Default for #name { fn default() -> Self { Self { - #(#field_names: std::default::Default::default(),)* + #(#default_field_inits),*, user_data: unsafe { std::mem::MaybeUninit::zeroed() } } } @@ -142,21 +170,34 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let trait_name = derive.name; let mut getters = Vec::new(); - for output in derive.outputs { + for CalyxPortDeclaration(name, width) in derive.outputs { + let name_bits = format_ident!("{}_bits", &name); + getters.push(quote! { - fn #output(&self) -> u64 { - self.#output + fn #name_bits(&self) -> &calyx_ffi::Value<#width> { + &self.#name + } + + fn #name(&self) -> u64 { + Self::#name(self) } - }) + }); } let mut setters = Vec::new(); - for input in derive.inputs { + for CalyxPortDeclaration(name, width) in derive.inputs { + let name_bits = format_ident!("{}_bits", name); + let setter = format_ident!("set_{}", name); + setters.push(quote! { - fn #input(&mut self) -> &mut u64 { - &mut self.#input + fn #name_bits(&mut self) -> &mut calyx_ffi::Value<#width> { + &mut self.#name } - }) + + fn #setter(&mut self, value: u64) { + Self::#setter(self, value); + } + }); } derive_impls.push(quote! { diff --git a/tools/calyx-ffi-macro/src/parse.rs b/tools/calyx-ffi-macro/src/parse.rs index f0642e5754..422b03cea3 100644 --- a/tools/calyx-ffi-macro/src/parse.rs +++ b/tools/calyx-ffi-macro/src/parse.rs @@ -6,10 +6,21 @@ use syn::{ parse::{Parse, ParseStream}, }; +pub struct CalyxPortDeclaration(pub syn::Ident, pub syn::LitInt); + +impl Parse for CalyxPortDeclaration { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse::()?; + input.parse::()?; + let width = input.parse::()?; + Ok(Self(name, width)) + } +} + pub struct CalyxInterface { pub name: syn::Ident, - pub inputs: Vec, - pub outputs: Vec, + pub inputs: Vec, + pub outputs: Vec, } impl Parse for CalyxInterface { @@ -19,13 +30,13 @@ impl Parse for CalyxInterface { let outputs; parenthesized!(inputs in input); let inputs = inputs - .parse_terminated(syn::Ident::parse, syn::Token![,])? + .parse_terminated(CalyxPortDeclaration::parse, syn::Token![,])? .into_iter() .collect(); input.parse::]>()?; parenthesized!(outputs in input); let outputs = outputs - .parse_terminated(syn::Ident::parse, syn::Token![,])? + .parse_terminated(CalyxPortDeclaration::parse, syn::Token![,])? .into_iter() .collect(); Ok(Self { diff --git a/tools/calyx-ffi/Cargo.toml b/tools/calyx-ffi/Cargo.toml index 862e34cbce..16e87525df 100644 --- a/tools/calyx-ffi/Cargo.toml +++ b/tools/calyx-ffi/Cargo.toml @@ -17,3 +17,7 @@ calyx-ffi-macro = { path = "../calyx-ffi-macro" } calyx-frontend.workspace = true calyx-ir.workspace = true interp = { path = "../../interp" } +paste = "1.0.15" + +[dev-dependencies] +rand = "0.8.5" diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs index f2f548742a..503087d5c4 100644 --- a/tools/calyx-ffi/src/backend/cider.rs +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -7,7 +7,7 @@ use interp::{ context::Context as CiderContext, environment::Simulator, }, }, - BitVecOps, BitVecValue, + BitVecValue, }; use std::rc::Rc; @@ -29,20 +29,17 @@ impl CiderFFIBackend { Self { simulator } } - pub fn write_port(&mut self, name: &'static str, value: u64) { + pub fn write_port(&mut self, name: &'static str, value: &BitVecValue) { if name == "go" || name == "reset" { return; } - self.simulator - .pin_value(name, BitVecValue::from_u64(value, 64)); + self.simulator.pin_value(name, value.clone()); } - pub fn read_port(&self, name: &'static str) -> u64 { + pub fn read_port(&self, name: &'static str) -> BitVecValue { self.simulator .lookup_port_from_string(&String::from(name)) .expect("wrong port name") - .to_u64() - .expect("type was not u64") } pub fn step(&mut self) { @@ -86,7 +83,7 @@ macro_rules! cider_ffi_backend { // println!("cider_ffi_backend tick"); let cider = unsafe { $dut.user_data.assume_init_mut() }; $( - cider.write_port(stringify!($input), $dut.$input); + cider.write_port(stringify!($input), &$dut.$input); )* cider.step(); $( @@ -97,7 +94,7 @@ macro_rules! cider_ffi_backend { // println!("cider_ffi_backend go"); let cider = unsafe { $dut.user_data.assume_init_mut() }; $( - cider.write_port(stringify!($input), $dut.$input); + cider.write_port(stringify!($input), &$dut.$input); )* cider.go(); $( diff --git a/tools/calyx-ffi/src/backend/useless.rs b/tools/calyx-ffi/src/backend/useless.rs index c890fdfce2..4bc32c4264 100644 --- a/tools/calyx-ffi/src/backend/useless.rs +++ b/tools/calyx-ffi/src/backend/useless.rs @@ -9,27 +9,27 @@ macro_rules! useless_ffi_backend { }; (@reset $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend reset"); - $dut.done = 0; - $dut.reset = 1; + $dut.done = interp::BitVecValue::from_u64(0, $dut.done_width() as u32); + $dut.set_reset(1); for i in 0..5 { $dut.tick(); } - $dut.reset = 0; + $dut.set_reset(0); }; (@can_tick $dut:ident; $($input:ident),*; $($output:ident),*) => { true }; (@tick $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend tick"); - if $dut.done == 1 { - $dut.done = 0; + if $dut.done() == 1 { + $dut.set_reset(0); } }; (@go $dut:ident; $($input:ident),*; $($output:ident),*) => { println!("useless_ffi_backend go"); - $dut.go = 1; - $dut.go = 0; - $dut.done = 1; + $dut.set_go(1); + $dut.set_go(0); + $dut.done = interp::BitVecValue::from_u64(1, $dut.done_width() as u32); $dut.tick(); }; } diff --git a/tools/calyx-ffi/src/lib.rs b/tools/calyx-ffi/src/lib.rs index b4644d545a..9ba180bc89 100644 --- a/tools/calyx-ffi/src/lib.rs +++ b/tools/calyx-ffi/src/lib.rs @@ -74,3 +74,9 @@ impl CalyxFFI { box_calyx_ffi_component(comp) } } + +pub type Value = interp::BitVecValue; + +pub fn value_from_u64(value: u64) -> Value { + Value::from_u64(value, N as u32) +} diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index f56ca89bab..59247b73e4 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -1,17 +1,26 @@ -pub use super::{CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef}; +pub use super::{ + value_from_u64, CalyxFFI, CalyxFFIComponent, CalyxFFIComponentRef, Value, +}; pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests}; pub use calyx_ir; +pub use interp; #[macro_export] -macro_rules! declare_calyx_ffi_interface { - ($name:ident($($input:ident),*) -> ($($output:ident),*)) => { - pub trait $name: CalyxFFIComponent { - $( - fn $input(&mut self) -> &mut u64; - )* - $( - fn $output(&self) -> u64; - )* +macro_rules! declare_interface { + ($name:ident($($input:ident: $input_width:literal),*) -> ($($output:ident: $output_width:literal),*)) => { + paste::paste! { + pub trait $name: CalyxFFIComponent { + $( + fn [<$input _bits>](&mut self) -> &mut calyx_ffi::Value<$input_width>; + + fn [](&mut self, value: u64); + )* + $( + fn [<$output _bits>](&self) -> &calyx_ffi::Value<$output_width>; + + fn $output(&self) -> u64; + )* + } } }; } diff --git a/tools/tb/examples/calyx-native/adder.futil b/tools/calyx-ffi/tests/adder.futil similarity index 100% rename from tools/tb/examples/calyx-native/adder.futil rename to tools/calyx-ffi/tests/adder.futil diff --git a/tools/tb/examples/calyx-native/test.rs b/tools/calyx-ffi/tests/arith_fuzz.rs similarity index 68% rename from tools/tb/examples/calyx-native/test.rs rename to tools/calyx-ffi/tests/arith_fuzz.rs index 3978511977..9bc7cdb427 100644 --- a/tools/tb/examples/calyx-native/test.rs +++ b/tools/calyx-ffi/tests/arith_fuzz.rs @@ -1,29 +1,28 @@ -use calyx_ffi::declare_calyx_ffi_interface; use calyx_ffi::prelude::*; use calyx_ffi::cider_ffi_backend; // not necessary, just to show it off -declare_calyx_ffi_interface! { - In2Out1(lhs, rhs) -> (result) +calyx_ffi::declare_interface! { + In2Out1(lhs: 64, rhs: 64) -> (result: 64) } #[calyx_ffi( - src = "adder.futil", + src = "tests/adder.futil", comp = "main", backend = cider_ffi_backend, derive = [ - In2Out1(lhs, rhs) -> (result) + In2Out1(lhs: 64, rhs: 64) -> (result: 64) ] )] struct Adder; #[calyx_ffi( - src = "subber.futil", + src = "tests/subber.futil", comp = "main", backend = cider_ffi_backend, derive = [ - In2Out1(lhs, rhs) -> (result) + In2Out1(lhs: 64, rhs: 64) -> (result: 64) ] )] struct Subber; @@ -31,9 +30,10 @@ struct Subber; #[cfg(test)] #[calyx_ffi_tests] mod tests { + use std::mem; + use super::*; use rand::Rng; - use std::mem; // inv: the left argument will always be greater than the right fn fuzz_in2out1 u64>( @@ -46,10 +46,17 @@ mod tests { if y > x { mem::swap(&mut x, &mut y); } - *comp.lhs() = x; - *comp.rhs() = y; + comp.set_lhs(x); + comp.set_rhs(y); comp.go(); - assert_eq!(oracle(x, y), comp.result(), "testing f({}, {})", x, y); + assert_eq!( + oracle(x, y), + comp.result(), + "component did not evaluate f({}, {}) = {} correctly", + x, + y, + oracle(x, y) + ); } } diff --git a/tools/calyx-ffi/tests/file.futil b/tools/calyx-ffi/tests/file.futil deleted file mode 100644 index f1b20be0ff..0000000000 --- a/tools/calyx-ffi/tests/file.futil +++ /dev/null @@ -1,5 +0,0 @@ -component main(foo: 1) -> () { - cells {} - wires {} - control {} -} diff --git a/tools/tb/examples/calyx-native/subber.futil b/tools/calyx-ffi/tests/subber.futil similarity index 100% rename from tools/tb/examples/calyx-native/subber.futil rename to tools/calyx-ffi/tests/subber.futil diff --git a/tools/calyx-ffi/tests/test.rs b/tools/calyx-ffi/tests/test.rs deleted file mode 100644 index 50e019ec51..0000000000 --- a/tools/calyx-ffi/tests/test.rs +++ /dev/null @@ -1,19 +0,0 @@ -use calyx_ffi::prelude::*; - -use calyx_ffi::useless_ffi_backend; - -#[calyx_ffi( - src = "tests/file.futil", - comp = "main", - backend = useless_ffi_backend -)] -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-native/Cargo.toml b/tools/tb/examples/calyx-native/Cargo.toml deleted file mode 100644 index 581eaff62e..0000000000 --- a/tools/tb/examples/calyx-native/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "calyx-ffi-example" -authors.workspace = true -license-file.workspace = true -keywords.workspace = true -repository.workspace = true -readme.workspace = true -description.workspace = true -categories.workspace = true -homepage.workspace = true -edition.workspace = true -version.workspace = true -rust-version.workspace = true - -[lib] -path = "test.rs" - -[dependencies] -calyx-ffi = { path = "../../../calyx-ffi" } -interp = { path = "../../../../interp" } -calyx-ir.workspace = true -rand = "0.8.5" From 8d6436d1a2e6344330d73f2625570faa7499b39e Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:04:19 -0500 Subject: [PATCH 34/41] Cleanup --- .../tests__test@plan_verilog-to-tb.snap.new | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new diff --git a/fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new b/fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new deleted file mode 100644 index 882f318b80..0000000000 --- a/fud2/tests/snapshots/tests__test@plan_verilog-to-tb.snap.new +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: fud2/tests/tests.rs -assertion_line: 66 -description: "emit plan: verilog-to-tb" ---- -build-tool = fud2 -rule get-rsrc - command = $build-tool get-rsrc $out - - -build /output.ext: verilog-to-tb /input.ext - -default /output.ext From 6d64dce6576866ce9aa1c116f972ddc9dbc71e2b Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:02:46 -0500 Subject: [PATCH 35/41] Make minimally-sized clonable simulator --- interp/Cargo.toml | 1 + .../src/flatten/primitives/combinational.rs | 15 ++ interp/src/flatten/primitives/macros.rs | 6 + interp/src/flatten/primitives/prim_trait.rs | 13 +- .../src/flatten/primitives/stateful/math.rs | 24 ++ .../flatten/primitives/stateful/memories.rs | 28 ++ interp/src/flatten/primitives/utils.rs | 1 + .../src/flatten/structures/environment/env.rs | 240 +++++++++++++----- .../src/flatten/structures/environment/mod.rs | 2 +- .../structures/environment/program_counter.rs | 2 +- interp/src/flatten/structures/indexed_map.rs | 14 + interp/src/flatten/structures/thread.rs | 4 +- 12 files changed, 276 insertions(+), 74 deletions(-) diff --git a/interp/Cargo.toml b/interp/Cargo.toml index 537518f77b..9a84606a02 100644 --- a/interp/Cargo.toml +++ b/interp/Cargo.toml @@ -48,6 +48,7 @@ baa = { version = "0.14.0", features = ["bigint", "serde1", "fraction1"] } fst-writer = "0.2.1" bon = "2.3" +# derivative = "2.2.0" [dev-dependencies] proptest = "1.0.0" diff --git a/interp/src/flatten/primitives/combinational.rs b/interp/src/flatten/primitives/combinational.rs index da6ddb1d50..8df70e4352 100644 --- a/interp/src/flatten/primitives/combinational.rs +++ b/interp/src/flatten/primitives/combinational.rs @@ -11,6 +11,7 @@ use baa::{BitVecOps, BitVecValue}; use super::prim_trait::UpdateResult; +#[derive(Clone)] pub struct StdConst { value: BitVecValue, out: GlobalPortIdx, @@ -44,8 +45,13 @@ impl Primitive for StdConst { fn has_stateful(&self) -> bool { false } + + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } } +#[derive(Clone)] pub struct StdMux { base: GlobalPortIdx, } @@ -80,6 +86,10 @@ impl Primitive for StdMux { fn has_stateful(&self) -> bool { false } + + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } } comb_primitive!(StdNot(input [0]) -> (out [1]) { @@ -275,6 +285,7 @@ comb_primitive!(StdUnsynSmod[WIDTH](left [0], right [1]) -> (out [2]) { Ok(Some(BitVecValue::from_big_int(&res, WIDTH))) }); +#[derive(Clone)] pub struct StdUndef(GlobalPortIdx); impl StdUndef { @@ -288,4 +299,8 @@ impl Primitive for StdUndef { port_map.write_undef(self.0)?; Ok(UpdateStatus::Unchanged) } + + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } } diff --git a/interp/src/flatten/primitives/macros.rs b/interp/src/flatten/primitives/macros.rs index 7a34274e4a..bf75c6f1b4 100644 --- a/interp/src/flatten/primitives/macros.rs +++ b/interp/src/flatten/primitives/macros.rs @@ -102,6 +102,12 @@ macro_rules! comb_primitive { false } + fn clone_boxed(&self) -> Box { + Box::new(Self { + base_port: self.base_port, + $($($param: self.$param,)+)? + }) + } } }; diff --git a/interp/src/flatten/primitives/prim_trait.rs b/interp/src/flatten/primitives/prim_trait.rs index ecfb0b2e5b..83a2f4dde5 100644 --- a/interp/src/flatten/primitives/prim_trait.rs +++ b/interp/src/flatten/primitives/prim_trait.rs @@ -1,3 +1,5 @@ +use std::mem; + use crate::{ errors::RuntimeResult, flatten::{ @@ -134,6 +136,8 @@ pub trait Primitive { fn dump_memory_state(&self) -> Option> { None } + + fn clone_boxed(&self) -> Box; } pub trait RaceDetectionPrimitive: Primitive { @@ -158,10 +162,13 @@ pub trait RaceDetectionPrimitive: Primitive { /// Get a reference to the underlying primitive. Unfortunately cannot add an /// optional default implementation due to size rules fn as_primitive(&self) -> &dyn Primitive; + + fn clone_boxed(&self) -> Box; } /// An empty primitive implementation used for testing. It does not do anything /// and has no ports of any kind +#[derive(Clone, Copy)] pub struct DummyPrimitive; impl DummyPrimitive { @@ -170,4 +177,8 @@ impl DummyPrimitive { } } -impl Primitive for DummyPrimitive {} +impl Primitive for DummyPrimitive { + fn clone_boxed(&self) -> Box { + Box::new(*self) + } +} diff --git a/interp/src/flatten/primitives/stateful/math.rs b/interp/src/flatten/primitives/stateful/math.rs index 30e6598100..c8fb091e48 100644 --- a/interp/src/flatten/primitives/stateful/math.rs +++ b/interp/src/flatten/primitives/stateful/math.rs @@ -10,6 +10,7 @@ use crate::flatten::{ use baa::{BitVecOps, BitVecValue, WidthInt}; use num_traits::Euclid; +#[derive(Clone)] pub struct StdMultPipe { base_port: GlobalPortIdx, pipeline: ShiftBuffer<(PortValue, PortValue), DEPTH>, @@ -32,6 +33,10 @@ impl StdMultPipe { } impl Primitive for StdMultPipe { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { ports![&self.base_port; out: Self::OUT, done: Self::DONE]; @@ -106,6 +111,7 @@ impl Primitive for StdMultPipe { } } +#[derive(Clone)] pub struct StdDivPipe { base_port: GlobalPortIdx, pipeline: ShiftBuffer<(PortValue, PortValue), DEPTH>, @@ -132,6 +138,10 @@ impl StdDivPipe { impl Primitive for StdDivPipe { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { ports![&self.base_port; out_quot: Self::OUT_QUOTIENT, @@ -253,6 +263,10 @@ impl Sqrt { } impl Primitive for Sqrt { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { ports![&self.base_port; out: Self::OUT, done: Self::DONE]; @@ -309,6 +323,7 @@ impl Primitive for Sqrt { } } +#[derive(Clone)] pub struct FxpMultPipe { base_port: GlobalPortIdx, pipeline: ShiftBuffer<(PortValue, PortValue), DEPTH>, @@ -339,6 +354,10 @@ impl FxpMultPipe { } impl Primitive for FxpMultPipe { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { ports![&self.base_port; out: Self::OUT, done: Self::DONE]; @@ -422,6 +441,7 @@ impl Primitive for FxpMultPipe { } } +#[derive(Clone)] pub struct FxpDivPipe { base_port: GlobalPortIdx, pipeline: ShiftBuffer<(PortValue, PortValue), DEPTH>, @@ -460,6 +480,10 @@ impl FxpDivPipe { impl Primitive for FxpDivPipe { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { ports![&self.base_port; out_quot: Self::OUT_QUOTIENT, diff --git a/interp/src/flatten/primitives/stateful/memories.rs b/interp/src/flatten/primitives/stateful/memories.rs index 7b2f5680b9..adb8aa6097 100644 --- a/interp/src/flatten/primitives/stateful/memories.rs +++ b/interp/src/flatten/primitives/stateful/memories.rs @@ -27,6 +27,7 @@ use crate::{ use baa::{BitVecOps, BitVecValue, WidthInt}; +#[derive(Clone)] pub struct StdReg { base_port: GlobalPortIdx, internal_state: ValueWithClock, @@ -55,6 +56,10 @@ impl StdReg { } impl Primitive for StdReg { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_cycle(&mut self, port_map: &mut PortMap) -> UpdateResult { ports![&self.base_port; input: Self::IN, @@ -139,6 +144,10 @@ impl Primitive for StdReg { } impl RaceDetectionPrimitive for StdReg { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn as_primitive(&self) -> &dyn Primitive { self } @@ -176,6 +185,7 @@ impl RaceDetectionPrimitive for StdReg { } } +#[derive(Clone)] pub struct MemDx { shape: Shape, } @@ -389,6 +399,7 @@ impl MemDx { } } +#[derive(Clone)] pub struct CombMem { base_port: GlobalPortIdx, internal_state: Vec, @@ -508,6 +519,10 @@ impl CombMem { } impl Primitive for CombMem { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { let addr: Option = self .addresser @@ -609,6 +624,10 @@ impl Primitive for CombMem { } impl RaceDetectionPrimitive for CombMem { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn as_primitive(&self) -> &dyn Primitive { self } @@ -674,6 +693,7 @@ impl RaceDetectionPrimitive for CombMem { } } +#[derive(Clone)] pub struct SeqMem { base_port: GlobalPortIdx, internal_state: Vec, @@ -812,6 +832,10 @@ impl SeqMem { } impl Primitive for SeqMem { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn exec_comb(&self, port_map: &mut PortMap) -> UpdateResult { let done_signal = port_map.insert_val_general( self.done(), @@ -915,6 +939,10 @@ impl Primitive for SeqMem { } impl RaceDetectionPrimitive for SeqMem { + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + fn as_primitive(&self) -> &dyn Primitive { self } diff --git a/interp/src/flatten/primitives/utils.rs b/interp/src/flatten/primitives/utils.rs index 6b91e17a3c..51ab75fd58 100644 --- a/interp/src/flatten/primitives/utils.rs +++ b/interp/src/flatten/primitives/utils.rs @@ -38,6 +38,7 @@ pub(crate) fn int_sqrt(i: &BigUint) -> BigUint { } /// A shift buffer of a fixed size +#[derive(Clone)] pub struct ShiftBuffer { buffer: VecDeque>, } diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index 479619a81f..606e9f735b 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -49,8 +49,8 @@ use itertools::Itertools; use owo_colors::OwoColorize; use slog::{info, warn, Logger}; -use std::fmt::Debug; use std::fmt::Write; +use std::{fmt::Debug, ops::Deref}; pub type PortMap = IndexedMap; @@ -156,6 +156,7 @@ pub(crate) type RefPortMap = IndexedMap>; pub(crate) type AssignmentRange = IndexRange; +#[derive(Clone)] pub(crate) struct ComponentLedger { pub(crate) index_bases: BaseIndices, pub(crate) comp_id: ComponentIdx, @@ -192,6 +193,26 @@ pub(crate) enum CellLedger { Component(ComponentLedger), } +impl Clone for CellLedger { + fn clone(&self) -> Self { + match self { + Self::Primitive { cell_dyn } => Self::Primitive { + cell_dyn: cell_dyn.clone_boxed(), + }, + Self::RaceDetectionPrimitive { cell_dyn } => { + Self::RaceDetectionPrimitive { + cell_dyn: RaceDetectionPrimitive::clone_boxed( + cell_dyn.deref(), + ), + } + } + Self::Component(component_ledger) => { + Self::Component(component_ledger.clone()) + } + } + } +} + impl From for CellLedger { fn from(v: ComponentLedger) -> Self { Self::Component(v) @@ -302,7 +323,7 @@ impl PinnedPorts { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Environment + Clone> { /// A map from global port IDs to their current values. ports: PortMap, @@ -1285,24 +1306,44 @@ impl + Clone> Environment { } } +/// The core functionality of a simulator. Clonable. +#[derive(Clone)] +pub struct BaseSimulator + Clone> { + env: Environment, + conf: RuntimeConfig, +} + /// A wrapper struct for the environment that provides the functions used to /// simulate the actual program. /// /// This is just to keep the simulation logic under a different namespace than /// the environment to avoid confusion pub struct Simulator + Clone> { - env: Environment, + base: BaseSimulator, wave: Option, - conf: RuntimeConfig, } impl + Clone> Simulator { - pub fn new( - env: Environment, + pub fn build_simulator( + ctx: C, + data_file: &Option, wave_file: &Option, - conf: RuntimeConfig, - ) -> Self { - // open the wave form file and declare all signals + runtime_config: RuntimeConfig, + ) -> Result { + let data_dump = data_file + .as_ref() + .map(|path| { + let mut file = std::fs::File::open(path)?; + DataDump::deserialize(&mut file) + }) + // flip to a result of an option + .map_or(Ok(None), |res| res.map(Some))?; + let env = Environment::new( + ctx, + data_dump, + runtime_config.check_data_race, + runtime_config.get_logging_config(), + ); let wave = wave_file.as_ref().map(|p| match WaveWriter::open(p, &env) { Ok(w) => w, @@ -1310,9 +1351,120 @@ impl + Clone> Simulator { todo!("deal more gracefully with error: {err:?}") } }); - let mut output = Self { env, wave, conf }; - output.set_root_go_high(); - output + Ok(Self { + base: BaseSimulator::new(env, runtime_config), + wave, + }) + } + + pub fn is_done(&self) -> bool { + self.base.is_done() + } + + pub fn step(&mut self) -> CiderResult<()> { + self.base.step() + } + + pub fn converge(&mut self) -> CiderResult<()> { + self.base.converge() + } + + pub fn get_currently_running_groups( + &self, + ) -> impl Iterator + '_ { + self.base.get_currently_running_groups() + } + + pub fn is_group_running(&self, group_idx: GroupIdx) -> bool { + self.base.is_group_running(group_idx) + } + + pub fn print_pc(&self) { + self.base.print_pc(); + } + + pub fn format_cell_state( + &self, + cell_idx: GlobalCellIdx, + print_code: PrintCode, + name: Option<&str>, + ) -> Option { + self.base.format_cell_state(cell_idx, print_code, name) + } + + pub fn format_cell_ports( + &self, + cell_idx: GlobalCellIdx, + print_code: PrintCode, + name: Option<&str>, + ) -> String { + self.base.format_cell_ports(cell_idx, print_code, name) + } + + pub fn format_port_value( + &self, + port_idx: GlobalPortIdx, + print_code: PrintCode, + ) -> String { + self.base.format_port_value(port_idx, print_code) + } + + pub fn traverse_name_vec( + &self, + name: &[String], + ) -> Result { + self.base.traverse_name_vec(name) + } + + pub fn get_full_name(&self, nameable: N) -> String + where + N: GetFullName, + { + self.base.get_full_name(nameable) + } + + pub(crate) fn env(&self) -> &Environment { + self.base.env() + } + + /// Evaluate the entire program + pub fn run_program(&mut self) -> CiderResult<()> { + if self.base.conf.debug_logging { + info!(self.base.env().logger, "Starting program execution"); + } + + match self.base.run_program_inner(self.wave.as_mut()) { + Ok(_) => { + if self.base.conf.debug_logging { + info!(self.base.env().logger, "Finished program execution"); + } + Ok(()) + } + Err(e) => { + if self.base.conf.debug_logging { + slog::error!( + self.base.env().logger, + "Program execution failed with error: {}", + e.red() + ); + } + Err(e) + } + } + } + + pub fn dump_memories( + &self, + dump_registers: bool, + all_mems: bool, + ) -> DataDump { + self.base.dump_memories(dump_registers, all_mems) + } +} + +impl + Clone> BaseSimulator { + pub fn new(env: Environment, conf: RuntimeConfig) -> Self { + Self { env, conf } } pub(crate) fn env(&self) -> &Environment { @@ -1332,33 +1484,6 @@ impl + Clone> Simulator { self.env } - pub fn build_simulator( - ctx: C, - data_file: &Option, - wave_file: &Option, - runtime_config: RuntimeConfig, - ) -> Result { - let data_dump = data_file - .as_ref() - .map(|path| { - let mut file = std::fs::File::open(path)?; - DataDump::deserialize(&mut file) - }) - // flip to a result of an option - .map_or(Ok(None), |res| res.map(Some))?; - - Ok(Simulator::new( - Environment::new( - ctx, - data_dump, - runtime_config.check_data_race, - runtime_config.get_logging_config(), - ), - wave_file, - runtime_config, - )) - } - pub fn is_group_running(&self, group_idx: GroupIdx) -> bool { self.env.is_group_running(group_idx) } @@ -1418,7 +1543,7 @@ impl + Clone> Simulator { } // =========================== simulation functions =========================== -impl + Clone> Simulator { +impl + Clone> BaseSimulator { #[inline] fn lookup_global_port_id(&self, port: GlobalPortRef) -> GlobalPortIdx { match port { @@ -2161,43 +2286,20 @@ impl + Clone> Simulator { .unwrap_or_default() } - /// Evaluate the entire program - pub fn run_program(&mut self) -> CiderResult<()> { - if self.conf.debug_logging { - info!(self.env.logger, "Starting program execution"); - } - - match self.run_program_inner() { - Ok(_) => { - if self.conf.debug_logging { - info!(self.env.logger, "Finished program execution"); - } - Ok(()) - } - Err(e) => { - if self.conf.debug_logging { - slog::error!( - self.env.logger, - "Program execution failed with error: {}", - e.red() - ); - } - Err(e) - } - } - } - - fn run_program_inner(&mut self) -> Result<(), BoxedCiderError> { + pub fn run_program_inner( + &mut self, + mut wave: Option<&mut WaveWriter>, + ) -> Result<(), BoxedCiderError> { let mut time = 0; while !self.is_done() { - if let Some(wave) = self.wave.as_mut() { + if let Some(wave) = wave.as_mut() { wave.write_values(time, &self.env.ports)?; } // self.print_pc(); self.step()?; time += 1; } - if let Some(wave) = self.wave.as_mut() { + if let Some(wave) = wave { wave.write_values(time, &self.env.ports)?; } Ok(()) diff --git a/interp/src/flatten/structures/environment/mod.rs b/interp/src/flatten/structures/environment/mod.rs index f5133e74af..40808bfbea 100644 --- a/interp/src/flatten/structures/environment/mod.rs +++ b/interp/src/flatten/structures/environment/mod.rs @@ -5,7 +5,7 @@ mod program_counter; mod traverser; mod wave; -pub use env::{Environment, PortMap, Simulator}; +pub use env::{BaseSimulator, Environment, PortMap, Simulator}; pub use traverser::{Path, PathError, PathResolution}; pub(crate) use env::CellLedger; diff --git a/interp/src/flatten/structures/environment/program_counter.rs b/interp/src/flatten/structures/environment/program_counter.rs index 3ce817a844..dc8223d248 100644 --- a/interp/src/flatten/structures/environment/program_counter.rs +++ b/interp/src/flatten/structures/environment/program_counter.rs @@ -375,7 +375,7 @@ impl WithEntry { /// The program counter for the whole program execution. Wraps over a vector of /// the active leaf statements for each component instance. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub(crate) struct ProgramCounter { vec: Vec, par_map: HashMap, diff --git a/interp/src/flatten/structures/indexed_map.rs b/interp/src/flatten/structures/indexed_map.rs index 137788b09b..e2dce880d8 100644 --- a/interp/src/flatten/structures/indexed_map.rs +++ b/interp/src/flatten/structures/indexed_map.rs @@ -143,6 +143,20 @@ where Self::new() } } + +impl Clone for IndexedMap +where + K: IndexRef, + T: Clone, +{ + fn clone(&self) -> Self { + Self { + data: self.data.clone(), + phantom: PhantomData, + } + } +} + #[allow(dead_code)] pub struct IndexedMapRangeIterator<'range, 'data, K, D> where diff --git a/interp/src/flatten/structures/thread.rs b/interp/src/flatten/structures/thread.rs index 938990dbae..8ca8a5bc9e 100644 --- a/interp/src/flatten/structures/thread.rs +++ b/interp/src/flatten/structures/thread.rs @@ -9,7 +9,7 @@ use super::{ pub struct ThreadIdx(NonZeroU32); impl_index_nonzero!(ThreadIdx); -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ThreadInfo { parent: Option, clock_id: ClockIdx, @@ -25,7 +25,7 @@ impl ThreadInfo { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ThreadMap { map: IndexedMap, } From f9eda74837a875c39df815cbf0e858ad82a2a5f0 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:03:25 -0500 Subject: [PATCH 36/41] Backup commit --- ]e | 2957 ++++++++++++++++++++++++++ tools/calyx-ffi/src/backend/cider.rs | 23 +- tools/calyx-ffi/src/prelude.rs | 9 +- tools/calyx-ffi/tests/fifo.futil | 130 ++ tools/calyx-ffi/tests/fifo.rs | 96 + tools/calyx-ffi/tests/stack.futil | 57 + tools/calyx-ffi/tests/stack.rs | 53 + 7 files changed, 3315 insertions(+), 10 deletions(-) create mode 100644 ]e create mode 100644 tools/calyx-ffi/tests/fifo.futil create mode 100644 tools/calyx-ffi/tests/fifo.rs create mode 100644 tools/calyx-ffi/tests/stack.futil create mode 100644 tools/calyx-ffi/tests/stack.rs diff --git a/]e b/]e new file mode 100644 index 0000000000..179fb494b5 --- /dev/null +++ b/]e @@ -0,0 +1,2957 @@ +use super::{ + super::{ + context::Context, index_trait::IndexRange, indexed_map::IndexedMap, + }, + assignments::{GroupInterfacePorts, ScheduledAssignments}, + clock::{ClockMap, VectorClock}, + program_counter::{ControlTuple, PcMaps, ProgramCounter, WithEntry}, + traverser::{Path, TraversalError}, +}; +use crate::{ + configuration::{LoggingConfig, RuntimeConfig}, + errors::{BoxedCiderError, CiderResult, ConflictingAssignments}, + flatten::{ + flat_ir::{ + base::{ + LocalCellOffset, LocalPortOffset, LocalRefCellOffset, + LocalRefPortOffset, + }, + cell_prototype::{CellPrototype, SingleWidthType}, + prelude::*, + wires::guards::Guard, + }, + primitives::{ + self, + prim_trait::{RaceDetectionPrimitive, UpdateStatus}, + Primitive, + }, + structures::{ + context::{LookupName, PortDefinitionInfo}, + environment::{ + program_counter::ControlPoint, traverser::Traverser, + }, + index_trait::IndexRef, + thread::{ThreadIdx, ThreadMap}, + }, + }, + logging, + serialization::{DataDump, MemoryDeclaration, PrintCode}, +}; +use crate::{ + errors::{RuntimeError, RuntimeResult}, + flatten::structures::environment::wave::WaveWriter, +}; +use ahash::HashSet; +use ahash::HashSetExt; +use ahash::{HashMap, HashMapExt}; +use baa::{BitVecOps, BitVecValue}; +use itertools::Itertools; +use owo_colors::OwoColorize; + +use slog::{info, warn, Logger}; +use std::fmt::Write; +use std::{fmt::Debug, ops::Deref}; + +pub type PortMap = IndexedMap; + +impl PortMap { + /// Essentially asserts that the port given is undefined, it errors out if + /// the port is defined and otherwise does nothing + pub fn write_undef(&mut self, target: GlobalPortIdx) -> RuntimeResult<()> { + if self[target].is_def() { + Err(RuntimeError::UndefiningDefinedPort(target).into()) + } else { + Ok(()) + } + } + + /// Sets the given index to the given value without checking whether or not + /// the assignment would conflict with an existing assignment. Should only + /// be used by cells to set values that may be undefined + pub fn write_exact_unchecked( + &mut self, + target: GlobalPortIdx, + val: PortValue, + ) -> UpdateStatus { + if self[target].is_undef() && val.is_undef() + || self[target].as_option() == val.as_option() + { + UpdateStatus::Unchanged + } else { + self[target] = val; + UpdateStatus::Changed + } + } + + /// Sets the given index to undefined without checking whether or not it was + /// already defined + #[inline] + pub fn write_undef_unchecked(&mut self, target: GlobalPortIdx) { + self[target] = PortValue::new_undef(); + } + + pub fn insert_val( + &mut self, + target: GlobalPortIdx, + val: AssignedValue, + ) -> Result { + match self[target].as_option() { + // unchanged + Some(t) if *t == val => Ok(UpdateStatus::Unchanged), + // conflict + // TODO: Fix to make the error more helpful + Some(t) + if t.has_conflict_with(&val) + // Assignment & cell values are allowed to override implicit but not the + // other way around + && !(*t.winner() == AssignmentWinner::Implicit) => + { + Err(ConflictingAssignments { + target, + a1: t.clone(), + a2: val, + }) + } + // changed + Some(_) | None => { + self[target] = PortValue::new(val); + Ok(UpdateStatus::Changed) + } + } + } + + /// Identical to `insert_val` but returns a `RuntimeError` instead of a + /// `ConflictingAssignments` error. This should be used inside of primitives + /// while the latter is used in the general simulation flow. + pub fn insert_val_general( + &mut self, + target: GlobalPortIdx, + val: AssignedValue, + ) -> RuntimeResult { + self.insert_val(target, val) + .map_err(|e| RuntimeError::ConflictingAssignments(e).into()) + } + + pub fn set_done( + &mut self, + target: GlobalPortIdx, + done_bool: bool, + ) -> RuntimeResult { + self.insert_val( + target, + AssignedValue::cell_value(if done_bool { + BitVecValue::tru() + } else { + BitVecValue::fals() + }), + ) + .map_err(|e| RuntimeError::ConflictingAssignments(e).into()) + } +} + +pub(crate) type CellMap = IndexedMap; +pub(crate) type RefCellMap = + IndexedMap>; +pub(crate) type RefPortMap = + IndexedMap>; +pub(crate) type AssignmentRange = IndexRange; + +#[derive(Clone)] +pub(crate) struct ComponentLedger { + pub(crate) index_bases: BaseIndices, + pub(crate) comp_id: ComponentIdx, +} + +impl ComponentLedger { + /// Convert a relative offset to a global one. Perhaps should take an owned + /// value rather than a pointer + pub fn convert_to_global_port(&self, port: &PortRef) -> GlobalPortRef { + match port { + PortRef::Local(l) => (&self.index_bases + l).into(), + PortRef::Ref(r) => (&self.index_bases + r).into(), + } + } + + pub fn convert_to_global_cell(&self, cell: &CellRef) -> GlobalCellRef { + match cell { + CellRef::Local(l) => (&self.index_bases + l).into(), + CellRef::Ref(r) => (&self.index_bases + r).into(), + } + } +} + +/// An enum encapsulating cell functionality. It is either a pointer to a +/// primitive or information about a calyx component instance +pub(crate) enum CellLedger { + Primitive { + // wish there was a better option with this one + cell_dyn: Box, + }, + RaceDetectionPrimitive { + cell_dyn: Box, + }, + Component(ComponentLedger), +} + +impl Clone for CellLedger { + fn clone(&self) -> Self { + match self { + Self::Primitive { cell_dyn } => Self::Primitive { + cell_dyn: cell_dyn.clone_boxed(), + }, + Self::RaceDetectionPrimitive { cell_dyn } => { + Self::RaceDetectionPrimitive { + cell_dyn: RaceDetectionPrimitive::clone_boxed( + cell_dyn.deref(), + ), + } + } + Self::Component(component_ledger) => { + Self::Component(component_ledger.clone()) + } + } + } +} + +impl From for CellLedger { + fn from(v: ComponentLedger) -> Self { + Self::Component(v) + } +} + +impl From> for CellLedger { + fn from(cell_dyn: Box) -> Self { + Self::RaceDetectionPrimitive { cell_dyn } + } +} + +impl From> for CellLedger { + fn from(cell_dyn: Box) -> Self { + Self::Primitive { cell_dyn } + } +} + +impl CellLedger { + fn new_comp + Clone>( + idx: ComponentIdx, + env: &Environment, + ) -> Self { + Self::Component(ComponentLedger { + index_bases: BaseIndices::new( + env.ports.peek_next_idx(), + (env.cells.peek_next_idx().index() + 1).into(), + env.ref_cells.peek_next_idx(), + env.ref_ports.peek_next_idx(), + ), + comp_id: idx, + }) + } + + pub fn as_comp(&self) -> Option<&ComponentLedger> { + match self { + Self::Component(comp) => Some(comp), + _ => None, + } + } + + #[inline] + pub fn unwrap_comp(&self) -> &ComponentLedger { + self.as_comp() + .expect("Unwrapped cell ledger as component but received primitive") + } + + #[must_use] + pub fn as_primitive(&self) -> Option<&dyn Primitive> { + match self { + Self::Primitive { cell_dyn } => Some(&**cell_dyn), + Self::RaceDetectionPrimitive { cell_dyn } => { + Some(cell_dyn.as_primitive()) + } + _ => None, + } + } + + pub fn unwrap_primitive(&self) -> &dyn Primitive { + self.as_primitive() + .expect("Unwrapped cell ledger as primitive but received component") + } +} + +impl Debug for CellLedger { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Primitive { .. } => f.debug_struct("Primitive").finish(), + Self::RaceDetectionPrimitive { .. } => { + f.debug_struct("RaceDetectionPrimitive").finish() + } + Self::Component(ComponentLedger { + index_bases, + comp_id, + }) => f + .debug_struct("Component") + .field("index_bases", index_bases) + .field("comp_id", comp_id) + .finish(), + } + } +} + +#[derive(Debug, Clone)] +struct PinnedPorts { + map: HashMap, +} + +impl PinnedPorts { + pub fn iter( + &self, + ) -> impl Iterator + '_ { + self.map.iter() + } + + pub fn new() -> Self { + Self { + map: HashMap::new(), + } + } + + pub fn insert(&mut self, port: GlobalPortIdx, val: BitVecValue) { + self.map.insert(port, val); + } + + pub fn remove(&mut self, port: GlobalPortIdx) { + self.map.remove(&port); + } +} + +#[derive(Debug, Clone)] +pub struct Environment + Clone> { + /// A map from global port IDs to their current values. + ports: PortMap, + /// A map from global cell IDs to their current state and execution info. + pub(super) cells: CellMap, + /// A map from global ref cell IDs to the cell they reference, if any. + pub(super) ref_cells: RefCellMap, + /// A map from global ref port IDs to the port they reference, if any. + pub(super) ref_ports: RefPortMap, + + /// The program counter for the whole program execution. + pub(super) pc: ProgramCounter, + + pinned_ports: PinnedPorts, + + clocks: ClockMap, + thread_map: ThreadMap, + control_ports: HashMap, + + /// The immutable context. This is retained for ease of use. + /// This value should have a cheap clone implementation, such as &Context + /// or RC. + pub(super) ctx: C, + + memory_header: Option>, + logger: Logger, +} + +impl + Clone> Environment { + /// A utility function to get the context from the environment. This is not + /// suitable for cases in which mutation is required. In such cases, the + /// context should be accessed directly. + pub fn ctx(&self) -> &Context { + self.ctx.as_ref() + } + /// Returns the full name and port list of each cell in the context + pub fn iter_cells( + &self, + ) -> impl Iterator)> + '_ { + let env = self; + let cell_names = self.cells.iter().map(|(idx, _ledger)| { + (idx.get_full_name(env), self.get_ports_for_cell(idx)) + }); + + cell_names + // get parent from cell, if not exist then lookup component ledger get base idxs, go to context and get signature to get ports + // for cells, same thing but in the cell ledger, subtract child offset from parent offset to get local offset, lookup in cell offset in component info + } + + //not sure if beneficial to change this to be impl iterator as well + fn get_ports_for_cell(&self, cell: GlobalCellIdx) -> Vec { + let parent = self.get_parent_cell_from_cell(cell); + match parent { + None => { + let comp_ledger = self.cells[cell].as_comp().unwrap(); + let comp_info = + self.ctx().secondary.comp_aux_info.get(comp_ledger.comp_id); + let port_ids = comp_info.signature().into_iter().map(|x| { + &self.ctx().secondary.local_port_defs + [comp_info.port_offset_map[x]] + .name + }); + let port_names = port_ids + .map(|x| String::from(x.lookup_name(self.ctx()))) + .collect_vec(); + port_names + } + Some(parent_cell) => { + let parent_comp_ledger = self.cells[parent_cell].unwrap_comp(); + let comp_info = self + .ctx() + .secondary + .comp_aux_info + .get(parent_comp_ledger.comp_id); + let local_offset = cell - &parent_comp_ledger.index_bases; + + let port_ids = self.ctx().secondary.local_cell_defs + [comp_info.cell_offset_map[local_offset]] + .ports + .into_iter() + .map(|x| { + &self.ctx().secondary.local_port_defs + [comp_info.port_offset_map[x]] + .name + }); + let names = port_ids + .map(|x| String::from(x.lookup_name(self.ctx()))) + .collect_vec(); + names + } + } + } + + pub fn new( + ctx: C, + data_map: Option, + check_race: bool, + logging_conf: LoggingConfig, + ) -> Self { + let root = ctx.as_ref().entry_point; + let aux = &ctx.as_ref().secondary[root]; + + let mut clocks = IndexedMap::new(); + let root_clock = clocks.push(VectorClock::new()); + + let mut env = Self { + ports: PortMap::with_capacity(aux.port_offset_map.count()), + cells: CellMap::with_capacity(aux.cell_offset_map.count()), + ref_cells: RefCellMap::with_capacity( + aux.ref_cell_offset_map.count(), + ), + ref_ports: RefPortMap::with_capacity( + aux.ref_port_offset_map.count(), + ), + pc: ProgramCounter::new_empty(), + clocks, + thread_map: ThreadMap::new(root_clock), + ctx, + memory_header: None, + pinned_ports: PinnedPorts::new(), + control_ports: HashMap::new(), + logger: logging::initialize_logger(logging_conf), + }; + + let root_node = CellLedger::new_comp(root, &env); + let root_cell = env.cells.push(root_node); + env.layout_component( + root_cell, + &data_map, + &mut HashSet::new(), + check_race, + ); + + let root_thread = ThreadMap::root_thread(); + env.clocks[root_clock].increment(&root_thread); + + // Initialize program counter + // TODO griffin: Maybe refactor into a separate function + for (idx, ledger) in env.cells.iter() { + if let CellLedger::Component(comp) = ledger { + if let Some(ctrl) = + &env.ctx.as_ref().primary[comp.comp_id].control + { + env.pc.vec_mut().push(( + if comp.comp_id == root { + Some(root_thread) + } else { + None + }, + ControlPoint { + comp: idx, + control_node_idx: *ctrl, + }, + )) + } + } + } + + if let Some(header) = data_map { + env.memory_header = Some(header.header.memories); + } + + env + } + + /// Internal function used to layout a given component from a cell id + /// + /// Layout is handled in the following order: + /// 1. component signature (input/output) + /// 2. group hole ports + /// 3. cells + ports, primitive + /// 4. sub-components + /// 5. ref-cells & ports + fn layout_component( + &mut self, + comp: GlobalCellIdx, + data_map: &Option, + memories_initialized: &mut HashSet, + check_race: bool, + ) { + // for mutability reasons, see note in `[Environment::new]` + let ctx = self.ctx.clone(); + let ctx_ref = ctx.as_ref(); + + let ComponentLedger { + index_bases, + comp_id, + } = self.cells[comp] + .as_comp() + .expect("Called layout component with a non-component cell."); + let comp_aux = &ctx_ref.secondary[*comp_id]; + + // Insert the component's continuous assignments into the program counter, if non-empty + let cont_assigns = + self.ctx.as_ref().primary[*comp_id].continuous_assignments; + if !cont_assigns.is_empty() { + self.pc.push_continuous_assigns(comp, cont_assigns); + } + + // first layout the signature + for sig_port in comp_aux.signature().iter() { + let def_idx = comp_aux.port_offset_map[sig_port]; + let info = &self.ctx.as_ref().secondary[def_idx]; + let idx = self.ports.push(PortValue::new_undef()); + + // the direction attached to the port is reversed for the signature. + // We only want to add the input ports to the control ports list. + if !info.is_data && info.direction != calyx_ir::Direction::Input { + self.control_ports + .insert(idx, info.width.try_into().unwrap()); + } + debug_assert_eq!(index_bases + sig_port, idx); + } + // second group ports + for group_idx in comp_aux.definitions.groups() { + //go + let go = self.ports.push(PortValue::new_undef()); + + //done + let done = self.ports.push(PortValue::new_undef()); + + // quick sanity check asserts + let go_actual = + index_bases + self.ctx.as_ref().primary[group_idx].go; + let done_actual = + index_bases + self.ctx.as_ref().primary[group_idx].done; + // Case 1 - Go defined before done + if self.ctx.as_ref().primary[group_idx].go + < self.ctx.as_ref().primary[group_idx].done + { + debug_assert_eq!(done, done_actual); + debug_assert_eq!(go, go_actual); + } + // Case 2 - Done defined before go + else { + // in this case go is defined after done, so our variable names + // are backward, but this is not a problem since they are + // initialized to the same value + debug_assert_eq!(go, done_actual); + debug_assert_eq!(done, go_actual); + } + self.control_ports.insert(go, 1); + self.control_ports.insert(done, 1); + } + + for (cell_off, def_idx) in comp_aux.cell_offset_map.iter() { + let info = &self.ctx.as_ref().secondary[*def_idx]; + if !info.prototype.is_component() { + let port_base = self.ports.peek_next_idx(); + for port in info.ports.iter() { + let idx = self.ports.push(PortValue::new_undef()); + debug_assert_eq!( + &self.cells[comp].as_comp().unwrap().index_bases + port, + idx + ); + let def_idx = comp_aux.port_offset_map[port]; + let port_info = &self.ctx.as_ref().secondary[def_idx]; + if !(port_info.direction == calyx_ir::Direction::Output + || port_info.is_data && info.is_data) + { + self.control_ports + .insert(idx, port_info.width.try_into().unwrap()); + } + } + let cell_dyn = primitives::build_primitive( + info, + port_base, + self.cells.peek_next_idx(), + self.ctx.as_ref(), + data_map, + memories_initialized, + check_race.then_some(&mut self.clocks), + ); + let cell = self.cells.push(cell_dyn); + + debug_assert_eq!( + &self.cells[comp].as_comp().unwrap().index_bases + cell_off, + cell + ); + } else { + let child_comp = info.prototype.as_component().unwrap(); + let child_comp = CellLedger::new_comp(*child_comp, self); + + let cell = self.cells.push(child_comp); + debug_assert_eq!( + &self.cells[comp].as_comp().unwrap().index_bases + cell_off, + cell + ); + + // layout sub-component but don't include the data map + self.layout_component( + cell, + &None, + memories_initialized, + check_race, + ); + } + } + + if let Some(data) = data_map { + for dec in data.header.memories.iter() { + if !memories_initialized.contains(&dec.name) { + // TODO griffin: maybe make this an error? + warn!(self.logger, "Initialization was provided for memory {} but no such memory exists in the entrypoint component.", dec.name); + } + } + } + + // ref cells and ports are initialized to None + for (ref_cell, def_idx) in comp_aux.ref_cell_offset_map.iter() { + let info = &self.ctx.as_ref().secondary[*def_idx]; + for port_idx in info.ports.iter() { + let port_actual = self.ref_ports.push(None); + debug_assert_eq!( + &self.cells[comp].as_comp().unwrap().index_bases + port_idx, + port_actual + ) + } + let cell_actual = self.ref_cells.push(None); + debug_assert_eq!( + &self.cells[comp].as_comp().unwrap().index_bases + ref_cell, + cell_actual + ) + } + } + + pub fn get_comp_go(&self, comp: GlobalCellIdx) -> GlobalPortIdx { + let ledger = self.cells[comp] + .as_comp() + .expect("Called get_comp_go with a non-component cell."); + + &ledger.index_bases + self.ctx.as_ref().primary[ledger.comp_id].go + } + + pub fn get_comp_done(&self, comp: GlobalCellIdx) -> GlobalPortIdx { + let ledger = self.cells[comp] + .as_comp() + .expect("Called get_comp_done with a non-component cell."); + + &ledger.index_bases + self.ctx.as_ref().primary[ledger.comp_id].done + } + + #[inline] + pub fn get_root_done(&self) -> GlobalPortIdx { + self.get_comp_done(Self::get_root()) + } + + #[inline] + pub fn get_root() -> GlobalCellIdx { + GlobalCellIdx::new(0) + } + + pub fn is_group_running(&self, group_idx: GroupIdx) -> bool { + self.get_currently_running_groups().any(|x| x == group_idx) + } + + pub fn get_currently_running_groups( + &self, + ) -> impl Iterator + '_ { + self.pc.iter().filter_map(|(_, point)| { + let node = &self.ctx.as_ref().primary[point.control_node_idx]; + match node { + ControlNode::Enable(x) => { + let comp_go = self.get_comp_go(point.comp); + if self.ports[comp_go].as_bool().unwrap_or_default() { + Some(x.group()) + } else { + None + } + } + _ => None, + } + }) + } + + /// Given a cell idx, return the component definition that this cell is an + /// instance of. Return None if the cell is not a component instance. + pub fn get_component_idx( + &self, + cell: GlobalCellIdx, + ) -> Option { + self.cells[cell].as_comp().map(|x| x.comp_id) + } + + // ===================== Environment print implementations ===================== + + pub fn _print_env(&self) { + let root_idx = GlobalCellIdx::new(0); + let mut hierarchy = Vec::new(); + self._print_component(root_idx, &mut hierarchy) + } + + fn _print_component( + &self, + target: GlobalCellIdx, + hierarchy: &mut Vec, + ) { + let info = self.cells[target].as_comp().unwrap(); + let comp = &self.ctx.as_ref().secondary[info.comp_id]; + hierarchy.push(target); + + // This funky iterator chain first pulls the first element (the + // entrypoint) and extracts its name. Subsequent element are pairs of + // global offsets produced by a staggered iteration, yielding `(root, + // child)` then `(child, grandchild)` and so on. All the strings are + // finally collected and concatenated with a `.` separator to produce + // the fully qualified name prefix for the given component instance. + let name_prefix = hierarchy + .first() + .iter() + .map(|x| { + let info = self.cells[**x].as_comp().unwrap(); + let prior_comp = &self.ctx.as_ref().secondary[info.comp_id]; + &self.ctx.as_ref().secondary[prior_comp.name] + }) + .chain(hierarchy.iter().zip(hierarchy.iter().skip(1)).map( + |(l, r)| { + let info = self.cells[*l].as_comp().unwrap(); + let prior_comp = &self.ctx.as_ref().secondary[info.comp_id]; + let local_target = r - (&info.index_bases); + + let def_idx = &prior_comp.cell_offset_map[local_target]; + + let id = &self.ctx.as_ref().secondary[*def_idx]; + &self.ctx.as_ref().secondary[id.name] + }, + )) + .join("."); + + for (cell_off, def_idx) in comp.cell_offset_map.iter() { + let definition = &self.ctx.as_ref().secondary[*def_idx]; + + println!( + "{}.{}", + name_prefix, + self.ctx.as_ref().secondary[definition.name] + ); + for port in definition.ports.iter() { + let definition = + &self.ctx.as_ref().secondary[comp.port_offset_map[port]]; + println!( + " {}: {} ({:?})", + self.ctx.as_ref().secondary[definition.name], + self.ports[&info.index_bases + port], + &info.index_bases + port + ); + } + + let cell_idx = &info.index_bases + cell_off; + + if definition.prototype.is_component() { + self._print_component(cell_idx, hierarchy); + } else if self.cells[cell_idx] + .as_primitive() + .unwrap() + .has_serializable_state() + { + println!( + " INTERNAL_DATA: {}", + serde_json::to_string_pretty( + &self.cells[cell_idx] + .as_primitive() + .unwrap() + .serialize(None) + ) + .unwrap() + ) + } + } + + hierarchy.pop(); + } + + pub fn _print_env_stats(&self) { + println!("Environment Stats:"); + println!(" Ports: {}", self.ports.len()); + println!(" Cells: {}", self.cells.len()); + println!(" Ref Cells: {}", self.ref_cells.len()); + println!(" Ref Ports: {}", self.ref_ports.len()); + } + + pub fn print_pc(&self) { + let current_nodes = self.pc.iter().filter(|(_thread, point)| { + let node = &self.ctx.as_ref().primary[point.control_node_idx]; + match node { + ControlNode::Enable(_) | ControlNode::Invoke(_) => { + let comp_go = self.get_comp_go(point.comp); + self.ports[comp_go].as_bool().unwrap_or_default() + } + + _ => false, + } + }); + + let ctx = &self.ctx.as_ref(); + + for (_thread, point) in current_nodes { + let node = &ctx.primary[point.control_node_idx]; + match node { + ControlNode::Enable(x) => { + println!( + "{}::{}", + self.get_full_name(point.comp), + ctx.lookup_name(x.group()).underline() + ); + } + ControlNode::Invoke(x) => { + let invoked_name = match x.cell { + CellRef::Local(l) => self.get_full_name( + &self.cells[point.comp].unwrap_comp().index_bases + + l, + ), + CellRef::Ref(r) => { + let ref_global_offset = &self.cells[point.comp] + .unwrap_comp() + .index_bases + + r; + let ref_actual = + self.ref_cells[ref_global_offset].unwrap(); + + self.get_full_name(ref_actual) + } + }; + + println!( + "{}: invoke {}", + self.get_full_name(point.comp), + invoked_name.underline() + ); + } + _ => unreachable!(), + } + } + } + + fn get_name_from_cell_and_parent( + &self, + parent: GlobalCellIdx, + cell: GlobalCellIdx, + ) -> Identifier { + let component = self.cells[parent].unwrap_comp(); + let local_offset = cell - &component.index_bases; + + let def_idx = &self.ctx.as_ref().secondary[component.comp_id] + .cell_offset_map[local_offset]; + let def_info = &self.ctx.as_ref().secondary[*def_idx]; + def_info.name + } + + /// Attempt to find the parent cell for a port. If no such cell exists (i.e. + /// it is a hole port, then it returns None) + fn get_parent_cell_from_port( + &self, + port: PortRef, + comp: GlobalCellIdx, + ) -> Option { + let component = self.cells[comp].unwrap_comp(); + let comp_info = &self.ctx.as_ref().secondary[component.comp_id]; + + match port { + PortRef::Local(l) => { + for (cell_offset, cell_def_idx) in + comp_info.cell_offset_map.iter() + { + if self.ctx.as_ref().secondary[*cell_def_idx] + .ports + .contains(l) + { + return Some(&component.index_bases + cell_offset); + } + } + } + PortRef::Ref(r) => { + for (cell_offset, cell_def_idx) in + comp_info.ref_cell_offset_map.iter() + { + if self.ctx.as_ref().secondary[*cell_def_idx] + .ports + .contains(r) + { + let ref_cell_idx = &component.index_bases + cell_offset; + return Some( + self.ref_cells[ref_cell_idx] + .expect("Ref cell has not been instantiated"), + ); + } + } + } + } + None + } + + /// returns the path from the root to the given cell, not including the cell + /// itself. If no such path exists, it returns None. + fn get_parent_path_from_cell>( + &self, + target: T, + ) -> Option> { + let target: GlobalCellRef = target.into(); + + let root = Self::get_root(); + if target.is_cell() && *target.as_cell().unwrap() == root { + Some(vec![]) + } else { + let mut path = vec![root]; + + loop { + // unrwap is safe since there is always at least one entry and + // the list only grows + let current = path.last().unwrap(); + let current_comp_ledger = + self.cells[*current].as_comp().unwrap(); + let comp_info = + &self.ctx.as_ref().secondary[current_comp_ledger.comp_id]; + + let possible_relative_offset: CellRef = match target { + GlobalCellRef::Cell(target_c) => { + (target_c - ¤t_comp_ledger.index_bases).into() + } + GlobalCellRef::Ref(target_r) => { + (target_r - ¤t_comp_ledger.index_bases).into() + } + }; + + // the target is a direct child + if match possible_relative_offset { + CellRef::Local(l) => comp_info.cell_offset_map.contains(l), + CellRef::Ref(r) => { + comp_info.ref_cell_offset_map.contains(r) + } + } { + return Some(path); + } + // the target is a non-direct descendent + else { + match target { + GlobalCellRef::Cell(target) => { + let mut highest_found = None; + for offset in comp_info.cell_offset_map.keys() { + let global_offset = + ¤t_comp_ledger.index_bases + offset; + if self.cells[global_offset].as_comp().is_some() + && global_offset < target + { + highest_found = Some(global_offset); + } else if global_offset > target { + break; + } + } + + if let Some(highest_found) = highest_found { + path.push(highest_found); + } else { + return None; + } + } + GlobalCellRef::Ref(r) => { + let mut highest_found = None; + for offset in comp_info.cell_offset_map.keys() { + let global_offset = + ¤t_comp_ledger.index_bases + offset; + + if let Some(ledger) = + self.cells[global_offset].as_comp() + { + if ledger.index_bases.ref_cell_base <= r { + highest_found = Some(global_offset); + } else { + break; + } + } + } + + if let Some(highest_found) = highest_found { + path.push(highest_found); + } else { + return None; + } + } + } + } + } + } + } + + // this is currently aggressively inefficient but is fine for the moment + fn get_parent_path_from_port>( + &self, + target: T, + ) -> Option<(Vec, Option)> { + let target: GlobalPortRef = target.into(); + + let mut path = vec![Self::get_root()]; + + loop { + let current = path.last().unwrap(); + let current_ledger = self.cells[*current].as_comp().unwrap(); + let current_info = + &self.ctx.as_ref().secondary[current_ledger.comp_id]; + + if match target { + GlobalPortRef::Port(p) => { + let candidate_offset = p - ¤t_ledger.index_bases; + current_info.port_offset_map.contains(candidate_offset) + } + GlobalPortRef::Ref(r) => { + let candidate_offset = r - ¤t_ledger.index_bases; + current_info.ref_port_offset_map.contains(candidate_offset) + } + } + // The port is defined in this component + { + // first check whether our target is part of the component's + // signature ports + if let Some(local) = target.as_port() { + let offset = local - ¤t_ledger.index_bases; + if current_info.signature().contains(offset) { + return Some((path, None)); + } + } + + // now search through the component's cells + match target { + GlobalPortRef::Port(target_idx) => { + let target_offset = + target_idx - ¤t_ledger.index_bases; + + for (offset, def_idx) in + current_info.cell_offset_map.iter() + { + let def = &self.ctx.as_ref().secondary[*def_idx]; + if def.ports.contains(target_offset) { + path.push(¤t_ledger.index_bases + offset); + return Some((path, None)); + } + } + // todo: handle group interface ports + return None; + } + GlobalPortRef::Ref(target_idx) => { + let target_offset = + target_idx - ¤t_ledger.index_bases; + for (offset, def_idx) in + current_info.ref_cell_offset_map.iter() + { + let def = &self.ctx.as_ref().secondary[*def_idx]; + if def.ports.contains(target_offset) { + return Some(( + path, + Some(¤t_ledger.index_bases + offset), + )); + } + } + return None; + } + } + } + // non-direct child + else { + let mut highest_found = None; + + for cell_offset in current_info.cell_offset_map.keys() { + let cell_offset = ¤t_ledger.index_bases + cell_offset; + if let Some(ledger) = self.cells[cell_offset].as_comp() { + match target { + GlobalPortRef::Port(target) => { + if ledger.index_bases.port_base <= target { + highest_found = Some(cell_offset); + } else { + break; + } + } + GlobalPortRef::Ref(target) => { + if ledger.index_bases.ref_port_base <= target { + highest_found = Some(cell_offset); + } else { + break; + } + } + } + } + } + + if let Some(highest_found) = highest_found { + path.push(highest_found); + } else { + return None; + } + } + } + } + + pub(super) fn get_parent_cell_from_cell( + &self, + cell: GlobalCellIdx, + ) -> Option { + self.get_parent_path_from_cell(cell) + .and_then(|x| x.last().copied()) + } + + /// Traverses the given name, and returns the end of the traversal. For + /// paths with ref cells this will resolve the concrete cell **currently** + /// pointed to by the ref cell. + pub fn traverse_name_vec( + &self, + name: &[String], + ) -> Result { + assert!(!name.is_empty(), "Name cannot be empty"); + + let ctx = self.ctx.as_ref(); + let mut current = Traverser::new(); + + if name.len() == 1 && &name[0] == ctx.lookup_name(ctx.entry_point) { + Ok(Path::Cell(Self::get_root())) + } else { + if name.len() != 1 { + let mut iter = name[0..name.len() - 1].iter(); + if &name[0] == ctx.lookup_name(ctx.entry_point) { + // skip the main name + iter.next(); + } + + for name in iter { + current.next_cell(self, name)?; + } + } + + let last = name.last().unwrap(); + current.last_step(self, last) + } + } + + pub fn get_ports_from_cell( + &self, + cell: GlobalCellIdx, + ) -> Box + '_> { + if let Some(parent) = self.get_parent_cell_from_cell(cell) { + let ledger = self.cells[parent].as_comp().unwrap(); + let comp = &self.ctx.as_ref().secondary[ledger.comp_id]; + let cell_offset = cell - &ledger.index_bases; + + Box::new( + self.ctx.as_ref().secondary[comp.cell_offset_map[cell_offset]] + .ports + .iter() + .map(|x| { + ( + self.ctx.as_ref().secondary + [comp.port_offset_map[x]] + .name, + &ledger.index_bases + x, + ) + }), + ) + } else { + let ledger = self.cells[cell].as_comp().unwrap(); + let comp = &self.ctx.as_ref().secondary[ledger.comp_id]; + Box::new(comp.signature().into_iter().map(|x| { + let def_idx = comp.port_offset_map[x]; + let def = &self.ctx.as_ref().secondary[def_idx]; + (def.name, &ledger.index_bases + x) + })) + } + } + + pub fn get_full_name>(&self, nameable: N) -> String { + nameable.get_full_name(self) + } + + pub fn format_path(&self, path: &[GlobalCellIdx]) -> String { + assert!(!path.is_empty(), "Path cannot be empty"); + assert!(path[0] == Self::get_root(), "Path must start with root"); + + let root_name = + self.ctx.as_ref().lookup_name(self.ctx.as_ref().entry_point); + + path.iter().zip(path.iter().skip(1)).fold( + root_name.clone(), + |acc, (a, b)| { + let id = self.get_name_from_cell_and_parent(*a, *b); + acc + "." + &self.ctx.as_ref().secondary[id] + }, + ) + } + + /// Lookup the value of a port on the entrypoint component by name. Will + /// error if the port is not found. + pub fn lookup_port_from_string>( + &self, + port: S, + ) -> Option { + // this is not the best way to do this but it's fine for now + let path = self + .traverse_name_vec(&[port.as_ref().to_string()]) + .unwrap(); + let path_resolution = path.resolve_path(self).unwrap(); + let idx = path_resolution.as_port().unwrap(); + + self.ports[*idx].as_option().map(|x| x.val().clone()) + } + + /// Returns an input port for the entrypoint component. Will error if the + /// port is not found. + fn get_root_input_port>(&self, port: S) -> GlobalPortIdx { + let string = port.as_ref(); + + let root = Self::get_root(); + + let ledger = self.cells[root].as_comp().unwrap(); + let mut def_list = self.ctx.as_ref().secondary[ledger.comp_id].inputs(); + let found = def_list.find(|offset| { + let def_idx = self.ctx.as_ref().secondary[ledger.comp_id].port_offset_map[*offset]; + self.ctx.as_ref().lookup_name(self.ctx.as_ref().secondary[def_idx].name) == string + }).unwrap_or_else(|| panic!("Could not find port '{string}' in the entrypoint component's input ports")); + + &ledger.index_bases + found + } + + /// Pins the port with the given name to the given value. This may only be + /// used for input ports on the entrypoint component (excluding the go port) + /// and will panic if used otherwise. Intended for external use. Unrelated + /// to the rust pin. + pub fn pin_value>(&mut self, port: S, val: BitVecValue) { + let port = self.get_root_input_port(port); + + let go = self.get_comp_go(Self::get_root()); + assert!(port != go, "Cannot pin the go port"); + + self.pinned_ports.insert(port, val); + } + + /// Unpins the port with the given name. This may only be + /// used for input ports on the entrypoint component (excluding the go port) + /// and will panic if used otherwise. Intended for external use. + pub fn unpin_value>(&mut self, port: S) { + let port = self.get_root_input_port(port); + self.pinned_ports.remove(port); + } + + pub fn get_def_info( + &self, + comp_idx: ComponentIdx, + cell: LocalCellOffset, + ) -> &crate::flatten::flat_ir::base::CellDefinitionInfo + { + let comp = &self.ctx.as_ref().secondary[comp_idx]; + let idx = comp.cell_offset_map[cell]; + &self.ctx.as_ref().secondary[idx] + } + + pub fn get_def_info_ref( + &self, + comp_idx: ComponentIdx, + cell: LocalRefCellOffset, + ) -> &crate::flatten::flat_ir::base::CellDefinitionInfo + { + let comp = &self.ctx.as_ref().secondary[comp_idx]; + let idx = comp.ref_cell_offset_map[cell]; + &self.ctx.as_ref().secondary[idx] + } + + pub fn get_port_def_info( + &self, + comp_idx: ComponentIdx, + port: LocalPortOffset, + ) -> &PortDefinitionInfo { + let comp = &self.ctx.as_ref().secondary[comp_idx]; + let idx = comp.port_offset_map[port]; + &self.ctx.as_ref().secondary[idx] + } + + pub fn get_port_def_info_ref( + &self, + comp_idx: ComponentIdx, + port: LocalRefPortOffset, + ) -> Identifier { + let comp = &self.ctx.as_ref().secondary[comp_idx]; + let idx = comp.ref_port_offset_map[port]; + self.ctx.as_ref().secondary[idx] + } +} + +/// A wrapper struct for the environment that provides the functions used to +/// simulate the actual program. +/// +/// This is just to keep the simulation logic under a different namespace than +/// the environment to avoid confusion +pub struct BaseSimulator + Clone> { + env: Environment, + conf: RuntimeConfig, +} + +pub struct Simulator + Clone> { + base: BaseSimulator, + wave: Option, +} + +impl + Clone> Simulator { + pub fn build_simulator( + ctx: C, + data_file: &Option, + wave_file: &Option, + runtime_config: RuntimeConfig, + ) -> Result { + let data_dump = data_file + .as_ref() + .map(|path| { + let mut file = std::fs::File::open(path)?; + DataDump::deserialize(&mut file) + }) + // flip to a result of an option + .map_or(Ok(None), |res| res.map(Some))?; + let env = Environment::new( + ctx, + data_dump, + runtime_config.check_data_race, + runtime_config.get_logging_config(), + ); + let wave = + wave_file.as_ref().map(|p| match WaveWriter::open(p, &env) { + Ok(w) => w, + Err(err) => { + todo!("deal more gracefully with error: {err:?}") + } + }); + Ok(Self { + base: BaseSimulator::new(env, runtime_config), + wave, + }) + } + + pub fn is_done(&self) -> bool { + self.base.is_done() + } + + pub fn step(&mut self) -> CiderResult<()> { + self.base.step() + } + + pub fn converge(&mut self) -> CiderResult<()> { + self.base.converge() + } + + pub fn get_currently_running_groups( + &self, + ) -> impl Iterator + '_ { + self.base.get_currently_running_groups() + } + + pub fn is_group_running(&self, group_idx: GroupIdx) -> bool { + self.base.is_group_running(group_idx) + } + + pub fn print_pc(&self) { + self.base.print_pc(); + } + + pub fn format_cell_state( + &self, + cell_idx: GlobalCellIdx, + print_code: PrintCode, + name: Option<&str>, + ) -> Option { + self.base.format_cell_state(cell_idx, print_code, name) + } + + pub fn format_cell_ports( + &self, + cell_idx: GlobalCellIdx, + print_code: PrintCode, + name: Option<&str>, + ) -> String { + self.base.format_cell_ports(cell_idx, print_code, name) + } + + pub fn format_port_value( + &self, + port_idx: GlobalPortIdx, + print_code: PrintCode, + ) -> String { + self.base.format_port_value(port_idx, print_code) + } + + pub fn traverse_name_vec( + &self, + name: &[String], + ) -> Result { + self.base.traverse_name_vec(name) + } + + pub fn get_full_name(&self, nameable: N) -> String + where + N: GetFullName, + { + self.base.get_full_name(nameable) + } + + pub(crate) fn env(&self) -> &Environment { + self.base.env() + } + + /// Evaluate the entire program + pub fn run_program(&mut self) -> CiderResult<()> { + if self.base.conf.debug_logging { + info!(self.base.env().logger, "Starting program execution"); + } + + match self.base.run_program_inner(self.wave.as_mut()) { + Ok(_) => { + if self.base.conf.debug_logging { + info!(self.base.env().logger, "Finished program execution"); + } + Ok(()) + } + Err(e) => { + if self.base.conf.debug_logging { + slog::error!( + self.base.env().logger, + "Program execution failed with error: {}", + e.red() + ); + } + Err(e) + } + } + } +} + +impl + Clone> BaseSimulator { + pub fn new(env: Environment, conf: RuntimeConfig) -> Self { + Self { env, conf } + } + + pub(crate) fn env(&self) -> &Environment { + &self.env + } + + pub fn _print_env(&self) { + self.env._print_env() + } + + #[inline] + pub fn ctx(&self) -> &Context { + self.env.ctx.as_ref() + } + + pub fn _unpack_env(self) -> Environment { + self.env + } + + pub fn is_group_running(&self, group_idx: GroupIdx) -> bool { + self.env.is_group_running(group_idx) + } + + pub fn get_currently_running_groups( + &self, + ) -> impl Iterator + '_ { + self.env.get_currently_running_groups() + } + + pub fn traverse_name_vec( + &self, + name: &[String], + ) -> Result { + self.env.traverse_name_vec(name) + } + + pub fn get_name_from_cell_and_parent( + &self, + parent: GlobalCellIdx, + cell: GlobalCellIdx, + ) -> Identifier { + self.env.get_name_from_cell_and_parent(parent, cell) + } + + #[inline] + pub fn get_full_name>(&self, nameable: N) -> String { + self.env.get_full_name(nameable) + } + + pub fn print_pc(&self) { + self.env.print_pc() + } + + /// Pins the port with the given name to the given value. This may only be + /// used for input ports on the entrypoint component (excluding the go port) + /// and will panic if used otherwise. Intended for external use. + pub fn pin_value>(&mut self, port: S, val: BitVecValue) { + self.env.pin_value(port, val) + } + + /// Unpins the port with the given name. This may only be + /// used for input ports on the entrypoint component (excluding the go port) + /// and will panic if used otherwise. Intended for external use. + pub fn unpin_value>(&mut self, port: S) { + self.env.unpin_value(port) + } + + /// Lookup the value of a port on the entrypoint component by name. Will + /// error if the port is not found. + pub fn lookup_port_from_string( + &self, + port: &String, + ) -> Option { + self.env.lookup_port_from_string(port) + } +} + +// =========================== simulation functions =========================== +impl + Clone> BaseSimulator { + #[inline] + fn lookup_global_port_id(&self, port: GlobalPortRef) -> GlobalPortIdx { + match port { + GlobalPortRef::Port(p) => p, + // TODO Griffin: Please make sure this error message is correct with + // respect to the compiler + GlobalPortRef::Ref(r) => self.env.ref_ports[r].expect("A ref port is being queried without a supplied ref-cell. This is an error?"), + } + } + + #[inline] + fn lookup_global_cell_id(&self, cell: GlobalCellRef) -> GlobalCellIdx { + match cell { + GlobalCellRef::Cell(c) => c, + // TODO Griffin: Please make sure this error message is correct with + // respect to the compiler + GlobalCellRef::Ref(r) => self.env.ref_cells[r].expect("A ref cell is being queried without a supplied ref-cell. This is an error?"), + } + } + + #[inline] + fn get_global_port_idx( + &self, + port: &PortRef, + comp: GlobalCellIdx, + ) -> GlobalPortIdx { + let ledger = self.env.cells[comp].unwrap_comp(); + self.lookup_global_port_id(ledger.convert_to_global_port(port)) + } + + #[inline] + fn get_global_cell_idx( + &self, + cell: &CellRef, + comp: GlobalCellIdx, + ) -> GlobalCellIdx { + let ledger = self.env.cells[comp].unwrap_comp(); + self.lookup_global_cell_id(ledger.convert_to_global_cell(cell)) + } + + pub(crate) fn get_root_component(&self) -> &ComponentLedger { + self.env.cells[Environment::::get_root()] + .as_comp() + .unwrap() + } + + /// Finds the root component of the simulation and sets its go port to high + fn set_root_go_high(&mut self) { + let ledger = self.get_root_component(); + let go = &ledger.index_bases + + self.env.ctx.as_ref().primary[ledger.comp_id].go; + self.env.ports[go] = PortValue::new_implicit(BitVecValue::tru()); + } + + // may want to make this iterate directly if it turns out that the vec + // allocation is too expensive in this context + fn get_assignments( + &self, + control_points: &[ControlTuple], + ) -> Vec { + control_points + .iter() + .filter_map(|(thread, node)| { + match &self.ctx().primary[node.control_node_idx] { + ControlNode::Enable(e) => { + let group = &self.ctx().primary[e.group()]; + + Some(ScheduledAssignments::new( + node.comp, + group.assignments, + Some(GroupInterfacePorts { + go: group.go, + done: group.done, + }), + *thread, + false, + )) + } + + ControlNode::Invoke(i) => Some(ScheduledAssignments::new( + node.comp, + i.assignments, + None, + *thread, + false, + )), + + ControlNode::Empty(_) => None, + // non-leaf nodes + ControlNode::If(_) + | ControlNode::While(_) + | ControlNode::Repeat(_) + | ControlNode::Seq(_) + | ControlNode::Par(_) => None, + } + }) + .chain(self.env.pc.continuous_assigns().iter().map(|x| { + ScheduledAssignments::new(x.comp, x.assigns, None, None, true) + })) + .chain(self.env.pc.with_map().iter().map( + |(ctrl_pt, with_entry)| { + let assigns = + self.ctx().primary[with_entry.group].assignments; + ScheduledAssignments::new( + ctrl_pt.comp, + assigns, + None, + with_entry.thread, + false, + ) + }, + )) + .collect() + } + + /// A helper function which inserts indicies for the ref cells and ports + /// used in the invoke statement + fn initialize_ref_cells( + &mut self, + parent_comp: GlobalCellIdx, + invoke: &Invoke, + ) { + if invoke.ref_cells.is_empty() { + return; + } + + let parent_ledger = self.env.cells[parent_comp].unwrap_comp(); + let parent_info = + &self.env.ctx.as_ref().secondary[parent_ledger.comp_id]; + + let child_comp = self.get_global_cell_idx(&invoke.cell, parent_comp); + // this unwrap should never fail because ref-cells can only exist on + // components, not primitives + let child_ledger = self.env.cells[child_comp] + .as_comp() + .expect("malformed invoke?"); + let child_info = &self.env.ctx.as_ref().secondary[child_ledger.comp_id]; + + for (offset, cell_ref) in invoke.ref_cells.iter() { + // first set the ref cell + let global_ref_cell_idx = &child_ledger.index_bases + offset; + let global_actual_cell_idx = + self.get_global_cell_idx(cell_ref, parent_comp); + self.env.ref_cells[global_ref_cell_idx] = + Some(global_actual_cell_idx); + + // then set the ports + let child_ref_cell_info = &self.env.ctx.as_ref().secondary + [child_info.ref_cell_offset_map[*offset]]; + + let cell_info_idx = parent_info.get_cell_info_idx(*cell_ref); + match cell_info_idx { + CellDefinitionRef::Local(l) => { + let info = &self.env.ctx.as_ref().secondary[l]; + assert_eq!( + child_ref_cell_info.ports.size(), + info.ports.size() + ); + + for (dest, source) in + child_ref_cell_info.ports.iter().zip(info.ports.iter()) + { + let dest_idx = &child_ledger.index_bases + dest; + let source_idx = &parent_ledger.index_bases + source; + self.env.ref_ports[dest_idx] = Some(source_idx); + } + } + CellDefinitionRef::Ref(r) => { + let info = &self.env.ctx.as_ref().secondary[r]; + assert_eq!( + child_ref_cell_info.ports.size(), + info.ports.size() + ); + + for (dest, source) in + child_ref_cell_info.ports.iter().zip(info.ports.iter()) + { + let dest_idx = &child_ledger.index_bases + dest; + let source_ref_idx = + &parent_ledger.index_bases + source; + // TODO griffin: Make this error message actually useful + let source_idx_actual = self.env.ref_ports + [source_ref_idx] + .expect("ref port not instantiated, this is a bug"); + + self.env.ref_ports[dest_idx] = Some(source_idx_actual); + } + } + } + } + } + + fn cleanup_ref_cells( + &mut self, + parent_comp: GlobalCellIdx, + invoke: &Invoke, + ) { + if invoke.ref_cells.is_empty() { + return; + } + + let child_comp = self.get_global_cell_idx(&invoke.cell, parent_comp); + // this unwrap should never fail because ref-cells can only exist on + // components, not primitives + let child_ledger = self.env.cells[child_comp] + .as_comp() + .expect("malformed invoke?"); + let child_info = &self.env.ctx.as_ref().secondary[child_ledger.comp_id]; + + for (offset, _) in invoke.ref_cells.iter() { + // first unset the ref cell + let global_ref_cell_idx = &child_ledger.index_bases + offset; + self.env.ref_cells[global_ref_cell_idx] = None; + + // then unset the ports + let child_ref_cell_info = &self.env.ctx.as_ref().secondary + [child_info.ref_cell_offset_map[*offset]]; + + for port in child_ref_cell_info.ports.iter() { + let port_idx = &child_ledger.index_bases + port; + self.env.ref_ports[port_idx] = None; + } + } + } + + // + pub fn converge(&mut self) -> CiderResult<()> { + self.undef_all_ports(); + self.set_root_go_high(); + // set the pinned values + for (port, val) in self.env.pinned_ports.iter() { + self.env.ports[*port] = PortValue::new_implicit(val.clone()); + } + + for (comp, id) in self.env.pc.finished_comps() { + let done_port = self.env.get_comp_done(*comp); + let v = PortValue::new_implicit(BitVecValue::tru()); + self.env.ports[done_port] = if self.conf.check_data_race { + v.with_thread(id.expect("finished comps should have a thread")) + } else { + v + } + } + + let (vecs, par_map, mut with_map, repeat_map) = + self.env.pc.take_fields(); + + // for mutability reasons, this should be a cheap clone, either an RC in + // the owned case or a simple reference clone + let ctx = self.env.ctx.clone(); + let ctx_ref = ctx.as_ref(); + + for (thread, node) in vecs.iter() { + let comp_done = self.env.get_comp_done(node.comp); + let comp_go = self.env.get_comp_go(node.comp); + let thread = thread.or_else(|| { + self.env.ports[comp_go].as_option().and_then(|t| t.thread()) + }); + + // if the done is not high & defined, we need to set it to low + if !self.env.ports[comp_done].as_bool().unwrap_or_default() { + self.env.ports[comp_done] = + PortValue::new_implicit(BitVecValue::fals()); + } + + match &ctx_ref.primary[node.control_node_idx] { + // actual nodes + ControlNode::Enable(enable) => { + let go_local = ctx_ref.primary[enable.group()].go; + let index_bases = &self.env.cells[node.comp] + .as_comp() + .unwrap() + .index_bases; + + // set go high + let go_idx = index_bases + go_local; + self.env.ports[go_idx] = + PortValue::new_implicit(BitVecValue::tru()); + } + ControlNode::Invoke(invoke) => { + if invoke.comb_group.is_some() + && !with_map.contains_key(node) + { + with_map.insert( + node.clone(), + WithEntry::new(invoke.comb_group.unwrap(), thread), + ); + } + + let go = self.get_global_port_idx(&invoke.go, node.comp); + self.env.ports[go] = + PortValue::new_implicit(BitVecValue::tru()) + .with_thread_optional( + if self.conf.check_data_race { + assert!(thread.is_some()); + thread + } else { + None + }, + ); + + // TODO griffin: should make this skip initialization if + // it's already initialized + self.initialize_ref_cells(node.comp, invoke); + } + // with nodes + ControlNode::If(i) => { + if i.cond_group().is_some() && !with_map.contains_key(node) + { + with_map.insert( + node.clone(), + WithEntry::new(i.cond_group().unwrap(), thread), + ); + } + } + ControlNode::While(w) => { + if w.cond_group().is_some() && !with_map.contains_key(node) + { + with_map.insert( + node.clone(), + WithEntry::new(w.cond_group().unwrap(), thread), + ); + } + } + // -- + ControlNode::Empty(_) + | ControlNode::Seq(_) + | ControlNode::Par(_) + | ControlNode::Repeat(_) => {} + } + } + + self.env + .pc + .restore_fields((vecs, par_map, with_map, repeat_map)); + + let assigns_bundle = self.get_assignments(self.env.pc.node_slice()); + + self.simulate_combinational(&assigns_bundle) + .map_err(|e| e.prettify_message(&self.env).into()) + } + + pub fn step(&mut self) -> CiderResult<()> { + self.converge()?; + + let out: Result<(), BoxedCiderError> = { + let mut result = Ok(()); + for cell in self.env.cells.values_mut() { + match cell { + CellLedger::Primitive { cell_dyn } => { + let res = cell_dyn.exec_cycle(&mut self.env.ports); + if res.is_err() { + result = Err(res.unwrap_err()); + break; + } + } + + CellLedger::RaceDetectionPrimitive { cell_dyn } => { + let res = cell_dyn.exec_cycle_checked( + &mut self.env.ports, + &mut self.env.clocks, + &self.env.thread_map, + ); + if res.is_err() { + result = Err(res.unwrap_err()); + break; + } + } + CellLedger::Component(_) => {} + } + } + result.map_err(|e| e.prettify_message(&self.env).into()) + }; + + self.env.pc.clear_finished_comps(); + + let mut new_nodes = vec![]; + let (mut vecs, mut par_map, mut with_map, mut repeat_map) = + self.env.pc.take_fields(); + + let mut removed = vec![]; + + for (i, node) in vecs.iter_mut().enumerate() { + let keep_node = self + .evaluate_control_node( + node, + &mut new_nodes, + (&mut par_map, &mut with_map, &mut repeat_map), + ) + .map_err(|e| e.prettify_message(&self.env))?; + if !keep_node { + removed.push(i); + } + } + + for i in removed.into_iter().rev() { + vecs.swap_remove(i); + } + + self.env + .pc + .restore_fields((vecs, par_map, with_map, repeat_map)); + + // insert all the new nodes from the par into the program counter + self.env.pc.vec_mut().extend(new_nodes); + + out + } + + fn evaluate_control_node( + &mut self, + node: &mut ControlTuple, + new_nodes: &mut Vec, + maps: PcMaps, + ) -> RuntimeResult { + let (node_thread, node) = node; + let (par_map, with_map, repeat_map) = maps; + let comp_go = self.env.get_comp_go(node.comp); + let comp_done = self.env.get_comp_done(node.comp); + + let thread = node_thread.or_else(|| { + self.env.ports[comp_go].as_option().and_then(|x| x.thread()) + }); + + // mutability trick + let ctx_clone = self.env.ctx.clone(); + let ctx = ctx_clone.as_ref(); + + if !self.env.ports[comp_go].as_bool().unwrap_or_default() + || self.env.ports[comp_done].as_bool().unwrap_or_default() + { + // if the go port is low or the done port is high, we skip the + // node without doing anything + return Ok(true); + } + + // just considering a single node case for the moment + let retain_bool = match &ctx.primary[node.control_node_idx] { + ControlNode::Seq(seq) => { + if !seq.is_empty() { + let next = seq.stms()[0]; + *node = node.new_retain_comp(next); + true + } else { + node.mutate_into_next(self.env.ctx.as_ref()) + } + } + ControlNode::Par(par) => { + if par_map.contains_key(node) { + let count = par_map.get_mut(node).unwrap(); + *count -= 1; + + if *count == 0 { + par_map.remove(node); + if self.conf.check_data_race { + let thread = + thread.expect("par nodes should have a thread"); + + let child_clock_idx = + self.env.thread_map.unwrap_clock_id(thread); + let parent = + self.env.thread_map[thread].parent().unwrap(); + let parent_clock = + self.env.thread_map.unwrap_clock_id(parent); + let child_clock = std::mem::take( + &mut self.env.clocks[child_clock_idx], + ); + self.env.clocks[parent_clock].sync(&child_clock); + self.env.clocks[child_clock_idx] = child_clock; + assert!(self.env.thread_map[thread] + .parent() + .is_some()); + *node_thread = Some(parent); + self.env.clocks[parent_clock].increment(&parent); + } + node.mutate_into_next(self.env.ctx.as_ref()) + } else { + false + } + } else { + par_map.insert( + node.clone(), + par.stms().len().try_into().expect( + "More than (2^16 - 1 threads) in a par block. Are you sure this is a good idea?", + ), + ); + new_nodes.extend(par.stms().iter().map(|x| { + let thread = if self.conf.check_data_race { + let thread = + thread.expect("par nodes should have a thread"); + + let new_thread_idx: ThreadIdx = *(self + .env + .pc + .lookup_thread(node.comp, thread, *x) + .or_insert_with(|| { + let new_clock_idx = + self.env.clocks.new_clock(); + + self.env + .thread_map + .spawn(thread, new_clock_idx) + })); + + let new_clock_idx = self + .env + .thread_map + .unwrap_clock_id(new_thread_idx); + + self.env.clocks[new_clock_idx] = self.env.clocks + [self.env.thread_map.unwrap_clock_id(thread)] + .clone(); + + self.env.clocks[new_clock_idx] + .increment(&new_thread_idx); + + Some(new_thread_idx) + } else { + None + }; + + (thread, node.new_retain_comp(*x)) + })); + + if check_data_race { + let thread = + thread.expect("par nodes should have a thread"); + let clock = self.env.thread_map.unwrap_clock_id(thread); + self.env.clocks[clock].increment(&thread); + } + + false + } + } + ControlNode::If(i) => self.handle_if(with_map, node, thread, i)?, + ControlNode::While(w) => { + self.handle_while(w, with_map, node, thread)? + } + ControlNode::Repeat(rep) => { + if let Some(count) = repeat_map.get_mut(node) { + *count -= 1; + if *count == 0 { + repeat_map.remove(node); + node.mutate_into_next(self.env.ctx.as_ref()) + } else { + *node = node.new_retain_comp(rep.body); + true + } + } else { + repeat_map.insert(node.clone(), rep.num_repeats); + *node = node.new_retain_comp(rep.body); + true + } + } + + // ===== leaf nodes ===== + ControlNode::Empty(_) => { + node.mutate_into_next(self.env.ctx.as_ref()) + } + ControlNode::Enable(e) => { + let done_local = self.env.ctx.as_ref().primary[e.group()].done; + let done_idx = + &self.env.cells[node.comp].as_comp().unwrap().index_bases + + done_local; + + if !self.env.ports[done_idx].as_bool().unwrap_or_default() { + true + } else { + // This group has finished running and may be removed + // this is somewhat dubious at the moment since it + // relies on the fact that the group done port will + // still be high since convergence hasn't propagated the + // low done signal yet. + node.mutate_into_next(self.env.ctx.as_ref()) + } + } + ControlNode::Invoke(i) => { + let done = self.get_global_port_idx(&i.done, node.comp); + + if i.comb_group.is_some() && !with_map.contains_key(node) { + with_map.insert( + node.clone(), + WithEntry::new(i.comb_group.unwrap(), thread), + ); + } + + if !self.env.ports[done].as_bool().unwrap_or_default() { + true + } else { + self.cleanup_ref_cells(node.comp, i); + + if i.comb_group.is_some() { + with_map.remove(node); + } + + node.mutate_into_next(self.env.ctx.as_ref()) + } + } + }; + + if !retain_bool && ControlPoint::get_next(node, self.env.ctx.as_ref()).is_none() && + // either we are not a par node, or we are the last par node + (!matches!(&self.env.ctx.as_ref().primary[node.control_node_idx], ControlNode::Par(_)) || !par_map.contains_key(node)) + { + if self.conf.check_data_race { + assert!( + thread.is_some(), + "finished comps should have a thread" + ); + } + + self.env.pc.set_finshed_comp(node.comp, thread); + let comp_ledger = self.env.cells[node.comp].unwrap_comp(); + *node = node.new_retain_comp( + self.env.ctx.as_ref().primary[comp_ledger.comp_id] + .control + .unwrap(), + ); + Ok(true) + } else { + Ok(retain_bool) + } + } + + fn handle_while( + &mut self, + w: &While, + with_map: &mut HashMap, + node: &mut ControlPoint, + thread: Option, + check_data_race: bool, + ) -> RuntimeResult { + let target = GlobalPortRef::from_local( + w.cond_port(), + &self.env.cells[node.comp].unwrap_comp().index_bases, + ); + + let idx = match target { + GlobalPortRef::Port(p) => p, + GlobalPortRef::Ref(r) => self.env.ref_ports[r] + .expect("While condition (ref) is undefined"), + }; + if check_data_race { + if let Some(clocks) = self.env.ports[idx].clocks() { + let read_clock = + self.env.thread_map.unwrap_clock_id(thread.unwrap()); + clocks + .check_read( + (thread.unwrap(), read_clock), + &mut self.env.clocks, + ) + .map_err(|e| { + e.add_cell_info( + self.env + .get_parent_cell_from_port( + w.cond_port(), + node.comp, + ) + .unwrap(), + ) + })?; + } + } + let result = self.env.ports[idx] + .as_bool() + .expect("While condition is undefined"); + + if result { + // enter the body + *node = node.new_retain_comp(w.body()); + Ok(true) + } else { + if w.cond_group().is_some() { + with_map.remove(node); + } + // ascend the tree + Ok(node.mutate_into_next(self.env.ctx.as_ref())) + } + } + + fn handle_if( + &mut self, + with_map: &mut HashMap, + node: &mut ControlPoint, + thread: Option, + i: &If, + ) -> RuntimeResult { + if i.cond_group().is_some() && with_map.get(node).unwrap().entered { + with_map.remove(node); + Ok(node.mutate_into_next(self.env.ctx.as_ref())) + } else { + if let Some(entry) = with_map.get_mut(node) { + entry.set_entered() + } + + let target = GlobalPortRef::from_local( + i.cond_port(), + &self.env.cells[node.comp].unwrap_comp().index_bases, + ); + let idx = match target { + GlobalPortRef::Port(p) => p, + GlobalPortRef::Ref(r) => self.env.ref_ports[r] + .expect("If condition (ref) is undefined"), + }; + + if self.conf.check_data_race { + if let Some(clocks) = self.env.ports[idx].clocks() { + let read_clock = + self.env.thread_map.unwrap_clock_id(thread.unwrap()); + clocks + .check_read( + (thread.unwrap(), read_clock), + &mut self.env.clocks, + ) + .map_err(|e| { + e.add_cell_info( + self.env + .get_parent_cell_from_port( + i.cond_port(), + node.comp, + ) + .unwrap(), + ) + })?; + } + } + + let result = self.env.ports[idx] + .as_bool() + .expect("If condition is undefined"); + + let target = if result { i.tbranch() } else { i.fbranch() }; + *node = node.new_retain_comp(target); + Ok(true) + } + } + + pub fn is_done(&self) -> bool { + self.env.ports[self.env.get_root_done()] + .as_bool() + .unwrap_or_default() + } + + fn run_program_inner( + &mut self, + mut wave: Option<&mut WaveWriter>, + ) -> Result<(), BoxedCiderError> { + let mut time = 0; + while !self.is_done() { + if let Some(wave) = wave.as_mut() { + wave.write_values(time, &self.env.ports)?; + } + // self.print_pc(); + self.step()?; + time += 1; + } + if let Some(wave) = wave { + wave.write_values(time, &self.env.ports)?; + } + Ok(()) + } + + fn evaluate_guard( + &self, + guard: GuardIdx, + comp: GlobalCellIdx, + ) -> Option { + let guard = &self.ctx().primary[guard]; + match guard { + Guard::True => Some(true), + Guard::Or(a, b) => { + let g1 = self.evaluate_guard(*a, comp)?; + let g2 = self.evaluate_guard(*b, comp)?; + Some(g1 || g2) + } + Guard::And(a, b) => { + let g1 = self.evaluate_guard(*a, comp)?; + let g2 = self.evaluate_guard(*b, comp)?; + Some(g1 && g2) + } + Guard::Not(n) => Some(!self.evaluate_guard(*n, comp)?), + Guard::Comp(c, a, b) => { + let comp_v = self.env.cells[comp].unwrap_comp(); + + let a = self + .lookup_global_port_id(comp_v.convert_to_global_port(a)); + let b = self + .lookup_global_port_id(comp_v.convert_to_global_port(b)); + + let a_val = self.env.ports[a].val()?; + let b_val = self.env.ports[b].val()?; + match c { + calyx_ir::PortComp::Eq => a_val == b_val, + calyx_ir::PortComp::Neq => a_val != b_val, + calyx_ir::PortComp::Gt => a_val.is_greater(b_val), + calyx_ir::PortComp::Lt => a_val.is_less(b_val), + calyx_ir::PortComp::Geq => a_val.is_greater_or_equal(b_val), + calyx_ir::PortComp::Leq => a_val.is_less_or_equal(b_val), + } + .into() + } + Guard::Port(p) => { + let comp_v = self.env.cells[comp].unwrap_comp(); + let p_idx = self + .lookup_global_port_id(comp_v.convert_to_global_port(p)); + self.env.ports[p_idx].as_bool() + } + } + } + + fn undef_all_ports(&mut self) { + for (_idx, port_val) in self.env.ports.iter_mut() { + port_val.set_undef(); + } + } + + fn simulate_combinational( + &mut self, + assigns_bundle: &[ScheduledAssignments], + ) -> RuntimeResult<()> { + let mut has_changed = true; + let mut have_zeroed_control_ports = false; + + if self.conf.debug_logging { + info!(self.env.logger, "Started combinational convergence"); + } + + while has_changed { + has_changed = false; + + // evaluate all the assignments and make updates + for ScheduledAssignments { + active_cell, + assignments, + interface_ports, + thread, + is_cont, + } in assigns_bundle.iter() + { + let ledger = self.env.cells[*active_cell].as_comp().unwrap(); + let go = interface_ports + .as_ref() + .map(|x| &ledger.index_bases + x.go); + let done = interface_ports + .as_ref() + .map(|x| &ledger.index_bases + x.done); + + let comp_go = self.env.get_comp_go(*active_cell); + let thread = self.compute_thread(comp_go, thread, go); + + // the go for the group is high + if go + .as_ref() + // the group must have its go signal high and the go + // signal of the component must also be high + .map(|g| { + self.env.ports[*g].as_bool().unwrap_or_default() + && self.env.ports[comp_go] + .as_bool() + .unwrap_or_default() + }) + .unwrap_or_else(|| { + // if there is no go signal, then we want to run the + // continuous assignments but not comb group assignments + if *is_cont { + true + } else { + self.env.ports[comp_go] + .as_bool() + .unwrap_or_default() + } + }) + { + for assign_idx in assignments { + let assign = &self.env.ctx.as_ref().primary[assign_idx]; + + // TODO griffin: Come back to this unwrap default later + // since we may want to do something different if the guard + // does not have a defined value + if self + .evaluate_guard(assign.guard, *active_cell) + .unwrap_or_default() + { + let port = self + .get_global_port_idx(&assign.src, *active_cell); + let val = &self.env.ports[port]; + + if self.conf.debug_logging { + info!( + self.env.logger, + "Assignment fired in {}: {}\n wrote {}", + self.env.get_full_name(active_cell), + self.ctx() + .printer() + .print_assignment( + ledger.comp_id, + assign_idx + ) + .yellow(), + val.bold() + ); + } + + if self.conf.check_data_race { + if let Some(clocks) = val.clocks() { + // skip checking clocks for continuous assignments + if !is_cont { + if let Some(thread) = thread { + let thread_clock = self + .env + .thread_map + .unwrap_clock_id(thread); + + clocks + .check_read( + (thread, thread_clock), + &mut self.env.clocks, + ) + .map_err(|e| { + // TODO griffin: find a less hacky way to + // do this + e.add_cell_info( + self.env + .get_parent_cell_from_port( + assign.src, + *active_cell, + ) + .unwrap(), + ) + })?; + } else { + panic!("cannot determine thread for non-continuous assignment that touches a checked port"); + } + } + } + } + + let dest = self + .get_global_port_idx(&assign.dst, *active_cell); + + if let Some(done) = done { + if dest != done { + let done_val = &self.env.ports[done]; + + if done_val.as_bool().unwrap_or(true) { + // skip this assignment when we are done or + // or the done signal is undefined + continue; + } + } + } + + if let Some(v) = val.as_option() { + let result = self.env.ports.insert_val( + dest, + AssignedValue::new( + v.val().clone(), + (assign_idx, *active_cell), + ) + .with_thread_optional(thread), + ); + + let changed = match result { + Ok(update) => update, + Err(e) => { + match e.a1.winner() { + AssignmentWinner::Assign(assignment_idx, global_cell_idx) => { + let assign = &self.env.ctx.as_ref().primary[*assignment_idx]; + if !self + .evaluate_guard(assign.guard, *global_cell_idx) + .unwrap_or_default() { + // the prior assignment is + // no longer valid so we + // replace it with the new + // one + let target = self.get_global_port_idx(&assign.dst, *global_cell_idx); + self.env.ports[target] = e.a2.into(); + + UpdateStatus::Changed + } else { + return Err(RuntimeError::ConflictingAssignments(e).into()); + } + }, + _ => return Err(RuntimeError::ConflictingAssignments(e).into()), + } + } + }; + + has_changed |= changed.as_bool(); + } + // attempts to undefine a control port that is zero + // will be ignored otherwise it is an error + // this is a bit of a hack and should be removed in + // the long run + else if self.env.ports[dest].is_def() + && !(self.env.control_ports.contains_key(&dest) + && self.env.ports[dest].is_zero().unwrap()) + { + todo!("Raise an error here since this assignment is undefining things: {}. Port currently has value: {}", self.env.ctx.as_ref().printer().print_assignment(ledger.comp_id, assign_idx), &self.env.ports[dest]) + } + } + + if self.conf.check_data_race { + if let Some(read_ports) = self + .env + .ctx + .as_ref() + .primary + .guard_read_map + .get(assign.guard) + { + for port in read_ports { + let port_idx = self.get_global_port_idx( + port, + *active_cell, + ); + if let Some(clocks) = + self.env.ports[port_idx].clocks() + { + let thread = thread + .expect("cannot determine thread"); + let thread_clock = self + .env + .thread_map + .unwrap_clock_id(thread); + clocks + .check_read( + (thread, thread_clock), + &mut self.env.clocks, + ) + .map_err(|e| { + // TODO griffin: find a less hacky way to + // do this + e.add_cell_info( + self.env + .get_parent_cell_from_port( + *port, + *active_cell, + ) + .unwrap(), + ) + })?; + } + } + } + } + } + } + } + + // Run all the primitives + let changed: bool = self + .env + .cells + .range() + .iter() + .filter_map(|x| match &mut self.env.cells[x] { + CellLedger::Primitive { cell_dyn } => { + Some(cell_dyn.exec_comb(&mut self.env.ports)) + } + CellLedger::RaceDetectionPrimitive { cell_dyn } => { + Some(cell_dyn.exec_comb_checked( + &mut self.env.ports, + &mut self.env.clocks, + &self.env.thread_map, + )) + } + + CellLedger::Component(_) => None, + }) + .fold_ok(UpdateStatus::Unchanged, |has_changed, update| { + has_changed | update + })? + .as_bool(); + + has_changed |= changed; + + // check for undefined done ports. If any remain after we've + // converged then they should be set to zero and we should continue + // convergence. Since these ports cannot become undefined again we + // only need to do this once + if !has_changed && !have_zeroed_control_ports { + have_zeroed_control_ports = true; + for (port, width) in self.env.control_ports.iter() { + if self.env.ports[*port].is_undef() { + self.env.ports[*port] = + PortValue::new_implicit(BitVecValue::zero(*width)); + has_changed = true; + + if self.conf.debug_logging { + info!( + self.env.logger, + "Control port {} has been implicitly set to zero", + self.env.get_full_name(*port) + ); + } + } + } + } + } + + if self.conf.undef_guard_check { + let mut error_v = vec![]; + for bundle in assigns_bundle.iter() { + let ledger = + self.env.cells[bundle.active_cell].as_comp().unwrap(); + let go = bundle + .interface_ports + .as_ref() + .map(|x| &ledger.index_bases + x.go); + let done = bundle + .interface_ports + .as_ref() + .map(|x| &ledger.index_bases + x.done); + + if !done + .and_then(|done| self.env.ports[done].as_bool()) + .unwrap_or_default() + && go + .and_then(|go| self.env.ports[go].as_bool()) + .unwrap_or(true) + { + for assign in bundle.assignments.iter() { + let guard_idx = self.ctx().primary[assign].guard; + if self + .evaluate_guard(guard_idx, bundle.active_cell) + .is_none() + { + let inner_v = self + .ctx() + .primary + .guard_read_map + .get(guard_idx) + .unwrap() + .iter() + .filter_map(|p| { + let p = self.get_global_port_idx( + p, + bundle.active_cell, + ); + if self.env.ports[p].is_undef() { + Some(p) + } else { + None + } + }) + .collect_vec(); + + error_v.push((bundle.active_cell, assign, inner_v)) + } + } + } + } + if !error_v.is_empty() { + return Err(RuntimeError::UndefinedGuardError(error_v).into()); + } + } + + if self.conf.debug_logging { + info!(self.env.logger, "Finished combinational convergence"); + } + + Ok(()) + } + + /// Attempts to compute the thread id for the given group/component. + /// + /// If the given thread is `None`, then the thread id is computed from the + /// go port for the group. If no such port exists, or it lacks a thread id, + /// then the thread id is computed from the go port for the component. If + /// none of these succeed then `None` is returned. + fn compute_thread( + &self, + comp_go: GlobalPortIdx, + thread: &Option, + go: Option, + ) -> Option { + thread.or_else(|| { + if let Some(go_idx) = go { + if let Some(go_thread) = + self.env.ports[go_idx].as_option().and_then(|a| a.thread()) + { + return Some(go_thread); + } + } + self.env.ports[comp_go].as_option().and_then(|x| x.thread()) + }) + } + + /// Dump the current state of the environment as a DataDump + pub fn dump_memories( + &self, + dump_registers: bool, + all_mems: bool, + ) -> DataDump { + let ctx = self.ctx(); + let entrypoint_secondary = &ctx.secondary[ctx.entry_point]; + + let mut dump = DataDump::new_empty_with_top_level( + ctx.resolve_id(entrypoint_secondary.name).clone(), + ); + + let root = self.get_root_component(); + + for (offset, idx) in entrypoint_secondary.cell_offset_map.iter() { + let cell_info = &ctx.secondary[*idx]; + let cell_index = &root.index_bases + offset; + let name = ctx.resolve_id(cell_info.name).clone(); + match &cell_info.prototype { + CellPrototype::Memory { + width, + dims, + is_external, + .. + } if *is_external | all_mems => { + let declaration = + if *is_external && self.env.memory_header.is_some() { + if let Some(dec) = self + .env + .memory_header + .as_ref() + .unwrap() + .iter() + .find(|x| x.name == name) + { + dec.clone() + } else { + MemoryDeclaration::new_bitnum( + name, + *width, + dims.as_serializing_dim(), + false, + ) + } + } else { + MemoryDeclaration::new_bitnum( + name, + *width, + dims.as_serializing_dim(), + false, + ) + }; + + dump.push_memory( + declaration, + self.env.cells[cell_index] + .unwrap_primitive() + .dump_memory_state() + .unwrap(), + ) + } + CellPrototype::SingleWidth { + op: SingleWidthType::Reg, + width, + } => { + if dump_registers { + dump.push_reg( + name, + *width, + self.env.cells[cell_index] + .unwrap_primitive() + .dump_memory_state() + .unwrap(), + ) + } + } + _ => (), + } + } + + dump + } + + pub fn get_port_name( + &self, + port_idx: GlobalPortIdx, + parent: GlobalCellIdx, + ) -> &String { + let ledger = self.env.cells[parent].as_comp().unwrap(); + let port_offset = port_idx - &ledger.index_bases; + let def_idx = + self.ctx().secondary[ledger.comp_id].port_offset_map[port_offset]; + let name = self.ctx().secondary[def_idx].name; + self.ctx().lookup_name(name) + } + + pub fn format_port_value( + &self, + port_idx: GlobalPortIdx, + print_code: PrintCode, + ) -> String { + self.env.ports[port_idx].format_value(print_code) + } + + pub fn format_cell_ports( + &self, + cell_idx: GlobalCellIdx, + print_code: PrintCode, + name: Option<&str>, + ) -> String { + let mut buf = String::new(); + + if let Some(name_override) = name { + writeln!(buf, "{name_override}:").unwrap(); + } else { + writeln!(buf, "{}:", self.get_full_name(cell_idx)).unwrap(); + } + for (identifier, port_idx) in self.env.get_ports_from_cell(cell_idx) { + writeln!( + buf, + " {}: {}", + self.ctx().lookup_name(identifier), + self.format_port_value(port_idx, print_code) + ) + .unwrap(); + } + + buf + } + + pub fn format_cell_state( + &self, + cell_idx: GlobalCellIdx, + print_code: PrintCode, + name: Option<&str>, + ) -> Option { + let cell = self.env.cells[cell_idx].unwrap_primitive(); + let state = cell.serialize(Some(print_code)); + + let mut output = String::new(); + + if state.has_state() { + if let Some(name_override) = name { + write!(output, "{name_override}: ").unwrap(); + } else { + write!(output, "{}: ", self.get_full_name(cell_idx)).unwrap(); + } + + writeln!(output, "{state}").unwrap(); + + Some(output) + } else { + None + } + } +} + +pub trait GetFullName + Clone> { + fn get_full_name(&self, env: &Environment) -> String; +} + +impl + Clone, T: GetFullName> GetFullName for &T { + fn get_full_name(&self, env: &Environment) -> String { + (*self).get_full_name(env) + } +} + +impl + Clone> GetFullName for GlobalCellIdx { + fn get_full_name(&self, env: &Environment) -> String { + { + let mut parent_path = env.get_parent_path_from_cell(*self).unwrap(); + parent_path.push(*self); + + env.format_path(&parent_path) + } + } +} + +impl + Clone> GetFullName for GlobalPortIdx { + fn get_full_name(&self, env: &Environment) -> String { + if let Some((parent_path, _)) = env.get_parent_path_from_port(*self) { + let path_str = env.format_path(&parent_path); + + let immediate_parent = parent_path.last().unwrap(); + let comp = if env.cells[*immediate_parent].as_comp().is_some() { + *immediate_parent + } else { + // get second-to-last parent + parent_path[parent_path.len() - 2] + }; + + let ledger = env.cells[comp].as_comp().unwrap(); + + let local_offset = *self - &ledger.index_bases; + let comp_def = &env.ctx().secondary[ledger.comp_id]; + let port_def_idx = &comp_def.port_offset_map[local_offset]; + let port_def = &env.ctx().secondary[*port_def_idx]; + let name = env.ctx().lookup_name(port_def.name); + + format!("{path_str}.{name}") + } else { + // TODO griffin: this is a hack plz fix + "".to_string() + } + } +} + +impl + Clone> GetFullName for GlobalRefCellIdx { + fn get_full_name(&self, env: &Environment) -> String { + let parent_path = env.get_parent_path_from_cell(*self).unwrap(); + let path_str = env.format_path(&parent_path); + + let immediate_parent = parent_path.last().unwrap(); + let comp = if env.cells[*immediate_parent].as_comp().is_some() { + *immediate_parent + } else { + // get second-to-last parent + parent_path[parent_path.len() - 2] + }; + + let ledger = env.cells[comp].as_comp().unwrap(); + + let local_offset = *self - &ledger.index_bases; + let comp_def = &env.ctx().secondary[ledger.comp_id]; + let ref_cell_def_idx = &comp_def.ref_cell_offset_map[local_offset]; + let ref_cell_def = &env.ctx().secondary[*ref_cell_def_idx]; + let name = env.ctx().lookup_name(ref_cell_def.name); + + format!("{path_str}.{name}") + } +} diff --git a/tools/calyx-ffi/src/backend/cider.rs b/tools/calyx-ffi/src/backend/cider.rs index 503087d5c4..a1b98a19e9 100644 --- a/tools/calyx-ffi/src/backend/cider.rs +++ b/tools/calyx-ffi/src/backend/cider.rs @@ -4,28 +4,31 @@ use interp::{ flatten::{ flat_ir, structures::{ - context::Context as CiderContext, environment::Simulator, + context::Context as CiderContext, + environment::{BaseSimulator, Environment}, }, }, BitVecValue, }; use std::rc::Rc; +#[derive(Clone)] pub struct CiderFFIBackend { - simulator: Simulator>, + simulator: BaseSimulator>, } impl CiderFFIBackend { pub fn from(ctx: &Context, _name: &'static str) -> Self { // TODO(ethan, maybe griffin): use _name to select the component somehow let ctx = flat_ir::translate(ctx); - let simulator = Simulator::build_simulator( + let config = RuntimeConfig::default(); + let enviroment = Environment::new( Rc::new(ctx), - &None, - &None, - RuntimeConfig::default(), - ) - .expect("we live on the edge"); + None, + false, + config.get_logging_config(), + ); + let simulator = BaseSimulator::new(enviroment, config); Self { simulator } } @@ -49,7 +52,9 @@ impl CiderFFIBackend { } pub fn go(&mut self) { - self.simulator.run_program().expect("failed to run program"); + self.simulator + .run_program_inner(None) + .expect("failed to run program"); self.step(); // since griffin said so } } diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index 59247b73e4..f042f63faf 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -7,7 +7,11 @@ pub use interp; #[macro_export] macro_rules! declare_interface { - ($name:ident($($input:ident: $input_width:literal),*) -> ($($output:ident: $output_width:literal),*)) => { + ($name:ident($($input:ident: $input_width:literal),*) + -> ($($output:ident: $output_width:literal),*) + $(impl { + $(fn $fn:ident(&mut $self:ident $(, $arg:ident: $argty:ty)* $(,)?) $(-> $ret:ty)? $body:block)* + })? ) => { paste::paste! { pub trait $name: CalyxFFIComponent { $( @@ -20,6 +24,9 @@ macro_rules! declare_interface { fn $output(&self) -> u64; )* + $($( + fn $fn(&mut $self, $($arg: $argty),*) $(-> $ret)* {$body} + )*)* } } }; diff --git a/tools/calyx-ffi/tests/fifo.futil b/tools/calyx-ffi/tests/fifo.futil new file mode 100644 index 0000000000..6bc65ba106 --- /dev/null +++ b/tools/calyx-ffi/tests/fifo.futil @@ -0,0 +1,130 @@ +import "primitives/core.futil"; +import "primitives/memories/seq.futil"; +import "primitives/binary_operators.futil"; +component fifo(cmd: 1, value: 32) -> () { + cells { + mem = seq_mem_d1(32, 16, 4); + reg_1 = std_reg(4); + reg_2 = std_reg(4); + ref ans = std_reg(32); + ref err = std_reg(1); + reg_3 = std_reg(5); + eq_4 = std_eq(5); + reg_2_incr_1_5 = std_add(4); + reg_3_decr_1_6 = std_sub(5); + eq_7 = std_eq(5); + reg_1_incr_1_8 = std_add(4); + reg_3_incr_1_9 = std_add(5); + cmd_eq_0_10 = std_eq(1); + cmd_eq_1_11 = std_eq(1); + } + wires { + group raise_err { + err.in = 1'd1; + err.write_en = 1'd1; + raise_err[done] = err.done; + } + comb group eq_4_group { + eq_4.left = reg_3.out; + eq_4.right = 5'd0; + } + group read_payload_from_mem_pop { + mem.addr0 = reg_2.out; + mem.content_en = 1'd1; + ans.write_en = mem.done ? 1'd1; + ans.in = mem.done ? mem.read_data; + read_payload_from_mem_pop[done] = ans.done; + } + group reg_2_incr_1_5_group { + reg_2_incr_1_5.left = reg_2.out; + reg_2_incr_1_5.right = 4'd1; + reg_2.write_en = 1'd1; + reg_2.in = reg_2_incr_1_5.out; + reg_2_incr_1_5_group[done] = reg_2.done; + } + group reg_3_decr_1_6_group { + reg_3_decr_1_6.left = reg_3.out; + reg_3_decr_1_6.right = 5'd1; + reg_3.write_en = 1'd1; + reg_3.in = reg_3_decr_1_6.out; + reg_3_decr_1_6_group[done] = reg_3.done; + } + comb group eq_7_group { + eq_7.left = reg_3.out; + eq_7.right = 5'd16; + } + group write_payload_to_mem { + mem.addr0 = reg_1.out; + mem.write_en = 1'd1; + mem.write_data = value; + write_payload_to_mem[done] = mem.done; + mem.content_en = 1'd1; + } + group reg_1_incr_1_8_group { + reg_1_incr_1_8.left = reg_1.out; + reg_1_incr_1_8.right = 4'd1; + reg_1.write_en = 1'd1; + reg_1.in = reg_1_incr_1_8.out; + reg_1_incr_1_8_group[done] = reg_1.done; + } + group reg_3_incr_1_9_group { + reg_3_incr_1_9.left = reg_3.out; + reg_3_incr_1_9.right = 5'd1; + reg_3.write_en = 1'd1; + reg_3.in = reg_3_incr_1_9.out; + reg_3_incr_1_9_group[done] = reg_3.done; + } + cmd_eq_0_10.left = cmd; + cmd_eq_0_10.right = 1'd0; + cmd_eq_1_11.left = cmd; + cmd_eq_1_11.right = 1'd1; + } + control { + par { + if cmd_eq_0_10.out { + if eq_4.out with eq_4_group { + raise_err; + } else { + seq { + read_payload_from_mem_pop; + reg_2_incr_1_5_group; + reg_3_decr_1_6_group; + } + } + } + if cmd_eq_1_11.out { + if eq_7.out with eq_7_group { + raise_err; + } else { + seq { + write_payload_to_mem; + reg_1_incr_1_8_group; + reg_3_incr_1_9_group; + } + } + } + } + } +} +component main(cmd: 1, value: 32) -> (ans: 32, err: 1) { + cells { + ans_reg = std_reg(32); + err_reg = std_reg(1); + queue = fifo(); + } + wires { + ans = ans_reg.out; + err = err_reg.out; + group dummy { + ans_reg.in = ans_reg.out; + ans_reg.write_en = 1'b1; + dummy[done] = ans_reg.done; + } + } + control { + seq { + invoke queue[ans = ans_reg, err = err_reg](cmd = cmd, value = value)(); + dummy; + } + } +} diff --git a/tools/calyx-ffi/tests/fifo.rs b/tools/calyx-ffi/tests/fifo.rs new file mode 100644 index 0000000000..c3d1cc45e9 --- /dev/null +++ b/tools/calyx-ffi/tests/fifo.rs @@ -0,0 +1,96 @@ +use calyx_ffi::cider_ffi_backend; +use calyx_ffi::prelude::*; + +enum QueueCommand { + Pop = 0, + Push = 1, +} + +#[derive(PartialEq, Eq, Debug)] +enum QueueStatus { + Ok = 0, + Err = 1, +} + +calyx_ffi::declare_interface! { + Queue(cmd: 1, value: 32) -> (ans: 32, err: 1) impl { + fn status(&mut self) -> QueueStatus { + if self.err() == 0 { QueueStatus::Ok } else { QueueStatus::Err } + } + + fn assert_no_error(&mut self) { + assert_eq!(QueueStatus::Ok, self.status(), "queue underflowed or overflowed"); + } + + fn push(&mut self, value: u32) { + self.reset(); + self.set_cmd(QueueCommand::Push as u64); + self.set_value(value as u64); + self.go(); + self.assert_no_error(); + } + + fn pop(&mut self) -> u32 { + self.reset(); + self.set_cmd(QueueCommand::Pop as u64); + self.go(); + self.assert_no_error(); + self.ans() as u32 + } + } +} + +#[calyx_ffi( + src = "tests/fifo.futil", + comp = "main", + backend = cider_ffi_backend, + derive = [ + Queue(cmd: 1, value: 32) -> (ans: 32, err: 1) + ] +)] +struct Fifo; + +// struct State { +// inner: T, +// } +// +// struct EquationalStrategy { +// leaves: Vec, +// builders: +// } +// +// impl Strategy for EquationalStrategy { +// fn generate(&mut self) -> T { +// self.leaves.ra +// } +// } +// +// trait Strategy { +// fn generate(&mut self) -> T; +// +// fn evolve(&mut self, value: &mut T) { +// *value = self.generate(); +// } +// } +// +// struct FifoStrategy: Strategy { +// +// } + +#[cfg(test)] +#[calyx_ffi_tests] +mod tests { + use super::*; + + #[calyx_ffi_test] + fn test_fifo(fifo: &mut Fifo) { + println!("testing fifo"); + + fifo.push(1); + fifo.push(2); + assert_eq!(1, fifo.pop()); + fifo.push(3); + assert_eq!(2, fifo.pop()); + assert_eq!(3, fifo.pop()); + } +} diff --git a/tools/calyx-ffi/tests/stack.futil b/tools/calyx-ffi/tests/stack.futil new file mode 100644 index 0000000000..1eb9029b8a --- /dev/null +++ b/tools/calyx-ffi/tests/stack.futil @@ -0,0 +1,57 @@ +import "primitives/core.futil"; +import "primitives/memories/seq.futil"; +import "primitives/binary_operators.futil"; + +component main(cmd: 1, value: 32) -> (out: 32) { + cells { + store = seq_mem_d1(32, 16, 4); + next = std_reg( 4); + incr = std_add( 4); + decr = std_sub( 4); + last = std_reg(32); + } + wires { + out = last.out; + + group write_at_next { + store.addr0 = next.out; + store.write_data = value; + store.write_en = 1'b1; + write_at_next[done] = store.done; + } + group read_from_next { + store.addr0 = next.out; + last.in = store.read_data; + last.write_en = 1'b1; + read_from_next[done] = last.done; + } + group increment_next { + incr.left = next.out; + incr.right = 4'd1; + next.in = incr.out; + next.write_en = 1'b1; + increment_next[done] = next.done; + } + group decrement_next { + decr.left = next.out; + decr.right = 4'd1; + next.in = decr.out; + next.write_en = 1'b1; + decrement_next[done] = next.done; + } + } + control { + if cmd { + seq { + write_at_next; + increment_next; + } + } else { + seq { + decrement_next; + read_from_next; + } + } + } +} + diff --git a/tools/calyx-ffi/tests/stack.rs b/tools/calyx-ffi/tests/stack.rs new file mode 100644 index 0000000000..a6301c7a34 --- /dev/null +++ b/tools/calyx-ffi/tests/stack.rs @@ -0,0 +1,53 @@ +use calyx_ffi::cider_ffi_backend; +use calyx_ffi::prelude::*; + +enum StackCommand { + Push = 0, + Pop = 1, +} + +calyx_ffi::declare_interface! { + Stack(cmd: 1, value: 32) -> (out: 32) impl { + fn push(&mut self, value: u32) { + self.reset(); + self.set_cmd(StackCommand::Push as u64); + self.set_value(value as u64); + self.go(); + } + + fn pop(&mut self) -> u32 { + self.reset(); + self.set_cmd(StackCommand::Pop as u64); + self.go(); + self.out() as u32 + } + } +} + +#[calyx_ffi( + src = "tests/stack.futil", + comp = "main", + backend = cider_ffi_backend, + derive = [ + Stack(cmd: 1, value: 32) -> (out: 32) + ] +)] +struct ReallyBadStack; + +#[cfg(test)] +#[calyx_ffi_tests] +mod tests { + use super::*; + + #[calyx_ffi_test] + fn test_stack(stack: &mut ReallyBadStack) { + println!("testing fifo"); + + stack.push(1); + stack.push(2); + assert_eq!(2, stack.pop()); + // fifo.push(3); + // assert_eq!(3, fifo.pop()); + assert_eq!(1, stack.pop()); + } +} From a8db1685adc64d6c80b0fc81dfb09f454a002b26 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:06:21 -0500 Subject: [PATCH 37/41] clippy --- interp/src/flatten/primitives/prim_trait.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/interp/src/flatten/primitives/prim_trait.rs b/interp/src/flatten/primitives/prim_trait.rs index 83a2f4dde5..e6c90fb4fa 100644 --- a/interp/src/flatten/primitives/prim_trait.rs +++ b/interp/src/flatten/primitives/prim_trait.rs @@ -1,5 +1,3 @@ -use std::mem; - use crate::{ errors::RuntimeResult, flatten::{ From 8119c22a6ebbb9d07ef9f2da5d132c1491c6aba2 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 17:15:32 -0500 Subject: [PATCH 38/41] Working sample tests --- tools/calyx-ffi/tests/fifo.rs | 27 ------------------------- tools/calyx-ffi/tests/stack.futil | 33 +++++++++++++++++++++---------- tools/calyx-ffi/tests/stack.rs | 22 ++++++++++++++------- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/tools/calyx-ffi/tests/fifo.rs b/tools/calyx-ffi/tests/fifo.rs index c3d1cc45e9..76ffd1159c 100644 --- a/tools/calyx-ffi/tests/fifo.rs +++ b/tools/calyx-ffi/tests/fifo.rs @@ -50,33 +50,6 @@ calyx_ffi::declare_interface! { )] struct Fifo; -// struct State { -// inner: T, -// } -// -// struct EquationalStrategy { -// leaves: Vec, -// builders: -// } -// -// impl Strategy for EquationalStrategy { -// fn generate(&mut self) -> T { -// self.leaves.ra -// } -// } -// -// trait Strategy { -// fn generate(&mut self) -> T; -// -// fn evolve(&mut self, value: &mut T) { -// *value = self.generate(); -// } -// } -// -// struct FifoStrategy: Strategy { -// -// } - #[cfg(test)] #[calyx_ffi_tests] mod tests { diff --git a/tools/calyx-ffi/tests/stack.futil b/tools/calyx-ffi/tests/stack.futil index 1eb9029b8a..11575c4c0c 100644 --- a/tools/calyx-ffi/tests/stack.futil +++ b/tools/calyx-ffi/tests/stack.futil @@ -2,27 +2,36 @@ import "primitives/core.futil"; import "primitives/memories/seq.futil"; import "primitives/binary_operators.futil"; -component main(cmd: 1, value: 32) -> (out: 32) { +component main(cmd: 1, value: 32) -> (out: 32, length: 4) { cells { store = seq_mem_d1(32, 16, 4); next = std_reg( 4); incr = std_add( 4); decr = std_sub( 4); last = std_reg(32); + test = std_eq(1); } wires { out = last.out; + length = next.out; + + comb group check_cmd { + test.left = cmd; + test.right = 1'b0; + } group write_at_next { store.addr0 = next.out; store.write_data = value; store.write_en = 1'b1; + store.content_en = 1'b1; write_at_next[done] = store.done; } group read_from_next { store.addr0 = next.out; + store.content_en = 1'b1; last.in = store.read_data; - last.write_en = 1'b1; + last.write_en = store.done; read_from_next[done] = last.done; } group increment_next { @@ -41,15 +50,19 @@ component main(cmd: 1, value: 32) -> (out: 32) { } } control { - if cmd { - seq { - write_at_next; - increment_next; + // if-else is buggy in cider2 + par { + if test.out with check_cmd { + seq { + write_at_next; + increment_next; + } } - } else { - seq { - decrement_next; - read_from_next; + if cmd { + seq { + decrement_next; + read_from_next; + } } } } diff --git a/tools/calyx-ffi/tests/stack.rs b/tools/calyx-ffi/tests/stack.rs index a6301c7a34..8009f51430 100644 --- a/tools/calyx-ffi/tests/stack.rs +++ b/tools/calyx-ffi/tests/stack.rs @@ -6,19 +6,27 @@ enum StackCommand { Pop = 1, } +const STACK_CAPACITY: u64 = 16; + calyx_ffi::declare_interface! { - Stack(cmd: 1, value: 32) -> (out: 32) impl { + Stack(cmd: 1, value: 32) -> (out: 32, length: 4) impl { fn push(&mut self, value: u32) { - self.reset(); + assert!(self.length() < STACK_CAPACITY, "tried to push when length={}", STACK_CAPACITY); + println!("stack has length {} before push", self.length()); + let old_length = self.length(); self.set_cmd(StackCommand::Push as u64); self.set_value(value as u64); self.go(); + assert_eq!(old_length + 1, self.length(), "stack length should increase by 1 on push"); } fn pop(&mut self) -> u32 { - self.reset(); + assert!(self.length() > 0, "tried to pop when stack empty"); + println!("stack has length {} before pop", self.length()); + let old_length = self.length(); self.set_cmd(StackCommand::Pop as u64); self.go(); + assert_eq!(old_length - 1, self.length(), "stack length should decrease by 1 on pop"); self.out() as u32 } } @@ -29,7 +37,7 @@ calyx_ffi::declare_interface! { comp = "main", backend = cider_ffi_backend, derive = [ - Stack(cmd: 1, value: 32) -> (out: 32) + Stack(cmd: 1, value: 32) -> (out: 32, length: 4) ] )] struct ReallyBadStack; @@ -41,13 +49,13 @@ mod tests { #[calyx_ffi_test] fn test_stack(stack: &mut ReallyBadStack) { - println!("testing fifo"); + println!("testing stack"); stack.push(1); stack.push(2); assert_eq!(2, stack.pop()); - // fifo.push(3); - // assert_eq!(3, fifo.pop()); + stack.push(3); + assert_eq!(3, stack.pop()); assert_eq!(1, stack.pop()); } } From 911e2deb9655749b43c9dec681619346e4f7bfdd Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 17:34:54 -0500 Subject: [PATCH 39/41] Fix macro hygiene --- tools/calyx-ffi/src/prelude.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index f042f63faf..d1f95663cd 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -4,6 +4,7 @@ pub use super::{ pub use calyx_ffi_macro::{calyx_ffi, calyx_ffi_test, calyx_ffi_tests}; pub use calyx_ir; pub use interp; +pub use paste; #[macro_export] macro_rules! declare_interface { @@ -12,7 +13,7 @@ macro_rules! declare_interface { $(impl { $(fn $fn:ident(&mut $self:ident $(, $arg:ident: $argty:ty)* $(,)?) $(-> $ret:ty)? $body:block)* })? ) => { - paste::paste! { + calyx_ffi::prelude::paste::paste! { pub trait $name: CalyxFFIComponent { $( fn [<$input _bits>](&mut self) -> &mut calyx_ffi::Value<$input_width>; From 48f2cc6c6b2c717adcde1719b383c32f3a73bae8 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 19:21:23 -0500 Subject: [PATCH 40/41] Derive clone --- tools/calyx-ffi-macro/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/calyx-ffi-macro/src/lib.rs b/tools/calyx-ffi-macro/src/lib.rs index ffa13d6f25..a4c7c76948 100644 --- a/tools/calyx-ffi-macro/src/lib.rs +++ b/tools/calyx-ffi-macro/src/lib.rs @@ -53,12 +53,15 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { let mut getters = vec![]; let mut setters = vec![]; let mut width_getters = vec![]; + let mut port_names = vec![]; for port in comp.signature.borrow().ports() { let port_name_str = port.borrow().name.to_string(); let port_name = syn::parse_str::(&port_name_str) .expect("failed to turn port name into identifier"); + port_names.push(port_name.clone()); + let port_width = port.borrow().width; let width_getter = format_ident!("{}_width", port_name); width_getters.push(quote! { @@ -133,6 +136,15 @@ pub fn calyx_ffi(attrs: TokenStream, item: TokenStream) -> TokenStream { } } + impl std::clone::Clone for #name { + fn clone(&self) -> Self { + Self { + #(#port_names: self.#port_names.clone()),*, + user_data: unsafe { std::mem::MaybeUninit::new(self.user_data.assume_init_ref().clone()) } + } + } + } + impl CalyxFFIComponent for #name { fn path(&self) -> &'static str { #comp_path From 454b29b48097e80e6ee06e76bd7efc80bade4686 Mon Sep 17 00:00:00 2001 From: Ethan Uppal <113849268+ethanuppal@users.noreply.github.com> Date: Sun, 10 Nov 2024 22:47:54 -0500 Subject: [PATCH 41/41] Split mut and ref impl blocks for interfaces --- tools/calyx-ffi/src/prelude.rs | 9 ++++++++- tools/calyx-ffi/tests/fifo.rs | 4 ++-- tools/calyx-ffi/tests/stack.rs | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/calyx-ffi/src/prelude.rs b/tools/calyx-ffi/src/prelude.rs index d1f95663cd..425a29c2ec 100644 --- a/tools/calyx-ffi/src/prelude.rs +++ b/tools/calyx-ffi/src/prelude.rs @@ -11,8 +11,12 @@ macro_rules! declare_interface { ($name:ident($($input:ident: $input_width:literal),*) -> ($($output:ident: $output_width:literal),*) $(impl { + $(fn $fn2:ident(& $self2:ident $(, $arg2:ident: $argty2:ty)* $(,)?) $(-> $ret2:ty)? $body2:block)* + })? + $(mut impl { $(fn $fn:ident(&mut $self:ident $(, $arg:ident: $argty:ty)* $(,)?) $(-> $ret:ty)? $body:block)* - })? ) => { + })? + ) => { calyx_ffi::prelude::paste::paste! { pub trait $name: CalyxFFIComponent { $( @@ -25,6 +29,9 @@ macro_rules! declare_interface { fn $output(&self) -> u64; )* + $($( + fn $fn2(&$self2, $($arg2: $argty2),*) $(-> $ret2)* {$body2} + )*)* $($( fn $fn(&mut $self, $($arg: $argty),*) $(-> $ret)* {$body} )*)* diff --git a/tools/calyx-ffi/tests/fifo.rs b/tools/calyx-ffi/tests/fifo.rs index 76ffd1159c..f1199f5406 100644 --- a/tools/calyx-ffi/tests/fifo.rs +++ b/tools/calyx-ffi/tests/fifo.rs @@ -14,10 +14,10 @@ enum QueueStatus { calyx_ffi::declare_interface! { Queue(cmd: 1, value: 32) -> (ans: 32, err: 1) impl { - fn status(&mut self) -> QueueStatus { + fn status(&self) -> QueueStatus { if self.err() == 0 { QueueStatus::Ok } else { QueueStatus::Err } } - + } mut impl { fn assert_no_error(&mut self) { assert_eq!(QueueStatus::Ok, self.status(), "queue underflowed or overflowed"); } diff --git a/tools/calyx-ffi/tests/stack.rs b/tools/calyx-ffi/tests/stack.rs index 8009f51430..8e5b0d60a8 100644 --- a/tools/calyx-ffi/tests/stack.rs +++ b/tools/calyx-ffi/tests/stack.rs @@ -9,8 +9,8 @@ enum StackCommand { const STACK_CAPACITY: u64 = 16; calyx_ffi::declare_interface! { - Stack(cmd: 1, value: 32) -> (out: 32, length: 4) impl { - fn push(&mut self, value: u32) { + Stack(cmd: 1, value: 32) -> (out: 32, length: 4) mut impl { + fn push(&mut self, value: u32,) { assert!(self.length() < STACK_CAPACITY, "tried to push when length={}", STACK_CAPACITY); println!("stack has length {} before push", self.length()); let old_length = self.length();