Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: CI

on:
push:
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- name: Cache cargo build
uses: Swatinem/rust-cache@v2

- name: Install nextest
uses: taiki-e/install-action@nextest

- name: Install external simulator and FPGA toolchain
run: |
sudo apt-get update
sudo apt-get install -y iverilog yosys nextpnr-ice40 fpga-icestorm

- name: Format check
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --workspace --all-targets

- name: Tests
run: cargo nextest run --workspace --no-fail-fast

- name: Doctests
run: cargo test --doc --workspace --no-fail-fast
26 changes: 26 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# List available recipes
default:
@just --list

# Run everything CI runs (fmt-check + clippy + test + doctest)
ci: fmt-check clippy test doctest

# Verify the codebase is formatted (fails if not)
fmt-check:
cargo fmt --all -- --check

# Reformat the codebase in place
fmt:
cargo fmt --all

# Run clippy lints across the workspace
clippy:
cargo clippy --workspace --all-targets

# Run the workspace test suite via nextest (faster, parallelizes test binaries)
test:
cargo nextest run --workspace --no-fail-fast

# Run doctests (nextest does not handle these)
doctest:
cargo test --doc --workspace --no-fail-fast
1 change: 1 addition & 0 deletions crates/rhdl-bsp/src/drivers/lattice/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

14 changes: 10 additions & 4 deletions crates/rhdl-core/src/circuit/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,28 @@ pin y|Driver+------->|7|<------+
//! It allows you to specify paths on the input and output of the circuit,
//! and automatically creates the necessary drivers and mount points.
//!
//# Example
//!```rust
//! # Example
//!
//! See [`rhdl::prelude::bind`](../../../rhdl/prelude/macro.bind.html) for a runnable
//! example. The `#[kernel]` attribute and `bind!` macro live in the `rhdl`
//! crate, so the doctest itself cannot be exercised from `rhdl-core`. The
//! sketch below shows the same wiring for reference:
//!
//!```ignore
//!use rhdl::prelude::*;
//!
//!#[kernel]
//!fn adder(a: Signal<(b4, b4), Red>) -> Signal<b4, Red> {
//! let (a, b) = a.val();
//! signal(a + b) // Return signal with value
//! signal(a + b)
//!}
//!
//!let adder = AsyncFunc::new::<adder>()?;
//!let mut fixture = Fixture::new("adder_top", adder);
//!bind!(fixture, a -> input.val().0);
//!bind!(fixture, b -> input.val().1);
//!bind!(fixture, sum -> output.val());
//!let vlog = fixture.module()?;
//!let vlog = fixture.module()?;
//!```
//!
//! This example creates a top level fixture that looks like this:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
error::RHDLError,
rhif::{
spec::{Cast, OpCode},
Object,
spec::{Cast, OpCode},
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::{
compiler::mir::error::ICE,
error::RHDLError,
rhif::{
spec::{OpCode, Retime},
Object,
spec::{OpCode, Retime},
},
};

Expand Down
4 changes: 2 additions & 2 deletions crates/rhdl-core/src/sim/iter/uniform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ where
///
/// ```rust
/// # use rhdl_bits::alias::b4;
/// # use rhdl_core::sim::uniform::uniform;
/// # use rhdl_core::sim::iter::uniform::uniform;
///
/// let samples = vec![b4(1), b4(2), b4(3)];
/// let uniform_samples = uniform(samples.into_iter(), 100);
Expand Down Expand Up @@ -87,7 +87,7 @@ where
///
/// ```rust
/// # use rhdl_bits::alias::b4;
/// # use rhdl_core::sim::uniform::UniformExt;
/// # use rhdl_core::sim::iter::uniform::UniformExt;
///
/// let samples = (1..).take(5).map(b4);
/// let uniform_samples = samples.uniform(100);
Expand Down
6 changes: 4 additions & 2 deletions crates/rhdl-core/src/trace/container/svg/gap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,13 @@ mod tests {
[
22..=28,
]
"#]].assert_debug_eq(&intervals);
"#]]
.assert_debug_eq(&intervals);
let intervals = gaps.break_interval_at_gaps(&(35..=39));
expect_test::expect![[r#"
[]
"#]].assert_debug_eq(&intervals);
"#]]
.assert_debug_eq(&intervals);
}

#[test]
Expand Down
16 changes: 16 additions & 0 deletions crates/rhdl-fpga/tests/faulty_reducer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,22 @@ fn test_no_combinatorial_paths() -> miette::Result<()> {
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor());
let mut msg = String::new();
handler.render_report(&mut msg, err.as_ref()).unwrap();
let msg = strip_workspace_root(&msg);
expect_test::expect_file!["faulty_reducer_no_combinatorial_paths.expect"].assert_eq(&msg);
Ok(())
}

// Removes the absolute workspace-root prefix from any embedded file paths,
// so snapshot files are stable across checkouts on different machines.
fn strip_workspace_root(s: &str) -> String {
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(|p| p.parent())
.map(|p| p.display().to_string())
.unwrap_or_default();
if workspace_root.is_empty() {
s.to_string()
} else {
s.replace(&format!("{workspace_root}/"), "")
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
× RHDL Combinatorial Path
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl-fpga/tests/faulty_reducer.rs:85:19]
╭─[crates/rhdl-fpga/tests/faulty_reducer.rs:85:19]
84 │ let (in_valid, in_data) = unpack::<Bits<DW>>(i.data, bits(0));
85 │ let stop_in = !i.ready;
· ────────
Expand Down
23 changes: 15 additions & 8 deletions crates/rhdl-toolchains/src/vivado/tcl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,15 @@ impl CreateIp {

impl std::fmt::Display for CreateIp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "create_ip -name {name} -vendor {vendor} -library {library} -version {version} -module_name {module_name}",
name = self.name,
vendor = self.vendor,
library = self.library,
version = self.version,
module_name = self.module_name)
write!(
f,
"create_ip -name {name} -vendor {vendor} -library {library} -version {version} -module_name {module_name}",
name = self.name,
vendor = self.vendor,
library = self.library,
version = self.version,
module_name = self.module_name
)
}
}

Expand Down Expand Up @@ -245,15 +248,19 @@ impl std::fmt::Display for Cell {
)?;
}
for connection in &self.pin_connections {
writeln!(f, "connect_bd_net [get_bd_pins {name}/{local_port}] [get_bd_pins {remote_node}/{remote_port}]",
writeln!(
f,
"connect_bd_net [get_bd_pins {name}/{local_port}] [get_bd_pins {remote_node}/{remote_port}]",
name = self.name,
local_port = connection.local_port,
remote_node = connection.remote_node,
remote_port = connection.remote_port
)?;
}
for connection in &self.interface_connections {
writeln!(f, "connect_bd_intf_net [get_bd_intf_pins {name}/{local_port}] [get_bd_intf_pins {remote_node}/{remote_port}]",
writeln!(
f,
"connect_bd_intf_net [get_bd_intf_pins {name}/{local_port}] [get_bd_intf_pins {remote_node}/{remote_port}]",
name = self.name,
local_port = connection.local_port,
remote_node = connection.remote_node,
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl-vlog/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//!
//! ```rust
//! use rhdl_vlog::*;
//! use quote::quote;
//! use syn::parse_quote;
//!
//! let module: ModuleDef = parse_quote! {
//! module my_module (input wire a, input wire b, output wire c);
Expand Down
8 changes: 6 additions & 2 deletions crates/rhdl/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub use rhdl_core::circuit::fixture::Fixture;
pub use rhdl_core::circuit::fixture::MountPoint;
pub use rhdl_core::circuit::fixture::passthrough_input_driver;
pub use rhdl_core::circuit::fixture::passthrough_output_driver;
pub use rhdl_core::circuit::function::asynchronous::AsyncFunc;
pub use rhdl_core::circuit::scoped_name::ScopedName;
pub use rhdl_core::const_max;
pub use rhdl_core::ntl::builder::circuit_black_box;
Expand Down Expand Up @@ -133,13 +134,16 @@ pub use rhdl_vlog::parse_quote_miette;
/// signal(a + b) // Return signal with value
///}
///
///# fn main() -> Result<(), Box<dyn std::error::Error>> {
///let adder = AsyncFunc::new::<adder>()?;
///let mut fixture = Fixture::new("adder_top", adder);
///let (input, output) = fixture.io();
///let (input, output) = fixture.io_dont_care();
///bind!(fixture, a -> input.val().0);
///bind!(fixture, b -> input.val().1);
///bind!(fixture, sum <- output.val());
///let vlog = fixture.module()?;
///let _vlog = fixture.module()?;
///# Ok(())
///# }
///```
/// When exported as Verilog, the fixture will look like this:
///
Expand Down
1 change: 1 addition & 0 deletions crates/rhdl/tests/array_circuit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

17 changes: 16 additions & 1 deletion crates/rhdl/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,20 @@ pub fn miette_report(err: RHDLError) -> String {
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor());
let mut msg = String::new();
handler.render_report(&mut msg, &err).unwrap();
msg
strip_workspace_root(&msg)
}

// Removes the absolute workspace-root prefix from any embedded file paths,
// so snapshot files are stable across checkouts on different machines.
pub fn strip_workspace_root(s: &str) -> String {
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(|p| p.parent())
.map(|p| p.display().to_string())
.unwrap_or_default();
if workspace_root.is_empty() {
s.to_string()
} else {
s.replace(&format!("{workspace_root}/"), "")
}
}
3 changes: 2 additions & 1 deletion crates/rhdl/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ fn test_derive_digital_complex_enum() {
let manifest_dir = std::env!("CARGO_MANIFEST_DIR");
let vcd_path = std::path::Path::new(manifest_dir).join("tests/expect/vcd_ng_enum.vcd");
let hash = vcd_file.dump_to_file(&vcd_path).unwrap();
expect_test::expect!["4975c2d1c4c686d797a316de76445703538e2a888ed73bd36a3e88b65ad0140b"].assert_eq(&hash);
expect_test::expect!["4975c2d1c4c686d797a316de76445703538e2a888ed73bd36a3e88b65ad0140b"]
.assert_eq(&hash);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/Add_binop_fails.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/type_rolling.rs:32:25]
╭─[crates/rhdl/tests/type_rolling.rs:32:25]
31 │ let h = h.val();
32 │ let j = h $op h;
· ───┬───
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/BitAnd_binop_fails.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/type_rolling.rs:32:25]
╭─[crates/rhdl/tests/type_rolling.rs:32:25]
31 │ let h = h.val();
32 │ let j = h $op h;
· ───┬───
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/BitOr_binop_fails.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/type_rolling.rs:32:25]
╭─[crates/rhdl/tests/type_rolling.rs:32:25]
31 │ let h = h.val();
32 │ let j = h $op h;
· ───┬───
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/BitXor_binop_fails.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/type_rolling.rs:32:25]
╭─[crates/rhdl/tests/type_rolling.rs:32:25]
31 │ let h = h.val();
32 │ let j = h $op h;
· ───┬───
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/Mul_binop_fails.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/type_rolling.rs:32:25]
╭─[crates/rhdl/tests/type_rolling.rs:32:25]
31 │ let h = h.val();
32 │ let j = h $op h;
· ───┬───
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/Sub_binop_fails.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/type_rolling.rs:32:25]
╭─[crates/rhdl/tests/type_rolling.rs:32:25]
31 │ let h = h.val();
32 │ let j = h $op h;
· ───┬───
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Type Check Error
╰─▶ RHDL Type Check Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/dyn_bits.rs:187:17]
╭─[crates/rhdl/tests/dyn_bits.rs:187:17]
186 │ let c = a1.xadd(a2);
187 │ let d = c.xadd(b4(1));
· ──────┬──────┬┬
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Clock Domain Violation
╰─▶ RHDL Clock Domain Violation
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/clock.rs:485:17]
╭─[crates/rhdl/tests/clock.rs:485:17]
484 │ let b = b.val();
485 │ let c = [a, b, a];
· ────┬────┬┬
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Clock Domain Violation
╰─▶ RHDL Clock Domain Violation
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/clock.rs:467:9]
╭─[crates/rhdl/tests/clock.rs:467:9]
466 │ let b = b.val();
467 │ signal([b; 3])
· ───────┬──────┬┬
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/empty_return_not_allowed.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Syntax Error
╰─▶ RHDL Syntax Error
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/returns.rs:68:5]
╭─[crates/rhdl/tests/returns.rs:68:5]
67 │ #[kernel]
68 │ fn foo(_a: bool) -> () {}
· ────────────┬────────────
Expand Down
2 changes: 1 addition & 1 deletion crates/rhdl/tests/expect/enum_basic_cross_clocks.expect
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
× RHDL Clock Domain Violation
╰─▶ RHDL Clock Domain Violation
╭─[/Users/samitbasu/Devel/rhdl/crates/rhdl/tests/clock.rs:449:17]
╭─[crates/rhdl/tests/clock.rs:449:17]
448 │ let b = b.val();
449 │ let c = if b { Foo::A } else { Foo::B(a) };
· ─────────────────┬────────────────┬┬┬
Expand Down
Loading