Skip to content

Commit 378f86a

Browse files
committed
Fix bug that sometimes double inverts are not removed
1 parent 4cc9e6a commit 378f86a

1 file changed

Lines changed: 36 additions & 11 deletions

File tree

pyrtl/passes.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,42 @@ def is_wirevector_used_elsewhere(wire, used_at_nets):
7878
for net in block.logic:
7979
if net.op == "~":
8080
invert_destination_wires[net.dests[0].name] = net
81-
for net in invert_destination_wires.values():
82-
# If the argument of the net is in invert_destination_wires, then it is a double invert
83-
# If the net is in net_exclude_set, then it was already removed, so we do not process it
84-
if net.args[0].name in invert_destination_wires and net not in net_exclude_set:
85-
previous_net = invert_destination_wires[net.args[0].name]
86-
if not is_wirevector_used_elsewhere(net.args[0], (net, previous_net)) \
87-
and previous_net not in net_exclude_set:
88-
new_logic.add(LogicNet('w', None, args=previous_net.args, dests=net.dests))
89-
wire_removal_set.add(net.args[0])
90-
net_exclude_set.add(net)
91-
net_exclude_set.add(previous_net)
81+
# If double invert nets are removed randomly, this may leave some double inverts behind.
82+
# Example: ~(~(~(~a)))
83+
# If we remove the middle two inverts first, we will end up with ~((~a)). These remaining
84+
# double inverts won't get removed because they aren't directly connected.
85+
# To avoid this, we remove double inverts in a chain sequentially from start to end.
86+
# For example, we first remove the two outer inverts from ~(~(~(~a))) to get ~(~a),
87+
# and then remove the remaining two inner inverts. To do this, we need iterate through
88+
# the invert_destination_wires dictionary multiple times, hence the outer while loop.
89+
repeat = True
90+
while repeat:
91+
repeat = False
92+
removed_nets = set()
93+
for net in invert_destination_wires.values():
94+
# If the argument of the net is in invert_destination_wires, then it is a double invert
95+
# If the net is in net_exclude_set, then it was already removed, so we do not process it
96+
if net.args[0].name in invert_destination_wires and net not in net_exclude_set:
97+
previous_net = invert_destination_wires[net.args[0].name]
98+
if not is_wirevector_used_elsewhere(net.args[0], (net, previous_net)) \
99+
and previous_net not in net_exclude_set:
100+
# If previous_net is in invert_destination_wires, we have a chain of
101+
# 3 or more double inverts. To make sure we remove double inverts
102+
# in these chains sequentially, we only remove the double invert
103+
# we found if the invert net whose destination is previous_net
104+
# was not removed yet. If it was not yet removed, the for loop
105+
# needs to run again, so we set repeat to True.
106+
if previous_net.args[0].name in invert_destination_wires:
107+
repeat = True
108+
else:
109+
new_logic.add(LogicNet('w', None, args=previous_net.args, dests=net.dests))
110+
wire_removal_set.add(net.args[0])
111+
removed_nets.add(net)
112+
removed_nets.add(previous_net)
113+
# remove removed_nets from invert_destination_wires to optimize the for loop
114+
for net in removed_nets:
115+
del invert_destination_wires[net.dests[0].name]
116+
net_exclude_set.update(removed_nets)
92117

93118
for net in block.logic:
94119
if net not in net_exclude_set:

0 commit comments

Comments
 (0)