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.
Files changed (44) hide show
  1. najaeda/__init__.py +20 -0
  2. najaeda/_version.py +16 -0
  3. najaeda/docs/.readthedocs.yaml +26 -0
  4. najaeda/docs/requirements.txt +7 -0
  5. najaeda/docs/source/api.rst +7 -0
  6. najaeda/docs/source/common_classes.rst +11 -0
  7. najaeda/docs/source/conf.py +57 -0
  8. najaeda/docs/source/equipotential.rst +15 -0
  9. najaeda/docs/source/examples.rst.in +66 -0
  10. najaeda/docs/source/index.rst +17 -0
  11. najaeda/docs/source/instance.rst +34 -0
  12. najaeda/docs/source/introduction.rst +68 -0
  13. najaeda/docs/source/net.rst +20 -0
  14. najaeda/docs/source/netlist_classes.rst +11 -0
  15. najaeda/docs/source/preprocessor.py +73 -0
  16. najaeda/docs/source/term.rst +20 -0
  17. najaeda/docs/source/visitors.rst +13 -0
  18. najaeda/instance_visitor.py +43 -0
  19. najaeda/naja.pyd +0 -0
  20. najaeda/naja_bne.dll +0 -0
  21. najaeda/naja_dnl.dll +0 -0
  22. najaeda/naja_metrics.dll +0 -0
  23. najaeda/naja_nl.dll +0 -0
  24. najaeda/naja_opt.dll +0 -0
  25. najaeda/naja_python.dll +0 -0
  26. najaeda/native/__init__.py +0 -0
  27. najaeda/native/stats.py +341 -0
  28. najaeda/net_visitor.py +53 -0
  29. najaeda/netlist.py +1990 -0
  30. najaeda/pandas_stats.py +32 -0
  31. najaeda/primitives/__init__.py +0 -0
  32. najaeda/primitives/utils.py +19 -0
  33. najaeda/primitives/xilinx.py +541 -0
  34. najaeda/primitives/yosys.py +288 -0
  35. najaeda/stats.py +410 -0
  36. najaeda-0.3.3.dist-info/DELVEWHEEL +2 -0
  37. najaeda-0.3.3.dist-info/METADATA +108 -0
  38. najaeda-0.3.3.dist-info/RECORD +44 -0
  39. najaeda-0.3.3.dist-info/WHEEL +5 -0
  40. najaeda-0.3.3.dist-info/licenses/AUTHORS +7 -0
  41. najaeda-0.3.3.dist-info/licenses/LICENSE +201 -0
  42. najaeda.libs/msvcp140.dll +0 -0
  43. najaeda.libs/tbb12.dll +0 -0
  44. 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]