Skip to content

Commit

Permalink
Merge pull request #4344 from widlarizer/emil/keep_hierarchy
Browse files Browse the repository at this point in the history
cost: add keep_hierarchy pass with min_cost argument
  • Loading branch information
widlarizer authored Jul 29, 2024
2 parents 9f869b2 + 4b29f64 commit 92cac63
Show file tree
Hide file tree
Showing 7 changed files with 383 additions and 64 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
195 changes: 195 additions & 0 deletions kernel/cost.cc
Original file line number Diff line number Diff line change
@@ -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;
}
65 changes: 24 additions & 41 deletions kernel/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,17 @@ YOSYS_NAMESPACE_BEGIN

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() {
// Default size heuristics for several common PDK standard cells
// used by abc and stat
static const dict<RTLIL::IdString, int> db = {
{ ID($_BUF_), 1 },
{ ID($_NOT_), 2 },
Expand All @@ -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<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 = {
{ ID($_BUF_), 1 },
{ ID($_NOT_), 2 },
Expand All @@ -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<RTLIL::IdString, int> mod_cost_cache;
const dict<RTLIL::IdString, int> *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
Expand Down
2 changes: 0 additions & 2 deletions passes/cmds/stat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions passes/hierarchy/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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

74 changes: 74 additions & 0 deletions passes/hierarchy/keep_hierarchy.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <[email protected]>
*
* 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
Loading

0 comments on commit 92cac63

Please sign in to comment.