@@ -52,11 +52,162 @@ def optimize(update_working_block=True, block=None, skip_sanity_check=False):
52
52
constant_propagation (block , True )
53
53
_remove_unlistened_nets (block )
54
54
common_subexp_elimination (block )
55
+ _optimize_inverter_chains (block , skip_sanity_check )
55
56
if (not skip_sanity_check ) or _get_debug_mode ():
56
57
block .sanity_check ()
57
58
return block
58
59
59
60
61
+ def _get_inverter_chains (wire_creator , wire_users ):
62
+ """Returns all inverter chains in the block.
63
+
64
+ The function returns a list of inverter chains in the block.
65
+ Each inverter chain is represented as a list of the WireVectors
66
+ in the chain.
67
+
68
+ Consider the following circuit, for example:
69
+ A -~-> B -~-> C -w-> X
70
+ D -~-> E -w-> Y
71
+ If the function is called on this circuit, it will return
72
+ [[A, B, C], [D, E]].
73
+ """
74
+
75
+ # Build a list of inverter chains. Each inverter chain is a list of WireVectors,
76
+ # from source to destination.
77
+ inverter_chains = []
78
+ for current_dest , current_creator in wire_creator .items ():
79
+ if current_creator .op != "~" :
80
+ # Skip non-inverters.
81
+ continue
82
+
83
+ # The current inverter connects current_arg (a WireVector) to current_dest (also
84
+ # a WireVector).
85
+ current_arg = current_creator .args [0 ]
86
+ # current_users is the number of LogicNets that use current_dest.
87
+ current_users = len (wire_users [current_dest ])
88
+
89
+ # Add the current inverter to the end of this inverter chain.
90
+ append_to = None
91
+ # Add the current inverter to the beginning of this inverter chain.
92
+ prepend_to = None
93
+ next_inverter_chains = []
94
+ for inverter_chain in inverter_chains :
95
+ chain_arg = inverter_chain [0 ]
96
+ chain_dest = inverter_chain [- 1 ]
97
+ chain_users = len (wire_users [chain_dest ])
98
+
99
+ if chain_dest is current_arg and chain_users == 1 :
100
+ # This chain's only destination is the current inverter. Append the
101
+ # current inverter to the chain.
102
+ append_to = inverter_chain
103
+ elif chain_arg is current_dest and current_users == 1 :
104
+ # This chain's only argument is the current inverter. Add the current
105
+ # inverter to the beginning of the chain.
106
+ prepend_to = inverter_chain
107
+ else :
108
+ # The current inverter is not connected to the inverter chain, so we
109
+ # pass the inverter chain through to next_inverter_chains
110
+ next_inverter_chains .append (inverter_chain )
111
+
112
+ if append_to and prepend_to :
113
+ # The current inverter joins two existing inverter chains.
114
+ next_inverter_chains .append (append_to + prepend_to )
115
+ elif append_to :
116
+ # Add the current inverter after 'append_to'.
117
+ next_inverter_chains .append (append_to + [current_dest ])
118
+ elif prepend_to :
119
+ # Add the current inverter before 'prepend_to'.
120
+ next_inverter_chains .append ([current_arg ] + prepend_to )
121
+ else :
122
+ # The current inverter is not connected to any inverter chain, so
123
+ # we start a new inverter chain with it
124
+ next_inverter_chains .append ([current_arg , current_dest ])
125
+
126
+ inverter_chains = next_inverter_chains
127
+ return inverter_chains
128
+
129
+
130
+ def _optimize_inverter_chains (block , skip_sanity_check = False ):
131
+ """ Optimizes inverter chains in the block.
132
+
133
+ An inverter chain means two or more inverters directly connected
134
+ to each other. Inverter chains are redundant and can be removed.
135
+ For example, A -~-> B -~-> C -w-> X can be reduced to A -w-> X.
136
+
137
+ After optimization, a chain of an even number of inverters will
138
+ be reduced a direct connection, and a chain of an odd number of
139
+ inverters will be reduced to one inverter.
140
+
141
+ If an inverter chain has intermediate users it won't be removed.
142
+ For example, the inverter chain in the following circuit won't be removed:
143
+ A -~-> B -~-> C -w-> X
144
+ B -w-> Y
145
+ """
146
+
147
+ # wire_creator maps from WireVector to the LogicNet that defines its value.
148
+ # wire_users maps from WireVector to a list of LogicNets that use its value.
149
+ wire_creator , wire_users = block .net_connections ()
150
+
151
+ new_logic = set ()
152
+ net_removal_set = set ()
153
+ wire_removal_set = set ()
154
+
155
+ # This ProducerList maps the end wire of an inverter chain to its beginning wire.
156
+ # We need this because when removing an inverter chain its end wire gets removed,
157
+ # so we need to replace the source of LogicNets using the end wire of the inverter
158
+ # chain with the chain's beginning wire.
159
+ #
160
+ # We need a ProducerList, rather than a simple dict, because if an inverter chain
161
+ # of more than two inverters has intermediate users, we may have to query the dict
162
+ # multiple times to get the replacement for the inverter chain's last wire.
163
+ # Consider the following circuit, for example:
164
+ # A -~-> B -~-> C -w-> X
165
+ # C -~-> D -~-> E -w-> Y
166
+ # This is the optimized version of the circuit:
167
+ # A -w-> X
168
+ # A -w-> Y
169
+ # The inverter chains found will be A-B-C and C-D-E (two separate chains will be
170
+ # found instead of A-B-C-D-E because C has an intermediate user). In the dict,
171
+ # C will be mapped to A and E will be mapped to C. Hence, when finding the
172
+ # replacement of E, we have to first query the dict to get C, and then query
173
+ # the dict again on C to get A.
174
+ wire_src_dict = _ProducerList ()
175
+
176
+ for inverter_chain in _get_inverter_chains (wire_creator , wire_users ):
177
+ # If len(inverter_chain) = n, there are n-1 inverters in the chain.
178
+ # We only remove inverters if there are at least two inverters in a chain.
179
+ if len (inverter_chain ) > 2 :
180
+ if len (inverter_chain ) % 2 == 1 : # There is an even number of inverters in a chain.
181
+ start_idx = 1
182
+ else : # There is an odd number of inverters in a chain.
183
+ start_idx = 2
184
+ # Remove wires used in the inverter chain.
185
+ wires_to_remove = inverter_chain [start_idx :]
186
+ wire_removal_set .update (wires_to_remove )
187
+ # Remove inverters used in the chain.
188
+ inverters_to_remove = {wire_creator [wire ] for wire in wires_to_remove }
189
+ net_removal_set .update (inverters_to_remove )
190
+ # Map the end wire of the inverter chain to the beginning wire.
191
+ wire_src_dict [inverter_chain [- 1 ]] = inverter_chain [start_idx - 1 ]
192
+
193
+ # This loop recreates the block with inverter chains removed. It adds each
194
+ # LogicNet in the original block to the new block if it is not marked for
195
+ # removal, and replaces the source of the LogicNet if its source was the end wire
196
+ # of a removed inverter chain.
197
+ for net in block .logic :
198
+ if net not in net_removal_set :
199
+ new_logic .add (LogicNet (net .op , net .op_param ,
200
+ args = tuple (wire_src_dict .find_producer (x ) for x in net .args ),
201
+ dests = net .dests ))
202
+
203
+ block .logic = new_logic
204
+ for dead_wirevector in wire_removal_set :
205
+ block .remove_wirevector (dead_wirevector )
206
+
207
+ if (not skip_sanity_check ) or _get_debug_mode ():
208
+ block .sanity_check ()
209
+
210
+
60
211
class _ProducerList (object ):
61
212
""" Maps from wire to its immediate producer and finds ultimate producers. """
62
213
def __init__ (self ):
0 commit comments