najaeda 0.1.22__cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.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.

Potentially problematic release.


This version of najaeda might be problematic. Click here for more details.

Files changed (40) hide show
  1. najaeda/__init__.py +0 -0
  2. najaeda/docs/.readthedocs.yaml +26 -0
  3. najaeda/docs/requirements.txt +7 -0
  4. najaeda/docs/source/api.rst +7 -0
  5. najaeda/docs/source/common_classes.rst +11 -0
  6. najaeda/docs/source/conf.py +57 -0
  7. najaeda/docs/source/equipotential.rst +15 -0
  8. najaeda/docs/source/examples.rst.in +66 -0
  9. najaeda/docs/source/index.rst +17 -0
  10. najaeda/docs/source/instance.rst +19 -0
  11. najaeda/docs/source/introduction.rst +53 -0
  12. najaeda/docs/source/net.rst +20 -0
  13. najaeda/docs/source/netlist_classes.rst +11 -0
  14. najaeda/docs/source/preprocessor.py +73 -0
  15. najaeda/docs/source/term.rst +20 -0
  16. najaeda/docs/source/visitors.rst +13 -0
  17. najaeda/instance_visitor.py +43 -0
  18. najaeda/libnaja_bne.so +0 -0
  19. najaeda/libnaja_dnl.so +0 -0
  20. najaeda/libnaja_nl.so +0 -0
  21. najaeda/libnaja_opt.so +0 -0
  22. najaeda/libnaja_python.so +0 -0
  23. najaeda/naja.so +0 -0
  24. najaeda/native/__init__.py +0 -0
  25. najaeda/native/stats.py +341 -0
  26. najaeda/net_visitor.py +53 -0
  27. najaeda/netlist.py +1576 -0
  28. najaeda/pandas_stats.py +32 -0
  29. najaeda/primitives/__init__.py +0 -0
  30. najaeda/primitives/xilinx.py +454 -0
  31. najaeda/stats.py +413 -0
  32. najaeda-0.1.22.dist-info/METADATA +79 -0
  33. najaeda-0.1.22.dist-info/RECORD +40 -0
  34. najaeda-0.1.22.dist-info/WHEEL +6 -0
  35. najaeda-0.1.22.dist-info/licenses/AUTHORS +7 -0
  36. najaeda-0.1.22.dist-info/licenses/LICENSE +201 -0
  37. najaeda.libs/libcapnp-1-d562dcbf.1.0.so +0 -0
  38. najaeda.libs/libkj-1-fe2e75c4.1.0.so +0 -0
  39. najaeda.libs/libtbb-58378cc2.so.12.15 +0 -0
  40. najaeda.libs/libtbbmalloc-8bd2c113.so.2.15 +0 -0
najaeda/netlist.py ADDED
@@ -0,0 +1,1576 @@
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 itertools
7
+ import time
8
+ import logging
9
+ import hashlib
10
+ import struct
11
+ import sys
12
+ import os
13
+ from enum import Enum
14
+ from typing import Union, List
15
+
16
+ from najaeda import naja
17
+
18
+
19
+ def get_none_existent():
20
+ return sys.maxsize
21
+
22
+
23
+ def consistent_hash(obj):
24
+ def default_serializer(o):
25
+ if isinstance(o, (str, int, float, bool, type(None))):
26
+ return o
27
+ elif isinstance(o, (list, tuple)):
28
+ return [default_serializer(i) for i in o]
29
+ else:
30
+ return str(o)
31
+
32
+ def hash_value(value):
33
+ if isinstance(value, int):
34
+ return struct.pack("!q", value)
35
+ else:
36
+ raise TypeError(f"Unsupported type: {type(value)}")
37
+
38
+ def hash_object(o):
39
+ if isinstance(o, (list, tuple)):
40
+ return b"".join(hash_object(i) for i in o)
41
+ else:
42
+ return hash_value(o)
43
+
44
+ serialized_obj = default_serializer(obj)
45
+ obj_bytes = hash_object(serialized_obj)
46
+ return int(hashlib.sha256(obj_bytes).hexdigest(), 16)
47
+
48
+
49
+ def get_snl_instance_from_id_list(id_list: list) -> naja.SNLInstance:
50
+ design = naja.NLUniverse.get().getTopDesign()
51
+ # instance = None
52
+ # for id in id_list:
53
+ # instance = design.getInstanceByID(id)
54
+ # assert instance is not None
55
+ # design = instance.getModel()
56
+ # return instance
57
+ return design.getInstanceByIDList(id_list)
58
+
59
+
60
+ def get_snl_path_from_id_list(id_list: list) -> naja.SNLPath:
61
+ top = naja.NLUniverse.get().getTopDesign()
62
+ design = top
63
+ path = naja.SNLPath()
64
+ for id in id_list:
65
+ instance = design.getInstanceByID(id)
66
+ assert instance is not None
67
+ path = naja.SNLPath(path, instance)
68
+ assert path.getTailInstance() is not None
69
+ design = instance.getModel()
70
+ if len(id_list) > 0:
71
+ assert path.getTailInstance() is not None
72
+ return path
73
+
74
+
75
+ class Equipotential:
76
+ """Class that represents the term and wraps
77
+ some of the snl occurrence API.
78
+ """
79
+
80
+ def __init__(self, term):
81
+ path = get_snl_path_from_id_list(term.pathIDs)
82
+ snl_term = get_snl_term_for_ids_with_path(path, term.termIDs)
83
+ inst_term = None
84
+ if isinstance(snl_term, naja.SNLBusTerm):
85
+ raise ValueError("Equipotential cannot be constructed on bus term")
86
+ if len(term.pathIDs) == 0:
87
+ net = term.get_lower_net()
88
+ if net is None:
89
+ self.equi = None
90
+ return
91
+ inst_term = next(net.get_inst_terms(), None)
92
+ if inst_term is None:
93
+ self.equi = None
94
+ return
95
+ else:
96
+ path = naja.SNLPath(path, get_snl_instance_from_id_list(inst_term.pathIDs))
97
+ snl_term = get_snl_term_for_ids(inst_term.pathIDs, inst_term.termIDs)
98
+ else:
99
+ inst_term = term
100
+ ito = naja.SNLNetComponentOccurrence(
101
+ path.getHeadPath(), path.getTailInstance().getInstTerm(snl_term)
102
+ )
103
+ self.equi = naja.SNLEquipotential(ito)
104
+
105
+ def __eq__(self, value):
106
+ return self.equi == value.equi
107
+
108
+ def dump_dot(self, path: str):
109
+ """Dump the dot file of this equipotential."""
110
+ self.equi.dumpDotFile(path)
111
+
112
+ def get_inst_terms(self):
113
+ """Iterate over the instance terminals of this equipotential.
114
+
115
+ :return: an iterator over the instance terminals of this equipotential.
116
+ :rtype: Iterator[Term]
117
+ """
118
+ if self.equi is not None:
119
+ for term in self.equi.getInstTermOccurrences():
120
+ path = term.getPath().getPathIDs()
121
+ path.append(term.getInstTerm().getInstance().getID())
122
+ yield Term(path,
123
+ term.getInstTerm().getBitTerm())
124
+
125
+ def get_top_terms(self):
126
+ """Iterate over the top terminals of this equipotential.
127
+
128
+ :return: an iterator over the top terminals of this equipotential.
129
+ :rtype: Iterator[Term]
130
+ """
131
+ if self.equi is not None:
132
+ for term in self.equi.getTerms():
133
+ yield Term([], term)
134
+
135
+ def get_leaf_readers(self):
136
+ if self.equi is not None:
137
+ for term in self.equi.getInstTermOccurrences():
138
+ direction = term.getInstTerm().getDirection()
139
+ if direction != naja.SNLTerm.Direction.Output:
140
+ if term.getInstTerm().getInstance().getModel().isLeaf():
141
+ path = term.getPath().getPathIDs()
142
+ path.append(term.getInstTerm().getInstance().getID())
143
+ yield Term(path,
144
+ term.getInstTerm().getBitTerm())
145
+
146
+ def get_leaf_drivers(self):
147
+ if self.equi is not None:
148
+ for term in self.equi.getInstTermOccurrences():
149
+ direction = term.getInstTerm().getDirection()
150
+ if direction != naja.SNLTerm.Direction.Input:
151
+ if term.getInstTerm().getInstance().getModel().isLeaf():
152
+ path = term.getPath().getPathIDs()
153
+ path.append(term.getInstTerm().getInstance().getID())
154
+ yield Term(path,
155
+ term.getInstTerm().getBitTerm())
156
+
157
+ def get_top_readers(self):
158
+ if self.equi is not None:
159
+ for term in self.equi.getTerms():
160
+ direction = term.getDirection()
161
+ if direction != naja.SNLTerm.Direction.Input:
162
+ yield Term([], term)
163
+
164
+ def get_top_drivers(self):
165
+ if self.equi is not None:
166
+ for term in self.equi.getTerms():
167
+ direction = term.getDirection()
168
+ if direction != naja.SNLTerm.Direction.Output:
169
+ yield Term([], term)
170
+
171
+
172
+ class Net:
173
+ class Type(Enum):
174
+ STANDARD = naja.SNLNet.Type.Standard
175
+ ASSIGN0 = naja.SNLNet.Type.Assign0
176
+ ASSIGN1 = naja.SNLNet.Type.Assign1
177
+ SUPPLY0 = naja.SNLNet.Type.Supply0
178
+ SUPPLY1 = naja.SNLNet.Type.Supply1
179
+
180
+ def __init__(self, path, net=None, net_concat=None):
181
+ if net is not None and net_concat is not None:
182
+ raise ValueError(
183
+ "Only one of `net` or `net_concat` should be provided, not both."
184
+ )
185
+ if isinstance(path, naja.SNLPath):
186
+ if path.size() > 0:
187
+ self.pathIDs = path.getPathIDs()
188
+ else:
189
+ self.pathIDs = []
190
+ elif isinstance(path, list):
191
+ self.pathIDs = path.copy()
192
+ if net is not None:
193
+ self.net = net
194
+ elif net_concat is not None:
195
+ self.net_concat = net_concat
196
+
197
+ def __eq__(self, other):
198
+ if not isinstance(other, Net):
199
+ return NotImplemented
200
+ return vars(self) == vars(other)
201
+
202
+ def __ne__(self, other):
203
+ eq_result = self.__eq__(other)
204
+ if eq_result is NotImplemented:
205
+ return NotImplemented
206
+ return not eq_result
207
+
208
+ def __str__(self):
209
+ if hasattr(self, "net"):
210
+ net_str = str(self.net)
211
+ elif hasattr(self, "net_concat"):
212
+ net_str = "{" + ",".join(map(str, self.net_concat)) + "}"
213
+ path = get_snl_path_from_id_list(self.pathIDs)
214
+ if path.size() > 0:
215
+ return f"{path}/{net_str}"
216
+ return net_str
217
+
218
+ def get_name(self) -> str:
219
+ """
220
+ :return: the name of this Net.
221
+ :rtype: str
222
+ """
223
+ if hasattr(self, "net"):
224
+ return self.net.getName()
225
+ return "{" + ",".join(map(str, self.net_concat)) + "}"
226
+
227
+ def get_msb(self) -> int:
228
+ """
229
+ :return: the most significant bit of the net if it is a bus.
230
+ :rtype: int
231
+ """
232
+ if hasattr(self, "net") and isinstance(self.net, naja.SNLBusNet):
233
+ return self.net.getMSB()
234
+ return None
235
+
236
+ def get_lsb(self) -> int:
237
+ """
238
+ :return: the least significant bit of the net if it is a bus.
239
+ :rtype: int
240
+ """
241
+ if hasattr(self, "net") and isinstance(self.net, naja.SNLBusNet):
242
+ return self.net.getLSB()
243
+ return None
244
+
245
+ def is_bus(self) -> bool:
246
+ """
247
+ :return: True if the net is a bus.
248
+ :rtype: bool
249
+ """
250
+ return hasattr(self, "net") and isinstance(self.net, naja.SNLBusNet)
251
+
252
+ def is_bus_bit(self) -> bool:
253
+ """
254
+ :return: True if the net is a bit of a bus.
255
+ :rtype: bool
256
+ """
257
+ return hasattr(self, "net") and isinstance(self.net, naja.SNLBusNetBit)
258
+
259
+ def is_scalar(self) -> bool:
260
+ """
261
+ :return: True if the net is a scalar.
262
+ :rtype: bool
263
+ """
264
+ return hasattr(self, "net") and isinstance(self.net, naja.SNLScalarNet)
265
+
266
+ def is_bit(self) -> bool:
267
+ """
268
+ :return: True if the net is a bit.
269
+ :rtype: bool
270
+ """
271
+ return self.is_scalar() or self.is_bus_bit()
272
+
273
+ def is_concat(self) -> bool:
274
+ """
275
+ :return: True if the net is a concatenation.
276
+ :rtype: bool
277
+ """
278
+ return hasattr(self, "net_concat")
279
+
280
+ def is_const(self) -> bool:
281
+ """
282
+ :return: True if the net is a constant generator.
283
+ :rtype: bool
284
+ """
285
+ if hasattr(self, "net"):
286
+ return self.net.isConstant()
287
+ else:
288
+ for net in self.net_concat:
289
+ if not net.isConstant():
290
+ return False
291
+ return True
292
+
293
+ def set_type(self, net_type: Type):
294
+ """
295
+ :param Type net_type: the type of the net.
296
+ """
297
+ if hasattr(self, "net"):
298
+ self.net.setType(net_type.value)
299
+ else:
300
+ for net in self.net_concat:
301
+ net.setType(net_type.value)
302
+
303
+ def get_width(self) -> int:
304
+ """
305
+ :return: the width of the net.
306
+ :rtype: int
307
+ """
308
+ if hasattr(self, "net"):
309
+ return self.net.getWidth()
310
+ return sum(1 for _ in self.net_concat)
311
+
312
+ def get_bits(self):
313
+ """Iterate over the bits of this Net.
314
+ The iterator will return itself if the Net is scalar.
315
+ :return: an iterator over the bits of this Net.
316
+ :rtype: Iterator[Net]
317
+ """
318
+ if hasattr(self, "net"):
319
+ if isinstance(self.net, naja.SNLBusNet):
320
+ for bit in self.net.getBits():
321
+ yield Net(self.pathIDs, bit)
322
+ else:
323
+ yield self
324
+ else:
325
+ for net in self.net_concat:
326
+ yield Net(net)
327
+
328
+ def get_bit(self, index: int):
329
+ """
330
+ :param int index: the index of the bit to get.
331
+ :return: the Net bit at the given index or None if it does not exist.
332
+ :rtype: Net
333
+ """
334
+ if hasattr(self, "net"):
335
+ if isinstance(self.net, naja.SNLBusNet):
336
+ return Net(self.pathIDs, self.net.getBit(index))
337
+ else:
338
+ return None
339
+ if 0 <= index < len(self.net_concat):
340
+ return Net(self.pathIDs, self.net_concat[index])
341
+ return None
342
+
343
+ def get_inst_terms(self):
344
+ """
345
+ :return: an iterator over the instance terminals of the net.
346
+ :rtype: Iterator[Term]
347
+ """
348
+ if hasattr(self, "net_concat"):
349
+ raise ValueError("Cannot get inst terms from a net_concat")
350
+ path = self.pathIDs.copy()
351
+ for term in self.net.getInstTerms():
352
+ path.append(term.getInstance().getID())
353
+ yield Term(path, term.getBitTerm())
354
+ path.pop()
355
+
356
+ def get_design_terms(self):
357
+ """
358
+ :return: an iterator over the design terminals of the net.
359
+ :rtype: Iterator[Term]
360
+ """
361
+ if hasattr(self, "net_concat"):
362
+ raise ValueError("Cannot get terms from a net_concat")
363
+ for term in self.net.getBitTerms():
364
+ yield Term(self.pathIDs, term)
365
+
366
+ def get_terms(self):
367
+ """
368
+ :return: an iterator over the terminals of the net.
369
+ :rtype: Iterator[Term]
370
+ """
371
+ for term in itertools.chain(self.get_design_terms(), self.get_inst_terms()):
372
+ yield term
373
+
374
+
375
+ def get_snl_term_for_ids(pathIDs, termIDs):
376
+ path = get_snl_path_from_id_list(pathIDs)
377
+ model = None
378
+ if len(pathIDs) == 0:
379
+ model = naja.NLUniverse.get().getTopDesign()
380
+ else:
381
+ model = path.getTailInstance().getModel()
382
+ if termIDs[1] == get_none_existent():
383
+ return model.getTermByID(termIDs[0])
384
+ else:
385
+ snlterm = model.getTermByID(termIDs[0])
386
+ if isinstance(snlterm, naja.SNLBusTerm):
387
+ return snlterm.getBusTermBit(termIDs[1])
388
+ else:
389
+ return snlterm
390
+
391
+
392
+ def get_snl_term_for_ids_with_path(path, termIDs):
393
+ model = None
394
+ if path.size() == 0:
395
+ model = naja.NLUniverse.get().getTopDesign()
396
+ else:
397
+ model = path.getTailInstance().getModel()
398
+ if termIDs[1] == get_none_existent():
399
+ return model.getTermByID(termIDs[0])
400
+ else:
401
+ snlterm = model.getTermByID(termIDs[0])
402
+ if isinstance(snlterm, naja.SNLBusTerm):
403
+ return snlterm.getBusTermBit(termIDs[1])
404
+ else:
405
+ return snlterm
406
+
407
+
408
+ class Term:
409
+ INPUT = naja.SNLTerm.Direction.Input
410
+ OUTPUT = naja.SNLTerm.Direction.Output
411
+ INOUT = naja.SNLTerm.Direction.InOut
412
+
413
+ def __init__(self, path, term):
414
+ # self.termIDs = []
415
+ # if isinstance(term, naja.SNLBusTerm):
416
+ # self.termIDs = [term.getID(), -1]
417
+ # else:
418
+ self.termIDs = [term.getID(), term.getBit()]
419
+ self.pathIDs = path.copy()
420
+
421
+ def __eq__(self, other) -> bool:
422
+ return self.pathIDs == other.pathIDs and self.termIDs == other.termIDs
423
+
424
+ def __ne__(self, other) -> bool:
425
+ return not self == other
426
+
427
+ def __lt__(self, other) -> bool:
428
+ if self.pathIDs != other.pathIDs:
429
+ return self.pathIDs < other.pathIDs
430
+ return self.termIDs < other.termIDs
431
+
432
+ def __le__(self, other) -> bool:
433
+ return self < other or self == other
434
+
435
+ def __gt__(self, other) -> bool:
436
+ return not self <= other
437
+
438
+ def __ge__(self, other) -> bool:
439
+ return not self < other
440
+
441
+ def __hash__(self):
442
+ termIDs = []
443
+ snlterm = get_snl_term_for_ids(self.pathIDs, self.termIDs)
444
+ if isinstance(snlterm, naja.SNLBusTerm):
445
+ termIDs = [snlterm.getID(), -1]
446
+ else:
447
+ termIDs = [snlterm.getID(), snlterm.getBit()]
448
+ return consistent_hash((self.pathIDs, termIDs))
449
+
450
+ def __str__(self):
451
+ term_str = ""
452
+ path = get_snl_path_from_id_list(self.pathIDs)
453
+ if path.size() == 0:
454
+ term_str = get_snl_term_for_ids(self.pathIDs, self.termIDs).getName()
455
+ else:
456
+ term_str = (
457
+ f"{path}/{get_snl_term_for_ids(self.pathIDs, self.termIDs).getName()}"
458
+ )
459
+ if self.is_bus():
460
+ term_str += f"[{self.get_msb()}:{self.get_lsb()}]"
461
+ elif self.is_bus_bit():
462
+ term_str += f"[{self.get_lsb()}]"
463
+ return term_str
464
+
465
+ def __repr__(self) -> str:
466
+ path = get_snl_path_from_id_list(self.pathIDs)
467
+ return f"Term({path}, {get_snl_term_for_ids(self.pathIDs, self.termIDs).getName()})"
468
+
469
+ def __make_unique(self):
470
+ path = get_snl_path_from_id_list(self.pathIDs)
471
+ if path.size() > 1:
472
+ path = path.getHeadPath()
473
+ naja.SNLUniquifier(path)
474
+
475
+ def is_bus(self) -> bool:
476
+ """
477
+ :return: True if the term is a bus.
478
+ :rtype: bool
479
+ """
480
+ return isinstance(
481
+ get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTerm
482
+ )
483
+
484
+ def is_bus_bit(self) -> bool:
485
+ """
486
+ :return: True if the term is a bit of a bus.
487
+ :rtype: bool
488
+ """
489
+ return isinstance(
490
+ get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTermBit
491
+ )
492
+
493
+ def is_scalar(self) -> bool:
494
+ """
495
+ :return: True if the term is a scalar.
496
+ :rtype: bool
497
+ """
498
+ return isinstance(
499
+ get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLScalarTerm
500
+ )
501
+
502
+ def is_bit(self) -> bool:
503
+ """
504
+ :return: True if the term is a bit.
505
+ :rtype: bool
506
+ """
507
+ return self.is_scalar() or self.is_bus_bit()
508
+
509
+ def get_bit_number(self):
510
+ """
511
+ :return: the bit index of the term if it is a bit.
512
+ :rtype: int or None
513
+ """
514
+ if isinstance(
515
+ get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTermBit
516
+ ):
517
+ return get_snl_term_for_ids(self.pathIDs, self.termIDs).getBit()
518
+ return None
519
+
520
+ def get_msb(self) -> int:
521
+ """
522
+ :return: the most significant bit of the term if it is a bus.
523
+ :rtype: int or None
524
+ """
525
+ if isinstance(get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTerm):
526
+ return get_snl_term_for_ids(self.pathIDs, self.termIDs).getMSB()
527
+ return None
528
+
529
+ def get_lsb(self) -> int:
530
+ """
531
+ :return: the least significant bit of the term if it is a bus.
532
+ :rtype: int or None
533
+ """
534
+ if isinstance(get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTerm):
535
+ return get_snl_term_for_ids(self.pathIDs, self.termIDs).getLSB()
536
+ return None
537
+
538
+ def get_width(self) -> int:
539
+ """
540
+ :return: the width of the term. 1 if scalar.
541
+ :rtype: int
542
+ """
543
+ return get_snl_term_for_ids(self.pathIDs, self.termIDs).getWidth()
544
+
545
+ def get_name(self) -> str:
546
+ """
547
+ :return: the name of the term.
548
+ :rtype: str
549
+ """
550
+ return get_snl_term_for_ids(self.pathIDs, self.termIDs).getName()
551
+
552
+ def get_direction(self) -> naja.SNLTerm.Direction:
553
+ """
554
+ :return: the direction of the term.
555
+ :rtype: naja.SNLTerm.Direction
556
+ """
557
+ snlterm = get_snl_term_for_ids(self.pathIDs, self.termIDs)
558
+ if snlterm.getDirection() == naja.SNLTerm.Direction.Input:
559
+ return Term.INPUT
560
+ elif snlterm.getDirection() == naja.SNLTerm.Direction.Output:
561
+ return Term.OUTPUT
562
+ elif snlterm.getDirection() == naja.SNLTerm.Direction.InOut:
563
+ return Term.INOUT
564
+
565
+ def __get_snl_bitnet(self, bit) -> Net:
566
+ # single bit
567
+ path = get_snl_path_from_id_list(self.pathIDs)
568
+ if path.size() > 0:
569
+ instTerm = path.getTailInstance().getInstTerm(bit)
570
+ return instTerm.getNet()
571
+ else:
572
+ return bit.getNet()
573
+
574
+ def __get_snl_lower_bitnet(self, bit) -> Net:
575
+ return bit.getNet()
576
+
577
+ def __get_snl_busnet(self, snl_nets) -> naja.SNLBusNet:
578
+ # iterate on all elements of the list and check if
579
+ # a full SNLBusNet can be reconstructed
580
+ snl_bus_net = None
581
+ for i in range(len(snl_nets)):
582
+ snl_net = snl_nets[i]
583
+ if not isinstance(snl_net, naja.SNLBusNetBit):
584
+ return None
585
+ bit_bus = snl_net.getBus()
586
+ if bit_bus.getWidth() != len(snl_nets):
587
+ return None
588
+ if snl_bus_net is None:
589
+ snl_bus_net = bit_bus
590
+ if snl_bus_net != bit_bus:
591
+ return None
592
+ if snl_bus_net.getBitAtPosition(i) != snl_net:
593
+ return None
594
+ return snl_bus_net
595
+
596
+ def __get_net(self, path, snl_term_net_accessor) -> Net:
597
+ if isinstance(get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTerm):
598
+ snl_nets = []
599
+ for bit in get_snl_term_for_ids(self.pathIDs, self.termIDs).getBits():
600
+ snl_net = snl_term_net_accessor(bit)
601
+ snl_nets.append(snl_net)
602
+ snl_bus_net = self.__get_snl_busnet(snl_nets)
603
+ if snl_bus_net is not None:
604
+ return Net(path, snl_bus_net)
605
+ else:
606
+ if all(element is not None for element in snl_nets):
607
+ return Net(path, net_concat=snl_nets)
608
+ else:
609
+ snl_net = snl_term_net_accessor(
610
+ get_snl_term_for_ids(self.pathIDs, self.termIDs)
611
+ )
612
+ if snl_net is not None:
613
+ return Net(path, snl_net)
614
+ return None
615
+
616
+ def get_lower_net(self) -> Net:
617
+ """
618
+ :return: the lower net of the term.
619
+ :rtype: Net
620
+ """
621
+ return self.__get_net(self.pathIDs, self.__get_snl_lower_bitnet)
622
+
623
+ def get_net(self) -> Net:
624
+ """
625
+ :return: the net of the term.
626
+ :rtype: Net
627
+ """
628
+ head_path = self.pathIDs.copy()
629
+ if len(head_path) == 0:
630
+ return None
631
+ # path is one level up
632
+ head_path.pop()
633
+ return self.__get_net(head_path, self.__get_snl_bitnet)
634
+
635
+ def get_instance(self):
636
+ """
637
+ :return: the instance of this Term.
638
+ :rtype: Instance
639
+ """
640
+ return Instance(self.pathIDs)
641
+
642
+ def get_flat_fanout(self):
643
+ return self.get_equipotential().get_leaf_readers()
644
+
645
+ def get_equipotential(self) -> Equipotential:
646
+ return Equipotential(self)
647
+
648
+ def is_input(self) -> bool:
649
+ """
650
+ :return: True if the term is an input.
651
+ :rtype: bool
652
+ """
653
+ snlterm = get_snl_term_for_ids(self.pathIDs, self.termIDs)
654
+ return snlterm.getDirection() == naja.SNLTerm.Direction.Input
655
+
656
+ def is_output(self) -> bool:
657
+ """
658
+ :return: True if the term is an output.
659
+ :rtype: bool
660
+ """
661
+ snlterm = get_snl_term_for_ids(self.pathIDs, self.termIDs)
662
+ return snlterm.getDirection() == naja.SNLTerm.Direction.Output
663
+
664
+ def get_bits(self):
665
+ """
666
+ :return: an iterator over the bits of the term.
667
+ If the term is scalar, it will return an iterator over itself.
668
+ :rtype: Iterator[Term]
669
+ """
670
+ if isinstance(get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTerm):
671
+ for bit in get_snl_term_for_ids(self.pathIDs, self.termIDs).getBits():
672
+ yield Term(self.pathIDs, bit)
673
+ else:
674
+ yield self
675
+
676
+ def get_bit(self, index: int):
677
+ """
678
+ :param int index: the index of the bit to get.
679
+ :return: the Term bit at the given index or None if it does not exist.
680
+ :rtype: Term or None
681
+ """
682
+ if isinstance(get_snl_term_for_ids(self.pathIDs, self.termIDs), naja.SNLBusTerm):
683
+ return Term(
684
+ self.pathIDs,
685
+ get_snl_term_for_ids(self.pathIDs, self.termIDs).getBusTermBit(index),
686
+ )
687
+ return None
688
+
689
+ def disconnect(self):
690
+ """Disconnect this term from its net."""
691
+ path = get_snl_path_from_id_list(self.pathIDs)
692
+ self.__make_unique()
693
+ inst = path.getTailInstance()
694
+ for bit in get_snl_term_for_ids(self.pathIDs, self.termIDs).getBits():
695
+ iterm = inst.getInstTerm(bit)
696
+ iterm.setNet(None)
697
+
698
+ def connect(self, net: Net):
699
+ """Connect this term to the given Net.
700
+
701
+ :param Net net: the Net to connect to.
702
+ """
703
+ if self.get_width() != net.get_width():
704
+ raise ValueError("Width mismatch")
705
+ if self.get_instance().is_top():
706
+ for bterm, bnet in zip(
707
+ get_snl_term_for_ids(self.pathIDs, self.termIDs).getBits(),
708
+ net.net.getBits(),
709
+ ):
710
+ logging.debug(f"Connecting {bterm} to {bnet}")
711
+ bterm.setNet(bnet)
712
+ else:
713
+ self.__make_unique()
714
+ path = get_snl_path_from_id_list(self.pathIDs)
715
+ inst = path.getTailInstance()
716
+ for bterm, bnet in zip(
717
+ get_snl_term_for_ids(self.pathIDs, self.termIDs).getBits(),
718
+ net.net.getBits(),
719
+ ):
720
+ iterm = inst.getInstTerm(bterm)
721
+ iterm.setNet(bnet)
722
+
723
+
724
+ def get_instance_by_path(names: list):
725
+ assert len(names) > 0
726
+ path = naja.SNLPath()
727
+ instance = None
728
+ top = naja.NLUniverse.get().getTopDesign()
729
+ design = top
730
+ for name in names:
731
+ path = naja.SNLPath(path, design.getInstance(name))
732
+ instance = design.getInstance(name)
733
+ assert instance is not None
734
+ design = instance.getModel()
735
+ return Instance(path)
736
+
737
+
738
+ # def refresh_path(path: naja.SNLPath):
739
+ # pathlist = path.getPathIDs()
740
+ # assert len(pathlist) > 0
741
+ # path = naja.SNLPath()
742
+ # instance = None
743
+ # top = naja.NLUniverse.get().getTopDesign()
744
+ # design = top
745
+ # for id in pathlist:
746
+ # path = naja.SNLPath(path, design.getInstanceByID(id))
747
+ # instance = design.getInstanceByID(id)
748
+ # assert instance is not None
749
+ # design = instance.getModel()
750
+ # return path
751
+
752
+
753
+ class Attribute:
754
+ def __init__(self, snlAttribute):
755
+ self.snlAttribute = snlAttribute
756
+
757
+ def __str__(self):
758
+ return str(self.snlAttribute)
759
+
760
+ def get_name(self):
761
+ """
762
+ :return: the name of the attribute.
763
+ :rtype: str
764
+ """
765
+ return self.snlAttribute.getName()
766
+
767
+ def has_value(self):
768
+ """
769
+ :return: True if the attribute has a value.
770
+ :rtype: bool
771
+ """
772
+ return self.snlAttribute.hasValue()
773
+
774
+ def get_value(self):
775
+ """
776
+ :return: the value of the attribute.
777
+ :rtype: str
778
+ """
779
+ return self.snlAttribute.getValue()
780
+
781
+
782
+ class Instance:
783
+ """Class that represents the instance and wraps some
784
+ of the snl occurrence API.
785
+ """
786
+
787
+ def __init__(self, path=naja.SNLPath()):
788
+ self.inst = None
789
+ self.revisionCount = 0
790
+ self.SNLID = [0, 0, 0, 0, 0, 0]
791
+ if isinstance(path, naja.SNLPath):
792
+ if path.size() > 0:
793
+ self.pathIDs = path.getPathIDs()
794
+ self.revisionCount = path.getTailInstance().getModel().getRevisionCount()
795
+ self.inst = path.getTailInstance()
796
+ else:
797
+ self.pathIDs = []
798
+ elif isinstance(path, list):
799
+ self.pathIDs = path.copy()
800
+ if len(path) > 0:
801
+ self.inst = get_snl_instance_from_id_list(path)
802
+ self.revisionCount = self.inst.getModel().getRevisionCount()
803
+ if self.inst is not None:
804
+ self.SNLID = self.inst.getModel().getNLID()
805
+
806
+ def __eq__(self, other) -> bool:
807
+ return self.pathIDs == other.pathIDs
808
+
809
+ def __str__(self):
810
+ if self.is_top():
811
+ top = self.__get_snl_model()
812
+ if top is not None:
813
+ return top.getName()
814
+ else:
815
+ return ""
816
+ else:
817
+ path = get_snl_path_from_id_list(self.pathIDs)
818
+ return str(path)
819
+
820
+ def __repr__(self) -> str:
821
+ path = get_snl_path_from_id_list(self.pathIDs)
822
+ return f"Instance({path})"
823
+
824
+ def __hash__(self):
825
+ return consistent_hash(self.pathIDs)
826
+
827
+ def get_leaf_children(self):
828
+ """Iterate over the leaf children of this Instance.
829
+ Equivalent to the underlying leaves of the instanciation tree.
830
+
831
+ :return: an iterator over the leaf children Instance of this Instance.
832
+ :rtype: Iterator[Instance]
833
+ """
834
+ initial_path = get_snl_path_from_id_list(self.pathIDs)
835
+ for inst in self.__get_snl_model().getInstances():
836
+ if inst.getModel().isLeaf():
837
+ yield Instance(naja.SNLPath(initial_path, inst))
838
+ path = naja.SNLPath(initial_path, inst)
839
+ stack = [[inst, path]]
840
+ while stack:
841
+ current = stack.pop()
842
+ current_inst = current[0]
843
+ current_path = current[1]
844
+ for inst_child in current_inst.getModel().getInstances():
845
+ path_child = naja.SNLPath(current_path, inst_child)
846
+ if inst_child.getModel().isLeaf():
847
+ yield Instance(path_child)
848
+ stack.append([inst_child, path_child])
849
+
850
+ def is_top(self) -> bool:
851
+ """
852
+ :return: True if this is the top design.
853
+ :rtype: bool
854
+ """
855
+ return len(self.pathIDs) == 0
856
+
857
+ def is_assign(self) -> bool:
858
+ """(assign a=b) will create an instance of assign connecting
859
+ the wire a to the output of the assign and b to the input.
860
+
861
+ :return: True if this is an assign. Assigns are represented with
862
+ anonymous Assign instances.
863
+ :rtype: bool
864
+ """
865
+ return self.__get_snl_model().isAssign()
866
+
867
+ def is_blackbox(self) -> bool:
868
+ """
869
+ :return: True if this is a blackbox.
870
+ :rtype: bool
871
+ """
872
+ return self.__get_snl_model().isBlackBox()
873
+
874
+ def is_leaf(self) -> bool:
875
+ """
876
+ :return: True if this is a leaf.
877
+ :rtype: bool
878
+ """
879
+ return self.__get_snl_model().isLeaf()
880
+
881
+ def is_const0(self) -> bool:
882
+ """
883
+ :return: True if this is a constant 0 generator.
884
+ :rtype: bool
885
+ """
886
+ return self.__get_snl_model().isConst0()
887
+
888
+ def is_const1(self) -> bool:
889
+ """
890
+ :return: True if this is a constant 1 generator.
891
+ :rtype: bool
892
+ """
893
+ return self.__get_snl_model().isConst1()
894
+
895
+ def is_const(self) -> bool:
896
+ """
897
+ :return: True if this is a constant generator.
898
+ :rtype: bool
899
+ """
900
+ return self.__get_snl_model().isConst()
901
+
902
+ def is_buf(self) -> bool:
903
+ """
904
+ :return: True if this is a buffer.
905
+ :rtype: bool
906
+ """
907
+ return self.__get_snl_model().isBuf()
908
+
909
+ def is_inv(self) -> bool:
910
+ """
911
+ :return: True if this is an inverter.
912
+ :rtype: bool
913
+ """
914
+ return self.__get_snl_model().isInv()
915
+
916
+ def is_basic_primitive(instance):
917
+ design = instance.__get_snl_model()
918
+ return (
919
+ design.isConst0() or design.isConst1() or design.isBuf() or design.isInv()
920
+ )
921
+
922
+ def __get_snl_model(self):
923
+ if self.is_top():
924
+ return naja.NLUniverse.get().getTopDesign()
925
+ if (
926
+ self.inst.getModel().getRevisionCount() != self.revisionCount or
927
+ self.inst.getModel().getNLID() != self.SNLID
928
+ ):
929
+ self.inst = get_snl_instance_from_id_list(self.pathIDs)
930
+ self.revisionCount = self.inst.getModel().getRevisionCount()
931
+ self.SNLID = self.inst.getModel().getNLID()
932
+
933
+ return self.inst.getModel()
934
+
935
+ def __get_leaf_snl_object(self):
936
+ if self.is_top():
937
+ return naja.NLUniverse.get().getTopDesign()
938
+ return get_snl_instance_from_id_list(self.pathIDs)
939
+
940
+ def __find_snl_model(self, name: str) -> naja.SNLDesign:
941
+ u = naja.NLUniverse.get()
942
+ for db in u.getUserDBs():
943
+ for lib in db.getLibraries():
944
+ found_model = lib.getSNLDesign(name)
945
+ if found_model is not None:
946
+ return found_model
947
+ return None
948
+
949
+ def dump_full_dot(self, path: str):
950
+ """Dump the full dot file of this instance."""
951
+ self.__get_snl_model().dumpFullDotFile(path)
952
+
953
+ def dump_context_dot(self, path: str):
954
+ self.__get_snl_model().dumpContextDotFile(path)
955
+
956
+ def get_child_instance(self, name: str):
957
+ """
958
+ :param str name: the name of the child Instance to get.
959
+ :return: the child Instance with the given name or None if it does not exist.
960
+ :rtype: Instance or None
961
+ """
962
+ childInst = self.__get_snl_model().getInstance(name)
963
+ if childInst is None:
964
+ return None
965
+ path = self.pathIDs.copy()
966
+ path.append(childInst.getID())
967
+ return Instance(path)
968
+
969
+ def get_child_instances(self):
970
+ """Iterate over the child instances of this instance.
971
+ Equivalent to go down one level in hierarchy.
972
+
973
+ :return: an iterator over the child instances of this instance.
974
+ :rtype: Iterator[Instance]
975
+ """
976
+ path = get_snl_path_from_id_list(self.pathIDs)
977
+ for inst in self.__get_snl_model().getInstances():
978
+ path_child = naja.SNLPath(path, inst)
979
+ yield Instance(path_child)
980
+ # path.pop()
981
+
982
+ def count_child_instances(self) -> int:
983
+ """
984
+ :return: the number of child instances of this instance.
985
+ :rtype: int
986
+ """
987
+ return sum(1 for _ in self.__get_snl_model().getInstances())
988
+
989
+ # def get_flat_primitive_instances(self):
990
+ # FIXME: concat first local path with the path of the instance
991
+ # model = self.__get_snl_model()
992
+ # for inst in model.getInstances():
993
+ # path = naja.SNLPath(inst)
994
+ # stack = [[inst, path]]
995
+ # while stack:
996
+ # current = stack.pop()
997
+ # current_inst = current[0]
998
+ # current_path = current[1]
999
+ # for inst_child in current_inst.getModel().getInstances():
1000
+ # path_child = naja.SNLPath(current_path, inst_child)
1001
+ # if inst_child.getModel().isPrimitive():
1002
+ # yield Instance(path_child)
1003
+ # stack.append([inst_child, path_child])
1004
+
1005
+ def get_nets(self):
1006
+ """Iterate over all scalar nets and bus nets.
1007
+
1008
+ :return: an iterator over the nets of this Instance.
1009
+ :rtype: Iterator[Net]
1010
+ """
1011
+ for net in self.__get_snl_model().getNets():
1012
+ yield Net(self.pathIDs, net)
1013
+
1014
+ def count_nets(self) -> int:
1015
+ """Count the number of scalar nets and bus nets of this Instance.
1016
+
1017
+ :return: the number of nets of this Instance.
1018
+ :rtype: int
1019
+ """
1020
+ return sum(1 for _ in self.get_nets())
1021
+
1022
+ def get_flat_nets(self):
1023
+ """Iterate over all scalar nets and bus net bits.
1024
+
1025
+ :return: an iterator over the flat nets of this Instance.
1026
+ :rtype: Iterator[Net]
1027
+ """
1028
+ for net in self.__get_snl_model().getNets():
1029
+ if isinstance(net, naja.SNLBusNet):
1030
+ for bit in net.getBits():
1031
+ yield Net(self.pathIDs, bit)
1032
+ else:
1033
+ yield Net(self.pathIDs, net)
1034
+
1035
+ def count_flat_nets(self) -> int:
1036
+ """Count the number of scalar nets and bus net bits of this Instance.
1037
+
1038
+ :return: the number of flat nets of this Instance.
1039
+ :rtype: int
1040
+ """
1041
+ return sum(1 for _ in self.get_flat_nets())
1042
+
1043
+ def get_net(self, name: str) -> Net:
1044
+ """
1045
+ :param str name: the name of the Net to get.
1046
+ :return: the Net with the given name or None if it does not exist.
1047
+ :rtype: Net or None
1048
+ """
1049
+ net = self.__get_snl_model().getNet(name)
1050
+ if net is not None:
1051
+ return Net(self.pathIDs, net)
1052
+ return None
1053
+
1054
+ def is_primitive(self) -> bool:
1055
+ """
1056
+ :return: True if this is a primitive.
1057
+ :rtype: bool
1058
+ """
1059
+ return self.__get_snl_model().isPrimitive()
1060
+
1061
+ def get_terms(self):
1062
+ """Iterate over all scalar terms and bus terms of this Instance.
1063
+
1064
+ :return: the terms of this Instance.
1065
+ :rtype: Iterator[Term]
1066
+ """
1067
+ for term in self.__get_snl_model().getTerms():
1068
+ yield Term(self.pathIDs, term)
1069
+
1070
+ def count_terms(self) -> int:
1071
+ """Count the number of scalar terms and bus terms of this Instance.
1072
+
1073
+ :return: the number of terms of this Instance.
1074
+ :rtype: int
1075
+ """
1076
+ return sum(1 for _ in self.get_terms())
1077
+
1078
+ def get_flat_terms(self):
1079
+ """Iterate over all scalar terms and bus term bits.
1080
+
1081
+ :return: the flat terms of this Instance.
1082
+ :rtype: Iterator[Term]
1083
+ """
1084
+ for term in self.__get_snl_model().getBitTerms():
1085
+ yield Term(self.pathIDs, term)
1086
+
1087
+ def count_flat_terms(self) -> int:
1088
+ """Count the number of scalar terms and bus term bits of this Instance.
1089
+
1090
+ :return: the number of flat terms of this Instance.
1091
+ :rtype: int
1092
+ """
1093
+ return sum(1 for _ in self.get_flat_terms())
1094
+
1095
+ def get_term(self, name: str) -> Term:
1096
+ """
1097
+ :param str name: the name of the Term to get.
1098
+ :return: the Term with the given name.
1099
+ :rtype: Term or None
1100
+ """
1101
+ term = self.__get_snl_model().getTerm(name)
1102
+ if term is not None:
1103
+ return Term(self.pathIDs, self.__get_snl_model().getTerm(name))
1104
+ return None
1105
+
1106
+ def get_input_terms(self):
1107
+ """Iterate over all scalar input terms and bus input terms
1108
+ of this Instance.
1109
+
1110
+ :return: the input terms of this Instance.
1111
+ :rtype: Iterator[Term]
1112
+ """
1113
+ for term in self.__get_snl_model().getTerms():
1114
+ if term.getDirection() != naja.SNLTerm.Direction.Output:
1115
+ yield Term(self.pathIDs, term)
1116
+
1117
+ def count_input_terms(self) -> int:
1118
+ """Count the number of scalar input terms and bus input terms
1119
+ of this Instance.
1120
+
1121
+ :return: the number of input terms of this Instance.
1122
+ :rtype: int
1123
+ """
1124
+ return sum(1 for _ in self.get_input_terms())
1125
+
1126
+ def get_flat_input_terms(self):
1127
+ """Iterate over all scalar input terms and bus input term bits
1128
+ of this Instance.
1129
+
1130
+ :return: the flat input terms of this Instance.
1131
+ :rtype: Iterator[Term]
1132
+ """
1133
+ for term in self.__get_snl_model().getTerms():
1134
+ if term.getDirection() != naja.SNLTerm.Direction.Output:
1135
+ if isinstance(term, naja.SNLBusTerm):
1136
+ for bit in term.getBits():
1137
+ yield Term(self.pathIDs, bit)
1138
+ else:
1139
+ yield Term(self.pathIDs, term)
1140
+
1141
+ def count_flat_input_terms(self) -> int:
1142
+ """Count the number of scalar input terms and bus input term bits
1143
+ of this Instance.
1144
+
1145
+ :return: the number of flat input terms of this Instance.
1146
+ :rtype: int
1147
+ """
1148
+ return sum(1 for _ in self.get_flat_input_terms())
1149
+
1150
+ def get_output_terms(self):
1151
+ """Iterate over all scalar output terms and bus output terms
1152
+ of this Instance.
1153
+
1154
+ :return: the output terms of this Instance.
1155
+ :rtype: Iterator[Term]
1156
+ """
1157
+ for term in self.__get_snl_model().getTerms():
1158
+ if term.getDirection() != naja.SNLTerm.Direction.Input:
1159
+ yield Term(self.pathIDs, term)
1160
+
1161
+ def count_output_terms(self) -> int:
1162
+ """Count the number of scalar output terms and bus output terms
1163
+ of this Instance.
1164
+
1165
+ :return: the number of output terms of this Instance.
1166
+ :rtype: int
1167
+ """
1168
+ return sum(1 for _ in self.get_output_terms())
1169
+
1170
+ def get_flat_output_terms(self):
1171
+ """Iterate over all scalar output terms and bus output term bits
1172
+ of this Instance.
1173
+
1174
+ :return: the flat output terms of this Instance.
1175
+ :rtype: Iterator[Term]
1176
+ """
1177
+ for term in self.__get_snl_model().getTerms():
1178
+ if term.getDirection() != naja.SNLTerm.Direction.Input:
1179
+ if isinstance(term, naja.SNLBusTerm):
1180
+ for bit in term.getBits():
1181
+ yield Term(self.pathIDs, bit)
1182
+ else:
1183
+ yield Term(self.pathIDs, term)
1184
+
1185
+ def count_flat_output_terms(self) -> int:
1186
+ """Count the number of scalar output terms and bus output term bits
1187
+ of this Instance.
1188
+
1189
+ :return: the number of flat output terms of this Instance.
1190
+ :rtype: int
1191
+ """
1192
+ return sum(1 for _ in self.get_flat_output_terms())
1193
+
1194
+ def get_attributes(self):
1195
+ """Iterate over the attributes of this Instance.
1196
+
1197
+ :return: the attributes of this Instance.
1198
+ :rtype: Iterator[Attribute]
1199
+ """
1200
+ leaf_object = self.__get_leaf_snl_object()
1201
+ for attribute in leaf_object.getAttributes():
1202
+ yield Attribute(attribute)
1203
+
1204
+ def delete_instance(self, name: str):
1205
+ """Delete the child instance with the given name."""
1206
+ if name == "":
1207
+ raise ValueError(
1208
+ "Cannot delete instance with empty name. Try delete_instance_by_id instead."
1209
+ )
1210
+ init_path = get_snl_path_from_id_list(self.pathIDs)
1211
+ path = naja.SNLPath(init_path, self.__get_snl_model().getInstance(name))
1212
+ naja.SNLUniquifier(path)
1213
+ if init_path.size() > 0:
1214
+ # Delete the last instance in uniq_path
1215
+ self.__get_snl_model().getInstance(name).destroy()
1216
+
1217
+ def delete_instance_by_id(self, id: str):
1218
+ """Delete the child instance with the given ID.
1219
+
1220
+ :param str id: the ID of the Instance to delete.
1221
+ """
1222
+ init_path = get_snl_path_from_id_list(self.pathIDs)
1223
+ path = naja.SNLPath(init_path, self.__get_snl_model().getInstanceByID(id))
1224
+ naja.SNLUniquifier(path)
1225
+ # Delete the last instance in uniq_path
1226
+ self.__get_snl_model().getInstanceByID(id).destroy()
1227
+
1228
+ def get_design(self):
1229
+ """
1230
+ :return: the Instance containing this instance.
1231
+ :rtype: Instance
1232
+ """
1233
+ path = self.pathIDs.copy()
1234
+ if len(self.pathIDs) == 1:
1235
+ return get_top()
1236
+ path.pop()
1237
+ return Instance(path)
1238
+
1239
+ def delete(self):
1240
+ """Delete this instance."""
1241
+ path = get_snl_path_from_id_list(self.pathIDs)
1242
+ naja.SNLUniquifier(path)
1243
+ self.get_design().delete_instance_by_id(path.getTailInstance().getID())
1244
+
1245
+ def get_name(self) -> str:
1246
+ """
1247
+ :return: the name of the instance or name of the top is this is the top.
1248
+ :rtype: str
1249
+ """
1250
+ path = get_snl_path_from_id_list(self.pathIDs)
1251
+ if self.is_top():
1252
+ return self.get_model_name()
1253
+ else:
1254
+ return path.getTailInstance().getName()
1255
+
1256
+ def get_model_name(self) -> str:
1257
+ """
1258
+ :return: the name of the model of the instance
1259
+ or name of the top is this is the top.
1260
+ :rtype: str
1261
+ """
1262
+ return self.__get_snl_model().getName()
1263
+
1264
+ def get_model_id(self) -> tuple[int, int, int]:
1265
+ """
1266
+ :return: the ID of the model of this Instance
1267
+ or ID of the top if this is the top.
1268
+ """
1269
+ model = self.__get_snl_model()
1270
+ return model.getDB().getID(), model.getLibrary().getID(), model.getID()
1271
+
1272
+ def create_child_instance(self, model: str, name: str):
1273
+ """Create a child instance with the given model and name.
1274
+
1275
+ :param str model: the name of the model of the instance to create.
1276
+ :param str name: the name of the instance to create.
1277
+ :return: the created Instance.
1278
+ :rtype: Instance
1279
+ """
1280
+ path = get_snl_path_from_id_list(self.pathIDs)
1281
+ if path.size() > 0:
1282
+ naja.SNLUniquifier(path)
1283
+ path = get_snl_path_from_id_list(self.pathIDs)
1284
+ design = self.__get_snl_model()
1285
+ new_instance_model = self.__find_snl_model(model)
1286
+ if new_instance_model is None:
1287
+ raise ValueError(
1288
+ f"Cannot create instance {name} in {self}: model {model} cannot be found"
1289
+ )
1290
+ newSNLInstance = naja.SNLInstance.create(design, new_instance_model, name)
1291
+ path = naja.SNLPath(path, newSNLInstance)
1292
+ return Instance(path)
1293
+
1294
+ def create_term(self, name: str, direction: naja.SNLTerm.Direction) -> Term:
1295
+ """Create a Term in this Instance with the given name and direction.
1296
+
1297
+ :param str name: the name of the Term to create.
1298
+ :param naja.SNLTerm.Direction direction: the direction of the Term to create.
1299
+ :return: the created Term.
1300
+ """
1301
+ path = get_snl_path_from_id_list(self.pathIDs)
1302
+ if path.size() > 0:
1303
+ naja.SNLUniquifier(path)
1304
+ path = get_snl_path_from_id_list(self.pathIDs)
1305
+ design = self.__get_snl_model()
1306
+ newSNLTerm = naja.SNLScalarTerm.create(design, direction, name)
1307
+ return Term(path.getPathIDs(), newSNLTerm)
1308
+
1309
+ def create_output_term(self, name: str) -> Term:
1310
+ """Create an output Term in this Instance with the given name.
1311
+
1312
+ :param str name: the name of the Term to create.
1313
+ :return: the created Term.
1314
+ :rtype: Term
1315
+ """
1316
+ return self.create_term(name, naja.SNLTerm.Direction.Output)
1317
+
1318
+ def create_input_term(self, name: str) -> Term:
1319
+ """Create an input Term in this Instance with the given name.
1320
+
1321
+ :param str name: the name of the Term to create.
1322
+ :return: the created Term.
1323
+ :rtype: Term
1324
+ """
1325
+ return self.create_term(name, naja.SNLTerm.Direction.Input)
1326
+
1327
+ def create_inout_term(self, name: str) -> Term:
1328
+ """Create an inout Term in this Instance with the given name.
1329
+
1330
+ :param str name: the name of the Term to create.
1331
+ :return: the created Term.
1332
+ :rtype: Term
1333
+ """
1334
+ return self.create_term(name, naja.SNLTerm.Direction.InOut)
1335
+
1336
+ def create_bus_term(self, name: str, msb: int, lsb: int, direction) -> Term:
1337
+ """Create a bus Term in this Instance with the given name, msb, lsb and direction.
1338
+
1339
+ :param str name: the name of the Term to create.
1340
+ :param int msb: the most significant bit of the Term to create.
1341
+ :param int lsb: the least significant bit of the Term to create.
1342
+ :param naja.SNLTerm.Direction direction: the direction of the Term to create.
1343
+ :return: the created Term.
1344
+ """
1345
+ path = get_snl_path_from_id_list(self.pathIDs)
1346
+ if path.size() > 0:
1347
+ naja.SNLUniquifier(path)
1348
+ design = self.__get_snl_model()
1349
+ newSNLTerm = naja.SNLBusTerm.create(design, direction, msb, lsb, name)
1350
+ return Term(self.pathIDs, newSNLTerm)
1351
+
1352
+ def create_inout_bus_term(self, name: str, msb: int, lsb: int) -> Term:
1353
+ """Create an inout bus Term in this Instance with the given name, msb and lsb.
1354
+
1355
+ :param str name: the name of the Term to create.
1356
+ :param int msb: the most significant bit of the Term to create.
1357
+ :param int lsb: the least significant bit of the Term to create.
1358
+ :return: the created Term.
1359
+ :rtype: Term
1360
+ """
1361
+ return self.create_bus_term(name, msb, lsb, naja.SNLTerm.Direction.InOut)
1362
+
1363
+ def create_output_bus_term(self, name: str, msb: int, lsb: int) -> Term:
1364
+ """Create an output bus Term in this Instance with the given name, msb and lsb.
1365
+
1366
+ :param str name: the name of the Term to create.
1367
+ :param int msb: the most significant bit of the Term to create.
1368
+ :param int lsb: the least significant bit of the Term to create.
1369
+ :return: the created Term.
1370
+ :rtype: Term
1371
+ """
1372
+ return self.create_bus_term(name, msb, lsb, naja.SNLTerm.Direction.Output)
1373
+
1374
+ def create_input_bus_term(self, name: str, msb: int, lsb: int) -> Term:
1375
+ """Create an input bus Term in this Instance with the given name, msb and lsb.
1376
+
1377
+ :param str name: the name of the Term to create.
1378
+ :param int msb: the most significant bit of the Term to create.
1379
+ :param int lsb: the least significant bit of the Term to create.
1380
+ :return: the created Term.
1381
+ :rtype: Term
1382
+ """
1383
+ return self.create_bus_term(name, msb, lsb, naja.SNLTerm.Direction.Input)
1384
+
1385
+ def create_net(self, name: str) -> Net:
1386
+ """Create a scalar Net in this Instance with the given name.
1387
+
1388
+ :param str name: the name of the Net to create.
1389
+ :return: the created Net.
1390
+ :rtype: Net
1391
+ """
1392
+ path = get_snl_path_from_id_list(self.pathIDs)
1393
+ if path.size() > 0:
1394
+ naja.SNLUniquifier(path)
1395
+ path = get_snl_path_from_id_list(self.pathIDs)
1396
+ model = self.__get_snl_model()
1397
+ newSNLNet = naja.SNLScalarNet.create(model, name)
1398
+ return Net(path, newSNLNet)
1399
+
1400
+ def create_bus_net(self, name: str, msb: int, lsb: int) -> Net:
1401
+ """Create a bus Net in this Instance with the given name, msb and lsb.
1402
+
1403
+ :param str name: the name of the Net to create.
1404
+ :param int msb: the most significant bit of the Net to create.
1405
+ :param int lsb: the least significant bit of the Net to create.
1406
+ :return: the created Net.
1407
+ :rtype: Net
1408
+ """
1409
+ path = get_snl_path_from_id_list(self.pathIDs)
1410
+ if path.size() > 0:
1411
+ naja.SNLUniquifier(path)
1412
+ path = get_snl_path_from_id_list(self.pathIDs)
1413
+ model = self.__get_snl_model()
1414
+ newSNLNet = naja.SNLBusNet.create(model, msb, lsb, name)
1415
+ return Net(path, newSNLNet)
1416
+
1417
+ def dump_verilog(self, path: str, name: str):
1418
+ """Dump the verilog of this instance.
1419
+
1420
+ :param str path: the path where to dump the verilog.
1421
+ :param str name: the name of the verilog file.
1422
+ """
1423
+ self.__get_snl_model().dumpVerilog(path, name)
1424
+
1425
+ def get_truth_table(self):
1426
+ """
1427
+ :return: the truth table of the instance.
1428
+ :rtype: list[str]
1429
+ """
1430
+ return self.__get_snl_model().getTruthTable()
1431
+
1432
+
1433
+ def __get_top_db() -> naja.NLDB:
1434
+ if naja.NLUniverse.get() is None:
1435
+ naja.NLUniverse.create()
1436
+ if naja.NLUniverse.get().getTopDB() is None:
1437
+ db = naja.NLDB.create(naja.NLUniverse.get())
1438
+ naja.NLUniverse.get().setTopDB(db)
1439
+ return naja.NLUniverse.get().getTopDB()
1440
+
1441
+
1442
+ def get_top():
1443
+ """
1444
+ :return: the top Instance.
1445
+ :rtype: Instance
1446
+ """
1447
+ return Instance(naja.SNLPath())
1448
+
1449
+
1450
+ def create_top(name: str) -> Instance:
1451
+ """Create a top instance with the given name.
1452
+
1453
+ :param str name: the name of the top instance to create.
1454
+ :return: the created top Instance.
1455
+ :rtype: Instance
1456
+ """
1457
+ # init
1458
+ db = __get_top_db()
1459
+ # create top design
1460
+ lib = naja.NLLibrary.create(db)
1461
+ top = naja.SNLDesign.create(lib, name)
1462
+ naja.NLUniverse.get().setTopDesign(top)
1463
+ return Instance()
1464
+
1465
+
1466
+ class VerilogConfig:
1467
+ def __init__(self, keep_assigns=True):
1468
+ self.keep_assigns = keep_assigns
1469
+
1470
+
1471
+ def load_verilog(files: Union[str, List[str]], config: VerilogConfig = None) -> Instance:
1472
+ """Load verilog files into the top design.
1473
+ :param files: a list of verilog files to load or a single file.
1474
+ :param config: the configuration to use when loading the files.
1475
+ :return: the top Instance.
1476
+ """
1477
+ if isinstance(files, str):
1478
+ files = [files]
1479
+ if not files or len(files) == 0:
1480
+ raise Exception("No verilog files provided")
1481
+ if config is None:
1482
+ config = VerilogConfig() # Use default settings
1483
+ start_time = time.time()
1484
+ logging.info(f"Loading verilog: {', '.join(files)}")
1485
+ __get_top_db().loadVerilog(files, keep_assigns=config.keep_assigns)
1486
+ execution_time = time.time() - start_time
1487
+ logging.info(f"Loading done in {execution_time:.2f} seconds")
1488
+ return get_top()
1489
+
1490
+
1491
+ def load_liberty(files: Union[str, List[str]]):
1492
+ """Load liberty files.
1493
+ :param files: a list of liberty files to load or a single file.
1494
+ """
1495
+ if isinstance(files, str):
1496
+ files = [files]
1497
+ if not files or len(files) == 0:
1498
+ raise Exception("No liberty files provided")
1499
+ logging.info(f"Loading liberty files: {', '.join(files)}")
1500
+ __get_top_db().loadLibertyPrimitives(files)
1501
+
1502
+
1503
+ def load_primitives(name: str):
1504
+ """Loads a primitive library embedded in najaeda.
1505
+
1506
+ Currently supported libraries are:
1507
+
1508
+ - xilinx
1509
+ """
1510
+ if name == "xilinx":
1511
+ from najaeda.primitives import xilinx
1512
+
1513
+ xilinx.load(__get_top_db())
1514
+ else:
1515
+ raise ValueError(f"Unknown primitives library: {name}")
1516
+
1517
+
1518
+ def load_primitives_from_file(file: str):
1519
+ """Loads a primitives library from a file.
1520
+
1521
+ :param str file: the path to the primitives library file.
1522
+ The file must define a function `load(db)`.
1523
+ """
1524
+ logging.info(f"Loading primitives from file: {file}")
1525
+ if not os.path.isfile(file):
1526
+ raise FileNotFoundError(f"Cannot load primitives from non existing file: {file}")
1527
+ import importlib.util
1528
+ spec = importlib.util.spec_from_file_location("user_module", file)
1529
+ module = importlib.util.module_from_spec(spec)
1530
+ sys.modules["user_module"] = module
1531
+ spec.loader.exec_module(module)
1532
+
1533
+ if not hasattr(module, "load"):
1534
+ raise RuntimeError(f"The file {file} must define a function named `load(db)`")
1535
+
1536
+ db = __get_top_db()
1537
+ module.load(db)
1538
+
1539
+
1540
+ def get_primitives_library() -> naja.NLLibrary:
1541
+ lib = __get_top_db().getLibrary("PRIMS")
1542
+ if lib is None:
1543
+ lib = naja.NLLibrary.createPrimitives(__get_top_db(), "PRIMS")
1544
+ return lib
1545
+
1546
+
1547
+ def get_model_name(id: tuple[int, int, int]) -> str:
1548
+ """
1549
+ :param tuple[int, int, int] id: the id of the model.
1550
+ :return: the name of the model given its id or None if it does not exist.
1551
+ :rtype: str or None
1552
+ """
1553
+ u = naja.NLUniverse.get()
1554
+ if u:
1555
+ db = u.getDB(id[0])
1556
+ if db:
1557
+ lib = db.getLibrary(id[1])
1558
+ if lib:
1559
+ model = lib.getSNLDesign(id[2])
1560
+ if model:
1561
+ return model.getName()
1562
+ return None
1563
+
1564
+
1565
+ def apply_dle():
1566
+ """Apply the DLE (Dead Logic Elimination) to the top design."""
1567
+ top = naja.NLUniverse.get().getTopDesign()
1568
+ if top is not None:
1569
+ naja.NLUniverse.get().applyDLE()
1570
+
1571
+
1572
+ def apply_constant_propagation():
1573
+ """Apply constant propagation to the top design."""
1574
+ top = naja.NLUniverse.get().getTopDesign()
1575
+ if top is not None:
1576
+ naja.NLUniverse.get().applyConstantPropagation()