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.
- ewal-0.1.0/Cargo.lock +262 -0
- ewal-0.1.0/Cargo.toml +3 -0
- ewal-0.1.0/PKG-INFO +11 -0
- ewal-0.1.0/crates/ewal-core/Cargo.toml +14 -0
- ewal-0.1.0/crates/ewal-core/src/eval/expr.rs +141 -0
- ewal-0.1.0/crates/ewal-core/src/eval/find.rs +33 -0
- ewal-0.1.0/crates/ewal-core/src/eval/mod.rs +3 -0
- ewal-0.1.0/crates/ewal-core/src/eval/scope.rs +76 -0
- ewal-0.1.0/crates/ewal-core/src/lib.rs +4 -0
- ewal-0.1.0/crates/ewal-core/src/signal/bitpack.rs +84 -0
- ewal-0.1.0/crates/ewal-core/src/signal/mod.rs +68 -0
- ewal-0.1.0/crates/ewal-core/src/signal/store.rs +89 -0
- ewal-0.1.0/crates/ewal-core/src/signal/virtual.rs +42 -0
- ewal-0.1.0/crates/ewal-core/src/trace/csv.rs +135 -0
- ewal-0.1.0/crates/ewal-core/src/trace/mod.rs +23 -0
- ewal-0.1.0/crates/ewal-core/src/trace/vcd.rs +257 -0
- ewal-0.1.0/crates/ewal-core/src/trace/virtual_trace.rs +96 -0
- ewal-0.1.0/crates/ewal-core/src/util/bitops.rs +44 -0
- ewal-0.1.0/crates/ewal-core/src/util/mod.rs +1 -0
- ewal-0.1.0/crates/ewal-py/Cargo.toml +12 -0
- ewal-0.1.0/crates/ewal-py/src/lib.rs +260 -0
- ewal-0.1.0/pyproject.toml +29 -0
- ewal-0.1.0/python/ewal/__init__.py +205 -0
- ewal-0.1.0/python/ewal/_core.pyi +57 -0
- ewal-0.1.0/python/ewal/assertions.py +315 -0
- ewal-0.1.0/python/ewal/cdc.py +258 -0
- ewal-0.1.0/python/ewal/cli.py +112 -0
- ewal-0.1.0/python/ewal/cocotb_compat.py +82 -0
- ewal-0.1.0/python/ewal/compat.py +59 -0
- ewal-0.1.0/python/ewal/container.py +37 -0
- ewal-0.1.0/python/ewal/correlation.py +154 -0
- ewal-0.1.0/python/ewal/debug.py +196 -0
- ewal-0.1.0/python/ewal/diffing.py +192 -0
- ewal-0.1.0/python/ewal/dsl.py +20 -0
- ewal-0.1.0/python/ewal/export.py +117 -0
- ewal-0.1.0/python/ewal/fsm.py +219 -0
- ewal-0.1.0/python/ewal/markers.py +205 -0
- ewal-0.1.0/python/ewal/notebook.py +210 -0
- ewal-0.1.0/python/ewal/pipeline.py +270 -0
- ewal-0.1.0/python/ewal/plugins.py +118 -0
- ewal-0.1.0/python/ewal/power.py +154 -0
- ewal-0.1.0/python/ewal/protocol/__init__.py +27 -0
- ewal-0.1.0/python/ewal/protocol/apb.py +74 -0
- ewal-0.1.0/python/ewal/protocol/avalon.py +91 -0
- ewal-0.1.0/python/ewal/protocol/axi4.py +239 -0
- ewal-0.1.0/python/ewal/protocol/axi4_stream.py +80 -0
- ewal-0.1.0/python/ewal/protocol/base.py +107 -0
- ewal-0.1.0/python/ewal/protocol/wishbone.py +70 -0
- ewal-0.1.0/python/ewal/query.py +301 -0
- ewal-0.1.0/python/ewal/repl.py +13 -0
- ewal-0.1.0/python/ewal/scope.py +14 -0
- ewal-0.1.0/python/ewal/signal.py +268 -0
- ewal-0.1.0/python/ewal/stdlib.py +37 -0
- ewal-0.1.0/python/ewal/streaming.py +298 -0
- ewal-0.1.0/python/ewal/temporal.py +266 -0
- ewal-0.1.0/python/ewal/trace.py +497 -0
- ewal-0.1.0/python/ewal/virtual.py +16 -0
- 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
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,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,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
|
+
}
|