Skip to content

Predicate DSL API Reference

predicate

Predicate DSL — composable expressions for waveform queries.

This module provides a Python DSL for building signal predicates that are evaluated entirely in Rust. Expressions are pure data structures (an AST) — no waveform access happens in Python. The entire expression tree is handed to the Rust evaluator in a single PyO3 call.

Quick start

from tsunami.predicate import Signal, scope

# Direct signal references
clk = Signal("tb.dut.clk")
valid = Signal("tb.dut.tl_a_valid")

# Using a scope prefix
with scope("tb.dut") as s:
    handshake = s.tl_a_valid & s.tl_a_ready
    acquire = handshake & (s.tl_a_opcode == 4)

# Compose with operators
rising_valid = valid.rise()
sequence = acquire >> (s.tl_d_valid, 50_000)  # with time window
violation = s.tl_d_valid.rise().preceded_by(acquire, within_ps=50_000).__invert__()

Supported operators

Syntax Meaning
a & b Logical AND
a \| b Logical OR
~a Logical NOT
a ^ b Logical XOR
sig == val Signal equals constant
sig > val / sig < val Unsigned comparison
sig.rise() Rising edge (0 -> non-zero)
sig.fall() Falling edge (non-zero -> 0)
a >> b Sequence: a followed by b
a >> (b, window_ps) Sequence with time window
a.preceded_by(b, within_ps=N) b occurred before a within window
sig[7:0] Bitfield extraction

Expr dataclass

Expr(tag: str)

Base class for all predicate expression nodes.

All expression nodes carry a tag string that the Rust evaluator uses to identify the node type via FromPyObject. You normally don't construct Expr directly — use Signal and operators instead.

__and__

__and__(other: Expr) -> Expr

Logical AND: a & b is true when both operands are non-zero.

__or__

__or__(other: Expr) -> Expr

Logical OR: a | b is true when either operand is non-zero.

__invert__

__invert__() -> Expr

Logical NOT: ~a is true when the operand is zero.

__xor__

__xor__(other: Expr) -> Expr

Logical XOR: a ^ b is true when exactly one operand is non-zero.

__eq__

__eq__(other: object) -> Expr

Equality: sig == 4 is true when the signal's value equals 4.

__gt__

__gt__(other: object) -> Expr

Unsigned greater-than comparison.

__lt__

__lt__(other: object) -> Expr

Unsigned less-than comparison.

__rshift__

__rshift__(other) -> Expr

Sequence operator.

  • a >> b — a followed by b (unbounded).
  • a >> (b, window_ps) — a followed by b within window_ps picoseconds.

rise

rise() -> Expr

Rising edge: true at the transition from zero to non-zero.

fall

fall() -> Expr

Falling edge: true at the transition from non-zero to zero.

preceded_by

preceded_by(other: Expr, within_ps: int | None = None) -> Expr

True when self is true AND other was true within within_ps before.

Parameters:

Name Type Description Default
other Expr

The preceding condition.

required
within_ps int | None

Maximum lookback window in picoseconds. If None, searches the entire history.

None

Signal dataclass

Signal(path: str)

Bases: Expr

Reference to a waveform signal by its full hierarchical path.

This is the primary leaf node in predicate expressions. The path must match a signal in the waveform file exactly (dot-separated hierarchy).

Parameters:

Name Type Description Default
path str

Full hierarchical path, e.g. "tb.dut.tl_a_valid".

required
Example
clk = Signal("tb.dut.clk")
opcode = Signal("tb.dut.tl_a_opcode")

# Use in expressions
rising_clk = clk.rise()
is_get = opcode == 4

# Bitfield extraction
low_nibble = opcode[3:0]
single_bit = opcode[2]

__getitem__

__getitem__(key) -> Expr

Extract a bitfield: sig[7:0] for a range or sig[3] for a single bit.

Const dataclass

Const(value: int)

Bases: Expr

Constant integer value.

And dataclass

And(tag: str = 'and', left: Expr = None, right: Expr = None)

Bases: Expr

Or dataclass

Or(tag: str = 'or', left: Expr = None, right: Expr = None)

Bases: Expr

Not dataclass

Not(tag: str = 'not', inner: Expr = None)

Bases: Expr

Xor dataclass

Xor(tag: str = 'xor', left: Expr = None, right: Expr = None)

Bases: Expr

Eq dataclass

Eq(tag: str = 'eq', left: Expr = None, right: Expr = None)

Bases: Expr

Gt dataclass

Gt(tag: str = 'gt', left: Expr = None, right: Expr = None)

Bases: Expr

Lt dataclass

Lt(tag: str = 'lt', left: Expr = None, right: Expr = None)

Bases: Expr

Rise dataclass

Rise(tag: str = 'rise', inner: Expr = None)

Bases: Expr

Fall dataclass

Fall(tag: str = 'fall', inner: Expr = None)

Bases: Expr

BitSlice dataclass

BitSlice(tag: str = 'bit_slice', inner: Expr = None, high: int = 0, low: int = 0)

Bases: Expr

Sequence dataclass

Sequence(tag: str = 'sequence', a: Expr = None, b: Expr = None, within_ps: Optional[int] = None)

Bases: Expr

PrecededBy dataclass

PrecededBy(tag: str = 'preceded_by', a: Expr = None, b: Expr = None, within_ps: Optional[int] = None)

Bases: Expr

scope

scope(prefix: str)

Context manager that sets a hierarchy prefix for signal access.

Attribute access on the yielded proxy creates Signal objects with the prefix prepended.

Parameters:

Name Type Description Default
prefix str

Hierarchy prefix (e.g. "tb.dut.core").

required
Example
with scope("tb.dut") as s:
    handshake = s.tl_a_valid & s.tl_a_ready
    # equivalent to:
    # Signal("tb.dut.tl_a_valid") & Signal("tb.dut.tl_a_ready")

signals

signals(**kwargs: str)

Context manager for aliased signal bindings.

Maps short alias names to full signal paths. Useful when working with signals from a configuration dict.

Parameters:

Name Type Description Default
**kwargs str

Mapping of alias name to full signal path.

{}
Example
with signals(v="tb.dut.tl_a_valid", r="tb.dut.tl_a_ready") as s:
    handshake = s.v & s.r

ScopeProxy

ScopeProxy(prefix: str)

Provides attribute-based signal access with a hierarchy prefix.

SignalsProxy

SignalsProxy(mapping: dict[str, str])

Provides aliased signal access.