diff --git a/passes/silimate/negopt.cc b/passes/silimate/negopt.cc index e3066b9b7..28d047ed9 100644 --- a/passes/silimate/negopt.cc +++ b/passes/silimate/negopt.cc @@ -85,16 +85,29 @@ struct NegoptPass : public Pass { for (auto module : design->selected_modules()) { if (run_pre) { + // manual2sub and sub2neg only need to run once: no downstream + // pre-subpass creates the patterns they match + // separate pm instances so sub2neg sees the $sub cells manual2sub creates. + { + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + pm.run_manual2sub(); + log_flush(); + } + { + peepopt_pm pm(module); + pm.setup(module->selected_cells()); + pm.run_sub2neg(); + log_flush(); + } + + // negexpand/negneg/negmux can feed each other. did_something = true; for (int iter = 0; iter < max_iterations && did_something; iter++) { did_something = false; peepopt_pm pm(module); pm.setup(module->selected_cells()); - pm.run_manual2sub(); // Reduce manual 2's complement to subtraction first - log_flush(); - pm.run_sub2neg(); - log_flush(); pm.run_negexpand(); log_flush(); pm.run_negneg(); diff --git a/passes/silimate/peepopt_manual2sub.pmg b/passes/silimate/peepopt_manual2sub.pmg index d82e5cf5f..b45a05308 100644 --- a/passes/silimate/peepopt_manual2sub.pmg +++ b/passes/silimate/peepopt_manual2sub.pmg @@ -12,9 +12,8 @@ pattern manual2sub state minuend subtrahend result_sig root_a root_b state is_signed -state inner_y +state inner_y inner_port_sig_A root_port_sig inner_port_sig_B -// 1. Match the "root" add (the one that produces the final result) match root_add select root_add->type == $add set root_a port(root_add, \A) @@ -23,8 +22,7 @@ match root_add set is_signed root_add->getParam(ID::A_SIGNED).as_bool() endmatch -// 2. Case A: (a + ~b) + 1 -// Check if root_add has a constant 1 +// Case A: (a + ~b) + 1 code root_add inner_y { SigSpec pa = root_a; @@ -51,20 +49,28 @@ code root_add inner_y } endcode -// Find the inner add -// inner_y is discovered in code so gating happens in the code block +// Find the inner add whose Y feeds the non-constant port of root_add match inner_add_A select inner_add_A->type == $add + index port(inner_add_A, \Y) === inner_y + filter nusers(port(inner_add_A, \Y)) == 2 endmatch -// Find the NOT gate on one of the ports of inner_add_A +// Branch over both ports of inner_add_A to find the NOT gate +code inner_port_sig_A + inner_port_sig_A = port(inner_add_A, \A); + branch; + inner_port_sig_A = port(inner_add_A, \B); +endcode + match not_gate_A select not_gate_A->type == $not + index port(not_gate_A, \Y) === inner_port_sig_A endmatch code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed { - // Require consistent signedness on the root add. + // Require consistent signedness across the chain if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; @@ -73,11 +79,7 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed if (inner_add_A->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; - if (port(inner_add_A, \Y) != inner_y) - reject; - if (nusers(port(inner_add_A, \Y)) != 2) - reject; - + // Determine which port is the NOT output and which is the minuend SigSpec not_y = port(not_gate_A, \Y); SigSpec add_a = port(inner_add_A, \A); SigSpec add_b = port(inner_add_A, \B); @@ -92,7 +94,6 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed subtrahend = port(not_gate_A, \A); - // Create the subtraction cell log("manual2sub in %s: Found (a + ~b) + 1 pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig)); Cell *cell = root_add; int width = GetSize(result_sig); @@ -100,27 +101,25 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed // Reject if the +1 wrap boundary is narrower than the final result if (inner_width < width) reject; + // Anchor both operands to the inner add width to preserve carry behavior SigSpec minuend_rs = minuend; SigSpec subtrahend_rs = subtrahend; - // Anchor both operands to the inner add width to preserve carry behavior minuend_rs.extend_u0(inner_width, is_signed); subtrahend_rs.extend_u0(inner_width, is_signed); SigSpec sub_y = result_sig; - // Extend the sub result back to the root width when needed if (inner_width != width) { sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width); } Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed); + // Extend the sub result back to root width when inner is wider if (inner_width != width) { SigSpec sub_y_rs = sub_y; sub_y_rs.extend_u0(width, is_signed); module->connect(result_sig, sub_y_rs); } - // Let fixup_parameters handle width adjustments sub->fixup_parameters(); - // Remove old cells autoremove(root_add); autoremove(inner_add_A); autoremove(not_gate_A); @@ -130,23 +129,36 @@ code root_add inner_add_A not_gate_A subtrahend minuend result_sig is_signed } endcode -// 3. Case B: a + (~b + 1) +// Case B: a + (~b + 1) + +// Branch over both ports of root_add to find the inner (~b + 1) add +code root_port_sig + root_port_sig = port(root_add, \A); + branch; + root_port_sig = port(root_add, \B); +endcode -// Find the inner add on either port of root_add match inner_add_B select inner_add_B->type == $add - filter port(inner_add_B, \Y) == root_a || port(inner_add_B, \Y) == root_b - select nusers(port(inner_add_B, \Y)) == 2 + index port(inner_add_B, \Y) === root_port_sig + filter nusers(port(inner_add_B, \Y)) == 2 endmatch -// Check if inner_add_B has a constant 1 and a NOT gate +// Branch over both ports of inner_add_B to find the NOT gate +code inner_port_sig_B + inner_port_sig_B = port(inner_add_B, \A); + branch; + inner_port_sig_B = port(inner_add_B, \B); +endcode + match not_gate_B select not_gate_B->type == $not + index port(not_gate_B, \Y) === inner_port_sig_B endmatch code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed { - // Require consistent signedness on the root add. + // Require consistent signedness across the chain if (root_add->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; @@ -155,6 +167,8 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed if (inner_add_B->getParam(ID::B_SIGNED).as_bool() != is_signed) reject; + // Verify inner_add_B has the form (~b + 1): one port is constant 1, + // the other is the NOT output SigSpec pa = inner_add_B->getPort(ID::A); SigSpec pb = inner_add_B->getPort(ID::B); SigSpec not_y = port(not_gate_B, \Y); @@ -175,13 +189,13 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed if (!valid) reject; + // The minuend is whichever root_add port is NOT the inner_add_B output subtrahend = port(not_gate_B, \A); if (inner_add_B->getPort(ID::Y) == root_add->getPort(ID::A)) minuend = root_b; else minuend = root_a; - // Create the subtraction cell log("manual2sub in %s: Found a + (~b + 1) pattern, creating $sub for %s\n", log_id(module), log_signal(result_sig)); Cell *cell = root_add; int width = GetSize(result_sig); @@ -190,32 +204,30 @@ code root_add inner_add_B not_gate_B minuend subtrahend result_sig is_signed // Reject if the +1 wrap boundary is narrower than the final result if (inner_width < width) reject; - + + // Anchor both operands to the inner add width to preserve carry behavior SigSpec minuend_rs = minuend; SigSpec subtrahend_rs = subtrahend; - // Anchor both operands to the inner add width to preserve carry behavior minuend_rs.extend_u0(inner_width, is_signed); subtrahend_rs.extend_u0(inner_width, is_signed); SigSpec sub_y = result_sig; - // Extend the sub result back to the root width when needed if (inner_width != width) { sub_y = module->addWire(NEW_ID2_SUFFIX("sub_y"), inner_width); } Cell *sub = module->addSub(NEW_ID2_SUFFIX("sub"), minuend_rs, subtrahend_rs, sub_y, is_signed); + // Extend the sub result back to root width when inner is wider if (inner_width != width) { SigSpec sub_y_rs = sub_y; sub_y_rs.extend_u0(width, is_signed); module->connect(result_sig, sub_y_rs); } - // Let fixup_parameters handle width adjustments sub->fixup_parameters(); - // Remove old cells autoremove(root_add); autoremove(inner_add_B); autoremove(not_gate_B); diff --git a/passes/silimate/peepopt_muxneg.pmg b/passes/silimate/peepopt_muxneg.pmg index 7a8c33eb5..e81fc7971 100644 --- a/passes/silimate/peepopt_muxneg.pmg +++ b/passes/silimate/peepopt_muxneg.pmg @@ -20,7 +20,7 @@ endmatch match neg_a select neg_a->type == $neg - select nusers(port(neg_a, \Y)) == 2 + filter nusers(port(neg_a, \Y)) == 2 index port(neg_a, \Y) === mux_a set neg_a_in port(neg_a, \A) set neg_a_y port(neg_a, \Y) @@ -29,7 +29,7 @@ endmatch match neg_b select neg_b->type == $neg - select nusers(port(neg_b, \Y)) == 2 + filter nusers(port(neg_b, \Y)) == 2 index port(neg_b, \Y) === mux_b set neg_b_in port(neg_b, \A) set neg_b_y port(neg_b, \Y) diff --git a/passes/silimate/peepopt_neg2sub.pmg b/passes/silimate/peepopt_neg2sub.pmg index 6a958e431..9594e3b47 100644 --- a/passes/silimate/peepopt_neg2sub.pmg +++ b/passes/silimate/peepopt_neg2sub.pmg @@ -28,7 +28,7 @@ endcode match neg select neg->type == $neg - select nusers(port(neg, \Y)) == 2 + filter nusers(port(neg, \Y)) == 2 index port(neg, \Y) === (neg_on_a ? add_a : add_b) set neg_a port(neg, \A) set neg_y port(neg, \Y) diff --git a/passes/silimate/peepopt_negexpand.pmg b/passes/silimate/peepopt_negexpand.pmg index a764bcb34..7ddc0b950 100644 --- a/passes/silimate/peepopt_negexpand.pmg +++ b/passes/silimate/peepopt_negexpand.pmg @@ -20,7 +20,7 @@ endmatch match add select add->type == $add index port(add, \Y) === neg_a - select nusers(port(add, \Y)) == 2 + filter nusers(port(add, \Y)) == 2 set add_a port(add, \A) set add_b port(add, \B) endmatch diff --git a/passes/silimate/peepopt_negmux.pmg b/passes/silimate/peepopt_negmux.pmg index 579732484..d1918e3bf 100644 --- a/passes/silimate/peepopt_negmux.pmg +++ b/passes/silimate/peepopt_negmux.pmg @@ -18,8 +18,8 @@ match neg endmatch match mux - select mux->type == $mux - select nusers(port(mux, \Y)) == 2 + select mux->type == $mux + select nusers(port(mux, \Y)) == 2 set mux_a port(mux, \A) set mux_b port(mux, \B) set mux_s port(mux, \S) @@ -43,7 +43,7 @@ code neg_a neg_y mux_a mux_b mux_s mux_y a_signed } { - // Anchor negations to the original negation width + // Anchor negations to the original neg output width int width = GetSize(neg_y); Cell *cell = neg; diff --git a/passes/silimate/peepopt_negneg.pmg b/passes/silimate/peepopt_negneg.pmg index eafebc0d2..166e641b0 100644 --- a/passes/silimate/peepopt_negneg.pmg +++ b/passes/silimate/peepopt_negneg.pmg @@ -18,7 +18,7 @@ endmatch match neg2 select neg2->type == $neg index port(neg2, \Y) === neg1_a - select nusers(port(neg2, \Y)) == 2 + filter nusers(port(neg2, \Y)) == 2 set neg2_a port(neg2, \A) endmatch diff --git a/passes/silimate/peepopt_negrebuild.pmg b/passes/silimate/peepopt_negrebuild.pmg index 57739b210..37c8fba5e 100644 --- a/passes/silimate/peepopt_negrebuild.pmg +++ b/passes/silimate/peepopt_negrebuild.pmg @@ -39,16 +39,16 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_ if (add_signed != add_b_signed) reject; - // Require negations to share the add signedness + // Require both negations share the add's signedness if (neg1_signed != add_signed || neg2_signed != add_signed) reject; { - // Avoid matching the same neg cell twice + // Avoid matching the same neg cell for both inputs if (neg1 == neg2) reject; - // Require a single wrap boundary for both negations and the sum + // Require a single wrap boundary across both negations and the sum if (GetSize(neg1_y) != GetSize(neg2_y)) reject; if (GetSize(add_y) != GetSize(neg1_y)) @@ -89,11 +89,13 @@ code add_a add_b add_y neg1_a neg1_y neg2_a neg2_y add_signed add_b_signed neg1_ neg1_a_rs.extend_u0(width, add_signed); neg2_a_rs.extend_u0(width, add_signed); + // Build -(a + b) to replace (-a) + (-b) SigSpec sum = module->addWire(NEW_ID2_SUFFIX("sum"), width); Cell *new_add = module->addAdd(NEW_ID2_SUFFIX("add"), neg1_a_rs, neg2_a_rs, sum, add_signed); SigSpec neg_out = module->addWire(NEW_ID2_SUFFIX("neg_y"), width); Cell *new_neg = module->addNeg(NEW_ID2_SUFFIX("neg"), sum, neg_out, add_signed); + // Extend result back to original add output width SigSpec neg_out_rs = neg_out; neg_out_rs.extend_u0(GetSize(add_y), add_signed); module->connect(add_y, neg_out_rs);