setVCD 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.
setvcd-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 VCD2Set Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
setvcd-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,337 @@
1
+ Metadata-Version: 2.4
2
+ Name: setVCD
3
+ Version: 0.2.0
4
+ Summary: Convert VCD signals to sets of time points based on conditions
5
+ Author-email: Michail Rontionov <mrontionov@mront.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/mrontio/setVCD
8
+ Project-URL: Repository, https://github.com/mrontio/setVCD
9
+ Project-URL: Issues, https://github.com/mrontio/setVCD/issues
10
+ Keywords: vcd,verilog,waveform,signal,testing,verification
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Testing
14
+ Classifier: Topic :: System :: Hardware
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: vcdvcd>=2.0.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
30
+ Requires-Dist: mypy>=1.0; extra == "dev"
31
+ Requires-Dist: black>=23.0; extra == "dev"
32
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
33
+ Requires-Dist: isort>=5.0; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # SetVCD
37
+ Programmatically inspect hardware VCD signals using a high-level functional interface.
38
+
39
+ Higher-order programming constructs and set operations are a natural fit for inspecting VCD signals, and this Python library allows you to easily specify, in text, what simulation timesteps matter to functional correctness.
40
+
41
+ ## Motivating Example
42
+ Say you are debugging a streaming interface, you often only care about the values of the data at timesteps meeting the following condition:
43
+
44
+ $\text{Rising edge} \land \text{Reset is 0} \land \text{ready} \land \text{valid}$
45
+
46
+ ![img/gtkwave.png](img/gtkwave.png "GTKWave screenshot of streaming interface we want to debug.")
47
+
48
+ You can filter through an individual signal with a filter function of this signature:
49
+
50
+ $(\text{Bits}, \text{Bits}, \text{Bits}) \rightarrow \text{Bool}$
51
+
52
+ with the left-hand tuple representing *values* at timestep $t$: $(t-1, t, t+1)$.
53
+
54
+ We then define our `get` method, which takes the name of the signal (as a String), a function with the above signature, and returns a set of *timesteps*:
55
+
56
+ $\texttt{get}: (\text{String}, ((\text{Bits}, \text{Bits}, \text{Bits}) \rightarrow \text{Bool})) \rightarrow \text{Set(Timestep)}$
57
+
58
+ As what is returned is a set, you can then use [set operations](https://en.wikipedia.org/wiki/Set_(mathematics)#Basic_operations) to manipulate them as needed, and finally extract the values from your desired signal using our `get_value` function:
59
+
60
+ $\texttt{get-value}: (\text{String}, \text{Set(Timestep)}) \rightarrow \text{List((Timestep, Bits))}$
61
+
62
+ Here's an example of finding the rising edges of the clock signal `TOP.clk` of our test wavefile `wave.vcd`:
63
+ ```python
64
+ from setVCD import SetVCD
65
+
66
+ # Load VCD file
67
+ vcd_path = "./tests/fixtures/wave.vcd"
68
+ sv = SetVCD(vcd_path, clock="TOP.clk")
69
+
70
+ rising_edges = sv.get("TOP.clk", lambda tm1, t, tp1: tm1 == 0 and t == 1)
71
+ print(rising_edges)
72
+ # {34, 36, 38, 40, 42, 44, ...}
73
+ ```
74
+
75
+ Because `rising_edges` is returned as a set, we can use set operations to combine it with other signals:
76
+ ```python
77
+ # Get times when the reset signal is 0
78
+ reset_is_0 = sv.get("TOP.reset", lambda tm1, t, tp1: t == 0)
79
+ # Use set intersection to get valid clock updates.
80
+ clock_update = rising_edges & reset_is_0
81
+ ```
82
+
83
+ Finally, you can search the wavefile with a regex (e.g. "output"), and apply the same operations to it:
84
+ ```python
85
+ # Find VCD signals relating to keyword "output"
86
+ sv.search("output")
87
+
88
+ # Get times when output_valid and output_ready are asserted.
89
+ out_valid = sv.get("TOP.Accelerator.io_output_valid", lambda tm1, t, tp1: t == 1)
90
+ out_ready = sv.get("TOP.Accelerator.io_output_ready", lambda tm1, t, tp1: t == 1)
91
+
92
+ # Get timesteps of valid outputs
93
+ valid_output_timesteps = rising_edges & reset0 & out_ready & out_valid
94
+
95
+ # Get the values of the Stream `value` signal (the data) at timesteps when it is valid
96
+ outputs = sv.get_values("TOP.Accelerator.io_output_payload_fragment_value_0[0:0]", valid_output_timesteps)
97
+ print(outputs)
98
+ # [(52, 0), (62, 0), (72, 1), ...] # Integer values
99
+ ```
100
+
101
+ ## Overview
102
+
103
+ SetVCD is a Python package for analyzing Verilog VCD files and extracting time points where specific signal conditions are met. It provides a simple, type-safe interface for working with simulation waveforms using set-based operations.
104
+
105
+ ## Installation
106
+ The package is available in PyPI:
107
+ ```bash
108
+ pip install setVCD
109
+ ```
110
+
111
+
112
+ ## Usage
113
+ ### Initialization
114
+
115
+ You can initialize SetVCD with either a filename or a vcdvcd object:
116
+
117
+ ```python
118
+ import setVCD
119
+ from pathlib import Path
120
+
121
+ # From string filename
122
+ sv = SetVCD("simulation.vcd", clock="clk")
123
+
124
+ # From Path object
125
+ sv = SetVCD(Path("simulation.vcd"), clock="clk")
126
+
127
+ # From vcdvcd object
128
+ import vcdvcd
129
+ vcd = vcdvcd.VCDVCD("simulation.vcd")
130
+ sv = SetVCD(vcd, clock="clk")
131
+ ```
132
+
133
+ The `clock` parameter must be the exact name of the clock signal in your VCD file (case-sensitive). This signal determines the time range for queries.
134
+
135
+ ### Signal Conditions
136
+
137
+ The `signal_condition` callback receives three arguments representing the signal value at three consecutive time points:
138
+
139
+ - `sm1`: Signal value at time-1 (None at time 0 or if value is x/z)
140
+ - `s`: Signal value at current time (None if value is x/z)
141
+ - `sp1`: Signal value at time+1 (None at last time or if value is x/z)
142
+
143
+ Signal values are `Optional[int]`:
144
+ - Integers: Binary values converted to decimal (e.g., "1010" → 10)
145
+ - None: Represents x/z values or boundary conditions (t-1 at time 0, t+1 at last time)
146
+
147
+ The callback should return `True` to include that time point in the result set.
148
+
149
+ ### Examples
150
+
151
+ #### Basic Signal Detection
152
+
153
+ ```python
154
+ # Rising edge: 0 -> 1 transition
155
+ rising = sv.get("clk", lambda sm1, s, sp1: sm1 == 0 and s == 1)
156
+
157
+ # Falling edge: 1 -> 0 transition
158
+ falling = sv.get("clk", lambda sm1, s, sp1: sm1 == 1 and s == 0)
159
+
160
+ # Any edge: value changed
161
+ edges = sv.get("data", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
162
+
163
+ # Level high
164
+ high = sv.get("enable", lambda sm1, s, sp1: s == 1)
165
+
166
+ # Level low
167
+ low = sv.get("reset", lambda sm1, s, sp1: s == 0)
168
+ ```
169
+
170
+ #### Multi-bit Signals
171
+
172
+ ```python
173
+ # Specific pattern on a bus (binary "1010" = decimal 10)
174
+ pattern = sv.get("bus[3:0]", lambda sm1, s, sp1: s == 10)
175
+
176
+ # Bus is non-zero
177
+ active = sv.get("data[7:0]", lambda sm1, s, sp1: s != 0)
178
+
179
+ # Bus transition detection
180
+ bus_changed = sv.get("addr[15:0]", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
181
+ ```
182
+
183
+ #### Complex Queries with Set Operations
184
+
185
+ ```python
186
+ # Rising clock edges when enable is high
187
+ clk_rising = sv.get("clk", lambda sm1, s, sp1: sm1 == 0 and s == 1)
188
+ enable_high = sv.get("enable", lambda sm1, s, sp1: s == 1)
189
+ valid_clocks = clk_rising & enable_high
190
+
191
+ # Data changes while not in reset
192
+ data_changes = sv.get("data", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
193
+ not_reset = sv.get("reset", lambda sm1, s, sp1: s == 0)
194
+ valid_changes = data_changes & not_reset
195
+
196
+ # Either signal is high
197
+ sig1_high = sv.get("sig1", lambda sm1, s, sp1: s == 1)
198
+ sig2_high = sv.get("sig2", lambda sm1, s, sp1: s == 1)
199
+ either_high = sig1_high | sig2_high
200
+
201
+ # Exclusive high (one but not both)
202
+ exclusive_high = sig1_high ^ sig2_high
203
+ ```
204
+
205
+ #### Advanced Pattern Detection
206
+
207
+ ```python
208
+ # Detect setup violation: data changes right before clock edge
209
+ data_change = sv.get("data", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
210
+ clk_about_to_rise = sv.get("clk", lambda sm1, s, sp1: s == 0 and sp1 == 1)
211
+ setup_violations = data_change & clk_about_to_rise
212
+
213
+ # Handshake protocol: valid and ready both high
214
+ valid_high = sv.get("valid", lambda sm1, s, sp1: s == 1)
215
+ ready_high = sv.get("ready", lambda sm1, s, sp1: s == 1)
216
+ handshake_times = valid_high & ready_high
217
+
218
+ # State machine transitions (binary "00" = 0, "01" = 1)
219
+ state_a = sv.get("state[1:0]", lambda sm1, s, sp1: s == 0)
220
+ state_b = sv.get("state[1:0]", lambda sm1, s, sp1: s == 1)
221
+ # Times when transitioning from state A to state B
222
+ transition = sv.get("state[1:0]", lambda sm1, s, sp1: sm1 == 0 and s == 1)
223
+ ```
224
+
225
+ ## Value Types
226
+
227
+ SetVCD supports three ValueType options to control how signal values are converted before being passed to your condition lambdas:
228
+
229
+ ### Raw() - Integer Conversion (Default)
230
+
231
+ Converts binary strings to decimal integers. X/Z values become `None`. This is the default behavior.
232
+
233
+ ```python
234
+ from setVCD import SetVCD, Raw
235
+
236
+ sv = SetVCD("simulation.vcd", clock="clk")
237
+
238
+ # Default behavior (Raw is implicit)
239
+ rising = sv.get("data[7:0]", lambda sm1, s, sp1: sm1 is not None and sm1 < s)
240
+
241
+ # Explicit Raw (same as above)
242
+ rising = sv.get("data[7:0]", lambda sm1, s, sp1: sm1 is not None and sm1 < s, value_type=Raw())
243
+
244
+ # Multi-bit signals converted to decimal
245
+ # Binary "00001010" → integer 10
246
+ high_values = sv.get("bus[7:0]", lambda sm1, s, sp1: s is not None and s > 128)
247
+ ```
248
+
249
+ ### String() - Preserve Raw Strings
250
+
251
+ Keeps vcdvcd's raw string representation, including X/Z values as literal strings. Useful for detecting unknown states.
252
+
253
+ ```python
254
+ from setVCD import SetVCD, String
255
+
256
+ sv = SetVCD("simulation.vcd", clock="clk")
257
+
258
+ # Detect X/Z values in data bus
259
+ has_x = sv.get("data[7:0]",
260
+ lambda sm1, s, sp1: s is not None and 'x' in s.lower(),
261
+ value_type=String())
262
+
263
+ # String pattern matching
264
+ all_ones = sv.get("bus[3:0]",
265
+ lambda sm1, s, sp1: s == "1111",
266
+ value_type=String())
267
+
268
+ # Get string values
269
+ values = sv.get_values("data", timesteps, value_type=String())
270
+ # Returns: [(50, "1010"), (60, "1111"), (70, "xxxx"), ...]
271
+ ```
272
+
273
+ **X/Z Handling:** X and Z values are preserved as strings (`"x"`, `"z"`, `"xxxx"`, etc.)
274
+
275
+ ### FP() - Fixed-Point to Float
276
+
277
+ Converts binary strings to floating-point by interpreting them as fixed-point numbers with configurable fractional bits and optional sign bit.
278
+
279
+ ```python
280
+ from setVCD import SetVCD, FP
281
+
282
+ sv = SetVCD("simulation.vcd", clock="clk")
283
+
284
+ # Temperature sensor with 8 fractional bits (Q8.8 format)
285
+ # Binary "0001100100000000" → 25.0 degrees
286
+ above_threshold = sv.get("temp_sensor[15:0]",
287
+ lambda sm1, s, sp1: s is not None and s > 25.5,
288
+ value_type=FP(frac=8, signed=False))
289
+
290
+ # Signed fixed-point (Q3.4 format - 1 sign bit, 3 integer bits, 4 fractional bits)
291
+ # Binary "11111110" → -0.125 (two's complement)
292
+ negative_values = sv.get("signed_value[7:0]",
293
+ lambda sm1, s, sp1: s is not None and s < 0,
294
+ value_type=FP(frac=4, signed=True))
295
+
296
+ # Get fixed-point values
297
+ voltages = sv.get_values("adc_reading[11:0]", timesteps,
298
+ value_type=FP(frac=12, signed=False))
299
+ # Returns: [(50, 1.2), (60, 2.5), (70, 3.8), ...]
300
+ ```
301
+
302
+ **X/Z Handling:** X and Z values become `float('nan')`. Use `math.isnan()` to detect them:
303
+
304
+ ```python
305
+ import math
306
+
307
+ # Filter out NaN values
308
+ valid_readings = sv.get("sensor",
309
+ lambda sm1, s, sp1: s is not None and not math.isnan(s),
310
+ value_type=FP(frac=8, signed=False))
311
+ ```
312
+
313
+ **Fixed-Point Formula:**
314
+ - Unsigned: `value = int_value / (2^frac)`
315
+ - Signed: Two's complement, then divide by `2^frac`
316
+
317
+ **Examples:**
318
+ - `"00001010"` with `frac=4, signed=False` → `10 / 16 = 0.625`
319
+ - `"11111110"` with `frac=4, signed=True` → `-2 / 16 = -0.125`
320
+ - `"00010000"` with `frac=0, signed=False` → `16 / 1 = 16.0` (integer)
321
+
322
+ ### Hardware Use Cases
323
+
324
+ **Raw (Default):** Most general-purpose verification - state machines, counters, addresses, data comparisons
325
+
326
+ **String:** Debugging X/Z propagation, detecting uninitialized signals, bit-pattern analysis
327
+
328
+ **FP:** Analog interfaces (ADC/DAC), sensor data, fixed-point DSP verification, power/temperature monitors
329
+
330
+ ## Future Enhancements
331
+
332
+ Planned for future versions:
333
+
334
+ - Higher-order operations for signal conditions
335
+ - Performance optimization for large VCD files
336
+ - Streaming interface for very large files
337
+ - MCP (Model Context Protocol) integration
setvcd-0.2.0/README.md ADDED
@@ -0,0 +1,302 @@
1
+ # SetVCD
2
+ Programmatically inspect hardware VCD signals using a high-level functional interface.
3
+
4
+ Higher-order programming constructs and set operations are a natural fit for inspecting VCD signals, and this Python library allows you to easily specify, in text, what simulation timesteps matter to functional correctness.
5
+
6
+ ## Motivating Example
7
+ Say you are debugging a streaming interface, you often only care about the values of the data at timesteps meeting the following condition:
8
+
9
+ $\text{Rising edge} \land \text{Reset is 0} \land \text{ready} \land \text{valid}$
10
+
11
+ ![img/gtkwave.png](img/gtkwave.png "GTKWave screenshot of streaming interface we want to debug.")
12
+
13
+ You can filter through an individual signal with a filter function of this signature:
14
+
15
+ $(\text{Bits}, \text{Bits}, \text{Bits}) \rightarrow \text{Bool}$
16
+
17
+ with the left-hand tuple representing *values* at timestep $t$: $(t-1, t, t+1)$.
18
+
19
+ We then define our `get` method, which takes the name of the signal (as a String), a function with the above signature, and returns a set of *timesteps*:
20
+
21
+ $\texttt{get}: (\text{String}, ((\text{Bits}, \text{Bits}, \text{Bits}) \rightarrow \text{Bool})) \rightarrow \text{Set(Timestep)}$
22
+
23
+ As what is returned is a set, you can then use [set operations](https://en.wikipedia.org/wiki/Set_(mathematics)#Basic_operations) to manipulate them as needed, and finally extract the values from your desired signal using our `get_value` function:
24
+
25
+ $\texttt{get-value}: (\text{String}, \text{Set(Timestep)}) \rightarrow \text{List((Timestep, Bits))}$
26
+
27
+ Here's an example of finding the rising edges of the clock signal `TOP.clk` of our test wavefile `wave.vcd`:
28
+ ```python
29
+ from setVCD import SetVCD
30
+
31
+ # Load VCD file
32
+ vcd_path = "./tests/fixtures/wave.vcd"
33
+ sv = SetVCD(vcd_path, clock="TOP.clk")
34
+
35
+ rising_edges = sv.get("TOP.clk", lambda tm1, t, tp1: tm1 == 0 and t == 1)
36
+ print(rising_edges)
37
+ # {34, 36, 38, 40, 42, 44, ...}
38
+ ```
39
+
40
+ Because `rising_edges` is returned as a set, we can use set operations to combine it with other signals:
41
+ ```python
42
+ # Get times when the reset signal is 0
43
+ reset_is_0 = sv.get("TOP.reset", lambda tm1, t, tp1: t == 0)
44
+ # Use set intersection to get valid clock updates.
45
+ clock_update = rising_edges & reset_is_0
46
+ ```
47
+
48
+ Finally, you can search the wavefile with a regex (e.g. "output"), and apply the same operations to it:
49
+ ```python
50
+ # Find VCD signals relating to keyword "output"
51
+ sv.search("output")
52
+
53
+ # Get times when output_valid and output_ready are asserted.
54
+ out_valid = sv.get("TOP.Accelerator.io_output_valid", lambda tm1, t, tp1: t == 1)
55
+ out_ready = sv.get("TOP.Accelerator.io_output_ready", lambda tm1, t, tp1: t == 1)
56
+
57
+ # Get timesteps of valid outputs
58
+ valid_output_timesteps = rising_edges & reset0 & out_ready & out_valid
59
+
60
+ # Get the values of the Stream `value` signal (the data) at timesteps when it is valid
61
+ outputs = sv.get_values("TOP.Accelerator.io_output_payload_fragment_value_0[0:0]", valid_output_timesteps)
62
+ print(outputs)
63
+ # [(52, 0), (62, 0), (72, 1), ...] # Integer values
64
+ ```
65
+
66
+ ## Overview
67
+
68
+ SetVCD is a Python package for analyzing Verilog VCD files and extracting time points where specific signal conditions are met. It provides a simple, type-safe interface for working with simulation waveforms using set-based operations.
69
+
70
+ ## Installation
71
+ The package is available in PyPI:
72
+ ```bash
73
+ pip install setVCD
74
+ ```
75
+
76
+
77
+ ## Usage
78
+ ### Initialization
79
+
80
+ You can initialize SetVCD with either a filename or a vcdvcd object:
81
+
82
+ ```python
83
+ import setVCD
84
+ from pathlib import Path
85
+
86
+ # From string filename
87
+ sv = SetVCD("simulation.vcd", clock="clk")
88
+
89
+ # From Path object
90
+ sv = SetVCD(Path("simulation.vcd"), clock="clk")
91
+
92
+ # From vcdvcd object
93
+ import vcdvcd
94
+ vcd = vcdvcd.VCDVCD("simulation.vcd")
95
+ sv = SetVCD(vcd, clock="clk")
96
+ ```
97
+
98
+ The `clock` parameter must be the exact name of the clock signal in your VCD file (case-sensitive). This signal determines the time range for queries.
99
+
100
+ ### Signal Conditions
101
+
102
+ The `signal_condition` callback receives three arguments representing the signal value at three consecutive time points:
103
+
104
+ - `sm1`: Signal value at time-1 (None at time 0 or if value is x/z)
105
+ - `s`: Signal value at current time (None if value is x/z)
106
+ - `sp1`: Signal value at time+1 (None at last time or if value is x/z)
107
+
108
+ Signal values are `Optional[int]`:
109
+ - Integers: Binary values converted to decimal (e.g., "1010" → 10)
110
+ - None: Represents x/z values or boundary conditions (t-1 at time 0, t+1 at last time)
111
+
112
+ The callback should return `True` to include that time point in the result set.
113
+
114
+ ### Examples
115
+
116
+ #### Basic Signal Detection
117
+
118
+ ```python
119
+ # Rising edge: 0 -> 1 transition
120
+ rising = sv.get("clk", lambda sm1, s, sp1: sm1 == 0 and s == 1)
121
+
122
+ # Falling edge: 1 -> 0 transition
123
+ falling = sv.get("clk", lambda sm1, s, sp1: sm1 == 1 and s == 0)
124
+
125
+ # Any edge: value changed
126
+ edges = sv.get("data", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
127
+
128
+ # Level high
129
+ high = sv.get("enable", lambda sm1, s, sp1: s == 1)
130
+
131
+ # Level low
132
+ low = sv.get("reset", lambda sm1, s, sp1: s == 0)
133
+ ```
134
+
135
+ #### Multi-bit Signals
136
+
137
+ ```python
138
+ # Specific pattern on a bus (binary "1010" = decimal 10)
139
+ pattern = sv.get("bus[3:0]", lambda sm1, s, sp1: s == 10)
140
+
141
+ # Bus is non-zero
142
+ active = sv.get("data[7:0]", lambda sm1, s, sp1: s != 0)
143
+
144
+ # Bus transition detection
145
+ bus_changed = sv.get("addr[15:0]", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
146
+ ```
147
+
148
+ #### Complex Queries with Set Operations
149
+
150
+ ```python
151
+ # Rising clock edges when enable is high
152
+ clk_rising = sv.get("clk", lambda sm1, s, sp1: sm1 == 0 and s == 1)
153
+ enable_high = sv.get("enable", lambda sm1, s, sp1: s == 1)
154
+ valid_clocks = clk_rising & enable_high
155
+
156
+ # Data changes while not in reset
157
+ data_changes = sv.get("data", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
158
+ not_reset = sv.get("reset", lambda sm1, s, sp1: s == 0)
159
+ valid_changes = data_changes & not_reset
160
+
161
+ # Either signal is high
162
+ sig1_high = sv.get("sig1", lambda sm1, s, sp1: s == 1)
163
+ sig2_high = sv.get("sig2", lambda sm1, s, sp1: s == 1)
164
+ either_high = sig1_high | sig2_high
165
+
166
+ # Exclusive high (one but not both)
167
+ exclusive_high = sig1_high ^ sig2_high
168
+ ```
169
+
170
+ #### Advanced Pattern Detection
171
+
172
+ ```python
173
+ # Detect setup violation: data changes right before clock edge
174
+ data_change = sv.get("data", lambda sm1, s, sp1: sm1 is not None and sm1 != s)
175
+ clk_about_to_rise = sv.get("clk", lambda sm1, s, sp1: s == 0 and sp1 == 1)
176
+ setup_violations = data_change & clk_about_to_rise
177
+
178
+ # Handshake protocol: valid and ready both high
179
+ valid_high = sv.get("valid", lambda sm1, s, sp1: s == 1)
180
+ ready_high = sv.get("ready", lambda sm1, s, sp1: s == 1)
181
+ handshake_times = valid_high & ready_high
182
+
183
+ # State machine transitions (binary "00" = 0, "01" = 1)
184
+ state_a = sv.get("state[1:0]", lambda sm1, s, sp1: s == 0)
185
+ state_b = sv.get("state[1:0]", lambda sm1, s, sp1: s == 1)
186
+ # Times when transitioning from state A to state B
187
+ transition = sv.get("state[1:0]", lambda sm1, s, sp1: sm1 == 0 and s == 1)
188
+ ```
189
+
190
+ ## Value Types
191
+
192
+ SetVCD supports three ValueType options to control how signal values are converted before being passed to your condition lambdas:
193
+
194
+ ### Raw() - Integer Conversion (Default)
195
+
196
+ Converts binary strings to decimal integers. X/Z values become `None`. This is the default behavior.
197
+
198
+ ```python
199
+ from setVCD import SetVCD, Raw
200
+
201
+ sv = SetVCD("simulation.vcd", clock="clk")
202
+
203
+ # Default behavior (Raw is implicit)
204
+ rising = sv.get("data[7:0]", lambda sm1, s, sp1: sm1 is not None and sm1 < s)
205
+
206
+ # Explicit Raw (same as above)
207
+ rising = sv.get("data[7:0]", lambda sm1, s, sp1: sm1 is not None and sm1 < s, value_type=Raw())
208
+
209
+ # Multi-bit signals converted to decimal
210
+ # Binary "00001010" → integer 10
211
+ high_values = sv.get("bus[7:0]", lambda sm1, s, sp1: s is not None and s > 128)
212
+ ```
213
+
214
+ ### String() - Preserve Raw Strings
215
+
216
+ Keeps vcdvcd's raw string representation, including X/Z values as literal strings. Useful for detecting unknown states.
217
+
218
+ ```python
219
+ from setVCD import SetVCD, String
220
+
221
+ sv = SetVCD("simulation.vcd", clock="clk")
222
+
223
+ # Detect X/Z values in data bus
224
+ has_x = sv.get("data[7:0]",
225
+ lambda sm1, s, sp1: s is not None and 'x' in s.lower(),
226
+ value_type=String())
227
+
228
+ # String pattern matching
229
+ all_ones = sv.get("bus[3:0]",
230
+ lambda sm1, s, sp1: s == "1111",
231
+ value_type=String())
232
+
233
+ # Get string values
234
+ values = sv.get_values("data", timesteps, value_type=String())
235
+ # Returns: [(50, "1010"), (60, "1111"), (70, "xxxx"), ...]
236
+ ```
237
+
238
+ **X/Z Handling:** X and Z values are preserved as strings (`"x"`, `"z"`, `"xxxx"`, etc.)
239
+
240
+ ### FP() - Fixed-Point to Float
241
+
242
+ Converts binary strings to floating-point by interpreting them as fixed-point numbers with configurable fractional bits and optional sign bit.
243
+
244
+ ```python
245
+ from setVCD import SetVCD, FP
246
+
247
+ sv = SetVCD("simulation.vcd", clock="clk")
248
+
249
+ # Temperature sensor with 8 fractional bits (Q8.8 format)
250
+ # Binary "0001100100000000" → 25.0 degrees
251
+ above_threshold = sv.get("temp_sensor[15:0]",
252
+ lambda sm1, s, sp1: s is not None and s > 25.5,
253
+ value_type=FP(frac=8, signed=False))
254
+
255
+ # Signed fixed-point (Q3.4 format - 1 sign bit, 3 integer bits, 4 fractional bits)
256
+ # Binary "11111110" → -0.125 (two's complement)
257
+ negative_values = sv.get("signed_value[7:0]",
258
+ lambda sm1, s, sp1: s is not None and s < 0,
259
+ value_type=FP(frac=4, signed=True))
260
+
261
+ # Get fixed-point values
262
+ voltages = sv.get_values("adc_reading[11:0]", timesteps,
263
+ value_type=FP(frac=12, signed=False))
264
+ # Returns: [(50, 1.2), (60, 2.5), (70, 3.8), ...]
265
+ ```
266
+
267
+ **X/Z Handling:** X and Z values become `float('nan')`. Use `math.isnan()` to detect them:
268
+
269
+ ```python
270
+ import math
271
+
272
+ # Filter out NaN values
273
+ valid_readings = sv.get("sensor",
274
+ lambda sm1, s, sp1: s is not None and not math.isnan(s),
275
+ value_type=FP(frac=8, signed=False))
276
+ ```
277
+
278
+ **Fixed-Point Formula:**
279
+ - Unsigned: `value = int_value / (2^frac)`
280
+ - Signed: Two's complement, then divide by `2^frac`
281
+
282
+ **Examples:**
283
+ - `"00001010"` with `frac=4, signed=False` → `10 / 16 = 0.625`
284
+ - `"11111110"` with `frac=4, signed=True` → `-2 / 16 = -0.125`
285
+ - `"00010000"` with `frac=0, signed=False` → `16 / 1 = 16.0` (integer)
286
+
287
+ ### Hardware Use Cases
288
+
289
+ **Raw (Default):** Most general-purpose verification - state machines, counters, addresses, data comparisons
290
+
291
+ **String:** Debugging X/Z propagation, detecting uninitialized signals, bit-pattern analysis
292
+
293
+ **FP:** Analog interfaces (ADC/DAC), sensor data, fixed-point DSP verification, power/temperature monitors
294
+
295
+ ## Future Enhancements
296
+
297
+ Planned for future versions:
298
+
299
+ - Higher-order operations for signal conditions
300
+ - Performance optimization for large VCD files
301
+ - Streaming interface for very large files
302
+ - MCP (Model Context Protocol) integration