Skip to content

Commit 47c6b78

Browse files
wanda-phiwhitequark
authored andcommitted
hdl._nir: speed up combinational cycle detection.
Fixes #1628.
1 parent ff44683 commit 47c6b78

File tree

1 file changed

+58
-2
lines changed

1 file changed

+58
-2
lines changed

amaranth/hdl/_nir.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ def traverse(net):
444444
busy.add(net)
445445

446446
cycle = None
447+
extra_nets = []
447448
if net.is_const:
448449
pass
449450
elif net.is_late:
@@ -452,10 +453,16 @@ def traverse(net):
452453
sig, bit = self.late_to_signal[net]
453454
cycle.path.append((sig, bit, sig.src_loc))
454455
else:
455-
for src, src_loc in self.cells[net.cell].comb_edges_to(net.bit):
456+
cell = self.cells[net.cell]
457+
if not cell.comb_edges_is_per_bit():
458+
extra_nets = [extra_net for extra_net in cell.output_nets(net.cell) if extra_net != net]
459+
for extra_net in extra_nets:
460+
assert extra_net not in checked
461+
busy.add(extra_net)
462+
for src, src_loc in cell.comb_edges_to(net.bit):
456463
cycle = traverse(src)
457464
if cycle is not None:
458-
cycle.path.append((self.cells[net.cell], net.bit, src_loc))
465+
cycle.path.append((cell, net.bit, src_loc))
459466
break
460467

461468
if cycle is not None and cycle.start == net:
@@ -473,6 +480,9 @@ def traverse(net):
473480

474481
busy.remove(net)
475482
checked.add(net)
483+
for extra_net in extra_nets:
484+
busy.remove(extra_net)
485+
checked.add(extra_net)
476486
return cycle
477487

478488
for cell_idx, cell in enumerate(self.cells):
@@ -579,6 +589,10 @@ def resolve_nets(self, netlist: Netlist):
579589
def comb_edges_to(self, bit: int) -> "Iterable[(Net, Any)]":
580590
raise NotImplementedError
581591

592+
def comb_edges_is_per_bit(self) -> bool:
593+
"""Returns True iff ``comb_edges_to`` looks at its argument."""
594+
raise NotImplementedError
595+
582596

583597
class Top(Cell):
584598
"""A special cell type representing top-level non-IO ports. Must be present in the netlist exactly
@@ -631,6 +645,9 @@ def __repr__(self):
631645
def comb_edges_to(self, bit):
632646
return []
633647

648+
def comb_edges_is_per_bit(self) -> bool:
649+
return False
650+
634651

635652
class Operator(Cell):
636653
"""Roughly corresponds to ``hdl.ast.Operator``.
@@ -722,6 +739,15 @@ def comb_edges_to(self, bit):
722739
yield (self.inputs[1][bit], self.src_loc)
723740
yield (self.inputs[2][bit], self.src_loc)
724741

742+
def comb_edges_is_per_bit(self) -> bool:
743+
if len(self.inputs) == 1 and self.operator == "~":
744+
return True
745+
elif len(self.inputs) == 2 and self.operator in ("&", "|", "^"):
746+
return True
747+
elif len(self.inputs) == 3:
748+
return True
749+
return False
750+
725751

726752
class Part(Cell):
727753
"""Corresponds to ``hdl.ast.Part``.
@@ -767,6 +793,9 @@ def comb_edges_to(self, bit):
767793
for net in self.offset:
768794
yield (net, self.src_loc)
769795

796+
def comb_edges_is_per_bit(self) -> bool:
797+
return False
798+
770799

771800
class Match(Cell):
772801
"""Used to represent a single switch on the control plane of processes.
@@ -812,6 +841,9 @@ def comb_edges_to(self, bit):
812841
for net in self.value:
813842
yield (net, self.src_loc)
814843

844+
def comb_edges_is_per_bit(self) -> bool:
845+
return False
846+
815847

816848
class Assignment:
817849
"""A single assignment in an ``AssignmentList``.
@@ -895,6 +927,9 @@ def comb_edges_to(self, bit):
895927
yield (assign.cond, assign.src_loc)
896928
yield (assign.value[bit - assign.start], assign.src_loc)
897929

930+
def comb_edges_is_per_bit(self) -> bool:
931+
return True
932+
898933

899934
class FlipFlop(Cell):
900935
"""A flip-flop. ``data`` is the data input. ``init`` is the initial and async reset value.
@@ -943,6 +978,9 @@ def comb_edges_to(self, bit):
943978
yield (self.clk, self.src_loc)
944979
yield (self.arst, self.src_loc)
945980

981+
def comb_edges_is_per_bit(self) -> bool:
982+
return False
983+
946984

947985
class Memory(Cell):
948986
"""Corresponds to ``Memory``. ``init`` must have length equal to ``depth``.
@@ -1054,6 +1092,9 @@ def comb_edges_to(self, bit):
10541092
for net in self.addr:
10551093
yield (net, self.src_loc)
10561094

1095+
def comb_edges_is_per_bit(self) -> bool:
1096+
return False
1097+
10571098

10581099
class SyncReadPort(Cell):
10591100
"""A single synchronous read port of a memory. The cell output is the data port.
@@ -1101,6 +1142,9 @@ def __repr__(self):
11011142
def comb_edges_to(self, bit):
11021143
return []
11031144

1145+
def comb_edges_is_per_bit(self) -> bool:
1146+
return False
1147+
11041148

11051149
class AsyncPrint(Cell):
11061150
"""Corresponds to ``Print`` in the "comb" domain.
@@ -1187,6 +1231,9 @@ def __repr__(self):
11871231
def comb_edges_to(self, bit):
11881232
return []
11891233

1234+
def comb_edges_is_per_bit(self) -> bool:
1235+
return False
1236+
11901237

11911238
class AnyValue(Cell):
11921239
"""Corresponds to ``AnyConst`` or ``AnySeq``. ``kind`` must be either ``'anyconst'``
@@ -1220,6 +1267,9 @@ def __repr__(self):
12201267
def comb_edges_to(self, bit):
12211268
return []
12221269

1270+
def comb_edges_is_per_bit(self) -> bool:
1271+
return False
1272+
12231273

12241274
class AsyncProperty(Cell):
12251275
"""Corresponds to ``Assert``, ``Assume``, or ``Cover`` in the "comb" domain.
@@ -1381,6 +1431,9 @@ def comb_edges_to(self, bit):
13811431
# don't ask me, I'm a housecat
13821432
return []
13831433

1434+
def comb_edges_is_per_bit(self) -> bool:
1435+
return False
1436+
13841437

13851438
class IOBuffer(Cell):
13861439
"""An IO buffer cell. This cell does two things:
@@ -1440,3 +1493,6 @@ def comb_edges_to(self, bit):
14401493
if self.dir is not IODirection.Input:
14411494
yield (self.o[bit], self.src_loc)
14421495
yield (self.oe, self.src_loc)
1496+
1497+
def comb_edges_is_per_bit(self) -> bool:
1498+
return True

0 commit comments

Comments
 (0)