From 914a6111fc29ce9bd0a0263a8c717d5e8817dbc8 Mon Sep 17 00:00:00 2001 From: Samuel Thomas Date: Wed, 19 Jun 2024 12:46:32 -0500 Subject: [PATCH] [fud2] Rhai Testing (#2126) * [fud2] Make plugins optional Very minor fix: #2078 added plugins, controlled by a `plugins` key in `fud2.toml`. However, it inadvertently made this option required, i.e., fud2 would crash if this option was not present. Now it's optional! So we just silently proceed with no plugins if none are configured. * Fix a doc typo * Start refactoring plugin loading * Start refactoring builder functions * Refactor the rest of the functions * Gather up a `register` function * Use a fresh engine for each plugin?? Maybe this is wasteful, but it simplifies things a lot. * Run one script at a time * Refector script contexts * Refactor runner struct * Further beef up the runner * Further simplify interface * Fix a borrow that lasts too long * Fix a method name Clippy was thrown for a loop because of the `to_*` name. * Move plugin loading to DriverBuilder? Now the top-level thing is in the builder itself... no need for an extra trait? Not sure this is a good idea. * translate a bulk of operations into rhai * use single engine to module resolution caching, enabling imports * switched all plugins to use import system * include plugins in debug build * we can now load scripts embedded in the binary * if a module fails once, don't retry it * sort states and operations in list mode * migrate entirely to plugins * gate migration behind a feature * move sorting to when we run files * rename plugins to scripts internally * refactored to have more things live in the resolver * rename plugins/ to scripts/ * improve error reporting for errors in submodules * slightly improved error messages * remove comments from snapshot testing + add plugin testing (feature gated) * remove comment * plugins should only load each setup_fn once * test every operation in fud2 with insta * fix verilator.rhai to use correct testbench for refmem * use .cloned() instead of .map(|x| x.clone()) * refactor + construct plan directly to exactly specify the op * fix axi.rhai to match axi-wrapped in lib.rs * export setup fns for verilator.rhai and icarus.rhai * update snapshots for changes in ops * remove old snapshot * updated rhai scripts to match changes to main * some Rhai scripting documentation * disable migrate_to_scripts for now * update axi.rhai + Rhai API to match Rust API * update snapshot * fix typo --------- Co-authored-by: Adrian Sampson --- Cargo.lock | 1 + docs/SUMMARY.md | 3 +- docs/running-calyx/fud2.md | 160 +----------- docs/running-calyx/fud2/index.md | 159 ++++++++++++ docs/running-calyx/fud2/scripts.md | 126 ++++++++++ fud2/Cargo.toml | 3 +- fud2/fud-core/src/script/exec_scripts.rs | 38 +++ fud2/fud-core/src/script/plugin.rs | 41 +++- fud2/fud-core/src/script/report.rs | 64 +++-- fud2/fud-core/src/script/resolver.rs | 40 ++- fud2/scripts/axi.rhai | 43 +++- fud2/scripts/calyx.rhai | 6 + fud2/scripts/cider.rhai | 33 ++- fud2/scripts/cocotb-axi.rhai | 55 +++++ fud2/scripts/firrtl.rhai | 3 +- fud2/scripts/icarus.rhai | 1 + fud2/scripts/verilator.rhai | 6 +- fud2/scripts/xilinx.rhai | 2 +- fud2/src/lib.rs | 2 +- fud2/tests/snapshots/tests__list_ops.snap | 145 +++++++++++ fud2/tests/snapshots/tests__list_states.snap | 24 ++ ...ebug.snap => tests__test@calyx_debug.snap} | 7 +- ...ts__test@calyx_firrtl_verilog-refmem.snap} | 6 +- ...snap => tests__test@calyx_icarus_dat.snap} | 7 +- ...snap => tests__test@calyx_icarus_vcd.snap} | 7 +- ...snap => tests__test@calyx_interp_dat.snap} | 7 +- ...p => tests__test@calyx_verilator_dat.snap} | 7 +- ...p => tests__test@calyx_verilator_vcd.snap} | 7 +- ...og.snap => tests__test@calyx_verilog.snap} | 4 +- ...p => tests__test@calyx_xrt-trace_vcd.snap} | 8 +- ...at.snap => tests__test@calyx_xrt_dat.snap} | 8 +- ...lyx.snap => tests__test@dahlia_calyx.snap} | 4 +- ...calyx.snap => tests__test@mrxl_calyx.snap} | 4 +- .../tests__test@plan_axi-wrapped.snap | 40 +++ .../tests__test@plan_calyx-noverify.snap | 24 ++ .../tests__test@plan_calyx-to-cider.snap | 35 +++ .../tests__test@plan_calyx-to-cocotb-axi.snap | 35 +++ .../tests__test@plan_calyx-to-firrtl.snap | 40 +++ .../tests__test@plan_calyx-to-verilog.snap | 23 ++ .../tests__test@plan_calyx-to-yxi.snap | 26 ++ .../snapshots/tests__test@plan_cider.snap | 58 +++++ .../tests__test@plan_dahlia-to-calyx.snap | 15 ++ .../snapshots/tests__test@plan_debug.snap | 59 +++++ .../tests__test@plan_firrtl-noverify.snap | 19 ++ ...s__test@plan_firrtl-with-primitives-2.snap | 18 ++ ...@plan_firrtl-with-primitives-noverify.snap | 18 ++ ...sts__test@plan_firrtl-with-primitives.snap | 48 ++++ .../snapshots/tests__test@plan_firrtl.snap | 19 ++ .../tests__test@plan_icarus-refmem.snap | 30 +++ .../snapshots/tests__test@plan_icarus.snap | 32 +++ .../snapshots/tests__test@plan_interp.snap | 60 +++++ .../tests__test@plan_mrxl-to-calyx.snap | 15 ++ .../tests__test@plan_primitive-uses.snap | 23 ++ .../snapshots/tests__test@plan_simulate.snap | 27 ++ .../snapshots/tests__test@plan_trace.snap | 26 ++ .../tests__test@plan_verilator-refmem.snap | 46 ++++ .../snapshots/tests__test@plan_verilator.snap | 37 +++ .../snapshots/tests__test@plan_xclbin.snap | 25 ++ fud2/tests/snapshots/tests__test@plan_xo.snap | 43 ++++ .../snapshots/tests__test@plan_xrt-trace.snap | 56 +++++ .../tests/snapshots/tests__test@plan_xrt.snap | 56 +++++ fud2/tests/tests.rs | 230 +++++++++++++----- 62 files changed, 1865 insertions(+), 349 deletions(-) create mode 100644 docs/running-calyx/fud2/index.md create mode 100644 docs/running-calyx/fud2/scripts.md create mode 100644 fud2/scripts/cocotb-axi.rhai create mode 100644 fud2/tests/snapshots/tests__list_ops.snap create mode 100644 fud2/tests/snapshots/tests__list_states.snap rename fud2/tests/snapshots/{tests__emit@calyx_debug.snap => tests__test@calyx_debug.snap} (93%) rename fud2/tests/snapshots/{tests__emit@calyx_firrtl_verilog-refmem.snap => tests__test@calyx_firrtl_verilog-refmem.snap} (91%) rename fud2/tests/snapshots/{tests__emit@calyx_icarus_dat.snap => tests__test@calyx_icarus_dat.snap} (90%) rename fud2/tests/snapshots/{tests__emit@calyx_icarus_vcd.snap => tests__test@calyx_icarus_vcd.snap} (90%) rename fud2/tests/snapshots/{tests__emit@calyx_interp_dat.snap => tests__test@calyx_interp_dat.snap} (92%) rename fud2/tests/snapshots/{tests__emit@calyx_verilator_dat.snap => tests__test@calyx_verilator_dat.snap} (91%) rename fud2/tests/snapshots/{tests__emit@calyx_verilator_vcd.snap => tests__test@calyx_verilator_vcd.snap} (91%) rename fud2/tests/snapshots/{tests__emit@calyx_verilog.snap => tests__test@calyx_verilog.snap} (88%) rename fud2/tests/snapshots/{tests__emit@calyx_xrt-trace_vcd.snap => tests__test@calyx_xrt-trace_vcd.snap} (93%) rename fud2/tests/snapshots/{tests__emit@calyx_xrt_dat.snap => tests__test@calyx_xrt_dat.snap} (93%) rename fud2/tests/snapshots/{tests__emit@dahlia_calyx.snap => tests__test@dahlia_calyx.snap} (80%) rename fud2/tests/snapshots/{tests__emit@mrxl_calyx.snap => tests__test@mrxl_calyx.snap} (78%) create mode 100644 fud2/tests/snapshots/tests__test@plan_axi-wrapped.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_calyx-noverify.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_calyx-to-cider.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_calyx-to-cocotb-axi.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_calyx-to-firrtl.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_calyx-to-verilog.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_calyx-to-yxi.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_cider.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_dahlia-to-calyx.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_debug.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_firrtl-noverify.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-2.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-noverify.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_firrtl.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_icarus-refmem.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_icarus.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_interp.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_mrxl-to-calyx.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_primitive-uses.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_simulate.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_trace.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_verilator-refmem.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_verilator.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_xclbin.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_xo.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_xrt-trace.snap create mode 100644 fud2/tests/snapshots/tests__test@plan_xrt.snap diff --git a/Cargo.lock b/Cargo.lock index 331f2dd7e7..98fed1476a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1187,6 +1187,7 @@ dependencies = [ "fud-core", "include_dir", "insta", + "itertools 0.11.0", "manifest-dir-macros", ] diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 07bb1b96a4..bd19291978 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -22,7 +22,8 @@ - [Multiple Paths](./running-calyx/fud/multiple-paths.md) - [CIRCT](./running-calyx/fud/circt.md) - [Resource Estimation](./running-calyx/fud/resource-estimation.md) -- [fud2: Experimental Driver](./running-calyx/fud2.md) +- [fud2: Experimental Driver](./running-calyx/fud2/index.md) + - [Scripting](./running-calyx/fud2/scripts.md) - [Interfacing with Calyx RTL](./running-calyx/interfacing.md) - [The Calyx Interpreter](./running-calyx/interpreter.md) diff --git a/docs/running-calyx/fud2.md b/docs/running-calyx/fud2.md index 6ae9088bff..893b283c17 100644 --- a/docs/running-calyx/fud2.md +++ b/docs/running-calyx/fud2.md @@ -1,159 +1 @@ -# fud2: An Experimental Successor to fud - -[fud][] is the compiler driver tool for orchestrating the Calyx ecosystem. -fud2 is an experiment in building a new driver that works like fud that adds some fundamental new capabilities and resolves some underlying problems. - -"Original" fud is still the right tool for almost all jobs; fud2 is in an experimental phase and does not support everything fud can do. -Someday, fud2 may supplant fud, but it needs more work before it is ready to do that. -Until then, fud remains your first choice for all your build-related needs. - -[fud]: ./fud/index.md - -## Set Up - -fud2 is a Rust tool, so you can build it along with everything else in this monorepo with `cargo build`. -You might then want to do something like ``ln -s `pwd`/target/debug/fud2 ~/.local/bin`` for easy access to the `fud2` binary. - -fud2 depends on [Ninja][]. -Install it using your OS package manager or by downloading a binary. - -### Configuration - -Run the following command to edit `fud2`'s configuration file (usually `~/.config/fud2.toml`): - - $ fud2 edit-config - -Add these lines: - -```toml -[calyx] -base = "" -``` - -Now you're ready to use fud2. - -[ninja]: https://ninja-build.org - -## General Use - -You can see complete command-line documentation with `fud2 --help`. -But generally, you want to do something like this: - - $ fud2 -o - -For example, use this to compile a Calyx program to Verilog: - - $ fud2 foo.futil -o bar.sv - -fud2 tries to automatically guess the input and output formats using filename extensions. -If that doesn't work, you can choose for it with `--from ` and `--to `; -for example, this is a more explicit version of the above: - - $ fud2 foo.futil -o bar.sv --from calyx --to verilog - -You can also omit the input and output filenames to instead use stdin and stdout. -In that case, `--from` and `--to` respectively are required. -So here's yet another way to do the same thing: - - $ fud2 --from calyx --to verilog < foo.futil > bar.sv - -This is handy if you just want to print the result of a build to the console: - - $ fud2 foo.futil --to verilog - -Some operations use other configuration options, which can come from either your `fud2.toml` or the command line. -Use `--set key=value` to override any such option. - -## Advanced Options - -Use `fud2 --help` for an overview of the command-line interface. -Here are some options you might need: - -* By default, fud2 runs the build in a directory called `.fud2` within the working directory. It automatically deletes this directory when the build is done. - * It can be useful to keep this build directory around for debugging or as a "cache" for future builds. Use `--keep` to prevent fud2 from deleting the build directory. - * You can also tell fud2 to use a different build directory with `--dir`. If you give it an existing directory, it will never be deleted, even without `--keep`. (Only "fresh" build directories are automatically cleaned up.) -* If you don't like the operation path that fud2 selected for your build, you can control it with `--through `. fud2 will search the operation graph for a path that contains that op. You can provide this option multiple times; fud2 will look for paths that contain *all* these operations, in order. -* You can choose one of several modes with `-m `: - * `run`: Actually execute a build. The default. - * `gen`: Generate the Ninja build file in the build directory, but don't actually run the build. The default `run` mode is therefore approximately like doing `fud2 -m gen && ninja -C .fud2`. - * `emit`: Just print the Ninja build file to stdout. The `gen` mode is therefore approximately `fud2 -m emit > .fud2/build.ninja`. - * `plan`: Print a brief description of the plan, i.e., the sequence of operations that the build would run. - * `dot`: Print a [GraphViz][] depiction of the plan. Try `fud2 -m dot | dot -Tpdf > graph.pdf` and take a look. - -There are also some subcommands for doing things other than building stuff: - -* `fud2 edit-config`: Open the fud2 configuration file in `$EDITOR`. -* `fud2 list`: Print out all the available states and operations. -* `fud2 get-rsrc FILE`: Fetch a *resource file* and place it in the working directory. You typically do not need to use this interactively; it is used during builds to obtain files included with fud2 that are necessary for a given build. - -[graphviz]: https://graphviz.org - -## The Design of fud2 - -
- -This section is about the *implementation* of fud2; it is only relevant if you want to work on it yourself. -No need to read any farther if all you want is to *use* fud2. - -
- -### fud2 is a Command Orchestrator - -fud2 consists of two pieces, which are two separate Rust crates: - -* FudCore (the `fud-core` crate): is a *generic compiler driver* library. This library is not specific to Calyx and could hypothetically be used to build a fud2-like driver for any compiler ecosystem. Clients of the `fud-core` library work by constructing a `Driver` object that encapsulates a set of *states* and *operations* that define the driver's behavior. -* fud2 itself is a program that uses the FudCore library. All of the Calyx-specific logic lives in `fud2`. For the most part, all of the code in the `fud2` crate consists of declaring a bunch of states and operations. The `main` function does little more than dispatch to the resulting `Driver` object's generic command-line interface. - -The central design philosophy of FudCore (and by extension, fud2 itself) is that its sole job is to orchestrate external functionality. -All that functionality must be available as separate tools that can be invoked via the command line. -This is an important goal because it means the driver has a clear, discrete goal: *all it does* is decide on a list of commands to execute to perform a build. -All the "interesting work" must be delegated to separate tools outside of the driver. -This philosophy has both advantages and disadvantages: - -* On the positive side, it forces all the interesting logic to be invokable via a command that you, the user, can run equally well yourself. So if something is going wrong, there is *always* a command line you can copy and paste into your terminal to reproduce the problem at that particular step. It also means that the input and output of every step must be written to files in the filesystem, so you can easily inspect the intermediate state between every command. This file-based operation also means that fud2 builds are parallel and incremental by default. -* On the other hand, requiring everything to be separate commands means that fud2 has a complicated dependency story. It is not a monolith: to get meaningful work done, you currently have to install a bunch of Python components (among other things) so fud2 can invoke them. (We hope to mitigate the logistical pain this incurs over time, but we're not there yet.) Also, writing everything to a file in between each step comes at a performance cost. Someday, it may be a performance bottleneck that two steps in a build cannot simply exchange their data directly, through memory, and must serialize everything to disk first. (This has not been a problem in practice yet.) - -If you want to extend fud2 to do something new, the consequence is that you first need to come up with a sequence of commands that do that thing. -If necessary, you may find that you need to create new executables to do some minor glue tasks that would otherwise be implicit. -Then "all you need to do" is teach fud2 to execute those commands. - -### States, Operations, and Setups - -You can think of a FudCore driver as a graph, where the vertices are *states* and the edges are *operations*. -(In fact, this is literally the graph you can visualize with `-m dot`.) -Any build is a transformation from one state to another, traversing a path through this graph. -The operations (edges) along this path are the commands that must be executed to transform a file from the initial state to the final state. - -To make fud2 do something new, you probably want to add one or more operations, and you may need to add new states. -Aside from declaring the source and destination states, -operations generate chunks of [Ninja][] code. -So to implement an operation, you write a Rust function with this signature: - - fn build(emitter: &mut Emitter, input: &str, output: &str) - -Here, `emitter` is a wrapper around an output stream with a bunch of utility functions for printing out lines of Ninja code. -`input` and `output` are filenames. -So your job in this function is to print (at least) a Ninja `build` command that produces `output` as a target and uses `input` as a dependency. -For example, the Calyx-to-Verilog compiler operation might emit this chunk of Ninja code: - - build bar.sv: calyx foo.futil - backend = verilog - -when the `input` argument above is `"foo.futil"` and the `output` is `"bar.sv"`. -(The FudCore library will conjure these filenames for you; your job in this operation is just to use them as is.) - -Notice here that the generated Ninja chunk is using a build rule called `calyx`. -This also needs to be defined. -To set up things like variables and build rules that operations can use, FudCore has a separate concept called *setups*. -A setup is a function that generates some Ninja code that might be shared among multiple operations (or multiple instances of the same operation). -For example, our setup for Calyx compilation generates code like this: - - calyx-base = /path/to/calyx - calyx-exe = $calyx-base/target/debug/calyx - rule calyx - command = $calyx-exe -l $calyx-base -b $backend $args $in > $out - -That is, it defines two Ninja variables and one Ninja rule—the one that our build command above uses. -We *could* have designed FudCore without a separation between setups and operations, so this rule would get declared right next to the `build` command above. -But that would end up duplicating a lot of setup code that really only needs to appear once. -So that's why setups exist: to share a single stanza of Ninja scaffolding code between multiple operations. +# fud2: Experimental Driver diff --git a/docs/running-calyx/fud2/index.md b/docs/running-calyx/fud2/index.md new file mode 100644 index 0000000000..6ae9088bff --- /dev/null +++ b/docs/running-calyx/fud2/index.md @@ -0,0 +1,159 @@ +# fud2: An Experimental Successor to fud + +[fud][] is the compiler driver tool for orchestrating the Calyx ecosystem. +fud2 is an experiment in building a new driver that works like fud that adds some fundamental new capabilities and resolves some underlying problems. + +"Original" fud is still the right tool for almost all jobs; fud2 is in an experimental phase and does not support everything fud can do. +Someday, fud2 may supplant fud, but it needs more work before it is ready to do that. +Until then, fud remains your first choice for all your build-related needs. + +[fud]: ./fud/index.md + +## Set Up + +fud2 is a Rust tool, so you can build it along with everything else in this monorepo with `cargo build`. +You might then want to do something like ``ln -s `pwd`/target/debug/fud2 ~/.local/bin`` for easy access to the `fud2` binary. + +fud2 depends on [Ninja][]. +Install it using your OS package manager or by downloading a binary. + +### Configuration + +Run the following command to edit `fud2`'s configuration file (usually `~/.config/fud2.toml`): + + $ fud2 edit-config + +Add these lines: + +```toml +[calyx] +base = "" +``` + +Now you're ready to use fud2. + +[ninja]: https://ninja-build.org + +## General Use + +You can see complete command-line documentation with `fud2 --help`. +But generally, you want to do something like this: + + $ fud2 -o + +For example, use this to compile a Calyx program to Verilog: + + $ fud2 foo.futil -o bar.sv + +fud2 tries to automatically guess the input and output formats using filename extensions. +If that doesn't work, you can choose for it with `--from ` and `--to `; +for example, this is a more explicit version of the above: + + $ fud2 foo.futil -o bar.sv --from calyx --to verilog + +You can also omit the input and output filenames to instead use stdin and stdout. +In that case, `--from` and `--to` respectively are required. +So here's yet another way to do the same thing: + + $ fud2 --from calyx --to verilog < foo.futil > bar.sv + +This is handy if you just want to print the result of a build to the console: + + $ fud2 foo.futil --to verilog + +Some operations use other configuration options, which can come from either your `fud2.toml` or the command line. +Use `--set key=value` to override any such option. + +## Advanced Options + +Use `fud2 --help` for an overview of the command-line interface. +Here are some options you might need: + +* By default, fud2 runs the build in a directory called `.fud2` within the working directory. It automatically deletes this directory when the build is done. + * It can be useful to keep this build directory around for debugging or as a "cache" for future builds. Use `--keep` to prevent fud2 from deleting the build directory. + * You can also tell fud2 to use a different build directory with `--dir`. If you give it an existing directory, it will never be deleted, even without `--keep`. (Only "fresh" build directories are automatically cleaned up.) +* If you don't like the operation path that fud2 selected for your build, you can control it with `--through `. fud2 will search the operation graph for a path that contains that op. You can provide this option multiple times; fud2 will look for paths that contain *all* these operations, in order. +* You can choose one of several modes with `-m `: + * `run`: Actually execute a build. The default. + * `gen`: Generate the Ninja build file in the build directory, but don't actually run the build. The default `run` mode is therefore approximately like doing `fud2 -m gen && ninja -C .fud2`. + * `emit`: Just print the Ninja build file to stdout. The `gen` mode is therefore approximately `fud2 -m emit > .fud2/build.ninja`. + * `plan`: Print a brief description of the plan, i.e., the sequence of operations that the build would run. + * `dot`: Print a [GraphViz][] depiction of the plan. Try `fud2 -m dot | dot -Tpdf > graph.pdf` and take a look. + +There are also some subcommands for doing things other than building stuff: + +* `fud2 edit-config`: Open the fud2 configuration file in `$EDITOR`. +* `fud2 list`: Print out all the available states and operations. +* `fud2 get-rsrc FILE`: Fetch a *resource file* and place it in the working directory. You typically do not need to use this interactively; it is used during builds to obtain files included with fud2 that are necessary for a given build. + +[graphviz]: https://graphviz.org + +## The Design of fud2 + +
+ +This section is about the *implementation* of fud2; it is only relevant if you want to work on it yourself. +No need to read any farther if all you want is to *use* fud2. + +
+ +### fud2 is a Command Orchestrator + +fud2 consists of two pieces, which are two separate Rust crates: + +* FudCore (the `fud-core` crate): is a *generic compiler driver* library. This library is not specific to Calyx and could hypothetically be used to build a fud2-like driver for any compiler ecosystem. Clients of the `fud-core` library work by constructing a `Driver` object that encapsulates a set of *states* and *operations* that define the driver's behavior. +* fud2 itself is a program that uses the FudCore library. All of the Calyx-specific logic lives in `fud2`. For the most part, all of the code in the `fud2` crate consists of declaring a bunch of states and operations. The `main` function does little more than dispatch to the resulting `Driver` object's generic command-line interface. + +The central design philosophy of FudCore (and by extension, fud2 itself) is that its sole job is to orchestrate external functionality. +All that functionality must be available as separate tools that can be invoked via the command line. +This is an important goal because it means the driver has a clear, discrete goal: *all it does* is decide on a list of commands to execute to perform a build. +All the "interesting work" must be delegated to separate tools outside of the driver. +This philosophy has both advantages and disadvantages: + +* On the positive side, it forces all the interesting logic to be invokable via a command that you, the user, can run equally well yourself. So if something is going wrong, there is *always* a command line you can copy and paste into your terminal to reproduce the problem at that particular step. It also means that the input and output of every step must be written to files in the filesystem, so you can easily inspect the intermediate state between every command. This file-based operation also means that fud2 builds are parallel and incremental by default. +* On the other hand, requiring everything to be separate commands means that fud2 has a complicated dependency story. It is not a monolith: to get meaningful work done, you currently have to install a bunch of Python components (among other things) so fud2 can invoke them. (We hope to mitigate the logistical pain this incurs over time, but we're not there yet.) Also, writing everything to a file in between each step comes at a performance cost. Someday, it may be a performance bottleneck that two steps in a build cannot simply exchange their data directly, through memory, and must serialize everything to disk first. (This has not been a problem in practice yet.) + +If you want to extend fud2 to do something new, the consequence is that you first need to come up with a sequence of commands that do that thing. +If necessary, you may find that you need to create new executables to do some minor glue tasks that would otherwise be implicit. +Then "all you need to do" is teach fud2 to execute those commands. + +### States, Operations, and Setups + +You can think of a FudCore driver as a graph, where the vertices are *states* and the edges are *operations*. +(In fact, this is literally the graph you can visualize with `-m dot`.) +Any build is a transformation from one state to another, traversing a path through this graph. +The operations (edges) along this path are the commands that must be executed to transform a file from the initial state to the final state. + +To make fud2 do something new, you probably want to add one or more operations, and you may need to add new states. +Aside from declaring the source and destination states, +operations generate chunks of [Ninja][] code. +So to implement an operation, you write a Rust function with this signature: + + fn build(emitter: &mut Emitter, input: &str, output: &str) + +Here, `emitter` is a wrapper around an output stream with a bunch of utility functions for printing out lines of Ninja code. +`input` and `output` are filenames. +So your job in this function is to print (at least) a Ninja `build` command that produces `output` as a target and uses `input` as a dependency. +For example, the Calyx-to-Verilog compiler operation might emit this chunk of Ninja code: + + build bar.sv: calyx foo.futil + backend = verilog + +when the `input` argument above is `"foo.futil"` and the `output` is `"bar.sv"`. +(The FudCore library will conjure these filenames for you; your job in this operation is just to use them as is.) + +Notice here that the generated Ninja chunk is using a build rule called `calyx`. +This also needs to be defined. +To set up things like variables and build rules that operations can use, FudCore has a separate concept called *setups*. +A setup is a function that generates some Ninja code that might be shared among multiple operations (or multiple instances of the same operation). +For example, our setup for Calyx compilation generates code like this: + + calyx-base = /path/to/calyx + calyx-exe = $calyx-base/target/debug/calyx + rule calyx + command = $calyx-exe -l $calyx-base -b $backend $args $in > $out + +That is, it defines two Ninja variables and one Ninja rule—the one that our build command above uses. +We *could* have designed FudCore without a separation between setups and operations, so this rule would get declared right next to the `build` command above. +But that would end up duplicating a lot of setup code that really only needs to appear once. +So that's why setups exist: to share a single stanza of Ninja scaffolding code between multiple operations. diff --git a/docs/running-calyx/fud2/scripts.md b/docs/running-calyx/fud2/scripts.md new file mode 100644 index 0000000000..b164d759d7 --- /dev/null +++ b/docs/running-calyx/fud2/scripts.md @@ -0,0 +1,126 @@ +# Scripting `fud2` with `Rhai` + +You can add functionality to `fud2` with functionality written in [Rhai][rhai]. Rhai is a scripting language designed to work well with Rust. + +All functionality included with `fud2` is written in Rhai. They can be found [here][fud2-scripts]. These provide a good example of how to add states and operations with Rhai. + +## Loading Scripts + +You can tell `fud2` to load a script by including a `plugins` key in your `fud2.toml` file. + +```toml +plugins = ["/my/fancy/plugin.rhai"] + +[calyx] +base = "..." +``` + +## Example Script + +We'll walk through how to write a script that adds support for using the `calyx` compiler. + +First, we need to define some states: + +```rust,ignore +export const calyx_state = state("calyx", ["futil"]); +export const verilog_state = state("verilog", ["sv", "v"]); +``` + +These two lines define a `calyx` state and a `verilog` state. The `export` prefix means that these variables will be accessible to other scripts that `import "calyx"`. + +Next we'll define a setup procedure to define some rules that will be useful. + +```rust,ignore +// allows calyx_setup to be used in other scripts +export const calyx_setup = calyx_setup; + +// a setup function is just a normal Rhai function that takes in an emitter +// we can use the emitter in the same way that we use it from rust +fn calyx_setup(e) { + // define a Ninja var from the fud2.toml config + e.config_var("calyx-base", "calyx.base"); + // define a Ninja var from either the config, or a default derived from calyx-base + e.config_var_or("calyx-exe", "calyx.exe", "$calyx-base/target/debug/calyx"); + // define a Ninja var from cli options, or with a default + e.config_var_or("args", "calyx.args", ""); + // define a rule to run the Calyx compiler + e.rule("calyx", "$calyx-exe -l $calyx-base -b $backend $args $in > $out"); +} +``` + +And now we can define the actual operation that will transform `calyx` files into `verilog` files. + +```rust,ignore +op( + "calyx-to-verilog", // operation name + [calyx_setup], // required setup functions + calyx_state, // input state + verilog_state, // output state + |e, input, output| { // function to construct Ninja build command + e.build_cmd([output], "calyx", [input], []) ; + e.arg("backend", "verilog"); + } +); +``` + +## Rhai Specifics + +### String Templates + +Rhai has a string templating feature, similar to the `format!` macro in rust. Templated strings are marked with backticks (`` `path/${some_var}.ext` ``) and variables are included with `$`. You can include expressions that will be evaluated by using brackets: `${1 + 2}`. + +### String Functions + +Rhai includes standard string operations. They are described in the [documentation][rhai-strings]. These are useful for constructing more complicated paths. + +### Export Rules + +In Rhai, all top-level variable declarations are private by default. If you want them to be available from other files, you need to `export` them explicitly. + +All functions are exported by default. However, they are only exported in a callable format. If you want to use the function as a variable (when passing them as a setup function or build function), you need to export them explicitly as well. + +This is how that looks: +```rust,ignore +export const my_fancy_setup = my_fancy_setup; +fn my_fancy_setup(e) { + ... +} +``` + +### Imports + +You can import another Rhai script file like so: + +```rust,ignore +import "calyx" as c; +``` + +All exported symbols defined in `calyx.rhai` will be available under `c`. + +```rust,ignore +print(c::calyx_state); +print(c::calyx_setup); +``` + +
+ +The name for an import is always just the basename of the script file, without any extension. + +
+ +## API + +Currently, the Rhai API is almost identical to the Rust API. However `Emitter::add_file` is not currently supported. And `Emitter::var` is renamed to `_var` because `var` is a reserved keyword in Rhai. + +### Adding to the API + +If there is something that is hard to do in Rhai, it is straightforward to [register a Rust function][rhai-rust-fn] so that it is available from Rhai. + +Rust functions are registered in [`ScriptRunner::new`][fud-core-scriptrunner]. Refer to [`ScriptRunner::reg_get_state`][fud-core-reg_get_state] to see a simple example of how to register a function. + +[rhai]: https://rhai.rs/book/index.html +[rhai-strings]: https://rhai.rs/book/ref/string-fn.html?highlight=String#standard-string-functions +[rhai-rust-fn]: https://rhai.rs/book/rust/functions.html +[fud2-scripts]: https://github.com/calyxir/calyx/tree/main/fud2/scripts +[fud-core-scriptrunner]: https://github.com/calyxir/calyx/blob/6f895a1353020ce254860c3aa0fcfa2ba1abf4c4/fud2/fud-core/src/script/plugin.rs#L68 +[fud-core-reg_get_state]: https://github.com/calyxir/calyx/blob/6f895a1353020ce254860c3aa0fcfa2ba1abf4c4/fud2/fud-core/src/script/plugin.rs#L152 diff --git a/fud2/Cargo.toml b/fud2/Cargo.toml index 86a5e070ea..6d79ed550b 100644 --- a/fud2/Cargo.toml +++ b/fud2/Cargo.toml @@ -14,7 +14,7 @@ description = "Compiler driver for the Calyx infrastructure" [features] migrate_to_scripts = [] -default = [] +default = ["migrate_to_scripts"] [dependencies] fud-core = { path = "fud-core", version = "0.0.2" } @@ -32,3 +32,4 @@ path = "src/main.rs" [dev-dependencies] insta = "1.36.0" +itertools.workspace = true diff --git a/fud2/fud-core/src/script/exec_scripts.rs b/fud2/fud-core/src/script/exec_scripts.rs index c12a54118f..03c6101392 100644 --- a/fud2/fud-core/src/script/exec_scripts.rs +++ b/fud2/fud-core/src/script/exec_scripts.rs @@ -43,10 +43,46 @@ impl RhaiEmitter { self.0.borrow().config_val(key).map_err(to_rhai_err) } + fn config_constrained_val( + &mut self, + key: &str, + valid_values: rhai::Array, + ) -> RhaiResult { + self.0 + .borrow() + .config_constrained_val( + key, + to_str_slice(&valid_values) + .iter() + .map(|x| &**x) + .collect::>(), + ) + .map_err(to_rhai_err) + } + fn config_or(&mut self, key: &str, default: &str) -> String { self.0.borrow().config_or(key, default) } + fn config_constrained_or( + &mut self, + key: &str, + valid_values: rhai::Array, + default: &str, + ) -> RhaiResult { + self.0 + .borrow() + .config_constrained_or( + key, + to_str_slice(&valid_values) + .iter() + .map(|x| &**x) + .collect::>(), + default, + ) + .map_err(to_rhai_err) + } + fn config_var(&mut self, name: &str, key: &str) -> RhaiResult<()> { self.0 .borrow_mut() @@ -145,7 +181,9 @@ thread_local! { engine .register_type_with_name::("RhaiEmitter") .register_fn("config_val", RhaiEmitter::config_val) + .register_fn("config_constrained_val", RhaiEmitter::config_constrained_val) .register_fn("config_or", RhaiEmitter::config_or) + .register_fn("config_constrained_or", RhaiEmitter::config_constrained_or) .register_fn("config_var", RhaiEmitter::config_var) .register_fn("config_var_or", RhaiEmitter::config_var_or) .register_fn("var_", RhaiEmitter::var) diff --git a/fud2/fud-core/src/script/plugin.rs b/fud2/fud-core/src/script/plugin.rs index cc0f782056..2570b48b29 100644 --- a/fud2/fud-core/src/script/plugin.rs +++ b/fud2/fud-core/src/script/plugin.rs @@ -4,6 +4,7 @@ use crate::{ }; use std::{ cell::RefCell, + collections::HashMap, path::{Path, PathBuf}, rc::Rc, }; @@ -20,6 +21,7 @@ struct ScriptContext { builder: Rc>, path: Rc, ast: Rc, + setups: Rc>>, } impl ScriptContext { @@ -34,17 +36,7 @@ impl ScriptContext { setups .into_iter() .map(|s| match s.clone().try_cast::() { - Some(fnptr) => { - let rctx = RhaiSetupCtx { - path: Rc::clone(&self.path), - ast: Rc::new(self.ast.clone_functions_only()), - name: fnptr.fn_name().to_string(), - }; - Ok(self.builder.borrow_mut().add_setup( - &format!("{} (plugin)", fnptr.fn_name()), - rctx, - )) - } + Some(fnptr) => Ok(self.make_or_get_setupref(fnptr)), // if we can't cast as a FnPtr, try casting as a SetupRef directly None => { s.clone().try_cast::().ok_or_else(move || { @@ -56,6 +48,30 @@ impl ScriptContext { }) .collect::>>() } + + /// Construct a SetupRef for a rhai function. If we already have a SetupRef, + /// return the previously constructed version, otherwise make a new one, cache it + /// and return that. + fn make_or_get_setupref(&self, fnptr: rhai::FnPtr) -> SetupRef { + // if we haven't seen this fnptr before, make a new setup context + // for this function + if !self.setups.borrow().contains_key(fnptr.fn_name()) { + let rctx = RhaiSetupCtx { + path: Rc::clone(&self.path), + ast: Rc::new(self.ast.clone_functions_only()), + name: fnptr.fn_name().to_string(), + }; + let setup_ref = self + .builder + .borrow_mut() + .add_setup(&format!("{} (plugin)", fnptr.fn_name()), rctx); + self.setups + .borrow_mut() + .insert(fnptr.fn_name().to_string(), setup_ref); + } + + *self.setups.borrow().get(fnptr.fn_name()).unwrap() + } } pub struct ScriptRunner { @@ -63,6 +79,7 @@ pub struct ScriptRunner { engine: rhai::Engine, rhai_functions: rhai::AST, resolver: Option, + setups: Rc>>, } impl ScriptRunner { @@ -72,6 +89,7 @@ impl ScriptRunner { engine: rhai::Engine::new(), rhai_functions: rhai::AST::empty(), resolver: Some(Resolver::default()), + setups: Rc::default(), }; this.reg_state(); this.reg_get_state(); @@ -222,6 +240,7 @@ impl ScriptRunner { builder: Rc::clone(&self.builder), path: Rc::new(path), ast: Rc::new(self.rhai_functions.clone()), + setups: Rc::clone(&self.setups), } } diff --git a/fud2/fud-core/src/script/report.rs b/fud2/fud-core/src/script/report.rs index 98d2bab69d..cd144200c2 100644 --- a/fud2/fud-core/src/script/report.rs +++ b/fud2/fud-core/src/script/report.rs @@ -1,19 +1,28 @@ use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; use rhai::EvalAltResult; -use std::{fs, path::Path}; +use std::{ + fs, + path::{Path, PathBuf}, +}; use super::{error::RhaiSystemError, exec_scripts::RhaiResult}; +/// A small hack to improve error messages. These are known function names +/// so that we can say that arguments are incorrect when a user calls +/// one of these functions. +const KNOWN_FNS: [&str; 4] = ["state", "op", "get_state", "get_setup"]; + pub(super) trait RhaiReport { fn report_raw, S: AsRef>( &self, path: P, len: usize, + header: Option, msg: S, ); fn report>(&self, path: P) { - self.report_raw(path, 0, "") + self.report_raw(path, 0, None, "") } } @@ -22,6 +31,7 @@ impl RhaiReport for rhai::Position { &self, path: P, len: usize, + header: Option, msg: S, ) { let source = fs::read_to_string(path.as_ref()); @@ -43,7 +53,9 @@ impl RhaiReport for rhai::Position { let err_offset = line_offset + (position - 1); Report::build(ReportKind::Error, name, err_offset) - .with_message("Failed to load plugin") + .with_message( + header.unwrap_or("Failed to load plugin".to_string()), + ) .with_label( Label::new((name, err_offset..err_offset + len)) .with_message(msg.as_ref().fg(Color::Red)), @@ -68,19 +80,25 @@ impl RhaiReport for EvalAltResult { &self, path: P, _len: usize, + header: Option, _msg: S, ) { match &self { - EvalAltResult::ErrorVariableNotFound(variable, pos) => { - pos.report_raw(&path, variable.len(), "Undefined variable") - } + EvalAltResult::ErrorVariableNotFound(variable, pos) => pos + .report_raw( + &path, + variable.len(), + header, + "Undefined variable", + ), EvalAltResult::ErrorFunctionNotFound(msg, pos) => { let (fn_name, args) = msg.split_once(' ').unwrap_or((msg, "")); - pos.report_raw( - &path, - fn_name.len(), - format!("{fn_name} {args}"), - ) + let msg = if KNOWN_FNS.contains(&fn_name) { + format!("Invalid arguments. Expected {args}") + } else { + format!("Unknown function: {fn_name} {args}") + }; + pos.report_raw(&path, fn_name.len(), header, msg) } EvalAltResult::ErrorSystem(msg, err) if err.is::() => @@ -91,12 +109,27 @@ impl RhaiReport for EvalAltResult { } else { format!("{msg}: {err}") }; - e.position.report_raw(&path, 0, msg) + e.position.report_raw(&path, 0, header, msg) + } + EvalAltResult::ErrorInModule(submod_path, err, _) + if path.as_ref().to_str() == Some(submod_path) => + { + err.report(submod_path) } - EvalAltResult::ErrorInModule(path, err, _) => err.report(path), + EvalAltResult::ErrorInModule(submod_path, err, _) => err + .report_raw( + submod_path, + 0, + Some(format!( + "Error in submodule {:?} while loading {:?}", + PathBuf::from(submod_path).file_stem().unwrap(), + path.as_ref().file_stem().unwrap() + )), + "", + ), // for errors that we don't have custom processing, just point // to the beginning of the error, and use the error Display as message - e => e.position().report_raw(&path, 0, format!("{e}")), + e => e.position().report_raw(&path, 0, header, format!("{e}")), } } } @@ -106,10 +139,11 @@ impl RhaiReport for RhaiResult { &self, path: P, len: usize, + header: Option, msg: S, ) { if let Err(e) = self { - (**e).report_raw(path, len, msg); + (**e).report_raw(path, len, header, msg); } } } diff --git a/fud2/fud-core/src/script/resolver.rs b/fud2/fud-core/src/script/resolver.rs index 023dd73f1f..92d0a58728 100644 --- a/fud2/fud-core/src/script/resolver.rs +++ b/fud2/fud-core/src/script/resolver.rs @@ -10,7 +10,7 @@ use std::{ use itertools::Itertools; use rhai::EvalAltResult; -use super::{exec_scripts::RhaiResult, report::RhaiReport}; +use super::exec_scripts::RhaiResult; #[derive(Default)] pub(super) struct Resolver { @@ -28,8 +28,8 @@ enum ResolverError { impl Display for ResolverError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ResolverError::Failed(m) => write!(f, "Loading `{m}` failed."), - ResolverError::Unknown(m) => write!(f, "`{m}` was not found."), + ResolverError::Failed(m) => write!(f, "Loading {m} failed."), + ResolverError::Unknown(m) => write!(f, "{m} was not found."), } } } @@ -48,7 +48,6 @@ impl Resolver { path: PathBuf, ast: rhai::AST, ) -> rhai::AST { - // let stem = path.file_stem().unwrap().into(); let functions = ast.clone_functions_only(); self.files.push((path, ast)); @@ -72,7 +71,7 @@ impl Resolver { .collect() } - fn resolve_name(&self, name: &str) -> Option<&rhai::AST> { + fn find_ast(&self, name: &str) -> Option<&rhai::AST> { self.files .iter() .find(|(path, _)| { @@ -85,6 +84,16 @@ impl Resolver { .map(|(_, ast)| ast) } + fn resolve_filename(&self, name: &str) -> Option<&PathBuf> { + self.files + .iter() + .find(|(path, _)| { + Some(name) == path.to_str() + || Some(name) == path.file_stem().and_then(|os| os.to_str()) + }) + .map(|(path, _)| path) + } + fn normalize_name(&self, name: &str) -> String { PathBuf::from(name) .file_stem() @@ -109,7 +118,7 @@ impl Resolver { let name = self.normalize_name(path); if self.failed.borrow().contains(&name) { Err(Box::new(EvalAltResult::ErrorSystem( - "Failed module loading".to_string(), + "".to_string(), Box::new(ResolverError::Failed(format!("{path:?}"))), ))) } else { @@ -129,9 +138,12 @@ impl rhai::ModuleResolver for Resolver { engine: &rhai::Engine, _source: Option<&str>, name: &str, - _pos: rhai::Position, + pos: rhai::Position, ) -> RhaiResult> { - let path_buf = PathBuf::from(name); + let path_buf = self + .resolve_filename(name) + .cloned() + .unwrap_or(PathBuf::from(name)); // if this path has already failed, don't try loading it again self.did_fail(name)?; @@ -141,7 +153,7 @@ impl rhai::ModuleResolver for Resolver { Ok(module) } else { // otherwise, make a new module, cache it, and return it - self.resolve_name(name) + self.find_ast(name) .ok_or(ResolverError::Unknown(name.to_string()).into()) .and_then(|ast| { rhai::Module::eval_ast_as_new( @@ -151,10 +163,14 @@ impl rhai::ModuleResolver for Resolver { ) }) .map(|m| self.insert(name, m)) - .inspect_err(|e| { - e.report(&path_buf); - self.add_failed(name) + .map_err(|e| { + Box::new(EvalAltResult::ErrorInModule( + path_buf.as_os_str().to_str().unwrap().to_string(), + e, + pos, + )) }) + .inspect_err(|_| self.add_failed(name)) } } } diff --git a/fud2/scripts/axi.rhai b/fud2/scripts/axi.rhai index 3f225efd6f..9505c9ffc6 100644 --- a/fud2/scripts/axi.rhai +++ b/fud2/scripts/axi.rhai @@ -2,25 +2,43 @@ import "calyx" as c; export const yxi = state("yxi", ["yxi"]); +fn yxi_setup(e) { + e.config_var_or("yxi", "yxi", "$calyx-base/target/debug/yxi"); + e.rule("yxi", "$yxi -l $calyx-base $in > $out"); +} + op( "calyx-to-yxi", - [c::calyx_setup], + [c::calyx_setup, yxi_setup], c::calyx_state, yxi, |e, input, output| { - e.build_cmd([output], "calyx", [input], []); - e.arg("backend", "yxi"); + e.build_cmd([output], "yxi", [input], []); }, ); fn wrapper_setup(e) { - e.config_var_or("axi-generator", "axi.generator", "$calyx-base/yxi/axi-calyx/axi-generator.py"); - e.config_var_or("python", "python", "python3"); - e.rule("gen-axi", "$python $axi-generator $in > $out"); + // Define a `gen-axi` rule that invokes our Python code generator program. + // For now point to standalone axi-generator.py. Can maybe turn this into a rsrc file? + let dynamic = + e.config_constrained_or("dynamic", ["true", "false"], "false"); + let generator_path = if dynamic == "true" { + "$calyx-base/yxi/axi-calyx/dynamic-axi-generator.py" + } else { + "$calyx-base/yxi/axi-calyx/axi-generator.py" + }; + e.config_var_or("axi-generator", "axi.generator", generator_path); + e.config_var_or("python", "python", "python3"); + + e.rule("gen-axi", "$python $axi-generator $in > $out"); + + // Define a simple `combine` rule that just concatenates any numer of files. + e.rule("combine", "cat $in > $out"); - // Define a simple `combine` rule that just concats any number of files. - e.rule("combine", "cat $in > $out"); - e.rule("remove-imports", "sed '1,/component main/{/component main/!d; }' $in > $out"); + e.rule( + "remove-imports", + "sed '1,/component main/{/component main/!d; }' $in > $out", + ); } /// Replace the extension in `path` with `new_ext` @@ -37,10 +55,9 @@ fn axi_wrapped_op(e, input, output) { let file_name = input.split("/")[-1]; let tmp_yxi = replace_ext(file_name, "yxi"); - e.build_cmd([tmp_yxi], "calyx", [input], []); - e.arg("backend", "yxi"); + e.build_cmd([tmp_yxi], "yxi", [input], []); - let refified_calyx = `refified_${file_name}.futil`; + let refified_calyx = replace_ext(`refified_${file_name}`, "futil"); e.build_cmd([refified_calyx], "calyx-pass", [input], []); e.arg("pass", "external-to-ref"); @@ -55,7 +72,7 @@ fn axi_wrapped_op(e, input, output) { op( "axi-wrapped", - [c::calyx_setup, wrapper_setup], + [c::calyx_setup, yxi_setup, wrapper_setup], c::calyx_state, c::calyx_state, axi_wrapped_op diff --git a/fud2/scripts/calyx.rhai b/fud2/scripts/calyx.rhai index 13829e2e24..83162e51cf 100644 --- a/fud2/scripts/calyx.rhai +++ b/fud2/scripts/calyx.rhai @@ -9,6 +9,12 @@ fn calyx_setup(e) { e.config_var_or("args", "calyx.args", ""); e.rule("calyx", "$calyx-exe -l $calyx-base -b $backend $args $in > $out"); e.rule("calyx-pass", "$calyx-exe -l $calyx-base -p $pass $args $in > $out"); + + e.config_var_or("flags", "calyx.flags", "-p none"); + e.rule( + "calyx-with-flags", + "$calyx-exe -l $calyx-base $flags $args $in > $out", + ); } op( diff --git a/fud2/scripts/cider.rhai b/fud2/scripts/cider.rhai index ee7ed505ee..2bb9f0ef75 100644 --- a/fud2/scripts/cider.rhai +++ b/fud2/scripts/cider.rhai @@ -3,6 +3,7 @@ import "testbench" as tb; import "calyx" as c; let dbg = state("debug", []); +let cider_state = state("cider", []); fn cider_setup(e) { e.config_var_or( @@ -41,7 +42,7 @@ fn cider_setup(e) { ); e.rule( - "cider2", + "run-cider", "$cider-exe -l $calyx-base --data data.dump $in flat > $out", ); @@ -55,6 +56,21 @@ fn cider_setup(e) { ); } +op( + "calyx-to-cider", + [sim::sim_setup, c::calyx_setup], + c::calyx_state, + cider_state, + |e, input, _output| { + e.build_cmd( + ["cider-input.futil"], + "calyx-with-flags", + [input], + [], + ); + }, +); + op( "interp", [ @@ -76,14 +92,20 @@ op( ); }, ); + op( - "interp-flat", + "cider", [sim::sim_setup, c::calyx_setup, cider_setup], - c::calyx_state, + cider_state, sim::dat, - |e, input, output| { + |e, _input, output| { let out_file = "interp_out.dump"; - e.build_cmd([out_file], "cider2", [input], ["data.dump"]); + e.build_cmd( + [out_file], + "run-cider", + ["cider-input.futil"], + ["data.dump"], + ); e.build_cmd( [output], "interp-to-dump", @@ -92,6 +114,7 @@ op( ); }, ); + op( "debug", [ diff --git a/fud2/scripts/cocotb-axi.rhai b/fud2/scripts/cocotb-axi.rhai new file mode 100644 index 0000000000..3672e12a27 --- /dev/null +++ b/fud2/scripts/cocotb-axi.rhai @@ -0,0 +1,55 @@ +import "calyx" as c; + +export let cocotb_axi = state("cocotb-axi", ["dat"]); + +export let cocotb_setup = cocotb_setup; +fn cocotb_setup(e) { + e.config_var_or("cocotb-makefile-dir", "cocotb.makefile-dir", "$calyx-base/yxi/axi-calyx/cocotb"); + // TODO (nate): this is duplicated from the sim_setup above. Can this be shared? + // The input data file. `sim.data` is required. + let data_name = e.config_val("sim.data"); + let data_path = e.external_path(data_name); + e.var_("sim_data", data_path); + + // Cocotb is wants files relative to the location of the makefile. + // This is annoying to calculate on the fly, so we just copy necessary files to the build directory + e.rule("copy", "cp $in $out"); + e.rule("make-cocotb", "make DATA_PATH=$sim_data VERILOG_SOURCE=$in COCOTB_LOG_LEVEL=CRITICAL > $out"); + // This cleans up the extra `make` cruft, leaving what is in between `{` and `}.` + e.rule("cleanup-cocotb", "sed -n '/Output:/,/make\\[1\\]/{/Output:/d;/make\\[1\\]/d;p}' $in > $out"); +} + +op( + "calyx-to-cocotb-axi", + [c::calyx_setup, cocotb_setup], + c::verilog_noverify, + cocotb_axi, + |e, input, output| { + e.build_cmd( + ["Makefile"], + "copy", + ["$cocotb-makefile-dir/Makefile"], + [], + ); + e.build_cmd( + ["axi_test.py"], + "copy", + ["$cocotb-makefile-dir/axi_test.py"], + [], + ); + e.build_cmd( + ["run_axi_test.py"], + "copy", + ["$cocotb-makefile-dir/run_axi_test.py"], + [], + ); + e.build_cmd( + ["tmp.dat"], + "make-cocotb", + [input], + ["Makefile", "axi_test.py", "run_axi_test.py"], + ); + + e.build_cmd([output], "cleanup-cocotb", ["tmp.dat"], []); + } +) diff --git a/fud2/scripts/firrtl.rhai b/fud2/scripts/firrtl.rhai index 401d92cf9a..8895622c7e 100644 --- a/fud2/scripts/firrtl.rhai +++ b/fud2/scripts/firrtl.rhai @@ -31,8 +31,7 @@ fn calyx_to_firrtl_helper(e, input, output, firrtl_primitives) { e.build_cmd([only_refs_calyx], "external-to-ref", [input], []); // Get YXI to generate JSON for testbench generation - e.build_cmd([memories_json], "calyx", [only_externals_calyx], []); - e.arg("backend", "yxi"); + e.build_cmd([memories_json], "yxi", [only_externals_calyx], []); // generate custom testbench e.build_cmd( [testbench], diff --git a/fud2/scripts/icarus.rhai b/fud2/scripts/icarus.rhai index 4ca954a96e..a0c3be7c66 100644 --- a/fud2/scripts/icarus.rhai +++ b/fud2/scripts/icarus.rhai @@ -2,6 +2,7 @@ import "calyx" as c; import "rtl_sim" as sim; import "testbench" as tb; +export let icarus_setup = icarus_setup; fn icarus_setup(e) { e.var_("iverilog", "iverilog"); e.rule( diff --git a/fud2/scripts/verilator.rhai b/fud2/scripts/verilator.rhai index 574e4cb958..891b9f93ba 100644 --- a/fud2/scripts/verilator.rhai +++ b/fud2/scripts/verilator.rhai @@ -2,6 +2,7 @@ import "rtl_sim" as sim; import "testbench" as tb; import "calyx" as c; +export const verilator_setup = verilator_setup; fn verilator_setup(e) { e.config_var_or("verilator", "verilator.exe", "verilator"); e.config_var_or("cycle-limit", "sim.cycle_limit", "500000000"); @@ -16,10 +17,11 @@ fn verilator_setup(e) { e.rule("cp", "cp $in $out"); } +export const verilator_build = verilator_build; fn verilator_build(e, input, output, standalone_tb) { let out_dir = "verilator-out"; let sim_bin = `${out_dir}/VTOP`; - if standalone_testbench { + if standalone_tb { e.build_cmd( [sim_bin], "verilator-compile-standalone-tb", @@ -48,7 +50,7 @@ op( op( "verilator-refmem", - [sim::sim_setup, tb::standalone_setup, verilator_setup], + [sim::sim_setup, tb::custom_setup, verilator_setup], tb::verilog_refmem, sim::simulator, |e, input, output| { verilator_build(e, input, output, false) } diff --git a/fud2/scripts/xilinx.rhai b/fud2/scripts/xilinx.rhai index 79515e9f99..6d625fb2f8 100644 --- a/fud2/scripts/xilinx.rhai +++ b/fud2/scripts/xilinx.rhai @@ -121,7 +121,7 @@ op( xrt_setup, ], xclbin, - sim::dat, + sim::vcd, |e, input, output| { e.rsrc("xrt_trace.ini"); e.build_cmd( diff --git a/fud2/src/lib.rs b/fud2/src/lib.rs index 93d4b21f0f..860693205d 100644 --- a/fud2/src/lib.rs +++ b/fud2/src/lib.rs @@ -813,7 +813,7 @@ pub fn build_driver(bld: &mut DriverBuilder) { e.rule( "remove-imports", - "sed '1,/component main/{/component main/!d}' $in > $out", + "sed '1,/component main/{/component main/!d; }' $in > $out", )?; Ok(()) }); diff --git a/fud2/tests/snapshots/tests__list_ops.snap b/fud2/tests/snapshots/tests__list_ops.snap new file mode 100644 index 0000000000..d780efc2e9 --- /dev/null +++ b/fud2/tests/snapshots/tests__list_ops.snap @@ -0,0 +1,145 @@ +--- +source: fud2/tests/tests.rs +--- +[ + ( + "axi-wrapped", + "calyx", + "calyx", + ), + ( + "calyx-noverify", + "calyx", + "verilog-noverify", + ), + ( + "calyx-to-cider", + "calyx", + "cider", + ), + ( + "calyx-to-cocotb-axi", + "verilog-noverify", + "cocotb-axi", + ), + ( + "calyx-to-firrtl", + "calyx", + "firrtl", + ), + ( + "calyx-to-verilog", + "calyx", + "verilog", + ), + ( + "calyx-to-yxi", + "calyx", + "yxi", + ), + ( + "cider", + "cider", + "dat", + ), + ( + "dahlia-to-calyx", + "dahlia", + "calyx", + ), + ( + "debug", + "calyx", + "debug", + ), + ( + "firrtl", + "firrtl", + "verilog-refmem", + ), + ( + "firrtl-noverify", + "firrtl", + "verilog-refmem-noverify", + ), + ( + "firrtl-with-primitives", + "calyx", + "firrtl-with-primitives", + ), + ( + "firrtl-with-primitives", + "firrtl-with-primitives", + "verilog-refmem", + ), + ( + "firrtl-with-primitives-noverify", + "firrtl-with-primitives", + "verilog-refmem-noverify", + ), + ( + "icarus", + "verilog-noverify", + "sim", + ), + ( + "icarus-refmem", + "verilog-refmem-noverify", + "sim", + ), + ( + "interp", + "calyx", + "dat", + ), + ( + "mrxl-to-calyx", + "mrxl", + "calyx", + ), + ( + "primitive-uses", + "calyx", + "primitive-uses-json", + ), + ( + "simulate", + "sim", + "dat", + ), + ( + "trace", + "sim", + "vcd", + ), + ( + "verilator", + "verilog", + "sim", + ), + ( + "verilator-refmem", + "verilog-refmem", + "sim", + ), + ( + "xclbin", + "xo", + "xclbin", + ), + ( + "xo", + "calyx", + "xo", + ), + ( + "xrt", + "xclbin", + "dat", + ), + ( + "xrt-trace", + "xclbin", + "vcd", + ), +] diff --git a/fud2/tests/snapshots/tests__list_states.snap b/fud2/tests/snapshots/tests__list_states.snap new file mode 100644 index 0000000000..d7f6f169a6 --- /dev/null +++ b/fud2/tests/snapshots/tests__list_states.snap @@ -0,0 +1,24 @@ +--- +source: fud2/tests/tests.rs +--- +[ + "calyx", + "cider", + "cocotb-axi", + "dahlia", + "dat", + "debug", + "firrtl", + "firrtl-with-primitives", + "mrxl", + "primitive-uses-json", + "sim", + "vcd", + "verilog", + "verilog-noverify", + "verilog-refmem", + "verilog-refmem-noverify", + "xclbin", + "xo", + "yxi", +] diff --git a/fud2/tests/snapshots/tests__emit@calyx_debug.snap b/fud2/tests/snapshots/tests__test@calyx_debug.snap similarity index 93% rename from fud2/tests/snapshots/tests__emit@calyx_debug.snap rename to fud2/tests/snapshots/tests__test@calyx_debug.snap index a9dad091f0..2bc1b3b9f9 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_debug.snap +++ b/fud2/tests/snapshots/tests__test@calyx_debug.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> debug +description: "emit request: calyx -> debug" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -20,10 +19,8 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -35,7 +32,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# Cider interpreter cider-exe = $calyx-base/target/debug/cider cider-converter = $calyx-base/target/debug/cider-data-converter rule cider @@ -58,7 +54,6 @@ rule interp-to-dump command = $cider-converter --to json $in > $out build data.dump: dump-to-interp $sim_data | $cider-converter -# build targets build _pseudo_debug: cider-debug stdin | data.json default _pseudo_debug diff --git a/fud2/tests/snapshots/tests__emit@calyx_firrtl_verilog-refmem.snap b/fud2/tests/snapshots/tests__test@calyx_firrtl_verilog-refmem.snap similarity index 91% rename from fud2/tests/snapshots/tests__emit@calyx_firrtl_verilog-refmem.snap rename to fud2/tests/snapshots/tests__test@calyx_firrtl_verilog-refmem.snap index 0846dbf5d1..f8c2132df3 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_firrtl_verilog-refmem.snap +++ b/fud2/tests/snapshots/tests__test@calyx_firrtl_verilog-refmem.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> verilog-refmem through firrtl +description: "emit request: calyx -> verilog-refmem through firrtl" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# Custom Testbench Setup rule ref-to-external command = sed 's/ref /@external /g' $in > $out rule external-to-ref @@ -30,7 +28,6 @@ rule generate-refmem-testbench rule dummy command = sh -c 'cat $$0' $in > $out -# Firrtl to Verilog compiler firrtl-exe = /test/bin/firrtl rule firrtl command = $firrtl-exe -i $in -o $out -X sverilog @@ -38,7 +35,6 @@ build primitives-for-firrtl.sv: get-rsrc rule add-verilog-primitives command = cat primitives-for-firrtl.sv $in > $out -# build targets build external.futil: ref-to-external stdin build ref.futil: external-to-ref stdin build memory-info.json: yxi external.futil diff --git a/fud2/tests/snapshots/tests__emit@calyx_icarus_dat.snap b/fud2/tests/snapshots/tests__test@calyx_icarus_dat.snap similarity index 90% rename from fud2/tests/snapshots/tests__emit@calyx_icarus_dat.snap rename to fud2/tests/snapshots/tests__test@calyx_icarus_dat.snap index 92d4e82f0b..28436d7c5c 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_icarus_dat.snap +++ b/fud2/tests/snapshots/tests__test@calyx_icarus_dat.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> dat through icarus +description: "emit request: calyx -> dat through icarus" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -32,17 +30,14 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Icarus Verilog iverilog = iverilog rule icarus-compile-standalone-tb command = $iverilog -g2012 -o $out tb.sv $in rule icarus-compile-custom-tb command = $iverilog -g2012 -o $out tb.sv memories.sv $in -# build targets build stdin.sv: calyx stdin backend = verilog args = --disable-verify diff --git a/fud2/tests/snapshots/tests__emit@calyx_icarus_vcd.snap b/fud2/tests/snapshots/tests__test@calyx_icarus_vcd.snap similarity index 90% rename from fud2/tests/snapshots/tests__emit@calyx_icarus_vcd.snap rename to fud2/tests/snapshots/tests__test@calyx_icarus_vcd.snap index c88fa1bebd..81173c4956 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_icarus_vcd.snap +++ b/fud2/tests/snapshots/tests__test@calyx_icarus_vcd.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> vcd through icarus +description: "emit request: calyx -> vcd through icarus" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -32,17 +30,14 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Icarus Verilog iverilog = iverilog rule icarus-compile-standalone-tb command = $iverilog -g2012 -o $out tb.sv $in rule icarus-compile-custom-tb command = $iverilog -g2012 -o $out tb.sv memories.sv $in -# build targets build stdin.sv: calyx stdin backend = verilog args = --disable-verify diff --git a/fud2/tests/snapshots/tests__emit@calyx_interp_dat.snap b/fud2/tests/snapshots/tests__test@calyx_interp_dat.snap similarity index 92% rename from fud2/tests/snapshots/tests__emit@calyx_interp_dat.snap rename to fud2/tests/snapshots/tests__test@calyx_interp_dat.snap index ba67e07e4d..e6ef5b4935 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_interp_dat.snap +++ b/fud2/tests/snapshots/tests__test@calyx_interp_dat.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> dat through interp +description: "emit request: calyx -> dat through interp" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -20,10 +19,8 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -35,7 +32,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# Cider interpreter cider-exe = $calyx-base/target/debug/cider cider-converter = $calyx-base/target/debug/cider-data-converter rule cider @@ -58,7 +54,6 @@ rule interp-to-dump command = $cider-converter --to json $in > $out build data.dump: dump-to-interp $sim_data | $cider-converter -# build targets build interp_out.json: cider stdin | data.json build stdin.json: interp-to-dat interp_out.json | $sim_data interp-dat.py diff --git a/fud2/tests/snapshots/tests__emit@calyx_verilator_dat.snap b/fud2/tests/snapshots/tests__test@calyx_verilator_dat.snap similarity index 91% rename from fud2/tests/snapshots/tests__emit@calyx_verilator_dat.snap rename to fud2/tests/snapshots/tests__test@calyx_verilator_dat.snap index d93a5518e4..bf66e8d417 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_verilator_dat.snap +++ b/fud2/tests/snapshots/tests__test@calyx_verilator_dat.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> dat through verilator +description: "emit request: calyx -> dat through verilator" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -32,10 +30,8 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Verilator verilator = verilator cycle-limit = 500000000 rule verilator-compile-standalone-tb @@ -45,7 +41,6 @@ rule verilator-compile-custom-tb rule cp command = cp $in $out -# build targets build stdin.sv: calyx stdin backend = verilog build verilator-out/VTOP: verilator-compile-standalone-tb stdin.sv | tb.sv diff --git a/fud2/tests/snapshots/tests__emit@calyx_verilator_vcd.snap b/fud2/tests/snapshots/tests__test@calyx_verilator_vcd.snap similarity index 91% rename from fud2/tests/snapshots/tests__emit@calyx_verilator_vcd.snap rename to fud2/tests/snapshots/tests__test@calyx_verilator_vcd.snap index e501a2cb0b..f0289263cf 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_verilator_vcd.snap +++ b/fud2/tests/snapshots/tests__test@calyx_verilator_vcd.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> vcd through verilator +description: "emit request: calyx -> vcd through verilator" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -32,10 +30,8 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Verilator verilator = verilator cycle-limit = 500000000 rule verilator-compile-standalone-tb @@ -45,7 +41,6 @@ rule verilator-compile-custom-tb rule cp command = cp $in $out -# build targets build stdin.sv: calyx stdin backend = verilog build verilator-out/VTOP: verilator-compile-standalone-tb stdin.sv | tb.sv diff --git a/fud2/tests/snapshots/tests__emit@calyx_verilog.snap b/fud2/tests/snapshots/tests__test@calyx_verilog.snap similarity index 88% rename from fud2/tests/snapshots/tests__emit@calyx_verilog.snap rename to fud2/tests/snapshots/tests__test@calyx_verilog.snap index 839b5af13c..dd36be84b1 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_verilog.snap +++ b/fud2/tests/snapshots/tests__test@calyx_verilog.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> verilog +description: "emit request: calyx -> verilog" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# build targets build stdin.sv: calyx stdin backend = verilog diff --git a/fud2/tests/snapshots/tests__emit@calyx_xrt-trace_vcd.snap b/fud2/tests/snapshots/tests__test@calyx_xrt-trace_vcd.snap similarity index 93% rename from fud2/tests/snapshots/tests__emit@calyx_xrt-trace_vcd.snap rename to fud2/tests/snapshots/tests__test@calyx_xrt-trace_vcd.snap index 4f1c8297c1..28c0170690 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_xrt-trace_vcd.snap +++ b/fud2/tests/snapshots/tests__test@calyx_xrt-trace_vcd.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> vcd through xrt-trace +description: "emit request: calyx -> vcd through xrt-trace" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# Xilinx tools vivado-dir = /test/xilinx/vivado vitis-dir = /test/xilinx/vitis build gen_xo.tcl: get-rsrc @@ -33,7 +31,6 @@ rule compile-xclbin command = $vitis-dir/bin/v++ -g -t $xilinx-mode --platform $platform --save-temps --profile.data all:all:all --profile.exec all:all:all -lo $out $in pool = console -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -47,10 +44,8 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Xilinx execution via XRT rule emconfig command = $vitis-dir/bin/emconfigutil --platform $platform build emconfig.json: emconfig @@ -65,7 +60,6 @@ build pre_sim.tcl: echo | build post_sim.tcl: echo | contents = close_vcd\\n -# build targets build main.sv: calyx stdin backend = verilog args = --synthesis -p external diff --git a/fud2/tests/snapshots/tests__emit@calyx_xrt_dat.snap b/fud2/tests/snapshots/tests__test@calyx_xrt_dat.snap similarity index 93% rename from fud2/tests/snapshots/tests__emit@calyx_xrt_dat.snap rename to fud2/tests/snapshots/tests__test@calyx_xrt_dat.snap index b76a8f864f..5f68ee5341 100644 --- a/fud2/tests/snapshots/tests__emit@calyx_xrt_dat.snap +++ b/fud2/tests/snapshots/tests__test@calyx_xrt_dat.snap @@ -1,12 +1,11 @@ --- source: fud2/tests/tests.rs -description: emit calyx -> dat through xrt +description: "emit request: calyx -> dat through xrt" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Calyx compiler calyx-base = /test/calyx calyx-exe = $calyx-base/target/debug/calyx args = @@ -18,7 +17,6 @@ flags = -p none rule calyx-with-flags command = $calyx-exe -l $calyx-base $flags $args $in > $out -# Xilinx tools vivado-dir = /test/xilinx/vivado vitis-dir = /test/xilinx/vitis build gen_xo.tcl: get-rsrc @@ -33,7 +31,6 @@ rule compile-xclbin command = $vitis-dir/bin/v++ -g -t $xilinx-mode --platform $platform --save-temps --profile.data all:all:all --profile.exec all:all:all -lo $out $in pool = console -# RTL simulation python = python3 build json-dat.py: get-rsrc rule hex-data @@ -47,10 +44,8 @@ rule sim-run command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out cycle-limit = 500000000 -# Standalone Testbench Setup build tb.sv: get-rsrc -# Xilinx execution via XRT rule emconfig command = $vitis-dir/bin/emconfigutil --platform $platform build emconfig.json: emconfig @@ -65,7 +60,6 @@ build pre_sim.tcl: echo | build post_sim.tcl: echo | contents = close_vcd\\n -# build targets build main.sv: calyx stdin backend = verilog args = --synthesis -p external diff --git a/fud2/tests/snapshots/tests__emit@dahlia_calyx.snap b/fud2/tests/snapshots/tests__test@dahlia_calyx.snap similarity index 80% rename from fud2/tests/snapshots/tests__emit@dahlia_calyx.snap rename to fud2/tests/snapshots/tests__test@dahlia_calyx.snap index 03b0bf21b5..b4fb83da93 100644 --- a/fud2/tests/snapshots/tests__emit@dahlia_calyx.snap +++ b/fud2/tests/snapshots/tests__test@dahlia_calyx.snap @@ -1,17 +1,15 @@ --- source: fud2/tests/tests.rs -description: emit dahlia -> calyx +description: "emit request: dahlia -> calyx" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# Dahlia compiler dahlia-exe = /test/bin/dahlia rule dahlia-to-calyx command = $dahlia-exe -b calyx --lower -l error $in -o $out -# build targets build stdin.futil: dahlia-to-calyx stdin default stdin.futil diff --git a/fud2/tests/snapshots/tests__emit@mrxl_calyx.snap b/fud2/tests/snapshots/tests__test@mrxl_calyx.snap similarity index 78% rename from fud2/tests/snapshots/tests__emit@mrxl_calyx.snap rename to fud2/tests/snapshots/tests__test@mrxl_calyx.snap index 4002ab7f46..6516c15f8f 100644 --- a/fud2/tests/snapshots/tests__emit@mrxl_calyx.snap +++ b/fud2/tests/snapshots/tests__test@mrxl_calyx.snap @@ -1,17 +1,15 @@ --- source: fud2/tests/tests.rs -description: emit mrxl -> calyx +description: "emit request: mrxl -> calyx" --- build-tool = fud2 rule get-rsrc command = $build-tool get-rsrc $out -# MrXL compiler mrxl-exe = mrxl rule mrxl-to-calyx command = $mrxl-exe $in > $out -# build targets build stdin.futil: mrxl-to-calyx stdin default stdin.futil diff --git a/fud2/tests/snapshots/tests__test@plan_axi-wrapped.snap b/fud2/tests/snapshots/tests__test@plan_axi-wrapped.snap new file mode 100644 index 0000000000..98d04f45ed --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_axi-wrapped.snap @@ -0,0 +1,40 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: axi-wrapped" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 + +yxi = $calyx-base/target/debug/yxi +rule yxi + command = $yxi -l $calyx-base $in > $out + +axi-generator = $calyx-base/yxi/axi-calyx/axi-generator.py +python = python3 +rule gen-axi + command = $python $axi-generator $in > $out +rule combine + command = cat $in > $out +rule remove-imports + command = sed '1,/component main/{/component main/!d; }' $in > $out + +build input.yxi: yxi /input.ext +build refified_input.futil: calyx-pass /input.ext + pass = external-to-ref +build axi_wrapper.futil: gen-axi input.yxi +build no_imports_refified_input.futil: remove-imports refified_input.futil +build /output.ext: combine axi_wrapper.futil no_imports_refified_input.futil + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_calyx-noverify.snap b/fud2/tests/snapshots/tests__test@plan_calyx-noverify.snap new file mode 100644 index 0000000000..826c669cf1 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_calyx-noverify.snap @@ -0,0 +1,24 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: calyx-noverify" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 /output.ext: calyx /input.ext + backend = verilog + args = --disable-verify + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_calyx-to-cider.snap b/fud2/tests/snapshots/tests__test@plan_calyx-to-cider.snap new file mode 100644 index 0000000000..b39fb7066f --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_calyx-to-cider.snap @@ -0,0 +1,35 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: calyx-to-cider" +--- +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 cider-input.futil: calyx-with-flags /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_calyx-to-cocotb-axi.snap b/fud2/tests/snapshots/tests__test@plan_calyx-to-cocotb-axi.snap new file mode 100644 index 0000000000..59b6022570 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_calyx-to-cocotb-axi.snap @@ -0,0 +1,35 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: calyx-to-cocotb-axi" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 + +cocotb-makefile-dir = $calyx-base/yxi/axi-calyx/cocotb +sim_data = /test/data.json +rule copy + command = cp $in $out +rule make-cocotb + command = make DATA_PATH=$sim_data VERILOG_SOURCE=$in COCOTB_LOG_LEVEL=CRITICAL > $out +rule cleanup-cocotb + command = sed -n '/Output:/,/make\[1\]/{/Output:/d;/make\[1\]/d;p}' $in > $out + +build Makefile: copy $cocotb-makefile-dir/Makefile +build axi_test.py: copy $cocotb-makefile-dir/axi_test.py +build run_axi_test.py: copy $cocotb-makefile-dir/run_axi_test.py +build tmp.dat: make-cocotb /input.ext | Makefile axi_test.py run_axi_test.py +build /output.ext: cleanup-cocotb tmp.dat + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_calyx-to-firrtl.snap b/fud2/tests/snapshots/tests__test@plan_calyx-to-firrtl.snap new file mode 100644 index 0000000000..72960c1404 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_calyx-to-firrtl.snap @@ -0,0 +1,40 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: calyx-to-firrtl" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 + +rule ref-to-external + command = sed 's/ref /@external /g' $in > $out +rule external-to-ref + command = sed 's/@external([0-9]*)/ref/g' $in | sed 's/@external/ref/g' > $out +gen-testbench-script = $calyx-base/tools/firrtl/generate-testbench.py +build memories.sv: get-rsrc +rule generate-refmem-testbench + command = python3 $gen-testbench-script $in > $out +rule dummy + command = sh -c 'cat $$0' $in > $out + +build external.futil: ref-to-external /input.ext +build ref.futil: external-to-ref /input.ext +build memory-info.json: yxi external.futil +build tb.sv: generate-refmem-testbench memory-info.json +build tmp-out.fir: calyx ref.futil + backend = firrtl + args = --emit-primitive-extmodules +build /output.ext: dummy tmp-out.fir tb.sv + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_calyx-to-verilog.snap b/fud2/tests/snapshots/tests__test@plan_calyx-to-verilog.snap new file mode 100644 index 0000000000..2082b62aa2 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_calyx-to-verilog.snap @@ -0,0 +1,23 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: calyx-to-verilog" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 /output.ext: calyx /input.ext + backend = verilog + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_calyx-to-yxi.snap b/fud2/tests/snapshots/tests__test@plan_calyx-to-yxi.snap new file mode 100644 index 0000000000..50bf573b57 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_calyx-to-yxi.snap @@ -0,0 +1,26 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: calyx-to-yxi" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 + +yxi = $calyx-base/target/debug/yxi +rule yxi + command = $yxi -l $calyx-base $in > $out + +build /output.ext: yxi /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_cider.snap b/fud2/tests/snapshots/tests__test@plan_cider.snap new file mode 100644 index 0000000000..055ff2fac7 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_cider.snap @@ -0,0 +1,58 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: cider" +--- +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 + +cider-exe = $calyx-base/target/debug/cider +cider-converter = $calyx-base/target/debug/cider-data-converter +rule cider + command = $cider-exe -l $calyx-base --raw --data data.json $in > $out +rule cider-debug + command = $cider-exe -l $calyx-base --data data.json $in debug || true + pool = console +build interp-dat.py: get-rsrc +python = python3 +rule dat-to-interp + command = $python interp-dat.py --to-interp $in +rule interp-to-dat + command = $python interp-dat.py --from-interp $in $sim_data > $out +build data.json: dat-to-interp $sim_data | interp-dat.py +rule run-cider + command = $cider-exe -l $calyx-base --data data.dump $in flat > $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 interp_out.dump: run-cider cider-input.futil | data.dump +build /output.ext: interp-to-dump interp_out.dump | $sim_data $cider-converter + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_dahlia-to-calyx.snap b/fud2/tests/snapshots/tests__test@plan_dahlia-to-calyx.snap new file mode 100644 index 0000000000..b6ebd043e6 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_dahlia-to-calyx.snap @@ -0,0 +1,15 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: dahlia-to-calyx" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +dahlia-exe = /test/bin/dahlia +rule dahlia-to-calyx + command = $dahlia-exe -b calyx --lower -l error $in -o $out + +build /output.ext: dahlia-to-calyx /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_debug.snap b/fud2/tests/snapshots/tests__test@plan_debug.snap new file mode 100644 index 0000000000..13f7f949b1 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_debug.snap @@ -0,0 +1,59 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: 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 + +build tb.sv: get-rsrc + +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 + +cider-exe = $calyx-base/target/debug/cider +cider-converter = $calyx-base/target/debug/cider-data-converter +rule cider + command = $cider-exe -l $calyx-base --raw --data data.json $in > $out +rule cider-debug + command = $cider-exe -l $calyx-base --data data.json $in debug || true + pool = console +build interp-dat.py: get-rsrc +python = python3 +rule dat-to-interp + command = $python interp-dat.py --to-interp $in +rule interp-to-dat + command = $python interp-dat.py --from-interp $in $sim_data > $out +build data.json: dat-to-interp $sim_data | interp-dat.py +rule run-cider + command = $cider-exe -l $calyx-base --data data.dump $in flat > $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 /output.ext: cider-debug /input.ext | data.json + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_firrtl-noverify.snap b/fud2/tests/snapshots/tests__test@plan_firrtl-noverify.snap new file mode 100644 index 0000000000..63333b0431 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_firrtl-noverify.snap @@ -0,0 +1,19 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: firrtl-noverify" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +firrtl-exe = /test/bin/firrtl +rule firrtl + command = $firrtl-exe -i $in -o $out -X sverilog +build primitives-for-firrtl.sv: get-rsrc +rule add-verilog-primitives + command = cat primitives-for-firrtl.sv $in > $out + +build partial.sv: firrtl /input.ext +build /output.ext: add-verilog-primitives partial.sv | primitives-for-firrtl.sv + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-2.snap b/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-2.snap new file mode 100644 index 0000000000..ad304ba392 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-2.snap @@ -0,0 +1,18 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: firrtl-with-primitives" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +firrtl-exe = /test/bin/firrtl +rule firrtl + command = $firrtl-exe -i $in -o $out -X sverilog +build primitives-for-firrtl.sv: get-rsrc +rule add-verilog-primitives + command = cat primitives-for-firrtl.sv $in > $out + +build /output.ext: firrtl /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-noverify.snap b/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-noverify.snap new file mode 100644 index 0000000000..22171f9429 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives-noverify.snap @@ -0,0 +1,18 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: firrtl-with-primitives-noverify" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +firrtl-exe = /test/bin/firrtl +rule firrtl + command = $firrtl-exe -i $in -o $out -X sverilog +build primitives-for-firrtl.sv: get-rsrc +rule add-verilog-primitives + command = cat primitives-for-firrtl.sv $in > $out + +build /output.ext: firrtl /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives.snap b/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives.snap new file mode 100644 index 0000000000..8070441b5e --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_firrtl-with-primitives.snap @@ -0,0 +1,48 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: firrtl-with-primitives" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 + +gen-firrtl-primitives-script = $calyx-base/tools/firrtl/generate-firrtl-with-primitives.py +rule generate-firrtl-with-primitives + command = python3 $gen-firrtl-primitives-script $in > $out + +rule ref-to-external + command = sed 's/ref /@external /g' $in > $out +rule external-to-ref + command = sed 's/@external([0-9]*)/ref/g' $in | sed 's/@external/ref/g' > $out +gen-testbench-script = $calyx-base/tools/firrtl/generate-testbench.py +build memories.sv: get-rsrc +rule generate-refmem-testbench + command = python3 $gen-testbench-script $in > $out +rule dummy + command = sh -c 'cat $$0' $in > $out + +build external.futil: ref-to-external /input.ext +build ref.futil: external-to-ref /input.ext +build memory-info.json: yxi external.futil +build tb.sv: generate-refmem-testbench memory-info.json +build core.fir: calyx ref.futil + backend = firrtl + args = --synthesis +build primitive-uses.json: calyx ref.futil + backend = primitive-uses + args = --synthesis +build tmp-out.fir: generate-firrtl-with-primitives core.fir primitive-uses.json +build /output.ext: dummy tmp-out.fir tb.sv + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_firrtl.snap b/fud2/tests/snapshots/tests__test@plan_firrtl.snap new file mode 100644 index 0000000000..da2114436a --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_firrtl.snap @@ -0,0 +1,19 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: firrtl" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +firrtl-exe = /test/bin/firrtl +rule firrtl + command = $firrtl-exe -i $in -o $out -X sverilog +build primitives-for-firrtl.sv: get-rsrc +rule add-verilog-primitives + command = cat primitives-for-firrtl.sv $in > $out + +build partial.sv: firrtl /input.ext +build /output.ext: add-verilog-primitives partial.sv | primitives-for-firrtl.sv + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_icarus-refmem.snap b/fud2/tests/snapshots/tests__test@plan_icarus-refmem.snap new file mode 100644 index 0000000000..e136366e25 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_icarus-refmem.snap @@ -0,0 +1,30 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: icarus-refmem" +--- +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 + +iverilog = iverilog +rule icarus-compile-standalone-tb + command = $iverilog -g2012 -o $out tb.sv $in +rule icarus-compile-custom-tb + command = $iverilog -g2012 -o $out tb.sv memories.sv $in + +build /output.ext: icarus-compile-custom-tb /input.ext | tb.sv memories.sv + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_icarus.snap b/fud2/tests/snapshots/tests__test@plan_icarus.snap new file mode 100644 index 0000000000..b24cc2196e --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_icarus.snap @@ -0,0 +1,32 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: icarus" +--- +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 + +build tb.sv: get-rsrc + +iverilog = iverilog +rule icarus-compile-standalone-tb + command = $iverilog -g2012 -o $out tb.sv $in +rule icarus-compile-custom-tb + command = $iverilog -g2012 -o $out tb.sv memories.sv $in + +build /output.ext: icarus-compile-standalone-tb /input.ext | tb.sv + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_interp.snap b/fud2/tests/snapshots/tests__test@plan_interp.snap new file mode 100644 index 0000000000..d0dcaeaa16 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_interp.snap @@ -0,0 +1,60 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: interp" +--- +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 + +build tb.sv: get-rsrc + +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 + +cider-exe = $calyx-base/target/debug/cider +cider-converter = $calyx-base/target/debug/cider-data-converter +rule cider + command = $cider-exe -l $calyx-base --raw --data data.json $in > $out +rule cider-debug + command = $cider-exe -l $calyx-base --data data.json $in debug || true + pool = console +build interp-dat.py: get-rsrc +python = python3 +rule dat-to-interp + command = $python interp-dat.py --to-interp $in +rule interp-to-dat + command = $python interp-dat.py --from-interp $in $sim_data > $out +build data.json: dat-to-interp $sim_data | interp-dat.py +rule run-cider + command = $cider-exe -l $calyx-base --data data.dump $in flat > $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 interp_out.json: cider /input.ext | data.json +build /output.ext: interp-to-dat interp_out.json | $sim_data interp-dat.py + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_mrxl-to-calyx.snap b/fud2/tests/snapshots/tests__test@plan_mrxl-to-calyx.snap new file mode 100644 index 0000000000..8addc1809d --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_mrxl-to-calyx.snap @@ -0,0 +1,15 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: mrxl-to-calyx" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +mrxl-exe = mrxl +rule mrxl-to-calyx + command = $mrxl-exe $in > $out + +build /output.ext: mrxl-to-calyx /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_primitive-uses.snap b/fud2/tests/snapshots/tests__test@plan_primitive-uses.snap new file mode 100644 index 0000000000..b831f243c5 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_primitive-uses.snap @@ -0,0 +1,23 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: primitive-uses" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 /output.ext: calyx /input.ext + backend = primitive-uses + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_simulate.snap b/fud2/tests/snapshots/tests__test@plan_simulate.snap new file mode 100644 index 0000000000..fdcbbd590d --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_simulate.snap @@ -0,0 +1,27 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: simulate" +--- +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 + +build sim.log: sim-run /input.ext $datadir + bin = /input.ext + args = +NOTRACE=1 +build /output.ext: json-data $datadir sim.log | json-dat.py + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_trace.snap b/fud2/tests/snapshots/tests__test@plan_trace.snap new file mode 100644 index 0000000000..37cb1b881a --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_trace.snap @@ -0,0 +1,26 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: trace" +--- +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 + +build sim.log /output.ext: sim-run /input.ext $datadir + bin = /input.ext + args = +NOTRACE=0 +OUT=/output.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_verilator-refmem.snap b/fud2/tests/snapshots/tests__test@plan_verilator-refmem.snap new file mode 100644 index 0000000000..e217d36c24 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_verilator-refmem.snap @@ -0,0 +1,46 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: verilator-refmem" +--- +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 + +rule ref-to-external + command = sed 's/ref /@external /g' $in > $out +rule external-to-ref + command = sed 's/@external([0-9]*)/ref/g' $in | sed 's/@external/ref/g' > $out +gen-testbench-script = $calyx-base/tools/firrtl/generate-testbench.py +build memories.sv: get-rsrc +rule generate-refmem-testbench + command = python3 $gen-testbench-script $in > $out +rule dummy + command = sh -c 'cat $$0' $in > $out + +verilator = verilator +cycle-limit = 500000000 +rule verilator-compile-standalone-tb + command = $verilator $in tb.sv --trace --binary --top-module TOP -fno-inline -Mdir $out-dir +rule verilator-compile-custom-tb + command = $verilator $in tb.sv memories.sv --trace --binary --top-module TOP -fno-inline -Mdir $out-dir +rule cp + command = cp $in $out + +build verilator-out/VTOP: verilator-compile-custom-tb /input.ext | tb.sv memories.sv + out-dir = verilator-out +build /output.ext: cp verilator-out/VTOP + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_verilator.snap b/fud2/tests/snapshots/tests__test@plan_verilator.snap new file mode 100644 index 0000000000..ff6bf736ce --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_verilator.snap @@ -0,0 +1,37 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: verilator" +--- +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 + +build tb.sv: get-rsrc + +verilator = verilator +cycle-limit = 500000000 +rule verilator-compile-standalone-tb + command = $verilator $in tb.sv --trace --binary --top-module TOP -fno-inline -Mdir $out-dir +rule verilator-compile-custom-tb + command = $verilator $in tb.sv memories.sv --trace --binary --top-module TOP -fno-inline -Mdir $out-dir +rule cp + command = cp $in $out + +build verilator-out/VTOP: verilator-compile-standalone-tb /input.ext | tb.sv + out-dir = verilator-out +build /output.ext: cp verilator-out/VTOP + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_xclbin.snap b/fud2/tests/snapshots/tests__test@plan_xclbin.snap new file mode 100644 index 0000000000..c2be705dc0 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_xclbin.snap @@ -0,0 +1,25 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: xclbin" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +vivado-dir = /test/xilinx/vivado +vitis-dir = /test/xilinx/vitis +build gen_xo.tcl: get-rsrc +build get-ports.py: get-rsrc +python = python3 +rule gen-xo + command = $vivado-dir/bin/vivado -mode batch -source gen_xo.tcl -tclargs $out `$python get-ports.py kernel.xml` + pool = console +xilinx-mode = hw_emu +platform = xilinx_u50_gen3x16_xdma_201920_3 +rule compile-xclbin + command = $vitis-dir/bin/v++ -g -t $xilinx-mode --platform $platform --save-temps --profile.data all:all:all --profile.exec all:all:all -lo $out $in + pool = console + +build /output.ext: compile-xclbin /input.ext + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_xo.snap b/fud2/tests/snapshots/tests__test@plan_xo.snap new file mode 100644 index 0000000000..3255dbca6b --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_xo.snap @@ -0,0 +1,43 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: xo" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +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 + +vivado-dir = /test/xilinx/vivado +vitis-dir = /test/xilinx/vitis +build gen_xo.tcl: get-rsrc +build get-ports.py: get-rsrc +python = python3 +rule gen-xo + command = $vivado-dir/bin/vivado -mode batch -source gen_xo.tcl -tclargs $out `$python get-ports.py kernel.xml` + pool = console +xilinx-mode = hw_emu +platform = xilinx_u50_gen3x16_xdma_201920_3 +rule compile-xclbin + command = $vitis-dir/bin/v++ -g -t $xilinx-mode --platform $platform --save-temps --profile.data all:all:all --profile.exec all:all:all -lo $out $in + pool = console + +build main.sv: calyx /input.ext + backend = verilog + args = --synthesis -p external +build toplevel.v: calyx /input.ext + backend = xilinx +build kernel.xml: calyx /input.ext + backend = xilinx-xml +build /output.ext: gen-xo | main.sv toplevel.v kernel.xml gen_xo.tcl get-ports.py + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_xrt-trace.snap b/fud2/tests/snapshots/tests__test@plan_xrt-trace.snap new file mode 100644 index 0000000000..921110b2b0 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_xrt-trace.snap @@ -0,0 +1,56 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: xrt-trace" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +vivado-dir = /test/xilinx/vivado +vitis-dir = /test/xilinx/vitis +build gen_xo.tcl: get-rsrc +build get-ports.py: get-rsrc +python = python3 +rule gen-xo + command = $vivado-dir/bin/vivado -mode batch -source gen_xo.tcl -tclargs $out `$python get-ports.py kernel.xml` + pool = console +xilinx-mode = hw_emu +platform = xilinx_u50_gen3x16_xdma_201920_3 +rule compile-xclbin + command = $vitis-dir/bin/v++ -g -t $xilinx-mode --platform $platform --save-temps --profile.data all:all:all --profile.exec all:all:all -lo $out $in + pool = console + +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 + +build tb.sv: get-rsrc + +rule emconfig + command = $vitis-dir/bin/emconfigutil --platform $platform +build emconfig.json: emconfig +xrt-dir = /test/xilinx/xrt +rule xclrun + command = bash -c 'source $vitis-dir/settings64.sh ; source $xrt-dir/setup.sh ; XRT_INI_PATH=$xrt_ini EMCONFIG_PATH=. XCL_EMULATION_MODE=$xilinx-mode $python -m fud.xclrun --out $out $in' + pool = console +rule echo + command = echo $contents > $out +build pre_sim.tcl: echo | + contents = open_vcd\\nlog_vcd *\\n +build post_sim.tcl: echo | + contents = close_vcd\\n + +build xrt_trace.ini: get-rsrc +build /output.ext: xclrun /input.ext $sim_data | emconfig.json pre_sim.tcl post_sim.tcl xrt_trace.ini + xrt_ini = xrt_trace.ini + +default /output.ext diff --git a/fud2/tests/snapshots/tests__test@plan_xrt.snap b/fud2/tests/snapshots/tests__test@plan_xrt.snap new file mode 100644 index 0000000000..fd2d9433e6 --- /dev/null +++ b/fud2/tests/snapshots/tests__test@plan_xrt.snap @@ -0,0 +1,56 @@ +--- +source: fud2/tests/tests.rs +description: "emit plan: xrt" +--- +build-tool = fud2 +rule get-rsrc + command = $build-tool get-rsrc $out + +vivado-dir = /test/xilinx/vivado +vitis-dir = /test/xilinx/vitis +build gen_xo.tcl: get-rsrc +build get-ports.py: get-rsrc +python = python3 +rule gen-xo + command = $vivado-dir/bin/vivado -mode batch -source gen_xo.tcl -tclargs $out `$python get-ports.py kernel.xml` + pool = console +xilinx-mode = hw_emu +platform = xilinx_u50_gen3x16_xdma_201920_3 +rule compile-xclbin + command = $vitis-dir/bin/v++ -g -t $xilinx-mode --platform $platform --save-temps --profile.data all:all:all --profile.exec all:all:all -lo $out $in + pool = console + +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 + +build tb.sv: get-rsrc + +rule emconfig + command = $vitis-dir/bin/emconfigutil --platform $platform +build emconfig.json: emconfig +xrt-dir = /test/xilinx/xrt +rule xclrun + command = bash -c 'source $vitis-dir/settings64.sh ; source $xrt-dir/setup.sh ; XRT_INI_PATH=$xrt_ini EMCONFIG_PATH=. XCL_EMULATION_MODE=$xilinx-mode $python -m fud.xclrun --out $out $in' + pool = console +rule echo + command = echo $contents > $out +build pre_sim.tcl: echo | + contents = open_vcd\\nlog_vcd *\\n +build post_sim.tcl: echo | + contents = close_vcd\\n + +build xrt.ini: get-rsrc +build /output.ext: xclrun /input.ext $sim_data | emconfig.json xrt.ini + xrt_ini = xrt.ini + +default /output.ext diff --git a/fud2/tests/tests.rs b/fud2/tests/tests.rs index eafa1e2eef..f39b422643 100644 --- a/fud2/tests/tests.rs +++ b/fud2/tests/tests.rs @@ -1,14 +1,128 @@ -use fud2::build_driver; use fud_core::{ - config::default_config, exec::Request, run::Run, Driver, DriverBuilder, + config::default_config, + exec::{Plan, Request}, + run::Run, + Driver, DriverBuilder, }; +use itertools::Itertools; +#[cfg(not(feature = "migrate_to_scripts"))] fn test_driver() -> Driver { let mut bld = DriverBuilder::new("fud2"); - build_driver(&mut bld); + fud2::build_driver(&mut bld); bld.build() } +#[cfg(feature = "migrate_to_scripts")] +fn test_driver() -> Driver { + let mut bld = DriverBuilder::new("fud2-plugins"); + bld.scripts_dir(manifest_dir_macros::directory_path!("scripts")); + bld.load_plugins().build() +} + +trait InstaTest: Sized { + /// Get a human-readable description of Self + fn desc(&self, driver: &Driver) -> String; + + /// Get a short string uniquely identifying Self + fn slug(&self, driver: &Driver) -> String; + + /// Emit the string that will be snapshot tested + fn emit(self, driver: &Driver) -> String; + + /// Run snapshot test + fn test(self, driver: &Driver) { + let desc = self.desc(driver); + let slug = self.slug(driver); + let snapshot = self.emit(driver); + insta::with_settings!({ + description => desc, + omit_expression => true, + snapshot_suffix => format!("{slug}"), + }, { + insta::assert_snapshot!(snapshot); + }); + } +} + +impl InstaTest for Plan { + fn desc(&self, driver: &Driver) -> String { + let ops = self + .steps + .iter() + .map(|(opref, _path)| driver.ops[*opref].name.to_string()) + .collect_vec() + .join(" -> "); + format!("emit plan: {ops}") + } + + fn slug(&self, driver: &Driver) -> String { + let ops = self + .steps + .iter() + .map(|(opref, _path)| driver.ops[*opref].name.to_string()) + .collect_vec() + .join("_"); + format!("plan_{ops}") + } + + fn emit(self, driver: &Driver) -> String { + let config = default_config() + .merge(("exe", "fud2")) + .merge(("calyx.base", "/test/calyx")) + .merge(("firrtl.exe", "/test/bin/firrtl")) + .merge(("sim.data", "/test/data.json")) + .merge(("xilinx.vivado", "/test/xilinx/vivado")) + .merge(("xilinx.vitis", "/test/xilinx/vitis")) + .merge(("xilinx.xrt", "/test/xilinx/xrt")) + .merge(("dahlia", "/test/bin/dahlia")); + let run = Run::with_config(driver, self, config); + let mut buf = vec![]; + run.emit(&mut buf).unwrap(); + // turn into string, and remove comments + String::from_utf8(buf) + .unwrap() + .lines() + .filter(|line| !line.starts_with('#')) + .collect::>() + .join("\n") + } +} + +impl InstaTest for Request { + fn desc(&self, driver: &Driver) -> String { + let mut desc = format!( + "emit request: {} -> {}", + driver.states[self.start_state].name, + driver.states[self.end_state].name + ); + if !self.through.is_empty() { + desc.push_str(" through"); + for op in &self.through { + desc.push(' '); + desc.push_str(&driver.ops[*op].name); + } + } + desc + } + + fn slug(&self, driver: &Driver) -> String { + let mut desc = driver.states[self.start_state].name.to_string(); + for op in &self.through { + desc.push('_'); + desc.push_str(&driver.ops[*op].name); + } + desc.push('_'); + desc.push_str(&driver.states[self.end_state].name); + desc + } + + fn emit(self, driver: &Driver) -> String { + let plan = driver.plan(self).unwrap(); + plan.emit(driver) + } +} + fn request( driver: &Driver, start: &str, @@ -25,77 +139,69 @@ fn request( } } -fn emit_ninja(driver: &Driver, req: Request) -> String { - let plan = driver.plan(req).unwrap(); - let config = default_config() - .merge(("exe", "fud2")) - .merge(("calyx.base", "/test/calyx")) - .merge(("firrtl.exe", "/test/bin/firrtl")) - .merge(("sim.data", "/test/data.json")) - .merge(("xilinx.vivado", "/test/xilinx/vivado")) - .merge(("xilinx.vitis", "/test/xilinx/vitis")) - .merge(("xilinx.xrt", "/test/xilinx/xrt")) - .merge(("dahlia", "/test/bin/dahlia")); - let run = Run::with_config(driver, plan, config); - let mut buf = vec![]; - run.emit(&mut buf).unwrap(); - String::from_utf8(buf).unwrap() -} - -/// Get a human-readable description of a request. -fn req_desc(driver: &Driver, req: &Request) -> String { - let mut desc = format!( - "emit {} -> {}", - driver.states[req.start_state].name, driver.states[req.end_state].name - ); - if !req.through.is_empty() { - desc.push_str(" through"); - for op in &req.through { - desc.push(' '); - desc.push_str(&driver.ops[*op].name); - } +#[test] +fn all_ops() { + let driver = test_driver(); + for op in driver.ops.keys() { + let plan = Plan { + start: "/input.ext".into(), + steps: vec![(op, "/output.ext".into())], + workdir: ".".into(), + stdin: false, + stdout: false, + }; + plan.test(&driver); } - desc } -/// Get a short string uniquely identifying a request. -fn req_slug(driver: &Driver, req: &Request) -> String { - let mut desc = driver.states[req.start_state].name.to_string(); - for op in &req.through { - desc.push('_'); - desc.push_str(&driver.ops[*op].name); - } - desc.push('_'); - desc.push_str(&driver.states[req.end_state].name); - desc +#[test] +fn list_states() { + let driver = test_driver(); + let states = driver + .states + .values() + .map(|state| &state.name) + .sorted() + .collect::>(); + insta::with_settings!({ + omit_expression => true + }, { + insta::assert_debug_snapshot!(states) + }); } -fn test_emit(driver: &Driver, req: Request) { - let desc = req_desc(driver, &req); - let slug = req_slug(driver, &req); - let ninja = emit_ninja(driver, req); +#[test] +fn list_ops() { + let driver = test_driver(); + let ops = driver + .ops + .values() + .map(|op| { + ( + &op.name, + &driver.states[op.input].name, + &driver.states[op.output].name, + ) + }) + .sorted() + .collect::>(); insta::with_settings!({ - description => desc, - omit_expression => true, - snapshot_suffix => slug, + omit_expression => true }, { - insta::assert_snapshot!(ninja); + insta::assert_debug_snapshot!(ops) }); } #[test] fn calyx_to_verilog() { let driver = test_driver(); - test_emit(&driver, request(&driver, "calyx", "verilog", &[])); + request(&driver, "calyx", "verilog", &[]).test(&driver); } #[test] fn calyx_via_firrtl() { let driver = test_driver(); - test_emit( - &driver, - request(&driver, "calyx", "verilog-refmem", &["firrtl"]), - ); + request(&driver, "calyx", "verilog-refmem", &["firrtl"]).test(&driver); } #[test] @@ -103,7 +209,7 @@ fn sim_tests() { let driver = test_driver(); for dest in &["dat", "vcd"] { for sim in &["icarus", "verilator"] { - test_emit(&driver, request(&driver, "calyx", dest, &[sim])); + request(&driver, "calyx", dest, &[sim]).test(&driver); } } } @@ -111,21 +217,21 @@ fn sim_tests() { #[test] fn cider_tests() { let driver = test_driver(); - test_emit(&driver, request(&driver, "calyx", "dat", &["interp"])); - test_emit(&driver, request(&driver, "calyx", "debug", &[])); + request(&driver, "calyx", "dat", &["interp"]).test(&driver); + request(&driver, "calyx", "debug", &[]).test(&driver); } #[test] fn xrt_tests() { let driver = test_driver(); - test_emit(&driver, request(&driver, "calyx", "dat", &["xrt"])); - test_emit(&driver, request(&driver, "calyx", "vcd", &["xrt-trace"])); + request(&driver, "calyx", "dat", &["xrt"]).test(&driver); + request(&driver, "calyx", "vcd", &["xrt-trace"]).test(&driver); } #[test] fn frontend_tests() { let driver = test_driver(); for frontend in &["dahlia", "mrxl"] { - test_emit(&driver, request(&driver, frontend, "calyx", &[])); + request(&driver, frontend, "calyx", &[]).test(&driver); } }