mirror of https://github.com/YosysHQ/yosys.git
cost: add model for techmapped cell count, keep_hierarchy pass with -min_cost parameter
This commit is contained in:
parent
960bca0196
commit
4b29f64142
2
Makefile
2
Makefile
|
@ -625,7 +625,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/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||||
OBJS += kernel/binding.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)
|
ifeq ($(ENABLE_ZLIB),1)
|
||||||
OBJS += kernel/fstdata.o
|
OBJS += kernel/fstdata.o
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -26,7 +26,17 @@ YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
struct CellCosts
|
struct CellCosts
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
dict<RTLIL::IdString, int> mod_cost_cache_;
|
||||||
|
Design *design_ = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CellCosts(RTLIL::Design *design) : design_(design) { }
|
||||||
|
|
||||||
static const dict<RTLIL::IdString, int>& default_gate_cost() {
|
static const dict<RTLIL::IdString, int>& default_gate_cost() {
|
||||||
|
// Default size heuristics for several common PDK standard cells
|
||||||
|
// used by abc and stat
|
||||||
static const dict<RTLIL::IdString, int> db = {
|
static const dict<RTLIL::IdString, int> db = {
|
||||||
{ ID($_BUF_), 1 },
|
{ ID($_BUF_), 1 },
|
||||||
{ ID($_NOT_), 2 },
|
{ ID($_NOT_), 2 },
|
||||||
|
@ -43,12 +53,14 @@ struct CellCosts
|
||||||
{ ID($_AOI4_), 7 },
|
{ ID($_AOI4_), 7 },
|
||||||
{ ID($_OAI4_), 7 },
|
{ ID($_OAI4_), 7 },
|
||||||
{ ID($_MUX_), 4 },
|
{ ID($_MUX_), 4 },
|
||||||
{ ID($_NMUX_), 4 }
|
{ ID($_NMUX_), 4 },
|
||||||
};
|
};
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const dict<RTLIL::IdString, int>& cmos_gate_cost() {
|
static const dict<RTLIL::IdString, int>& cmos_gate_cost() {
|
||||||
|
// Estimated CMOS transistor counts for several common PDK standard cells
|
||||||
|
// used by stat and optionally by abc
|
||||||
static const dict<RTLIL::IdString, int> db = {
|
static const dict<RTLIL::IdString, int> db = {
|
||||||
{ ID($_BUF_), 1 },
|
{ ID($_BUF_), 1 },
|
||||||
{ ID($_NOT_), 2 },
|
{ ID($_NOT_), 2 },
|
||||||
|
@ -65,50 +77,21 @@ struct CellCosts
|
||||||
{ ID($_AOI4_), 8 },
|
{ ID($_AOI4_), 8 },
|
||||||
{ ID($_OAI4_), 8 },
|
{ ID($_OAI4_), 8 },
|
||||||
{ ID($_MUX_), 12 },
|
{ ID($_MUX_), 12 },
|
||||||
{ ID($_NMUX_), 10 }
|
{ ID($_NMUX_), 10 },
|
||||||
|
{ ID($_DFF_P_), 16 },
|
||||||
|
{ ID($_DFF_N_), 16 },
|
||||||
};
|
};
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict<RTLIL::IdString, int> mod_cost_cache;
|
// Get the cell cost for a cell based on its parameters.
|
||||||
const dict<RTLIL::IdString, int> *gate_cost = nullptr;
|
// This cost is an *approximate* upper bound for the number of gates that
|
||||||
Design *design = nullptr;
|
// the cell will get mapped to with "opt -fast; techmap"
|
||||||
|
// The intended usage is for flattening heuristics and similar situations
|
||||||
int get(RTLIL::IdString type) const
|
unsigned int get(RTLIL::Cell *cell);
|
||||||
{
|
// Sum up the cell costs of all cells in the module
|
||||||
if (gate_cost && gate_cost->count(type))
|
// and all its submodules recursively
|
||||||
return gate_cost->at(type);
|
unsigned int get(RTLIL::Module *mod);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
YOSYS_NAMESPACE_END
|
YOSYS_NAMESPACE_END
|
||||||
|
|
|
@ -230,8 +230,6 @@ struct statdata_t
|
||||||
|
|
||||||
if (gate_costs.count(ctype))
|
if (gate_costs.count(ctype))
|
||||||
tran_cnt += cnum * gate_costs.at(ctype);
|
tran_cnt += cnum * gate_costs.at(ctype);
|
||||||
else if (ctype.in(ID($_DFF_P_), ID($_DFF_N_)))
|
|
||||||
tran_cnt += cnum * 16;
|
|
||||||
else
|
else
|
||||||
*tran_cnt_exact = false;
|
*tran_cnt_exact = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
OBJS += passes/hierarchy/hierarchy.o
|
OBJS += passes/hierarchy/hierarchy.o
|
||||||
OBJS += passes/hierarchy/uniquify.o
|
OBJS += passes/hierarchy/uniquify.o
|
||||||
OBJS += passes/hierarchy/submod.o
|
OBJS += passes/hierarchy/submod.o
|
||||||
|
OBJS += passes/hierarchy/keep_hierarchy.o
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||||
|
*
|
||||||
|
* 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 <min_cost>\n");
|
||||||
|
log(" only add the attribute to modules estimated to have more\n");
|
||||||
|
log(" than <min_cost> gates after simple techmapping. Intended\n");
|
||||||
|
log(" for tuning trade-offs between quality and yosys runtime.\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> 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
|
|
@ -23,11 +23,13 @@
|
||||||
#include "kernel/consteval.h"
|
#include "kernel/consteval.h"
|
||||||
#include "kernel/celledges.h"
|
#include "kernel/celledges.h"
|
||||||
#include "kernel/macc.h"
|
#include "kernel/macc.h"
|
||||||
|
#include "kernel/cost.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static int bloat_factor = 1;
|
||||||
static uint32_t xorshift32_state = 123456789;
|
static uint32_t xorshift32_state = 123456789;
|
||||||
|
|
||||||
static uint32_t xorshift32(uint32_t limit) {
|
static uint32_t xorshift32(uint32_t limit) {
|
||||||
|
@ -37,7 +39,7 @@ static uint32_t xorshift32(uint32_t limit) {
|
||||||
return xorshift32_state % 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::Module *module = design->addModule(ID(gold));
|
||||||
RTLIL::Cell *cell = module->addCell(ID(UUT), cell_type);
|
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)))
|
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);
|
int swidth = cell_type == ID($mux) ? 1 : 1 + xorshift32(8);
|
||||||
|
|
||||||
wire = module->addWire(ID::A);
|
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))
|
if (cell_type == ID($bmux))
|
||||||
{
|
{
|
||||||
int width = 1 + xorshift32(8);
|
int width = 1 + xorshift32(8 * bloat_factor);
|
||||||
int swidth = 1 + xorshift32(4);
|
int swidth = 1 + xorshift32(4 * bloat_factor);
|
||||||
|
|
||||||
wire = module->addWire(ID::A);
|
wire = module->addWire(ID::A);
|
||||||
wire->width = width << swidth;
|
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))
|
if (cell_type == ID($demux))
|
||||||
{
|
{
|
||||||
int width = 1 + xorshift32(8);
|
int width = 1 + xorshift32(8 * bloat_factor);
|
||||||
int swidth = 1 + xorshift32(6);
|
int swidth = 1 + xorshift32(6 * bloat_factor);
|
||||||
|
|
||||||
wire = module->addWire(ID::A);
|
wire = module->addWire(ID::A);
|
||||||
wire->width = width;
|
wire->width = width;
|
||||||
|
@ -113,7 +115,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
|
||||||
|
|
||||||
if (cell_type == ID($fa))
|
if (cell_type == ID($fa))
|
||||||
{
|
{
|
||||||
int width = 1 + xorshift32(8);
|
int width = 1 + xorshift32(8 * bloat_factor);
|
||||||
|
|
||||||
wire = module->addWire(ID::A);
|
wire = module->addWire(ID::A);
|
||||||
wire->width = width;
|
wire->width = width;
|
||||||
|
@ -143,7 +145,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
|
||||||
|
|
||||||
if (cell_type == ID($lcu))
|
if (cell_type == ID($lcu))
|
||||||
{
|
{
|
||||||
int width = 1 + xorshift32(8);
|
int width = 1 + xorshift32(8 * bloat_factor);
|
||||||
|
|
||||||
wire = module->addWire(ID::P);
|
wire = module->addWire(ID::P);
|
||||||
wire->width = width;
|
wire->width = width;
|
||||||
|
@ -168,7 +170,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
|
||||||
if (cell_type == ID($macc))
|
if (cell_type == ID($macc))
|
||||||
{
|
{
|
||||||
Macc macc;
|
Macc macc;
|
||||||
int width = 1 + xorshift32(8);
|
int width = 1 + xorshift32(8 * bloat_factor);
|
||||||
int depth = 1 + xorshift32(6);
|
int depth = 1 + xorshift32(6);
|
||||||
int mulbits_a = 0, mulbits_b = 0;
|
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))
|
if (cell_type == ID($lut))
|
||||||
{
|
{
|
||||||
int width = 1 + xorshift32(6);
|
int width = 1 + xorshift32(6 * bloat_factor);
|
||||||
|
|
||||||
wire = module->addWire(ID::A);
|
wire = module->addWire(ID::A);
|
||||||
wire->width = width;
|
wire->width = width;
|
||||||
|
@ -235,8 +237,8 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
|
||||||
|
|
||||||
if (cell_type == ID($sop))
|
if (cell_type == ID($sop))
|
||||||
{
|
{
|
||||||
int width = 1 + xorshift32(8);
|
int width = 1 + xorshift32(8 * bloat_factor);
|
||||||
int depth = 1 + xorshift32(8);
|
int depth = 1 + xorshift32(8 * bloat_factor);
|
||||||
|
|
||||||
wire = module->addWire(ID::A);
|
wire = module->addWire(ID::A);
|
||||||
wire->width = width;
|
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) {
|
if (cell_type_flags.find('A') != std::string::npos) {
|
||||||
wire = module->addWire(ID::A);
|
wire = module->addWire(ID::A);
|
||||||
wire->width = 1 + xorshift32(8);
|
wire->width = 1 + xorshift32(8 * bloat_factor);
|
||||||
wire->port_input = true;
|
wire->port_input = true;
|
||||||
cell->setPort(ID::A, wire);
|
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) {
|
if (cell_type_flags.find('B') != std::string::npos) {
|
||||||
wire = module->addWire(ID::B);
|
wire = module->addWire(ID::B);
|
||||||
if (cell_type_flags.find('h') != std::string::npos)
|
if (cell_type_flags.find('h') != std::string::npos)
|
||||||
wire->width = 1 + xorshift32(6);
|
wire->width = 1 + xorshift32(6 * bloat_factor);
|
||||||
else
|
else
|
||||||
wire->width = 1 + xorshift32(8);
|
wire->width = 1 + xorshift32(8 * bloat_factor);
|
||||||
wire->port_input = true;
|
wire->port_input = true;
|
||||||
cell->setPort(ID::B, wire);
|
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) {
|
if (cell_type_flags.find('Y') != std::string::npos) {
|
||||||
wire = module->addWire(ID::Y);
|
wire = module->addWire(ID::Y);
|
||||||
wire->width = 1 + xorshift32(8);
|
wire->width = 1 + xorshift32(8 * bloat_factor);
|
||||||
wire->port_output = true;
|
wire->port_output = true;
|
||||||
cell->setPort(ID::Y, wire);
|
cell->setPort(ID::Y, wire);
|
||||||
}
|
}
|
||||||
|
@ -380,6 +382,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
|
||||||
module->fixup_ports();
|
module->fixup_ports();
|
||||||
cell->fixup_parameters();
|
cell->fixup_parameters();
|
||||||
cell->check();
|
cell->check();
|
||||||
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_edges_test(RTLIL::Design *design, bool verbose)
|
static void run_edges_test(RTLIL::Design *design, bool verbose)
|
||||||
|
@ -752,6 +755,9 @@ struct TestCellPass : public Pass {
|
||||||
log(" -noeval\n");
|
log(" -noeval\n");
|
||||||
log(" do not check const-eval models\n");
|
log(" do not check const-eval models\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
log(" -noopt\n");
|
||||||
|
log(" do not opt tecchmapped design\n");
|
||||||
|
log("\n");
|
||||||
log(" -edges\n");
|
log(" -edges\n");
|
||||||
log(" test cell edges db creator against sat-based implementation\n");
|
log(" test cell edges db creator against sat-based implementation\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -760,6 +766,11 @@ struct TestCellPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -vlog {filename}\n");
|
log(" -vlog {filename}\n");
|
||||||
log(" create a Verilog test bench to test simlib and write_verilog\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");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design*) override
|
void execute(std::vector<std::string> args, RTLIL::Design*) override
|
||||||
|
@ -774,7 +785,9 @@ struct TestCellPass : public Pass {
|
||||||
bool constmode = false;
|
bool constmode = false;
|
||||||
bool nosat = false;
|
bool nosat = false;
|
||||||
bool noeval = false;
|
bool noeval = false;
|
||||||
|
bool noopt = false;
|
||||||
bool edges = false;
|
bool edges = false;
|
||||||
|
bool check_cost = false;
|
||||||
|
|
||||||
int argidx;
|
int argidx;
|
||||||
for (argidx = 1; argidx < GetSize(args); argidx++)
|
for (argidx = 1; argidx < GetSize(args); argidx++)
|
||||||
|
@ -828,6 +841,10 @@ struct TestCellPass : public Pass {
|
||||||
noeval = true;
|
noeval = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-noopt") {
|
||||||
|
noopt = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-edges") {
|
if (args[argidx] == "-edges") {
|
||||||
edges = true;
|
edges = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -842,6 +859,14 @@ struct TestCellPass : public Pass {
|
||||||
log_cmd_error("Failed to open output file `%s'.\n", args[argidx].c_str());
|
log_cmd_error("Failed to open output file `%s'.\n", args[argidx].c_str());
|
||||||
continue;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -965,21 +990,30 @@ struct TestCellPass : public Pass {
|
||||||
|
|
||||||
std::vector<std::string> uut_names;
|
std::vector<std::string> 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++)
|
for (int i = 0; i < num_iter; i++)
|
||||||
{
|
{
|
||||||
|
Cell* uut = nullptr;
|
||||||
RTLIL::Design *design = new RTLIL::Design;
|
RTLIL::Design *design = new RTLIL::Design;
|
||||||
if (cell_type == ID(rtlil))
|
if (cell_type == ID(rtlil))
|
||||||
Frontend::frontend_call(design, NULL, std::string(), "rtlil " + rtlil_file);
|
Frontend::frontend_call(design, NULL, std::string(), "rtlil " + rtlil_file);
|
||||||
else
|
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()) {
|
if (!write_prefix.empty()) {
|
||||||
Pass::call(design, stringf("write_rtlil %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i));
|
Pass::call(design, stringf("write_rtlil %s_%s_%05d.il", write_prefix.c_str(), cell_type.c_str()+1, i));
|
||||||
} else if (edges) {
|
} else if (edges) {
|
||||||
Pass::call(design, "dump gold");
|
Pass::call(design, "dump gold");
|
||||||
run_edges_test(design, verbose);
|
run_edges_test(design, verbose);
|
||||||
} else {
|
} 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)
|
if (!nosat)
|
||||||
Pass::call(design, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter");
|
Pass::call(design, "miter -equiv -flatten -make_outputs -ignore_gold_x gold gate miter");
|
||||||
if (verbose)
|
if (verbose)
|
||||||
|
@ -997,10 +1031,44 @@ struct TestCellPass : public Pass {
|
||||||
}
|
}
|
||||||
if (!noeval)
|
if (!noeval)
|
||||||
run_eval_test(design, verbose, nosat, uut_name, vlog_file);
|
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;
|
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()) {
|
if (vlog_file.is_open()) {
|
||||||
vlog_file << "\nmodule testbench;\n";
|
vlog_file << "\nmodule testbench;\n";
|
||||||
for (auto &uut : uut_names)
|
for (auto &uut : uut_names)
|
||||||
|
|
Loading…
Reference in New Issue