@@ -178,28 +178,47 @@ class Dff {
178178 }
179179
180180 // If the lowest priority async rule assigns the output value to itself,
181- // remove the rule and fold this into the input signal.
181+ // remove the rule and fold this into the input signal. If the LSB assigns
182+ // the output to itself but higher bits don't, we resize down to just the
183+ // LSBs that assign to themselves, allowing more optimized representations
184+ // for those bits.
182185 void optimize_self_assign (ConstEval& ce) {
183186 SigSpec sig_out_mapped = sig_out;
184187 ce.assign_map .apply (sig_out_mapped);
185188
186- size_t new_size = async_rules.size ();
187- for (auto it = async_rules.crbegin (); it != async_rules.crend (); it++) {
188- const auto & [value, trigger] = *it;
189+ // Calculate the number of low priority rules that can be folded into
190+ // the input signal for a given bit position
191+ const auto foldable_rules = [&](const size_t i) {
192+ size_t foldable = 0 ;
193+ for (auto it = async_rules.crbegin (); it != async_rules.crend (); it++, foldable++) {
194+ const auto & [value, trigger] = *it;
195+ if (value[i] != sig_out_mapped[i])
196+ break ;
197+ }
198+ return foldable;
199+ };
200+
201+ // Work out how many bits from the lsb can be folded into the same
202+ // number of rules
203+ const size_t lsb_foldable_rules = foldable_rules (0 );
189204
190- if (value != sig_out_mapped)
205+ size_t new_size;
206+ for (new_size = 1 ; new_size < size (); new_size++)
207+ if (foldable_rules (new_size) != lsb_foldable_rules)
191208 break ;
192209
193- if (!trigger.inverted )
194- sig_in = mod.Mux (NEW_ID, sig_in, value, trigger.sig );
195- else
196- sig_in = mod.Mux (NEW_ID, value, sig_in, trigger.sig );
210+ resize (new_size);
197211
198- ce.eval (sig_in);
199- new_size--;
200- }
212+ // Calculate the disjunction of triggers
213+ SigSpec triggers;
214+ for (size_t i = 0 ; i < lsb_foldable_rules; i++)
215+ triggers.append (async_rules.crbegin ()[i].trigger .positive_trigger (mod));
216+
217+ const auto trigger = mod.ReduceOr (NEW_ID, triggers);
218+ sig_in = mod.Mux (NEW_ID, sig_in, sig_out, trigger);
219+ ce.eval (sig_in);
201220
202- async_rules.resize (new_size );
221+ async_rules.resize (async_rules. size () - lsb_foldable_rules );
203222 }
204223
205224 // If we have only a single rule, this means we will generate either an $aldff
0 commit comments