najaeda 0.3.3__cp314-cp314-win_amd64.whl
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.
- najaeda/__init__.py +20 -0
- najaeda/_version.py +16 -0
- najaeda/docs/.readthedocs.yaml +26 -0
- najaeda/docs/requirements.txt +7 -0
- najaeda/docs/source/api.rst +7 -0
- najaeda/docs/source/common_classes.rst +11 -0
- najaeda/docs/source/conf.py +57 -0
- najaeda/docs/source/equipotential.rst +15 -0
- najaeda/docs/source/examples.rst.in +66 -0
- najaeda/docs/source/index.rst +17 -0
- najaeda/docs/source/instance.rst +34 -0
- najaeda/docs/source/introduction.rst +68 -0
- najaeda/docs/source/net.rst +20 -0
- najaeda/docs/source/netlist_classes.rst +11 -0
- najaeda/docs/source/preprocessor.py +73 -0
- najaeda/docs/source/term.rst +20 -0
- najaeda/docs/source/visitors.rst +13 -0
- najaeda/instance_visitor.py +43 -0
- najaeda/naja.pyd +0 -0
- najaeda/naja_bne.dll +0 -0
- najaeda/naja_dnl.dll +0 -0
- najaeda/naja_metrics.dll +0 -0
- najaeda/naja_nl.dll +0 -0
- najaeda/naja_opt.dll +0 -0
- najaeda/naja_python.dll +0 -0
- najaeda/native/__init__.py +0 -0
- najaeda/native/stats.py +341 -0
- najaeda/net_visitor.py +53 -0
- najaeda/netlist.py +1990 -0
- najaeda/pandas_stats.py +32 -0
- najaeda/primitives/__init__.py +0 -0
- najaeda/primitives/utils.py +19 -0
- najaeda/primitives/xilinx.py +541 -0
- najaeda/primitives/yosys.py +288 -0
- najaeda/stats.py +410 -0
- najaeda-0.3.3.dist-info/DELVEWHEEL +2 -0
- najaeda-0.3.3.dist-info/METADATA +108 -0
- najaeda-0.3.3.dist-info/RECORD +44 -0
- najaeda-0.3.3.dist-info/WHEEL +5 -0
- najaeda-0.3.3.dist-info/licenses/AUTHORS +7 -0
- najaeda-0.3.3.dist-info/licenses/LICENSE +201 -0
- najaeda.libs/msvcp140.dll +0 -0
- najaeda.libs/tbb12.dll +0 -0
- najaeda.libs/tbbmalloc.dll +0 -0
najaeda/netlist.py
ADDED
|
@@ -0,0 +1,1990 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 The Naja authors
|
|
2
|
+
# <https://github.com/najaeda/naja/blob/main/AUTHORS>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
|
|
6
|
+
import time
|
|
7
|
+
import logging
|
|
8
|
+
import hashlib
|
|
9
|
+
import struct
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Union, List, Iterator
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
from najaeda import naja
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def consistent_hash(obj):
|
|
20
|
+
def default_serializer(o):
|
|
21
|
+
if isinstance(o, (str, int, float, bool, type(None))):
|
|
22
|
+
return o
|
|
23
|
+
elif isinstance(o, (list, tuple)):
|
|
24
|
+
return [default_serializer(i) for i in o]
|
|
25
|
+
else:
|
|
26
|
+
return str(o)
|
|
27
|
+
|
|
28
|
+
def hash_value(value):
|
|
29
|
+
if isinstance(value, int):
|
|
30
|
+
return struct.pack("!q", value)
|
|
31
|
+
else:
|
|
32
|
+
raise TypeError(f"Unsupported type: {type(value)}")
|
|
33
|
+
|
|
34
|
+
def hash_object(o):
|
|
35
|
+
if isinstance(o, (list, tuple)):
|
|
36
|
+
return b"".join(hash_object(i) for i in o)
|
|
37
|
+
else:
|
|
38
|
+
return hash_value(o)
|
|
39
|
+
|
|
40
|
+
serialized_obj = default_serializer(obj)
|
|
41
|
+
obj_bytes = hash_object(serialized_obj)
|
|
42
|
+
return int(hashlib.sha256(obj_bytes).hexdigest(), 16)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_snl_instance_from_id_list(id_list: list) -> naja.SNLInstance:
|
|
46
|
+
design = naja.NLUniverse.get().getTopDesign()
|
|
47
|
+
# instance = None
|
|
48
|
+
# for id in id_list:
|
|
49
|
+
# instance = design.getInstanceByID(id)
|
|
50
|
+
# assert instance is not None
|
|
51
|
+
# design = instance.getModel()
|
|
52
|
+
# return instance
|
|
53
|
+
return design.getInstanceByIDList(id_list)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_snl_path_from_id_list(id_list: list) -> naja.SNLPath:
|
|
57
|
+
top = naja.NLUniverse.get().getTopDesign()
|
|
58
|
+
design = top
|
|
59
|
+
path = naja.SNLPath()
|
|
60
|
+
for id in id_list:
|
|
61
|
+
instance = design.getInstanceByID(id)
|
|
62
|
+
assert instance is not None
|
|
63
|
+
path = naja.SNLPath(path, instance)
|
|
64
|
+
assert path.getTailInstance() is not None
|
|
65
|
+
design = instance.getModel()
|
|
66
|
+
if len(id_list) > 0:
|
|
67
|
+
assert path.getTailInstance() is not None
|
|
68
|
+
return path
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Attribute:
|
|
72
|
+
def __init__(self, snlAttribute):
|
|
73
|
+
self.snlAttribute = snlAttribute
|
|
74
|
+
|
|
75
|
+
def __str__(self):
|
|
76
|
+
return str(self.snlAttribute)
|
|
77
|
+
|
|
78
|
+
def get_name(self):
|
|
79
|
+
"""
|
|
80
|
+
:return: the name of the attribute.
|
|
81
|
+
:rtype: str
|
|
82
|
+
"""
|
|
83
|
+
return self.snlAttribute.getName()
|
|
84
|
+
|
|
85
|
+
def has_value(self):
|
|
86
|
+
"""
|
|
87
|
+
:return: True if the attribute has a value.
|
|
88
|
+
:rtype: bool
|
|
89
|
+
"""
|
|
90
|
+
return self.snlAttribute.hasValue()
|
|
91
|
+
|
|
92
|
+
def get_value(self):
|
|
93
|
+
"""
|
|
94
|
+
:return: the value of the attribute.
|
|
95
|
+
:rtype: str
|
|
96
|
+
"""
|
|
97
|
+
return self.snlAttribute.getValue()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class Equipotential:
|
|
101
|
+
"""Class that represents the term and wraps
|
|
102
|
+
some of the snl occurrence API.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(self, term):
|
|
106
|
+
path = get_snl_path_from_id_list(term.pathIDs)
|
|
107
|
+
snl_term = get_snl_term_for_ids_with_path(path, term.termID, term.bit)
|
|
108
|
+
if isinstance(snl_term, naja.SNLBusTerm):
|
|
109
|
+
raise ValueError("Equipotential cannot be constructed on bus term")
|
|
110
|
+
if len(term.pathIDs) == 0:
|
|
111
|
+
self.equi = naja.SNLEquipotential(snl_term)
|
|
112
|
+
else:
|
|
113
|
+
ito = naja.SNLOccurrence(
|
|
114
|
+
path.getHeadPath(), path.getTailInstance().getInstTerm(snl_term)
|
|
115
|
+
)
|
|
116
|
+
self.equi = naja.SNLEquipotential(ito)
|
|
117
|
+
|
|
118
|
+
def __eq__(self, value):
|
|
119
|
+
return self.equi == value.equi
|
|
120
|
+
|
|
121
|
+
def dump_dot(self, path: str):
|
|
122
|
+
"""Dump the dot file of this equipotential."""
|
|
123
|
+
self.equi.dumpDotFile(path)
|
|
124
|
+
|
|
125
|
+
def get_inst_terms(self):
|
|
126
|
+
"""Iterate over the instance terminals of this equipotential.
|
|
127
|
+
|
|
128
|
+
:return: an iterator over the instance terminals of this equipotential.
|
|
129
|
+
:rtype: Iterator[Term]
|
|
130
|
+
"""
|
|
131
|
+
if self.equi is not None:
|
|
132
|
+
for term in self.equi.getInstTermOccurrences():
|
|
133
|
+
path = term.getPath().getInstanceIDs()
|
|
134
|
+
path.append(term.getInstTerm().getInstance().getID())
|
|
135
|
+
yield Term(path,
|
|
136
|
+
term.getInstTerm().getBitTerm())
|
|
137
|
+
|
|
138
|
+
def get_top_terms(self):
|
|
139
|
+
"""Iterate over the top terminals of this equipotential.
|
|
140
|
+
|
|
141
|
+
:return: an iterator over the top terminals of this equipotential.
|
|
142
|
+
:rtype: Iterator[Term]
|
|
143
|
+
"""
|
|
144
|
+
if self.equi is not None:
|
|
145
|
+
for term in self.equi.getTerms():
|
|
146
|
+
yield Term([], term)
|
|
147
|
+
|
|
148
|
+
def get_leaf_readers(self, filter=None):
|
|
149
|
+
if self.equi is not None:
|
|
150
|
+
for term in self.equi.getInstTermOccurrences():
|
|
151
|
+
direction = term.getInstTerm().getDirection()
|
|
152
|
+
if (
|
|
153
|
+
direction != naja.SNLTerm.Direction.Output and
|
|
154
|
+
term.getInstTerm().getInstance().getModel().isLeaf() and
|
|
155
|
+
(filter is None or filter(term))
|
|
156
|
+
):
|
|
157
|
+
path = term.getPath().getInstanceIDs()
|
|
158
|
+
path.append(term.getInstTerm().getInstance().getID())
|
|
159
|
+
yield Term(path, term.getInstTerm().getBitTerm())
|
|
160
|
+
|
|
161
|
+
def get_leaf_drivers(self, filter=None):
|
|
162
|
+
if self.equi is not None:
|
|
163
|
+
for term in self.equi.getInstTermOccurrences():
|
|
164
|
+
direction = term.getInstTerm().getDirection()
|
|
165
|
+
if (
|
|
166
|
+
direction != naja.SNLTerm.Direction.Input and
|
|
167
|
+
term.getInstTerm().getInstance().getModel().isLeaf() and
|
|
168
|
+
(filter is None or filter(term))
|
|
169
|
+
):
|
|
170
|
+
path = term.getPath().getInstanceIDs()
|
|
171
|
+
path.append(term.getInstTerm().getInstance().getID())
|
|
172
|
+
yield Term(path, term.getInstTerm().getBitTerm())
|
|
173
|
+
|
|
174
|
+
def get_top_readers(self):
|
|
175
|
+
if self.equi is not None:
|
|
176
|
+
for term in self.equi.getTerms():
|
|
177
|
+
direction = term.getDirection()
|
|
178
|
+
if direction != naja.SNLTerm.Direction.Input:
|
|
179
|
+
yield Term([], term)
|
|
180
|
+
|
|
181
|
+
def get_top_drivers(self):
|
|
182
|
+
if self.equi is not None:
|
|
183
|
+
for term in self.equi.getTerms():
|
|
184
|
+
direction = term.getDirection()
|
|
185
|
+
if direction != naja.SNLTerm.Direction.Output:
|
|
186
|
+
yield Term([], term)
|
|
187
|
+
|
|
188
|
+
def is_const(self) -> bool:
|
|
189
|
+
"""Check if this equipotential is a constant generator.
|
|
190
|
+
|
|
191
|
+
:return: True if this equipotential is a constant generator.
|
|
192
|
+
:rtype: bool
|
|
193
|
+
"""
|
|
194
|
+
return self.is_const0() or self.is_const1()
|
|
195
|
+
|
|
196
|
+
def is_const0(self) -> bool:
|
|
197
|
+
"""Check if this equipotential is a constant 0 generator.
|
|
198
|
+
|
|
199
|
+
:return: True if this equipotential is a constant 0 generator.
|
|
200
|
+
:rtype: bool
|
|
201
|
+
"""
|
|
202
|
+
return self.equi.isConst0()
|
|
203
|
+
|
|
204
|
+
def is_const1(self) -> bool:
|
|
205
|
+
"""Check if this equipotential is a constant 1 generator.
|
|
206
|
+
|
|
207
|
+
:return: True if this equipotential is a constant 1 generator.
|
|
208
|
+
:rtype: bool
|
|
209
|
+
"""
|
|
210
|
+
return self.equi.isConst1()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class Net:
|
|
214
|
+
class Type(Enum):
|
|
215
|
+
STANDARD = naja.SNLNet.Type.Standard
|
|
216
|
+
ASSIGN0 = naja.SNLNet.Type.Assign0
|
|
217
|
+
ASSIGN1 = naja.SNLNet.Type.Assign1
|
|
218
|
+
SUPPLY0 = naja.SNLNet.Type.Supply0
|
|
219
|
+
SUPPLY1 = naja.SNLNet.Type.Supply1
|
|
220
|
+
|
|
221
|
+
def __init__(self, path, net=None, net_concat=None):
|
|
222
|
+
if net is not None and net_concat is not None:
|
|
223
|
+
raise ValueError(
|
|
224
|
+
"Only one of `net` or `net_concat` should be provided, not both."
|
|
225
|
+
)
|
|
226
|
+
if isinstance(path, naja.SNLPath):
|
|
227
|
+
if path.size() > 0:
|
|
228
|
+
self.pathIDs = path.getInstanceIDs()
|
|
229
|
+
else:
|
|
230
|
+
self.pathIDs = []
|
|
231
|
+
elif isinstance(path, list):
|
|
232
|
+
self.pathIDs = path.copy()
|
|
233
|
+
if net is not None:
|
|
234
|
+
self.net = net
|
|
235
|
+
elif net_concat is not None:
|
|
236
|
+
self.net_concat = net_concat
|
|
237
|
+
|
|
238
|
+
def key(self):
|
|
239
|
+
"""Stable, hashable identity for Net."""
|
|
240
|
+
if hasattr(self, "net"):
|
|
241
|
+
return (tuple(self.pathIDs), ("net", self.net))
|
|
242
|
+
else:
|
|
243
|
+
return (tuple(self.pathIDs), ("concat", tuple(self.net_concat)))
|
|
244
|
+
|
|
245
|
+
def __eq__(self, other):
|
|
246
|
+
if not isinstance(other, Net):
|
|
247
|
+
return NotImplemented
|
|
248
|
+
return self.key() == other.key()
|
|
249
|
+
|
|
250
|
+
def __hash__(self):
|
|
251
|
+
return hash(self.key())
|
|
252
|
+
|
|
253
|
+
def __str__(self):
|
|
254
|
+
if self.is_concat():
|
|
255
|
+
net_str = "{" + ",".join(map(str, self.net_concat)) + "}"
|
|
256
|
+
return net_str
|
|
257
|
+
net_str = str(self.net)
|
|
258
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
259
|
+
if path.size() > 0:
|
|
260
|
+
return f"{path}/{net_str}"
|
|
261
|
+
else:
|
|
262
|
+
return net_str
|
|
263
|
+
|
|
264
|
+
def delete(self):
|
|
265
|
+
if hasattr(self, "net"):
|
|
266
|
+
self.net.destroy()
|
|
267
|
+
else:
|
|
268
|
+
for net in self.net_concat:
|
|
269
|
+
net.destroy()
|
|
270
|
+
|
|
271
|
+
def get_name(self) -> str:
|
|
272
|
+
"""
|
|
273
|
+
:return: the name of this Net.
|
|
274
|
+
:rtype: str
|
|
275
|
+
"""
|
|
276
|
+
if hasattr(self, "net"):
|
|
277
|
+
return self.net.getName()
|
|
278
|
+
return "{" + ",".join(map(str, self.net_concat)) + "}"
|
|
279
|
+
|
|
280
|
+
def set_name(self, name: str):
|
|
281
|
+
"""
|
|
282
|
+
:param str name: the name to set for this Net.
|
|
283
|
+
"""
|
|
284
|
+
if self.is_concat():
|
|
285
|
+
raise ValueError(
|
|
286
|
+
f"set_name of {self}: cannot set name for a concatenated net. "
|
|
287
|
+
"Use the individual nets instead."
|
|
288
|
+
)
|
|
289
|
+
elif self.is_bus_bit():
|
|
290
|
+
raise ValueError(
|
|
291
|
+
f"set_name of {self}: cannot set name for a bus bit. "
|
|
292
|
+
"Use the bus net instead."
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
# We need to uniquify until parent instance if parent instance is not top.
|
|
296
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
297
|
+
if path.size():
|
|
298
|
+
naja.SNLUniquifier(path.getHeadPath())
|
|
299
|
+
self.net.setName(name)
|
|
300
|
+
|
|
301
|
+
def get_msb(self) -> int:
|
|
302
|
+
"""
|
|
303
|
+
:return: the most significant bit of the net if it is a bus.
|
|
304
|
+
:rtype: int
|
|
305
|
+
"""
|
|
306
|
+
if hasattr(self, "net") and isinstance(self.net, naja.SNLBusNet):
|
|
307
|
+
return self.net.getMSB()
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
def get_lsb(self) -> int:
|
|
311
|
+
"""
|
|
312
|
+
:return: the least significant bit of the net if it is a bus.
|
|
313
|
+
:rtype: int
|
|
314
|
+
"""
|
|
315
|
+
if hasattr(self, "net") and isinstance(self.net, naja.SNLBusNet):
|
|
316
|
+
return self.net.getLSB()
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
def is_bus(self) -> bool:
|
|
320
|
+
"""
|
|
321
|
+
:return: True if the net is a bus.
|
|
322
|
+
:rtype: bool
|
|
323
|
+
"""
|
|
324
|
+
return hasattr(self, "net") and isinstance(self.net, naja.SNLBusNet)
|
|
325
|
+
|
|
326
|
+
def is_bus_bit(self) -> bool:
|
|
327
|
+
"""
|
|
328
|
+
:return: True if the net is a bit of a bus.
|
|
329
|
+
:rtype: bool
|
|
330
|
+
"""
|
|
331
|
+
return hasattr(self, "net") and isinstance(self.net, naja.SNLBusNetBit)
|
|
332
|
+
|
|
333
|
+
def is_scalar(self) -> bool:
|
|
334
|
+
"""
|
|
335
|
+
:return: True if the net is a scalar.
|
|
336
|
+
:rtype: bool
|
|
337
|
+
"""
|
|
338
|
+
return hasattr(self, "net") and isinstance(self.net, naja.SNLScalarNet)
|
|
339
|
+
|
|
340
|
+
def is_bit(self) -> bool:
|
|
341
|
+
"""
|
|
342
|
+
:return: True if the net is a bit.
|
|
343
|
+
:rtype: bool
|
|
344
|
+
"""
|
|
345
|
+
return self.is_scalar() or self.is_bus_bit()
|
|
346
|
+
|
|
347
|
+
def is_concat(self) -> bool:
|
|
348
|
+
"""
|
|
349
|
+
:return: True if the net is a concatenation.
|
|
350
|
+
:rtype: bool
|
|
351
|
+
"""
|
|
352
|
+
return hasattr(self, "net_concat")
|
|
353
|
+
|
|
354
|
+
def is_const(self) -> bool:
|
|
355
|
+
"""
|
|
356
|
+
:return: True if the net is a constant generator.
|
|
357
|
+
:rtype: bool
|
|
358
|
+
"""
|
|
359
|
+
if hasattr(self, "net"):
|
|
360
|
+
return self.net.isConstant()
|
|
361
|
+
else:
|
|
362
|
+
return all(net.isConstant() for net in self.net_concat)
|
|
363
|
+
|
|
364
|
+
def is_const0(self) -> bool:
|
|
365
|
+
"""
|
|
366
|
+
:return: True if the net is a constant 0 generator.
|
|
367
|
+
:rtype: bool
|
|
368
|
+
"""
|
|
369
|
+
if hasattr(self, "net"):
|
|
370
|
+
return self.net.isConstant0()
|
|
371
|
+
else:
|
|
372
|
+
return all(net.isConstant0() for net in self.net_concat)
|
|
373
|
+
|
|
374
|
+
def is_const1(self) -> bool:
|
|
375
|
+
"""
|
|
376
|
+
:return: True if the net is a constant 1 generator.
|
|
377
|
+
:rtype: bool
|
|
378
|
+
"""
|
|
379
|
+
if hasattr(self, "net"):
|
|
380
|
+
return self.net.isConstant1()
|
|
381
|
+
else:
|
|
382
|
+
return all(net.isConstant1() for net in self.net_concat)
|
|
383
|
+
|
|
384
|
+
def set_type(self, net_type: Type):
|
|
385
|
+
"""
|
|
386
|
+
:param Type net_type: the type of the net.
|
|
387
|
+
"""
|
|
388
|
+
if hasattr(self, "net"):
|
|
389
|
+
self.net.setType(net_type.value)
|
|
390
|
+
else:
|
|
391
|
+
for net in self.net_concat:
|
|
392
|
+
net.setType(net_type.value)
|
|
393
|
+
|
|
394
|
+
def get_width(self) -> int:
|
|
395
|
+
"""
|
|
396
|
+
:return: the width of the net.
|
|
397
|
+
:rtype: int
|
|
398
|
+
"""
|
|
399
|
+
if hasattr(self, "net"):
|
|
400
|
+
return self.net.getWidth()
|
|
401
|
+
return sum(1 for _ in self.net_concat)
|
|
402
|
+
|
|
403
|
+
def get_bits(self):
|
|
404
|
+
"""Iterate over the bits of this Net.
|
|
405
|
+
The iterator will return itself if the Net is scalar.
|
|
406
|
+
:return: an iterator over the bits of this Net.
|
|
407
|
+
:rtype: Iterator[Net]
|
|
408
|
+
"""
|
|
409
|
+
if hasattr(self, "net"):
|
|
410
|
+
if isinstance(self.net, naja.SNLBusNet):
|
|
411
|
+
for bit in self.net.getBits():
|
|
412
|
+
yield Net(self.pathIDs, bit)
|
|
413
|
+
else:
|
|
414
|
+
yield self
|
|
415
|
+
else:
|
|
416
|
+
for net in self.net_concat:
|
|
417
|
+
yield Net(self.pathIDs, net)
|
|
418
|
+
|
|
419
|
+
def get_bit(self, index: int):
|
|
420
|
+
"""
|
|
421
|
+
:param int index: the index of the bit to get.
|
|
422
|
+
:return: the Net bit at the given index or None if it does not exist.
|
|
423
|
+
:rtype: Net
|
|
424
|
+
"""
|
|
425
|
+
if hasattr(self, "net"):
|
|
426
|
+
if isinstance(self.net, naja.SNLBusNet):
|
|
427
|
+
return Net(self.pathIDs, self.net.getBit(index))
|
|
428
|
+
else:
|
|
429
|
+
return None
|
|
430
|
+
if 0 <= index < len(self.net_concat):
|
|
431
|
+
return Net(self.pathIDs, self.net_concat[index])
|
|
432
|
+
return None
|
|
433
|
+
|
|
434
|
+
def get_inst_terms(self):
|
|
435
|
+
"""
|
|
436
|
+
:return: an iterator over the instance terminals of the net.
|
|
437
|
+
:rtype: Iterator[Term]
|
|
438
|
+
"""
|
|
439
|
+
if hasattr(self, "net_concat"):
|
|
440
|
+
raise ValueError("Cannot get inst terms from a net_concat")
|
|
441
|
+
path = self.pathIDs.copy()
|
|
442
|
+
for term in self.net.getInstTerms():
|
|
443
|
+
path.append(term.getInstance().getID())
|
|
444
|
+
yield Term(path, term.getBitTerm())
|
|
445
|
+
path.pop()
|
|
446
|
+
|
|
447
|
+
def count_inst_terms(self) -> int:
|
|
448
|
+
"""
|
|
449
|
+
Count the instance terminals of this net.
|
|
450
|
+
|
|
451
|
+
:return: the number of instance terminals of this net.
|
|
452
|
+
:rtype: int
|
|
453
|
+
"""
|
|
454
|
+
return sum(1 for _ in self.get_inst_terms())
|
|
455
|
+
|
|
456
|
+
def get_design_terms(self):
|
|
457
|
+
"""Return an iterator over the design terminals of the net.
|
|
458
|
+
This includes only the terminals that are part of the current design.
|
|
459
|
+
The iterator will yield Term objects bit per bit.
|
|
460
|
+
|
|
461
|
+
:return: an iterator over the design terminals of the net.
|
|
462
|
+
:rtype: Iterator[Term]
|
|
463
|
+
"""
|
|
464
|
+
if hasattr(self, "net_concat"):
|
|
465
|
+
raise ValueError("Cannot get terms from a net_concat")
|
|
466
|
+
for term in self.net.getBitTerms():
|
|
467
|
+
yield Term(self.pathIDs, term)
|
|
468
|
+
|
|
469
|
+
def count_design_terms(self) -> int:
|
|
470
|
+
"""Count the design terminals of this net.
|
|
471
|
+
|
|
472
|
+
:return: the number of design terminals of this net.
|
|
473
|
+
:rtype: int
|
|
474
|
+
"""
|
|
475
|
+
return sum(1 for _ in self.get_design_terms())
|
|
476
|
+
|
|
477
|
+
def get_terms(self):
|
|
478
|
+
"""Return an iterator over the terminals of the net.
|
|
479
|
+
This includes both design and instance terminals.
|
|
480
|
+
|
|
481
|
+
:return: an iterator over the terminals of the net.
|
|
482
|
+
:rtype: Iterator[Term]
|
|
483
|
+
"""
|
|
484
|
+
yield from self.get_design_terms()
|
|
485
|
+
yield from self.get_inst_terms()
|
|
486
|
+
|
|
487
|
+
def get_attributes(self) -> Iterator[Attribute]:
|
|
488
|
+
"""Iterate over the attributes of this Net.
|
|
489
|
+
|
|
490
|
+
:return: the attributes of this Net.
|
|
491
|
+
:rtype: Iterator[Attribute]
|
|
492
|
+
"""
|
|
493
|
+
if hasattr(self, "net"):
|
|
494
|
+
snlnet = self.net
|
|
495
|
+
for attribute in snlnet.getAttributes():
|
|
496
|
+
yield Attribute(attribute)
|
|
497
|
+
|
|
498
|
+
def count_attributes(self) -> int:
|
|
499
|
+
"""Count the attributes of this Net.
|
|
500
|
+
|
|
501
|
+
:return: the number of attributes of this Net.
|
|
502
|
+
:rtype: int
|
|
503
|
+
"""
|
|
504
|
+
return sum(1 for _ in self.get_attributes())
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
def get_snl_term_for_ids_with_path(path, termID, bit):
|
|
508
|
+
model = None
|
|
509
|
+
if path.size() == 0:
|
|
510
|
+
model = naja.NLUniverse.get().getTopDesign()
|
|
511
|
+
else:
|
|
512
|
+
model = path.getTailInstance().getModel()
|
|
513
|
+
snl_term = model.getTermByID(termID)
|
|
514
|
+
if bit is None:
|
|
515
|
+
return snl_term
|
|
516
|
+
else:
|
|
517
|
+
return snl_term.getBusTermBit(bit)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
class Term:
|
|
521
|
+
class Direction(Enum):
|
|
522
|
+
"""Enum for the direction of a term."""
|
|
523
|
+
INPUT = naja.SNLTerm.Direction.Input
|
|
524
|
+
OUTPUT = naja.SNLTerm.Direction.Output
|
|
525
|
+
INOUT = naja.SNLTerm.Direction.InOut
|
|
526
|
+
|
|
527
|
+
def __init__(self, path, term):
|
|
528
|
+
self.pathIDs = list(path)
|
|
529
|
+
self.termID = term.getID()
|
|
530
|
+
self.bit: int | None = (
|
|
531
|
+
term.getBit() if isinstance(term, naja.SNLBusTermBit) else None
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
def key(self) -> tuple:
|
|
535
|
+
"""
|
|
536
|
+
Unique, hashable identity of this Term.
|
|
537
|
+
Stable within a design revision.
|
|
538
|
+
"""
|
|
539
|
+
return (
|
|
540
|
+
tuple(self.pathIDs),
|
|
541
|
+
self.termID,
|
|
542
|
+
self.bit,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
def __eq__(self, other):
|
|
546
|
+
if not isinstance(other, Term):
|
|
547
|
+
return NotImplemented
|
|
548
|
+
return self.key() == other.key()
|
|
549
|
+
|
|
550
|
+
def __ne__(self, other):
|
|
551
|
+
eq = self.__eq__(other)
|
|
552
|
+
if eq is NotImplemented:
|
|
553
|
+
return NotImplemented
|
|
554
|
+
return not eq
|
|
555
|
+
|
|
556
|
+
def __lt__(self, other):
|
|
557
|
+
if not isinstance(other, Term):
|
|
558
|
+
return NotImplemented
|
|
559
|
+
return self.key() < other.key()
|
|
560
|
+
|
|
561
|
+
def __le__(self, other):
|
|
562
|
+
if not isinstance(other, Term):
|
|
563
|
+
return NotImplemented
|
|
564
|
+
eq = self.__eq__(other)
|
|
565
|
+
if eq is NotImplemented:
|
|
566
|
+
return NotImplemented
|
|
567
|
+
return eq or self.__lt__(other)
|
|
568
|
+
|
|
569
|
+
def __gt__(self, other):
|
|
570
|
+
if not isinstance(other, Term):
|
|
571
|
+
return NotImplemented
|
|
572
|
+
le = self.__le__(other)
|
|
573
|
+
if le is NotImplemented:
|
|
574
|
+
return NotImplemented
|
|
575
|
+
return not le
|
|
576
|
+
|
|
577
|
+
def __ge__(self, other):
|
|
578
|
+
if not isinstance(other, Term):
|
|
579
|
+
return NotImplemented
|
|
580
|
+
lt = self.__lt__(other)
|
|
581
|
+
if lt is NotImplemented:
|
|
582
|
+
return NotImplemented
|
|
583
|
+
return not lt
|
|
584
|
+
|
|
585
|
+
def __hash__(self):
|
|
586
|
+
termIDs = []
|
|
587
|
+
if self.bit is not None:
|
|
588
|
+
termIDs = [self.termID, self.bit]
|
|
589
|
+
else:
|
|
590
|
+
termIDs = [self.termID, -1]
|
|
591
|
+
return consistent_hash((self.pathIDs, termIDs))
|
|
592
|
+
|
|
593
|
+
def __str__(self):
|
|
594
|
+
term_str = ""
|
|
595
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
596
|
+
snl_term = self.get_snl_term()
|
|
597
|
+
snl_term_str = snl_term.getName()
|
|
598
|
+
if snl_term.isUnnamed():
|
|
599
|
+
snl_term_str = "<unnamed>"
|
|
600
|
+
if path.size() == 0:
|
|
601
|
+
term_str = snl_term_str
|
|
602
|
+
else:
|
|
603
|
+
term_str = f"{path}/{snl_term_str}"
|
|
604
|
+
if self.is_bus():
|
|
605
|
+
term_str += f"[{self.get_msb()}:{self.get_lsb()}]"
|
|
606
|
+
bit = self.get_bit_number()
|
|
607
|
+
if bit is not None:
|
|
608
|
+
term_str += f"[{bit}]"
|
|
609
|
+
return term_str
|
|
610
|
+
|
|
611
|
+
def __repr__(self) -> str:
|
|
612
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
613
|
+
return f"Term({path}, {self.get_snl_term().getName()})"
|
|
614
|
+
|
|
615
|
+
def __make_unique(self):
|
|
616
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
617
|
+
if path.size() > 1:
|
|
618
|
+
path = path.getHeadPath()
|
|
619
|
+
naja.SNLUniquifier(path)
|
|
620
|
+
|
|
621
|
+
def get_snl_term(self):
|
|
622
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
623
|
+
model = None
|
|
624
|
+
if len(self.pathIDs) == 0:
|
|
625
|
+
model = naja.NLUniverse.get().getTopDesign()
|
|
626
|
+
else:
|
|
627
|
+
model = path.getTailInstance().getModel()
|
|
628
|
+
snlterm = model.getTermByID(self.termID)
|
|
629
|
+
if self.bit is None:
|
|
630
|
+
return snlterm
|
|
631
|
+
else:
|
|
632
|
+
return snlterm.getBusTermBit(self.bit)
|
|
633
|
+
|
|
634
|
+
def is_bus(self) -> bool:
|
|
635
|
+
"""
|
|
636
|
+
:return: True if the term is a bus.
|
|
637
|
+
:rtype: bool
|
|
638
|
+
"""
|
|
639
|
+
return isinstance(self.get_snl_term(), naja.SNLBusTerm)
|
|
640
|
+
|
|
641
|
+
def is_bus_bit(self) -> bool:
|
|
642
|
+
"""
|
|
643
|
+
:return: True if the term is a bit of a bus.
|
|
644
|
+
:rtype: bool
|
|
645
|
+
"""
|
|
646
|
+
return isinstance(self.get_snl_term(), naja.SNLBusTermBit)
|
|
647
|
+
|
|
648
|
+
def is_scalar(self) -> bool:
|
|
649
|
+
"""
|
|
650
|
+
:return: True if the term is a scalar.
|
|
651
|
+
:rtype: bool
|
|
652
|
+
"""
|
|
653
|
+
return isinstance(self.get_snl_term(), naja.SNLScalarTerm)
|
|
654
|
+
|
|
655
|
+
def is_bit(self) -> bool:
|
|
656
|
+
"""
|
|
657
|
+
:return: True if the term is a bit.
|
|
658
|
+
:rtype: bool
|
|
659
|
+
"""
|
|
660
|
+
return self.is_scalar() or self.is_bus_bit()
|
|
661
|
+
|
|
662
|
+
def is_sequential(self) -> bool:
|
|
663
|
+
"""
|
|
664
|
+
:return: True if the term is a sequential term.
|
|
665
|
+
:rtype: bool
|
|
666
|
+
"""
|
|
667
|
+
for term in self.get_instance().get_bit_terms():
|
|
668
|
+
clockRelatedInputs = term.get_clock_related_inputs()
|
|
669
|
+
if self in clockRelatedInputs:
|
|
670
|
+
return True
|
|
671
|
+
clockRelatedOutputs = term.get_clock_related_outputs()
|
|
672
|
+
if self in clockRelatedOutputs:
|
|
673
|
+
return True
|
|
674
|
+
if (len(clockRelatedInputs) > 0 or len(clockRelatedOutputs) > 0) and (term == self):
|
|
675
|
+
return True
|
|
676
|
+
return False
|
|
677
|
+
|
|
678
|
+
def get_bit_number(self):
|
|
679
|
+
"""
|
|
680
|
+
:return: the bit index of the term if it is a bit.
|
|
681
|
+
:rtype: int or None
|
|
682
|
+
"""
|
|
683
|
+
snl_term = self.get_snl_term()
|
|
684
|
+
if isinstance(snl_term, naja.SNLBusTermBit):
|
|
685
|
+
return snl_term.getBit()
|
|
686
|
+
return None
|
|
687
|
+
|
|
688
|
+
def get_msb(self) -> int:
|
|
689
|
+
"""
|
|
690
|
+
:return: the most significant bit of the term if it is a bus.
|
|
691
|
+
:rtype: int or None
|
|
692
|
+
"""
|
|
693
|
+
snl_term = self.get_snl_term()
|
|
694
|
+
if isinstance(snl_term, naja.SNLBusTerm):
|
|
695
|
+
return snl_term.getMSB()
|
|
696
|
+
return None
|
|
697
|
+
|
|
698
|
+
def get_lsb(self) -> int:
|
|
699
|
+
"""
|
|
700
|
+
:return: the least significant bit of the term if it is a bus.
|
|
701
|
+
:rtype: int or None
|
|
702
|
+
"""
|
|
703
|
+
snl_term = self.get_snl_term()
|
|
704
|
+
if isinstance(snl_term, naja.SNLBusTerm):
|
|
705
|
+
return snl_term.getLSB()
|
|
706
|
+
return None
|
|
707
|
+
|
|
708
|
+
def get_width(self) -> int:
|
|
709
|
+
"""
|
|
710
|
+
:return: the width of the term. 1 if scalar.
|
|
711
|
+
:rtype: int
|
|
712
|
+
"""
|
|
713
|
+
return self.get_snl_term().getWidth()
|
|
714
|
+
|
|
715
|
+
def get_name(self) -> str:
|
|
716
|
+
"""
|
|
717
|
+
:return: the name of the term.
|
|
718
|
+
:rtype: str
|
|
719
|
+
"""
|
|
720
|
+
return self.get_snl_term().getName()
|
|
721
|
+
|
|
722
|
+
def is_unnamed(self) -> bool:
|
|
723
|
+
"""
|
|
724
|
+
:return: True if the term is unnamed.
|
|
725
|
+
:rtype: bool
|
|
726
|
+
"""
|
|
727
|
+
return self.get_snl_term().isUnnamed()
|
|
728
|
+
|
|
729
|
+
def get_direction(self) -> Direction:
|
|
730
|
+
"""
|
|
731
|
+
:return: the direction of the term.
|
|
732
|
+
:rtype: Term.Direction
|
|
733
|
+
"""
|
|
734
|
+
snlterm = self.get_snl_term()
|
|
735
|
+
if snlterm.getDirection() == naja.SNLTerm.Direction.Input:
|
|
736
|
+
return Term.Direction.INPUT
|
|
737
|
+
elif snlterm.getDirection() == naja.SNLTerm.Direction.Output:
|
|
738
|
+
return Term.Direction.OUTPUT
|
|
739
|
+
elif snlterm.getDirection() == naja.SNLTerm.Direction.InOut:
|
|
740
|
+
return Term.Direction.INOUT
|
|
741
|
+
|
|
742
|
+
def get_attributes(self) -> Iterator[Attribute]:
|
|
743
|
+
"""Iterate over the attributes of this Term.
|
|
744
|
+
|
|
745
|
+
:return: the attributes of this Term.
|
|
746
|
+
:rtype: Iterator[Attribute]
|
|
747
|
+
"""
|
|
748
|
+
snlterm = self.get_snl_term()
|
|
749
|
+
for attribute in snlterm.getAttributes():
|
|
750
|
+
yield Attribute(attribute)
|
|
751
|
+
|
|
752
|
+
def count_attributes(self) -> int:
|
|
753
|
+
"""Count the attributes of this Term.
|
|
754
|
+
|
|
755
|
+
:return: the number of attributes of this Term.
|
|
756
|
+
:rtype: int
|
|
757
|
+
"""
|
|
758
|
+
return sum(1 for _ in self.get_attributes())
|
|
759
|
+
|
|
760
|
+
def get_combinatorial_inputs(self):
|
|
761
|
+
"""Get all combinatorial input terms of this instance.
|
|
762
|
+
|
|
763
|
+
:return: a list of combinatorial input terms.
|
|
764
|
+
:rtype: List[Term]
|
|
765
|
+
"""
|
|
766
|
+
terms = self.__get_snl_model().getCombinatorialInputs(self.get_snl_term())
|
|
767
|
+
# Convert SNL terms to Term objects
|
|
768
|
+
return [Term(self.pathIDs, term) for term in terms]
|
|
769
|
+
|
|
770
|
+
def get_combinatorial_outputs(self):
|
|
771
|
+
"""Get all combinatorial output terms of this instance.
|
|
772
|
+
|
|
773
|
+
:return: a list of combinatorial output terms.
|
|
774
|
+
:rtype: List[Term]
|
|
775
|
+
"""
|
|
776
|
+
terms = self.__get_snl_model().getCombinatorialOutputs(self.get_snl_term())
|
|
777
|
+
# Convert SNL terms to Term objects
|
|
778
|
+
return [Term(self.pathIDs, term) for term in terms]
|
|
779
|
+
|
|
780
|
+
def get_clock_related_inputs(self):
|
|
781
|
+
"""Get all input terms that are related to the given clock term.
|
|
782
|
+
|
|
783
|
+
:param clock_term: the clock term to check for related inputs.
|
|
784
|
+
:return: a list of input terms that are related to the clock term.
|
|
785
|
+
:rtype: List[Term]
|
|
786
|
+
"""
|
|
787
|
+
terms = self.__get_snl_model().getClockRelatedInputs(self.get_snl_term())
|
|
788
|
+
# Convert SNL terms to Term objects
|
|
789
|
+
return [Term(self.pathIDs, term) for term in terms]
|
|
790
|
+
|
|
791
|
+
def get_clock_related_outputs(self):
|
|
792
|
+
"""Get all output terms that are related to the given clock term.
|
|
793
|
+
|
|
794
|
+
:param clock_term: the clock term to check for related outputs.
|
|
795
|
+
:return: a list of output terms that are related to the clock term.
|
|
796
|
+
:rtype: List[Term]
|
|
797
|
+
"""
|
|
798
|
+
terms = self.__get_snl_model().getClockRelatedOutputs(self.get_snl_term())
|
|
799
|
+
# Convert SNL terms to Term objects
|
|
800
|
+
return [Term(self.pathIDs, term) for term in terms]
|
|
801
|
+
|
|
802
|
+
def __get_snl_model(self):
|
|
803
|
+
snlterm = self.get_snl_term()
|
|
804
|
+
return snlterm.getDesign()
|
|
805
|
+
|
|
806
|
+
def __get_snl_bitnet(self, bit) -> Net:
|
|
807
|
+
# single bit
|
|
808
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
809
|
+
if path.size():
|
|
810
|
+
instTerm = path.getTailInstance().getInstTerm(bit)
|
|
811
|
+
return instTerm.getNet()
|
|
812
|
+
else:
|
|
813
|
+
return bit.getNet()
|
|
814
|
+
|
|
815
|
+
def __get_snl_lower_bitnet(self, bit) -> Net:
|
|
816
|
+
return bit.getNet()
|
|
817
|
+
|
|
818
|
+
def __get_snl_busnet(self, snl_nets) -> naja.SNLBusNet:
|
|
819
|
+
# iterate on all elements of the list and check if
|
|
820
|
+
# a full SNLBusNet can be reconstructed
|
|
821
|
+
snl_bus_net = None
|
|
822
|
+
for i in range(len(snl_nets)):
|
|
823
|
+
snl_net = snl_nets[i]
|
|
824
|
+
if not isinstance(snl_net, naja.SNLBusNetBit):
|
|
825
|
+
return None
|
|
826
|
+
bit_bus = snl_net.getBus()
|
|
827
|
+
if bit_bus.getWidth() != len(snl_nets):
|
|
828
|
+
return None
|
|
829
|
+
if snl_bus_net is None:
|
|
830
|
+
snl_bus_net = bit_bus
|
|
831
|
+
if snl_bus_net != bit_bus:
|
|
832
|
+
return None
|
|
833
|
+
if snl_bus_net.getBitAtPosition(i) != snl_net:
|
|
834
|
+
return None
|
|
835
|
+
return snl_bus_net
|
|
836
|
+
|
|
837
|
+
def __get_net(self, path, snl_term_net_accessor) -> Net:
|
|
838
|
+
if isinstance(self.get_snl_term(), naja.SNLBusTerm):
|
|
839
|
+
snl_nets = []
|
|
840
|
+
for bit in self.get_snl_term().getBits():
|
|
841
|
+
snl_net = snl_term_net_accessor(bit)
|
|
842
|
+
snl_nets.append(snl_net)
|
|
843
|
+
snl_bus_net = self.__get_snl_busnet(snl_nets)
|
|
844
|
+
if snl_bus_net is not None:
|
|
845
|
+
return Net(path, snl_bus_net)
|
|
846
|
+
else:
|
|
847
|
+
if all(element is not None for element in snl_nets):
|
|
848
|
+
return Net(path, net_concat=snl_nets)
|
|
849
|
+
else:
|
|
850
|
+
snl_net = snl_term_net_accessor(self.get_snl_term())
|
|
851
|
+
if snl_net is not None:
|
|
852
|
+
return Net(path, snl_net)
|
|
853
|
+
return None
|
|
854
|
+
|
|
855
|
+
def get_lower_net(self) -> Net:
|
|
856
|
+
"""
|
|
857
|
+
:return: the lower net of the term.
|
|
858
|
+
:rtype: Net
|
|
859
|
+
"""
|
|
860
|
+
return self.__get_net(self.pathIDs, self.__get_snl_lower_bitnet)
|
|
861
|
+
|
|
862
|
+
def get_upper_net(self) -> Net:
|
|
863
|
+
"""
|
|
864
|
+
:return: the upper net of the term.
|
|
865
|
+
:rtype: Net
|
|
866
|
+
:remark: If the term is a top level term, it will return None.
|
|
867
|
+
"""
|
|
868
|
+
head_path = self.pathIDs.copy()
|
|
869
|
+
if len(head_path) == 0:
|
|
870
|
+
return None
|
|
871
|
+
# path is one level up
|
|
872
|
+
head_path.pop()
|
|
873
|
+
return self.__get_net(head_path, self.__get_snl_bitnet)
|
|
874
|
+
|
|
875
|
+
def get_instance(self):
|
|
876
|
+
"""
|
|
877
|
+
:return: the instance of this Term.
|
|
878
|
+
:rtype: Instance
|
|
879
|
+
"""
|
|
880
|
+
return Instance(self.pathIDs)
|
|
881
|
+
|
|
882
|
+
def get_flat_fanout(self, filter=None):
|
|
883
|
+
return self.get_equipotential().get_leaf_readers(filter=filter)
|
|
884
|
+
|
|
885
|
+
def count_flat_fanout(self, filter=None):
|
|
886
|
+
return sum(1 for _ in self.get_flat_fanout(filter=filter))
|
|
887
|
+
|
|
888
|
+
def get_equipotential(self) -> Equipotential:
|
|
889
|
+
"""
|
|
890
|
+
:return: the Equipotential of this Term.
|
|
891
|
+
:rtype: Equipotential
|
|
892
|
+
"""
|
|
893
|
+
return Equipotential(self)
|
|
894
|
+
|
|
895
|
+
def is_input(self) -> bool:
|
|
896
|
+
"""
|
|
897
|
+
:return: True if the term is an input.
|
|
898
|
+
:rtype: bool
|
|
899
|
+
"""
|
|
900
|
+
snlterm = self.get_snl_term()
|
|
901
|
+
return snlterm.getDirection() == naja.SNLTerm.Direction.Input
|
|
902
|
+
|
|
903
|
+
def is_output(self) -> bool:
|
|
904
|
+
"""
|
|
905
|
+
:return: True if the term is an output.
|
|
906
|
+
:rtype: bool
|
|
907
|
+
"""
|
|
908
|
+
snlterm = self.get_snl_term()
|
|
909
|
+
return snlterm.getDirection() == naja.SNLTerm.Direction.Output
|
|
910
|
+
|
|
911
|
+
def get_bits(self):
|
|
912
|
+
"""
|
|
913
|
+
:return: an iterator over the bits of the term.
|
|
914
|
+
If the term is scalar, it will return an iterator over itself.
|
|
915
|
+
:rtype: Iterator[Term]
|
|
916
|
+
"""
|
|
917
|
+
snl_term = self.get_snl_term()
|
|
918
|
+
if isinstance(snl_term, naja.SNLBusTerm):
|
|
919
|
+
for bit in snl_term.getBits():
|
|
920
|
+
yield Term(self.pathIDs, bit)
|
|
921
|
+
else:
|
|
922
|
+
yield self
|
|
923
|
+
|
|
924
|
+
def get_bit(self, index: int):
|
|
925
|
+
"""
|
|
926
|
+
:param int index: the index of the bit to get.
|
|
927
|
+
:return: the Term bit at the given index or None if it does not exist.
|
|
928
|
+
:rtype: Term or None
|
|
929
|
+
"""
|
|
930
|
+
snl_term = self.get_snl_term()
|
|
931
|
+
if isinstance(snl_term, naja.SNLBusTerm):
|
|
932
|
+
return Term(
|
|
933
|
+
self.pathIDs,
|
|
934
|
+
snl_term.getBusTermBit(index),
|
|
935
|
+
)
|
|
936
|
+
return None
|
|
937
|
+
|
|
938
|
+
def disconnect_upper_net(self):
|
|
939
|
+
"""Disconnect this term from its upper net."""
|
|
940
|
+
if self.get_instance().is_top():
|
|
941
|
+
raise ValueError("Cannot disconnect the upper net of a top level term")
|
|
942
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
943
|
+
self.__make_unique()
|
|
944
|
+
inst = path.getTailInstance()
|
|
945
|
+
for bit in self.get_snl_term().getBits():
|
|
946
|
+
iterm = inst.getInstTerm(bit)
|
|
947
|
+
iterm.setNet(None)
|
|
948
|
+
|
|
949
|
+
def disconnect_lower_net(self):
|
|
950
|
+
"""Disconnect this term from its lower net."""
|
|
951
|
+
self.__make_unique()
|
|
952
|
+
for bit in self.get_snl_term().getBits():
|
|
953
|
+
bit.setNet(None)
|
|
954
|
+
|
|
955
|
+
def connect_upper_net(self, net: Net):
|
|
956
|
+
"""Connect this term to the given upper Net.
|
|
957
|
+
|
|
958
|
+
:param Net net: the upper Net to connect to.
|
|
959
|
+
"""
|
|
960
|
+
if self.get_instance().is_top():
|
|
961
|
+
raise ValueError("Cannot connect the upper net of a top level term")
|
|
962
|
+
if self.get_width() != net.get_width():
|
|
963
|
+
raise ValueError("Width mismatch")
|
|
964
|
+
self.__make_unique()
|
|
965
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
966
|
+
inst = path.getTailInstance()
|
|
967
|
+
for bterm, bnet in zip(
|
|
968
|
+
self.get_snl_term().getBits(),
|
|
969
|
+
net.net.getBits(),
|
|
970
|
+
):
|
|
971
|
+
iterm = inst.getInstTerm(bterm)
|
|
972
|
+
iterm.setNet(bnet)
|
|
973
|
+
|
|
974
|
+
def connect_lower_net(self, net: Net):
|
|
975
|
+
"""Connect this term to the given lower Net.
|
|
976
|
+
|
|
977
|
+
:param Net net: the lower Net to connect to.
|
|
978
|
+
"""
|
|
979
|
+
if self.get_width() != net.get_width():
|
|
980
|
+
raise ValueError("Width mismatch")
|
|
981
|
+
self.__make_unique()
|
|
982
|
+
for bterm, bnet in zip(
|
|
983
|
+
self.get_snl_term().getBits(),
|
|
984
|
+
net.net.getBits(),
|
|
985
|
+
):
|
|
986
|
+
bterm.setNet(bnet)
|
|
987
|
+
|
|
988
|
+
def get_truth_table(self):
|
|
989
|
+
# check the index of the output
|
|
990
|
+
if not self.is_output():
|
|
991
|
+
raise ValueError("Truth table can only be computed for output terms")
|
|
992
|
+
# get the instance
|
|
993
|
+
if self.is_bus():
|
|
994
|
+
raise ValueError("Truth table cannot be computed for bus terms")
|
|
995
|
+
inst = self.get_instance()
|
|
996
|
+
# get the model
|
|
997
|
+
model = inst._Instance__get_snl_model()
|
|
998
|
+
for term in model.getTerms():
|
|
999
|
+
if term.getDirection() == naja.SNLTerm.Direction.Output:
|
|
1000
|
+
if term.getName() == self.get_name():
|
|
1001
|
+
# get the truth table
|
|
1002
|
+
tt = model.getTruthTableByOutputID(term.getID())
|
|
1003
|
+
if tt is not None:
|
|
1004
|
+
return model.getTruthTableByOutputID(term.getID())
|
|
1005
|
+
return None
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
def get_instance_by_path(names: list):
|
|
1009
|
+
return get_top().get_child_instance(names)
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
class Instance:
|
|
1013
|
+
"""Class that represents an instance in the design hierarchy.
|
|
1014
|
+
"""
|
|
1015
|
+
|
|
1016
|
+
def __init__(self, path=naja.SNLPath()):
|
|
1017
|
+
self.inst = None
|
|
1018
|
+
self.revisionCount = 0
|
|
1019
|
+
self.SNLID = [0] * 6
|
|
1020
|
+
if isinstance(path, naja.SNLPath):
|
|
1021
|
+
if path.size():
|
|
1022
|
+
self.pathIDs = path.getInstanceIDs()
|
|
1023
|
+
self.revisionCount = path.getTailInstance().getModel().getRevisionCount()
|
|
1024
|
+
self.inst = path.getTailInstance()
|
|
1025
|
+
else:
|
|
1026
|
+
self.pathIDs = []
|
|
1027
|
+
elif isinstance(path, list):
|
|
1028
|
+
self.pathIDs = path.copy()
|
|
1029
|
+
if path:
|
|
1030
|
+
self.inst = get_snl_instance_from_id_list(path)
|
|
1031
|
+
self.revisionCount = self.inst.getModel().getRevisionCount()
|
|
1032
|
+
if self.inst:
|
|
1033
|
+
self.SNLID = self.inst.getModel().getNLID()
|
|
1034
|
+
|
|
1035
|
+
def __eq__(self, other) -> bool:
|
|
1036
|
+
return self.pathIDs == other.pathIDs
|
|
1037
|
+
|
|
1038
|
+
def __str__(self):
|
|
1039
|
+
if self.is_top():
|
|
1040
|
+
top = self.__get_snl_model()
|
|
1041
|
+
if top is not None:
|
|
1042
|
+
return top.getName()
|
|
1043
|
+
else:
|
|
1044
|
+
return ""
|
|
1045
|
+
else:
|
|
1046
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1047
|
+
return str(path)
|
|
1048
|
+
|
|
1049
|
+
def __repr__(self) -> str:
|
|
1050
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1051
|
+
return f"Instance({path})"
|
|
1052
|
+
|
|
1053
|
+
def __hash__(self):
|
|
1054
|
+
return consistent_hash(self.pathIDs)
|
|
1055
|
+
|
|
1056
|
+
def get_leaf_children(self):
|
|
1057
|
+
"""Iterate over the leaf children of this Instance.
|
|
1058
|
+
Equivalent to the underlying leaves of the instanciation tree.
|
|
1059
|
+
|
|
1060
|
+
:return: an iterator over the leaf children Instance of this Instance.
|
|
1061
|
+
:rtype: Iterator[Instance]
|
|
1062
|
+
"""
|
|
1063
|
+
initial_path = get_snl_path_from_id_list(self.pathIDs)
|
|
1064
|
+
for inst in self.__get_snl_model().getInstances():
|
|
1065
|
+
if inst.getModel().isLeaf():
|
|
1066
|
+
yield Instance(naja.SNLPath(initial_path, inst))
|
|
1067
|
+
path = naja.SNLPath(initial_path, inst)
|
|
1068
|
+
stack = [[inst, path]]
|
|
1069
|
+
while stack:
|
|
1070
|
+
current = stack.pop()
|
|
1071
|
+
current_inst = current[0]
|
|
1072
|
+
current_path = current[1]
|
|
1073
|
+
for inst_child in current_inst.getModel().getInstances():
|
|
1074
|
+
path_child = naja.SNLPath(current_path, inst_child)
|
|
1075
|
+
if inst_child.getModel().isLeaf():
|
|
1076
|
+
yield Instance(path_child)
|
|
1077
|
+
stack.append([inst_child, path_child])
|
|
1078
|
+
|
|
1079
|
+
def is_top(self) -> bool:
|
|
1080
|
+
"""
|
|
1081
|
+
:return: True if this is the top design.
|
|
1082
|
+
:rtype: bool
|
|
1083
|
+
"""
|
|
1084
|
+
return not self.pathIDs
|
|
1085
|
+
|
|
1086
|
+
def is_assign(self) -> bool:
|
|
1087
|
+
"""(assign a=b) will create an instance of assign connecting
|
|
1088
|
+
the wire a to the output of the assign and b to the input.
|
|
1089
|
+
|
|
1090
|
+
:return: True if this is an assign (represented by an unnamed assign instance).
|
|
1091
|
+
:rtype: bool
|
|
1092
|
+
"""
|
|
1093
|
+
return self.__get_snl_model().isAssign()
|
|
1094
|
+
|
|
1095
|
+
def is_blackbox(self) -> bool:
|
|
1096
|
+
"""
|
|
1097
|
+
:return: True if this is a blackbox.
|
|
1098
|
+
:rtype: bool
|
|
1099
|
+
"""
|
|
1100
|
+
return self.__get_snl_model().isBlackBox()
|
|
1101
|
+
|
|
1102
|
+
def is_leaf(self) -> bool:
|
|
1103
|
+
"""
|
|
1104
|
+
:return: True if this is a leaf.
|
|
1105
|
+
:rtype: bool
|
|
1106
|
+
"""
|
|
1107
|
+
return self.__get_snl_model().isLeaf()
|
|
1108
|
+
|
|
1109
|
+
def is_const0(self) -> bool:
|
|
1110
|
+
"""
|
|
1111
|
+
:return: True if this is a constant 0 generator.
|
|
1112
|
+
:rtype: bool
|
|
1113
|
+
"""
|
|
1114
|
+
return self.__get_snl_model().isConst0()
|
|
1115
|
+
|
|
1116
|
+
def is_const1(self) -> bool:
|
|
1117
|
+
"""
|
|
1118
|
+
:return: True if this is a constant 1 generator.
|
|
1119
|
+
:rtype: bool
|
|
1120
|
+
"""
|
|
1121
|
+
return self.__get_snl_model().isConst1()
|
|
1122
|
+
|
|
1123
|
+
def is_const(self) -> bool:
|
|
1124
|
+
"""
|
|
1125
|
+
:return: True if this is a constant generator.
|
|
1126
|
+
:rtype: bool
|
|
1127
|
+
"""
|
|
1128
|
+
return self.__get_snl_model().isConst()
|
|
1129
|
+
|
|
1130
|
+
def is_buf(self) -> bool:
|
|
1131
|
+
"""
|
|
1132
|
+
:return: True if this is a buffer.
|
|
1133
|
+
:rtype: bool
|
|
1134
|
+
"""
|
|
1135
|
+
return self.__get_snl_model().isBuf()
|
|
1136
|
+
|
|
1137
|
+
def is_inv(self) -> bool:
|
|
1138
|
+
"""
|
|
1139
|
+
:return: True if this is an inverter.
|
|
1140
|
+
:rtype: bool
|
|
1141
|
+
"""
|
|
1142
|
+
return self.__get_snl_model().isInv()
|
|
1143
|
+
|
|
1144
|
+
def __get_snl_model(self):
|
|
1145
|
+
if self.is_top():
|
|
1146
|
+
return naja.NLUniverse.get().getTopDesign()
|
|
1147
|
+
if (
|
|
1148
|
+
self.inst.getModel().getRevisionCount() != self.revisionCount or
|
|
1149
|
+
self.inst.getModel().getNLID() != self.SNLID
|
|
1150
|
+
):
|
|
1151
|
+
self.inst = get_snl_instance_from_id_list(self.pathIDs)
|
|
1152
|
+
self.revisionCount = self.inst.getModel().getRevisionCount()
|
|
1153
|
+
self.SNLID = self.inst.getModel().getNLID()
|
|
1154
|
+
|
|
1155
|
+
return self.inst.getModel()
|
|
1156
|
+
|
|
1157
|
+
def __get_leaf_snl_object(self):
|
|
1158
|
+
if self.is_top():
|
|
1159
|
+
return naja.NLUniverse.get().getTopDesign()
|
|
1160
|
+
return get_snl_instance_from_id_list(self.pathIDs)
|
|
1161
|
+
|
|
1162
|
+
def __find_snl_model(self, name: str) -> naja.SNLDesign:
|
|
1163
|
+
u = naja.NLUniverse.get()
|
|
1164
|
+
for db in u.getUserDBs():
|
|
1165
|
+
for lib in db.getLibraries():
|
|
1166
|
+
found_model = lib.getSNLDesign(name)
|
|
1167
|
+
if found_model is not None:
|
|
1168
|
+
return found_model
|
|
1169
|
+
return None
|
|
1170
|
+
|
|
1171
|
+
def dump_full_dot(self, path: str):
|
|
1172
|
+
"""Dump the full dot file of this instance."""
|
|
1173
|
+
self.__get_snl_model().dumpFullDotFile(path)
|
|
1174
|
+
|
|
1175
|
+
def dump_context_dot(self, path: str):
|
|
1176
|
+
self.__get_snl_model().dumpContextDotFile(path)
|
|
1177
|
+
|
|
1178
|
+
def get_child_instance(self, names: Union[str, list]):
|
|
1179
|
+
"""
|
|
1180
|
+
:param names: the name of the child instance
|
|
1181
|
+
or the path to the child Instance as a list of names.
|
|
1182
|
+
:return: the child Instance at the given path or None if it does not exist.
|
|
1183
|
+
:rtype: Instance or None
|
|
1184
|
+
"""
|
|
1185
|
+
if isinstance(names, str):
|
|
1186
|
+
names = [names]
|
|
1187
|
+
if not names:
|
|
1188
|
+
raise ValueError("Names argument cannot be empty")
|
|
1189
|
+
model = self.__get_snl_model()
|
|
1190
|
+
path = self.pathIDs.copy()
|
|
1191
|
+
for name in names:
|
|
1192
|
+
childInst = model.getInstance(name)
|
|
1193
|
+
if childInst is None:
|
|
1194
|
+
return None
|
|
1195
|
+
path.append(childInst.getID())
|
|
1196
|
+
model = childInst.getModel()
|
|
1197
|
+
return Instance(path)
|
|
1198
|
+
|
|
1199
|
+
def get_child_instance_by_id(self, ids: Union[int, list[int]]):
|
|
1200
|
+
"""
|
|
1201
|
+
:param ids: the ID of the child instance
|
|
1202
|
+
or the path to the child Instance as a list of IDs.
|
|
1203
|
+
:return: the child Instance at the given path or None if it does not exist.
|
|
1204
|
+
:rtype: Instance or None
|
|
1205
|
+
"""
|
|
1206
|
+
if isinstance(ids, int):
|
|
1207
|
+
ids = [ids]
|
|
1208
|
+
if not ids:
|
|
1209
|
+
raise ValueError("IDs argument cannot be empty")
|
|
1210
|
+
model = self.__get_snl_model()
|
|
1211
|
+
path = self.pathIDs.copy()
|
|
1212
|
+
for id in ids:
|
|
1213
|
+
childInst = model.getInstanceByID(id)
|
|
1214
|
+
if childInst is None:
|
|
1215
|
+
return None
|
|
1216
|
+
path.append(childInst.getID())
|
|
1217
|
+
model = childInst.getModel()
|
|
1218
|
+
return Instance(path)
|
|
1219
|
+
|
|
1220
|
+
def get_child_instances(self):
|
|
1221
|
+
"""Iterate over the child instances of this instance.
|
|
1222
|
+
Equivalent to go down one level in hierarchy.
|
|
1223
|
+
|
|
1224
|
+
:return: an iterator over the child instances of this instance.
|
|
1225
|
+
:rtype: Iterator[Instance]
|
|
1226
|
+
"""
|
|
1227
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1228
|
+
for inst in self.__get_snl_model().getInstances():
|
|
1229
|
+
path_child = naja.SNLPath(path, inst)
|
|
1230
|
+
yield Instance(path_child)
|
|
1231
|
+
# path.pop()
|
|
1232
|
+
|
|
1233
|
+
def count_child_instances(self) -> int:
|
|
1234
|
+
"""
|
|
1235
|
+
:return: the number of child instances of this instance.
|
|
1236
|
+
:rtype: int
|
|
1237
|
+
"""
|
|
1238
|
+
return sum(1 for _ in self.__get_snl_model().getInstances())
|
|
1239
|
+
|
|
1240
|
+
def get_nets(self):
|
|
1241
|
+
"""Iterate over all scalar nets and bus nets.
|
|
1242
|
+
|
|
1243
|
+
:return: an iterator over the nets of this Instance.
|
|
1244
|
+
:rtype: Iterator[Net]
|
|
1245
|
+
"""
|
|
1246
|
+
for net in self.__get_snl_model().getNets():
|
|
1247
|
+
yield Net(self.pathIDs, net)
|
|
1248
|
+
|
|
1249
|
+
def count_nets(self) -> int:
|
|
1250
|
+
"""Count the number of scalar nets and bus nets of this Instance.
|
|
1251
|
+
|
|
1252
|
+
:return: the number of nets of this Instance.
|
|
1253
|
+
:rtype: int
|
|
1254
|
+
"""
|
|
1255
|
+
return sum(1 for _ in self.get_nets())
|
|
1256
|
+
|
|
1257
|
+
def get_bit_nets(self):
|
|
1258
|
+
"""Iterate over all scalar nets and bus net bits.
|
|
1259
|
+
|
|
1260
|
+
:return: an iterator over the nets at bit level of this Instance.
|
|
1261
|
+
:rtype: Iterator[Net]
|
|
1262
|
+
"""
|
|
1263
|
+
for net in self.__get_snl_model().getNets():
|
|
1264
|
+
if isinstance(net, naja.SNLBusNet):
|
|
1265
|
+
for bit in net.getBits():
|
|
1266
|
+
yield Net(self.pathIDs, bit)
|
|
1267
|
+
else:
|
|
1268
|
+
yield Net(self.pathIDs, net)
|
|
1269
|
+
|
|
1270
|
+
def count_bit_nets(self) -> int:
|
|
1271
|
+
"""Count the number of scalar nets and bus net bits of this Instance.
|
|
1272
|
+
|
|
1273
|
+
:return: the number of bit nets of this Instance.
|
|
1274
|
+
:rtype: int
|
|
1275
|
+
"""
|
|
1276
|
+
return sum(1 for _ in self.get_bit_nets())
|
|
1277
|
+
|
|
1278
|
+
def get_net(self, name: str) -> Net:
|
|
1279
|
+
"""
|
|
1280
|
+
:param str name: the name of the Net to get.
|
|
1281
|
+
:return: the Net with the given name or None if it does not exist.
|
|
1282
|
+
:rtype: Net or None
|
|
1283
|
+
"""
|
|
1284
|
+
net = self.__get_snl_model().getNet(name)
|
|
1285
|
+
if net is not None:
|
|
1286
|
+
return Net(self.pathIDs, net)
|
|
1287
|
+
return None
|
|
1288
|
+
|
|
1289
|
+
def is_primitive(self) -> bool:
|
|
1290
|
+
"""
|
|
1291
|
+
:return: True if this is a primitive.
|
|
1292
|
+
:rtype: bool
|
|
1293
|
+
"""
|
|
1294
|
+
return self.__get_snl_model().isPrimitive()
|
|
1295
|
+
|
|
1296
|
+
def is_sequential(self) -> bool:
|
|
1297
|
+
"""
|
|
1298
|
+
:return: True if this is a sequential element.
|
|
1299
|
+
:rtype: bool
|
|
1300
|
+
"""
|
|
1301
|
+
return self.__get_snl_model().isSequential()
|
|
1302
|
+
|
|
1303
|
+
def has_modeling(self) -> bool:
|
|
1304
|
+
"""
|
|
1305
|
+
:return: True if this instance has timing modeling.
|
|
1306
|
+
:rtype: bool
|
|
1307
|
+
"""
|
|
1308
|
+
return self.__get_snl_model().hasModeling()
|
|
1309
|
+
|
|
1310
|
+
def get_terms(self):
|
|
1311
|
+
"""Iterate over all scalar terms and bus terms of this Instance.
|
|
1312
|
+
|
|
1313
|
+
:return: the terms of this Instance.
|
|
1314
|
+
:rtype: Iterator[Term]
|
|
1315
|
+
"""
|
|
1316
|
+
for term in self.__get_snl_model().getTerms():
|
|
1317
|
+
yield Term(self.pathIDs, term)
|
|
1318
|
+
|
|
1319
|
+
def count_terms(self) -> int:
|
|
1320
|
+
"""Count the number of scalar terms and bus terms of this Instance.
|
|
1321
|
+
|
|
1322
|
+
:return: the number of terms of this Instance.
|
|
1323
|
+
:rtype: int
|
|
1324
|
+
"""
|
|
1325
|
+
return sum(1 for _ in self.get_terms())
|
|
1326
|
+
|
|
1327
|
+
def get_bit_terms(self):
|
|
1328
|
+
"""Iterate over all scalar terms and bus term bits.
|
|
1329
|
+
|
|
1330
|
+
:return: the bit terms of this Instance.
|
|
1331
|
+
:rtype: Iterator[Term]
|
|
1332
|
+
"""
|
|
1333
|
+
for term in self.__get_snl_model().getBitTerms():
|
|
1334
|
+
yield Term(self.pathIDs, term)
|
|
1335
|
+
|
|
1336
|
+
def count_bit_terms(self) -> int:
|
|
1337
|
+
"""Count the number of scalar terms and bus term bits of this Instance.
|
|
1338
|
+
|
|
1339
|
+
:return: the number of bit terms of this Instance.
|
|
1340
|
+
:rtype: int
|
|
1341
|
+
"""
|
|
1342
|
+
return sum(1 for _ in self.get_bit_terms())
|
|
1343
|
+
|
|
1344
|
+
def get_term(self, name: str) -> Term:
|
|
1345
|
+
"""
|
|
1346
|
+
:param str name: the name of the Term to get.
|
|
1347
|
+
:return: the Term with the given name.
|
|
1348
|
+
:rtype: Term or None
|
|
1349
|
+
"""
|
|
1350
|
+
term = self.__get_snl_model().getTerm(name)
|
|
1351
|
+
if term is not None:
|
|
1352
|
+
return Term(self.pathIDs, self.__get_snl_model().getTerm(name))
|
|
1353
|
+
return None
|
|
1354
|
+
|
|
1355
|
+
def get_input_terms(self):
|
|
1356
|
+
"""Iterate over all scalar input terms and bus input terms
|
|
1357
|
+
of this Instance.
|
|
1358
|
+
|
|
1359
|
+
:return: the input terms of this Instance.
|
|
1360
|
+
:rtype: Iterator[Term]
|
|
1361
|
+
"""
|
|
1362
|
+
for term in self.__get_snl_model().getTerms():
|
|
1363
|
+
if term.getDirection() != naja.SNLTerm.Direction.Output:
|
|
1364
|
+
yield Term(self.pathIDs, term)
|
|
1365
|
+
|
|
1366
|
+
def count_input_terms(self) -> int:
|
|
1367
|
+
"""Count the number of scalar input terms and bus input terms
|
|
1368
|
+
of this Instance.
|
|
1369
|
+
|
|
1370
|
+
:return: the number of input terms of this Instance.
|
|
1371
|
+
:rtype: int
|
|
1372
|
+
"""
|
|
1373
|
+
return sum(1 for _ in self.get_input_terms())
|
|
1374
|
+
|
|
1375
|
+
def get_input_bit_terms(self):
|
|
1376
|
+
"""Iterate over all scalar input terms and bus input term bits
|
|
1377
|
+
of this Instance.
|
|
1378
|
+
|
|
1379
|
+
:return: the bit input terms of this Instance.
|
|
1380
|
+
:rtype: Iterator[Term]
|
|
1381
|
+
"""
|
|
1382
|
+
for term in self.__get_snl_model().getTerms():
|
|
1383
|
+
if term.getDirection() != naja.SNLTerm.Direction.Output:
|
|
1384
|
+
if isinstance(term, naja.SNLBusTerm):
|
|
1385
|
+
for bit in term.getBits():
|
|
1386
|
+
yield Term(self.pathIDs, bit)
|
|
1387
|
+
else:
|
|
1388
|
+
yield Term(self.pathIDs, term)
|
|
1389
|
+
|
|
1390
|
+
def count_input_bit_terms(self) -> int:
|
|
1391
|
+
"""Count the number of scalar input terms and bus input term bits
|
|
1392
|
+
of this Instance.
|
|
1393
|
+
|
|
1394
|
+
:return: the number of bit input terms of this Instance.
|
|
1395
|
+
:rtype: int
|
|
1396
|
+
"""
|
|
1397
|
+
return sum(1 for _ in self.get_input_bit_terms())
|
|
1398
|
+
|
|
1399
|
+
def get_output_terms(self):
|
|
1400
|
+
"""Iterate over all scalar output terms and bus output terms
|
|
1401
|
+
of this Instance.
|
|
1402
|
+
|
|
1403
|
+
:return: the output terms of this Instance.
|
|
1404
|
+
:rtype: Iterator[Term]
|
|
1405
|
+
"""
|
|
1406
|
+
for term in self.__get_snl_model().getTerms():
|
|
1407
|
+
if term.getDirection() != naja.SNLTerm.Direction.Input:
|
|
1408
|
+
yield Term(self.pathIDs, term)
|
|
1409
|
+
|
|
1410
|
+
def count_output_terms(self) -> int:
|
|
1411
|
+
"""Count the number of scalar output terms and bus output terms
|
|
1412
|
+
of this Instance.
|
|
1413
|
+
|
|
1414
|
+
:return: the number of output terms of this Instance.
|
|
1415
|
+
:rtype: int
|
|
1416
|
+
"""
|
|
1417
|
+
return sum(1 for _ in self.get_output_terms())
|
|
1418
|
+
|
|
1419
|
+
def get_output_bit_terms(self):
|
|
1420
|
+
"""Iterate over all scalar output terms and bus output term bits
|
|
1421
|
+
of this Instance.
|
|
1422
|
+
|
|
1423
|
+
:return: the bit output terms of this Instance.
|
|
1424
|
+
:rtype: Iterator[Term]
|
|
1425
|
+
"""
|
|
1426
|
+
for term in self.__get_snl_model().getTerms():
|
|
1427
|
+
if term.getDirection() != naja.SNLTerm.Direction.Input:
|
|
1428
|
+
if isinstance(term, naja.SNLBusTerm):
|
|
1429
|
+
for bit in term.getBits():
|
|
1430
|
+
yield Term(self.pathIDs, bit)
|
|
1431
|
+
else:
|
|
1432
|
+
yield Term(self.pathIDs, term)
|
|
1433
|
+
|
|
1434
|
+
def count_output_bit_terms(self) -> int:
|
|
1435
|
+
"""Count the number of scalar output terms and bus output term bits
|
|
1436
|
+
of this Instance.
|
|
1437
|
+
|
|
1438
|
+
:return: the number of bit output terms of this Instance.
|
|
1439
|
+
:rtype: int
|
|
1440
|
+
"""
|
|
1441
|
+
return sum(1 for _ in self.get_output_bit_terms())
|
|
1442
|
+
|
|
1443
|
+
def get_attributes(self) -> Iterator[Attribute]:
|
|
1444
|
+
"""Iterate over the attributes of this Instance.
|
|
1445
|
+
|
|
1446
|
+
:return: the attributes of this Instance.
|
|
1447
|
+
:rtype: Iterator[Attribute]
|
|
1448
|
+
"""
|
|
1449
|
+
leaf_object = self.__get_leaf_snl_object()
|
|
1450
|
+
for attribute in leaf_object.getAttributes():
|
|
1451
|
+
yield Attribute(attribute)
|
|
1452
|
+
|
|
1453
|
+
def count_attributes(self) -> int:
|
|
1454
|
+
"""Count the attributes of this Instance.
|
|
1455
|
+
|
|
1456
|
+
:return: the number of attributes of this Instance.
|
|
1457
|
+
:rtype: int
|
|
1458
|
+
"""
|
|
1459
|
+
return sum(1 for _ in self.get_attributes())
|
|
1460
|
+
|
|
1461
|
+
def get_design(self) -> "Instance":
|
|
1462
|
+
"""
|
|
1463
|
+
:return: the Instance containing this instance.
|
|
1464
|
+
:rtype: Instance
|
|
1465
|
+
"""
|
|
1466
|
+
path = self.pathIDs.copy()
|
|
1467
|
+
if len(self.pathIDs) == 1:
|
|
1468
|
+
return get_top()
|
|
1469
|
+
path.pop()
|
|
1470
|
+
return Instance(path)
|
|
1471
|
+
|
|
1472
|
+
def delete(self):
|
|
1473
|
+
"""Delete this instance."""
|
|
1474
|
+
if self.is_top():
|
|
1475
|
+
raise ValueError("Cannot delete the top instance")
|
|
1476
|
+
# FIXME: should be upper path ?
|
|
1477
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1478
|
+
inst = get_snl_instance_from_id_list(self.pathIDs)
|
|
1479
|
+
naja.SNLUniquifier(path)
|
|
1480
|
+
inst.destroy()
|
|
1481
|
+
|
|
1482
|
+
def get_name(self) -> str:
|
|
1483
|
+
"""
|
|
1484
|
+
:return: the name of the instance or name of the top is this is the top.
|
|
1485
|
+
:rtype: str
|
|
1486
|
+
"""
|
|
1487
|
+
if self.is_top():
|
|
1488
|
+
return self.get_model_name()
|
|
1489
|
+
else:
|
|
1490
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1491
|
+
return path.getTailInstance().getName()
|
|
1492
|
+
|
|
1493
|
+
def set_name(self, name: str):
|
|
1494
|
+
"""Set the name of this instance.
|
|
1495
|
+
|
|
1496
|
+
:param str name: the new name of the instance.
|
|
1497
|
+
"""
|
|
1498
|
+
if self.is_top():
|
|
1499
|
+
topSNLDesign = naja.NLUniverse.get().getTopDesign()
|
|
1500
|
+
topSNLDesign.setName(name)
|
|
1501
|
+
else:
|
|
1502
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1503
|
+
# We need to uniquify until parent instance if parent instance
|
|
1504
|
+
# is not top. path.size == 1 for instance under top
|
|
1505
|
+
if path.size() > 1:
|
|
1506
|
+
naja.SNLUniquifier(path.getHeadPath())
|
|
1507
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1508
|
+
inst = path.getTailInstance()
|
|
1509
|
+
inst.setName(name)
|
|
1510
|
+
|
|
1511
|
+
def get_model_name(self) -> str:
|
|
1512
|
+
"""
|
|
1513
|
+
:return: the name of the model of the instance
|
|
1514
|
+
or name of the top is this is the top.
|
|
1515
|
+
:rtype: str
|
|
1516
|
+
"""
|
|
1517
|
+
return self.__get_snl_model().getName()
|
|
1518
|
+
|
|
1519
|
+
def get_model_id(self) -> tuple[int, int, int]:
|
|
1520
|
+
"""
|
|
1521
|
+
:return: the ID of the model of this Instance
|
|
1522
|
+
or ID of the top if this is the top.
|
|
1523
|
+
"""
|
|
1524
|
+
model = self.__get_snl_model()
|
|
1525
|
+
return model.getDB().getID(), model.getLibrary().getID(), model.getID()
|
|
1526
|
+
|
|
1527
|
+
def create_child_instance(self, model: str, name: str) -> "Instance":
|
|
1528
|
+
"""Create a child instance with the given model and name.
|
|
1529
|
+
|
|
1530
|
+
:param str model: the name of the model of the instance to create.
|
|
1531
|
+
:param str name: the name of the instance to create.
|
|
1532
|
+
:return: the created Instance.
|
|
1533
|
+
:rtype: Instance
|
|
1534
|
+
"""
|
|
1535
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1536
|
+
if path.size() > 0:
|
|
1537
|
+
naja.SNLUniquifier(path)
|
|
1538
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1539
|
+
design = self.__get_snl_model()
|
|
1540
|
+
new_instance_model = self.__find_snl_model(model)
|
|
1541
|
+
if new_instance_model is None:
|
|
1542
|
+
raise ValueError(
|
|
1543
|
+
f"Cannot create instance {name} in {self}: model {model} cannot be found"
|
|
1544
|
+
)
|
|
1545
|
+
newSNLInstance = naja.SNLInstance.create(design, new_instance_model, name)
|
|
1546
|
+
path = naja.SNLPath(path, newSNLInstance)
|
|
1547
|
+
return Instance(path)
|
|
1548
|
+
|
|
1549
|
+
def create_term(self, name: str, direction: Term.Direction) -> Term:
|
|
1550
|
+
"""Create a Term in this Instance with the given name and direction.
|
|
1551
|
+
|
|
1552
|
+
:param str name: the name of the Term to create.
|
|
1553
|
+
:param Term.Direction direction: the direction of the Term to create.
|
|
1554
|
+
:return: the created Term.
|
|
1555
|
+
"""
|
|
1556
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1557
|
+
if path.size() > 0:
|
|
1558
|
+
naja.SNLUniquifier(path)
|
|
1559
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1560
|
+
design = self.__get_snl_model()
|
|
1561
|
+
newSNLTerm = naja.SNLScalarTerm.create(design, direction.value, name)
|
|
1562
|
+
return Term(path.getInstanceIDs(), newSNLTerm)
|
|
1563
|
+
|
|
1564
|
+
def create_output_term(self, name: str) -> Term:
|
|
1565
|
+
"""Create an output Term in this Instance with the given name.
|
|
1566
|
+
|
|
1567
|
+
:param str name: the name of the Term to create.
|
|
1568
|
+
:return: the created Term.
|
|
1569
|
+
:rtype: Term
|
|
1570
|
+
"""
|
|
1571
|
+
return self.create_term(name, Term.Direction.OUTPUT)
|
|
1572
|
+
|
|
1573
|
+
def create_input_term(self, name: str) -> Term:
|
|
1574
|
+
"""Create an input Term in this Instance with the given name.
|
|
1575
|
+
|
|
1576
|
+
:param str name: the name of the Term to create.
|
|
1577
|
+
:return: the created Term.
|
|
1578
|
+
:rtype: Term
|
|
1579
|
+
"""
|
|
1580
|
+
return self.create_term(name, Term.Direction.INPUT)
|
|
1581
|
+
|
|
1582
|
+
def create_inout_term(self, name: str) -> Term:
|
|
1583
|
+
"""Create an inout Term in this Instance with the given name.
|
|
1584
|
+
|
|
1585
|
+
:param str name: the name of the Term to create.
|
|
1586
|
+
:return: the created Term.
|
|
1587
|
+
:rtype: Term
|
|
1588
|
+
"""
|
|
1589
|
+
return self.create_term(name, Term.Direction.INOUT)
|
|
1590
|
+
|
|
1591
|
+
def create_bus_term(self, name: str, msb: int, lsb: int, direction: Term.Direction) -> Term:
|
|
1592
|
+
"""Create a bus Term in this Instance with the given name, msb, lsb and direction.
|
|
1593
|
+
|
|
1594
|
+
:param str name: the name of the Term to create.
|
|
1595
|
+
:param int msb: the most significant bit of the Term to create.
|
|
1596
|
+
:param int lsb: the least significant bit of the Term to create.
|
|
1597
|
+
:param Term.Direction direction: the direction of the Term to create.
|
|
1598
|
+
:return: the created Term.
|
|
1599
|
+
"""
|
|
1600
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1601
|
+
if path.size() > 0:
|
|
1602
|
+
naja.SNLUniquifier(path)
|
|
1603
|
+
design = self.__get_snl_model()
|
|
1604
|
+
newSNLTerm = naja.SNLBusTerm.create(design, direction.value, msb, lsb, name)
|
|
1605
|
+
return Term(self.pathIDs, newSNLTerm)
|
|
1606
|
+
|
|
1607
|
+
def create_inout_bus_term(self, name: str, msb: int, lsb: int) -> Term:
|
|
1608
|
+
"""Create an inout bus Term in this Instance with the given name, msb and lsb.
|
|
1609
|
+
|
|
1610
|
+
:param str name: the name of the Term to create.
|
|
1611
|
+
:param int msb: the most significant bit of the Term to create.
|
|
1612
|
+
:param int lsb: the least significant bit of the Term to create.
|
|
1613
|
+
:return: the created Term.
|
|
1614
|
+
:rtype: Term
|
|
1615
|
+
"""
|
|
1616
|
+
return self.create_bus_term(name, msb, lsb, Term.Direction.INOUT)
|
|
1617
|
+
|
|
1618
|
+
def create_output_bus_term(self, name: str, msb: int, lsb: int) -> Term:
|
|
1619
|
+
"""Create an output bus Term in this Instance with the given name, msb and lsb.
|
|
1620
|
+
|
|
1621
|
+
:param str name: the name of the Term to create.
|
|
1622
|
+
:param int msb: the most significant bit of the Term to create.
|
|
1623
|
+
:param int lsb: the least significant bit of the Term to create.
|
|
1624
|
+
:return: the created Term.
|
|
1625
|
+
:rtype: Term
|
|
1626
|
+
"""
|
|
1627
|
+
return self.create_bus_term(name, msb, lsb, Term.Direction.OUTPUT)
|
|
1628
|
+
|
|
1629
|
+
def create_input_bus_term(self, name: str, msb: int, lsb: int) -> Term:
|
|
1630
|
+
"""Create an input bus Term in this Instance with the given name, msb and lsb.
|
|
1631
|
+
|
|
1632
|
+
:param str name: the name of the Term to create.
|
|
1633
|
+
:param int msb: the most significant bit of the Term to create.
|
|
1634
|
+
:param int lsb: the least significant bit of the Term to create.
|
|
1635
|
+
:return: the created Term.
|
|
1636
|
+
:rtype: Term
|
|
1637
|
+
"""
|
|
1638
|
+
return self.create_bus_term(name, msb, lsb, Term.Direction.INPUT)
|
|
1639
|
+
|
|
1640
|
+
def create_net(self, name: str) -> Net:
|
|
1641
|
+
"""Create a scalar Net in this Instance with the given name.
|
|
1642
|
+
|
|
1643
|
+
:param str name: the name of the Net to create.
|
|
1644
|
+
:return: the created Net.
|
|
1645
|
+
:rtype: Net
|
|
1646
|
+
"""
|
|
1647
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1648
|
+
if path.size():
|
|
1649
|
+
naja.SNLUniquifier(path)
|
|
1650
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1651
|
+
model = self.__get_snl_model()
|
|
1652
|
+
newSNLNet = naja.SNLScalarNet.create(model, name)
|
|
1653
|
+
return Net(path, newSNLNet)
|
|
1654
|
+
|
|
1655
|
+
def create_bus_net(self, name: str, msb: int, lsb: int) -> Net:
|
|
1656
|
+
"""Create a bus Net in this Instance with the given name, msb and lsb.
|
|
1657
|
+
|
|
1658
|
+
:param str name: the name of the Net to create.
|
|
1659
|
+
:param int msb: the most significant bit of the Net to create.
|
|
1660
|
+
:param int lsb: the least significant bit of the Net to create.
|
|
1661
|
+
:return: the created Net.
|
|
1662
|
+
:rtype: Net
|
|
1663
|
+
"""
|
|
1664
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1665
|
+
if path.size():
|
|
1666
|
+
naja.SNLUniquifier(path)
|
|
1667
|
+
path = get_snl_path_from_id_list(self.pathIDs)
|
|
1668
|
+
model = self.__get_snl_model()
|
|
1669
|
+
newSNLNet = naja.SNLBusNet.create(model, msb, lsb, name)
|
|
1670
|
+
return Net(path, newSNLNet)
|
|
1671
|
+
|
|
1672
|
+
def dump_verilog(self, path: str):
|
|
1673
|
+
"""Dump the verilog of this instance.
|
|
1674
|
+
|
|
1675
|
+
:param str path: the file path where to dump the verilog.
|
|
1676
|
+
:rtype: None
|
|
1677
|
+
:raises ValueError: if the path does not end with .v.
|
|
1678
|
+
:raises FileNotFoundError: if the directory of the path does not exist.
|
|
1679
|
+
"""
|
|
1680
|
+
# path should be a file path of the form "path/to/file.v"
|
|
1681
|
+
if not path.endswith(".v"):
|
|
1682
|
+
raise ValueError("The path must end with .v")
|
|
1683
|
+
dir_path = os.path.dirname(path) or "."
|
|
1684
|
+
if not os.path.exists(dir_path):
|
|
1685
|
+
raise FileNotFoundError(f"The directory {dir_path} does not exist")
|
|
1686
|
+
self.__get_snl_model().dumpVerilog(dir_path, os.path.basename(path))
|
|
1687
|
+
|
|
1688
|
+
def get_truth_table(self):
|
|
1689
|
+
"""
|
|
1690
|
+
:return: the truth table of the instance.
|
|
1691
|
+
:rtype: list[str]
|
|
1692
|
+
"""
|
|
1693
|
+
return self.__get_snl_model().getTruthTable()
|
|
1694
|
+
|
|
1695
|
+
def add_clock_related_inputs(self, clock_term: Term, input_terms: List[Term]):
|
|
1696
|
+
"""Add input terms that are related to the given clock term.
|
|
1697
|
+
|
|
1698
|
+
:param clock_term: the clock term to check for related inputs.
|
|
1699
|
+
:param input_terms: a list of input terms to add.
|
|
1700
|
+
:return: None
|
|
1701
|
+
"""
|
|
1702
|
+
snlterms = [term.get_snl_term() for term in input_terms]
|
|
1703
|
+
self.__get_snl_model().addInputsToClockArcs(snlterms, clock_term.get_snl_term())
|
|
1704
|
+
|
|
1705
|
+
def add_clock_related_outputs(self, clock_term: Term, output_terms: List[Term]):
|
|
1706
|
+
"""Add output terms that are related to the given clock term.
|
|
1707
|
+
|
|
1708
|
+
:param clock_term: the clock term to check for related outputs.
|
|
1709
|
+
:param output_terms: a list of output terms to add.
|
|
1710
|
+
:return: None
|
|
1711
|
+
"""
|
|
1712
|
+
# convert Term objects to SNL terms
|
|
1713
|
+
snlterms = [term.get_snl_term() for term in output_terms]
|
|
1714
|
+
self.__get_snl_model().addClockToOutputsArcs(clock_term.get_snl_term(), snlterms)
|
|
1715
|
+
|
|
1716
|
+
def add_combinatorial_arcs(self, input_terms: List[Term], output_terms: List[Term]):
|
|
1717
|
+
"""Add input terms that are combinatorial inputs for the given output term.
|
|
1718
|
+
|
|
1719
|
+
:param output_term: the output term to check for combinatorial inputs.
|
|
1720
|
+
:param input_terms: a list of input terms to add.
|
|
1721
|
+
:return: None
|
|
1722
|
+
"""
|
|
1723
|
+
self.__get_snl_model().addCombinatorialArcs(
|
|
1724
|
+
[term.get_snl_term() for term in input_terms],
|
|
1725
|
+
[term.get_snl_term() for term in output_terms])
|
|
1726
|
+
|
|
1727
|
+
|
|
1728
|
+
def __get_top_db() -> naja.NLDB:
|
|
1729
|
+
if naja.NLUniverse.get() is None:
|
|
1730
|
+
naja.NLUniverse.create()
|
|
1731
|
+
if naja.NLUniverse.get().getTopDB() is None:
|
|
1732
|
+
db = naja.NLDB.create(naja.NLUniverse.get())
|
|
1733
|
+
naja.NLUniverse.get().setTopDB(db)
|
|
1734
|
+
return naja.NLUniverse.get().getTopDB()
|
|
1735
|
+
|
|
1736
|
+
|
|
1737
|
+
def reset():
|
|
1738
|
+
"""Reset the environment by deleting everything.
|
|
1739
|
+
:rtype: None
|
|
1740
|
+
"""
|
|
1741
|
+
u = naja.NLUniverse.get()
|
|
1742
|
+
if u is not None:
|
|
1743
|
+
u.destroy()
|
|
1744
|
+
|
|
1745
|
+
|
|
1746
|
+
def get_top():
|
|
1747
|
+
"""
|
|
1748
|
+
:return: the top Instance.
|
|
1749
|
+
:rtype: Instance
|
|
1750
|
+
"""
|
|
1751
|
+
return Instance(naja.SNLPath())
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
def create_top(name: str) -> Instance:
|
|
1755
|
+
"""Create a top instance with the given name.
|
|
1756
|
+
|
|
1757
|
+
:param str name: the name of the top instance to create.
|
|
1758
|
+
:return: the created top Instance.
|
|
1759
|
+
:rtype: Instance
|
|
1760
|
+
"""
|
|
1761
|
+
# init
|
|
1762
|
+
db = __get_top_db()
|
|
1763
|
+
# create top design
|
|
1764
|
+
lib = naja.NLLibrary.create(db)
|
|
1765
|
+
top = naja.SNLDesign.create(lib, name)
|
|
1766
|
+
naja.NLUniverse.get().setTopDesign(top)
|
|
1767
|
+
return Instance()
|
|
1768
|
+
|
|
1769
|
+
|
|
1770
|
+
@dataclass
|
|
1771
|
+
class VerilogConfig:
|
|
1772
|
+
keep_assigns: bool = True
|
|
1773
|
+
allow_unknown_designs: bool = False
|
|
1774
|
+
|
|
1775
|
+
|
|
1776
|
+
def load_verilog(files: Union[str, List[str]], config: VerilogConfig = None) -> Instance:
|
|
1777
|
+
"""Load verilog files into the top design.
|
|
1778
|
+
|
|
1779
|
+
:param files: a list of verilog files to load or a single file.
|
|
1780
|
+
:param config: the configuration to use when loading the files.
|
|
1781
|
+
:return: the top Instance.
|
|
1782
|
+
:rtype: Instance
|
|
1783
|
+
:raises Exception: if no files are provided.
|
|
1784
|
+
"""
|
|
1785
|
+
if isinstance(files, str):
|
|
1786
|
+
files = [files]
|
|
1787
|
+
if not files or len(files) == 0:
|
|
1788
|
+
raise Exception("No verilog files provided")
|
|
1789
|
+
if config is None:
|
|
1790
|
+
config = VerilogConfig() # Use default settings
|
|
1791
|
+
start_time = time.time()
|
|
1792
|
+
logging.info(f"Loading verilog: {', '.join(files)}")
|
|
1793
|
+
__get_top_db().loadVerilog(
|
|
1794
|
+
files,
|
|
1795
|
+
keep_assigns=config.keep_assigns,
|
|
1796
|
+
allow_unknown_designs=config.allow_unknown_designs
|
|
1797
|
+
)
|
|
1798
|
+
execution_time = time.time() - start_time
|
|
1799
|
+
logging.info(f"Loading done in {execution_time:.2f} seconds")
|
|
1800
|
+
return get_top()
|
|
1801
|
+
|
|
1802
|
+
|
|
1803
|
+
def load_liberty(files: Union[str, List[str]]):
|
|
1804
|
+
"""Load liberty files.
|
|
1805
|
+
|
|
1806
|
+
:param files: a list of liberty files to load or a single file.
|
|
1807
|
+
:rtype: None
|
|
1808
|
+
:raises Exception: if no liberty files are provided.
|
|
1809
|
+
"""
|
|
1810
|
+
if isinstance(files, str):
|
|
1811
|
+
files = [files]
|
|
1812
|
+
if not files or len(files) == 0:
|
|
1813
|
+
raise Exception("No liberty files provided")
|
|
1814
|
+
logging.info(f"Loading liberty files: {', '.join(files)}")
|
|
1815
|
+
__get_top_db().loadLibertyPrimitives(files)
|
|
1816
|
+
|
|
1817
|
+
|
|
1818
|
+
def load_primitives(name: str):
|
|
1819
|
+
"""Loads a primitive library embedded in najaeda.
|
|
1820
|
+
|
|
1821
|
+
Currently supported libraries are:
|
|
1822
|
+
|
|
1823
|
+
- xilinx
|
|
1824
|
+
- yosys
|
|
1825
|
+
:param str name: the name of the primitives library to load.
|
|
1826
|
+
:raises ValueError: if the name is not recognized.
|
|
1827
|
+
:rtype: None
|
|
1828
|
+
"""
|
|
1829
|
+
if name == "xilinx":
|
|
1830
|
+
from najaeda.primitives import xilinx
|
|
1831
|
+
xilinx.load(__get_top_db())
|
|
1832
|
+
elif name == "yosys":
|
|
1833
|
+
from najaeda.primitives import yosys
|
|
1834
|
+
yosys.load(__get_top_db())
|
|
1835
|
+
else:
|
|
1836
|
+
raise ValueError(f"Unknown primitives library: {name}")
|
|
1837
|
+
|
|
1838
|
+
|
|
1839
|
+
def load_primitives_from_file(file: str):
|
|
1840
|
+
"""Loads a primitives library from a file.
|
|
1841
|
+
|
|
1842
|
+
:param str file: the path to the primitives library file.
|
|
1843
|
+
The file must define a function `load(db)`.
|
|
1844
|
+
"""
|
|
1845
|
+
logging.info(f"Loading primitives from file: {file}")
|
|
1846
|
+
if not os.path.isfile(file):
|
|
1847
|
+
raise FileNotFoundError(f"Cannot load primitives from non existing file: {file}")
|
|
1848
|
+
import importlib.util
|
|
1849
|
+
spec = importlib.util.spec_from_file_location("user_module", file)
|
|
1850
|
+
module = importlib.util.module_from_spec(spec)
|
|
1851
|
+
sys.modules["user_module"] = module
|
|
1852
|
+
spec.loader.exec_module(module)
|
|
1853
|
+
|
|
1854
|
+
if not hasattr(module, "load"):
|
|
1855
|
+
raise RuntimeError(f"The file {file} must define a function named `load(db)`")
|
|
1856
|
+
|
|
1857
|
+
db = __get_top_db()
|
|
1858
|
+
module.load(db)
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
def load_naja_if(path: str):
|
|
1862
|
+
"""Load the Naja IF from the given path."""
|
|
1863
|
+
if not os.path.isdir(path):
|
|
1864
|
+
raise FileNotFoundError(f"Cannot load Naja IF from non existing directory: {path}")
|
|
1865
|
+
logging.info(f"Loading Naja IF from {path}")
|
|
1866
|
+
naja.NLDB.loadNajaIF(path)
|
|
1867
|
+
|
|
1868
|
+
|
|
1869
|
+
def dump_naja_if(path: str):
|
|
1870
|
+
"""Dump the Naja IF to the given path."""
|
|
1871
|
+
__get_top_db().dumpNajaIF(path)
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
def get_primitives_library() -> naja.NLLibrary:
|
|
1875
|
+
lib = __get_top_db().getLibrary("PRIMS")
|
|
1876
|
+
if lib is None:
|
|
1877
|
+
lib = naja.NLLibrary.createPrimitives(__get_top_db(), "PRIMS")
|
|
1878
|
+
return lib
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
def get_model_name(id: tuple[int, int, int]) -> str:
|
|
1882
|
+
"""
|
|
1883
|
+
:param tuple[int, int, int] id: the id of the model.
|
|
1884
|
+
:return: the name of the model given its id or None if it does not exist.
|
|
1885
|
+
:rtype: str or None
|
|
1886
|
+
"""
|
|
1887
|
+
u = naja.NLUniverse.get()
|
|
1888
|
+
if u:
|
|
1889
|
+
db = u.getDB(id[0])
|
|
1890
|
+
if db:
|
|
1891
|
+
lib = db.getLibrary(id[1])
|
|
1892
|
+
if lib:
|
|
1893
|
+
model = lib.getSNLDesign(id[2])
|
|
1894
|
+
if model:
|
|
1895
|
+
return model.getName()
|
|
1896
|
+
return None
|
|
1897
|
+
|
|
1898
|
+
|
|
1899
|
+
def apply_dle():
|
|
1900
|
+
"""Apply the DLE (Dead Logic Elimination) to the top design.
|
|
1901
|
+
:rtype: None
|
|
1902
|
+
"""
|
|
1903
|
+
top = naja.NLUniverse.get().getTopDesign()
|
|
1904
|
+
if top is not None:
|
|
1905
|
+
naja.NLUniverse.get().applyDLE()
|
|
1906
|
+
|
|
1907
|
+
|
|
1908
|
+
def apply_constant_propagation():
|
|
1909
|
+
"""Apply constant propagation to the top design.
|
|
1910
|
+
:rtype: None
|
|
1911
|
+
"""
|
|
1912
|
+
top = naja.NLUniverse.get().getTopDesign()
|
|
1913
|
+
if top is not None:
|
|
1914
|
+
naja.NLUniverse.get().applyConstantPropagation()
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
def get_max_fanout() -> list:
|
|
1918
|
+
"""Get the maximum fanout of the top design.
|
|
1919
|
+
|
|
1920
|
+
:return: the maximum fanout of the top design.
|
|
1921
|
+
:rtype: int
|
|
1922
|
+
"""
|
|
1923
|
+
u = naja.NLUniverse.get()
|
|
1924
|
+
if u is not None:
|
|
1925
|
+
top = naja.NLUniverse.get().getTopDesign()
|
|
1926
|
+
if top is not None:
|
|
1927
|
+
max_fanout = naja.NLUniverse.get().getMaxFanout()
|
|
1928
|
+
result = []
|
|
1929
|
+
index = 0
|
|
1930
|
+
result.append(max_fanout[0])
|
|
1931
|
+
fanouts = []
|
|
1932
|
+
for entry in max_fanout[1]:
|
|
1933
|
+
fanout = []
|
|
1934
|
+
driver = entry[0]
|
|
1935
|
+
component = driver.getNetComponent()
|
|
1936
|
+
path = driver.getPath().getInstanceIDs()
|
|
1937
|
+
if isinstance(component, naja.SNLInstTerm):
|
|
1938
|
+
path.append(component.getInstance().getID())
|
|
1939
|
+
component = component.getBitTerm()
|
|
1940
|
+
print(component)
|
|
1941
|
+
print(path)
|
|
1942
|
+
fanout.append(Term(path, component))
|
|
1943
|
+
readers = []
|
|
1944
|
+
for item in entry[1]:
|
|
1945
|
+
component = item.getNetComponent()
|
|
1946
|
+
path = item.getPath().getInstanceIDs()
|
|
1947
|
+
if isinstance(component, naja.SNLInstTerm):
|
|
1948
|
+
path.append(component.getInstance().getID())
|
|
1949
|
+
component = component.getBitTerm()
|
|
1950
|
+
readers.append(Term(path, component))
|
|
1951
|
+
fanout.append(readers)
|
|
1952
|
+
fanouts.append(fanout)
|
|
1953
|
+
index += 1
|
|
1954
|
+
result.append(fanouts)
|
|
1955
|
+
return result
|
|
1956
|
+
return [0]
|
|
1957
|
+
|
|
1958
|
+
|
|
1959
|
+
def get_max_logic_level() -> list:
|
|
1960
|
+
"""Get the maximum logic level of the top design.
|
|
1961
|
+
|
|
1962
|
+
:return: the maximum logic level of the top design.
|
|
1963
|
+
:rtype: int
|
|
1964
|
+
"""
|
|
1965
|
+
u = naja.NLUniverse.get()
|
|
1966
|
+
if u is not None:
|
|
1967
|
+
top = naja.NLUniverse.get().getTopDesign()
|
|
1968
|
+
if top is not None:
|
|
1969
|
+
max_logic_level = naja.NLUniverse.get().getMaxLogicLevel()
|
|
1970
|
+
result = []
|
|
1971
|
+
index = 0
|
|
1972
|
+
for entry in max_logic_level:
|
|
1973
|
+
if index == 0:
|
|
1974
|
+
result.append(max_logic_level[0])
|
|
1975
|
+
else:
|
|
1976
|
+
paths = []
|
|
1977
|
+
for path in entry:
|
|
1978
|
+
llpath = []
|
|
1979
|
+
for item in path:
|
|
1980
|
+
component = item.getNetComponent()
|
|
1981
|
+
pathIDs = item.getPath().getInstanceIDs()
|
|
1982
|
+
if isinstance(component, naja.SNLInstTerm):
|
|
1983
|
+
pathIDs.append(component.getInstance().getID())
|
|
1984
|
+
component = component.getBitTerm()
|
|
1985
|
+
llpath.append(Term(pathIDs, component))
|
|
1986
|
+
paths.append(llpath)
|
|
1987
|
+
result.append(paths)
|
|
1988
|
+
index += 1
|
|
1989
|
+
return result
|
|
1990
|
+
return [0]
|