diff --git a/Makefile b/Makefile index a8d38770769..f6186499307 100644 --- a/Makefile +++ b/Makefile @@ -635,7 +635,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/cost.cc b/kernel/cost.cc new file mode 100644 index 00000000000..5d897ab7a1c --- /dev/null +++ b/kernel/cost.cc @@ -0,0 +1,195 @@ +#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) +{ + if ( + // equality + type.in(ID($bweqx), ID($nex), ID($eqx)) || + // basic logic + type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($not)) || + // mux + type.in(ID($bwmux), ID($mux)) || + // others + type == ID($tribuf)) { + return 1; + } else if (type == ID($neg)) { + return 4; + } else if (type == ID($demux)) { + return 2; + } else if (type == ID($fa)) { + return 5; + } else if (type.in(ID($add), ID($sub), ID($alu))) { + // multi-bit adders + return 8; + } else if (type.in(ID($shl), ID($sshl))) { + // left shift + return 10; + } + return 0; +} + +static unsigned int max_inp_coef(RTLIL::IdString type) +{ + if ( + // binop reduce + type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)) || + // others + type.in(ID($logic_not), ID($pmux), ID($bmux))) { + return 1; + } else if ( + // equality + type.in(ID($eq), ID($ne)) || + // logic + type.in(ID($logic_and), ID($logic_or))) { + return 2; + } else if (type == ID($lcu)) { + return 5; + } else if (type.in(ID($lt), ID($le), ID($ge), ID($gt))) { + // comparison + return 7; + } + return 0; +} + +static unsigned int sum_coef(RTLIL::IdString type) +{ + if (type.in(ID($shr), ID($sshr))) { + // right shift + return 4; + } else if (type.in(ID($shift), ID($shiftx))) { + // shift + return 8; + } + return 0; +} + +static unsigned int is_div_mod(RTLIL::IdString type) +{ + return (type == ID($div) || type == ID($divfloor) || type == ID($mod) || type == ID($modfloor)); +} + +static bool is_free(RTLIL::IdString type) +{ + return ( + // tags + type.in(ID($overwrite_tag), ID($set_tag), ID($original_tag), ID($get_tag)) || + // formal + type.in(ID($check), ID($equiv), ID($initstate), ID($assert), ID($assume), ID($live), ID($cover), ID($fair)) || + type.in(ID($allseq), ID($allconst), ID($anyseq), ID($anyconst), ID($anyinit)) || + // utilities + type.in(ID($scopeinfo), ID($print)) || + // real but free + type.in(ID($concat), ID($slice), ID($pos)) || + // specify + type.in(ID($specrule), ID($specify2), ID($specify3))); +} + +unsigned int max_inp_width(RTLIL::Cell *cell) +{ + unsigned int max = 0; + RTLIL::IdString input_width_params[] = { + ID::WIDTH, + ID::A_WIDTH, + ID::B_WIDTH, + ID::S_WIDTH, + }; + + if (cell->type == ID($bmux)) + return cell->getParam(ID::WIDTH).as_int() << cell->getParam(ID::S_WIDTH).as_int(); + + for (RTLIL::IdString param : input_width_params) + if (cell->hasParam(param)) + max = std::max(max, (unsigned int)cell->getParam(param).as_int()); + return max; +} + +unsigned int port_width_sum(RTLIL::Cell *cell) +{ + 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(); + + return sum; +} + +unsigned int CellCosts::get(RTLIL::Cell *cell) +{ + + // simple 1-bit cells + if (cmos_gate_cost().count(cell->type)) + return 1; + + 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(); + if (cell->type == ID($demux)) + width <<= cell->getParam(ID::S_WIDTH).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 = port_width_sum(cell); + 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 = max_inp_width(cell); + log_debug("%s max*coef %d * %d\n", cell->name.c_str(), max, max_inp_coef(cell->type)); + return max * max_inp_coef(cell->type); + } else if (is_div_mod(cell->type) || cell->type == ID($mul)) { + // quadratic with sum of port widths + unsigned int sum = port_width_sum(cell); + unsigned int coef = cell->type == ID($mul) ? 3 : 5; + log_debug("%s coef*(sum**2) %d * %d\n", cell->name.c_str(), coef, sum * sum); + return coef * sum * sum; + } else if (cell->type == ID($lut)) { + int width = cell->getParam(ID::WIDTH).as_int(); + unsigned int cost = 1U << (unsigned int)width; + log_debug("%s is 2**%d\n", cell->name.c_str(), width); + return cost; + } else if (cell->type == ID($sop)) { + int width = cell->getParam(ID::WIDTH).as_int(); + int depth = cell->getParam(ID::DEPTH).as_int(); + log_debug("%s is (2*%d + 1)*%d\n", cell->name.c_str(), width, depth); + return (2 * width + 1) * depth; + } else if (is_free(cell->type)) { + log_debug("%s is free\n", cell->name.c_str()); + return 0; + } + // TODO: $fsm $mem.* $macc + // 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..15d74d7b30e 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -26,7 +26,17 @@ YOSYS_NAMESPACE_BEGIN struct CellCosts { + + private: + dict mod_cost_cache_; + Design *design_ = nullptr; + + public: + CellCosts(RTLIL::Design *design) : design_(design) { } + static const dict& default_gate_cost() { + // Default size heuristics for several common PDK standard cells + // used by abc and stat static const dict db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, @@ -43,12 +53,14 @@ struct CellCosts { ID($_AOI4_), 7 }, { ID($_OAI4_), 7 }, { ID($_MUX_), 4 }, - { ID($_NMUX_), 4 } + { ID($_NMUX_), 4 }, }; return db; } static const dict& cmos_gate_cost() { + // Estimated CMOS transistor counts for several common PDK standard cells + // used by stat and optionally by abc static const dict db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, @@ -65,50 +77,21 @@ 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; - } - - log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); - return 1; - } + // Get the cell cost for a cell based on its parameters. + // This cost is an *approximate* upper bound for the number of gates that + // the cell will get mapped to with "opt -fast; techmap" + // The intended usage is for flattening heuristics and similar situations + unsigned int get(RTLIL::Cell *cell); + // Sum up the cell costs of all cells in the module + // and all its submodules recursively + unsigned int get(RTLIL::Module *mod); }; YOSYS_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index c2bb72f3bb3..379ce209790 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -230,8 +230,6 @@ struct statdata_t 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; 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..6b947e67370 --- /dev/null +++ b/passes/hierarchy/keep_hierarchy.cc @@ -0,0 +1,74 @@ +/* + * 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", "add the keep_hierarchy attribute") {} + 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(" -min_cost \n"); + log(" only add the attribute to modules estimated to have more\n"); + log(" than gates after simple techmapping. Intended\n"); + log(" for tuning trade-offs between quality and yosys runtime.\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + unsigned int min_cost = 0; + + log_header(design, "Executing KEEP_HIERARCHY pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-min_cost" && argidx+1 < args.size()) { + min_cost = std::stoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + CellCosts costs(design); + + for (auto module : design->selected_modules()) { + if (min_cost) { + unsigned int cost = costs.get(module); + if (cost > min_cost) { + log("Marking %s (module too big: %d > %d).\n", log_id(module), cost, min_cost); + module->set_bool_attribute(ID::keep_hierarchy); + } + } else { + log("Marking %s.\n", log_id(module)); + module->set_bool_attribute(ID::keep_hierarchy); + } + } + } +} KeepHierarchyPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index e21ec452cc8..fc2aac2f83d 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -23,11 +23,13 @@ #include "kernel/consteval.h" #include "kernel/celledges.h" #include "kernel/macc.h" +#include "kernel/cost.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +static int bloat_factor = 1; static uint32_t xorshift32_state = 123456789; static uint32_t xorshift32(uint32_t limit) { @@ -37,7 +39,7 @@ static uint32_t xorshift32(uint32_t limit) { return xorshift32_state % limit; } -static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode, bool muxdiv) +static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, std::string cell_type_flags, bool constmode, bool muxdiv) { RTLIL::Module *module = design->addModule(ID(gold)); RTLIL::Cell *cell = module->addCell(ID(UUT), cell_type); @@ -45,7 +47,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type.in(ID($mux), ID($pmux))) { - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); int swidth = cell_type == ID($mux) ? 1 : 1 + xorshift32(8); wire = module->addWire(ID::A); @@ -71,8 +73,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($bmux)) { - int width = 1 + xorshift32(8); - int swidth = 1 + xorshift32(4); + int width = 1 + xorshift32(8 * bloat_factor); + int swidth = 1 + xorshift32(4 * bloat_factor); wire = module->addWire(ID::A); wire->width = width << swidth; @@ -92,8 +94,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($demux)) { - int width = 1 + xorshift32(8); - int swidth = 1 + xorshift32(6); + int width = 1 + xorshift32(8 * bloat_factor); + int swidth = 1 + xorshift32(6 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -113,7 +115,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($fa)) { - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -143,7 +145,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($lcu)) { - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); wire = module->addWire(ID::P); wire->width = width; @@ -168,7 +170,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($macc)) { Macc macc; - int width = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); int depth = 1 + xorshift32(6); int mulbits_a = 0, mulbits_b = 0; @@ -215,7 +217,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($lut)) { - int width = 1 + xorshift32(6); + int width = 1 + xorshift32(6 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -235,8 +237,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type == ID($sop)) { - int width = 1 + xorshift32(8); - int depth = 1 + xorshift32(8); + int width = 1 + xorshift32(8 * bloat_factor); + int depth = 1 + xorshift32(8 * bloat_factor); wire = module->addWire(ID::A); wire->width = width; @@ -270,7 +272,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire(ID::A); - wire->width = 1 + xorshift32(8); + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_input = true; cell->setPort(ID::A, wire); } @@ -278,9 +280,9 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type_flags.find('B') != std::string::npos) { wire = module->addWire(ID::B); if (cell_type_flags.find('h') != std::string::npos) - wire->width = 1 + xorshift32(6); + wire->width = 1 + xorshift32(6 * bloat_factor); else - wire->width = 1 + xorshift32(8); + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_input = true; cell->setPort(ID::B, wire); } @@ -301,7 +303,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, if (cell_type_flags.find('Y') != std::string::npos) { wire = module->addWire(ID::Y); - wire->width = 1 + xorshift32(8); + wire->width = 1 + xorshift32(8 * bloat_factor); wire->port_output = true; cell->setPort(ID::Y, wire); } @@ -380,6 +382,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, module->fixup_ports(); cell->fixup_parameters(); cell->check(); + return cell; } static void run_edges_test(RTLIL::Design *design, bool verbose) @@ -752,6 +755,9 @@ struct TestCellPass : public Pass { log(" -noeval\n"); log(" do not check const-eval models\n"); log("\n"); + log(" -noopt\n"); + log(" do not opt tecchmapped design\n"); + log("\n"); log(" -edges\n"); log(" test cell edges db creator against sat-based implementation\n"); log("\n"); @@ -760,6 +766,11 @@ struct TestCellPass : public Pass { log("\n"); log(" -vlog {filename}\n"); log(" create a Verilog test bench to test simlib and write_verilog\n"); + log(" -bloat {factor}\n"); + log(" increase cell size limits b{factor} times where possible\n"); + log(" -check_cost\n"); + log(" check if the estimated cell cost is a valid upper bound for\n"); + log(" the techmapped cell count \n"); log("\n"); } void execute(std::vector args, RTLIL::Design*) override @@ -774,7 +785,9 @@ struct TestCellPass : public Pass { bool constmode = false; bool nosat = false; bool noeval = false; + bool noopt = false; bool edges = false; + bool check_cost = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -828,6 +841,10 @@ struct TestCellPass : public Pass { noeval = true; continue; } + if (args[argidx] == "-noopt") { + noopt = true; + continue; + } if (args[argidx] == "-edges") { edges = true; continue; @@ -842,6 +859,14 @@ struct TestCellPass : public Pass { log_cmd_error("Failed to open output file `%s'.\n", args[argidx].c_str()); continue; } + if (args[argidx] == "-bloat" && argidx+1 < GetSize(args)) { + bloat_factor = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-check_cost") { + check_cost = true; + continue; + } break; } @@ -965,21 +990,30 @@ struct TestCellPass : public Pass { std::vector uut_names; - for (auto cell_type : selected_cell_types) + for (auto cell_type : selected_cell_types) { + // Cells that failed cell cost check + int failed = 0; + // How much bigger is the worst offender than estimated? + int worst_abs = 0; + // How many times is it bigger than estimated? + float worst_rel = 0.0; for (int i = 0; i < num_iter; i++) { + Cell* uut = nullptr; RTLIL::Design *design = new RTLIL::Design; if (cell_type == ID(rtlil)) Frontend::frontend_call(design, NULL, std::string(), "rtlil " + rtlil_file); else - create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv); + uut = create_gold_module(design, cell_type, cell_types.at(cell_type), constmode, muxdiv); if (!write_prefix.empty()) { Pass::call(design, stringf("write_rtlil %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i)); } else if (edges) { Pass::call(design, "dump gold"); run_edges_test(design, verbose); } else { - Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..; opt -fast gate", techmap_cmd.c_str())); + Pass::call(design, stringf("copy gold gate; cd gate; %s; cd ..", techmap_cmd.c_str())); + if (!noopt) + Pass::call(design, "opt -fast gate"); if (!nosat) Pass::call(design, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter"); if (verbose) @@ -997,10 +1031,44 @@ struct TestCellPass : public Pass { } if (!noeval) run_eval_test(design, verbose, nosat, uut_name, vlog_file); + if (check_cost && uut) { + Pass::call(design, "select gate"); + int num_cells = 0; + for (auto mod : design->selected_modules()) { + // Expected to run once + for (auto cell : mod->selected_cells()) { + (void) cell; + num_cells++; + } + } + CellCosts costs(design); + Pass::call(design, "select gold"); + for (auto mod : design->selected_modules()) { + log_assert(mod->name.str() == "\\gold"); + // Expected to run once + int num_cells_estimate = costs.get(uut); + if (num_cells <= num_cells_estimate) { + log_debug("Correct upper bound for %s: %d <= %d\n", cell_type.c_str(), num_cells, num_cells_estimate); + } else { + failed++; + if (worst_abs < num_cells - num_cells_estimate) { + worst_abs = num_cells - num_cells_estimate; + worst_rel = (float)(num_cells - num_cells_estimate) / (float)num_cells_estimate; + } + log_warning("Upper bound violated for %s: %d > %d\n", cell_type.c_str(), num_cells, num_cells_estimate); + } + } + } } delete design; } - + if (check_cost && failed) { + log_warning("Cell type %s cost underestimated in %.1f%% cases " + "with worst offender being by %d (%.1f%%)\n", + cell_type.c_str(), 100 * (float)failed / (float)num_iter, + worst_abs, 100 * worst_rel); + } + } if (vlog_file.is_open()) { vlog_file << "\nmodule testbench;\n"; for (auto &uut : uut_names)