Skip to content

Commit

Permalink
feat(verilator): Support dynamically-created model bindings (#32)
Browse files Browse the repository at this point in the history
* feat(verilator): Support dynamically-created model bindings

* ci: Test dynamic models in CI

* ci: Fix CI

* docs: Write docs for dynamic models

* docs: Link to new tutorial in README

* docs: Update README features

* ci: Use newer Spade
  • Loading branch information
ethanuppal authored Feb 3, 2025
1 parent a248353 commit a11fff2
Show file tree
Hide file tree
Showing 15 changed files with 773 additions and 239 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ jobs:
- name: Test
run: |
cd verilog-support/example-project
cargo run -p example-verilog-project
cargo run -p example-verilog-project # rerun
cargo run --package example-verilog-project --bin tutorial
cargo run --package example-verilog-project --bin tutorial # rerun
cargo run --package example-verilog-project --bin dynamic_model
test_spade:
strategy:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target/
**/artifacts/
**/artifacts2/
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Still, a lot of these are less than optimal.

- 🚀 Minimal overhead over directly using `verilator`
- 🔌 Works completely drop-in in your existing projects
- 🪙 Declarative API for usability + Dynamic API for programmability
- 🦀 Rust. Did I say Rust?

## ⚡️ Requirements
Expand All @@ -68,6 +69,7 @@ I'll write more documentation once I get further in the development process.

- [Testing a Verilog project](./docs/testing_verilog.md)
- [Testing a Spade project](./docs/testing_spade.md)
- [Using dynamic Verilog models](./docs/verilog_dynamic_models.md)

## 💡 How it works

Expand Down
4 changes: 3 additions & 1 deletion docs/testing_verilog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> This tutorial is aimed at Unix-like systems like macOS, Linux, and WSL.
In this tutorial, we'll setup a SystemVerilog project and test our code with
dumbname. You can find the full source code for this tutorial [here](../verilog-support/example-project/). We won't touch on the advanced aspects or features; the goal is just to provide a simple overfiew sufficient to get started.
dumbname. You can find the full source code for this tutorial [here](../verilog-support/example-project/) (in the `tutorial.rs` file). We won't touch on the advanced aspects or features; the goal is just to provide a simple overfiew sufficient to get started.

## Part 1: The Basics

Expand Down Expand Up @@ -58,6 +58,8 @@ colog = "1.3.0" # optional, whatever version

The only required package is `verilog` from dumbname; everything else is just
for fun.
It's a good idea to fix a particular revision at this stage of development (and
make sure to update it frequently insofar as it doesn't break your code!).

Finally, we'll want to actually write the code that drives our project in `main.rs`:

Expand Down
113 changes: 113 additions & 0 deletions docs/verilog_dynamic_models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

# Using dynamic Verilog models

> [!NOTE]
> This tutorial is aimed at Unix-like systems like macOS, Linux, and WSL.
In this tutorial, we'll explore how to use dumbname to dynamically create
bindings to Verilog modules.
You can find the full source code for this tutorial [here](../verilog-support/example-project/) (in the `dyamic_model.rs` file).

I'll be assuming you've read the [tutorial on testing Verilog projects](./testing_verilog.md); if not, read that first and come back.
In particular, I won't be reexplaining things I discussed in that tutorial,
although I will still walk through the entire setup.

## Part 1: The Basics

Let's call our project "tutorial-project-2" (you are free to call it however you
like):
```shell
mkdir tutorial-project-2
cd tutorial-project-2
git init # optional, if using git
```

Let's use the same SystemVerilog module from the [Verilog tutorial](./testing_verilog.md).
```shell
mkdir sv
vi sv/main.sv
```

```systemverilog
// file: sv/main.sv
module main(
input[31:0] medium_input,
output[31:0] medium_output
);
assign medium_output = medium_input;
endmodule
```

## Part 2: Testing

We'll create a new Rust project:
```shell
cargo init --bin .
```

Next, we'll add dumbname and other desired dependencies.
```toml
# file: Cargo.toml
[dependencies]
# other dependencies...
verilog = { git = "https://github.com/ethanuppal/dumbname" }
snafu = "0.8.5" # optional, whatever version
colog = "1.3.0" # optional, whatever version
```

The code for dynamic models is slightly more verbose.
It's not necessarily meant for human usage, though; this API is better suited for
using dumbname as a library (e.g., writing an interpreter).

```rust
// file: src/main.rs
use snafu::Whatever;
use verilog::{verilog, VerilatorRuntime};

#[verilog(src = "sv/main.sv", name = "main")]
struct Main;

#[snafu::report]
fn main() -> Result<(), Whatever> {
colog::init();

let mut runtime = VerilatorRuntime::new(
"artifacts2".into(),
&["sv/main.sv".as_ref()],
true,
)?;

let mut main = runtime.create_dyn_model(
"main",
"sv/sv.main",
&[
("medium_input", 31, 0, PortDirection::Input),
("medium_output", 31, 0, PortDirection::Output),
],
)?;

main.pin("medium_input", u32::MAX).whatever_context("pin")?;
println!("{}", main.read("medium_output").whatever_context("read")?);
assert_eq!(
main.read("medium_output").whatever_context("read")?,
0u32.into()
);
main.eval();
println!("{}", main.read("medium_output").whatever_context("read")?);
assert_eq!(
main.read("medium_output").whatever_context("read")?,
u32::MAX.into()
);

Ok(())
}
```

We can `cargo run` as usual to test.

Make sure you pass in the correct filename to `create_dyn_model`.
You only need to pass in a correct _subset_ of the ports.

One current issue is that if you use multiple dynamic models, since models are
lazy-built and cached, omitting ports in the first `create_dyn_model` for a
given module means that no later model can access those omitted ports.
2 changes: 1 addition & 1 deletion spade-support/example-project/swim.lock
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[spade]
commit = "0c96a2248fe80b550430be86d3efc1dc9cd15c6f"
commit = "fd914099e8def300f269c033694afa0e6dfe6ce7"
1 change: 1 addition & 0 deletions verilator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ snafu.workspace = true
camino.workspace = true
libloading.workspace = true
log.workspace = true
libc.workspace = true
Loading

0 comments on commit a11fff2

Please # to comment.