From fedefa26bccaf023fdf4ee82d51fe63ecdbf6191 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Wed, 6 Sep 2023 16:35:17 -0700 Subject: [PATCH 01/13] multpass -- create Booth Encoded multipliers for --- passes/techmap/Makefile.inc | 1 + passes/techmap/multpass.cc | 1541 +++++++++++++++++++++++++++++++++++ 2 files changed, 1542 insertions(+) create mode 100644 passes/techmap/multpass.cc diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 1b834fabc..8b0b2aa23 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -4,6 +4,7 @@ OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o OBJS += passes/techmap/maccmap.o +OBJS += passes/techmap/multpass.o OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) diff --git a/passes/techmap/multpass.cc b/passes/techmap/multpass.cc new file mode 100644 index 000000000..e55a04414 --- /dev/null +++ b/passes/techmap/multpass.cc @@ -0,0 +1,1541 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + MultPass + -------- + + Replace $mul with booth encoded multipliers. Two different + architectures used for signed/unsigned. + + References: + Signed architecture: A Low Power Radix-4 Booth Multipliers with Pre-Encoded Mechanism, IEEE Access + https://ieeexplore.ieee.org/document/9121226 + + Unsigned architecture: Gary Bewick, Fast Multiplication algorithms and implementation. Stanford PhD: + http://i.stanford.edu/pub/cstr/reports/csl/tr/94/617/CSL-TR-94-617.pdf + + How to use: + Add multpass to your yosys script eg: + + read_verilog smultiply5_rtl.v + opt + wreduce + opt + multpass + alumacc + maccmap + opt + techmap -map ./techmap.v + dfflibmap -liberty NangateOpenCellLibrary_typical.lib + abc -liberty NangateOpenCellLibrary_typical.lib + stat -liberty NangateOpenCellLibrary_typical.lib + write_verilog -norename booth_final.v +*/ + +#include "kernel/sigtools.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct MultPassWorker { + + RTLIL::Module *module; + SigMap sigmap; + int booth_counter; + + MultPassWorker(RTLIL::Module *module) : module(module), sigmap(module) { booth_counter = 0; } + + // Helper routines for building architecture subcomponents + + void connect_sigSpecToWire(RTLIL::Wire *ip, const SigSpec &v) + { + auto g = module->addCell(NEW_ID, ID($pos)); + g->setParam(ID::A_WIDTH, 1); + g->setParam(ID::Y_WIDTH, 1); + g->setParam(ID::A_SIGNED, false); + g->setPort(ID::A, ip); + g->setPort(ID::Y, v); + } + + RTLIL::Wire *mk_wireFromSigSpec(const SigSpec &v) + { + + auto g = module->addCell(NEW_ID, ID($pos)); + Wire *ret = module->addWire(NEW_ID, 1); + g->setPort(ID::A, v); + g->setPort(ID::Y, ret); + g->setParam(ID::A_WIDTH, 1); + g->setParam(ID::Y_WIDTH, 1); + g->setParam(ID::A_SIGNED, false); + return ret; + } + + // fuse wires. + void join_wires_with_buffer(RTLIL::Wire *ip, RTLIL::Wire *op) + { + std::string wire_name = "join_"; + auto g = module->addCell(new_id(wire_name, __LINE__, ""), ID($pos)); + g->setParam(ID::A_WIDTH, 1); + g->setParam(ID::Y_WIDTH, 1); + g->setParam(ID::A_SIGNED, false); + g->setPort(ID::A, ip); + g->setPort(ID::Y, op); + } + + // Unary gate + RTLIL::Wire *mk_ugate1(const RTLIL::IdString &red_typ, std::string &name, RTLIL::Wire *ip1, std::string &op_name) + { + std::string op_wire_name; + if (op_name.empty()) + op_wire_name = name + "_o"; + else + op_wire_name = op_name; + RTLIL::Wire *ret = module->addWire(new_id(op_wire_name, __LINE__, ""), 1); + auto g = module->addCell(new_id(name, __LINE__, ""), red_typ); + g->setPort(ID::A, ip1); + g->setPort(ID::Y, ret); + g->setParam(ID::A_SIGNED, false); + g->setParam(ID::A_WIDTH, 1); + g->setParam(ID::Y_WIDTH, 1); + return ret; + } + + // Binary gate + RTLIL::Wire *mk_ugate2(const RTLIL::IdString &red_typ, std::string &name, RTLIL::Wire *ip1, RTLIL::Wire *ip2, std::string &op_name) + { + auto g = module->addCell(new_id(name, __LINE__, ""), red_typ); + std::string op_wire_name; + if (op_name.empty()) + op_wire_name = name + "_o"; + else + op_wire_name = op_name; + + auto ret = module->addWire(new_id(op_wire_name, __LINE__, ""), 1); + + g->setPort(ID::A, ip1); + g->setPort(ID::B, ip2); + g->setPort(ID::Y, ret); + g->setParam(ID::A_SIGNED, false); + g->setParam(ID::B_SIGNED, false); + g->setParam(ID::A_WIDTH, 1); + g->setParam(ID::B_WIDTH, 1); + g->setParam(ID::Y_WIDTH, 1); + return ret; + } + + // Booth unsigned decoder lsb + void BuildBur4d_lsb(std::string &name, RTLIL::Wire *lsb_i, RTLIL::Wire *one_i, RTLIL::Wire *s_i, RTLIL::Wire *&ppij_o, + std::string op_wire_name) + { + std::string empty; + auto and_op = mk_ugate2(ID($and), name, lsb_i, one_i, empty); + ppij_o = mk_ugate2(ID($xor), name, and_op, s_i, op_wire_name); + } + + // Booth unsigned radix4 decoder + void BuildBur4d_n(std::string &name, RTLIL::Wire *yn_i, RTLIL::Wire *ynm1_i, RTLIL::Wire *one_i, RTLIL::Wire *two_i, RTLIL::Wire *s_i, + RTLIL::Wire *&ppij_o) + { + // ppij = ((yn & one) | (ynm1 & two)) ^ s; + std::string empty; + auto an1 = mk_ugate2(ID($and), name, yn_i, one_i, empty); + auto an2 = mk_ugate2(ID($and), name, ynm1_i, two_i, empty); + auto or1 = mk_ugate2(ID($or), name, an1, an2, empty); + ppij_o = mk_ugate2(ID($xor), name, s_i, or1, empty); + } + + // Booth unsigned radix4 decoder + void BuildBur4d_msb(std::string &name, RTLIL::Wire *msb_i, RTLIL::Wire *two_i, RTLIL::Wire *s_i, RTLIL::Wire *&ppij_o) + { + // ppij = (msb & two) ^ s; + std::string empty; + auto an1 = mk_ugate2(ID($and), name, msb_i, two_i, empty); + ppij_o = mk_ugate2(ID($xor), name, s_i, an1, empty); + } + + // half adder, used in CPA + void BuildHa(std::string &name, RTLIL::Wire *a_i, RTLIL::Wire *b_i, RTLIL::Wire *&s_o, RTLIL::Wire *&c_o) + { + std::string empty; + s_o = mk_ugate2(ID($xor), name, a_i, b_i, empty); + c_o = mk_ugate2(ID($and), name, a_i, b_i, empty); + } + + // Booth unsigned radix 4 encoder + void BuildBur4e(std::string &name, RTLIL::Wire *y0_i, RTLIL::Wire *y1_i, RTLIL::Wire *y2_i, + + RTLIL::Wire *&one_o, RTLIL::Wire *&two_o, RTLIL::Wire *&s_o, RTLIL::Wire *&sb_o) + { + + std::string empty; + one_o = mk_ugate2(ID($xor), name, y0_i, y1_i, empty); + s_o = y2_i; + sb_o = mk_ugate1(ID($not), name, y2_i, empty); + auto inv_y1_xor_y2 = mk_ugate1(ID($not), name, mk_ugate2(ID($xor), name, y1_i, y2_i, empty), empty); + two_o = mk_ugate1(ID($not), name, mk_ugate2(ID($or), name, inv_y1_xor_y2, one_o, empty), empty); + } + + void BuildBr4e(std::string &name, RTLIL::Wire *y2_m1_i, + RTLIL::Wire *y2_i, // y2i + RTLIL::Wire *y2_p1_i, + + RTLIL::Wire *&negi_o, RTLIL::Wire *&twoi_n_o, RTLIL::Wire *&onei_n_o, RTLIL::Wire *&cori_o) + { + + std::string empty; + auto y2_p1_n = mk_ugate1(ID($not), name, y2_p1_i, empty); + auto y2_n = mk_ugate1(ID($not), name, y2_i, empty); + auto y2_m1_n = mk_ugate1(ID($not), name, y2_m1_i, empty); + + // negi_o = y2_p1_i + negi_o = mk_ugate1(ID($pos), name, y2_p1_i, empty); + // twoi_n = ~( + // (y2_p1_n & y2_i & y2_m1_i) | + // (y2_p1 & y2_n & y2_m1_n) + // ) + auto and3_1 = mk_ugate2(ID($and), name, y2_p1_n, mk_ugate2(ID($and), name, y2_i, y2_m1_i, empty), empty); + auto and3_2 = mk_ugate2(ID($and), name, y2_p1_i, mk_ugate2(ID($and), name, y2_n, y2_m1_n, empty), empty); + + twoi_n_o = mk_ugate1(ID($not), name, mk_ugate2(ID($or), name, and3_1, and3_2, empty), empty); + // onei_n = ~(y2_m1_i ^ y2_i); + onei_n_o = mk_ugate1(ID($not), name, mk_ugate2(ID($xor), name, y2_m1_i, y2_i, empty), empty); + // cori = (y2_m1_n | y2_n) & y2_p1_i; + cori_o = mk_ugate2(ID($and), name, y2_p1_i, mk_ugate2(ID($or), name, y2_m1_n, y2_n, empty), empty); + } + + // + // signed booth radix 4 decoder + // + void BuildBr4d(std::string &name, RTLIL::Wire *nxj_m1_i, RTLIL::Wire *twoi_n_i, RTLIL::Wire *xj_i, RTLIL::Wire *negi_i, RTLIL::Wire *onei_n_i, + + RTLIL::Wire *&ppij_o, RTLIL::Wire *&nxj_o) + { + + std::string empty; + // nxj_in = xnor(xj,negi) + // nxj_o = xnj_in, + // ppij = ~( (nxj_m1_i | twoi_n_i) & (nxj_int | onei_n_i)); + nxj_o = mk_ugate2(ID($xnor), name, xj_i, negi_i, empty); + RTLIL::Wire *or1 = mk_ugate2(ID($or), name, nxj_m1_i, twoi_n_i, empty); + RTLIL::Wire *or2 = mk_ugate2(ID($or), name, nxj_o, onei_n_i, empty); + ppij_o = mk_ugate1(ID($not), name, mk_ugate2(ID($and), name, or1, or2, empty), empty); + } + + /* + In signed case 1st two bits best realised + using non-booth encoded logic. We can save a booth + encoder for the first couple of bits. + */ + void BuildBoothQ1(std::string &name, RTLIL::Wire *negi_i, RTLIL::Wire *cori_i, RTLIL::Wire *x0_i, RTLIL::Wire *x1_i, RTLIL::Wire *y0_i, + RTLIL::Wire *y1_i, + + RTLIL::Wire *&nxj_o, RTLIL::Wire *&cor_o, RTLIL::Wire *&pp0_o, RTLIL::Wire *&pp1_o + + ) + { + /* + assign NXJO = ~(X1 ^ NEGI); + assign PP0 = (X0 & Y0); + //and terms for multiply + wire pp1_1_int = X1 & Y0; + wire pp1_2_int = X0 & Y1; + //sum generation for pp[1] + assign PP1 = pp1_1_int ^ pp1_2_int; + //correction propagation + assign CORO = (~PP1 & ~PP0)? CORI : 1'b0; + */ + std::string empty; + nxj_o = mk_ugate2(ID($xnor), name, x1_i, negi_i, empty); + pp0_o = mk_ugate2(ID($and), name, x0_i, y0_i, empty); + RTLIL::Wire *pp1_1_int = mk_ugate2(ID($and), name, x1_i, y0_i, empty); + RTLIL::Wire *pp1_2_int = mk_ugate2(ID($and), name, x0_i, y1_i, empty); + pp1_o = mk_ugate2(ID($xor), name, pp1_1_int, pp1_2_int, empty); + + RTLIL::Wire *pp1_nor_pp0 = mk_ugate1(ID($not), name, mk_ugate2(ID($or), name, pp1_o, pp0_o, empty), empty); + cor_o = mk_ugate2(ID($and), name, pp1_nor_pp0, cori_i, empty); + } + + void run() + { + log("Extracting $mul cells in module %s and generating Booth Realization:\n", log_id(module)); + for (auto cell : module->selected_cells()) { + if (cell->type.in(ID($mul))) { + RTLIL::SigSpec A = sigmap(cell->getPort(ID::A)); + RTLIL::SigSpec B = sigmap(cell->getPort(ID::B)); + RTLIL::SigSpec Y = sigmap(cell->getPort(ID::Y)); + if (GetSize(A) >= 4 && GetSize(B) >= 4 && GetSize(Y) >= 8 && + ((cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) || + (!cell->getParam(ID::A_SIGNED).as_bool() && !cell->getParam(ID::B_SIGNED).as_bool()))) { + bool is_signed = false; + if (cell->getParam(ID::A_SIGNED).as_bool()) { + log(" By passing macc inferencing for signed multiplier -- generating booth\n"); + is_signed = true; + } else + log(" By passing macc inferencing for unsigned multiplier -- generating booth\n"); + + if (is_signed == false) /* unsigned multiplier */ { + int x_sz = GetSize(A); + int y_sz = GetSize(B); + int z_sz = GetSize(Y); + + // create a buffer for each ip + std::string buf_name = "u_mult_multiplicand_buf_"; + auto A_Buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + A_Buf->setParam(ID::A_WIDTH, x_sz); + A_Buf->setParam(ID::Y_WIDTH, x_sz); + A_Buf->setPort(ID::A, SigSpec(A)); + A_Buf->setParam(ID::A_SIGNED, false); + + std::string wire_name = "u_mult_A"; + auto A_Wire = module->addWire(new_id(wire_name, __LINE__, ""), x_sz); + + A_Buf->setPort(ID::Y, A_Wire); + + buf_name = "u_mult_multiplier_buf_"; + auto B_Buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + B_Buf->setParam(ID::A_WIDTH, y_sz); + B_Buf->setParam(ID::Y_WIDTH, y_sz); + B_Buf->setPort(ID::A, SigSpec(B)); + B_Buf->setParam(ID::A_SIGNED, false); + + wire_name = "u_mult_B"; + auto B_Wire = module->addWire(new_id(wire_name, __LINE__, ""), y_sz); + B_Buf->setPort(ID::Y, B_Wire); + + buf_name = "u_mult_result_buf_"; + auto Z_Buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + Z_Buf->setParam(ID::A_WIDTH, z_sz); + Z_Buf->setParam(ID::Y_WIDTH, z_sz); + Z_Buf->setPort(ID::Y, SigSpec(Y)); + Z_Buf->setParam(ID::A_SIGNED, false); + wire_name = "u_mult_Z"; + auto Z_Wire = module->addWire(new_id(wire_name, __LINE__, ""), z_sz); + Z_Buf->setPort(ID::A, Z_Wire); + + CreateBoothUMult(module, x_sz, y_sz, z_sz, + A_Wire, // multiplicand + B_Wire, // multiplier(scanned) + Z_Wire // result + ); + module->remove(cell); + booth_counter++; + continue; + } + + else /*signed multiplier */ { + int x_sz = GetSize(A); + int y_sz = GetSize(B); + int z_sz = GetSize(Y); + + // make wire of correct size to feed multiplier + Wire *expanded_A = module->addWire(NEW_ID, x_sz); + + Wire *expanded_B = module->addWire(NEW_ID, y_sz); + + std::string buf_name = "expand_a_buf_"; + auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setParam(ID::A_WIDTH, x_sz); + buf->setParam(ID::Y_WIDTH, x_sz); + buf->setPort(ID::A, SigSpec(A)); + buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setPort(ID::Y, SigSpec(expanded_A)); + + buf_name = "expand_b_buf_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, SigSpec(B)); + buf->setParam(ID::A_WIDTH, y_sz); + buf->setParam(ID::Y_WIDTH, y_sz); + + buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setPort(ID::Y, SigSpec(expanded_B)); + + // + // Make a wire to take the expanded output + // wires + // + + Wire *expanded_Y = module->addWire(NEW_ID, (is_signed == false ? z_sz + 2 : z_sz)); + CreateBoothSMult(module, x_sz, y_sz, (is_signed == false ? z_sz + 2 : z_sz), + expanded_A, // multiplicand + expanded_B, // multiplier(scanned) + expanded_Y // result + ); + // now connect the expanded_Y with a tap to fill out sig Spec Y + + buf_name = "reducer_buf_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, expanded_Y); + buf->setParam(ID::A_WIDTH, is_signed == false ? z_sz + 2 : z_sz); + buf->setParam(ID::Y_WIDTH, z_sz); + buf->setParam(ID::A_SIGNED, is_signed ? true : false); + // wire in output Y + buf->setPort(ID::Y, SigSpec(Y)); + + // kill original multiplier + module->remove(cell); + booth_counter++; + continue; + } + } + } + } + } + + /* + Build Unsigned Multiplier. + ------------------------- + Create a booth unsigned multiplier. + Uses a generic booth multiplier with + extra row of decoders and extended multiplier + */ + + void CreateBoothUMult(RTLIL::Module *module, int x_sz, int y_sz, int z_sz, + RTLIL::Wire *X, // multiplicand + RTLIL::Wire *Y, // multiplier + RTLIL::Wire *Z) + { // result + + std::vector one_int; + std::vector two_int; + std::vector s_int; + std::vector sb_int; + int encoder_count = 0; + + BuildBoothUMultEncoders(Y, y_sz, one_int, two_int, s_int, sb_int, module, encoder_count); + + // Build the decoder rows + // format of each Partial product to be passed to CSA + // tree builder: + // + // Bits to be added + // Shift + // Sign bit to be added + // + std::vector, int, RTLIL::Wire *>> ppij_int; + + static int constant_ix; + constant_ix++; + std::string buf_name = "constant_buf_" + std::to_string(constant_ix); + auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + RTLIL::Wire *constant_one = module->addWire(new_id(buf_name, __LINE__, ""), 1); + buf->setPort(ID::A, State::S1); + buf->setParam(ID::A_WIDTH, 1); + buf->setParam(ID::Y_WIDTH, 1); + buf->setParam(ID::A_SIGNED, true); + buf->setPort(ID::Y, constant_one); + + constant_ix++; + buf_name = "constant_buf_" + std::to_string(constant_ix); + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + RTLIL::Wire *constant_zero = module->addWire(new_id(buf_name, __LINE__, ""), 1); + buf->setPort(ID::A, State::S0); + buf->setParam(ID::A_WIDTH, 1); + buf->setParam(ID::Y_WIDTH, 1); + buf->setParam(ID::A_SIGNED, true); + buf->setPort(ID::Y, constant_zero); + + // Row 0: special case 1. Format S/.S.S.C.Data + std::vector ppij_row_0; + BuildBoothUMultDecoderRow0(module, X, s_int, sb_int, one_int, two_int, ppij_row_0); + + // data, shift, sign + ppij_int.push_back(std::make_tuple(ppij_row_0, 0, s_int[0])); + + for (int i = 1; i < encoder_count - 2; i++) { + // format 1,S.Data.shift = encoder_ix*2,sign = sb_int[i] + std::vector ppij_row_n; + + BuildBoothUMultDecoderRowN(module, + X, // multiplicand + one_int[i], two_int[i], s_int[i], sb_int[i], ppij_row_n, constant_one, i, + false, // include sign + false // include constant + ); + // data, shift, sign + ppij_int.push_back(std::make_tuple(ppij_row_n, i * 2, s_int[i])); + } + + // Build second to last row + // format S/,Data + sign bit + std::vector ppij_row_em1; + BuildBoothUMultDecoderRowN(module, X, one_int[encoder_count - 2], two_int[encoder_count - 2], s_int[encoder_count - 2], + sb_int[encoder_count - 2], ppij_row_em1, constant_one, encoder_count - 2, + false, // include sign + true // no constant + ); + ppij_int.push_back(std::make_tuple(ppij_row_em1, (encoder_count - 2) * 2, s_int[encoder_count - 2])); + // Build last row + // format Data + sign bit + std::vector ppij_row_e; + BuildBoothUMultDecoderRowN(module, X, one_int[encoder_count - 1], two_int[encoder_count - 1], s_int[encoder_count - 1], + sb_int[encoder_count - 1], ppij_row_e, constant_one, encoder_count - 1, + true, // no sign + true // no constant + ); + ppij_int.push_back(std::make_tuple(ppij_row_e, (encoder_count - 1) * 2, s_int[encoder_count - 1])); + + // Debug dump out partial products + // DebugDumpPP(ppij_int); + + // Summation of Partial Products (Wallace Tree) + std::vector> aligned_pp; + aligned_pp.resize(encoder_count + 1); // make an entirely redundant row + // just for sign bit in lsb. (We then filter this out). + + // resize all to be same size as z + for (int i = 0; i < encoder_count + 1; i++) + aligned_pp[i].resize(z_sz); + + AlignPP(x_sz, z_sz, ppij_int, aligned_pp); + + // Debug: dump out aligned partial products. + // Later on yosys will clean up unused constants + // DebugDumpAlignPP(aligned_pp); + + std::vector s_vec; + std::vector c_vec; + std::vector> debug_csa_trees; + + debug_csa_trees.resize(z_sz); + + BuildCSATree(module, aligned_pp, s_vec, c_vec, debug_csa_trees); + + // Debug code: Dump out the csa trees + // DumpCSATrees(debug_csa_trees); + // Build the CPA to do the final accumulation. + BuildCPA(module, s_vec, c_vec, Z); + } + + /* + Build Row 0 of decoders + */ + + void BuildBoothUMultDecoderRow0(RTLIL::Module *module, + RTLIL::Wire *X, // multiplicand + std::vector &s_int, std::vector &sb_int, std::vector &one_int, + std::vector &two_int, std::vector &ppij_vec) + { + (void)module; + int x_sz = GetSize(X); + + // lsb + std::string dec_name = "row0_lsb_dec"; + + RTLIL::Wire *ppij; + std::string ppij_name = "ppij_0_0"; + BuildBur4d_lsb(dec_name, mk_wireFromSigSpec(SigSpec(X, 0, 1)), one_int[0], s_int[0], ppij, ppij_name); + ppij_vec.push_back(ppij); + + // 1..xsize -1 + for (int i = 1; i < x_sz; i++) { + dec_name = "row0_dec_" + std::to_string(i); + RTLIL::Wire *ppij; + BuildBur4d_n(dec_name, mk_wireFromSigSpec(SigSpec(X, i, 1)), mk_wireFromSigSpec(SigSpec(X, i - 1, 1)), one_int[0], two_int[0], + s_int[0], ppij); + ppij_vec.push_back(ppij); + } + + // The redundant bit. Duplicate decoding of last bit. + dec_name = "row0_dec_msb"; + BuildBur4d_msb(dec_name, mk_wireFromSigSpec(SigSpec(X, x_sz - 1, 1)), two_int[0], s_int[0], ppij); + ppij_vec.push_back(ppij); + + // append the sign bits + ppij_vec.push_back(s_int[0]); + ppij_vec.push_back(s_int[0]); + ppij_vec.push_back(sb_int[0]); + } + + // Build a generic row of decoders. + + void BuildBoothUMultDecoderRowN(RTLIL::Module *module, + RTLIL::Wire *X, // multiplicand + RTLIL::Wire *one_int, RTLIL::Wire *two_int, RTLIL::Wire *s_int, RTLIL::Wire *sb_int, + std::vector &ppij_vec, RTLIL::Wire *constant_one, int row_ix, bool no_sign, bool no_constant) + { + (void)module; + int x_sz = GetSize(X); + + // lsb + std::string ppij_name = "ppij_" + std::to_string(row_ix) + "_0"; + RTLIL::Wire *ppij = nullptr; + std::string empty; + std::string dec_name = "row" + std::to_string(row_ix) + "_lsb_dec"; + BuildBur4d_lsb(dec_name, mk_wireFromSigSpec(SigSpec(X, 0, 1)), one_int, s_int, ppij, empty); + + ppij_vec.push_back(ppij); + + // core bits + for (int i = 1; i < x_sz; i++) { + + dec_name = "row_" + std::to_string(row_ix) + "_dec_" + std::to_string(i); + RTLIL::Wire *ppij = nullptr; + BuildBur4d_n(dec_name, mk_wireFromSigSpec(SigSpec(X, i, 1)), mk_wireFromSigSpec(SigSpec(X, i - 1, 1)), one_int, two_int, + s_int, ppij); + ppij_vec.push_back(ppij); + } + + // redundant bit + + dec_name = "row_dec_red"; + BuildBur4d_msb(dec_name, mk_wireFromSigSpec(SigSpec(X, x_sz - 1, 1)), two_int, s_int, ppij); + ppij_vec.push_back(ppij); + + // sign bit + if (no_sign == false) // if no sign is false then make a sign bit + ppij_vec.push_back(sb_int); + + // constant bit + if (no_constant == false) { // if non constant is false make a constant bit + ppij_vec.push_back(constant_one); + } + } + + void DebugDumpAlignPP(std::vector> &aligned_pp) + { + printf("Aligned & Padded Partial products\n"); + int pp_ix = 0; + for (auto pp_row : aligned_pp) { + printf("PP_%d \t", pp_ix); + for (unsigned i = 0; i < pp_row.size(); i++) + printf("[%d] %s ", i, pp_row[i] == nullptr ? " 0 " : pp_row[i]->name.c_str()); + printf("\n"); + pp_ix++; + } + } + + // Debug routines to inspect intermediate results + void DebugDumpPP(std::vector, int, RTLIL::Wire *>> &ppij_int) + { + printf("Debug dump of partial products\n"); + int pp_ix = 0; + + for (auto pp : ppij_int) { + int shift = get<1>(pp); + RTLIL::Wire *sign_bit = get<2>(pp); + + printf("PP %d\n", pp_ix); + printf("\tShift %d\n", shift); + printf("\tData (0 lsb)\n\t"); + int ix = 0; + + for (auto pp_wire : get<0>(pp)) { + RTLIL::IdString wire_name = pp_wire->name; + + printf(" [%d]:%s ", ix, wire_name.c_str()); + ix++; + } + printf("\n"); + printf("\tSign bit to add in: %s\n", sign_bit->name.c_str()); + + pp_ix++; + } + } + + void DumpCSATrees(std::vector> &debug_csa_trees) + { + int i = 0; + for (auto csa_tree : debug_csa_trees) { + printf("CSA Tree column %d\n", i); + int ix = 0; + for (auto csa_elem : csa_tree) { + printf("\tCell %d %s type %s\n", ix, csa_elem->name.c_str(), csa_elem->type.c_str()); + if (csa_elem->getPort(ID::A) == State::S0) + printf("\tA set to constant 0\n"); + else if (csa_elem->getPort(ID::A) == State::S1) + printf("\tA set to constant 1\n"); + else + printf("\tA driven by %s\n", csa_elem->getPort(ID::A).as_wire()->name.c_str()); + + if (csa_elem->getPort(ID::B) == State::S0) + printf("\tB set to constant 0\n"); + else if (csa_elem->getPort(ID::B) == State::S1) + printf("\tB set to constant 1\n"); + else + printf("\tB driven by %s\n", csa_elem->getPort(ID::B).as_wire()->name.c_str()); + + if (csa_elem->getPort(ID::C) == State::S0) + printf("\tC set to constant 0\n"); + else if (csa_elem->getPort(ID::C) == State::S1) + printf("\tC set to constant 1\n"); + else + printf("\tC driven by %s\n", csa_elem->getPort(ID::C).as_wire()->name.c_str()); + + printf("Carry out: %s\n", csa_elem->getPort(ID::X).as_wire()->name.c_str()); + printf("Sum out: %s\n", csa_elem->getPort(ID::Y).as_wire()->name.c_str()); + + ix++; + } + i++; + } + } + + void BuildCSATree(RTLIL::Module *module, std::vector> &bits_to_reduce, std::vector &s_vec, + std::vector &c_vec, std::vector> &debug_csa_trees) + { + + if (!(bits_to_reduce.size() > 0)) + return; + + int column_size = bits_to_reduce[0].size(); + int row_size = bits_to_reduce.size(); + std::vector carry_bits_to_add_to_next_column; + + for (int column_ix = 0; column_ix < column_size; column_ix++) { + + // get the bits in this column. + std::vector column_bits; + for (int row_ix = 0; row_ix < row_size; row_ix++) { + if (bits_to_reduce[row_ix].at(column_ix)) + column_bits.push_back(bits_to_reduce[row_ix].at(column_ix)); + } + for (auto c : carry_bits_to_add_to_next_column) { +#ifdef DEBUG_CSA + printf("\t Propagating column bit %s to column %d from column %d\n", c->name.c_str(), column_ix, column_ix - 1); +#endif + column_bits.push_back(c); + } + + carry_bits_to_add_to_next_column.resize(0); + +#ifdef DEBUG_CSA + printf("Column %d Reducing %d bits\n", column_ix, column_bits.size()); + for (auto b : column_bits) { + printf("\t %s\n", b->name.c_str()); + } + printf("\n"); +#endif + + RTLIL::Wire *s = nullptr; + RTLIL::Wire *c = nullptr; +#ifdef DEBUG_CSA + int csa_count_before = debug_csa_trees[column_ix].size(); +#endif + + ReduceBits(module, column_ix, column_bits, s, c, carry_bits_to_add_to_next_column, debug_csa_trees); + + s_vec.push_back(s); + c_vec.push_back(c); + +#ifdef DEBUG_CSA + int csa_count_after = debug_csa_trees[column_ix].size(); + + printf("Column %d Created %d csa tree elements\n", column_ix, csa_count_after - csa_count_before); +#endif + } + } + + /* + Alignment: + --------- + + Concept traverse from last row. + Pad row by shift + Add sign bit from prior row to 2 bits right of end of data. + + Example + + SCDDDDDDD- +S + DDDDDDDD_ + + ==> + SCDDDDDDD- + DDDDDDDD_S <-- prior rows sign bit added 2 columns to right on next row. + + Pad out rows with zeros and left the opt pass clean them up. + + */ + void AlignPP(int x_sz, int z_sz, std::vector, int, RTLIL::Wire *>> &ppij_int, + std::vector> &aligned_pp) + { + unsigned aligned_pp_ix = aligned_pp.size() - 1; + + // default is zero for everything (so don't have to think to hard + // about padding). + + for (unsigned i = 0; i < aligned_pp.size(); i++) { + for (int j = 0; j < z_sz; j++) { + aligned_pp[i][j] = nullptr; + } + } + + // for very last row we just have the sign bit + // Note that the aligned_pp is one row bigger + // than the ppij_int. We put the sign bit + // in first column of the last partial product + // which is at index corresponding to size of multiplicand + { + RTLIL::Wire *prior_row_sign = nullptr; + prior_row_sign = get<2>(ppij_int[aligned_pp_ix - 1]); + if (prior_row_sign) { + log_assert(aligned_pp_ix < aligned_pp.size()); + log_assert(x_sz - 1 < (int)(aligned_pp[aligned_pp_ix].size())); + aligned_pp[aligned_pp_ix][x_sz - 1] = prior_row_sign; + } + } + + for (int row_ix = aligned_pp_ix - 1; row_ix >= 0; row_ix--) { + int shift_amount = get<1>(ppij_int[row_ix]); + RTLIL::Wire *prior_row_sign = nullptr; + + // copy in data + unsigned copy_ix = shift_amount; + for (auto w : get<0>(ppij_int[row_ix])) { + if (copy_ix < aligned_pp[row_ix].size()) { + aligned_pp[row_ix][copy_ix] = w; + } + copy_ix++; + } + + // copy in the sign bit from the prior row + if (row_ix > 0) { + // if sign bit on prior row, copy in + // the destination of the sign bit is the (row_ix -1)*2 + // eg destination for sign bit for row 0 is 0. + // eg destination for sign bit for row 1 is 1 + prior_row_sign = get<2>(ppij_int[row_ix - 1]); + copy_ix = (row_ix - 1) * 2; + aligned_pp[row_ix][copy_ix] = prior_row_sign; + } + } + } + + /* + Build a Carry Propagate Adder + ----------------------------- + First build the sum and carry vectors to be added. + Axioms: + c_vec.size() == s_vec.size() + result.size() == s_vec.size() + 2; (assume result is reserved to hold correct size) + */ + void BuildCPA(RTLIL::Module *module, std::vector &s_vec, std::vector &c_vec, RTLIL::Wire *result) + { + + static int cpa_id; + cpa_id++; + + RTLIL::Wire *carry = nullptr; + + log_assert(s_vec.size() == c_vec.size()); + + for (unsigned n = 0; n < s_vec.size(); n++) { + std::string carry_name; + + // Base Case: Bit 0 is sum 0 + if (n == 0) { + std::string buf_name = "base_buf_" + std::to_string(cpa_id) + "_" + std::to_string(n); + auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, s_vec[0]); + buf->setParam(ID::A_WIDTH, 1); + buf->setParam(ID::Y_WIDTH, 1); + buf->setParam(ID::A_SIGNED, false); + buf->setPort(ID::Y, SigSpec(result, 0, 1)); + +#ifdef DEBUG_CPA + printf("CPA bit [%d] Cell %s IP 0 %s \n", n, buf->name.c_str(), s_vec[0]->name.c_str()); +#endif + } + + // + // Base Case + // c,s = ha(s_vec[1],c_vec[0]) + // + else if (n == 1) { + std::string ha_name = "cpa_" + std::to_string(cpa_id) + "_ha_" + std::to_string(n); + RTLIL::Wire *ha_op; + BuildHa(ha_name, s_vec[n], c_vec[n - 1], ha_op, carry); + + connect_sigSpecToWire(ha_op, SigSpec(result, n, 1)); + +#ifdef DEBUG_CPA + printf("CPA bit [%d] Cell %s IPs [%s] [%s] \n", n, ha_cell->name.c_str(), s_vec[n]->name.c_str(), + c_vec[n - 1]->name.c_str()); +#endif + + } + // End Case + else if (n == (unsigned)((s_vec.size() - 1))) { + // Make the carry results.. Two extra bits after fa. + std::string fa_name = "cpa_" + std::to_string(cpa_id) + "_fa_" + std::to_string(n); + auto fa_cell = module->addCell(new_id(fa_name, __LINE__, ""), ID($fa)); + fa_cell->setParam(ID::WIDTH, 1); + carry_name = "cpa_" + std::to_string(cpa_id) + "carry_" + std::to_string(n); + fa_cell->setPort(ID::A, s_vec[n]); + fa_cell->setPort(ID::B, c_vec[n - 1]); + fa_cell->setPort(ID::C, carry); + // wire in result and carry out + fa_cell->setPort(ID::Y, SigSpec(result, n, 1)); + + // make a new carry bit for carry out... + carry = module->addWire(new_id(carry_name, __LINE__, ""), 1); + fa_cell->setPort(ID::X, carry); + +#ifdef DEBUG_CPA + printf("CPA bit [%d] Cell %s IPs [%s] [%s] [%s]\n", n, fa_cell->name.c_str(), s_vec[n]->name.c_str(), + c_vec[n - 1]->name.c_str(), carry->name.c_str()); +#endif + if (n + 1 < (unsigned)(GetSize(result))) { + // Now make a half adder: c_vec[n] = carry + std::string ha_name = "cpa_" + std::to_string(cpa_id) + "_ha_" + std::to_string(n); + RTLIL::Wire *ha_sum; + RTLIL::Wire *ha_carry; + BuildHa(ha_name, c_vec[n], carry, ha_sum, ha_carry); + + if (n + 1 < (unsigned)GetSize(result)) + connect_sigSpecToWire(ha_sum, SigSpec(result, n + 1, 1)); + if (n + 2 < (unsigned)GetSize(result)) + connect_sigSpecToWire(ha_carry, SigSpec(result, n + 2, 1)); + } + } + // Step case + else { + std::string fa_name = "cpa_" + std::to_string(cpa_id) + "_fa_" + std::to_string(n); + auto fa_cell = module->addCell(new_id(fa_name, __LINE__, ""), ID($fa)); + fa_cell->setParam(ID::WIDTH, 1); + + carry_name = "cpa_" + std::to_string(cpa_id) + "carry_" + std::to_string(n); + fa_cell->setPort(ID::A, s_vec[n]); + fa_cell->setPort(ID::B, c_vec[n - 1]); + fa_cell->setPort(ID::C, carry); + // wire in result and carry out + fa_cell->setPort(ID::Y, SigSpec(result, n, 1)); + // make a new carry bit for carry out... + carry = module->addWire(new_id(carry_name, __LINE__, ""), 1); + fa_cell->setPort(ID::X, carry); + +#ifdef DEBUG_CPA + printf("CPA bit [%d] Cell %s IPs [%s] [%s] [%s]\n", n, fa_cell->name.c_str(), s_vec[n]->name.c_str(), + c_vec[n - 1]->name.c_str(), carry->name.c_str()); +#endif + } + } + } + + // Sum the bits in the current column + // Pass the carry bits from each csa to the next + // column for summation. + + void ReduceBits(RTLIL::Module *module, int column_ix, std::vector &column_bits, RTLIL::Wire *&s_result, RTLIL::Wire *&c_result, + std::vector &carry_bits_to_sum, std::vector> &debug_csa_trees) + { + + int csa_ix = 0; + int column_size = column_bits.size(); + static int unique_id = 0; + + unique_id++; + + if (column_size > 0) { + unsigned var_ix = 0; + std::vector first_csa_ips; + // get the first 3 inputs, if possible + for (var_ix = 0; var_ix < column_bits.size() && first_csa_ips.size() != 3; var_ix++) { + if (column_bits[var_ix]) + first_csa_ips.push_back(column_bits[var_ix]); + } + + if (first_csa_ips.size() > 0) { + // build the first csa + std::string csa_name = + "csa_" + std::to_string(column_ix) + "_" + std::to_string(csa_ix) + "_" + std::to_string(unique_id) + "_"; + auto csa = module->addCell(NEW_ID, + // new_id(csa_name, + // __LINE__, + // ""), + ID($fa)); + csa->setParam(ID::WIDTH, 1); + debug_csa_trees[column_ix].push_back(csa); + csa_ix++; + + csa->setPort(ID::A, first_csa_ips[0]); + + if (first_csa_ips.size() > 1) + csa->setPort(ID::B, first_csa_ips[1]); + else + csa->setPort(ID::B, State::S0); + + if (first_csa_ips.size() > 2) + csa->setPort(ID::C, first_csa_ips[2]); + else + csa->setPort(ID::C, State::S0); + + std::string sum_wire_name = "csa_" + std::to_string(column_ix) + "_" + std::to_string(csa_ix) + "_s"; + auto s_wire = module->addWire(new_id(sum_wire_name, __LINE__, ""), 1); + csa->setPort(ID::Y, s_wire); + s_result = s_wire; + std::string carry_wire_name = "csa_" + std::to_string(column_ix) + "_" + std::to_string(csa_ix) + "_c"; + auto c_wire = module->addWire(new_id(carry_wire_name, __LINE__, ""), 1); + csa->setPort(ID::X, c_wire); + c_result = c_wire; + + if (var_ix <= column_bits.size() - 1) + carry_bits_to_sum.push_back(c_wire); + + // Now build the rest of the tree if we can + while (var_ix <= column_bits.size() - 1) { + std::vector csa_ips; + // get the next two variables to sum + for (; var_ix <= column_bits.size() - 1 && csa_ips.size() < 2;) { + // skip any empty bits + if (column_bits[var_ix] != nullptr) + csa_ips.push_back(column_bits[var_ix]); + var_ix++; + } + + if (csa_ips.size() > 0) { + csa_name = "csa_" + std::to_string(column_ix) + "_" + std::to_string(csa_ix); + auto csa = module->addCell(new_id(csa_name, __LINE__, ""), ID($fa)); + csa->setParam(ID::WIDTH, 1); + debug_csa_trees[column_ix].push_back(csa); + + csa_ix++; + // prior level + csa->setPort(ID::A, s_wire); + csa->setPort(ID::B, csa_ips[0]); + if (csa_ips.size() > 1) + csa->setPort(ID::C, csa_ips[1]); + else + csa->setPort(ID::C, State::S0); + + carry_wire_name = "csa_" + std::to_string(column_ix) + "_" + std::to_string(csa_ix) + "_c"; + c_wire = module->addWire(new_id(carry_wire_name, __LINE__, ""), 1); + + if (var_ix <= column_bits.size() - 1) + carry_bits_to_sum.push_back(c_wire); + + sum_wire_name = "csa_" + std::to_string(column_ix) + "_" + std::to_string(csa_ix) + "_s"; + s_wire = module->addWire(new_id(sum_wire_name, __LINE__, ""), 1); + + csa->setPort(ID::X, c_wire); + csa->setPort(ID::Y, s_wire); + + s_result = s_wire; + c_result = c_wire; + } + } + } + } + } + + void BuildBoothUMultEncoders(RTLIL::Wire *Y, int y_sz, std::vector &one_int, std::vector &two_int, + std::vector &s_int, std::vector &sb_int, RTLIL::Module *module, int &encoder_ix) + { + for (int y_ix = 0; y_ix < y_sz;) { + std::string enc_name = "bur_enc_" + std::to_string(encoder_ix) + "_"; + log("Created booth encoder %s\n", enc_name.c_str()); + + std::string two_name = "two_int" + std::to_string(encoder_ix); + two_int.push_back(module->addWire(new_id(two_name, __LINE__, ""), 1)); + + std::string one_name = "one_int" + std::to_string(encoder_ix); + one_int.push_back(module->addWire(new_id(one_name, __LINE__, ""), 1)); + + std::string s_name = "s_int" + std::to_string(encoder_ix); + s_int.push_back(module->addWire(new_id(s_name, __LINE__, ""), 1)); + + std::string sb_name = "sb_int" + std::to_string(encoder_ix); + sb_int.push_back(module->addWire(new_id(sb_name, __LINE__, ""), 1)); + + if (y_ix == 0) { + + BuildBur4e(enc_name, mk_wireFromSigSpec(State::S0), mk_wireFromSigSpec(SigSpec(Y, y_ix, 1)), + mk_wireFromSigSpec(SigSpec(Y, y_ix + 1, 1)), one_int[encoder_ix], two_int[encoder_ix], s_int[encoder_ix], + sb_int[encoder_ix]); + + y_ix = y_ix + 1; + encoder_ix++; + } else { + // + // step case. If multiplier ends on a boundary + // then add an extra booth encoder bounded by + // zeroes to ensure unsigned works. + // + RTLIL::Wire *y0_wire; + RTLIL::Wire *y1_wire; + RTLIL::Wire *y2_wire; + + bool need_padded_cell = false; + + if (y_ix > y_sz - 1) { + y0_wire = mk_wireFromSigSpec(SigSpec(Y, State::S0)); + need_padded_cell = false; + } else { + y0_wire = mk_wireFromSigSpec(SigSpec(Y, y_ix, 1)); + y_ix++; + } + + if (y_ix > y_sz - 1) { + need_padded_cell = false; + y1_wire = mk_wireFromSigSpec(SigSpec(Y, State::S0)); + } else { + y1_wire = mk_wireFromSigSpec(SigSpec(Y, y_ix, 1)); + y_ix++; + } + + if (y_ix > y_sz - 1) { + need_padded_cell = false; + y2_wire = mk_wireFromSigSpec(SigSpec(Y, State::S0)); + } else { + if (y_ix == y_sz - 1) + need_padded_cell = true; + else + need_padded_cell = false; + y2_wire = mk_wireFromSigSpec(SigSpec(Y, y_ix, 1)); + + BuildBur4e(enc_name, y0_wire, y1_wire, y2_wire, one_int[encoder_ix], two_int[encoder_ix], s_int[encoder_ix], + sb_int[encoder_ix]); + } + + encoder_ix++; + + if (need_padded_cell == true) { + + log(" Creating padded encoder for unsigned\n"); + + // make extra encoder cell + // y_ix at y0, rest 0 + + std::string enc_name = "br_enc_pad" + std::to_string(encoder_ix) + "_"; + log("Created (padded) booth encoder %s\n", enc_name.c_str()); + + std::string two_name = "two_int" + std::to_string(encoder_ix); + two_int.push_back(module->addWire(new_id(two_name, __LINE__, ""), 1)); + + std::string one_name = "one_int" + std::to_string(encoder_ix); + one_int.push_back(module->addWire(new_id(two_name, __LINE__, ""), 1)); + + std::string s_name = "s_int" + std::to_string(encoder_ix); + s_int.push_back(module->addWire(new_id(s_name, __LINE__, ""), 1)); + + std::string sb_name = "sb_int" + std::to_string(encoder_ix); + sb_int.push_back(module->addWire(new_id(sb_name, __LINE__, ""), 1)); + + RTLIL::Wire *one_o_int, *two_o_int, *s_o_int, *sb_o_int; + BuildBur4e(enc_name, mk_wireFromSigSpec(SigSpec(Y, y_ix, 1)), mk_wireFromSigSpec(State::S0), + mk_wireFromSigSpec(State::S0), one_o_int, two_o_int, s_o_int, sb_o_int); + + join_wires_with_buffer(one_o_int, one_int[encoder_ix]); + join_wires_with_buffer(two_o_int, two_int[encoder_ix]); + join_wires_with_buffer(s_o_int, s_int[encoder_ix]); + join_wires_with_buffer(sb_o_int, sb_int[encoder_ix]); + y_ix++; + encoder_ix++; + } + } + } + } + + /* + Signed Multiplier + */ + + void CreateBoothSMult(RTLIL::Module *module, int x_sz, int y_sz, int z_sz, RTLIL::Wire *X, RTLIL::Wire *Y, RTLIL::Wire *Z) + { // product + unsigned enc_count = (y_sz / 2) + (((y_sz % 2) != 0) ? 1 : 0); + int dec_count = x_sz + 1; + + int fa_count = x_sz + 4; + int fa_row_count = enc_count - 1; + + log("Signed multiplier generator using low Power Negative First Booth Algorithm. Multiplicand of size %d Multiplier of size %d. " + "Result of size %d. %d encoders %d decoders\n", + x_sz, y_sz, z_sz, enc_count, dec_count); + + RTLIL::Wire *negi_n_int[enc_count]; + RTLIL::Wire *twoi_n_int[enc_count]; + RTLIL::Wire *onei_n_int[enc_count]; + RTLIL::Wire *cori_n_int[enc_count]; + + for (unsigned encoder_ix = 1; encoder_ix <= enc_count; encoder_ix++) { + std::string enc_name = "enc_" + std::to_string(encoder_ix) + "_"; + std::string negi_name = "negi_n_int" + std::to_string(encoder_ix) + "_"; + negi_n_int[encoder_ix - 1] = module->addWire(new_id(negi_name, __LINE__, ""), 1); + std::string twoi_name = "twoi_n_int_" + std::to_string(encoder_ix) + "_"; + twoi_n_int[encoder_ix - 1] = module->addWire(new_id(twoi_name, __LINE__, ""), 1); + std::string onei_name = "onei_n_int_" + std::to_string(encoder_ix) + "_"; + onei_n_int[encoder_ix - 1] = module->addWire(new_id(onei_name, __LINE__, ""), 1); + std::string cori_name = "cori_n_int_" + std::to_string(encoder_ix) + "_"; + cori_n_int[encoder_ix - 1] = module->addWire(new_id(cori_name, __LINE__, ""), 1); + + if (encoder_ix == 1) { + + BuildBr4e(enc_name, mk_wireFromSigSpec(SigSpec(State::S0)), mk_wireFromSigSpec(SigSpec(Y, 0, 1)), + mk_wireFromSigSpec(SigSpec(Y, 1, 1)), + + negi_n_int[encoder_ix - 1], twoi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], + cori_n_int[encoder_ix - 1]); + + } else { + RTLIL::Wire *y1_wire; + RTLIL::Wire *y2_wire; + RTLIL::Wire *y3_wire; + + y1_wire = mk_wireFromSigSpec(SigSpec(Y, ((encoder_ix - 1) * 2 - 1), 1)); //-1 + if ((encoder_ix - 1) * 2 >= (unsigned)y_sz) + y2_wire = mk_wireFromSigSpec(SigSpec(State::S0)); // constant 0 + else + y2_wire = mk_wireFromSigSpec(SigSpec(Y, ((encoder_ix - 1) * 2), 1)); // 0 + + if (((encoder_ix - 1) * 2 + 1) >= (unsigned)y_sz) + y3_wire = mk_wireFromSigSpec(SigSpec(State::S0)); // constant 0 + else + y3_wire = mk_wireFromSigSpec(SigSpec(Y, ((encoder_ix - 1) * 2 + 1), 1)); //+1 + + BuildBr4e(enc_name, y1_wire, y2_wire, y3_wire, + + negi_n_int[encoder_ix - 1], twoi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], + cori_n_int[encoder_ix - 1]); + } + } + // Decoders and PP generation + RTLIL::Wire *PPij[enc_count][dec_count]; + RTLIL::Wire *nxj[enc_count][dec_count]; + for (int encoder_ix = 1; encoder_ix <= (int)enc_count; encoder_ix++) { + for (int decoder_ix = 1; decoder_ix <= dec_count; decoder_ix++) { + std::string ppij_name = "ppij_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; + PPij[encoder_ix - 1][decoder_ix - 1] = module->addWire(new_id(ppij_name, __LINE__, ""), 1); + std::string nxj_name; + if (decoder_ix == 1) + nxj_name = "nxj_pre_dec" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; + else + nxj_name = "nxj_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; + + nxj[encoder_ix - 1][decoder_ix - 1] = module->addWire(new_id(nxj_name, __LINE__, ""), 1); + } + } + + // + // build decoder array + // + + for (int encoder_ix = 1; encoder_ix <= (int)enc_count; encoder_ix++) { + // pre-decoder + std::string pre_dec_name = "pre_dec_" + std::to_string(encoder_ix) + "_"; + + if (encoder_ix == 1) { + // quadrant 1 optimization + } else { + auto cell = module->addCell(new_id(pre_dec_name, __LINE__, ""), ID($_NOT_)); + cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + cell->setPort(ID::A, negi_n_int[encoder_ix - 1]); + cell->setPort(ID::Y, nxj[encoder_ix - 1][0]); + } + + for (int decoder_ix = 1; decoder_ix < dec_count; decoder_ix++) { + // range 1..8 + + // quadrant 1 optimization. + if ((decoder_ix == 1 || decoder_ix == 2) && encoder_ix == 1) + continue; + + std::string dec_name = "dec_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; + + BuildBr4d(dec_name, nxj[encoder_ix - 1][decoder_ix - 1], twoi_n_int[encoder_ix - 1], + mk_wireFromSigSpec(SigSpec(X, decoder_ix - 1, 1)), negi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], + PPij[encoder_ix - 1][decoder_ix - 1], nxj[encoder_ix - 1][decoder_ix]); + } + + // duplicate end for sign fix + // applies to 9th decoder (xsz+1 decoder). + std::string dec_name = "dec_" + std::to_string(encoder_ix) + "_" + std::to_string(x_sz + 1) + "_"; + RTLIL::Wire *unused_op = nullptr; + BuildBr4d(dec_name, nxj[encoder_ix - 1][dec_count - 1], twoi_n_int[encoder_ix - 1], + mk_wireFromSigSpec(SigSpec(X, dec_count - 2, 1)), negi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], + PPij[encoder_ix - 1][dec_count - 1], unused_op); + } + + // + // sum up the partial products + // + int fa_el_ix = 0; + int fa_row_ix = 0; + RTLIL::Wire *fa_sum_n[fa_row_count][fa_count]; + RTLIL::Wire *fa_carry_n[fa_row_count][fa_count]; + + for (fa_row_ix = 0; fa_row_ix < fa_row_count; fa_row_ix++) { + for (fa_el_ix = 0; fa_el_ix < fa_count; fa_el_ix++) { + + std::string fa_sum_name = "fa_sum_n_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_"; + fa_sum_n[fa_row_ix][fa_el_ix] = module->addWire(new_id(fa_sum_name, __LINE__, ""), 1); + std::string fa_carry_name = "fa_carry_n" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_"; + fa_carry_n[fa_row_ix][fa_el_ix] = module->addWire(new_id(fa_carry_name, __LINE__, ""), 1); + } + } + + // full adder creation + std::string bfa_name; + std::string exc_inv_name; + for (fa_row_ix = 0; fa_row_ix < fa_row_count; fa_row_ix++) { + for (fa_el_ix = 0; fa_el_ix < fa_count; fa_el_ix++) { + // base case: 1st row. Inputs from decoders + // Note in rest of tree inputs from prior addition and a decoder + if (fa_row_ix == 0) { + // beginning + // base case: + // first two cells: have B input hooked to 0. + if (fa_el_ix == 0) { + // quadrant 1: we hard code these using non-booth + fa_el_ix++; + + } + // step case + else if (fa_el_ix >= 2 && fa_el_ix <= x_sz) { + // middle (2...x_sz cells) + bfa_name = "bfa_0_step_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; + auto cell = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell->setParam(ID::WIDTH, 1); + cell->setPort(ID::A, PPij[0][fa_el_ix]); + cell->setPort(ID::B, PPij[1][fa_el_ix - 2]); + cell->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + } + // end 3 cells: x_sz+1.2.3 + // + else { + // fa_el_ix = x_sz+1 + bfa_name = "bfa_0_se_0" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; + auto cell1 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell1->setParam(ID::WIDTH, 1); + cell1->setPort(ID::A, PPij[0][x_sz]); + cell1->setPort(ID::B, PPij[1][fa_el_ix - 2]); + cell1->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell1->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell1->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + + // exception:invert ppi + fa_el_ix++; + exc_inv_name = "bfa_0_exc_inv1_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; + auto cellinv1 = module->addCell(new_id(exc_inv_name, __LINE__, ""), ID($_NOT_)); + cellinv1->add_strpool_attribute(ID::src, cellinv1->get_strpool_attribute(ID::src)); + + RTLIL::Wire *d08_inv = module->addWire(NEW_ID, 1); + + cellinv1->setPort(ID::A, PPij[0][dec_count - 1]); + cellinv1->setPort(ID::Y, d08_inv); + + exc_inv_name = "bfa_0_exc_inv2_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; + + auto cellinv2 = module->addCell(new_id(exc_inv_name, __LINE__, ""), ID($_NOT_)); + cellinv2->add_strpool_attribute(ID::src, cellinv2->get_strpool_attribute(ID::src)); + RTLIL::Wire *d18_inv = module->addWire(NEW_ID, 1); + cellinv2->setPort(ID::A, PPij[1][dec_count - 1]); + cellinv2->setPort(ID::Y, d18_inv); + + bfa_name = "bfa_0_se_1_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; + + auto cell2 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell2->setParam(ID::WIDTH, 1); + cell2->setPort(ID::A, d08_inv); + cell2->setPort(ID::B, d18_inv); + cell2->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell2->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell2->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + + // sign extension + fa_el_ix++; + bfa_name = "bfa_0_se_2_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; + auto cell3 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell3->setParam(ID::WIDTH, 1); + cell3->setPort(ID::A, State::S0); + cell3->setPort(ID::B, State::S1); + cell3->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell3->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell3->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + } + } + + // step case: 2nd and rest of rows. (fa_row_ix == 1...n) + // special because these are driven by a decoder and prior fa. + else { + // beginning + if (fa_el_ix == 0) { + // first two cells: have B input hooked to 0. + // column is offset by row_ix*2 + bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_base_" + std::to_string(fa_row_ix) + "_" + + std::to_string(fa_el_ix) + "_L"; + auto cell1 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell1->setParam(ID::WIDTH, 1); + cell1->setPort(ID::A, fa_sum_n[fa_row_ix - 1][2]); // from prior full adder row + cell1->setPort(ID::B, State::S0); + cell1->setPort(ID::C, cori_n_int[fa_row_ix]); + cell1->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell1->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + fa_el_ix++; + + bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_base_" + std::to_string(fa_row_ix) + "_" + + std::to_string(fa_el_ix) + "_L"; + auto cell2 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell2->setParam(ID::WIDTH, 1); + cell2->setPort(ID::A, fa_sum_n[fa_row_ix - 1][3]); // from prior full adder row + cell2->setPort(ID::B, State::S0); + cell2->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell2->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell2->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + } + + else if (fa_el_ix >= 2 && fa_el_ix <= x_sz + 1) { + // middle (2...x_sz+1 cells) + bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_step_" + std::to_string(fa_row_ix) + "_" + + std::to_string(fa_el_ix) + "_L"; + auto cell = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell->setParam(ID::WIDTH, 1); + cell->setPort(ID::A, fa_sum_n[fa_row_ix - 1][fa_el_ix + 2]); + cell->setPort(ID::B, PPij[fa_row_ix + 1][fa_el_ix - 2]); + cell->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + } + + else if (fa_el_ix > x_sz + 1) { + // end two bits: sign extension + std::string se_inv_name; + se_inv_name = "bfa_" + std::to_string(fa_row_ix) + "_se_inv_" + std::to_string(fa_row_ix) + "_" + + std::to_string(fa_el_ix) + "_L"; + auto cellinv = module->addCell(new_id(se_inv_name, __LINE__, ""), ID($_NOT_)); + cellinv->add_strpool_attribute(ID::src, cellinv->get_strpool_attribute(ID::src)); + RTLIL::Wire *d_inv = module->addWire(NEW_ID, 1); + cellinv->setPort(ID::A, PPij[fa_row_ix + 1][dec_count - 1]); + cellinv->setPort(ID::Y, d_inv); + + bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_se_" + std::to_string(fa_row_ix) + "_" + + std::to_string(fa_el_ix) + "_L"; + auto cell1 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell1->setParam(ID::WIDTH, 1); + cell1->setPort(ID::A, fa_carry_n[fa_row_ix - 1][fa_count - 1]); + cell1->setPort(ID::B, d_inv); + cell1->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell1->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell1->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + fa_el_ix++; + + // sign extension + bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_se_" + std::to_string(fa_row_ix) + "_" + + std::to_string(fa_el_ix) + "_L"; + auto cell2 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); + cell2->setParam(ID::WIDTH, 1); + cell2->setPort(ID::A, State::S0); + cell2->setPort(ID::B, State::S1); + cell2->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); + cell2->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); + cell2->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + } + } + } + } + + // instantiate the cpa + RTLIL::Wire *cpa_carry[z_sz]; + + if (x_sz + y_sz != z_sz) + log("Result of multiplier is incomplete with respect to domain of inputs\n"); + + for (int cix = 0; cix < z_sz; cix++) { + std::string cpa_cix_name = "cpa_carry_" + std::to_string(cix) + "_"; + cpa_carry[cix] = module->addWire(new_id(cpa_cix_name, __LINE__, ""), 1); + } + log(" Building cpa array for booth\n"); + for (int cpa_ix = 0; cpa_ix < z_sz; cpa_ix++) { + + // The end case where we pass the last two summands + // from prior row directly to product output + // without using a cpa cell. This is always + // 0,1 index of prior fa row + if (cpa_ix <= fa_row_count * 2 - 1) { + int fa_row_ix = cpa_ix / 2; + + std::string buf_name = + "pp_buf_" + std::to_string(cpa_ix) + "_" + "driven_by_fa_row_" + std::to_string(fa_row_ix) + "_"; + auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, fa_sum_n[fa_row_ix][0]); + buf->setParam(ID::A_WIDTH, 1); + buf->setParam(ID::Y_WIDTH, 1); + buf->setParam(ID::A_SIGNED, true); + buf->setPort(ID::Y, SigSpec(Z, cpa_ix, 1)); + + cpa_ix++; + buf_name = "pp_buf_" + std::to_string(cpa_ix) + "_" + "driven_by_fa_row_" + std::to_string(fa_row_ix) + "_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, fa_sum_n[fa_row_ix][1]); + buf->setParam(ID::A_WIDTH, 1); + buf->setParam(ID::Y_WIDTH, 1); + buf->setParam(ID::A_SIGNED, true); + buf->setPort(ID::Y, SigSpec(Z, cpa_ix, 1)); + } else { + int offset = fa_row_count * 2; + bool base_case = cpa_ix - offset == 0 ? true : false; + std::string cpa_name = "cpa_" + std::to_string(cpa_ix - offset) + "_"; + + RTLIL::Wire *ci_wire; + if (base_case) + ci_wire = cori_n_int[enc_count - 1]; + else + ci_wire = cpa_carry[cpa_ix - offset - 1]; + + RTLIL::Wire *op_wire = module->addWire(NEW_ID, 1); + BuildHa(cpa_name, fa_sum_n[fa_row_count - 1][cpa_ix - offset + 2], ci_wire, op_wire, cpa_carry[cpa_ix - offset]); + connect_sigSpecToWire(op_wire, SigSpec(Z, cpa_ix, 1)); + } + } + + // + // instantiate the quadrant 1 cell. This is the upper right + // quadrant which can be realized using non-booth encoded logic. + // + std::string q1_name = "icb_booth_q1_"; + + RTLIL::Wire *pp0_o_int; + RTLIL::Wire *pp1_o_int; + RTLIL::Wire *nxj_o_int; + RTLIL::Wire *cor_o_int; + + BuildBoothQ1(q1_name, + negi_n_int[0], // negi + cori_n_int[0], // cori + + mk_wireFromSigSpec(SigSpec(X, 0, 1)), // x0 + mk_wireFromSigSpec(SigSpec(X, 1, 1)), // x1 + mk_wireFromSigSpec(SigSpec(Y, 0, 1)), // y0 + mk_wireFromSigSpec(SigSpec(Y, 1, 1)), // y1 + + nxj_o_int, cor_o_int, pp0_o_int, pp1_o_int); + + join_wires_with_buffer(pp0_o_int, fa_sum_n[0][0]); + join_wires_with_buffer(pp1_o_int, fa_sum_n[0][1]); + join_wires_with_buffer(cor_o_int, fa_carry_n[0][1]); + join_wires_with_buffer(nxj_o_int, nxj[0][2]); + } +}; + +struct MultPass : public Pass { + MultPass() : Pass("multpass") {} + void execute(vector args, RTLIL::Design *design) override + { + (void)args; + log("Multiplier Pass. Generating Booth Multiplier structures for signed/unsigned multipliers of 4 bits or more\n"); + for (auto mod : design->selected_modules()) + if (!mod->has_processes_warn()) { + MultPassWorker worker(mod); + worker.run(); + } + } +} MultPass; + +PRIVATE_NAMESPACE_END From 0c2a99ca475dfc912ccc211afb1264a7e8fb8247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 7 Sep 2023 14:46:59 +0200 Subject: [PATCH 02/13] techmap: Test the Booth multiplier --- tests/techmap/booth.ys | 1 + tests/techmap/booth_map_script.ys_ | 1 + 2 files changed, 2 insertions(+) create mode 100644 tests/techmap/booth.ys create mode 100644 tests/techmap/booth_map_script.ys_ diff --git a/tests/techmap/booth.ys b/tests/techmap/booth.ys new file mode 100644 index 000000000..17e202456 --- /dev/null +++ b/tests/techmap/booth.ys @@ -0,0 +1 @@ +test_cell -script booth_map_script.ys_ $mul diff --git a/tests/techmap/booth_map_script.ys_ b/tests/techmap/booth_map_script.ys_ new file mode 100644 index 000000000..0f323bdd8 --- /dev/null +++ b/tests/techmap/booth_map_script.ys_ @@ -0,0 +1 @@ +multpass From 25a33d408280ba496366ef282506521a4caef305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 7 Sep 2023 14:56:56 +0200 Subject: [PATCH 03/13] techmap: Make the Booth test deterministic --- tests/techmap/booth.ys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/techmap/booth.ys b/tests/techmap/booth.ys index 17e202456..f1dce1f3b 100644 --- a/tests/techmap/booth.ys +++ b/tests/techmap/booth.ys @@ -1 +1 @@ -test_cell -script booth_map_script.ys_ $mul +test_cell -s 1694091355 -n 1000 -script booth_map_script.ys_ $mul From 411acc4a0a27a6b3cb198faa63f6e501a1b8df19 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Fri, 8 Sep 2023 13:41:31 -0700 Subject: [PATCH 04/13] Fixed edge size cases for signed/unsigned booth generator --- passes/techmap/multpass.cc | 181 ++++++++++++++++++++++++++----------- 1 file changed, 128 insertions(+), 53 deletions(-) diff --git a/passes/techmap/multpass.cc b/passes/techmap/multpass.cc index e55a04414..9981250ae 100644 --- a/passes/techmap/multpass.cc +++ b/passes/techmap/multpass.cc @@ -295,45 +295,85 @@ struct MultPassWorker { int y_sz = GetSize(B); int z_sz = GetSize(Y); - // create a buffer for each ip - std::string buf_name = "u_mult_multiplicand_buf_"; - auto A_Buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - A_Buf->setParam(ID::A_WIDTH, x_sz); - A_Buf->setParam(ID::Y_WIDTH, x_sz); - A_Buf->setPort(ID::A, SigSpec(A)); - A_Buf->setParam(ID::A_SIGNED, false); + // To simplify the generator size the arguments + // to be the same. Then allow logic synthesis to + // clean things up. Size to biggest - std::string wire_name = "u_mult_A"; - auto A_Wire = module->addWire(new_id(wire_name, __LINE__, ""), x_sz); + int x_sz_revised = x_sz; + int y_sz_revised = y_sz; - A_Buf->setPort(ID::Y, A_Wire); + if (x_sz != y_sz) { + if (x_sz < y_sz) { + if (y_sz % 2 != 0) { + x_sz_revised = y_sz + 1; + y_sz_revised = y_sz + 1; + } else + x_sz_revised = y_sz; - buf_name = "u_mult_multiplier_buf_"; - auto B_Buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - B_Buf->setParam(ID::A_WIDTH, y_sz); - B_Buf->setParam(ID::Y_WIDTH, y_sz); - B_Buf->setPort(ID::A, SigSpec(B)); - B_Buf->setParam(ID::A_SIGNED, false); + log("Resized x to %d ", x_sz_revised); + } else { + if (x_sz % 2 != 0) { + y_sz_revised = x_sz + 1; + x_sz_revised = x_sz + 1; + } else + y_sz_revised = x_sz; + log("Resized y to %d ", y_sz_revised); + } + } else { + if (x_sz % 2 != 0) { + y_sz_revised = y_sz + 1; + x_sz_revised = x_sz + 1; + } + } - wire_name = "u_mult_B"; - auto B_Wire = module->addWire(new_id(wire_name, __LINE__, ""), y_sz); - B_Buf->setPort(ID::Y, B_Wire); + log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0)); - buf_name = "u_mult_result_buf_"; - auto Z_Buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - Z_Buf->setParam(ID::A_WIDTH, z_sz); - Z_Buf->setParam(ID::Y_WIDTH, z_sz); - Z_Buf->setPort(ID::Y, SigSpec(Y)); - Z_Buf->setParam(ID::A_SIGNED, false); - wire_name = "u_mult_Z"; - auto Z_Wire = module->addWire(new_id(wire_name, __LINE__, ""), z_sz); - Z_Buf->setPort(ID::A, Z_Wire); + Wire *expanded_A = module->addWire(NEW_ID, x_sz_revised); + Wire *expanded_B = module->addWire(NEW_ID, y_sz_revised); - CreateBoothUMult(module, x_sz, y_sz, z_sz, - A_Wire, // multiplicand - B_Wire, // multiplier(scanned) - Z_Wire // result + std::string buf_name = "expand_a_buf_"; + auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setParam(ID::A_WIDTH, x_sz); + buf->setParam(ID::Y_WIDTH, x_sz_revised); + buf->setPort(ID::A, SigSpec(A)); + buf->setParam(ID::A_SIGNED, false); + buf->setPort(ID::Y, SigSpec(expanded_A)); + + buf_name = "expand_b_buf_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, SigSpec(B)); + buf->setParam(ID::A_WIDTH, y_sz); + buf->setParam(ID::Y_WIDTH, y_sz_revised); + buf->setParam(ID::A_SIGNED, false); + buf->setPort(ID::Y, SigSpec(expanded_B)); + + // Make sure output domain is big enough to take + // all combinations. + // Later logic synthesis will kill unused + // portions of the output domain. + + unsigned required_op_size = x_sz_revised + y_sz_revised; + + Wire *expanded_Y = module->addWire(NEW_ID, required_op_size); + + CreateBoothUMult(module, x_sz_revised, y_sz_revised, required_op_size, + expanded_A, // multiplicand + expanded_B, // multiplier(scanned) + expanded_Y // result ); + + // now connect the expanded_Y with a tap to fill out sig Spec Y + + log("Adding reducer on output from %u to %u ", required_op_size, z_sz); + buf_name = "reducer_buf_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, expanded_Y); + buf->setParam(ID::A_WIDTH, required_op_size); + buf->setParam(ID::Y_WIDTH, z_sz); // The real user width + buf->setParam(ID::A_SIGNED, false); + // wire in output Y + buf->setPort(ID::Y, SigSpec(Y)); + module->remove(cell); booth_counter++; continue; @@ -344,47 +384,81 @@ struct MultPassWorker { int y_sz = GetSize(B); int z_sz = GetSize(Y); - // make wire of correct size to feed multiplier - Wire *expanded_A = module->addWire(NEW_ID, x_sz); + // To simplify the generator size the arguments + // to be the same. Then allow logic synthesis to + // clean things up. Size to biggest - Wire *expanded_B = module->addWire(NEW_ID, y_sz); + int x_sz_revised = x_sz; + int y_sz_revised = y_sz; + + if (x_sz != y_sz) { + if (x_sz < y_sz) { + if (y_sz % 2 != 0) { + x_sz_revised = y_sz + 1; + y_sz_revised = y_sz + 1; + } else + x_sz_revised = y_sz; + + log("Resized x to %d ", x_sz_revised); + } else { + if (x_sz % 2 != 0) { + y_sz_revised = x_sz + 1; + x_sz_revised = x_sz + 1; + } else + y_sz_revised = x_sz; + log("Resized y to %d ", y_sz_revised); + } + } else { + if (x_sz % 2 != 0) { + y_sz_revised = y_sz + 1; + x_sz_revised = x_sz + 1; + } + } + + log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0)); + + Wire *expanded_A = module->addWire(NEW_ID, x_sz_revised); + Wire *expanded_B = module->addWire(NEW_ID, y_sz_revised); std::string buf_name = "expand_a_buf_"; auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); buf->setParam(ID::A_WIDTH, x_sz); - buf->setParam(ID::Y_WIDTH, x_sz); + buf->setParam(ID::Y_WIDTH, x_sz_revised); buf->setPort(ID::A, SigSpec(A)); - buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setParam(ID::A_SIGNED, true); buf->setPort(ID::Y, SigSpec(expanded_A)); buf_name = "expand_b_buf_"; buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); buf->setPort(ID::A, SigSpec(B)); buf->setParam(ID::A_WIDTH, y_sz); - buf->setParam(ID::Y_WIDTH, y_sz); - - buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setParam(ID::Y_WIDTH, y_sz_revised); + buf->setParam(ID::A_SIGNED, true); buf->setPort(ID::Y, SigSpec(expanded_B)); - // - // Make a wire to take the expanded output - // wires - // + // Make sure output domain is big enough to take + // all combinations. + // Later logic synthesis will kill unused + // portions of the output domain. - Wire *expanded_Y = module->addWire(NEW_ID, (is_signed == false ? z_sz + 2 : z_sz)); - CreateBoothSMult(module, x_sz, y_sz, (is_signed == false ? z_sz + 2 : z_sz), + unsigned required_op_size = x_sz_revised + y_sz_revised; + + Wire *expanded_Y = module->addWire(NEW_ID, required_op_size); + + CreateBoothSMult(module, x_sz_revised, y_sz_revised, required_op_size, expanded_A, // multiplicand expanded_B, // multiplier(scanned) - expanded_Y // result + expanded_Y // result (sized) ); // now connect the expanded_Y with a tap to fill out sig Spec Y + log("Adding reducer on output from %u to %u ", required_op_size, z_sz); buf_name = "reducer_buf_"; buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); buf->setPort(ID::A, expanded_Y); - buf->setParam(ID::A_WIDTH, is_signed == false ? z_sz + 2 : z_sz); - buf->setParam(ID::Y_WIDTH, z_sz); - buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setParam(ID::A_WIDTH, required_op_size); + buf->setParam(ID::Y_WIDTH, z_sz); // The real user width + buf->setParam(ID::A_SIGNED, true); // wire in output Y buf->setPort(ID::Y, SigSpec(Y)); @@ -1451,7 +1525,7 @@ struct MultPassWorker { std::string cpa_cix_name = "cpa_carry_" + std::to_string(cix) + "_"; cpa_carry[cix] = module->addWire(new_id(cpa_cix_name, __LINE__, ""), 1); } - log(" Building cpa array for booth\n"); + log(" Building cpa array for booth for output size %u\n", z_sz); for (int cpa_ix = 0; cpa_ix < z_sz; cpa_ix++) { // The end case where we pass the last two summands @@ -1525,15 +1599,16 @@ struct MultPassWorker { }; struct MultPass : public Pass { - MultPass() : Pass("multpass") {} + MultPass() : Pass("multpass", "Map $mul to booth multipliers") {} void execute(vector args, RTLIL::Design *design) override { (void)args; - log("Multiplier Pass. Generating Booth Multiplier structures for signed/unsigned multipliers of 4 bits or more\n"); + log_header(design, "Executing multpass. Generating Booth Multiplier structures for signed/unsigned multipliers of 4 bits or more\n"); for (auto mod : design->selected_modules()) if (!mod->has_processes_warn()) { MultPassWorker worker(mod); worker.run(); + log_header(design, "Created %d booth multipliers.\n", worker.booth_counter); } } } MultPass; From 6d29dc659b19a016d33f75561c6f2dd96b42ca8f Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Fri, 8 Sep 2023 15:34:56 -0700 Subject: [PATCH 05/13] renamed passname to booth, replaced connect_sigSpecToWire with connect, updated test script --- passes/techmap/multpass.cc | 244 ++++++++--------------------- tests/techmap/booth_map_script.ys_ | 2 +- 2 files changed, 68 insertions(+), 178 deletions(-) diff --git a/passes/techmap/multpass.cc b/passes/techmap/multpass.cc index 9981250ae..c0e56f2ad 100644 --- a/passes/techmap/multpass.cc +++ b/passes/techmap/multpass.cc @@ -64,16 +64,6 @@ struct MultPassWorker { // Helper routines for building architecture subcomponents - void connect_sigSpecToWire(RTLIL::Wire *ip, const SigSpec &v) - { - auto g = module->addCell(NEW_ID, ID($pos)); - g->setParam(ID::A_WIDTH, 1); - g->setParam(ID::Y_WIDTH, 1); - g->setParam(ID::A_SIGNED, false); - g->setPort(ID::A, ip); - g->setPort(ID::Y, v); - } - RTLIL::Wire *mk_wireFromSigSpec(const SigSpec &v) { @@ -274,7 +264,6 @@ struct MultPassWorker { void run() { - log("Extracting $mul cells in module %s and generating Booth Realization:\n", log_id(module)); for (auto cell : module->selected_cells()) { if (cell->type.in(ID($mul))) { RTLIL::SigSpec A = sigmap(cell->getPort(ID::A)); @@ -290,183 +279,92 @@ struct MultPassWorker { } else log(" By passing macc inferencing for unsigned multiplier -- generating booth\n"); - if (is_signed == false) /* unsigned multiplier */ { - int x_sz = GetSize(A); - int y_sz = GetSize(B); - int z_sz = GetSize(Y); + int x_sz = GetSize(A); + int y_sz = GetSize(B); + int z_sz = GetSize(Y); - // To simplify the generator size the arguments - // to be the same. Then allow logic synthesis to - // clean things up. Size to biggest + // To simplify the generator size the arguments + // to be the same. Then allow logic synthesis to + // clean things up. Size to biggest - int x_sz_revised = x_sz; - int y_sz_revised = y_sz; + int x_sz_revised = x_sz; + int y_sz_revised = y_sz; - if (x_sz != y_sz) { - if (x_sz < y_sz) { - if (y_sz % 2 != 0) { - x_sz_revised = y_sz + 1; - y_sz_revised = y_sz + 1; - } else - x_sz_revised = y_sz; + if (x_sz != y_sz) { + if (x_sz < y_sz) { + if (y_sz % 2 != 0) { + x_sz_revised = y_sz + 1; + y_sz_revised = y_sz + 1; + } else + x_sz_revised = y_sz; - log("Resized x to %d ", x_sz_revised); - } else { - if (x_sz % 2 != 0) { - y_sz_revised = x_sz + 1; - x_sz_revised = x_sz + 1; - } else - y_sz_revised = x_sz; - log("Resized y to %d ", y_sz_revised); - } } else { if (x_sz % 2 != 0) { - y_sz_revised = y_sz + 1; + y_sz_revised = x_sz + 1; x_sz_revised = x_sz + 1; - } + } else + y_sz_revised = x_sz; } + } else { + if (x_sz % 2 != 0) { + y_sz_revised = y_sz + 1; + x_sz_revised = x_sz + 1; + } + } - log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0)); + log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0)); - Wire *expanded_A = module->addWire(NEW_ID, x_sz_revised); - Wire *expanded_B = module->addWire(NEW_ID, y_sz_revised); + Wire *expanded_A = module->addWire(NEW_ID, x_sz_revised); + Wire *expanded_B = module->addWire(NEW_ID, y_sz_revised); - std::string buf_name = "expand_a_buf_"; - auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setParam(ID::A_WIDTH, x_sz); - buf->setParam(ID::Y_WIDTH, x_sz_revised); - buf->setPort(ID::A, SigSpec(A)); - buf->setParam(ID::A_SIGNED, false); - buf->setPort(ID::Y, SigSpec(expanded_A)); + std::string buf_name = "expand_a_buf_"; + auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setParam(ID::A_WIDTH, x_sz); + buf->setParam(ID::Y_WIDTH, x_sz_revised); + buf->setPort(ID::A, SigSpec(A)); + buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setPort(ID::Y, SigSpec(expanded_A)); - buf_name = "expand_b_buf_"; - buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setPort(ID::A, SigSpec(B)); - buf->setParam(ID::A_WIDTH, y_sz); - buf->setParam(ID::Y_WIDTH, y_sz_revised); - buf->setParam(ID::A_SIGNED, false); - buf->setPort(ID::Y, SigSpec(expanded_B)); + buf_name = "expand_b_buf_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, SigSpec(B)); + buf->setParam(ID::A_WIDTH, y_sz); + buf->setParam(ID::Y_WIDTH, y_sz_revised); + buf->setParam(ID::A_SIGNED, is_signed ? true : false); + buf->setPort(ID::Y, SigSpec(expanded_B)); - // Make sure output domain is big enough to take - // all combinations. - // Later logic synthesis will kill unused - // portions of the output domain. + // Make sure output domain is big enough to take + // all combinations. + // Later logic synthesis will kill unused + // portions of the output domain. - unsigned required_op_size = x_sz_revised + y_sz_revised; - - Wire *expanded_Y = module->addWire(NEW_ID, required_op_size); + unsigned required_op_size = x_sz_revised + y_sz_revised; + Wire *expanded_Y = module->addWire(NEW_ID, required_op_size); + // now connect the expanded_Y with a tap to fill out sig Spec Y + buf_name = "reducer_buf_"; + buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); + buf->setPort(ID::A, expanded_Y); + buf->setParam(ID::A_WIDTH, required_op_size); + buf->setParam(ID::Y_WIDTH, z_sz); // The real user width + buf->setParam(ID::A_SIGNED, is_signed ? true : false); + // wire in output Y + buf->setPort(ID::Y, SigSpec(Y)); + if (is_signed == false) /* unsigned multiplier */ CreateBoothUMult(module, x_sz_revised, y_sz_revised, required_op_size, expanded_A, // multiplicand expanded_B, // multiplier(scanned) expanded_Y // result ); - - // now connect the expanded_Y with a tap to fill out sig Spec Y - - log("Adding reducer on output from %u to %u ", required_op_size, z_sz); - buf_name = "reducer_buf_"; - buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setPort(ID::A, expanded_Y); - buf->setParam(ID::A_WIDTH, required_op_size); - buf->setParam(ID::Y_WIDTH, z_sz); // The real user width - buf->setParam(ID::A_SIGNED, false); - // wire in output Y - buf->setPort(ID::Y, SigSpec(Y)); - - module->remove(cell); - booth_counter++; - continue; - } - - else /*signed multiplier */ { - int x_sz = GetSize(A); - int y_sz = GetSize(B); - int z_sz = GetSize(Y); - - // To simplify the generator size the arguments - // to be the same. Then allow logic synthesis to - // clean things up. Size to biggest - - int x_sz_revised = x_sz; - int y_sz_revised = y_sz; - - if (x_sz != y_sz) { - if (x_sz < y_sz) { - if (y_sz % 2 != 0) { - x_sz_revised = y_sz + 1; - y_sz_revised = y_sz + 1; - } else - x_sz_revised = y_sz; - - log("Resized x to %d ", x_sz_revised); - } else { - if (x_sz % 2 != 0) { - y_sz_revised = x_sz + 1; - x_sz_revised = x_sz + 1; - } else - y_sz_revised = x_sz; - log("Resized y to %d ", y_sz_revised); - } - } else { - if (x_sz % 2 != 0) { - y_sz_revised = y_sz + 1; - x_sz_revised = x_sz + 1; - } - } - - log_assert((x_sz_revised == y_sz_revised) && (x_sz_revised % 2 == 0) && (y_sz_revised % 2 == 0)); - - Wire *expanded_A = module->addWire(NEW_ID, x_sz_revised); - Wire *expanded_B = module->addWire(NEW_ID, y_sz_revised); - - std::string buf_name = "expand_a_buf_"; - auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setParam(ID::A_WIDTH, x_sz); - buf->setParam(ID::Y_WIDTH, x_sz_revised); - buf->setPort(ID::A, SigSpec(A)); - buf->setParam(ID::A_SIGNED, true); - buf->setPort(ID::Y, SigSpec(expanded_A)); - - buf_name = "expand_b_buf_"; - buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setPort(ID::A, SigSpec(B)); - buf->setParam(ID::A_WIDTH, y_sz); - buf->setParam(ID::Y_WIDTH, y_sz_revised); - buf->setParam(ID::A_SIGNED, true); - buf->setPort(ID::Y, SigSpec(expanded_B)); - - // Make sure output domain is big enough to take - // all combinations. - // Later logic synthesis will kill unused - // portions of the output domain. - - unsigned required_op_size = x_sz_revised + y_sz_revised; - - Wire *expanded_Y = module->addWire(NEW_ID, required_op_size); - + else /*signed multiplier */ CreateBoothSMult(module, x_sz_revised, y_sz_revised, required_op_size, expanded_A, // multiplicand expanded_B, // multiplier(scanned) expanded_Y // result (sized) ); - // now connect the expanded_Y with a tap to fill out sig Spec Y - - log("Adding reducer on output from %u to %u ", required_op_size, z_sz); - buf_name = "reducer_buf_"; - buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setPort(ID::A, expanded_Y); - buf->setParam(ID::A_WIDTH, required_op_size); - buf->setParam(ID::Y_WIDTH, z_sz); // The real user width - buf->setParam(ID::A_SIGNED, true); - // wire in output Y - buf->setPort(ID::Y, SigSpec(Y)); - - // kill original multiplier - module->remove(cell); - booth_counter++; - continue; - } + module->remove(cell); + booth_counter++; + continue; } } } @@ -936,7 +834,7 @@ struct MultPassWorker { RTLIL::Wire *ha_op; BuildHa(ha_name, s_vec[n], c_vec[n - 1], ha_op, carry); - connect_sigSpecToWire(ha_op, SigSpec(result, n, 1)); + module->connect(ha_op, SigSpec(result, n, 1)); #ifdef DEBUG_CPA printf("CPA bit [%d] Cell %s IPs [%s] [%s] \n", n, ha_cell->name.c_str(), s_vec[n]->name.c_str(), @@ -971,11 +869,10 @@ struct MultPassWorker { RTLIL::Wire *ha_sum; RTLIL::Wire *ha_carry; BuildHa(ha_name, c_vec[n], carry, ha_sum, ha_carry); - if (n + 1 < (unsigned)GetSize(result)) - connect_sigSpecToWire(ha_sum, SigSpec(result, n + 1, 1)); + module->connect(ha_sum, SigSpec(result, n + 1, 1)); if (n + 2 < (unsigned)GetSize(result)) - connect_sigSpecToWire(ha_carry, SigSpec(result, n + 2, 1)); + module->connect(ha_carry, SigSpec(result, n + 2, 1)); } } // Step case @@ -1113,7 +1010,6 @@ struct MultPassWorker { { for (int y_ix = 0; y_ix < y_sz;) { std::string enc_name = "bur_enc_" + std::to_string(encoder_ix) + "_"; - log("Created booth encoder %s\n", enc_name.c_str()); std::string two_name = "two_int" + std::to_string(encoder_ix); two_int.push_back(module->addWire(new_id(two_name, __LINE__, ""), 1)); @@ -1181,13 +1077,10 @@ struct MultPassWorker { if (need_padded_cell == true) { - log(" Creating padded encoder for unsigned\n"); - // make extra encoder cell // y_ix at y0, rest 0 std::string enc_name = "br_enc_pad" + std::to_string(encoder_ix) + "_"; - log("Created (padded) booth encoder %s\n", enc_name.c_str()); std::string two_name = "two_int" + std::to_string(encoder_ix); two_int.push_back(module->addWire(new_id(two_name, __LINE__, ""), 1)); @@ -1518,14 +1411,11 @@ struct MultPassWorker { // instantiate the cpa RTLIL::Wire *cpa_carry[z_sz]; - if (x_sz + y_sz != z_sz) - log("Result of multiplier is incomplete with respect to domain of inputs\n"); - for (int cix = 0; cix < z_sz; cix++) { std::string cpa_cix_name = "cpa_carry_" + std::to_string(cix) + "_"; cpa_carry[cix] = module->addWire(new_id(cpa_cix_name, __LINE__, ""), 1); } - log(" Building cpa array for booth for output size %u\n", z_sz); + for (int cpa_ix = 0; cpa_ix < z_sz; cpa_ix++) { // The end case where we pass the last two summands @@ -1565,7 +1455,7 @@ struct MultPassWorker { RTLIL::Wire *op_wire = module->addWire(NEW_ID, 1); BuildHa(cpa_name, fa_sum_n[fa_row_count - 1][cpa_ix - offset + 2], ci_wire, op_wire, cpa_carry[cpa_ix - offset]); - connect_sigSpecToWire(op_wire, SigSpec(Z, cpa_ix, 1)); + module->connect(op_wire, SigSpec(Z, cpa_ix, 1)); } } @@ -1599,7 +1489,7 @@ struct MultPassWorker { }; struct MultPass : public Pass { - MultPass() : Pass("multpass", "Map $mul to booth multipliers") {} + MultPass() : Pass("booth", "Map $mul to booth multipliers") {} void execute(vector args, RTLIL::Design *design) override { (void)args; diff --git a/tests/techmap/booth_map_script.ys_ b/tests/techmap/booth_map_script.ys_ index 0f323bdd8..d2fe5d3f0 100644 --- a/tests/techmap/booth_map_script.ys_ +++ b/tests/techmap/booth_map_script.ys_ @@ -1 +1 @@ -multpass +booth From 1d92ea8001d7fde5aca8d8fe27c3c4839a97453a Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Fri, 8 Sep 2023 16:16:24 -0700 Subject: [PATCH 06/13] Support for turning on mult pass from generic synth command --- techlibs/common/synth.cc | 70 +++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 63395c368..01dadae71 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -17,17 +17,16 @@ * */ -#include "kernel/register.h" #include "kernel/celltypes.h" -#include "kernel/rtlil.h" #include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct SynthPass : public ScriptPass -{ - SynthPass() : ScriptPass("synth", "generic synthesis script") { } +struct SynthPass : public ScriptPass { + SynthPass() : ScriptPass("synth", "generic synthesis script") {} void help() override { @@ -60,6 +59,9 @@ struct SynthPass : public ScriptPass log(" -noabc\n"); log(" do not run abc (as if yosys was compiled without ABC support)\n"); log("\n"); + log(" -booth\n"); + log(" run the booth pass to convert $mul to Booth encoded multipliers"); + log("\n"); log(" -noalumacc\n"); log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n"); log(" their direct form ($add, $sub, etc.).\n"); @@ -93,7 +95,8 @@ struct SynthPass : public ScriptPass } string top_module, fsm_opts, memory_opts, abc; - bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap; + bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, mult; + int lut; void clear_flags() override @@ -110,6 +113,7 @@ struct SynthPass : public ScriptPass noabc = false; noshare = false; flowmap = false; + mult = false; abc = "abc"; } @@ -119,24 +123,23 @@ struct SynthPass : public ScriptPass clear_flags(); size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { - if (args[argidx] == "-top" && argidx+1 < args.size()) { + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-top" && argidx + 1 < args.size()) { top_module = args[++argidx]; continue; } - if (args[argidx] == "-encfile" && argidx+1 < args.size()) { + if (args[argidx] == "-encfile" && argidx + 1 < args.size()) { fsm_opts = " -encfile " + args[++argidx]; continue; } - if (args[argidx] == "-run" && argidx+1 < args.size()) { - size_t pos = args[argidx+1].find(':'); + if (args[argidx] == "-run" && argidx + 1 < args.size()) { + size_t pos = args[argidx + 1].find(':'); if (pos == std::string::npos) { run_from = args[++argidx]; run_to = args[argidx]; } else { run_from = args[++argidx].substr(0, pos); - run_to = args[argidx].substr(pos+1); + run_to = args[argidx].substr(pos + 1); } continue; } @@ -164,6 +167,11 @@ struct SynthPass : public ScriptPass noalumacc = true; continue; } + if (args[argidx] == "-mult") { + mult = true; + continue; + } + if (args[argidx] == "-nordff") { memory_opts += " -nordff"; continue; @@ -206,8 +214,7 @@ struct SynthPass : public ScriptPass void script() override { - if (check_label("begin")) - { + if (check_label("begin")) { if (help_mode) { run("hierarchy -check [-top | -auto-top]"); } else { @@ -221,8 +228,7 @@ struct SynthPass : public ScriptPass } } - if (check_label("coarse")) - { + if (check_label("coarse")) { run("proc"); if (help_mode || flatten) run("flatten", " (if -flatten)"); @@ -240,6 +246,8 @@ struct SynthPass : public ScriptPass run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)"); else if (lut) run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut)); + if (mult) + run("booth"); if (!noalumacc) run("alumacc", " (unless -noalumacc)"); if (!noshare) @@ -249,50 +257,40 @@ struct SynthPass : public ScriptPass run("opt_clean"); } - if (check_label("fine")) - { + if (check_label("fine")) { run("opt -fast -full"); run("memory_map"); run("opt -full"); run("techmap"); - if (help_mode) - { + if (help_mode) { run("techmap -map +/gate2lut.v", "(if -noabc and -lut)"); run("clean; opt_lut", " (if -noabc and -lut)"); run("flowmap -maxlut K", " (if -flowmap and -lut)"); - } - else if (noabc && lut) - { + } else if (noabc && lut) { run(stringf("techmap -map +/gate2lut.v -D LUT_WIDTH=%d", lut)); run("clean; opt_lut"); - } - else if (flowmap) - { + } else if (flowmap) { run(stringf("flowmap -maxlut %d", lut)); } run("opt -fast"); if (!noabc && !flowmap) { - #ifdef YOSYS_ENABLE_ABC - if (help_mode) - { +#ifdef YOSYS_ENABLE_ABC + if (help_mode) { run(abc + " -fast", " (unless -noabc, unless -lut)"); run(abc + " -fast -lut k", "(unless -noabc, if -lut)"); - } - else - { + } else { if (lut) run(stringf("%s -fast -lut %d", abc.c_str(), lut)); else run(abc + " -fast"); } run("opt -fast", " (unless -noabc)"); - #endif +#endif } } - if (check_label("check")) - { + if (check_label("check")) { run("hierarchy -check"); run("stat"); run("check"); From 0fa412502cd19deef2d2972391e9b0e7c1726074 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Fri, 8 Sep 2023 16:44:59 -0700 Subject: [PATCH 07/13] mult -> booth in synth.cc, to turn on use synth -booth --- techlibs/common/synth.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 01dadae71..006a3c8dd 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -95,7 +95,7 @@ struct SynthPass : public ScriptPass { } string top_module, fsm_opts, memory_opts, abc; - bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, mult; + bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, booth; int lut; @@ -113,7 +113,7 @@ struct SynthPass : public ScriptPass { noabc = false; noshare = false; flowmap = false; - mult = false; + booth = false; abc = "abc"; } @@ -167,8 +167,8 @@ struct SynthPass : public ScriptPass { noalumacc = true; continue; } - if (args[argidx] == "-mult") { - mult = true; + if (args[argidx] == "-booth") { + booth = true; continue; } @@ -246,7 +246,7 @@ struct SynthPass : public ScriptPass { run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v", " (if -lut)"); else if (lut) run(stringf("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", lut)); - if (mult) + if (booth) run("booth"); if (!noalumacc) run("alumacc", " (unless -noalumacc)"); From d77fb8150708f2e86bd6caa62424970cb3b71757 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Sun, 10 Sep 2023 12:45:36 -0700 Subject: [PATCH 08/13] 2d array -> 1d array in module generator --- passes/techmap/multpass.cc | 122 +++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/passes/techmap/multpass.cc b/passes/techmap/multpass.cc index c0e56f2ad..5e8dcbc3b 100644 --- a/passes/techmap/multpass.cc +++ b/passes/techmap/multpass.cc @@ -1112,7 +1112,6 @@ struct MultPassWorker { /* Signed Multiplier */ - void CreateBoothSMult(RTLIL::Module *module, int x_sz, int y_sz, int z_sz, RTLIL::Wire *X, RTLIL::Wire *Y, RTLIL::Wire *Z) { // product unsigned enc_count = (y_sz / 2) + (((y_sz % 2) != 0) ? 1 : 0); @@ -1171,20 +1170,22 @@ struct MultPassWorker { cori_n_int[encoder_ix - 1]); } } + // Decoders and PP generation - RTLIL::Wire *PPij[enc_count][dec_count]; - RTLIL::Wire *nxj[enc_count][dec_count]; + RTLIL::Wire *PPij[enc_count * dec_count]; + RTLIL::Wire *nxj[enc_count * dec_count]; + for (int encoder_ix = 1; encoder_ix <= (int)enc_count; encoder_ix++) { for (int decoder_ix = 1; decoder_ix <= dec_count; decoder_ix++) { std::string ppij_name = "ppij_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; - PPij[encoder_ix - 1][decoder_ix - 1] = module->addWire(new_id(ppij_name, __LINE__, ""), 1); + PPij[((encoder_ix - 1) * dec_count) + decoder_ix - 1] = module->addWire(new_id(ppij_name, __LINE__, ""), 1); std::string nxj_name; if (decoder_ix == 1) nxj_name = "nxj_pre_dec" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; else nxj_name = "nxj_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; - nxj[encoder_ix - 1][decoder_ix - 1] = module->addWire(new_id(nxj_name, __LINE__, ""), 1); + nxj[((encoder_ix - 1) * dec_count) + decoder_ix - 1] = module->addWire(new_id(nxj_name, __LINE__, ""), 1); } } @@ -1202,7 +1203,7 @@ struct MultPassWorker { auto cell = module->addCell(new_id(pre_dec_name, __LINE__, ""), ID($_NOT_)); cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); cell->setPort(ID::A, negi_n_int[encoder_ix - 1]); - cell->setPort(ID::Y, nxj[encoder_ix - 1][0]); + cell->setPort(ID::Y, nxj[(encoder_ix - 1) * dec_count]); } for (int decoder_ix = 1; decoder_ix < dec_count; decoder_ix++) { @@ -1214,18 +1215,18 @@ struct MultPassWorker { std::string dec_name = "dec_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; - BuildBr4d(dec_name, nxj[encoder_ix - 1][decoder_ix - 1], twoi_n_int[encoder_ix - 1], + BuildBr4d(dec_name, nxj[((encoder_ix - 1) * dec_count) + decoder_ix - 1], twoi_n_int[encoder_ix - 1], mk_wireFromSigSpec(SigSpec(X, decoder_ix - 1, 1)), negi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], - PPij[encoder_ix - 1][decoder_ix - 1], nxj[encoder_ix - 1][decoder_ix]); + PPij[((encoder_ix - 1) * dec_count) + decoder_ix - 1], nxj[((encoder_ix - 1) * dec_count) + decoder_ix]); } // duplicate end for sign fix // applies to 9th decoder (xsz+1 decoder). std::string dec_name = "dec_" + std::to_string(encoder_ix) + "_" + std::to_string(x_sz + 1) + "_"; RTLIL::Wire *unused_op = nullptr; - BuildBr4d(dec_name, nxj[encoder_ix - 1][dec_count - 1], twoi_n_int[encoder_ix - 1], + BuildBr4d(dec_name, nxj[((encoder_ix - 1) * dec_count) + dec_count - 1], twoi_n_int[encoder_ix - 1], mk_wireFromSigSpec(SigSpec(X, dec_count - 2, 1)), negi_n_int[encoder_ix - 1], onei_n_int[encoder_ix - 1], - PPij[encoder_ix - 1][dec_count - 1], unused_op); + PPij[((encoder_ix - 1) * dec_count) + dec_count - 1], unused_op); } // @@ -1233,16 +1234,17 @@ struct MultPassWorker { // int fa_el_ix = 0; int fa_row_ix = 0; - RTLIL::Wire *fa_sum_n[fa_row_count][fa_count]; - RTLIL::Wire *fa_carry_n[fa_row_count][fa_count]; + // use 1 d arrays (2d cannot have variable sized indices) + RTLIL::Wire *fa_sum_n[fa_row_count * fa_count]; + RTLIL::Wire *fa_carry_n[fa_row_count * fa_count]; for (fa_row_ix = 0; fa_row_ix < fa_row_count; fa_row_ix++) { for (fa_el_ix = 0; fa_el_ix < fa_count; fa_el_ix++) { std::string fa_sum_name = "fa_sum_n_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_"; - fa_sum_n[fa_row_ix][fa_el_ix] = module->addWire(new_id(fa_sum_name, __LINE__, ""), 1); + fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix] = module->addWire(new_id(fa_sum_name, __LINE__, ""), 1); std::string fa_carry_name = "fa_carry_n" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_"; - fa_carry_n[fa_row_ix][fa_el_ix] = module->addWire(new_id(fa_carry_name, __LINE__, ""), 1); + fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix] = module->addWire(new_id(fa_carry_name, __LINE__, ""), 1); } } @@ -1268,11 +1270,11 @@ struct MultPassWorker { bfa_name = "bfa_0_step_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; auto cell = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); cell->setParam(ID::WIDTH, 1); - cell->setPort(ID::A, PPij[0][fa_el_ix]); - cell->setPort(ID::B, PPij[1][fa_el_ix - 2]); - cell->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell->setPort(ID::A, PPij[(0 * dec_count) + fa_el_ix]); + cell->setPort(ID::B, PPij[(1 * dec_count) + fa_el_ix - 2]); + cell->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); } // end 3 cells: x_sz+1.2.3 // @@ -1281,11 +1283,11 @@ struct MultPassWorker { bfa_name = "bfa_0_se_0" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; auto cell1 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); cell1->setParam(ID::WIDTH, 1); - cell1->setPort(ID::A, PPij[0][x_sz]); - cell1->setPort(ID::B, PPij[1][fa_el_ix - 2]); - cell1->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell1->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell1->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell1->setPort(ID::A, PPij[(0 * dec_count) + x_sz]); + cell1->setPort(ID::B, PPij[(1 * dec_count) + fa_el_ix - 2]); + cell1->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell1->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell1->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); // exception:invert ppi fa_el_ix++; @@ -1295,7 +1297,7 @@ struct MultPassWorker { RTLIL::Wire *d08_inv = module->addWire(NEW_ID, 1); - cellinv1->setPort(ID::A, PPij[0][dec_count - 1]); + cellinv1->setPort(ID::A, PPij[(0 * dec_count) + dec_count - 1]); cellinv1->setPort(ID::Y, d08_inv); exc_inv_name = "bfa_0_exc_inv2_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; @@ -1303,7 +1305,7 @@ struct MultPassWorker { auto cellinv2 = module->addCell(new_id(exc_inv_name, __LINE__, ""), ID($_NOT_)); cellinv2->add_strpool_attribute(ID::src, cellinv2->get_strpool_attribute(ID::src)); RTLIL::Wire *d18_inv = module->addWire(NEW_ID, 1); - cellinv2->setPort(ID::A, PPij[1][dec_count - 1]); + cellinv2->setPort(ID::A, PPij[(1 * dec_count) + dec_count - 1]); cellinv2->setPort(ID::Y, d18_inv); bfa_name = "bfa_0_se_1_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; @@ -1312,9 +1314,9 @@ struct MultPassWorker { cell2->setParam(ID::WIDTH, 1); cell2->setPort(ID::A, d08_inv); cell2->setPort(ID::B, d18_inv); - cell2->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell2->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell2->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell2->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell2->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell2->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); // sign extension fa_el_ix++; @@ -1323,9 +1325,9 @@ struct MultPassWorker { cell3->setParam(ID::WIDTH, 1); cell3->setPort(ID::A, State::S0); cell3->setPort(ID::B, State::S1); - cell3->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell3->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell3->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell3->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell3->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell3->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); } } @@ -1340,22 +1342,23 @@ struct MultPassWorker { std::to_string(fa_el_ix) + "_L"; auto cell1 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); cell1->setParam(ID::WIDTH, 1); - cell1->setPort(ID::A, fa_sum_n[fa_row_ix - 1][2]); // from prior full adder row + cell1->setPort(ID::A, fa_sum_n[(fa_row_ix - 1) * fa_count + 2]); // from prior full adder row cell1->setPort(ID::B, State::S0); cell1->setPort(ID::C, cori_n_int[fa_row_ix]); - cell1->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell1->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell1->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell1->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); fa_el_ix++; bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_base_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; auto cell2 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); cell2->setParam(ID::WIDTH, 1); - cell2->setPort(ID::A, fa_sum_n[fa_row_ix - 1][3]); // from prior full adder row + cell2->setPort(ID::A, + fa_sum_n[(fa_row_ix - 1) * fa_count + 3]); // from prior full adder row cell2->setPort(ID::B, State::S0); - cell2->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell2->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell2->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell2->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell2->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell2->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); } else if (fa_el_ix >= 2 && fa_el_ix <= x_sz + 1) { @@ -1364,11 +1367,11 @@ struct MultPassWorker { std::to_string(fa_el_ix) + "_L"; auto cell = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); cell->setParam(ID::WIDTH, 1); - cell->setPort(ID::A, fa_sum_n[fa_row_ix - 1][fa_el_ix + 2]); - cell->setPort(ID::B, PPij[fa_row_ix + 1][fa_el_ix - 2]); - cell->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell->setPort(ID::A, fa_sum_n[(fa_row_ix - 1) * fa_count + fa_el_ix + 2]); + cell->setPort(ID::B, PPij[(fa_row_ix + 1) * dec_count + fa_el_ix - 2]); + cell->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); } else if (fa_el_ix > x_sz + 1) { @@ -1379,18 +1382,18 @@ struct MultPassWorker { auto cellinv = module->addCell(new_id(se_inv_name, __LINE__, ""), ID($_NOT_)); cellinv->add_strpool_attribute(ID::src, cellinv->get_strpool_attribute(ID::src)); RTLIL::Wire *d_inv = module->addWire(NEW_ID, 1); - cellinv->setPort(ID::A, PPij[fa_row_ix + 1][dec_count - 1]); + cellinv->setPort(ID::A, PPij[((fa_row_ix + 1) * dec_count) + dec_count - 1]); cellinv->setPort(ID::Y, d_inv); bfa_name = "bfa_" + std::to_string(fa_row_ix) + "_se_" + std::to_string(fa_row_ix) + "_" + std::to_string(fa_el_ix) + "_L"; auto cell1 = module->addCell(new_id(bfa_name, __LINE__, ""), ID($fa)); cell1->setParam(ID::WIDTH, 1); - cell1->setPort(ID::A, fa_carry_n[fa_row_ix - 1][fa_count - 1]); + cell1->setPort(ID::A, fa_carry_n[((fa_row_ix - 1) * fa_count) + fa_count - 1]); cell1->setPort(ID::B, d_inv); - cell1->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell1->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell1->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell1->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell1->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell1->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); fa_el_ix++; // sign extension @@ -1400,9 +1403,9 @@ struct MultPassWorker { cell2->setParam(ID::WIDTH, 1); cell2->setPort(ID::A, State::S0); cell2->setPort(ID::B, State::S1); - cell2->setPort(ID::C, fa_carry_n[fa_row_ix][fa_el_ix - 1]); - cell2->setPort(ID::X, fa_carry_n[fa_row_ix][fa_el_ix]); - cell2->setPort(ID::Y, fa_sum_n[fa_row_ix][fa_el_ix]); + cell2->setPort(ID::C, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1]); + cell2->setPort(ID::X, fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix]); + cell2->setPort(ID::Y, fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]); } } } @@ -1428,7 +1431,7 @@ struct MultPassWorker { std::string buf_name = "pp_buf_" + std::to_string(cpa_ix) + "_" + "driven_by_fa_row_" + std::to_string(fa_row_ix) + "_"; auto buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setPort(ID::A, fa_sum_n[fa_row_ix][0]); + buf->setPort(ID::A, fa_sum_n[(fa_row_ix * fa_count) + 0]); buf->setParam(ID::A_WIDTH, 1); buf->setParam(ID::Y_WIDTH, 1); buf->setParam(ID::A_SIGNED, true); @@ -1437,7 +1440,7 @@ struct MultPassWorker { cpa_ix++; buf_name = "pp_buf_" + std::to_string(cpa_ix) + "_" + "driven_by_fa_row_" + std::to_string(fa_row_ix) + "_"; buf = module->addCell(new_id(buf_name, __LINE__, ""), ID($pos)); - buf->setPort(ID::A, fa_sum_n[fa_row_ix][1]); + buf->setPort(ID::A, fa_sum_n[(fa_row_ix * fa_count) + 1]); buf->setParam(ID::A_WIDTH, 1); buf->setParam(ID::Y_WIDTH, 1); buf->setParam(ID::A_SIGNED, true); @@ -1454,7 +1457,8 @@ struct MultPassWorker { ci_wire = cpa_carry[cpa_ix - offset - 1]; RTLIL::Wire *op_wire = module->addWire(NEW_ID, 1); - BuildHa(cpa_name, fa_sum_n[fa_row_count - 1][cpa_ix - offset + 2], ci_wire, op_wire, cpa_carry[cpa_ix - offset]); + BuildHa(cpa_name, fa_sum_n[(fa_row_count - 1) * fa_count + cpa_ix - offset + 2], ci_wire, op_wire, + cpa_carry[cpa_ix - offset]); module->connect(op_wire, SigSpec(Z, cpa_ix, 1)); } } @@ -1481,10 +1485,10 @@ struct MultPassWorker { nxj_o_int, cor_o_int, pp0_o_int, pp1_o_int); - join_wires_with_buffer(pp0_o_int, fa_sum_n[0][0]); - join_wires_with_buffer(pp1_o_int, fa_sum_n[0][1]); - join_wires_with_buffer(cor_o_int, fa_carry_n[0][1]); - join_wires_with_buffer(nxj_o_int, nxj[0][2]); + join_wires_with_buffer(pp0_o_int, fa_sum_n[(0 * fa_count) + 0]); + join_wires_with_buffer(pp1_o_int, fa_sum_n[(0 * fa_count) + 1]); + join_wires_with_buffer(cor_o_int, fa_carry_n[(0 * fa_count) + 1]); + join_wires_with_buffer(nxj_o_int, nxj[(0 * dec_count) + 2]); } }; From 8d4b6c2f69b5f6b2fb09289a535b74a20e7a7735 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Sun, 10 Sep 2023 13:31:47 -0700 Subject: [PATCH 09/13] Switched arrays for signed multiplier construction to heap --- passes/techmap/multpass.cc | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/passes/techmap/multpass.cc b/passes/techmap/multpass.cc index 5e8dcbc3b..00dcc6e27 100644 --- a/passes/techmap/multpass.cc +++ b/passes/techmap/multpass.cc @@ -1124,10 +1124,10 @@ struct MultPassWorker { "Result of size %d. %d encoders %d decoders\n", x_sz, y_sz, z_sz, enc_count, dec_count); - RTLIL::Wire *negi_n_int[enc_count]; - RTLIL::Wire *twoi_n_int[enc_count]; - RTLIL::Wire *onei_n_int[enc_count]; - RTLIL::Wire *cori_n_int[enc_count]; + RTLIL::Wire **negi_n_int = new RTLIL::Wire *[enc_count]; + RTLIL::Wire **twoi_n_int = new RTLIL::Wire *[enc_count]; + RTLIL::Wire **onei_n_int = new RTLIL::Wire *[enc_count]; + RTLIL::Wire **cori_n_int = new RTLIL::Wire *[enc_count]; for (unsigned encoder_ix = 1; encoder_ix <= enc_count; encoder_ix++) { std::string enc_name = "enc_" + std::to_string(encoder_ix) + "_"; @@ -1172,8 +1172,8 @@ struct MultPassWorker { } // Decoders and PP generation - RTLIL::Wire *PPij[enc_count * dec_count]; - RTLIL::Wire *nxj[enc_count * dec_count]; + RTLIL::Wire **PPij = new RTLIL::Wire *[enc_count * dec_count]; + RTLIL::Wire **nxj = new RTLIL::Wire *[enc_count * dec_count]; for (int encoder_ix = 1; encoder_ix <= (int)enc_count; encoder_ix++) { for (int decoder_ix = 1; decoder_ix <= dec_count; decoder_ix++) { @@ -1235,8 +1235,8 @@ struct MultPassWorker { int fa_el_ix = 0; int fa_row_ix = 0; // use 1 d arrays (2d cannot have variable sized indices) - RTLIL::Wire *fa_sum_n[fa_row_count * fa_count]; - RTLIL::Wire *fa_carry_n[fa_row_count * fa_count]; + RTLIL::Wire **fa_sum_n = new RTLIL::Wire *[fa_row_count * fa_count]; + RTLIL::Wire **fa_carry_n = new RTLIL::Wire *[fa_row_count * fa_count]; for (fa_row_ix = 0; fa_row_ix < fa_row_count; fa_row_ix++) { for (fa_el_ix = 0; fa_el_ix < fa_count; fa_el_ix++) { @@ -1489,6 +1489,14 @@ struct MultPassWorker { join_wires_with_buffer(pp1_o_int, fa_sum_n[(0 * fa_count) + 1]); join_wires_with_buffer(cor_o_int, fa_carry_n[(0 * fa_count) + 1]); join_wires_with_buffer(nxj_o_int, nxj[(0 * dec_count) + 2]); + + delete[] negi_n_int; + delete[] twoi_n_int; + delete[] onei_n_int; + delete[] cori_n_int; + + delete[] fa_sum_n; + delete[] fa_carry_n; } }; From 1b5287af5908e71edd07d9de2d6872f3c2a389ed Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Sun, 10 Sep 2023 14:20:30 -0700 Subject: [PATCH 10/13] cpa_carry array added to heap --- passes/techmap/multpass.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/passes/techmap/multpass.cc b/passes/techmap/multpass.cc index 00dcc6e27..9e97fc674 100644 --- a/passes/techmap/multpass.cc +++ b/passes/techmap/multpass.cc @@ -1412,7 +1412,7 @@ struct MultPassWorker { } // instantiate the cpa - RTLIL::Wire *cpa_carry[z_sz]; + RTLIL::Wire **cpa_carry = new RTLIL::Wire *[z_sz]; for (int cix = 0; cix < z_sz; cix++) { std::string cpa_cix_name = "cpa_carry_" + std::to_string(cix) + "_"; @@ -1497,6 +1497,7 @@ struct MultPassWorker { delete[] fa_sum_n; delete[] fa_carry_n; + delete[] cpa_carry; } }; From a2c8e47295dbb93faeb2f949ce354c4faa919fc9 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Mon, 11 Sep 2023 11:39:13 -0700 Subject: [PATCH 11/13] multpass.cc -> booth.cc, added author/support contact info --- passes/techmap/Makefile.inc | 5 +---- passes/techmap/{multpass.cc => booth.cc} | 12 ++++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) rename passes/techmap/{multpass.cc => booth.cc} (99%) diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 8b0b2aa23..fb003103d 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -4,7 +4,7 @@ OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o OBJS += passes/techmap/maccmap.o -OBJS += passes/techmap/multpass.o +OBJS += passes/techmap/booth.o OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) @@ -30,9 +30,6 @@ OBJS += passes/techmap/extract_reduce.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dffinit.o OBJS += passes/techmap/pmuxtree.o -OBJS += passes/techmap/bmuxmap.o -OBJS += passes/techmap/demuxmap.o -OBJS += passes/techmap/bwmuxmap.o OBJS += passes/techmap/muxcover.o OBJS += passes/techmap/aigmap.o OBJS += passes/techmap/tribuf.o diff --git a/passes/techmap/multpass.cc b/passes/techmap/booth.cc similarity index 99% rename from passes/techmap/multpass.cc rename to passes/techmap/booth.cc index 9e97fc674..21f16aec7 100644 --- a/passes/techmap/multpass.cc +++ b/passes/techmap/booth.cc @@ -1,6 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * + * Copyright (C) 2023 Andy Fox https://www.linkedin.com/in/awfox/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,8 +18,8 @@ */ /* - MultPass - -------- + Booth Pass + ---------- Replace $mul with booth encoded multipliers. Two different architectures used for signed/unsigned. @@ -31,13 +32,13 @@ http://i.stanford.edu/pub/cstr/reports/csl/tr/94/617/CSL-TR-94-617.pdf How to use: - Add multpass to your yosys script eg: + Add booth pass to your yosys script eg: read_verilog smultiply5_rtl.v opt wreduce opt - multpass + booth alumacc maccmap opt @@ -46,6 +47,9 @@ abc -liberty NangateOpenCellLibrary_typical.lib stat -liberty NangateOpenCellLibrary_typical.lib write_verilog -norename booth_final.v + +or in generic synthesis call with -booth argument: +synth -top my_design -booth */ #include "kernel/sigtools.h" From eccc0ae6db50effc7f42d69038918483d6888089 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Mon, 11 Sep 2023 12:14:12 -0700 Subject: [PATCH 12/13] Based passes/techmap/Makefile.inc changes on latest in yosys --- passes/techmap/Makefile.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index fb003103d..97d8b76f3 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -30,6 +30,9 @@ OBJS += passes/techmap/extract_reduce.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dffinit.o OBJS += passes/techmap/pmuxtree.o +OBJS += passes/techmap/bmuxmap.o +OBJS += passes/techmap/demuxmap.o +OBJS += passes/techmap/bwmuxmap.o OBJS += passes/techmap/muxcover.o OBJS += passes/techmap/aigmap.o OBJS += passes/techmap/tribuf.o From e4fe522767c3ddfeb90c2f4091890cb45584e0e9 Mon Sep 17 00:00:00 2001 From: andyfox-rushc Date: Mon, 11 Sep 2023 13:00:11 -0700 Subject: [PATCH 13/13] MultPassWorker -> BoothPassWorker --- passes/techmap/booth.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/passes/techmap/booth.cc b/passes/techmap/booth.cc index 21f16aec7..e74916852 100644 --- a/passes/techmap/booth.cc +++ b/passes/techmap/booth.cc @@ -58,13 +58,13 @@ synth -top my_design -booth USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct MultPassWorker { +struct BoothPassWorker { RTLIL::Module *module; SigMap sigmap; int booth_counter; - MultPassWorker(RTLIL::Module *module) : module(module), sigmap(module) { booth_counter = 0; } + BoothPassWorker(RTLIL::Module *module) : module(module), sigmap(module) { booth_counter = 0; } // Helper routines for building architecture subcomponents @@ -1505,15 +1505,16 @@ struct MultPassWorker { } }; -struct MultPass : public Pass { - MultPass() : Pass("booth", "Map $mul to booth multipliers") {} +struct BoothPass : public Pass { + BoothPass() : Pass("booth", "Map $mul to booth multipliers") {} void execute(vector args, RTLIL::Design *design) override { (void)args; - log_header(design, "Executing multpass. Generating Booth Multiplier structures for signed/unsigned multipliers of 4 bits or more\n"); + log_header(design, + "Executing booth pass. Generating Booth Multiplier structures for signed/unsigned multipliers of 4 bits or more\n"); for (auto mod : design->selected_modules()) if (!mod->has_processes_warn()) { - MultPassWorker worker(mod); + BoothPassWorker worker(mod); worker.run(); log_header(design, "Created %d booth multipliers.\n", worker.booth_counter); }