Skip to content

Engine API Reference

The engine module (tsunami._engine) is a compiled Rust extension. All functions are re-exported from tsunami directly.

_engine

WaveformHandle

Opaque handle to an opened waveform file.

Created by open(). Pass this handle as the first argument to all query functions. The underlying waveform data is memory-mapped and loaded lazily — only the signals you query are read from disk.

open builtin

open(path: str) -> WaveformHandle

Open an FST or VCD waveform file.

Parameters:

Name Type Description Default
path str

Path to the waveform file. Format is auto-detected.

required

Returns:

Type Description
WaveformHandle

A handle to use with all other query functions.

Raises:

Type Description
PanicException

If the file does not exist or cannot be parsed.

Example
import tsunami
handle = tsunami.open("simulation.fst")

waveform_info builtin

waveform_info(handle: WaveformHandle) -> dict[str, Any]

Get waveform metadata.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle from open().

required

Returns:

Type Description
dict[str, Any]

Dict with keys:

dict[str, Any]
  • timescale_factor (int): Timescale multiplier (e.g. 100).
dict[str, Any]
  • timescale_unit (str): Time unit (e.g. "PicoSeconds", "NanoSeconds").
dict[str, Any]
  • duration (int): Last time point in the waveform.
dict[str, Any]
  • num_signals (int): Total number of unique signals.
dict[str, Any]
  • num_time_points (int): Number of time points in the time table.
dict[str, Any]
  • file_format (str): "Fst" or "Vcd".
Example
info = tsunami.waveform_info(handle)
print(f"Duration: {info['duration']}, Signals: {info['num_signals']}")

list_signals builtin

list_signals(handle: WaveformHandle, pattern: str = '*') -> list[dict[str, Any]]

List signals matching a glob pattern.

This is typically the first call in any debugging session — use it to discover signal names before querying values.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
pattern str

Glob pattern to match against full hierarchical signal paths. Supports * (any characters) and ? (single character).

'*'

Returns:

Type Description
list[dict[str, Any]]

List of dicts, each with keys:

list[dict[str, Any]]
  • path (str): Full hierarchical signal path.
list[dict[str, Any]]
  • width (int): Bit width of the signal.
list[dict[str, Any]]
  • type (str): Variable type (e.g. "Wire", "Reg", "Integer").
list[dict[str, Any]]
  • direction (str): Port direction (e.g. "Input", "Output", "Internal").
Example
clocks = tsunami.list_signals(handle, "*clk*")
for sig in clocks:
    print(f"[{sig['width']:>4}] {sig['path']}")

list_scopes builtin

list_scopes(handle: WaveformHandle, prefix: str = '') -> list[str]

List scopes (hierarchy levels) matching a prefix.

Use this to browse the design hierarchy without listing individual signals.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
prefix str

Only return scopes whose full path starts with this string. Use "" to list all scopes.

''

Returns:

Type Description
list[str]

List of full scope path strings.

Example
scopes = tsunami.list_scopes(handle, "tb.dut")
# ['tb.dut.core', 'tb.dut.core.iq', 'tb.dut.cache', ...]

get_value builtin

get_value(handle: WaveformHandle, signal: str, time_ps: int) -> dict[str, Any]

Get a signal's value at a specific time.

For querying multiple signals at the same time, prefer get_snapshot() which is more efficient.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signal str

Full hierarchical signal path (e.g. "tb.dut.clk").

required
time_ps int

Time point to query.

required

Returns:

Type Description
dict[str, Any]

Dict with keys:

dict[str, Any]
  • hex (str): Value as a hex string (e.g. "1", "3f", "x" for unknown).
dict[str, Any]
  • is_x (bool): True if the value contains unknown (X) bits.
dict[str, Any]
  • is_z (bool): True if the value contains high-impedance (Z) bits.

Raises:

Type Description
ValueError

If the signal path is not found.

Example
val = tsunami.get_value(handle, "tb.dut.clk", 1000)
print(f"0x{val['hex']}")  # "0x1"

get_snapshot builtin

get_snapshot(handle: WaveformHandle, signals: list[str], time_ps: int) -> dict[str, dict[str, Any]]

Get values of multiple signals at a single time point.

More efficient than calling get_value() in a loop — signals are loaded in a single batch.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signals list[str]

List of full signal paths.

required
time_ps int

Time point to query.

required

Returns:

Type Description
dict[str, dict[str, Any]]

Dict mapping each signal path to its value dict (same format as

dict[str, dict[str, Any]]
Example
snap = tsunami.get_snapshot(handle, [
    "tb.dut.tl_a_valid",
    "tb.dut.tl_a_ready",
    "tb.dut.tl_a_opcode",
], time_ps=1_284_000)
for sig, val in snap.items():
    print(f"{sig} = 0x{val['hex']}")

get_transitions builtin

get_transitions(handle: WaveformHandle, signal: str, t0_ps: int, t1_ps: int, max_edges: int = 1000) -> dict[str, Any]

Get signal transitions (value changes) in a time range.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signal str

Full signal path.

required
t0_ps int

Start time (inclusive).

required
t1_ps int

End time (inclusive).

required
max_edges int

Maximum number of transitions to return. If the signal has more transitions in the range, the result is truncated but total_transitions still reflects the true count.

1000

Returns:

Type Description
dict[str, Any]

Dict with keys:

dict[str, Any]
  • signal (str): The queried signal path.
dict[str, Any]
  • t0_ps (int): Start time.
dict[str, Any]
  • t1_ps (int): End time.
dict[str, Any]
  • total_transitions (int): Actual number of transitions in range.
dict[str, Any]
  • truncated (bool): True if output was capped at max_edges.
dict[str, Any]
  • transitions (list[dict]): List of {"time": int, "value": str} dicts.
Example
result = tsunami.get_transitions(handle, "tb.dut.clk", 0, 10_000)
for tr in result["transitions"]:
    print(f"  t={tr['time']}: 0x{tr['value']}")

find_next_edge builtin

find_next_edge(handle: WaveformHandle, signal: str, direction: str, after_ps: int) -> int | None

Find the next edge (value change) on a signal after a given time.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signal str

Full signal path.

required
direction str

One of "rising", "falling", or "any".

required
after_ps int

Search for edges strictly after this time.

required

Returns:

Type Description
int | None

Time of the next matching edge, or None if no edge is found.

Raises:

Type Description
ValueError

If direction is not one of the accepted values.

Example
t = tsunami.find_next_edge(handle, "tb.dut.clk", "rising", after_ps=0)

find_first builtin

find_first(handle: WaveformHandle, expr: Any, after_ps: int) -> int | None

Find the first timestamp where a predicate expression is true.

The entire scan runs in Rust in a single pass over the union of transition points of all referenced signals.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
expr Any

A predicate expression built with the tsunami.predicate DSL.

required
after_ps int

Search after this time.

required

Returns:

Type Description
int | None

Time of the first match, or None if no match is found.

Example
from tsunami.predicate import Signal
valid = Signal("tb.dut.tl_a_valid")
ready = Signal("tb.dut.tl_a_ready")
t = tsunami.find_first(handle, valid & ready, after_ps=0)

find_all builtin

find_all(handle: WaveformHandle, expr: Any, t0_ps: int, t1_ps: int) -> list[int]

Find all timestamps where a predicate expression is true.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
expr Any

A predicate expression.

required
t0_ps int

Start of search window.

required
t1_ps int

End of search window.

required

Returns:

Type Description
list[int]

List of timestamps where the predicate evaluated to true.

Example
from tsunami.predicate import Signal
handshake = Signal("tb.dut.tl_a_valid") & Signal("tb.dut.tl_a_ready")
times = tsunami.find_all(handle, handshake, 0, 10_000_000)
print(f"{len(times)} handshakes found")

scan builtin

scan(handle: WaveformHandle, expr: Any, t0_ps: int, t1_ps: int) -> list[dict[str, Any]]

Evaluate a predicate at every transition point in a window.

Unlike find_all() which returns only matching timestamps, scan() returns the evaluated value at each point where the predicate is true. Useful for building transaction decoders.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
expr Any

A predicate expression.

required
t0_ps int

Start of scan window.

required
t1_ps int

End of scan window.

required

Returns:

Type Description
list[dict[str, Any]]

List of {"time": int, "value": int} dicts for each matching point.

summarize builtin

summarize(handle: WaveformHandle, signal: str, t0_ps: int, t1_ps: int) -> dict[str, Any]

Summarize a signal over a time window.

Computes statistics entirely in Rust before returning to Python. This is the mechanism that prevents large waveform windows from flooding the context window when used with an LLM.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signal str

Full signal path.

required
t0_ps int

Start time.

required
t1_ps int

End time.

required

Returns:

Type Description
dict[str, Any]

Dict with keys:

dict[str, Any]
  • total_transitions (int): Number of value changes in the window.
dict[str, Any]
  • dominant_period_ps (int | None): Most common interval between transitions.
dict[str, Any]
  • duty_cycle (float | None): Fraction of time the signal is high (1-bit signals only).
dict[str, Any]
  • value_histogram (dict[str, int]): Counts of each distinct value.
dict[str, Any]
Example
summary = tsunami.summarize(handle, "tb.dut.clk", 0, 10_000_000)
print(f"Period: {summary['dominant_period_ps']}ps")
print(f"Duty cycle: {summary['duty_cycle']:.1%}")

summarize_window builtin

summarize_window(handle: WaveformHandle, signals: list[str], t0_ps: int, t1_ps: int) -> dict[str, dict[str, Any]]

Summarize multiple signals over a time window.

Calls summarize() for each signal.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signals list[str]

List of signal paths.

required
t0_ps int

Start time.

required
t1_ps int

End time.

required

Returns:

Type Description
dict[str, dict[str, Any]]

Dict mapping each signal path to its summary dict.

find_anomalies builtin

find_anomalies(handle: WaveformHandle, signal: str, t0_ps: int, t1_ps: int, expected_period_ps: int | None = None) -> list[dict[str, Any]]

Detect anomalies in a signal's transition pattern.

Looks for three kinds of anomaly:

  • glitch: An interval shorter than 25% of the dominant period.
  • gap: An interval longer than 200% of the dominant period.
  • stuck: No transitions for a long time at the end of the window.

Parameters:

Name Type Description Default
handle WaveformHandle

Waveform handle.

required
signal str

Full signal path.

required
t0_ps int

Start time.

required
t1_ps int

End time.

required
expected_period_ps int | None

Expected period between transitions. If None, the dominant period is auto-inferred from the data.

None

Returns:

Type Description
list[dict[str, Any]]

List of anomaly dicts, each with keys:

list[dict[str, Any]]
  • time_ps (int): Time where the anomaly was detected.
list[dict[str, Any]]
  • kind (str): One of "glitch", "gap", or "stuck".
list[dict[str, Any]]
  • detail (str): Human-readable description.
Example
anomalies = tsunami.find_anomalies(handle, "tb.dut.clk", 0, 10_000_000)
for a in anomalies:
    print(f"  t={a['time_ps']}: {a['kind']}{a['detail']}")