ewal 0.1.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.
Files changed (58) hide show
  1. ewal-0.1.0/Cargo.lock +262 -0
  2. ewal-0.1.0/Cargo.toml +3 -0
  3. ewal-0.1.0/PKG-INFO +11 -0
  4. ewal-0.1.0/crates/ewal-core/Cargo.toml +14 -0
  5. ewal-0.1.0/crates/ewal-core/src/eval/expr.rs +141 -0
  6. ewal-0.1.0/crates/ewal-core/src/eval/find.rs +33 -0
  7. ewal-0.1.0/crates/ewal-core/src/eval/mod.rs +3 -0
  8. ewal-0.1.0/crates/ewal-core/src/eval/scope.rs +76 -0
  9. ewal-0.1.0/crates/ewal-core/src/lib.rs +4 -0
  10. ewal-0.1.0/crates/ewal-core/src/signal/bitpack.rs +84 -0
  11. ewal-0.1.0/crates/ewal-core/src/signal/mod.rs +68 -0
  12. ewal-0.1.0/crates/ewal-core/src/signal/store.rs +89 -0
  13. ewal-0.1.0/crates/ewal-core/src/signal/virtual.rs +42 -0
  14. ewal-0.1.0/crates/ewal-core/src/trace/csv.rs +135 -0
  15. ewal-0.1.0/crates/ewal-core/src/trace/mod.rs +23 -0
  16. ewal-0.1.0/crates/ewal-core/src/trace/vcd.rs +257 -0
  17. ewal-0.1.0/crates/ewal-core/src/trace/virtual_trace.rs +96 -0
  18. ewal-0.1.0/crates/ewal-core/src/util/bitops.rs +44 -0
  19. ewal-0.1.0/crates/ewal-core/src/util/mod.rs +1 -0
  20. ewal-0.1.0/crates/ewal-py/Cargo.toml +12 -0
  21. ewal-0.1.0/crates/ewal-py/src/lib.rs +260 -0
  22. ewal-0.1.0/pyproject.toml +29 -0
  23. ewal-0.1.0/python/ewal/__init__.py +205 -0
  24. ewal-0.1.0/python/ewal/_core.pyi +57 -0
  25. ewal-0.1.0/python/ewal/assertions.py +315 -0
  26. ewal-0.1.0/python/ewal/cdc.py +258 -0
  27. ewal-0.1.0/python/ewal/cli.py +112 -0
  28. ewal-0.1.0/python/ewal/cocotb_compat.py +82 -0
  29. ewal-0.1.0/python/ewal/compat.py +59 -0
  30. ewal-0.1.0/python/ewal/container.py +37 -0
  31. ewal-0.1.0/python/ewal/correlation.py +154 -0
  32. ewal-0.1.0/python/ewal/debug.py +196 -0
  33. ewal-0.1.0/python/ewal/diffing.py +192 -0
  34. ewal-0.1.0/python/ewal/dsl.py +20 -0
  35. ewal-0.1.0/python/ewal/export.py +117 -0
  36. ewal-0.1.0/python/ewal/fsm.py +219 -0
  37. ewal-0.1.0/python/ewal/markers.py +205 -0
  38. ewal-0.1.0/python/ewal/notebook.py +210 -0
  39. ewal-0.1.0/python/ewal/pipeline.py +270 -0
  40. ewal-0.1.0/python/ewal/plugins.py +118 -0
  41. ewal-0.1.0/python/ewal/power.py +154 -0
  42. ewal-0.1.0/python/ewal/protocol/__init__.py +27 -0
  43. ewal-0.1.0/python/ewal/protocol/apb.py +74 -0
  44. ewal-0.1.0/python/ewal/protocol/avalon.py +91 -0
  45. ewal-0.1.0/python/ewal/protocol/axi4.py +239 -0
  46. ewal-0.1.0/python/ewal/protocol/axi4_stream.py +80 -0
  47. ewal-0.1.0/python/ewal/protocol/base.py +107 -0
  48. ewal-0.1.0/python/ewal/protocol/wishbone.py +70 -0
  49. ewal-0.1.0/python/ewal/query.py +301 -0
  50. ewal-0.1.0/python/ewal/repl.py +13 -0
  51. ewal-0.1.0/python/ewal/scope.py +14 -0
  52. ewal-0.1.0/python/ewal/signal.py +268 -0
  53. ewal-0.1.0/python/ewal/stdlib.py +37 -0
  54. ewal-0.1.0/python/ewal/streaming.py +298 -0
  55. ewal-0.1.0/python/ewal/temporal.py +266 -0
  56. ewal-0.1.0/python/ewal/trace.py +497 -0
  57. ewal-0.1.0/python/ewal/virtual.py +16 -0
  58. ewal-0.1.0/python/ewal/wawk.py +61 -0
ewal-0.1.0/Cargo.lock ADDED
@@ -0,0 +1,262 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "crossbeam-deque"
7
+ version = "0.8.6"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
10
+ dependencies = [
11
+ "crossbeam-epoch",
12
+ "crossbeam-utils",
13
+ ]
14
+
15
+ [[package]]
16
+ name = "crossbeam-epoch"
17
+ version = "0.9.18"
18
+ source = "registry+https://github.com/rust-lang/crates.io-index"
19
+ checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
20
+ dependencies = [
21
+ "crossbeam-utils",
22
+ ]
23
+
24
+ [[package]]
25
+ name = "crossbeam-utils"
26
+ version = "0.8.21"
27
+ source = "registry+https://github.com/rust-lang/crates.io-index"
28
+ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
29
+
30
+ [[package]]
31
+ name = "csv"
32
+ version = "1.4.0"
33
+ source = "registry+https://github.com/rust-lang/crates.io-index"
34
+ checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938"
35
+ dependencies = [
36
+ "csv-core",
37
+ "itoa",
38
+ "ryu",
39
+ "serde_core",
40
+ ]
41
+
42
+ [[package]]
43
+ name = "csv-core"
44
+ version = "0.1.13"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
47
+ dependencies = [
48
+ "memchr",
49
+ ]
50
+
51
+ [[package]]
52
+ name = "either"
53
+ version = "1.15.0"
54
+ source = "registry+https://github.com/rust-lang/crates.io-index"
55
+ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
56
+
57
+ [[package]]
58
+ name = "ewal-core"
59
+ version = "0.1.0"
60
+ dependencies = [
61
+ "csv",
62
+ "memmap2",
63
+ "rayon",
64
+ ]
65
+
66
+ [[package]]
67
+ name = "ewal-py"
68
+ version = "0.1.0"
69
+ dependencies = [
70
+ "ewal-core",
71
+ "pyo3",
72
+ ]
73
+
74
+ [[package]]
75
+ name = "heck"
76
+ version = "0.5.0"
77
+ source = "registry+https://github.com/rust-lang/crates.io-index"
78
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
79
+
80
+ [[package]]
81
+ name = "itoa"
82
+ version = "1.0.18"
83
+ source = "registry+https://github.com/rust-lang/crates.io-index"
84
+ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
85
+
86
+ [[package]]
87
+ name = "libc"
88
+ version = "0.2.183"
89
+ source = "registry+https://github.com/rust-lang/crates.io-index"
90
+ checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
91
+
92
+ [[package]]
93
+ name = "memchr"
94
+ version = "2.8.0"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
97
+
98
+ [[package]]
99
+ name = "memmap2"
100
+ version = "0.9.10"
101
+ source = "registry+https://github.com/rust-lang/crates.io-index"
102
+ checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
103
+ dependencies = [
104
+ "libc",
105
+ ]
106
+
107
+ [[package]]
108
+ name = "once_cell"
109
+ version = "1.21.4"
110
+ source = "registry+https://github.com/rust-lang/crates.io-index"
111
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
112
+
113
+ [[package]]
114
+ name = "portable-atomic"
115
+ version = "1.13.1"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
118
+
119
+ [[package]]
120
+ name = "proc-macro2"
121
+ version = "1.0.106"
122
+ source = "registry+https://github.com/rust-lang/crates.io-index"
123
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
124
+ dependencies = [
125
+ "unicode-ident",
126
+ ]
127
+
128
+ [[package]]
129
+ name = "pyo3"
130
+ version = "0.28.2"
131
+ source = "registry+https://github.com/rust-lang/crates.io-index"
132
+ checksum = "cf85e27e86080aafd5a22eae58a162e133a589551542b3e5cee4beb27e54f8e1"
133
+ dependencies = [
134
+ "libc",
135
+ "once_cell",
136
+ "portable-atomic",
137
+ "pyo3-build-config",
138
+ "pyo3-ffi",
139
+ "pyo3-macros",
140
+ ]
141
+
142
+ [[package]]
143
+ name = "pyo3-build-config"
144
+ version = "0.28.2"
145
+ source = "registry+https://github.com/rust-lang/crates.io-index"
146
+ checksum = "8bf94ee265674bf76c09fa430b0e99c26e319c945d96ca0d5a8215f31bf81cf7"
147
+ dependencies = [
148
+ "target-lexicon",
149
+ ]
150
+
151
+ [[package]]
152
+ name = "pyo3-ffi"
153
+ version = "0.28.2"
154
+ source = "registry+https://github.com/rust-lang/crates.io-index"
155
+ checksum = "491aa5fc66d8059dd44a75f4580a2962c1862a1c2945359db36f6c2818b748dc"
156
+ dependencies = [
157
+ "libc",
158
+ "pyo3-build-config",
159
+ ]
160
+
161
+ [[package]]
162
+ name = "pyo3-macros"
163
+ version = "0.28.2"
164
+ source = "registry+https://github.com/rust-lang/crates.io-index"
165
+ checksum = "f5d671734e9d7a43449f8480f8b38115df67bef8d21f76837fa75ee7aaa5e52e"
166
+ dependencies = [
167
+ "proc-macro2",
168
+ "pyo3-macros-backend",
169
+ "quote",
170
+ "syn",
171
+ ]
172
+
173
+ [[package]]
174
+ name = "pyo3-macros-backend"
175
+ version = "0.28.2"
176
+ source = "registry+https://github.com/rust-lang/crates.io-index"
177
+ checksum = "22faaa1ce6c430a1f71658760497291065e6450d7b5dc2bcf254d49f66ee700a"
178
+ dependencies = [
179
+ "heck",
180
+ "proc-macro2",
181
+ "pyo3-build-config",
182
+ "quote",
183
+ "syn",
184
+ ]
185
+
186
+ [[package]]
187
+ name = "quote"
188
+ version = "1.0.45"
189
+ source = "registry+https://github.com/rust-lang/crates.io-index"
190
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
191
+ dependencies = [
192
+ "proc-macro2",
193
+ ]
194
+
195
+ [[package]]
196
+ name = "rayon"
197
+ version = "1.11.0"
198
+ source = "registry+https://github.com/rust-lang/crates.io-index"
199
+ checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
200
+ dependencies = [
201
+ "either",
202
+ "rayon-core",
203
+ ]
204
+
205
+ [[package]]
206
+ name = "rayon-core"
207
+ version = "1.13.0"
208
+ source = "registry+https://github.com/rust-lang/crates.io-index"
209
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
210
+ dependencies = [
211
+ "crossbeam-deque",
212
+ "crossbeam-utils",
213
+ ]
214
+
215
+ [[package]]
216
+ name = "ryu"
217
+ version = "1.0.23"
218
+ source = "registry+https://github.com/rust-lang/crates.io-index"
219
+ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
220
+
221
+ [[package]]
222
+ name = "serde_core"
223
+ version = "1.0.228"
224
+ source = "registry+https://github.com/rust-lang/crates.io-index"
225
+ checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
226
+ dependencies = [
227
+ "serde_derive",
228
+ ]
229
+
230
+ [[package]]
231
+ name = "serde_derive"
232
+ version = "1.0.228"
233
+ source = "registry+https://github.com/rust-lang/crates.io-index"
234
+ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
235
+ dependencies = [
236
+ "proc-macro2",
237
+ "quote",
238
+ "syn",
239
+ ]
240
+
241
+ [[package]]
242
+ name = "syn"
243
+ version = "2.0.117"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
246
+ dependencies = [
247
+ "proc-macro2",
248
+ "quote",
249
+ "unicode-ident",
250
+ ]
251
+
252
+ [[package]]
253
+ name = "target-lexicon"
254
+ version = "0.13.5"
255
+ source = "registry+https://github.com/rust-lang/crates.io-index"
256
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
257
+
258
+ [[package]]
259
+ name = "unicode-ident"
260
+ version = "1.0.24"
261
+ source = "registry+https://github.com/rust-lang/crates.io-index"
262
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
ewal-0.1.0/Cargo.toml ADDED
@@ -0,0 +1,3 @@
1
+ [workspace]
2
+ members = ["crates/ewal-core", "crates/ewal-py"]
3
+ resolver = "2"
ewal-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: ewal
3
+ Version: 0.1.0
4
+ Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
5
+ Classifier: Development Status :: 3 - Alpha
6
+ Classifier: Programming Language :: Rust
7
+ Classifier: Programming Language :: Python :: 3
8
+ Summary: Embedded Waveform Analysis Library
9
+ Keywords: verilog,vhdl,vcd,fst,eda,fpga,waveform
10
+ License: BSD-3-Clause
11
+ Requires-Python: >=3.9
@@ -0,0 +1,14 @@
1
+ [package]
2
+ name = "ewal-core"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [dependencies]
7
+ memmap2 = "0.9"
8
+ csv = { version = "1.3", optional = true }
9
+ rayon = "1.10"
10
+
11
+ [features]
12
+ default = ["vcd", "csv_format"]
13
+ vcd = []
14
+ csv_format = ["dep:csv"]
@@ -0,0 +1,141 @@
1
+ use crate::signal::bitpack::{bit_slice, sign_extend};
2
+ use crate::signal::SignalValue;
3
+ use crate::trace::{SignalId, Trace};
4
+
5
+ /// Expression tree compiled from Python-side SignalExpr.
6
+ #[derive(Clone)]
7
+ pub enum Expr {
8
+ Signal(SignalId),
9
+ Const(SignalValue),
10
+ RelEval(Box<Expr>, i64),
11
+ Not(Box<Expr>),
12
+ And(Box<Expr>, Box<Expr>),
13
+ Or(Box<Expr>, Box<Expr>),
14
+ Eq(Box<Expr>, Box<Expr>),
15
+ Neq(Box<Expr>, Box<Expr>),
16
+ Gt(Box<Expr>, Box<Expr>),
17
+ Lt(Box<Expr>, Box<Expr>),
18
+ Gte(Box<Expr>, Box<Expr>),
19
+ Lte(Box<Expr>, Box<Expr>),
20
+ Add(Box<Expr>, Box<Expr>),
21
+ Sub(Box<Expr>, Box<Expr>),
22
+ BitAnd(Box<Expr>, Box<Expr>),
23
+ BitOr(Box<Expr>, Box<Expr>),
24
+ BitXor(Box<Expr>, Box<Expr>),
25
+ Slice(Box<Expr>, u32, u32),
26
+ Signed(Box<Expr>),
27
+ True,
28
+ }
29
+
30
+ /// Evaluate an expression at a given index, returning a SignalValue.
31
+ pub fn eval(trace: &dyn Trace, expr: &Expr, index: usize) -> SignalValue {
32
+ match expr {
33
+ Expr::Signal(id) => {
34
+ trace.signal_value_by_id(*id, index)
35
+ }
36
+ Expr::Const(v) => v.clone(),
37
+ Expr::True => SignalValue::Scalar(1),
38
+ Expr::RelEval(e, offset) => {
39
+ let new_idx = index as i64 + offset;
40
+ if new_idx < 0 || new_idx as usize > trace.max_index() {
41
+ SignalValue::Unknown
42
+ } else {
43
+ eval(trace, e, new_idx as usize)
44
+ }
45
+ }
46
+ Expr::Not(e) => {
47
+ let v = eval(trace, e, index);
48
+ SignalValue::Scalar(if v.is_truthy() { 0 } else { 1 })
49
+ }
50
+ Expr::And(a, b) => {
51
+ let va = eval(trace, a, index);
52
+ let vb = eval(trace, b, index);
53
+ SignalValue::Scalar(if va.is_truthy() && vb.is_truthy() { 1 } else { 0 })
54
+ }
55
+ Expr::Or(a, b) => {
56
+ let va = eval(trace, a, index);
57
+ let vb = eval(trace, b, index);
58
+ SignalValue::Scalar(if va.is_truthy() || vb.is_truthy() { 1 } else { 0 })
59
+ }
60
+ Expr::Eq(a, b) => {
61
+ let va = eval(trace, a, index);
62
+ let vb = eval(trace, b, index);
63
+ SignalValue::Scalar(if va == vb { 1 } else {
64
+ // Also compare numerically
65
+ match (va.to_u64(), vb.to_u64()) {
66
+ (Some(x), Some(y)) if x == y => 1,
67
+ _ => 0,
68
+ }
69
+ })
70
+ }
71
+ Expr::Neq(a, b) => {
72
+ let eq = eval(trace, &Expr::Eq(Box::new(Expr::Const(eval(trace, a, index))),
73
+ Box::new(Expr::Const(eval(trace, b, index)))), index);
74
+ SignalValue::Scalar(if eq.is_truthy() { 0 } else { 1 })
75
+ }
76
+ Expr::Gt(a, b) => binop_cmp(trace, a, b, index, |x, y| x > y),
77
+ Expr::Lt(a, b) => binop_cmp(trace, a, b, index, |x, y| x < y),
78
+ Expr::Gte(a, b) => binop_cmp(trace, a, b, index, |x, y| x >= y),
79
+ Expr::Lte(a, b) => binop_cmp(trace, a, b, index, |x, y| x <= y),
80
+ Expr::Add(a, b) => binop_arith(trace, a, b, index, |x, y| x.wrapping_add(y)),
81
+ Expr::Sub(a, b) => binop_arith(trace, a, b, index, |x, y| x.wrapping_sub(y)),
82
+ Expr::BitAnd(a, b) => binop_arith(trace, a, b, index, |x, y| x & y),
83
+ Expr::BitOr(a, b) => binop_arith(trace, a, b, index, |x, y| x | y),
84
+ Expr::BitXor(a, b) => binop_arith(trace, a, b, index, |x, y| x ^ y),
85
+ Expr::Slice(e, high, low) => {
86
+ let v = eval(trace, e, index);
87
+ match v.to_u64() {
88
+ Some(val) => SignalValue::Scalar(bit_slice(val, *high, *low)),
89
+ None => SignalValue::Unknown,
90
+ }
91
+ }
92
+ Expr::Signed(e) => {
93
+ let v = eval(trace, e, index);
94
+ // Find width from context - use 64 as default
95
+ match v.to_u64() {
96
+ Some(val) => {
97
+ // Try to determine width; default to 64
98
+ let width = 64u32;
99
+ let signed = sign_extend(val, width);
100
+ SignalValue::Scalar(signed as u64)
101
+ }
102
+ None => SignalValue::Unknown,
103
+ }
104
+ }
105
+ }
106
+ }
107
+
108
+ fn binop_cmp(
109
+ trace: &dyn Trace,
110
+ a: &Expr,
111
+ b: &Expr,
112
+ index: usize,
113
+ f: fn(u64, u64) -> bool,
114
+ ) -> SignalValue {
115
+ let va = eval(trace, a, index);
116
+ let vb = eval(trace, b, index);
117
+ match (va.to_u64(), vb.to_u64()) {
118
+ (Some(x), Some(y)) => SignalValue::Scalar(if f(x, y) { 1 } else { 0 }),
119
+ _ => SignalValue::Unknown,
120
+ }
121
+ }
122
+
123
+ fn binop_arith(
124
+ trace: &dyn Trace,
125
+ a: &Expr,
126
+ b: &Expr,
127
+ index: usize,
128
+ f: fn(u64, u64) -> u64,
129
+ ) -> SignalValue {
130
+ let va = eval(trace, a, index);
131
+ let vb = eval(trace, b, index);
132
+ match (va.to_u64(), vb.to_u64()) {
133
+ (Some(x), Some(y)) => SignalValue::Scalar(f(x, y)),
134
+ _ => SignalValue::Unknown,
135
+ }
136
+ }
137
+
138
+ /// Evaluate an expression as a boolean at a given index.
139
+ pub fn eval_bool(trace: &dyn Trace, expr: &Expr, index: usize) -> bool {
140
+ eval(trace, expr, index).is_truthy()
141
+ }
@@ -0,0 +1,33 @@
1
+ use crate::eval::expr::{eval_bool, Expr};
2
+ use crate::trace::Trace;
3
+ use rayon::prelude::*;
4
+
5
+ /// Find all timestep indices where the expression evaluates to true.
6
+ /// Single-threaded — iterates sequentially.
7
+ pub fn find(trace: &dyn Trace, expr: &Expr) -> Vec<usize> {
8
+ (0..=trace.max_index())
9
+ .filter(|&idx| eval_bool(trace, expr, idx))
10
+ .collect()
11
+ }
12
+
13
+ /// Find all matching indices using rayon parallel iteration.
14
+ /// Splits the index range across CPU cores for true multi-core evaluation.
15
+ /// The Trace must be Send+Sync (which our trait requires).
16
+ pub fn parallel_find(trace: &dyn Trace, expr: &Expr) -> Vec<usize> {
17
+ let max = trace.max_index();
18
+ let mut results: Vec<usize> = (0..=max)
19
+ .into_par_iter()
20
+ .filter(|&idx| eval_bool(trace, expr, idx))
21
+ .collect();
22
+ results.sort_unstable();
23
+ results
24
+ }
25
+
26
+ /// Count matching indices in parallel.
27
+ pub fn parallel_count(trace: &dyn Trace, expr: &Expr) -> usize {
28
+ let max = trace.max_index();
29
+ (0..=max)
30
+ .into_par_iter()
31
+ .filter(|&idx| eval_bool(trace, expr, idx))
32
+ .count()
33
+ }
@@ -0,0 +1,3 @@
1
+ pub mod expr;
2
+ pub mod find;
3
+ pub mod scope;
@@ -0,0 +1,76 @@
1
+ use crate::trace::Trace;
2
+
3
+ /// Resolve a short signal name within a scope prefix to a full path.
4
+ /// For example, resolve("tb.dut", "counter", trace) -> Some("tb.dut.counter")
5
+ pub fn resolve(trace: &dyn Trace, scope: &str, short_name: &str) -> Option<String> {
6
+ let candidate = if scope.is_empty() {
7
+ short_name.to_string()
8
+ } else {
9
+ format!("{}.{}", scope, short_name)
10
+ };
11
+ if trace.signal_id(&candidate).is_some() {
12
+ return Some(candidate);
13
+ }
14
+ // Try finding any signal ending with the short name
15
+ for name in trace.signal_names() {
16
+ if name.ends_with(short_name) {
17
+ let prefix = &name[..name.len() - short_name.len()];
18
+ if prefix.is_empty() || prefix.ends_with('.') {
19
+ if scope.is_empty() || name.starts_with(scope) {
20
+ return Some(name.clone());
21
+ }
22
+ }
23
+ }
24
+ }
25
+ None
26
+ }
27
+
28
+ /// List signals that are direct children of the given scope.
29
+ pub fn local_signals(trace: &dyn Trace, scope: &str) -> Vec<String> {
30
+ let prefix = if scope.is_empty() {
31
+ String::new()
32
+ } else {
33
+ format!("{}.", scope)
34
+ };
35
+
36
+ trace
37
+ .signal_names()
38
+ .iter()
39
+ .filter(|name| {
40
+ if prefix.is_empty() {
41
+ !name.contains('.')
42
+ } else if let Some(rest) = name.strip_prefix(&prefix) {
43
+ !rest.contains('.')
44
+ } else {
45
+ false
46
+ }
47
+ })
48
+ .cloned()
49
+ .collect()
50
+ }
51
+
52
+ /// List child scopes of the given scope.
53
+ pub fn local_scopes(trace: &dyn Trace, scope: &str) -> Vec<String> {
54
+ let prefix = if scope.is_empty() {
55
+ String::new()
56
+ } else {
57
+ format!("{}.", scope)
58
+ };
59
+
60
+ let mut result: Vec<String> = Vec::new();
61
+ for s in trace.scopes() {
62
+ if prefix.is_empty() {
63
+ if !s.contains('.') && !result.contains(s) {
64
+ result.push(s.clone());
65
+ }
66
+ } else if let Some(rest) = s.strip_prefix(&prefix) {
67
+ // Direct child scope: no more dots
68
+ let child = rest.split('.').next().unwrap_or(rest);
69
+ let full = format!("{}.{}", scope, child);
70
+ if !result.contains(&full) {
71
+ result.push(full);
72
+ }
73
+ }
74
+ }
75
+ result
76
+ }
@@ -0,0 +1,4 @@
1
+ pub mod signal;
2
+ pub mod trace;
3
+ pub mod eval;
4
+ pub mod util;
@@ -0,0 +1,84 @@
1
+ use super::{Logic, SignalValue};
2
+
3
+ /// Parse a VCD binary string like "b0101" or "b01xz" into a SignalValue.
4
+ pub fn parse_binary_str(s: &str) -> SignalValue {
5
+ let s = s.strip_prefix('b').or_else(|| s.strip_prefix('B')).unwrap_or(s);
6
+ if s.is_empty() {
7
+ return SignalValue::Unknown;
8
+ }
9
+
10
+ let has_xz = s.chars().any(|c| c == 'x' || c == 'X' || c == 'z' || c == 'Z');
11
+ if has_xz {
12
+ let bits: Vec<Logic> = s
13
+ .chars()
14
+ .map(|c| match c {
15
+ '0' => Logic::Zero,
16
+ '1' => Logic::One,
17
+ 'x' | 'X' => Logic::X,
18
+ 'z' | 'Z' => Logic::Z,
19
+ _ => Logic::X,
20
+ })
21
+ .collect();
22
+ SignalValue::FourState(bits)
23
+ } else {
24
+ let mut val: u64 = 0;
25
+ for c in s.chars() {
26
+ val = (val << 1) | if c == '1' { 1 } else { 0 };
27
+ }
28
+ SignalValue::Scalar(val)
29
+ }
30
+ }
31
+
32
+ /// Extract bits [high:low] from val.
33
+ pub fn bit_slice(val: u64, high: u32, low: u32) -> u64 {
34
+ if high < low {
35
+ return 0;
36
+ }
37
+ let width = high - low + 1;
38
+ if width >= 64 {
39
+ return val >> low;
40
+ }
41
+ let mask = (1u64 << width) - 1;
42
+ (val >> low) & mask
43
+ }
44
+
45
+ /// Sign-extend a value of given bit width to i64.
46
+ pub fn sign_extend(val: u64, width: u32) -> i64 {
47
+ if width == 0 || width > 64 {
48
+ return val as i64;
49
+ }
50
+ if width == 64 {
51
+ return val as i64;
52
+ }
53
+ let sign_bit = 1u64 << (width - 1);
54
+ if val & sign_bit != 0 {
55
+ let mask = !((1u64 << width) - 1);
56
+ (val | mask) as i64
57
+ } else {
58
+ val as i64
59
+ }
60
+ }
61
+
62
+ #[cfg(test)]
63
+ mod tests {
64
+ use super::*;
65
+
66
+ #[test]
67
+ fn test_parse_binary() {
68
+ assert_eq!(parse_binary_str("b0101"), SignalValue::Scalar(5));
69
+ assert_eq!(parse_binary_str("b0000"), SignalValue::Scalar(0));
70
+ assert_eq!(parse_binary_str("b1111"), SignalValue::Scalar(15));
71
+ }
72
+
73
+ #[test]
74
+ fn test_bit_slice() {
75
+ assert_eq!(bit_slice(0b11010, 4, 1), 0b1101);
76
+ assert_eq!(bit_slice(0xFF, 3, 0), 0xF);
77
+ }
78
+
79
+ #[test]
80
+ fn test_sign_extend() {
81
+ assert_eq!(sign_extend(0b1111, 4), -1i64);
82
+ assert_eq!(sign_extend(0b0111, 4), 7i64);
83
+ }
84
+ }