diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 5cce8c0ee..29ec094b4 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -41,18 +41,22 @@ struct AlumaccWorker std::vector cells; RTLIL::SigSpec a, b, c, y; std::vector> cmp; - bool is_signed, invert_b; + bool is_signed, invert_b, oversized; RTLIL::Cell *alu_cell; RTLIL::SigSpec cached_lt, cached_slt, cached_gt, cached_sgt, cached_eq, cached_ne; RTLIL::SigSpec cached_cf, cached_of, cached_sf; - RTLIL::SigSpec get_lt(bool is_signed) { - if (is_signed) { + RTLIL::SigSpec get_lt(bool this_signed) { + if (this_signed) { if (GetSize(cached_slt) == 0) { - get_of(); get_sf(); - cached_slt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf); + if (is_signed && oversized) { + cached_slt = cached_sf; + } else { + get_of(); + cached_slt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf); + } } return cached_slt; @@ -65,10 +69,10 @@ struct AlumaccWorker } } - RTLIL::SigSpec get_gt(bool is_signed) { - if (is_signed) { + RTLIL::SigSpec get_gt(bool this_signed) { + if (this_signed) { if (GetSize(cached_sgt) == 0) { - get_lt(is_signed); + get_lt(this_signed); get_eq(); SigSpec Or = alu_cell->module->Or(NEW_ID, cached_slt, cached_eq); cached_sgt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute()); @@ -77,7 +81,7 @@ struct AlumaccWorker return cached_sgt; } else { if (GetSize(cached_gt) == 0) { - get_lt(is_signed); + get_lt(this_signed); get_eq(); SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq); cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute()); @@ -112,7 +116,14 @@ struct AlumaccWorker if (GetSize(cached_of) == 0) { cached_of = {alu_cell->getPort(ID::CO), alu_cell->getPort(ID::CI)}; log_assert(GetSize(cached_of) >= 2); - cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(cached_of)-1], cached_of[GetSize(cached_of)-2]); + if (oversized) { + auto max_input = max(GetSize(a), GetSize(b)); + log_assert(max_input >= 1); + cached_of = alu_cell->module->Xor(NEW_ID, cached_of[max_input], cached_of[max_input-1]); + } else { + log_assert(GetSize(y) >= 1); + cached_of = alu_cell->module->Xor(NEW_ID, cached_of[GetSize(y)], cached_of[GetSize(y)-1]); + } } return cached_of; } @@ -120,7 +131,12 @@ struct AlumaccWorker RTLIL::SigSpec get_sf() { if (GetSize(cached_sf) == 0) { cached_sf = alu_cell->getPort(ID::Y); - cached_sf = cached_sf[GetSize(cached_sf)-1]; + log_assert(GetSize(cached_sf) >= 1); + if (oversized && !is_signed) { + cached_sf = cached_sf[max(GetSize(a), GetSize(b))-1]; + } else { + cached_sf = cached_sf[GetSize(y)-1]; + } } return cached_sf; } @@ -357,13 +373,17 @@ struct AlumaccWorker alunode->cells.push_back(n->cell); alunode->is_signed = a_signed; alunode->invert_b = subtract_b; + alunode->oversized = max(GetSize(A), GetSize(B)) < GetSize(n->y); alunode->a = A; alunode->b = B; alunode->c = C; alunode->y = n->y; - sig_alu[RTLIL::SigSig(A, B)].insert(alunode); + // only consider merging if the output is at least as wide as the inputs + if (max(GetSize(A), GetSize(B)) <= GetSize(n->y)) { + sig_alu[RTLIL::SigSig(A, B)].insert(alunode); + } delete_nodes.insert(n); next_macc:; } @@ -410,6 +430,20 @@ struct AlumaccWorker eq_cells.push_back(cell); } + auto cmp_mergeable = [](const alunode_t *node, const SigSpec cmp_A, const SigSpec cmp_B, bool is_signed) { + if (!node->invert_b || node->c != State::S1) { + // comparators need subtraction + return false; + } + if (is_signed != node->is_signed) { + if (GetSize(cmp_A) != GetSize(cmp_B)) { + // mismatched input widths are problematic when mixing signedness + return false; + } + } + return true; + }; + for (auto cell : lge_cells) { log(" creating $alu model for %s (%s):", cell, cell->type.unescape()); @@ -425,14 +459,14 @@ struct AlumaccWorker alunode_t *n = nullptr; for (auto node : sig_alu[RTLIL::SigSig(A, B)]) - if (node->invert_b && node->c == State::S1) { + if (cmp_mergeable(node, A, B, is_signed)) { n = node; break; } if (n == nullptr) { for (auto node : sig_alu[RTLIL::SigSig(B, A)]) - if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) { + if (cmp_mergeable(node, B, A, is_signed)) { n = node; cmp_less = !cmp_less; std::swap(A, B); @@ -448,6 +482,7 @@ struct AlumaccWorker n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B))); n->is_signed = is_signed; n->invert_b = true; + n->oversized = false; sig_alu[RTLIL::SigSig(A, B)].insert(n); log(" new $alu\n"); } else { @@ -470,14 +505,14 @@ struct AlumaccWorker alunode_t *n = nullptr; for (auto node : sig_alu[RTLIL::SigSig(A, B)]) - if (node->invert_b && node->c == State::S1) { + if (cmp_mergeable(node, A, B, is_signed)) { n = node; break; } if (n == nullptr) { for (auto node : sig_alu[RTLIL::SigSig(B, A)]) - if (node->is_signed == is_signed && node->invert_b && node->c == State::S1) { + if (cmp_mergeable(node, B, A, is_signed)) { n = node; break; } diff --git a/tests/alumacc/.gitignore b/tests/alumacc/.gitignore new file mode 100644 index 000000000..90ea6e92a --- /dev/null +++ b/tests/alumacc/.gitignore @@ -0,0 +1 @@ +uut_*.* diff --git a/tests/opt/alumacc.ys b/tests/alumacc/cmp_merge.ys similarity index 67% rename from tests/opt/alumacc.ys rename to tests/alumacc/cmp_merge.ys index 043bdaaee..ae46ba883 100644 --- a/tests/opt/alumacc.ys +++ b/tests/alumacc/cmp_merge.ys @@ -26,8 +26,18 @@ assign ne = ra != rb; endmodule EOT -proc - equiv_opt -assert alumacc -alumacc +design -load postopt select -assert-count 1 t:$alu + +design -reset +read_verilog << EOT +module top(input signed a, b, output x, output [1:0] y); +assign y = $unsigned(a) - $unsigned(b); +assign x = a > b; +endmodule +EOT +hierarchy +wreduce +equiv_opt -assert alumacc + diff --git a/tests/alumacc/generate_mk.py b/tests/alumacc/generate_mk.py index 6a921d5a0..997870aa4 100644 --- a/tests/alumacc/generate_mk.py +++ b/tests/alumacc/generate_mk.py @@ -5,4 +5,99 @@ sys.path.append("..") import gen_tests_makefile +import argparse +import random +from itertools import product + +TEMPLATE=r"""read_rtlil << EOT +module \top + wire width {a_width} input 1 signed \a + wire width {b_width} input 2 signed \b + wire output 3 \x + wire width {y_width} output 4 \y + cell ${c1} \c1 + parameter \A_SIGNED {c1_signed} + parameter \B_SIGNED {c1_signed} + parameter \A_WIDTH {a_width} + parameter \B_WIDTH {b_width} + parameter \Y_WIDTH 1 + connect \A \a + connect \B \b + connect \Y \x + end + cell ${c2} \c2 + parameter \A_SIGNED {c2_signed} + parameter \B_SIGNED {c2_signed} + parameter \A_WIDTH {a_width} + parameter \B_WIDTH {b_width} + parameter \Y_WIDTH {y_width} + connect \A \a + connect \B \b + connect \Y \y + end +end +EOT +equiv_opt -assert alumacc +design -load postopt +{postopt_assert} +""" + +EQ = ["eq", "eqx", "ne", "nex"] +LGE = ["lt", "gt"] +CMP = EQ + LGE +ARITH = ["add", "sub"] +SIGNED = [0, 1] +WIDTH = list(range(1, 4)) + +parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('-S', '--seed', type = int, help = 'seed for PRNG') +args = parser.parse_args() + +seed = args.seed +if seed is None: + seed = random.randrange(sys.maxsize) +print("alumacc PRNG seed: %d" % seed) +random.seed(seed) + +count = 0 +for c1_signed, c2, c2_signed, a_width in product( + SIGNED, [EQ, LGE, ARITH], SIGNED, WIDTH +): + c1 = random.choice(LGE) + if isinstance(c2, list): + c2 = random.choice(c2) + + b_widths: list[int] + if a_width == 2: + b_widths = [1, 2, 3] + else: + b_widths = [a_width] + y_widths: list[int] + if c2 in CMP: + y_widths = [1] + else: + y_widths = b_widths + + for b_width, y_width in product(b_widths, y_widths): + out_cells = "t:$alu" + for c in c1, c2: + if c in EQ: + # LGE cells will always remap to $alu, but EQ cells only will if merged + out_cells += f" t:${c}" + + undersized = c2 in ARITH and y_width < max(a_width, b_width) + mixed_sign = c1_signed != c2_signed and a_width != b_width + if c2 != "add" and not undersized and not mixed_sign: + # cells correctly merged + postopt_assert = f"select -assert-count 1 {out_cells}" + else: + # currently these all produce 2, but assert-max allows for future improvement + postopt_assert = f"select -assert-max 2 {out_cells}" + + with open(f"uut_{'s' if c1_signed else 'u'}{c1}_{'s' if c2_signed else 'u'}{c2}_{a_width}_{b_width}_{y_width}.ys", "w") as f: + print(str.format_map(TEMPLATE, locals()), file=f) + count = count + 1 + +# print(count) + gen_tests_makefile.generate(["--yosys-scripts"])