10 releases (4 breaking)

Uses new Rust 2024

new 0.5.0 Mar 24, 2025
0.4.0 Mar 18, 2025
0.3.4 Mar 11, 2025
0.2.1 Feb 24, 2025
0.1.0 Feb 9, 2025

#817 in Hardware support

Download history 146/week @ 2025-02-09 190/week @ 2025-02-16 169/week @ 2025-02-23 289/week @ 2025-03-02 377/week @ 2025-03-09 196/week @ 2025-03-16

1,064 downloads per month
Used in 8 crates

MPL-2.0 license

59KB
1K SLoC

marlin 🐟

CI Badge Code Style Badge cargo-deny badge Crates.io Version docs.rs Crates.io License

Read the documentation | Read the API reference

Marlin is a really powerful library (and API) that lets you "import" hardware modules into Rust (or Rust functions into hardware modules!).

No precompilation step and manual updates with verilator harnesses; no Makefiles and quirky decorators with cocotb testbenches. You're writing a regular Rust crate here.

Add this library to your Cargo.toml like any other library. Use hardware modules as structs like any other Rust struct. Hook them up to tokio or serde even. cargo test as hard as you want.

Marlin works out of the box on macOS and Linux (verified under continuous integration).

Early example of using this with Spade

[!NOTE] The above screenshot is pre-0.1.0, so it's a bit out of date -- Marlin has improved a lot since then!

🔥 Motivation

Why does hardware testing suck? Consider the ways we have to test (System)Verilog:

  • Test natively: Verilog is already a terrible enough language, and writing tests in Verilog is really annoying.
  • Use Verilator harnesses: You have to first run Verilator to get the right headers, recompile manually every time, deal with raw pointers and C++, etc.
  • Use cocotb: You have to use Makefiles or write your own Python runner, performance isn't the greatest, you get no LSP support for ports, etc.

The problem gets worse with custom HDLs, so they've come up with some creative solutions:

Still, a lot of these are less than optimal.

✨ Features

  • 🚀 Minimal overhead over directly using verilator
  • 🔌 Works completely drop-in in your existing projects
  • 📈 Safe VCD tracing support
  • 🪙 Declarative API for usability + Dynamic API for programmability
  • 🔄 DPI support in Rust: call Rust functions from (System)Verilog
  • 🦀 Rust. Did I say Rust?

⚡️ Requirements

📦 Install

Marlin is on crates.io, so just use cargo add --dev marlin to add Marlin as a dependency for your tests (dev-dependencies).

❓ How it works

I'll write more on this once I get further in the development process. The TLDR is procedural macros + dlopen.

Hardware simulation tools are slow! How does Marlin deal with that?

Simulation tools take an absurd amount of time to run. For example, when you use Marlin in a Spade project, it calls out to:

  • swim build, which recompiles the entire Spade compiler from source
  • verilator, which compiles and links C++ code

Marlin automatically runs them with all the right flags and arguments --- and it caches and only invokes them when needed.

  • verilated-rs is a super cool library that uses a build script to statically link in verilated bindings, but is unmaintained for years as of writing this.

Marlin is licensed under the Mozilla Public License 2.0. This license is similar to the Lesser GNU Public License, except that the copyleft applies only to the source code of this library, not any library that uses it. That means you can statically or dynamically link with unfree code (see https://www.mozilla.org/en-US/MPL/2.0/FAQ/#virality).

I use cargo-deny (see the deny.toml to ensure no licensing violations occur. I also check this on CI to prevent merging any new dependencies or dependency updates that introduce incompatible licenses.

Verilator

Verilator is licensed under the Lesser GNU General Public License 3.0. However, Marlin will dlopen Verilated code, which is permitted via this clause:

  1. Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.

Through VerilatorRuntimeOptions::verilator_executable, you can specify your own interface-compatible Verilator wrapper, enabling (b).

Dependencies

~2–25MB
~338K SLoC