Wavekit is a fundamental Python library for digital waveform analysis. By seamlessly converting VCD and FSDB data into Numpy arrays, it empowers engineers to perform high-performance signal processing, protocol analysis, and automated verification with ease.
- High Performance: Cython-optimized parsers and Numpy-backed storage deliver exceptional speed and memory efficiency for large waveform files.
- Flexible Extraction: Effortlessly batch-extract signals using glob patterns or regular expressions.
- Rich Analysis Tools: Comprehensive toolkit for bit-precise manipulation, temporal analysis, and signal transformation, streamlining complex analysis tasks with succinct, Numpy-like APIs.
You can install the library using pip:
pip install wavekitNote: To read FSDB files, ensure the VERDI_HOME environment variable is set before installation.
This project uses Poetry for dependency management.
-
Clone the repository:
git clone https://github.com/cxzzzz/wavekit.git cd wavekit -
Install dependencies:
poetry install
Wavekit simplifies extracting multiple signals using brace expansion or regular expressions.
1. Using Brace Expansion
Use brace patterns (e.g., {state,next} or {0..7}) to load related signals in one go.
from wavekit import VcdReader
with VcdReader("jtag.vcd") as f:
# Load both J_state and J_next signals
# Returns: { ('state',): Waveform(...), ('next',): Waveform(...) }
waves = f.load_matched_waveforms(
"tb.u0.J_{state,next}[3:0]",
clock="tb.tck"
)
for (suffix,), wave in waves.items():
print(f"Signal J_{suffix}: width={wave.width}")2. Using Regular Expressions
Prefix the pattern with @ to enable regex matching. Capture groups (...) become the dictionary keys.
with VcdReader("jtag.vcd") as f:
# Use '@' prefix for regex mode
# Match signals like J_state[3:0] and J_next[3:0]
# Capture the suffix (state/next)
regex_pattern = r"tb.u0.@J_([a-z]+)"
# Returns: { ('state',): Waveform(...), ('next',): Waveform(...) }
waves = f.load_matched_waveforms(regex_pattern, clock="tb.tck")
for (name_part,), wave in waves.items():
print(f"Matched: {name_part}")Here is a simple example demonstrating how to read a VCD file and calculate the average occupancy level of a FIFO:
import numpy as np
from wavekit import VcdReader
with VcdReader("fifo_tb.vcd") as f:
clock = "fifo_tb.s_fifo.clk"
depth = 8
# Load signals with clock synchronization
w_ptr = f.load_waveform("fifo_tb.s_fifo.w_ptr[2:0]", clock=clock)
r_ptr = f.load_waveform("fifo_tb.s_fifo.r_ptr[2:0]", clock=clock)
# Perform vectorized arithmetic operations directly on waveforms
fifo_water_level = (w_ptr + depth - r_ptr) % depth
# Calculate average occupancy using Numpy
average_level = np.mean(fifo_water_level.value)
print(f"Average FIFO occupancy: {average_level:.2f}")Use the powerful masking capabilities to filter data based on valid signals:
from wavekit import VcdReader
with VcdReader("bus_tb.vcd") as f:
clock = "top.clk"
# Load data and valid signals
data = f.load_waveform("top.bus_data[31:0]", clock=clock)
valid = f.load_waveform("top.bus_valid", clock=clock)
# Filter data where valid is high (1)
# The mask() method accepts a boolean waveform or numpy array
valid_data = data.mask(valid == 1)
# Analyze the valid transactions
print(f"Total valid transactions: {len(valid_data.value)}")
print(f"First valid data: {hex(valid_data.value[0])}")This project adheres to strict code quality standards using modern Python tooling:
Ensure all checks pass before submitting a Pull Request:
# Run linting and formatting checks
poetry run ruff check .
poetry run ruff format --check .
# Run type checks
poetry run mypy .This project is licensed under the MIT License. See the LICENSE file for details.