wavekit 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wavekit-0.2.0/PKG-INFO +173 -0
- wavekit-0.2.0/README.md +151 -0
- wavekit-0.2.0/build.py +52 -0
- wavekit-0.2.0/pyproject.toml +49 -0
- wavekit-0.2.0/src/wavekit/__init__.py +21 -0
- wavekit-0.2.0/src/wavekit/readers/base.py +142 -0
- wavekit-0.1.1/src/wavekit/fsdb_reader.py → wavekit-0.2.0/src/wavekit/readers/fsdb/reader.py +66 -38
- {wavekit-0.1.1/src/wavekit → wavekit-0.2.0/src/wavekit/readers}/pattern_parser.py +20 -13
- wavekit-0.2.0/src/wavekit/readers/vcd/reader.py +129 -0
- wavekit-0.2.0/src/wavekit/scope.py +164 -0
- wavekit-0.2.0/src/wavekit/signal.py +35 -0
- wavekit-0.2.0/src/wavekit/waveform.py +829 -0
- wavekit-0.1.1/PKG-INFO +0 -90
- wavekit-0.1.1/README.md +0 -69
- wavekit-0.1.1/build.py +0 -54
- wavekit-0.1.1/pyproject.toml +0 -19
- wavekit-0.1.1/src/wavekit/__init__.py +0 -15
- wavekit-0.1.1/src/wavekit/reader.py +0 -269
- wavekit-0.1.1/src/wavekit/value_change.cpython-310-x86_64-linux-gnu.so +0 -0
- wavekit-0.1.1/src/wavekit/vcd_reader.py +0 -81
- wavekit-0.1.1/src/wavekit/waveform.py +0 -779
- {wavekit-0.1.1 → wavekit-0.2.0}/LICENSE +0 -0
- {wavekit-0.1.1/src/wavekit → wavekit-0.2.0/src/wavekit/readers/fsdb}/npi_fsdb.pxd +0 -0
- {wavekit-0.1.1/src/wavekit → wavekit-0.2.0/src/wavekit/readers/fsdb}/npi_fsdb_reader.pyx +0 -0
- {wavekit-0.1.1/src/wavekit → wavekit-0.2.0/src/wavekit/readers}/value_change.pyx +0 -0
wavekit-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wavekit
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: a fundamental package for digital circuit waveform analysis
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: cxzzzz
|
|
7
|
+
Author-email: cxz19961010@outlook.com
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Dist: numpy (>=2.0.0,<3.0.0)
|
|
17
|
+
Requires-Dist: pytest (>=8.2.2,<9.0.0)
|
|
18
|
+
Requires-Dist: vcdvcd (>=2.3.5,<3.0.0)
|
|
19
|
+
Project-URL: Repository, https://github.com/cxzzzz/wavekit
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# wavekit
|
|
23
|
+
|
|
24
|
+
[](https://pypi.org/project/wavekit/)
|
|
25
|
+
[](https://pypi.org/project/wavekit/)
|
|
26
|
+
[](LICENSE)
|
|
27
|
+
|
|
28
|
+
**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.
|
|
29
|
+
|
|
30
|
+
## ✨ Features
|
|
31
|
+
|
|
32
|
+
- **High Performance**: Cython-optimized parsers and Numpy-backed storage deliver exceptional speed and memory efficiency for large waveform files.
|
|
33
|
+
- **Flexible Extraction**: Effortlessly batch-extract signals using glob patterns or regular expressions.
|
|
34
|
+
- **Rich Analysis Tools**: Comprehensive toolkit for bit-precise manipulation, temporal analysis, and signal transformation, streamlining complex analysis tasks with succinct, Numpy-like APIs.
|
|
35
|
+
|
|
36
|
+
## 📦 Installation
|
|
37
|
+
|
|
38
|
+
### Using pip (For Users)
|
|
39
|
+
|
|
40
|
+
You can install the library using pip:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install wavekit
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Note**: To read FSDB files, ensure the `VERDI_HOME` environment variable is set before installation.
|
|
47
|
+
|
|
48
|
+
### From Source (For Developers)
|
|
49
|
+
|
|
50
|
+
This project uses [Poetry](https://python-poetry.org/) for dependency management.
|
|
51
|
+
|
|
52
|
+
1. Clone the repository:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git clone https://github.com/cxzzzz/wavekit.git
|
|
56
|
+
cd wavekit
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
2. Install dependencies:
|
|
60
|
+
```bash
|
|
61
|
+
poetry install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 🚀 Quick Start
|
|
65
|
+
|
|
66
|
+
### Batch Signal Extraction
|
|
67
|
+
|
|
68
|
+
Wavekit simplifies extracting multiple signals using brace expansion or regular expressions.
|
|
69
|
+
|
|
70
|
+
**1. Using Brace Expansion**
|
|
71
|
+
Use brace patterns (e.g., `{state,next}` or `{0..7}`) to load related signals in one go.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from wavekit import VcdReader
|
|
75
|
+
|
|
76
|
+
with VcdReader("jtag.vcd") as f:
|
|
77
|
+
# Load both J_state and J_next signals
|
|
78
|
+
# Returns: { ('state',): Waveform(...), ('next',): Waveform(...) }
|
|
79
|
+
waves = f.load_matched_waveforms(
|
|
80
|
+
"tb.u0.J_{state,next}[3:0]",
|
|
81
|
+
clock="tb.tck"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
for (suffix,), wave in waves.items():
|
|
85
|
+
print(f"Signal J_{suffix}: width={wave.width}")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**2. Using Regular Expressions**
|
|
89
|
+
Prefix the pattern with `@` to enable regex matching. Capture groups `(...)` become the dictionary keys.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
with VcdReader("jtag.vcd") as f:
|
|
93
|
+
# Use '@' prefix for regex mode
|
|
94
|
+
# Match signals like J_state[3:0] and J_next[3:0]
|
|
95
|
+
# Capture the suffix (state/next)
|
|
96
|
+
regex_pattern = r"tb.u0.@J_([a-z]+)"
|
|
97
|
+
|
|
98
|
+
# Returns: { ('state',): Waveform(...), ('next',): Waveform(...) }
|
|
99
|
+
waves = f.load_matched_waveforms(regex_pattern, clock="tb.tck")
|
|
100
|
+
|
|
101
|
+
for (name_part,), wave in waves.items():
|
|
102
|
+
print(f"Matched: {name_part}")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Basic: FIFO Occupancy Analysis
|
|
106
|
+
|
|
107
|
+
Here is a simple example demonstrating how to read a VCD file and calculate the average occupancy level of a FIFO:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import numpy as np
|
|
111
|
+
from wavekit import VcdReader
|
|
112
|
+
|
|
113
|
+
with VcdReader("fifo_tb.vcd") as f:
|
|
114
|
+
clock = "fifo_tb.s_fifo.clk"
|
|
115
|
+
depth = 8
|
|
116
|
+
|
|
117
|
+
# Load signals with clock synchronization
|
|
118
|
+
w_ptr = f.load_waveform("fifo_tb.s_fifo.w_ptr[2:0]", clock=clock)
|
|
119
|
+
r_ptr = f.load_waveform("fifo_tb.s_fifo.r_ptr[2:0]", clock=clock)
|
|
120
|
+
|
|
121
|
+
# Perform vectorized arithmetic operations directly on waveforms
|
|
122
|
+
fifo_water_level = (w_ptr + depth - r_ptr) % depth
|
|
123
|
+
|
|
124
|
+
# Calculate average occupancy using Numpy
|
|
125
|
+
average_level = np.mean(fifo_water_level.value)
|
|
126
|
+
print(f"Average FIFO occupancy: {average_level:.2f}")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Advanced: Data Validity Filtering
|
|
130
|
+
|
|
131
|
+
Use the powerful masking capabilities to filter data based on valid signals:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from wavekit import VcdReader
|
|
135
|
+
|
|
136
|
+
with VcdReader("bus_tb.vcd") as f:
|
|
137
|
+
clock = "top.clk"
|
|
138
|
+
|
|
139
|
+
# Load data and valid signals
|
|
140
|
+
data = f.load_waveform("top.bus_data[31:0]", clock=clock)
|
|
141
|
+
valid = f.load_waveform("top.bus_valid", clock=clock)
|
|
142
|
+
|
|
143
|
+
# Filter data where valid is high (1)
|
|
144
|
+
# The mask() method accepts a boolean waveform or numpy array
|
|
145
|
+
valid_data = data.mask(valid == 1)
|
|
146
|
+
|
|
147
|
+
# Analyze the valid transactions
|
|
148
|
+
print(f"Total valid transactions: {len(valid_data.value)}")
|
|
149
|
+
print(f"First valid data: {hex(valid_data.value[0])}")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 🛠️ Development
|
|
153
|
+
|
|
154
|
+
This project adheres to strict code quality standards using modern Python tooling:
|
|
155
|
+
|
|
156
|
+
- **Linting & Formatting**: [Ruff](https://github.com/astral-sh/ruff)
|
|
157
|
+
- **Type Checking**: [Mypy](https://mypy-lang.org/)
|
|
158
|
+
|
|
159
|
+
Ensure all checks pass before submitting a Pull Request:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Run linting and formatting checks
|
|
163
|
+
poetry run ruff check .
|
|
164
|
+
poetry run ruff format --check .
|
|
165
|
+
|
|
166
|
+
# Run type checks
|
|
167
|
+
poetry run mypy .
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 📄 License
|
|
171
|
+
|
|
172
|
+
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
|
173
|
+
|
wavekit-0.2.0/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# wavekit
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/wavekit/)
|
|
4
|
+
[](https://pypi.org/project/wavekit/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**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.
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- **High Performance**: Cython-optimized parsers and Numpy-backed storage deliver exceptional speed and memory efficiency for large waveform files.
|
|
12
|
+
- **Flexible Extraction**: Effortlessly batch-extract signals using glob patterns or regular expressions.
|
|
13
|
+
- **Rich Analysis Tools**: Comprehensive toolkit for bit-precise manipulation, temporal analysis, and signal transformation, streamlining complex analysis tasks with succinct, Numpy-like APIs.
|
|
14
|
+
|
|
15
|
+
## 📦 Installation
|
|
16
|
+
|
|
17
|
+
### Using pip (For Users)
|
|
18
|
+
|
|
19
|
+
You can install the library using pip:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install wavekit
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Note**: To read FSDB files, ensure the `VERDI_HOME` environment variable is set before installation.
|
|
26
|
+
|
|
27
|
+
### From Source (For Developers)
|
|
28
|
+
|
|
29
|
+
This project uses [Poetry](https://python-poetry.org/) for dependency management.
|
|
30
|
+
|
|
31
|
+
1. Clone the repository:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/cxzzzz/wavekit.git
|
|
35
|
+
cd wavekit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Install dependencies:
|
|
39
|
+
```bash
|
|
40
|
+
poetry install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 🚀 Quick Start
|
|
44
|
+
|
|
45
|
+
### Batch Signal Extraction
|
|
46
|
+
|
|
47
|
+
Wavekit simplifies extracting multiple signals using brace expansion or regular expressions.
|
|
48
|
+
|
|
49
|
+
**1. Using Brace Expansion**
|
|
50
|
+
Use brace patterns (e.g., `{state,next}` or `{0..7}`) to load related signals in one go.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from wavekit import VcdReader
|
|
54
|
+
|
|
55
|
+
with VcdReader("jtag.vcd") as f:
|
|
56
|
+
# Load both J_state and J_next signals
|
|
57
|
+
# Returns: { ('state',): Waveform(...), ('next',): Waveform(...) }
|
|
58
|
+
waves = f.load_matched_waveforms(
|
|
59
|
+
"tb.u0.J_{state,next}[3:0]",
|
|
60
|
+
clock="tb.tck"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
for (suffix,), wave in waves.items():
|
|
64
|
+
print(f"Signal J_{suffix}: width={wave.width}")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**2. Using Regular Expressions**
|
|
68
|
+
Prefix the pattern with `@` to enable regex matching. Capture groups `(...)` become the dictionary keys.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
with VcdReader("jtag.vcd") as f:
|
|
72
|
+
# Use '@' prefix for regex mode
|
|
73
|
+
# Match signals like J_state[3:0] and J_next[3:0]
|
|
74
|
+
# Capture the suffix (state/next)
|
|
75
|
+
regex_pattern = r"tb.u0.@J_([a-z]+)"
|
|
76
|
+
|
|
77
|
+
# Returns: { ('state',): Waveform(...), ('next',): Waveform(...) }
|
|
78
|
+
waves = f.load_matched_waveforms(regex_pattern, clock="tb.tck")
|
|
79
|
+
|
|
80
|
+
for (name_part,), wave in waves.items():
|
|
81
|
+
print(f"Matched: {name_part}")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Basic: FIFO Occupancy Analysis
|
|
85
|
+
|
|
86
|
+
Here is a simple example demonstrating how to read a VCD file and calculate the average occupancy level of a FIFO:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import numpy as np
|
|
90
|
+
from wavekit import VcdReader
|
|
91
|
+
|
|
92
|
+
with VcdReader("fifo_tb.vcd") as f:
|
|
93
|
+
clock = "fifo_tb.s_fifo.clk"
|
|
94
|
+
depth = 8
|
|
95
|
+
|
|
96
|
+
# Load signals with clock synchronization
|
|
97
|
+
w_ptr = f.load_waveform("fifo_tb.s_fifo.w_ptr[2:0]", clock=clock)
|
|
98
|
+
r_ptr = f.load_waveform("fifo_tb.s_fifo.r_ptr[2:0]", clock=clock)
|
|
99
|
+
|
|
100
|
+
# Perform vectorized arithmetic operations directly on waveforms
|
|
101
|
+
fifo_water_level = (w_ptr + depth - r_ptr) % depth
|
|
102
|
+
|
|
103
|
+
# Calculate average occupancy using Numpy
|
|
104
|
+
average_level = np.mean(fifo_water_level.value)
|
|
105
|
+
print(f"Average FIFO occupancy: {average_level:.2f}")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Advanced: Data Validity Filtering
|
|
109
|
+
|
|
110
|
+
Use the powerful masking capabilities to filter data based on valid signals:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from wavekit import VcdReader
|
|
114
|
+
|
|
115
|
+
with VcdReader("bus_tb.vcd") as f:
|
|
116
|
+
clock = "top.clk"
|
|
117
|
+
|
|
118
|
+
# Load data and valid signals
|
|
119
|
+
data = f.load_waveform("top.bus_data[31:0]", clock=clock)
|
|
120
|
+
valid = f.load_waveform("top.bus_valid", clock=clock)
|
|
121
|
+
|
|
122
|
+
# Filter data where valid is high (1)
|
|
123
|
+
# The mask() method accepts a boolean waveform or numpy array
|
|
124
|
+
valid_data = data.mask(valid == 1)
|
|
125
|
+
|
|
126
|
+
# Analyze the valid transactions
|
|
127
|
+
print(f"Total valid transactions: {len(valid_data.value)}")
|
|
128
|
+
print(f"First valid data: {hex(valid_data.value[0])}")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 🛠️ Development
|
|
132
|
+
|
|
133
|
+
This project adheres to strict code quality standards using modern Python tooling:
|
|
134
|
+
|
|
135
|
+
- **Linting & Formatting**: [Ruff](https://github.com/astral-sh/ruff)
|
|
136
|
+
- **Type Checking**: [Mypy](https://mypy-lang.org/)
|
|
137
|
+
|
|
138
|
+
Ensure all checks pass before submitting a Pull Request:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Run linting and formatting checks
|
|
142
|
+
poetry run ruff check .
|
|
143
|
+
poetry run ruff format --check .
|
|
144
|
+
|
|
145
|
+
# Run type checks
|
|
146
|
+
poetry run mypy .
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 📄 License
|
|
150
|
+
|
|
151
|
+
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
wavekit-0.2.0/build.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from Cython.Build import cythonize
|
|
5
|
+
from setuptools import Extension, setup
|
|
6
|
+
|
|
7
|
+
extensions = [
|
|
8
|
+
Extension(
|
|
9
|
+
'src.wavekit.readers.value_change',
|
|
10
|
+
sources=['src/wavekit/readers/value_change.pyx'],
|
|
11
|
+
include_dirs=[np.get_include()],
|
|
12
|
+
extra_compile_args=['-fpic', '-O3', '-march=native'],
|
|
13
|
+
extra_link_args=['-O3', '-march=native'],
|
|
14
|
+
language='c++',
|
|
15
|
+
)
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
if VERDI_HOME := os.environ.get('VERDI_HOME'):
|
|
19
|
+
npi_include_dir = os.path.join(VERDI_HOME, 'share/NPI/inc')
|
|
20
|
+
npi_library_dir = os.path.join(VERDI_HOME, 'share/NPI/lib/LINUX64')
|
|
21
|
+
|
|
22
|
+
extensions.append(
|
|
23
|
+
Extension(
|
|
24
|
+
'src.wavekit.readers.fsdb.npi_fsdb_reader',
|
|
25
|
+
sources=['src/wavekit/readers/fsdb/npi_fsdb_reader.pyx'],
|
|
26
|
+
include_dirs=[np.get_include(), npi_include_dir],
|
|
27
|
+
library_dirs=[npi_library_dir],
|
|
28
|
+
libraries=['NPI', 'rt'],
|
|
29
|
+
extra_compile_args=['-fpic', '-O3', '-march=native'],
|
|
30
|
+
extra_link_args=[
|
|
31
|
+
'-O3',
|
|
32
|
+
'-march=native',
|
|
33
|
+
f'-Wl,-rpath,{npi_library_dir}',
|
|
34
|
+
],
|
|
35
|
+
language='c++',
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
setup(
|
|
40
|
+
ext_modules=cythonize(
|
|
41
|
+
extensions,
|
|
42
|
+
compiler_directives={
|
|
43
|
+
'language_level': 3,
|
|
44
|
+
'embedsignature': True,
|
|
45
|
+
'boundscheck': False,
|
|
46
|
+
'wraparound': False,
|
|
47
|
+
'cdivision': True,
|
|
48
|
+
'nonecheck': False,
|
|
49
|
+
},
|
|
50
|
+
),
|
|
51
|
+
script_args=['build_ext', '--inplace'],
|
|
52
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "wavekit"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "a fundamental package for digital circuit waveform analysis"
|
|
5
|
+
authors = ["cxzzzz <cxz19961010@outlook.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
repository = "https://github.com/cxzzzz/wavekit"
|
|
8
|
+
build = "build.py"
|
|
9
|
+
include = ["src/wavekit/**/*.so"]
|
|
10
|
+
|
|
11
|
+
[tool.poetry.dependencies]
|
|
12
|
+
python = "^3.9"
|
|
13
|
+
numpy = "^2.0.0"
|
|
14
|
+
vcdvcd = "^2.3.5"
|
|
15
|
+
pytest = "^8.2.2"
|
|
16
|
+
|
|
17
|
+
[tool.poetry.group.dev.dependencies]
|
|
18
|
+
ruff = "^0.6.2"
|
|
19
|
+
mypy = "^1.11.2"
|
|
20
|
+
|
|
21
|
+
[tool.ruff]
|
|
22
|
+
target-version = "py39"
|
|
23
|
+
line-length = 100
|
|
24
|
+
src = ["src", "tests"]
|
|
25
|
+
|
|
26
|
+
[tool.ruff.lint]
|
|
27
|
+
select = ["E", "F", "W", "I", "UP", "B"]
|
|
28
|
+
|
|
29
|
+
[tool.ruff.lint.per-file-ignores]
|
|
30
|
+
"tests/**.py" = ["B017"]
|
|
31
|
+
|
|
32
|
+
[tool.ruff.format]
|
|
33
|
+
quote-style = "single"
|
|
34
|
+
line-ending = "lf"
|
|
35
|
+
|
|
36
|
+
[tool.mypy]
|
|
37
|
+
python_version = "3.9"
|
|
38
|
+
files = ["src", "tests"]
|
|
39
|
+
warn_unused_ignores = true
|
|
40
|
+
warn_redundant_casts = true
|
|
41
|
+
warn_unused_configs = true
|
|
42
|
+
check_untyped_defs = false
|
|
43
|
+
ignore_missing_imports = true
|
|
44
|
+
no_implicit_optional = false
|
|
45
|
+
disable_error_code = ["misc"]
|
|
46
|
+
|
|
47
|
+
[build-system]
|
|
48
|
+
requires = ["poetry-core", "Cython", "setuptools", "wheel", "numpy>=2.0.0"]
|
|
49
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See LICENSE in project root for information.
|
|
4
|
+
# -------------------------------------------------------------
|
|
5
|
+
"""Python Package Template"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from importlib import metadata
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
__version__ = metadata.version('wavekit')
|
|
13
|
+
except metadata.PackageNotFoundError:
|
|
14
|
+
__version__ = 'unknown'
|
|
15
|
+
|
|
16
|
+
from .readers.vcd.reader import VcdReader as VcdReader
|
|
17
|
+
from .scope import Scope as Scope
|
|
18
|
+
from .signal import Signal as Signal
|
|
19
|
+
from .waveform import Waveform as Waveform
|
|
20
|
+
|
|
21
|
+
__all__ = ['Waveform', 'VcdReader', 'FsdbReader', 'Scope', 'Signal']
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from ..scope import Scope, traverse_scope
|
|
10
|
+
from ..signal import Signal
|
|
11
|
+
from ..waveform import Waveform
|
|
12
|
+
from .pattern_parser import (
|
|
13
|
+
PatternMap,
|
|
14
|
+
expand_brace_pattern,
|
|
15
|
+
split_by_hierarchy,
|
|
16
|
+
)
|
|
17
|
+
from .value_change import value_change_to_value_array
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Reader:
|
|
21
|
+
def __init__(self):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def __enter__(self):
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
28
|
+
self.close()
|
|
29
|
+
# exception wont be suppressed
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def load_waveform(
|
|
34
|
+
self,
|
|
35
|
+
signal: str,
|
|
36
|
+
clock: str,
|
|
37
|
+
xz_value: int = 0,
|
|
38
|
+
signed: bool = False,
|
|
39
|
+
sample_on_posedge: bool = False,
|
|
40
|
+
begin_time: int | None = None,
|
|
41
|
+
end_time: int | None = None,
|
|
42
|
+
) -> Waveform:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def get_signal_width(self, signal: str) -> int:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def value_change_to_waveform(
|
|
51
|
+
value_change: np.ndarray,
|
|
52
|
+
clock_changes: np.ndarray,
|
|
53
|
+
width: int | None,
|
|
54
|
+
signed: bool,
|
|
55
|
+
sample_on_posedge: bool = False,
|
|
56
|
+
signal: str = '',
|
|
57
|
+
) -> Waveform:
|
|
58
|
+
value, clock, time = value_change_to_value_array(
|
|
59
|
+
value_change, clock_changes, sample_on_posedge=sample_on_posedge
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return Waveform(
|
|
63
|
+
value=value,
|
|
64
|
+
clock=clock,
|
|
65
|
+
time=time,
|
|
66
|
+
signal=Signal(signal, width, signed),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def top_scope_list(self) -> Sequence[Scope]:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
def get_matched_signals(
|
|
74
|
+
self,
|
|
75
|
+
pattern: str,
|
|
76
|
+
) -> dict[tuple[Any, ...], str]:
|
|
77
|
+
def combine_dict(
|
|
78
|
+
dict1: dict[tuple[Any, ...], str],
|
|
79
|
+
dict2: dict[tuple[Any, ...], str],
|
|
80
|
+
) -> dict[tuple[Any, ...], str]:
|
|
81
|
+
common_keys = set(dict1.keys()).intersection(dict2.keys())
|
|
82
|
+
|
|
83
|
+
if common_keys:
|
|
84
|
+
signal1s = [dict1[k] for k in common_keys]
|
|
85
|
+
signal2s = [dict2[k] for k in common_keys]
|
|
86
|
+
raise Exception(
|
|
87
|
+
'found more than one signal with the same keys: '
|
|
88
|
+
f'keys:{list(common_keys)} , signals:{signal1s + signal2s}'
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return {**dict1, **dict2}
|
|
92
|
+
|
|
93
|
+
pattern = pattern.strip()
|
|
94
|
+
expanded_pattern_list: list[PatternMap] = [
|
|
95
|
+
expand_brace_pattern(p) for p in split_by_hierarchy(pattern)
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
matched_signals: dict[tuple[Any, ...], str] = {}
|
|
99
|
+
for scope in self.top_scope_list():
|
|
100
|
+
matched_signals = combine_dict(
|
|
101
|
+
matched_signals,
|
|
102
|
+
traverse_scope(scope, expanded_pattern_list),
|
|
103
|
+
)
|
|
104
|
+
return matched_signals
|
|
105
|
+
|
|
106
|
+
def load_matched_waveforms(
|
|
107
|
+
self,
|
|
108
|
+
pattern: str,
|
|
109
|
+
clock_pattern: str,
|
|
110
|
+
xz_value: int = 0,
|
|
111
|
+
signed: bool = False,
|
|
112
|
+
sample_on_posedge: bool = False,
|
|
113
|
+
begin_time: int | None = None,
|
|
114
|
+
end_time: int | None = None,
|
|
115
|
+
) -> dict[tuple[Any, ...], Waveform]:
|
|
116
|
+
clock_patterns = self.get_matched_signals(clock_pattern)
|
|
117
|
+
if not clock_patterns:
|
|
118
|
+
raise Exception(f'clock pattern {clock_pattern} can not match any signal')
|
|
119
|
+
if len(clock_patterns) > 1:
|
|
120
|
+
raise Exception(
|
|
121
|
+
f'clock pattern {clock_pattern} match more than one signal: {clock_patterns}'
|
|
122
|
+
)
|
|
123
|
+
clock_full_name = next(iter(clock_patterns.values()))
|
|
124
|
+
|
|
125
|
+
matched_signals = self.get_matched_signals(pattern)
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
k: self.load_waveform(
|
|
129
|
+
s,
|
|
130
|
+
clock_full_name,
|
|
131
|
+
xz_value=xz_value,
|
|
132
|
+
signed=signed,
|
|
133
|
+
sample_on_posedge=sample_on_posedge,
|
|
134
|
+
begin_time=begin_time,
|
|
135
|
+
end_time=end_time,
|
|
136
|
+
)
|
|
137
|
+
for k, s in matched_signals.items()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@abstractmethod
|
|
141
|
+
def close(self):
|
|
142
|
+
pass
|