1
- from typing import Iterable
1
+ from typing import Iterable , Any
2
2
import enum
3
3
4
4
from ._ast import SignalDict
7
7
8
8
__all__ = [
9
9
# Netlist core
10
+ "CombinationalCycle" ,
10
11
"Net" , "Value" , "IONet" , "IOValue" ,
11
- "FormatValue" , "Format" ,
12
+ "FormatValue" , "Format" , "SignalField" ,
12
13
"Netlist" , "ModuleNetFlow" , "IODirection" , "Module" , "Cell" , "Top" ,
13
14
# Computation cells
14
15
"Operator" , "Part" ,
25
26
]
26
27
27
28
29
+ class CombinationalCycle (Exception ):
30
+ pass
31
+
32
+
28
33
class Net (int ):
29
34
__slots__ = ()
30
35
@@ -335,6 +340,7 @@ class Netlist:
335
340
modules : list of ``Module``
336
341
cells : list of ``Cell``
337
342
connections : dict of (negative) int to int
343
+ late_to_signal : dict of (late) Net to its Signal and bit number
338
344
io_ports : list of ``IOPort``
339
345
signals : dict of Signal to ``Value``
340
346
signal_fields: dict of Signal to dict of tuple[str | int] to SignalField
@@ -344,6 +350,7 @@ def __init__(self):
344
350
self .modules : list [Module ] = []
345
351
self .cells : list [Cell ] = [Top ()]
346
352
self .connections : dict [Net , Net ] = {}
353
+ self .late_to_signal : dict [Net , (_ast .Signal , int )] = {}
347
354
self .io_ports : list [_ast .IOPort ] = []
348
355
self .signals = SignalDict ()
349
356
self .signal_fields = SignalDict ()
@@ -405,16 +412,75 @@ def add_value_cell(self, width: int, cell):
405
412
cell_idx = self .add_cell (cell )
406
413
return Value (Net .from_cell (cell_idx , bit ) for bit in range (width ))
407
414
408
- def alloc_late_value (self , width : int ):
409
- self .last_late_net -= width
410
- return Value (Net .from_late (self .last_late_net + bit ) for bit in range (width ))
415
+ def alloc_late_value (self , signal : _ast .Signal ):
416
+ self .last_late_net -= len (signal )
417
+ value = Value (Net .from_late (self .last_late_net + bit ) for bit in range (len (signal )))
418
+ for bit , net in enumerate (value ):
419
+ self .late_to_signal [net ] = signal , bit
420
+ return value
411
421
412
422
@property
413
423
def top (self ):
414
424
top = self .cells [0 ]
415
425
assert isinstance (top , Top )
416
426
return top
417
427
428
+ def check_comb_cycles (self ):
429
+ class Cycle :
430
+ def __init__ (self , start ):
431
+ self .start = start
432
+ self .path = []
433
+
434
+ checked = set ()
435
+ busy = set ()
436
+
437
+ def traverse (net ):
438
+ if net in checked :
439
+ return None
440
+
441
+ if net in busy :
442
+ return Cycle (net )
443
+ busy .add (net )
444
+
445
+ cycle = None
446
+ if net .is_const :
447
+ pass
448
+ elif net .is_late :
449
+ cycle = traverse (self .connections [net ])
450
+ if cycle is not None :
451
+ sig , bit = self .late_to_signal [net ]
452
+ cycle .path .append ((sig , bit , sig .src_loc ))
453
+ else :
454
+ for src , src_loc in self .cells [net .cell ].comb_edges_to (net .bit ):
455
+ cycle = traverse (src )
456
+ if cycle is not None :
457
+ cycle .path .append ((self .cells [net .cell ], net .bit , src_loc ))
458
+ break
459
+
460
+ if cycle is not None and cycle .start == net :
461
+ msg = ["Combinational cycle detected, path:\n " ]
462
+ for obj , bit , src_loc in reversed (cycle .path ):
463
+ if isinstance (obj , _ast .Signal ):
464
+ obj = f"signal { obj .name } "
465
+ elif isinstance (obj , Operator ):
466
+ obj = f"operator { obj .operator } "
467
+ else :
468
+ obj = f"cell { obj .__class__ .__name__ } "
469
+ src_loc = "[unknown]" if src_loc is None else f"{ src_loc [0 ]} :{ src_loc [1 ]} "
470
+ msg .append (f" { src_loc } : { obj } bit { bit } \n " )
471
+ raise CombinationalCycle ("" .join (msg )) from None
472
+
473
+ busy .remove (net )
474
+ checked .add (net )
475
+ return cycle
476
+
477
+ for cell_idx , cell in enumerate (self .cells ):
478
+ for net in cell .output_nets (cell_idx ):
479
+ assert traverse (net ) is None
480
+ for value in self .signals .values ():
481
+ for net in value :
482
+ assert traverse (net ) is None
483
+
418
484
419
485
class ModuleNetFlow (enum .Enum ):
420
486
"""Describes how a given Net flows into or out of a Module.
@@ -509,6 +575,9 @@ def io_nets(self):
509
575
def resolve_nets (self , netlist : Netlist ):
510
576
raise NotImplementedError
511
577
578
+ def comb_edges_to (self , bit : int ) -> "Iterable[(Net, Any)]" :
579
+ raise NotImplementedError
580
+
512
581
513
582
class Top (Cell ):
514
583
"""A special cell type representing top-level non-IO ports. Must be present in the netlist exactly
@@ -558,6 +627,9 @@ def __repr__(self):
558
627
ports = "" .join (ports )
559
628
return f"(top{ ports } )"
560
629
630
+ def comb_edges_to (self , bit ):
631
+ return []
632
+
561
633
562
634
class Operator (Cell ):
563
635
"""Roughly corresponds to ``hdl.ast.Operator``.
@@ -627,6 +699,28 @@ def __repr__(self):
627
699
inputs = " " .join (repr (input ) for input in self .inputs )
628
700
return f"({ self .operator } { inputs } )"
629
701
702
+ def comb_edges_to (self , bit ):
703
+ if len (self .inputs ) == 1 :
704
+ if self .operator == "~" :
705
+ yield (self .inputs [0 ][bit ], self .src_loc )
706
+ else :
707
+ for net in self .inputs [0 ]:
708
+ yield (net , self .src_loc )
709
+ elif len (self .inputs ) == 2 :
710
+ if self .operator in ("&" , "|" , "^" ):
711
+ yield (self .inputs [0 ][bit ], self .src_loc )
712
+ yield (self .inputs [1 ][bit ], self .src_loc )
713
+ else :
714
+ for net in self .inputs [0 ]:
715
+ yield (net , self .src_loc )
716
+ for net in self .inputs [1 ]:
717
+ yield (net , self .src_loc )
718
+ else :
719
+ assert self .operator == "m"
720
+ yield (self .inputs [0 ][0 ], self .src_loc )
721
+ yield (self .inputs [1 ][bit ], self .src_loc )
722
+ yield (self .inputs [2 ][bit ], self .src_loc )
723
+
630
724
631
725
class Part (Cell ):
632
726
"""Corresponds to ``hdl.ast.Part``.
@@ -666,6 +760,12 @@ def __repr__(self):
666
760
value_signed = "signed" if self .value_signed else "unsigned"
667
761
return f"(part { self .value } { value_signed } { self .offset } { self .width } { self .stride } )"
668
762
763
+ def comb_edges_to (self , bit ):
764
+ for net in self .value :
765
+ yield (net , self .src_loc )
766
+ for net in self .offset :
767
+ yield (net , self .src_loc )
768
+
669
769
670
770
class Matches (Cell ):
671
771
"""A combinatorial cell performing a comparison like ``Value.matches``
@@ -698,6 +798,10 @@ def __repr__(self):
698
798
patterns = " " .join (self .patterns )
699
799
return f"(matches { self .value } { patterns } )"
700
800
801
+ def comb_edges_to (self , bit ):
802
+ for net in self .value :
803
+ yield (net , self .src_loc )
804
+
701
805
702
806
class PriorityMatch (Cell ):
703
807
"""Used to represent a single switch on the control plane of processes.
@@ -733,6 +837,11 @@ def resolve_nets(self, netlist: Netlist):
733
837
def __repr__ (self ):
734
838
return f"(priority_match { self .en } { self .inputs } )"
735
839
840
+ def comb_edges_to (self , bit ):
841
+ yield (self .en , self .src_loc )
842
+ for net in self .inputs [:bit + 1 ]:
843
+ yield (net , self .src_loc )
844
+
736
845
737
846
class Assignment :
738
847
"""A single assignment in an ``AssignmentList``.
@@ -809,6 +918,13 @@ def __repr__(self):
809
918
assignments = " " .join (repr (assign ) for assign in self .assignments )
810
919
return f"(assignment_list { self .default } { assignments } )"
811
920
921
+ def comb_edges_to (self , bit ):
922
+ yield (self .default [bit ], self .src_loc )
923
+ for assign in self .assignments :
924
+ yield (assign .cond , assign .src_loc )
925
+ if bit >= assign .start and bit < assign .start + len (assign .value ):
926
+ yield (assign .value [bit - assign .start ], assign .src_loc )
927
+
812
928
813
929
class FlipFlop (Cell ):
814
930
"""A flip-flop. ``data`` is the data input. ``init`` is the initial and async reset value.
@@ -853,6 +969,10 @@ def __repr__(self):
853
969
attributes = "" .join (f" (attr { key } { val !r} )" for key , val in self .attributes .items ())
854
970
return f"(flipflop { self .data } { self .init } { self .clk_edge } { self .clk } { self .arst } { attributes } )"
855
971
972
+ def comb_edges_to (self , bit ):
973
+ yield (self .clk , self .src_loc )
974
+ yield (self .arst , self .src_loc )
975
+
856
976
857
977
class Memory (Cell ):
858
978
"""Corresponds to ``Memory``. ``init`` must have length equal to ``depth``.
@@ -960,6 +1080,10 @@ def resolve_nets(self, netlist: Netlist):
960
1080
def __repr__ (self ):
961
1081
return f"(read_port { self .memory } { self .width } { self .addr } )"
962
1082
1083
+ def comb_edges_to (self , bit ):
1084
+ for net in self .addr :
1085
+ yield (net , self .src_loc )
1086
+
963
1087
964
1088
class SyncReadPort (Cell ):
965
1089
"""A single synchronous read port of a memory. The cell output is the data port.
@@ -1004,6 +1128,9 @@ def __repr__(self):
1004
1128
transparent_for = " " .join (str (port ) for port in self .transparent_for )
1005
1129
return f"(read_port { self .memory } { self .width } { self .addr } { self .en } { self .clk_edge } { self .clk } ({ transparent_for } ))"
1006
1130
1131
+ def comb_edges_to (self , bit ):
1132
+ return []
1133
+
1007
1134
1008
1135
class AsyncPrint (Cell ):
1009
1136
"""Corresponds to ``Print`` in the "comb" domain.
@@ -1087,6 +1214,9 @@ def resolve_nets(self, netlist: Netlist):
1087
1214
def __repr__ (self ):
1088
1215
return f"(initial)"
1089
1216
1217
+ def comb_edges_to (self , bit ):
1218
+ return []
1219
+
1090
1220
1091
1221
class AnyValue (Cell ):
1092
1222
"""Corresponds to ``AnyConst`` or ``AnySeq``. ``kind`` must be either ``'anyconst'``
@@ -1117,6 +1247,9 @@ def resolve_nets(self, netlist: Netlist):
1117
1247
def __repr__ (self ):
1118
1248
return f"({ self .kind } { self .width } )"
1119
1249
1250
+ def comb_edges_to (self , bit ):
1251
+ return []
1252
+
1120
1253
1121
1254
class AsyncProperty (Cell ):
1122
1255
"""Corresponds to ``Assert``, ``Assume``, or ``Cover`` in the "comb" domain.
@@ -1274,6 +1407,10 @@ def __repr__(self):
1274
1407
items = " " .join (items )
1275
1408
return f"(instance { self .type !r} { self .name !r} { items } )"
1276
1409
1410
+ def comb_edges_to (self , bit ):
1411
+ # don't ask me, I'm a housecat
1412
+ return []
1413
+
1277
1414
1278
1415
class IOBuffer (Cell ):
1279
1416
"""An IO buffer cell. This cell does two things:
@@ -1328,3 +1465,8 @@ def __repr__(self):
1328
1465
return f"(iob { self .dir .value } { self .port } )"
1329
1466
else :
1330
1467
return f"(iob { self .dir .value } { self .port } { self .o } { self .oe } )"
1468
+
1469
+ def comb_edges_to (self , bit ):
1470
+ if self .dir is not IODirection .Input :
1471
+ yield (self .o [bit ], self .src_loc )
1472
+ yield (self .oe , self .src_loc )
0 commit comments