From 08394c51a2fb99caf52394931e91120f2a1aae07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 13 Dec 2024 18:59:35 +0100 Subject: [PATCH 01/11] rtlil: Add const append helper --- kernel/rtlil.cc | 6 ++++++ kernel/rtlil.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index cb0f7da7807..d705813d9f0 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -540,6 +540,12 @@ void RTLIL::Const::bitvectorize() const { } } +void RTLIL::Const::append(const RTLIL::Const &other) { + bitvectorize(); + bitvectype& bv = get_bits(); + bv.insert(bv.end(), other.begin(), other.end()); +} + RTLIL::State RTLIL::Const::const_iterator::operator*() const { if (auto bv = parent.get_if_bits()) return (*bv)[idx]; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index f9cacd151cd..33742cb701c 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -738,6 +738,8 @@ struct RTLIL::Const bool empty() const; void bitvectorize() const; + void append(const RTLIL::Const &other); + class const_iterator { private: const Const& parent; From c5fd96ebb0c9067927bd4df381af92783bb4b547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 13 Dec 2024 19:01:41 +0100 Subject: [PATCH 02/11] macc_v2: Start new cell --- kernel/constids.inc | 4 ++ kernel/macc.h | 104 ++++++++++++++++++---------- kernel/rtlil.cc | 29 ++++++++ passes/techmap/maccmap.cc | 2 +- tests/alumacc/macc_infer_n_unmap.ys | 19 +++++ 5 files changed, 120 insertions(+), 38 deletions(-) create mode 100644 tests/alumacc/macc_infer_n_unmap.ys diff --git a/kernel/constids.inc b/kernel/constids.inc index d68e2dfe646..15d3cc83b29 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -276,3 +276,7 @@ X(Y) X(Y_WIDTH) X(area) X(capacitance) +X(NTERMS) +X(TERM_NEGATED) +X(A_WIDTHS) +X(B_WIDTHS) diff --git a/kernel/macc.h b/kernel/macc.h index 55940769de2..e969dd86d0a 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -82,7 +82,7 @@ struct Macc new_ports.swap(ports); } - void from_cell(RTLIL::Cell *cell) + void from_cell_v1(RTLIL::Cell *cell) { RTLIL::SigSpec port_a = cell->getPort(ID::A); @@ -136,52 +136,82 @@ struct Macc log_assert(port_a_cursor == GetSize(port_a)); } - void to_cell(RTLIL::Cell *cell) const + void from_cell(RTLIL::Cell *cell) { - RTLIL::SigSpec port_a; - std::vector config_bits; - int max_size = 0, num_bits = 0; - - for (auto &port : ports) { - max_size = max(max_size, GetSize(port.in_a)); - max_size = max(max_size, GetSize(port.in_b)); + if (cell->type == ID($macc)) { + from_cell_v1(cell); + return; } + log_assert(cell->type == ID($macc_v2)); - while (max_size) - num_bits++, max_size /= 2; + RTLIL::SigSpec port_a = cell->getPort(ID::A); + RTLIL::SigSpec port_b = cell->getPort(ID::B); - log_assert(num_bits < 16); - config_bits.push_back(num_bits & 1 ? State::S1 : State::S0); - config_bits.push_back(num_bits & 2 ? State::S1 : State::S0); - config_bits.push_back(num_bits & 4 ? State::S1 : State::S0); - config_bits.push_back(num_bits & 8 ? State::S1 : State::S0); + ports.clear(); - for (auto &port : ports) - { - if (GetSize(port.in_a) == 0) - continue; + int nterms = cell->getParam(ID::NTERMS).as_int(); + const Const &neg = cell->getParam(ID::TERM_NEGATED); + const Const &a_widths = cell->getParam(ID::A_WIDTHS); + const Const &b_widths = cell->getParam(ID::B_WIDTHS); + const Const &a_signed = cell->getParam(ID::A_SIGNED); + const Const &b_signed = cell->getParam(ID::B_SIGNED); + + int ai = 0, bi = 0; + for (int i = 0; i < nterms; i++) { + port_t term; + + log_assert(a_signed[i] == b_signed[i]); + term.is_signed = (a_signed[i] == State::S1); + int a_width = a_widths.extract(16 * i, 16).as_int(false); + int b_width = b_widths.extract(16 * i, 16).as_int(false); + + term.in_a = port_a.extract(ai, a_width); + ai += a_width; + term.in_b = port_b.extract(bi, b_width); + bi += b_width; + term.do_subtract = (neg[i] == State::S1); + + ports.push_back(term); + } + log_assert(port_a.size() == ai); + log_assert(port_b.size() == bi); + } - config_bits.push_back(port.is_signed ? State::S1 : State::S0); - config_bits.push_back(port.do_subtract ? State::S1 : State::S0); + void to_cell(RTLIL::Cell *cell) + { + cell->type = ID($macc_v2); - int size_a = GetSize(port.in_a); - for (int i = 0; i < num_bits; i++) - config_bits.push_back(size_a & (1 << i) ? State::S1 : State::S0); + int nterms = ports.size(); + const auto Sx = State::Sx; + Const a_signed(Sx, nterms), b_signed(Sx, nterms), negated(Sx, nterms); + Const a_widths, b_widths; + SigSpec a, b; - int size_b = GetSize(port.in_b); - for (int i = 0; i < num_bits; i++) - config_bits.push_back(size_b & (1 << i) ? State::S1 : State::S0); + for (int i = 0; i < nterms; i++) { + SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b; - port_a.append(port.in_a); - port_a.append(port.in_b); - } + a_widths.append(Const(term_a.size(), 16)); + b_widths.append(Const(term_b.size(), 16)); - cell->setPort(ID::A, port_a); - cell->setPort(ID::B, {}); - cell->setParam(ID::CONFIG, config_bits); - cell->setParam(ID::CONFIG_WIDTH, GetSize(config_bits)); - cell->setParam(ID::A_WIDTH, GetSize(port_a)); - cell->setParam(ID::B_WIDTH, 0); + a_signed.bits()[i] = b_signed.bits()[i] = + (ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + negated.bits()[i] = (ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + + a.append(term_a); + b.append(term_b); + } + negated.is_fully_def(); + a_signed.is_fully_def(); + b_signed.is_fully_def(); + + cell->setParam(ID::NTERMS, nterms); + cell->setParam(ID::TERM_NEGATED, negated); + cell->setParam(ID::A_SIGNED, a_signed); + cell->setParam(ID::B_SIGNED, b_signed); + cell->setParam(ID::A_WIDTHS, a_widths); + cell->setParam(ID::B_WIDTHS, b_widths); + cell->setPort(ID::A, a); + cell->setPort(ID::B, b); } bool eval(RTLIL::Const &result) const diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index d705813d9f0..ac491f4cd0b 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1467,6 +1467,30 @@ namespace { return; } + if (cell->type == ID($macc_v2)) { + if (param(ID::NTERMS) <= 0) + error(__LINE__); + param_bits(ID::TERM_NEGATED, param(ID::NTERMS)); + param_bits(ID::A_SIGNED, param(ID::NTERMS)); + param_bits(ID::B_SIGNED, param(ID::NTERMS)); + if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) + error(__LINE__); + param_bits(ID::A_WIDTHS, param(ID::NTERMS) * 16); + param_bits(ID::B_WIDTHS, param(ID::NTERMS) * 16); + const Const &a_width = cell->getParam(ID::A_WIDTHS); + const Const &b_width = cell->getParam(ID::B_WIDTHS); + int a_width_sum = 0, b_width_sum = 0; + for (int i = 0; i < param(ID::NTERMS); i++) { + a_width_sum += a_width.extract(16 * i, 16).as_int(false); + b_width_sum += b_width.extract(16 * i, 16).as_int(false); + } + port(ID::A, a_width_sum); + port(ID::B, b_width_sum); + port(ID::Y, param(ID::Y_WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($logic_not)) { param_bool(ID::A_SIGNED); port(ID::A, param(ID::A_WIDTH)); @@ -4099,6 +4123,11 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) return; } + if (type == ID($macc_v2)) { + parameters[ID::Y_WIDTH] = GetSize(connections_[ID::Y]); + return; + } + bool signedness_ab = !type.in(ID($slice), ID($concat), ID($macc)); if (connections_.count(ID::A)) { diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 911d66cfa1b..3dde9243803 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -403,7 +403,7 @@ struct MaccmapPass : public Pass { for (auto mod : design->selected_modules()) for (auto cell : mod->selected_cells()) - if (cell->type == ID($macc)) { + if (cell->type.in(ID($macc), ID($macc_v2))) { log("Mapping %s.%s (%s).\n", log_id(mod), log_id(cell), log_id(cell->type)); maccmap(mod, cell, unmap_mode); mod->remove(cell); diff --git a/tests/alumacc/macc_infer_n_unmap.ys b/tests/alumacc/macc_infer_n_unmap.ys new file mode 100644 index 00000000000..569511b64d0 --- /dev/null +++ b/tests/alumacc/macc_infer_n_unmap.ys @@ -0,0 +1,19 @@ +read_verilog < Date: Fri, 10 Jan 2025 11:44:54 +0100 Subject: [PATCH 03/11] macc_v2: Add C port --- kernel/constids.inc | 8 +++-- kernel/macc.h | 79 +++++++++++++++++++++++++++++++-------------- kernel/rtlil.cc | 26 ++++++++++----- 3 files changed, 78 insertions(+), 35 deletions(-) diff --git a/kernel/constids.inc b/kernel/constids.inc index 15d3cc83b29..4fdbb3dc84d 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -276,7 +276,11 @@ X(Y) X(Y_WIDTH) X(area) X(capacitance) -X(NTERMS) -X(TERM_NEGATED) +X(NPRODUCTS) +X(NADDENDS) +X(PRODUCT_NEGATED) +X(ADDEND_NEGATED) X(A_WIDTHS) X(B_WIDTHS) +X(C_WIDTHS) +X(C_SIGNED) diff --git a/kernel/macc.h b/kernel/macc.h index e969dd86d0a..fdd738d4582 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -146,18 +146,18 @@ struct Macc RTLIL::SigSpec port_a = cell->getPort(ID::A); RTLIL::SigSpec port_b = cell->getPort(ID::B); + RTLIL::SigSpec port_c = cell->getPort(ID::C); ports.clear(); - int nterms = cell->getParam(ID::NTERMS).as_int(); - const Const &neg = cell->getParam(ID::TERM_NEGATED); + int nproducts = cell->getParam(ID::NPRODUCTS).as_int(); + const Const &product_neg = cell->getParam(ID::PRODUCT_NEGATED); const Const &a_widths = cell->getParam(ID::A_WIDTHS); const Const &b_widths = cell->getParam(ID::B_WIDTHS); const Const &a_signed = cell->getParam(ID::A_SIGNED); const Const &b_signed = cell->getParam(ID::B_SIGNED); - int ai = 0, bi = 0; - for (int i = 0; i < nterms; i++) { + for (int i = 0; i < nproducts; i++) { port_t term; log_assert(a_signed[i] == b_signed[i]); @@ -169,49 +169,78 @@ struct Macc ai += a_width; term.in_b = port_b.extract(bi, b_width); bi += b_width; - term.do_subtract = (neg[i] == State::S1); + term.do_subtract = (product_neg[i] == State::S1); ports.push_back(term); } log_assert(port_a.size() == ai); log_assert(port_b.size() == bi); + + int naddends = cell->getParam(ID::NADDENDS).as_int(); + const Const &addend_neg = cell->getParam(ID::ADDEND_NEGATED); + const Const &c_widths = cell->getParam(ID::C_WIDTHS); + const Const &c_signed = cell->getParam(ID::C_SIGNED); + int ci = 0; + for (int i = 0; i < naddends; i++) { + port_t term; + + term.is_signed = (c_signed[i] == State::S1); + int c_width = c_widths.extract(16 * i, 16).as_int(false); + + term.in_a = port_c.extract(ci, c_width); + ci += c_width; + term.do_subtract = (addend_neg[i] == State::S1); + + ports.push_back(term); + } + log_assert(port_c.size() == ci); } void to_cell(RTLIL::Cell *cell) { cell->type = ID($macc_v2); - int nterms = ports.size(); - const auto Sx = State::Sx; - Const a_signed(Sx, nterms), b_signed(Sx, nterms), negated(Sx, nterms); - Const a_widths, b_widths; - SigSpec a, b; + int nproducts = 0, naddends = 0; + Const a_signed, b_signed, a_widths, b_widths, product_negated; + Const c_signed, c_widths, addend_negated; + SigSpec a, b, c; - for (int i = 0; i < nterms; i++) { + for (int i = 0; i < (int) ports.size(); i++) { SigSpec term_a = ports[i].in_a, term_b = ports[i].in_b; - a_widths.append(Const(term_a.size(), 16)); - b_widths.append(Const(term_b.size(), 16)); - - a_signed.bits()[i] = b_signed.bits()[i] = - (ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); - negated.bits()[i] = (ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); - - a.append(term_a); - b.append(term_b); + if (term_b.empty()) { + // addend + c_widths.append(Const(term_a.size(), 16)); + c_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + addend_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + c.append(term_a); + naddends++; + } else { + // product + a_widths.append(Const(term_a.size(), 16)); + b_widths.append(Const(term_b.size(), 16)); + a_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + b_signed.append(ports[i].is_signed ? RTLIL::S1 : RTLIL::S0); + product_negated.append(ports[i].do_subtract ? RTLIL::S1 : RTLIL::S0); + a.append(term_a); + b.append(term_b); + nproducts++; + } } - negated.is_fully_def(); - a_signed.is_fully_def(); - b_signed.is_fully_def(); - cell->setParam(ID::NTERMS, nterms); - cell->setParam(ID::TERM_NEGATED, negated); + cell->setParam(ID::NPRODUCTS, nproducts); + cell->setParam(ID::PRODUCT_NEGATED, product_negated); + cell->setParam(ID::NADDENDS, naddends); + cell->setParam(ID::ADDEND_NEGATED, addend_negated); cell->setParam(ID::A_SIGNED, a_signed); cell->setParam(ID::B_SIGNED, b_signed); + cell->setParam(ID::C_SIGNED, c_signed); cell->setParam(ID::A_WIDTHS, a_widths); cell->setParam(ID::B_WIDTHS, b_widths); + cell->setParam(ID::C_WIDTHS, c_widths); cell->setPort(ID::A, a); cell->setPort(ID::B, b); + cell->setPort(ID::C, c); } bool eval(RTLIL::Const &result) const diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index ac491f4cd0b..f2f36ba301b 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1468,24 +1468,34 @@ namespace { } if (cell->type == ID($macc_v2)) { - if (param(ID::NTERMS) <= 0) + if (param(ID::NPRODUCTS) <= 0) error(__LINE__); - param_bits(ID::TERM_NEGATED, param(ID::NTERMS)); - param_bits(ID::A_SIGNED, param(ID::NTERMS)); - param_bits(ID::B_SIGNED, param(ID::NTERMS)); + if (param(ID::NADDENDS) <= 0) + error(__LINE__); + param_bits(ID::PRODUCT_NEGATED, param(ID::NPRODUCTS)); + param_bits(ID::ADDEND_NEGATED, param(ID::NADDENDS)); + param_bits(ID::A_SIGNED, param(ID::NPRODUCTS)); + param_bits(ID::B_SIGNED, param(ID::NPRODUCTS)); + param_bits(ID::C_SIGNED, param(ID::NADDENDS)); if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) error(__LINE__); - param_bits(ID::A_WIDTHS, param(ID::NTERMS) * 16); - param_bits(ID::B_WIDTHS, param(ID::NTERMS) * 16); + param_bits(ID::A_WIDTHS, param(ID::NPRODUCTS) * 16); + param_bits(ID::B_WIDTHS, param(ID::NPRODUCTS) * 16); + param_bits(ID::C_WIDTHS, param(ID::NADDENDS) * 16); const Const &a_width = cell->getParam(ID::A_WIDTHS); const Const &b_width = cell->getParam(ID::B_WIDTHS); - int a_width_sum = 0, b_width_sum = 0; - for (int i = 0; i < param(ID::NTERMS); i++) { + const Const &c_width = cell->getParam(ID::C_WIDTHS); + int a_width_sum = 0, b_width_sum = 0, c_width_sum = 0; + for (int i = 0; i < param(ID::NPRODUCTS); i++) { a_width_sum += a_width.extract(16 * i, 16).as_int(false); b_width_sum += b_width.extract(16 * i, 16).as_int(false); } + for (int i = 0; i < param(ID::NADDENDS); i++) { + c_width_sum += c_width.extract(16 * i, 16).as_int(false); + } port(ID::A, a_width_sum); port(ID::B, b_width_sum); + port(ID::C, c_width_sum); port(ID::Y, param(ID::Y_WIDTH)); check_expected(); return; From 61450e8b6eb246342971fb0460e1d3ce0091c6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 10 Jan 2025 13:42:26 +0100 Subject: [PATCH 04/11] Update codebase for macc_v2 --- docs/source/cell/word_arith.rst | 2 +- docs/source/code_examples/fifo/fifo.ys | 2 +- docs/source/getting_started/example_synth.rst | 2 +- kernel/celledges.cc | 2 +- kernel/celltypes.h | 1 + kernel/consteval.h | 2 +- kernel/rtlil.cc | 16 ++++++++-------- kernel/satgen.cc | 2 +- passes/techmap/booth.cc | 2 +- 9 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/source/cell/word_arith.rst b/docs/source/cell/word_arith.rst index 49070814add..8e3daa27f30 100644 --- a/docs/source/cell/word_arith.rst +++ b/docs/source/cell/word_arith.rst @@ -1,7 +1,7 @@ Coarse arithmetics ------------------ -.. todo:: Add information about `$alu`, `$fa`, and `$lcu` cells. +.. todo:: Add information about `$alu`, `$fa`, `$macc_v2`, and `$lcu` cells. The `$macc` cell type represents a generalized multiply and accumulate operation. The cell is purely combinational. It outputs the result of summing up diff --git a/docs/source/code_examples/fifo/fifo.ys b/docs/source/code_examples/fifo/fifo.ys index 57a28e63e8e..e6b9bf69d4c 100644 --- a/docs/source/code_examples/fifo/fifo.ys +++ b/docs/source/code_examples/fifo/fifo.ys @@ -67,7 +67,7 @@ show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdat # ======================================================== alumacc -select -set new_cells t:$alu t:$macc +select -set new_cells t:$alu t:$macc_v2 show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci* # ======================================================== diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst index 189eaddfada..e215586cc9c 100644 --- a/docs/source/getting_started/example_synth.rst +++ b/docs/source/getting_started/example_synth.rst @@ -523,7 +523,7 @@ That brings us to the fourth and final part for the iCE40 synthesis flow: :name: synth_coarse4 Where before each type of arithmetic operation had its own cell, e.g. `$add`, we -now want to extract these into `$alu` and `$macc` cells which can help identify +now want to extract these into `$alu` and `$macc_v2` cells which can help identify opportunities for reusing logic. We do this by running `alumacc`, which we can see produce the following changes in our example design: diff --git a/kernel/celledges.cc b/kernel/celledges.cc index bad7124d9b1..95f160612c9 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -453,7 +453,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL } // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat - // FIXME: $lut $sop $alu $lcu $macc $fa + // FIXME: $lut $sop $alu $lcu $macc $macc_v2 $fa // FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx // FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 3167a9addf9..0ce5db54d46 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -144,6 +144,7 @@ struct CellTypes setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true); setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true); + setup_type(ID($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, true); setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); } diff --git a/kernel/consteval.h b/kernel/consteval.h index 331d8f12854..05e94ab86a3 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -310,7 +310,7 @@ struct ConstEval } } } - else if (cell->type == ID($macc)) + else if (cell->type.in(ID($macc), ID($macc_v2))) { Macc macc; macc.from_cell(cell); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index f2f36ba301b..29a7ebf3ee7 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1472,16 +1472,16 @@ namespace { error(__LINE__); if (param(ID::NADDENDS) <= 0) error(__LINE__); - param_bits(ID::PRODUCT_NEGATED, param(ID::NPRODUCTS)); - param_bits(ID::ADDEND_NEGATED, param(ID::NADDENDS)); - param_bits(ID::A_SIGNED, param(ID::NPRODUCTS)); - param_bits(ID::B_SIGNED, param(ID::NPRODUCTS)); - param_bits(ID::C_SIGNED, param(ID::NADDENDS)); + param_bits(ID::PRODUCT_NEGATED, min(param(ID::NPRODUCTS), 1)); + param_bits(ID::ADDEND_NEGATED, min(param(ID::NADDENDS), 1)); + param_bits(ID::A_SIGNED, min(param(ID::NPRODUCTS), 1)); + param_bits(ID::B_SIGNED, min(param(ID::NPRODUCTS), 1)); + param_bits(ID::C_SIGNED, min(param(ID::NADDENDS), 1)); if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) error(__LINE__); - param_bits(ID::A_WIDTHS, param(ID::NPRODUCTS) * 16); - param_bits(ID::B_WIDTHS, param(ID::NPRODUCTS) * 16); - param_bits(ID::C_WIDTHS, param(ID::NADDENDS) * 16); + param_bits(ID::A_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::B_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::C_WIDTHS, min(param(ID::NADDENDS) * 16, 1)); const Const &a_width = cell->getParam(ID::A_WIDTHS); const Const &b_width = cell->getParam(ID::B_WIDTHS); const Const &c_width = cell->getParam(ID::C_WIDTHS); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index dd15b51f3bd..9e5fa911155 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -740,7 +740,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (cell->type == ID($macc)) + if (cell->type.in(ID($macc), ID($macc_v2))) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); diff --git a/passes/techmap/booth.cc b/passes/techmap/booth.cc index dce7da486f5..cd9012e6cd3 100644 --- a/passes/techmap/booth.cc +++ b/passes/techmap/booth.cc @@ -218,7 +218,7 @@ struct BoothPassWorker { log_assert(cell->getParam(ID::A_SIGNED).as_bool() == cell->getParam(ID::B_SIGNED).as_bool()); is_signed = cell->getParam(ID::A_SIGNED).as_bool(); - } else if (cell->type == ID($macc)) { + } else if (cell->type.in(ID($macc), ID($macc_v2))) { Macc macc; macc.from_cell(cell); From 1e8aa56f7fdcf7510159927bef9b81040354c119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 10 Jan 2025 13:43:54 +0100 Subject: [PATCH 05/11] macc_v2: Init simlib model --- techlibs/common/simlib.v | 105 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index df9e1feb1e4..248c93513e8 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1207,6 +1207,111 @@ end endmodule +// -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $macc_v2 (A, B, C, Y) +//* group arith +//- +//- Multiply and add. +//- This cell represents a generic fused multiply-add operation, it supersedes the +//- earlier $macc cell. +//- +module \$macc_v2 (A, B, C, Y); + +parameter NPRODUCTS = 0; +parameter NADDENDS = 0; +parameter A_WIDTHS = 16'h0000; +parameter B_WIDTHS = 16'h0000; +parameter C_WIDTHS = 16'h0000; +parameter Y_WIDTH = 0; + +parameter PRODUCT_NEGATED = 1'bx; +parameter ADDEND_NEGATED = 1'bx; +parameter A_SIGNED = 1'bx; +parameter B_SIGNED = 1'bx; +parameter C_SIGNED = 1'bx; + +function integer sum_widths1; + input [(16*NPRODUCTS)-1:0] widths; + int i; + sum_widths1 = 0; + begin + for (i = 0; i < NPRODUCTS; i++) begin + sum_widths1 += widths[16*i+:16]; + end + end +endfunction + +function integer sum_widths2; + input [(16*NADDENDS)-1:0] widths; + int i; + sum_widths2 = 0; + begin + for (i = 0; i < NADDENDS; i++) begin + sum_widths2 += widths[16*i+:16]; + end + end +endfunction + +input [sum_widths1(A_WIDTHS)-1:0] A; // concatenation of LHS factors +input [sum_widths1(B_WIDTHS)-1:0] B; // concatenation of RHS factors +input [sum_widths2(C_WIDTHS)-1:0] C; // concatenation of summands +output reg [Y_WIDTH-1:0] Y; // output sum + +integer i, j, ai, bi, ci, aw, bw, cw; +reg [Y_WIDTH-1:0] product; +reg signed [Y_WIDTH-1:0] product_signed; +reg [Y_WIDTH-1:0] addend; +reg signed [Y_WIDTH-1:0] addend_signed; + +always @* begin + Y = 0; + ai = 0; + bi = 0; + for (i = 0; i < NPRODUCTS; i = i+1) + begin + aw = A_WIDTHS[16*i+:16]; + bw = B_WIDTHS[16*i+:16]; + + product = A[ai +: aw] * B[bi +: bw]; + product_signed = $signed(A[ai +: aw]) * $signed(B[bi +: bw]); + + // A_SIGNED[i] == B_SIGNED[i] as RTLIL invariant + if (A_SIGNED[i] && B_SIGNED[i]) + product = product_signed; + + if (PRODUCT_NEGATED[i]) + Y = Y - product; + else + Y = Y + product; + + ai += aw; + bi += bw; + end + + ci = 0; + for (i = 0; i < NADDENDS; i = i+1) + begin + cw = C_WIDTHS[16*i+:16]; + + addend = C[ci +: cw]; + addend_signed = $signed(C[ci +: cw]); + + if (C_SIGNED[i]) + addend = addend_signed; + + if (ADDEND_NEGATED[i]) + Y = Y - addend; + else + Y = Y + addend; + + ci += cw; + end +end + +endmodule + // -------------------------------------------------------- //* ver 2 //* title Divider From cb8f855f347da751d65eb002f80247d107b25e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 24 Jan 2025 12:35:23 +0100 Subject: [PATCH 06/11] rtlil: Fix macc_v2 cell check --- kernel/rtlil.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 29a7ebf3ee7..c10e879c56c 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1472,16 +1472,16 @@ namespace { error(__LINE__); if (param(ID::NADDENDS) <= 0) error(__LINE__); - param_bits(ID::PRODUCT_NEGATED, min(param(ID::NPRODUCTS), 1)); - param_bits(ID::ADDEND_NEGATED, min(param(ID::NADDENDS), 1)); - param_bits(ID::A_SIGNED, min(param(ID::NPRODUCTS), 1)); - param_bits(ID::B_SIGNED, min(param(ID::NPRODUCTS), 1)); - param_bits(ID::C_SIGNED, min(param(ID::NADDENDS), 1)); + param_bits(ID::PRODUCT_NEGATED, max(param(ID::NPRODUCTS), 1)); + param_bits(ID::ADDEND_NEGATED, max(param(ID::NADDENDS), 1)); + param_bits(ID::A_SIGNED, max(param(ID::NPRODUCTS), 1)); + param_bits(ID::B_SIGNED, max(param(ID::NPRODUCTS), 1)); + param_bits(ID::C_SIGNED, max(param(ID::NADDENDS), 1)); if (cell->getParam(ID::A_SIGNED) != cell->getParam(ID::B_SIGNED)) error(__LINE__); - param_bits(ID::A_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); - param_bits(ID::B_WIDTHS, min(param(ID::NPRODUCTS) * 16, 1)); - param_bits(ID::C_WIDTHS, min(param(ID::NADDENDS) * 16, 1)); + param_bits(ID::A_WIDTHS, max(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::B_WIDTHS, max(param(ID::NPRODUCTS) * 16, 1)); + param_bits(ID::C_WIDTHS, max(param(ID::NADDENDS) * 16, 1)); const Const &a_width = cell->getParam(ID::A_WIDTHS); const Const &b_width = cell->getParam(ID::B_WIDTHS); const Const &c_width = cell->getParam(ID::C_WIDTHS); From 3184b335dacc42dc4631931058630a04c4bcf277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 24 Jan 2025 12:35:36 +0100 Subject: [PATCH 07/11] macc_v2: Fix language constructs in simlib model --- techlibs/common/simlib.v | 53 +++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 248c93513e8..028e4e5a9e4 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1234,22 +1234,22 @@ parameter C_SIGNED = 1'bx; function integer sum_widths1; input [(16*NPRODUCTS)-1:0] widths; - int i; - sum_widths1 = 0; + integer i; begin + sum_widths1 = 0; for (i = 0; i < NPRODUCTS; i++) begin - sum_widths1 += widths[16*i+:16]; + sum_widths1 = sum_widths1 + widths[16*i+:16]; end end endfunction function integer sum_widths2; input [(16*NADDENDS)-1:0] widths; - int i; - sum_widths2 = 0; + integer i; begin + sum_widths2 = 0; for (i = 0; i < NADDENDS; i++) begin - sum_widths2 += widths[16*i+:16]; + sum_widths2 = sum_widths2 + widths[16*i+:16]; end end endfunction @@ -1261,9 +1261,7 @@ output reg [Y_WIDTH-1:0] Y; // output sum integer i, j, ai, bi, ci, aw, bw, cw; reg [Y_WIDTH-1:0] product; -reg signed [Y_WIDTH-1:0] product_signed; -reg [Y_WIDTH-1:0] addend; -reg signed [Y_WIDTH-1:0] addend_signed; +reg [Y_WIDTH-1:0] addend, oper_a, oper_b; always @* begin Y = 0; @@ -1274,20 +1272,29 @@ always @* begin aw = A_WIDTHS[16*i+:16]; bw = B_WIDTHS[16*i+:16]; - product = A[ai +: aw] * B[bi +: bw]; - product_signed = $signed(A[ai +: aw]) * $signed(B[bi +: bw]); - + oper_a = 0; + oper_b = 0; + for (j = 0; j < Y_WIDTH && j < aw; j = j + 1) + oper_a[j] = A[ai + j]; + for (j = 0; j < Y_WIDTH && j < bw; j = j + 1) + oper_b[j] = B[bi + j]; // A_SIGNED[i] == B_SIGNED[i] as RTLIL invariant - if (A_SIGNED[i] && B_SIGNED[i]) - product = product_signed; + if (A_SIGNED[i] && B_SIGNED[i]) begin + for (j = aw; j > 0 && j < Y_WIDTH; j = j + 1) + oper_a[j] = oper_a[j - 1]; + for (j = bw; j > 0 && j < Y_WIDTH; j = j + 1) + oper_b[j] = oper_b[j - 1]; + end + + product = oper_a * oper_b; if (PRODUCT_NEGATED[i]) Y = Y - product; else Y = Y + product; - ai += aw; - bi += bw; + ai = ai + aw; + bi = bi + bw; end ci = 0; @@ -1295,18 +1302,20 @@ always @* begin begin cw = C_WIDTHS[16*i+:16]; - addend = C[ci +: cw]; - addend_signed = $signed(C[ci +: cw]); - - if (C_SIGNED[i]) - addend = addend_signed; + addend = 0; + for (j = 0; j < Y_WIDTH && j < cw; j = j + 1) + addend[j] = C[ci + j]; + if (C_SIGNED[i]) begin + for (j = cw; j > 0 && j < Y_WIDTH; j = j + 1) + addend[j] = addend[j - 1]; + end if (ADDEND_NEGATED[i]) Y = Y - addend; else Y = Y + addend; - ci += cw; + ci = ci + cw; end end From aabb4ea1df3d5ef216b6fce95caf989333a14e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 27 Jan 2025 13:08:19 +0100 Subject: [PATCH 08/11] macc_v2: Fix checks and parameter padding --- kernel/macc.h | 17 +++++++++++++++++ kernel/rtlil.cc | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/kernel/macc.h b/kernel/macc.h index fdd738d4582..5cf8d634dfa 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -228,6 +228,23 @@ struct Macc } } + if (a_signed.empty()) + a_signed = {RTLIL::Sx}; + if (b_signed.empty()) + a_signed = {RTLIL::Sx}; + if (c_signed.empty()) + c_signed = {RTLIL::Sx}; + if (a_widths.empty()) + a_widths = {RTLIL::Sx}; + if (b_widths.empty()) + b_widths = {RTLIL::Sx}; + if (c_widths.empty()) + c_widths = {RTLIL::Sx}; + if (product_negated.empty()) + product_negated = {RTLIL::Sx}; + if (addend_negated.empty()) + addend_negated = {RTLIL::Sx}; + cell->setParam(ID::NPRODUCTS, nproducts); cell->setParam(ID::PRODUCT_NEGATED, product_negated); cell->setParam(ID::NADDENDS, naddends); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index c10e879c56c..d197660bd78 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1470,7 +1470,7 @@ namespace { if (cell->type == ID($macc_v2)) { if (param(ID::NPRODUCTS) <= 0) error(__LINE__); - if (param(ID::NADDENDS) <= 0) + if (param(ID::NADDENDS) < 0) error(__LINE__); param_bits(ID::PRODUCT_NEGATED, max(param(ID::NPRODUCTS), 1)); param_bits(ID::ADDEND_NEGATED, max(param(ID::NADDENDS), 1)); From 6c76dcec3ea59d31b4fdda59be50ea87f2d8294d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 27 Jan 2025 13:08:44 +0100 Subject: [PATCH 09/11] macc_v2: Fix v2 omissions --- passes/techmap/techmap.cc | 8 ++++---- techlibs/common/techmap.v | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index b77bab69eb0..95c733f6240 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -554,8 +554,8 @@ struct TechmapWorker if (extmapper_name == "maccmap") { log("Creating %s with maccmap.\n", log_id(extmapper_module)); - if (extmapper_cell->type != ID($macc)) - log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type)); + if (!extmapper_cell->type.in(ID($macc), ID($macc_v2))) + log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(extmapper_cell->type)); maccmap(extmapper_module, extmapper_cell); extmapper_module->remove(extmapper_cell); } @@ -600,8 +600,8 @@ struct TechmapWorker } if (extmapper_name == "maccmap") { - if (cell->type != ID($macc)) - log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type)); + if (!cell->type.in(ID($macc), ID($macc_v2))) + log_error("The maccmap mapper can only map $macc/$macc_v2 (not %s) cells!\n", log_id(cell->type)); maccmap(module, cell); } diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 11929614763..75651bb791b 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -290,7 +290,7 @@ module _90_alu (A, B, CI, BI, X, Y, CO); endmodule (* techmap_maccmap *) -(* techmap_celltype = "$macc" *) +(* techmap_celltype = "$macc $macc_v2" *) module _90_macc; endmodule From 916fe998abfca65fabb417fec5737684dd323933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 27 Jan 2025 13:18:35 +0100 Subject: [PATCH 10/11] macc_v2: Add test --- tests/alumacc/basic.ys | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/alumacc/basic.ys diff --git a/tests/alumacc/basic.ys b/tests/alumacc/basic.ys new file mode 100644 index 00000000000..cdd20c47200 --- /dev/null +++ b/tests/alumacc/basic.ys @@ -0,0 +1,61 @@ +read_verilog < Date: Mon, 27 Jan 2025 15:52:49 +0100 Subject: [PATCH 11/11] macc_v2: Relax and fix cell check --- kernel/macc.h | 2 +- kernel/rtlil.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/macc.h b/kernel/macc.h index 5cf8d634dfa..5fc6036ed1e 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -231,7 +231,7 @@ struct Macc if (a_signed.empty()) a_signed = {RTLIL::Sx}; if (b_signed.empty()) - a_signed = {RTLIL::Sx}; + b_signed = {RTLIL::Sx}; if (c_signed.empty()) c_signed = {RTLIL::Sx}; if (a_widths.empty()) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index d197660bd78..3b9a4a8b11c 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1468,7 +1468,7 @@ namespace { } if (cell->type == ID($macc_v2)) { - if (param(ID::NPRODUCTS) <= 0) + if (param(ID::NPRODUCTS) < 0) error(__LINE__); if (param(ID::NADDENDS) < 0) error(__LINE__);