diff --git a/Makefile b/Makefile index f8d628010c5..5306c8498cc 100644 --- a/Makefile +++ b/Makefile @@ -646,7 +646,7 @@ $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o OBJS += kernel/binding.o -OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o +OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o ifeq ($(ENABLE_ZLIB),1) OBJS += kernel/fstdata.o endif diff --git a/kernel/constids.inc b/kernel/constids.inc index 7db21debb0e..4adbc83ae3a 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -57,6 +57,7 @@ X(CO) X(COLLISION_X_MASK) X(CONFIG) X(CONFIG_WIDTH) +X(cost) X(CTRL_IN) X(CTRL_IN_WIDTH) X(CTRL_OUT) diff --git a/kernel/cost.cc b/kernel/cost.cc new file mode 100644 index 00000000000..d7641c26735 --- /dev/null +++ b/kernel/cost.cc @@ -0,0 +1,203 @@ +#include "kernel/cost.h" + +USING_YOSYS_NAMESPACE + +unsigned int CellCosts::get(RTLIL::Module *mod) +{ + if (mod_cost_cache_.count(mod->name)) + return mod_cost_cache_.at(mod->name); + + unsigned int module_cost = 1; + for (auto c : mod->cells()) { + unsigned int new_cost = module_cost + get(c); + module_cost = new_cost >= module_cost ? new_cost : INT_MAX; + } + + mod_cost_cache_[mod->name] = module_cost; + return module_cost; +} + +static unsigned int y_coef(RTLIL::IdString type) +{ + // clang-format off + if (// equality + type == ID($bweqx) || + type == ID($nex) || + type == ID($eqx) || + // basic logic + type == ID($and) || + type == ID($or) || + type == ID($xor) || + type == ID($xnor) || + type == ID($not) || + // mux + type == ID($bwmux) || + type == ID($mux) || + type == ID($demux) || + // others + type == ID($tribuf)) { + return 1; + } else if (type == ID($neg)) { + return 4; + } else if (type == ID($fa)) { + return 5; + } else if (// multi-bit adders + type == ID($add) || + type == ID($sub) || + type == ID($alu)) { + return 8; + } else if (// left shift + type == ID($shl) || + type == ID($sshl)) { + return 10; + } + // clang-format on + return 0; +} + +static unsigned int max_inp_coef(RTLIL::IdString type) +{ + // clang-format off + if (// binop reduce + type == ID($reduce_and) || + type == ID($reduce_or) || + type == ID($reduce_xor) || + type == ID($reduce_xnor) || + type == ID($reduce_bool) || + // others + type == ID($logic_not) || + type == ID($pmux) || + type == ID($bmux)) { + return 1; + } else if (// equality + type == ID($eq) || + type == ID($ne) || + // logic + type == ID($logic_and) || + type == ID($logic_or)) { + return 2; + } else if (type == ID($lcu)) { + return 5; + } else if (// comparison + type == ID($lt) || + type == ID($le) || + type == ID($ge) || + type == ID($gt) || + // others + type == ID($sop)) { + return 6; + } + // clang-format on + return 0; +} +static unsigned int sum_coef(RTLIL::IdString type) +{ + // clang-format off + if (// right shift + type == ID($shr) || + type == ID($sshr)) { + return 4; + } else if (// shift + type == ID($shift) || + type == ID($shiftx)) { + return 8; + } + // clang-format on + return 0; +} + +static bool is_free(RTLIL::IdString type) +{ + // clang-format off + return (// tags + type == ID($overwrite_tag) || + type == ID($set_tag) || + type == ID($original_tag) || + type == ID($get_tag) || + // formal + type == ID($check) || + type == ID($equiv) || + type == ID($initstate) || + type == ID($assert) || + type == ID($assume) || + type == ID($live) || + type == ID($cover) || + type == ID($allseq) || + type == ID($allconst) || + type == ID($anyseq) || + type == ID($anyinit) || + type == ID($anyconst) || + type == ID($fair) || + // utilities + type == ID($scopeinfo) || + type == ID($print) || + // real but free + type == ID($concat) || + type == ID($slice) || + type == ID($pos) || + // specify + type == ID($specrule) || + type == ID($specify2) || + type == ID($specify3)); + // clang-format on +} + +unsigned int CellCosts::get(RTLIL::Cell *cell) +{ + + if (gate_type_cost().count(cell->type)) + return gate_type_cost().at(cell->type); + + if (design_ && design_->module(cell->type) && cell->parameters.empty()) { + log_debug("%s is a module, recurse\n", cell->name.c_str()); + return get(design_->module(cell->type)); + } else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + log_assert(cell->hasPort(ID::Q) && "Weird flip flop"); + log_debug("%s is ff\n", cell->name.c_str()); + return cell->getParam(ID::WIDTH).as_int(); + } else if (y_coef(cell->type)) { + // linear with Y_WIDTH or WIDTH + log_assert((cell->hasParam(ID::Y_WIDTH) || cell->hasParam(ID::WIDTH)) && "Unknown width"); + auto param = cell->hasParam(ID::Y_WIDTH) ? ID::Y_WIDTH : ID::WIDTH; + int width = cell->getParam(param).as_int(); + log_debug("%s Y*coef %d * %d\n", cell->name.c_str(), width, y_coef(cell->type)); + return width * y_coef(cell->type); + } else if (sum_coef(cell->type)) { + // linear with sum of port widths + unsigned int sum = 0; + RTLIL::IdString port_width_params[] = { + ID::WIDTH, ID::A_WIDTH, ID::B_WIDTH, ID::S_WIDTH, ID::Y_WIDTH, + }; + + for (auto param : port_width_params) + if (cell->hasParam(param)) + sum += cell->getParam(param).as_int(); + + log_debug("%s sum*coef %d * %d\n", cell->name.c_str(), sum, sum_coef(cell->type)); + return sum * sum_coef(cell->type); + } else if (max_inp_coef(cell->type)) { + // linear with largest input width + unsigned int max = 0; + RTLIL::IdString input_width_params[] = { + ID::WIDTH, + ID::A_WIDTH, + ID::B_WIDTH, + ID::S_WIDTH, + }; + + for (RTLIL::IdString param : input_width_params) + if (cell->hasParam(param)) + max = std::max(max, (unsigned int)cell->getParam(param).as_int()); + + log_debug("%s max*coef %d * %d\n", cell->name.c_str(), max, max_inp_coef(cell->type)); + return max; + } else if (is_free(cell->type)) { + log_debug("%s is free\n", cell->name.c_str()); + return 0; + } + // TODO: $fsm $mem.* + // ignored: $pow + + log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); + return 1; +} diff --git a/kernel/cost.h b/kernel/cost.h index b81420af756..fbf1201aacf 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -26,8 +26,22 @@ YOSYS_NAMESPACE_BEGIN struct CellCosts { - static const dict& default_gate_cost() { - static const dict db = { + + enum CostKind { + DEFAULT, + CMOS, + }; + + private: + dict mod_cost_cache_; + CostKind kind_; + Design *design_ = nullptr; + + public: + CellCosts(CellCosts::CostKind kind, RTLIL::Design *design) : kind_(kind), design_(design) { } + + const dict& gate_type_cost() { + static const dict default_gate_db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, { ID($_AND_), 4 }, @@ -43,13 +57,12 @@ struct CellCosts { ID($_AOI4_), 7 }, { ID($_OAI4_), 7 }, { ID($_MUX_), 4 }, - { ID($_NMUX_), 4 } + { ID($_NMUX_), 4 }, + { ID($_DFF_P_), 1 }, + { ID($_DFF_N_), 1 }, }; - return db; - } - static const dict& cmos_gate_cost() { - static const dict db = { + static const dict cmos_transistors_db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, { ID($_AND_), 6 }, @@ -65,50 +78,22 @@ struct CellCosts { ID($_AOI4_), 8 }, { ID($_OAI4_), 8 }, { ID($_MUX_), 12 }, - { ID($_NMUX_), 10 } + { ID($_NMUX_), 10 }, + { ID($_DFF_P_), 16 }, + { ID($_DFF_N_), 16 }, }; - return db; - } - - dict mod_cost_cache; - const dict *gate_cost = nullptr; - Design *design = nullptr; - - int get(RTLIL::IdString type) const - { - if (gate_cost && gate_cost->count(type)) - return gate_cost->at(type); - - log_warning("Can't determine cost of %s cell.\n", log_id(type)); - return 1; - } - - int get(RTLIL::Cell *cell) - { - if (gate_cost && gate_cost->count(cell->type)) - return gate_cost->at(cell->type); - - if (design && design->module(cell->type) && cell->parameters.empty()) - { - RTLIL::Module *mod = design->module(cell->type); - - if (mod->attributes.count(ID(cost))) - return mod->attributes.at(ID(cost)).as_int(); - - if (mod_cost_cache.count(mod->name)) - return mod_cost_cache.at(mod->name); - - int module_cost = 1; - for (auto c : mod->cells()) - module_cost += get(c); - - mod_cost_cache[mod->name] = module_cost; - return module_cost; + switch (kind_) { + case DEFAULT: + return default_gate_db; + case CMOS: + return cmos_transistors_db; + default: + log_assert(false && "Unreachable: Invalid cell cost kind\n"); } - - log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); - return 1; } + + unsigned int get(RTLIL::Module *mod); + unsigned int get(RTLIL::Cell *cell); }; YOSYS_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index c2bb72f3bb3..1761ddf6892 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -222,16 +222,16 @@ struct statdata_t unsigned int cmos_transistor_count(bool *tran_cnt_exact) { unsigned int tran_cnt = 0; - auto &gate_costs = CellCosts::cmos_gate_cost(); + auto cost_kind = CellCosts::CMOS; + CellCosts costs(cost_kind, nullptr); + auto cell_type_cost = costs.gate_type_cost(); for (auto it : num_cells_by_type) { auto ctype = it.first; auto cnum = it.second; - if (gate_costs.count(ctype)) - tran_cnt += cnum * gate_costs.at(ctype); - else if (ctype.in(ID($_DFF_P_), ID($_DFF_N_))) - tran_cnt += cnum * 16; + if (cell_type_cost.count(ctype)) + tran_cnt += cnum * cell_type_cost.at(ctype); else *tran_cnt_exact = false; } diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index b3f139b72e7..3cd1b6180c6 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -2,4 +2,5 @@ OBJS += passes/hierarchy/hierarchy.o OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o +OBJS += passes/hierarchy/keep_hierarchy.o diff --git a/passes/hierarchy/keep_hierarchy.cc b/passes/hierarchy/keep_hierarchy.cc new file mode 100644 index 00000000000..41ddaa9f7d0 --- /dev/null +++ b/passes/hierarchy/keep_hierarchy.cc @@ -0,0 +1,73 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * 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. + * + */ + +#include "kernel/yosys.h" +#include "kernel/cost.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct KeepHierarchyPass : public Pass { + KeepHierarchyPass() : Pass("keep_hierarchy", "TODO") {} // TODO + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" keep_hierarchy [options]\n"); + log("\n"); + log("Add the keep_hierarchy attribute.\n"); + log("\n"); + log(" -max_cost \n"); + log(" only add the attribute to modules estimated to have more\n"); + log(" than gates\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + unsigned int max_cost = 0; + + log_header(design, "Executing KEEP_HIERARCHY pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-max_cost" && argidx+1 < args.size()) { + max_cost = std::stoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + CellCosts costs(CellCosts::DEFAULT, design); + + for (auto module : design->selected_modules()) { + if (max_cost) { + unsigned int cost = costs.get(module); + if (cost > max_cost) { + log("Marking %s (module too big: %d > %d).\n", log_id(module), cost, max_cost); + module->set_bool_attribute(ID::keep_hierarchy); + } + } else { + log("Marking %s.\n", log_id(module)); + module->set_bool_attribute(ID::keep_hierarchy); + } + } + } +} ZinitPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 42287966288..be02a781d65 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1047,7 +1047,9 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin { log_header(design, "Executing ABC.\n"); - auto &cell_cost = cmos_cost ? CellCosts::cmos_gate_cost() : CellCosts::default_gate_cost(); + auto cost_kind = cmos_cost ? CellCosts::CMOS : CellCosts::DEFAULT; + CellCosts costs(cost_kind, design); + auto cell_type_cost = costs.gate_type_cost(); buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); @@ -1055,42 +1057,42 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); fprintf(f, "GATE ONE 1 Y=CONST1;\n"); - fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_BUF_))); - fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NOT_))); + fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_BUF_))); + fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_NOT_))); if (enabled_gates.count("AND")) - fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_AND_))); + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_AND_))); if (enabled_gates.count("NAND")) - fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NAND_))); + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_NAND_))); if (enabled_gates.count("OR")) - fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_OR_))); + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_OR_))); if (enabled_gates.count("NOR")) - fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_NOR_))); + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_NOR_))); if (enabled_gates.count("XOR")) - fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_XOR_))); + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_type_cost.at(ID($_XOR_))); if (enabled_gates.count("XNOR")) - fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_XNOR_))); + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_type_cost.at(ID($_XNOR_))); if (enabled_gates.count("ANDNOT")) - fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_ANDNOT_))); + fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_type_cost.at(ID($_ANDNOT_))); if (enabled_gates.count("ORNOT")) - fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_ORNOT_))); + fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", cell_type_cost.at(ID($_ORNOT_))); if (enabled_gates.count("AOI3")) - fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_AOI3_))); + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_AOI3_))); if (enabled_gates.count("OAI3")) - fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_OAI3_))); + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_OAI3_))); if (enabled_gates.count("AOI4")) - fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_AOI4_))); + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_AOI4_))); if (enabled_gates.count("OAI4")) - fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", cell_cost.at(ID($_OAI4_))); + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", cell_type_cost.at(ID($_OAI4_))); if (enabled_gates.count("MUX")) - fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_MUX_))); + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_type_cost.at(ID($_MUX_))); if (enabled_gates.count("NMUX")) - fprintf(f, "GATE NMUX %d Y=!((A*B)+(S*B)+(!S*A)); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_cost.at(ID($_NMUX_))); + fprintf(f, "GATE NMUX %d Y=!((A*B)+(S*B)+(!S*A)); PIN * UNKNOWN 1 999 1 0 1 0\n", cell_type_cost.at(ID($_NMUX_))); if (map_mux4) - fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*cell_cost.at(ID($_MUX_))); + fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*cell_type_cost.at(ID($_MUX_))); if (map_mux8) - fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*cell_cost.at(ID($_MUX_))); + fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*cell_type_cost.at(ID($_MUX_))); if (map_mux16) - fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_))); + fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_type_cost.at(ID($_MUX_))); fclose(f); if (!lut_costs.empty()) {