wavepeek 0.5.0

Command-line tool for RTL waveform inspection with deterministic machine-friendly output.
Documentation
mod support;

use std::hint::black_box;

use criterion::{Criterion, criterion_group, criterion_main};

use crate::support::{
    BenchHost, BenchSignal, bit_ty, criterion_config, event_ty, integral_signal, real_ty, string_ty,
};
use wavepeek::expr::{
    EventEvalFrame, SampledValue, bind_event_expr_ast, event_matches_at, parse_event_expr_ast,
};

fn event_host() -> BenchHost {
    let mut host = BenchHost::new();
    host.insert_signal(
        "clk",
        1,
        integral_signal(
            bit_ty(1, true, false),
            vec![
                (0, "0"),
                (5, "1"),
                (8, "0"),
                (10, "1"),
                (12, "0"),
                (15, "1"),
            ],
        ),
    );
    host.insert_signal(
        "rstn",
        2,
        integral_signal(bit_ty(1, true, false), vec![(0, "1"), (10, "0")]),
    );
    host.insert_signal(
        "count",
        3,
        integral_signal(
            bit_ty(8, true, false),
            vec![(10, "00000010"), (15, "00000011")],
        ),
    );
    host.insert_signal(
        "flag",
        4,
        integral_signal(bit_ty(4, true, false), vec![(10, "0000"), (15, "0001")]),
    );
    host.insert_signal(
        "ready",
        5,
        integral_signal(bit_ty(1, true, false), vec![(0, "0"), (7, "1")]),
    );
    host.insert_signal(
        "data",
        6,
        integral_signal(
            bit_ty(8, true, false),
            vec![(0, "00000000"), (10, "00001111")],
        ),
    );
    host.insert_signal(
        "x_sig",
        7,
        integral_signal(bit_ty(1, true, false), vec![(0, "x")]),
    );
    host.insert_signal(
        "a",
        8,
        integral_signal(
            bit_ty(8, true, false),
            vec![(0, "11001010"), (30, "11110000")],
        ),
    );
    host.insert_signal(
        "temp",
        9,
        BenchSignal::new(
            real_ty(),
            vec![(0, SampledValue::Real { value: Some(1.5) })],
        ),
    );
    host.insert_signal(
        "msg",
        10,
        BenchSignal::new(
            string_ty(),
            vec![(
                0,
                SampledValue::String {
                    value: Some("go".to_string()),
                },
            )],
        ),
    );
    host.insert_signal(
        "ev",
        11,
        BenchSignal::with_events(event_ty(), vec![], vec![10]),
    );
    host
}

fn bench_bind_event_union_iff(c: &mut Criterion) {
    let source = "posedge clk iff (!rstn && count >= 8'd3) or edge data";
    let ast = parse_event_expr_ast(source).expect("bind bench source should parse");
    let host = event_host();

    c.bench_function("event__bind_event_union_iff", |b| {
        b.iter(|| {
            let bound = bind_event_expr_ast(black_box(&ast), black_box(&host))
                .expect("bind_event_union_iff must bind");
            black_box(bound);
        })
    });
}

fn bench_eval_event_union_iff_true(c: &mut Criterion) {
    let source = "posedge clk iff (!rstn && (count >= 8'd3) || flag == 'h1) or ready";
    let host = event_host();
    let ast = parse_event_expr_ast(source).expect("eval true source should parse");
    let bound = bind_event_expr_ast(&ast, &host).expect("eval true source should bind");
    let tracked = [host.handle("clk"), host.handle("ready")];
    let frame = EventEvalFrame {
        timestamp: 15,
        previous_timestamp: Some(12),
        tracked_signals: tracked.as_slice(),
    };
    assert!(
        event_matches_at(&bound, &host, &frame).expect("eval true setup must evaluate"),
        "eval_event_union_iff_true should match"
    );

    c.bench_function("event__eval_event_union_iff_true", |b| {
        b.iter(|| {
            let matched = event_matches_at(black_box(&bound), black_box(&host), black_box(&frame))
                .expect("eval_event_union_iff_true must evaluate");
            black_box(matched);
        })
    });
}

fn bench_eval_event_union_iff_unknown(c: &mut Criterion) {
    let source = "posedge clk iff (x_sig && 1)";
    let host = event_host();
    let ast = parse_event_expr_ast(source).expect("eval unknown source should parse");
    let bound = bind_event_expr_ast(&ast, &host).expect("eval unknown source should bind");
    let tracked = [host.handle("clk")];
    let frame = EventEvalFrame {
        timestamp: 10,
        previous_timestamp: Some(8),
        tracked_signals: tracked.as_slice(),
    };
    assert!(
        !event_matches_at(&bound, &host, &frame).expect("eval unknown setup must evaluate"),
        "eval_event_union_iff_unknown should be suppressed by unknown iff result"
    );

    c.bench_function("event__eval_event_union_iff_unknown", |b| {
        b.iter(|| {
            let matched = event_matches_at(black_box(&bound), black_box(&host), black_box(&frame))
                .expect("eval_event_union_iff_unknown must evaluate");
            black_box(matched);
        })
    });
}

fn bench_eval_event_iff_core_integral(c: &mut Criterion) {
    let source = "posedge clk iff a[1]";
    let host = event_host();
    let ast = parse_event_expr_ast(source).expect("event iff source should parse");
    let bound = bind_event_expr_ast(&ast, &host).expect("event iff source should bind");
    let tracked = [host.handle("clk")];
    let frame = EventEvalFrame {
        timestamp: 10,
        previous_timestamp: Some(0),
        tracked_signals: tracked.as_slice(),
    };
    assert!(
        event_matches_at(&bound, &host, &frame).expect("event iff setup must evaluate"),
        "eval_event_iff_core_integral should match"
    );

    c.bench_function("event__eval_event_iff_core_integral", |b| {
        b.iter(|| {
            let matched = event_matches_at(black_box(&bound), black_box(&host), black_box(&frame))
                .expect("eval_event_iff_core_integral must evaluate");
            black_box(matched);
        })
    });
}

fn bench_eval_event_iff_triggered_rich(c: &mut Criterion) {
    let source = "posedge clk iff (ev.triggered() && (temp > 1.0) && (msg == \"go\"))";
    let host = event_host();
    let ast = parse_event_expr_ast(source).expect("event bench source should parse");
    let bound = bind_event_expr_ast(&ast, &host).expect("event bench source should bind");
    let tracked = [host.handle("clk")];
    let frame = EventEvalFrame {
        timestamp: 10,
        previous_timestamp: Some(0),
        tracked_signals: tracked.as_slice(),
    };
    assert!(event_matches_at(&bound, &host, &frame).expect("event bench setup must evaluate"));

    c.bench_function("event__eval_event_iff_triggered_rich", |b| {
        b.iter(|| {
            let matched = event_matches_at(black_box(&bound), black_box(&host), black_box(&frame))
                .expect("eval_event_iff_triggered_rich must evaluate");
            black_box(matched);
        })
    });
}

criterion_group!(
    name = benches;
    config = criterion_config();
    targets =
        bench_bind_event_union_iff,
        bench_eval_event_union_iff_true,
        bench_eval_event_union_iff_unknown,
        bench_eval_event_iff_core_integral,
        bench_eval_event_iff_triggered_rich
);
criterion_main!(benches);