diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 1b834fabc..97d8b76f3 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/booth.o OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) diff --git a/passes/techmap/booth.cc b/passes/techmap/booth.cc new file mode 100644 index 000000000..e74916852 --- /dev/null +++ b/passes/techmap/booth.cc @@ -0,0 +1,1524 @@ +/* + * 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 + * 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. + * + */ + +/* + Booth Pass + ---------- + + 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 booth pass to your yosys script eg: + + read_verilog smultiply5_rtl.v + opt + wreduce + opt + booth + 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 + +or in generic synthesis call with -booth argument: +synth -top my_design -booth +*/ + +#include "kernel/sigtools.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct BoothPassWorker { + + RTLIL::Module *module; + SigMap sigmap; + int booth_counter; + + BoothPassWorker(RTLIL::Module *module) : module(module), sigmap(module) { booth_counter = 0; } + + // Helper routines for building architecture subcomponents + + 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() + { + 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"); + + 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; + + } else { + if (x_sz % 2 != 0) { + 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)); + + 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, 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, 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. + + 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 + ); + 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) + ); + 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); + + 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(), + 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)) + module->connect(ha_sum, SigSpec(result, n + 1, 1)); + if (n + 2 < (unsigned)GetSize(result)) + module->connect(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) + "_"; + + 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) { + + // make extra encoder cell + // y_ix at y0, rest 0 + + std::string enc_name = "br_enc_pad" + std::to_string(encoder_ix) + "_"; + + 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 = 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) + "_"; + 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 = 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++) { + std::string ppij_name = "ppij_" + std::to_string(encoder_ix) + "_" + std::to_string(decoder_ix) + "_"; + 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) * dec_count) + 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) * dec_count]); + } + + 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) * 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) * 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) + 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) + dec_count - 1], unused_op); + } + + // + // sum up the partial products + // + 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 = 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++) { + + 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_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_count) + 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 * 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 + // + 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 * 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++; + 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) + 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) + 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_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++; + 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_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]); + } + } + + // 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) * 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_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) * 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_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) { + // 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_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) { + // 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) + 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) + fa_count - 1]); + cell1->setPort(ID::B, d_inv); + 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 + 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_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]); + } + } + } + } + + // instantiate the cpa + 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) + "_"; + cpa_carry[cix] = module->addWire(new_id(cpa_cix_name, __LINE__, ""), 1); + } + + 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 * fa_count) + 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 * fa_count) + 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) * fa_count + cpa_ix - offset + 2], ci_wire, op_wire, + cpa_carry[cpa_ix - offset]); + module->connect(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 * 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]); + + delete[] negi_n_int; + delete[] twoi_n_int; + delete[] onei_n_int; + delete[] cori_n_int; + + delete[] fa_sum_n; + delete[] fa_carry_n; + delete[] cpa_carry; + } +}; + +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 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()) { + BoothPassWorker worker(mod); + worker.run(); + log_header(design, "Created %d booth multipliers.\n", worker.booth_counter); + } + } +} MultPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 63395c368..006a3c8dd 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, booth; + int lut; void clear_flags() override @@ -110,6 +113,7 @@ struct SynthPass : public ScriptPass noabc = false; noshare = false; flowmap = false; + booth = 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] == "-booth") { + booth = 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 (booth) + 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"); diff --git a/tests/techmap/booth.ys b/tests/techmap/booth.ys new file mode 100644 index 000000000..f1dce1f3b --- /dev/null +++ b/tests/techmap/booth.ys @@ -0,0 +1 @@ +test_cell -s 1694091355 -n 1000 -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..d2fe5d3f0 --- /dev/null +++ b/tests/techmap/booth_map_script.ys_ @@ -0,0 +1 @@ +booth