mirror of https://github.com/YosysHQ/yosys.git
Compare commits
17 Commits
2b960ab9b5
...
1ac2c1a937
Author | SHA1 | Date |
---|---|---|
Martin Povišer | 1ac2c1a937 | |
Emil J | 5b6baa3ef1 | |
Martin Povišer | 53a4ec375b | |
George Rennie | 9043dc0ad6 | |
Emil J. Tywoniak | 4d96cbec75 | |
Emil J. Tywoniak | 983c54c75f | |
Emil J. Tywoniak | a5bc36f77e | |
Emil J. Tywoniak | e6793da9a0 | |
Emil J. Tywoniak | b08441d95c | |
Emil J. Tywoniak | 1e3f8cc630 | |
Emil J. Tywoniak | c921d85a85 | |
Emil J. Tywoniak | 45880ea7f2 | |
Martin Povišer | 69a36aec3b | |
Martin Povišer | 426ef53c20 | |
Martin Povišer | c8fffce2b5 | |
Martin Povišer | cf79630be0 | |
Martin Povišer | 2425352551 |
|
@ -591,6 +591,9 @@ Non-standard or SystemVerilog features for formal verification
|
||||||
``@(posedge <netname>)`` or ``@(negedge <netname>)`` when ``<netname>``
|
``@(posedge <netname>)`` or ``@(negedge <netname>)`` when ``<netname>``
|
||||||
is marked with the ``(* gclk *)`` Verilog attribute.
|
is marked with the ``(* gclk *)`` Verilog attribute.
|
||||||
|
|
||||||
|
- The `gate_cost_equivalent` attribute on a module can be used to specify
|
||||||
|
the estimated cost of a module as an equivalent number of basic gate
|
||||||
|
instances.
|
||||||
|
|
||||||
Supported features from SystemVerilog
|
Supported features from SystemVerilog
|
||||||
=====================================
|
=====================================
|
||||||
|
|
|
@ -23,20 +23,77 @@
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct ThresholdHiearchyKeeping {
|
||||||
|
Design *design;
|
||||||
|
CellCosts costs;
|
||||||
|
dict<Module *, int> done;
|
||||||
|
pool<Module *> in_progress;
|
||||||
|
uint64_t threshold;
|
||||||
|
|
||||||
|
ThresholdHiearchyKeeping(Design *design, uint64_t threshold)
|
||||||
|
: design(design), costs(design), threshold(threshold) {}
|
||||||
|
|
||||||
|
uint64_t visit(RTLIL::Module *module) {
|
||||||
|
if (module->has_attribute(ID(gate_cost_equivalent)))
|
||||||
|
return module->attributes[ID(gate_cost_equivalent)].as_int();
|
||||||
|
|
||||||
|
if (module->get_blackbox_attribute())
|
||||||
|
log_error("Missing cost information on instanced blackbox %s\n", log_id(module));
|
||||||
|
|
||||||
|
if (done.count(module))
|
||||||
|
return done.at(module);
|
||||||
|
|
||||||
|
if (in_progress.count(module))
|
||||||
|
log_error("Circular hierarchy\n");
|
||||||
|
in_progress.insert(module);
|
||||||
|
|
||||||
|
uint64_t size = 0;
|
||||||
|
module->has_processes_warn();
|
||||||
|
|
||||||
|
for (auto cell : module->cells()) {
|
||||||
|
if (!cell->type.isPublic()) {
|
||||||
|
size += costs.get(cell);
|
||||||
|
} else {
|
||||||
|
RTLIL::Module *submodule = design->module(cell->type);
|
||||||
|
if (!submodule)
|
||||||
|
log_error("Hierarchy contains unknown module '%s' (instanced as %s in %s)\n",
|
||||||
|
log_id(cell->type), log_id(cell), log_id(module));
|
||||||
|
size += visit(submodule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > threshold) {
|
||||||
|
log("Keeping %s (estimated size above threshold: %llu > %llu).\n", log_id(module), size, threshold);
|
||||||
|
module->set_bool_attribute(ID::keep_hierarchy);
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_progress.erase(module);
|
||||||
|
done[module] = size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct KeepHierarchyPass : public Pass {
|
struct KeepHierarchyPass : public Pass {
|
||||||
KeepHierarchyPass() : Pass("keep_hierarchy", "add the keep_hierarchy attribute") {}
|
KeepHierarchyPass() : Pass("keep_hierarchy", "selectively add the keep_hierarchy attribute") {}
|
||||||
void help() override
|
void help() override
|
||||||
{
|
{
|
||||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" keep_hierarchy [options]\n");
|
log(" keep_hierarchy [options] [selection]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Add the keep_hierarchy attribute.\n");
|
log("Add the keep_hierarchy attribute.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -min_cost <min_cost>\n");
|
log(" -min_cost <min_cost>\n");
|
||||||
log(" only add the attribute to modules estimated to have more\n");
|
log(" only add the attribute to modules estimated to have more than <min_cost>\n");
|
||||||
log(" than <min_cost> gates after simple techmapping. Intended\n");
|
log(" gates after simple techmapping. Intended for tuning trade-offs between\n");
|
||||||
log(" for tuning trade-offs between quality and yosys runtime.\n");
|
log(" quality and yosys runtime.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" When evaluating a module's cost, gates which are within a submodule\n");
|
||||||
|
log(" which is marked with the keep_hierarchy attribute are not counted\n");
|
||||||
|
log(" towards the upper module's cost. This applies to both when the attribute\n");
|
||||||
|
log(" was added by this command or was pre-existing.\n");
|
||||||
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
|
@ -54,16 +111,15 @@ struct KeepHierarchyPass : public Pass {
|
||||||
}
|
}
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
CellCosts costs(design);
|
if (min_cost) {
|
||||||
|
RTLIL::Module *top = design->top_module();
|
||||||
|
if (!top)
|
||||||
|
log_cmd_error("'-min_cost' mode requires a single top module in the design\n");
|
||||||
|
|
||||||
for (auto module : design->selected_modules()) {
|
ThresholdHiearchyKeeping worker(design, min_cost);
|
||||||
if (min_cost) {
|
worker.visit(top);
|
||||||
unsigned int cost = costs.get(module);
|
} else {
|
||||||
if (cost > min_cost) {
|
for (auto module : design->selected_modules()) {
|
||||||
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));
|
log("Marking %s.\n", log_id(module));
|
||||||
module->set_bool_attribute(ID::keep_hierarchy);
|
module->set_bool_attribute(ID::keep_hierarchy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "kernel/yosys.h"
|
#include "kernel/yosys.h"
|
||||||
#include "kernel/ff.h"
|
#include "kernel/ff.h"
|
||||||
|
#include "libparse.h"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
|
@ -10,6 +11,7 @@ struct ClockGateCell {
|
||||||
IdString ce_pin;
|
IdString ce_pin;
|
||||||
IdString clk_in_pin;
|
IdString clk_in_pin;
|
||||||
IdString clk_out_pin;
|
IdString clk_out_pin;
|
||||||
|
std::vector<IdString> tie_lo_pins;
|
||||||
};
|
};
|
||||||
|
|
||||||
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
|
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
|
||||||
|
@ -37,6 +39,166 @@ ClockGateCell icg_from_arg(std::string& name, std::string& str) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::pair<std::optional<ClockGateCell>, std::optional<ClockGateCell>>
|
||||||
|
find_icgs(std::string filename, std::vector<std::string> const& dont_use_cells) {
|
||||||
|
std::ifstream f;
|
||||||
|
f.open(filename.c_str());
|
||||||
|
if (f.fail())
|
||||||
|
log_cmd_error("Can't open liberty file `%s': %s\n", filename.c_str(), strerror(errno));
|
||||||
|
LibertyParser libparser(f);
|
||||||
|
f.close();
|
||||||
|
auto ast = libparser.ast;
|
||||||
|
|
||||||
|
// We will pick the most suitable ICG absed on tie_lo count and area
|
||||||
|
struct ICGRankable : public ClockGateCell { double area; };
|
||||||
|
std::optional<ICGRankable> best_pos;
|
||||||
|
std::optional<ICGRankable> best_neg;
|
||||||
|
|
||||||
|
if (ast->id != "library")
|
||||||
|
log_error("Format error in liberty file.\n");
|
||||||
|
|
||||||
|
// This is a lot of boilerplate, isn't it?
|
||||||
|
for (auto cell : ast->children)
|
||||||
|
{
|
||||||
|
if (cell->id != "cell" || cell->args.size() != 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const LibertyAst *dn = cell->find("dont_use");
|
||||||
|
if (dn != nullptr && dn->value == "true")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool dont_use = false;
|
||||||
|
for (auto dont_use_cell : dont_use_cells)
|
||||||
|
{
|
||||||
|
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
|
||||||
|
{
|
||||||
|
dont_use = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dont_use)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const LibertyAst *icg_kind_ast = cell->find("clock_gating_integrated_cell");
|
||||||
|
if (icg_kind_ast == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto cell_name = cell->args[0];
|
||||||
|
auto icg_kind = icg_kind_ast->value;
|
||||||
|
auto starts_with = [&](std::string prefix) {
|
||||||
|
return icg_kind.compare(0, prefix.size(), prefix) == 0;
|
||||||
|
};
|
||||||
|
bool clk_pol;
|
||||||
|
if (icg_kind == "latch_posedge" || starts_with("latch_posedge_")) {
|
||||||
|
clk_pol = true;
|
||||||
|
} else if (icg_kind == "latch_negedge" || starts_with("latch_negedge_")) {
|
||||||
|
clk_pol = false;
|
||||||
|
} else {
|
||||||
|
log("Ignoring ICG primitive %s of kind '%s'\n", cell_name.c_str(), icg_kind.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("maybe valid icg: %s\n", cell_name.c_str());
|
||||||
|
ClockGateCell icg_interface;
|
||||||
|
icg_interface.name = RTLIL::escape_id(cell_name);
|
||||||
|
|
||||||
|
for (auto pin : cell->children) {
|
||||||
|
if (pin->id != "pin" || pin->args.size() != 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (auto clk = pin->find("clock_gate_clock_pin")) {
|
||||||
|
if (!icg_interface.clk_in_pin.empty()) {
|
||||||
|
log_warning("Malformed liberty file - multiple clock_gate_clock_pin in cell %s\n",
|
||||||
|
cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
icg_interface.clk_in_pin = RTLIL::escape_id(pin->args[0]);
|
||||||
|
} else if (auto gclk = pin->find("clock_gate_out_pin")) {
|
||||||
|
if (!icg_interface.clk_out_pin.empty()) {
|
||||||
|
log_warning("Malformed liberty file - multiple clock_gate_out_pin in cell %s\n",
|
||||||
|
cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
icg_interface.clk_out_pin = RTLIL::escape_id(pin->args[0]);
|
||||||
|
} else if (auto en = pin->find("clock_gate_enable_pin")) {
|
||||||
|
if (!icg_interface.ce_pin.empty()) {
|
||||||
|
log_warning("Malformed liberty file - multiple clock_gate_enable_pin in cell %s\n",
|
||||||
|
cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
} else
|
||||||
|
icg_interface.ce_pin = RTLIL::escape_id(pin->args[0]);
|
||||||
|
} else if (auto se = pin->find("clock_gate_test_pin")) {
|
||||||
|
icg_interface.tie_lo_pins.push_back(RTLIL::escape_id(pin->args[0]));
|
||||||
|
} else {
|
||||||
|
const LibertyAst *dir = pin->find("direction");
|
||||||
|
if (dir->value == "internal")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
log_warning("Malformed liberty file - extra pin %s in cell %s\n",
|
||||||
|
pin->args[0].c_str(), cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icg_interface.clk_in_pin.empty()) {
|
||||||
|
log_warning("Malformed liberty file - missing clock_gate_clock_pin in cell %s",
|
||||||
|
cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (icg_interface.clk_out_pin.empty()) {
|
||||||
|
log_warning("Malformed liberty file - missing clock_gate_out_pin in cell %s",
|
||||||
|
cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (icg_interface.ce_pin.empty()) {
|
||||||
|
log_warning("Malformed liberty file - missing clock_gate_enable_pin in cell %s",
|
||||||
|
cell_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double area = 0;
|
||||||
|
const LibertyAst *ar = cell->find("area");
|
||||||
|
if (ar != nullptr && !ar->value.empty())
|
||||||
|
area = atof(ar->value.c_str());
|
||||||
|
|
||||||
|
std::optional<ICGRankable>& icg_to_beat = clk_pol ? best_pos : best_neg;
|
||||||
|
|
||||||
|
bool winning = false;
|
||||||
|
if (icg_to_beat) {
|
||||||
|
log_debug("ties: %zu ? %zu\n", icg_to_beat->tie_lo_pins.size(),
|
||||||
|
icg_interface.tie_lo_pins.size());
|
||||||
|
log_debug("area: %f ? %f\n", icg_to_beat->area, area);
|
||||||
|
|
||||||
|
// Prefer fewer test enables over area reduction (unlikely to matter)
|
||||||
|
auto goal = std::make_pair(icg_to_beat->tie_lo_pins.size(), icg_to_beat->area);
|
||||||
|
auto cost = std::make_pair(icg_interface.tie_lo_pins.size(), area);
|
||||||
|
winning = cost < goal;
|
||||||
|
|
||||||
|
if (winning)
|
||||||
|
log_debug("%s beats %s\n", icg_interface.name.c_str(), icg_to_beat->name.c_str());
|
||||||
|
} else {
|
||||||
|
log_debug("%s is the first of its polarity\n", icg_interface.name.c_str());
|
||||||
|
winning = true;
|
||||||
|
}
|
||||||
|
if (winning) {
|
||||||
|
ICGRankable new_icg {icg_interface, area};
|
||||||
|
icg_to_beat.emplace(new_icg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ClockGateCell> pos;
|
||||||
|
std::optional<ClockGateCell> neg;
|
||||||
|
if (best_pos) {
|
||||||
|
log("Selected rising edge ICG %s from Liberty file\n", best_pos->name.c_str());
|
||||||
|
pos.emplace(*best_pos);
|
||||||
|
}
|
||||||
|
if (best_neg) {
|
||||||
|
log("Selected falling edge ICG %s from Liberty file\n", best_neg->name.c_str());
|
||||||
|
neg.emplace(*best_neg);
|
||||||
|
}
|
||||||
|
return std::make_pair(pos, neg);
|
||||||
|
}
|
||||||
|
|
||||||
struct ClockgatePass : public Pass {
|
struct ClockgatePass : public Pass {
|
||||||
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
|
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
|
||||||
void help() override {
|
void help() override {
|
||||||
|
@ -60,12 +222,20 @@ struct ClockgatePass : public Pass {
|
||||||
log(" user-specified <celltype> ICG (integrated clock gating)\n");
|
log(" user-specified <celltype> ICG (integrated clock gating)\n");
|
||||||
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
|
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
|
||||||
log(" The ICG's clock enable pin must be active high.\n");
|
log(" The ICG's clock enable pin must be active high.\n");
|
||||||
|
log(" -liberty <filename>\n");
|
||||||
|
log(" If specified, ICGs will be selected from the liberty file\n");
|
||||||
|
log(" if available. Priority is given to cells with fewer tie_lo\n");
|
||||||
|
log(" inputs and smaller size. This removes the need to manually\n");
|
||||||
|
log(" specify -pos or -neg and -tie_lo.\n");
|
||||||
|
log(" -dont_use <celltype>\n");
|
||||||
|
log(" Cells <celltype> won't be considered when searching for ICGs\n");
|
||||||
|
log(" in the liberty file specified by -liberty.\n");
|
||||||
log(" -tie_lo <port_name>\n");
|
log(" -tie_lo <port_name>\n");
|
||||||
log(" Port <port_name> of the ICG will be tied to zero.\n");
|
log(" Port <port_name> of the ICG will be tied to zero.\n");
|
||||||
log(" Intended for DFT scan-enable pins.\n");
|
log(" Intended for DFT scan-enable pins.\n");
|
||||||
log(" -min_net_size <n>\n");
|
log(" -min_net_size <n>\n");
|
||||||
log(" Only transform sets of at least <n> eligible FFs.\n");
|
log(" Only transform sets of at least <n> eligible FFs.\n");
|
||||||
// log(" \n");
|
log(" \n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// One ICG will be generated per ClkNetInfo
|
// One ICG will be generated per ClkNetInfo
|
||||||
|
@ -110,7 +280,9 @@ struct ClockgatePass : public Pass {
|
||||||
|
|
||||||
std::optional<ClockGateCell> pos_icg_desc;
|
std::optional<ClockGateCell> pos_icg_desc;
|
||||||
std::optional<ClockGateCell> neg_icg_desc;
|
std::optional<ClockGateCell> neg_icg_desc;
|
||||||
std::vector<std::string> tie_lo_ports;
|
std::vector<std::string> tie_lo_pins;
|
||||||
|
std::string liberty_file;
|
||||||
|
std::vector<std::string> dont_use_cells;
|
||||||
int min_net_size = 0;
|
int min_net_size = 0;
|
||||||
|
|
||||||
size_t argidx;
|
size_t argidx;
|
||||||
|
@ -126,13 +298,33 @@ struct ClockgatePass : public Pass {
|
||||||
neg_icg_desc = icg_from_arg(name, rest);
|
neg_icg_desc = icg_from_arg(name, rest);
|
||||||
}
|
}
|
||||||
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
|
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
|
||||||
tie_lo_ports.push_back(RTLIL::escape_id(args[++argidx]));
|
tie_lo_pins.push_back(RTLIL::escape_id(args[++argidx]));
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
|
||||||
|
liberty_file = args[++argidx];
|
||||||
|
rewrite_filename(liberty_file);
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-dont_use" && argidx+1 < args.size()) {
|
||||||
|
dont_use_cells.push_back(args[++argidx]);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
|
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
|
||||||
min_net_size = atoi(args[++argidx].c_str());
|
min_net_size = atoi(args[++argidx].c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!liberty_file.empty())
|
||||||
|
std::tie(pos_icg_desc, neg_icg_desc) =
|
||||||
|
find_icgs(liberty_file, dont_use_cells);
|
||||||
|
else {
|
||||||
|
for (auto pin : tie_lo_pins) {
|
||||||
|
if (pos_icg_desc)
|
||||||
|
pos_icg_desc->tie_lo_pins.push_back(pin);
|
||||||
|
if (neg_icg_desc)
|
||||||
|
neg_icg_desc->tie_lo_pins.push_back(pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extra_args(args, argidx, design);
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
pool<Cell*> ce_ffs;
|
pool<Cell*> ce_ffs;
|
||||||
|
@ -185,7 +377,7 @@ struct ClockgatePass : public Pass {
|
||||||
gclk.new_net = module->addWire(NEW_ID);
|
gclk.new_net = module->addWire(NEW_ID);
|
||||||
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
|
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
|
||||||
// Tie low DFT ports like scan chain enable
|
// Tie low DFT ports like scan chain enable
|
||||||
for (auto port : tie_lo_ports)
|
for (auto port : matching_icg_desc->tie_lo_pins)
|
||||||
icg->setPort(port, Const(0, 1));
|
icg->setPort(port, Const(0, 1));
|
||||||
// Fix CE polarity if needed
|
// Fix CE polarity if needed
|
||||||
if (!clk.pol_ce) {
|
if (!clk.pol_ce) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
read_ilang <<EOT
|
read_rtlil <<EOT
|
||||||
autoidx 1
|
autoidx 1
|
||||||
module \top
|
module \top
|
||||||
wire output 1 \Y
|
wire output 1 \Y
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
library(test) {
|
||||||
|
/* Integrated clock gating cells */
|
||||||
|
cell (pos_small_tielo) {
|
||||||
|
area : 1;
|
||||||
|
clock_gating_integrated_cell : latch_posedge_precontrol;
|
||||||
|
pin (GCLK) {
|
||||||
|
clock_gate_out_pin : true;
|
||||||
|
direction : output;
|
||||||
|
}
|
||||||
|
pin (CLK) {
|
||||||
|
clock_gate_clock_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CE) {
|
||||||
|
clock_gate_enable_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (SE) {
|
||||||
|
clock_gate_test_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell (pos_big) {
|
||||||
|
area : 10;
|
||||||
|
clock_gating_integrated_cell : latch_posedge;
|
||||||
|
pin (GCLK) {
|
||||||
|
clock_gate_out_pin : true;
|
||||||
|
direction : output;
|
||||||
|
}
|
||||||
|
pin (CLK) {
|
||||||
|
clock_gate_clock_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CE) {
|
||||||
|
clock_gate_enable_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell (pos_small) {
|
||||||
|
area : 1;
|
||||||
|
clock_gating_integrated_cell : latch_posedge;
|
||||||
|
pin (GCLK) {
|
||||||
|
clock_gate_out_pin : true;
|
||||||
|
direction : output;
|
||||||
|
}
|
||||||
|
pin (CLK) {
|
||||||
|
clock_gate_clock_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CE) {
|
||||||
|
clock_gate_enable_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell (neg_big) {
|
||||||
|
area : 10;
|
||||||
|
clock_gating_integrated_cell : latch_negedge;
|
||||||
|
pin (GCLK) {
|
||||||
|
clock_gate_out_pin : true;
|
||||||
|
direction : output;
|
||||||
|
}
|
||||||
|
pin (CLK) {
|
||||||
|
clock_gate_clock_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CE) {
|
||||||
|
clock_gate_enable_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell (neg_small_tielo) {
|
||||||
|
area : 1;
|
||||||
|
clock_gating_integrated_cell : latch_negedge_precontrol;
|
||||||
|
pin (GCLK) {
|
||||||
|
clock_gate_out_pin : true;
|
||||||
|
direction : output;
|
||||||
|
}
|
||||||
|
pin (CLK) {
|
||||||
|
clock_gate_clock_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CE) {
|
||||||
|
clock_gate_enable_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (SE) {
|
||||||
|
clock_gate_test_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell (neg_small) {
|
||||||
|
area : 1;
|
||||||
|
clock_gating_integrated_cell : latch_negedge;
|
||||||
|
pin (GCLK) {
|
||||||
|
clock_gate_out_pin : true;
|
||||||
|
direction : output;
|
||||||
|
}
|
||||||
|
pin (CLK) {
|
||||||
|
clock_gate_clock_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CE) {
|
||||||
|
clock_gate_enable_pin : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ clockgate -pos pdk_icg ce:clkin:clkout -tie_lo scanen
|
||||||
# falling edge clock flops don't get matched on -pos
|
# falling edge clock flops don't get matched on -pos
|
||||||
select -module dffe_00 -assert-count 0 t:\\pdk_icg
|
select -module dffe_00 -assert-count 0 t:\\pdk_icg
|
||||||
select -module dffe_01 -assert-count 0 t:\\pdk_icg
|
select -module dffe_01 -assert-count 0 t:\\pdk_icg
|
||||||
# falling edge clock flops do get matched on -pos
|
# rising edge clock flops do get matched on -pos
|
||||||
select -module dffe_10 -assert-count 1 t:\\pdk_icg
|
select -module dffe_10 -assert-count 1 t:\\pdk_icg
|
||||||
select -module dffe_11 -assert-count 1 t:\\pdk_icg
|
select -module dffe_11 -assert-count 1 t:\\pdk_icg
|
||||||
# if necessary, EN is inverted, since the given ICG
|
# if necessary, EN is inverted, since the given ICG
|
||||||
|
@ -79,10 +79,10 @@ select -module dffe_wide_11 -assert-count 1 t:\\pdk_icg
|
||||||
design -load before
|
design -load before
|
||||||
clockgate -min_net_size 1 -neg pdk_icg ce:clkin:clkout -tie_lo scanen
|
clockgate -min_net_size 1 -neg pdk_icg ce:clkin:clkout -tie_lo scanen
|
||||||
|
|
||||||
# rising edge clock flops don't get matched on -neg
|
# falling edge clock flops do get matched on -neg
|
||||||
select -module dffe_00 -assert-count 1 t:\\pdk_icg
|
select -module dffe_00 -assert-count 1 t:\\pdk_icg
|
||||||
select -module dffe_01 -assert-count 1 t:\\pdk_icg
|
select -module dffe_01 -assert-count 1 t:\\pdk_icg
|
||||||
# rising edge clock flops do get matched on -neg
|
# rising edge clock flops don't get matched on -neg
|
||||||
select -module dffe_10 -assert-count 0 t:\\pdk_icg
|
select -module dffe_10 -assert-count 0 t:\\pdk_icg
|
||||||
select -module dffe_11 -assert-count 0 t:\\pdk_icg
|
select -module dffe_11 -assert-count 0 t:\\pdk_icg
|
||||||
# if necessary, EN is inverted, since the given ICG
|
# if necessary, EN is inverted, since the given ICG
|
||||||
|
@ -193,4 +193,55 @@ select -assert-count 1 t:\\pdk_icg
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
# TODO test -tie_lo
|
design -load before
|
||||||
|
clockgate -liberty clockgate.lib
|
||||||
|
|
||||||
|
# rising edge ICGs
|
||||||
|
select -module dffe_00 -assert-count 0 t:\\pos_small
|
||||||
|
select -module dffe_01 -assert-count 0 t:\\pos_small
|
||||||
|
|
||||||
|
select -module dffe_10 -assert-count 1 t:\\pos_small
|
||||||
|
select -module dffe_11 -assert-count 1 t:\\pos_small
|
||||||
|
|
||||||
|
# falling edge ICGs
|
||||||
|
select -module dffe_00 -assert-count 1 t:\\neg_small
|
||||||
|
select -module dffe_01 -assert-count 1 t:\\neg_small
|
||||||
|
|
||||||
|
select -module dffe_10 -assert-count 0 t:\\neg_small
|
||||||
|
select -module dffe_11 -assert-count 0 t:\\neg_small
|
||||||
|
|
||||||
|
# and nothing else
|
||||||
|
select -module dffe_00 -assert-count 0 t:\\pos_big
|
||||||
|
select -module dffe_01 -assert-count 0 t:\\pos_big
|
||||||
|
select -module dffe_10 -assert-count 0 t:\\pos_big
|
||||||
|
select -module dffe_11 -assert-count 0 t:\\pos_big
|
||||||
|
select -module dffe_00 -assert-count 0 t:\\pos_small_tielo
|
||||||
|
select -module dffe_01 -assert-count 0 t:\\pos_small_tielo
|
||||||
|
select -module dffe_10 -assert-count 0 t:\\pos_small_tielo
|
||||||
|
select -module dffe_11 -assert-count 0 t:\\pos_small_tielo
|
||||||
|
select -module dffe_00 -assert-count 0 t:\\neg_big
|
||||||
|
select -module dffe_01 -assert-count 0 t:\\neg_big
|
||||||
|
select -module dffe_10 -assert-count 0 t:\\neg_big
|
||||||
|
select -module dffe_11 -assert-count 0 t:\\neg_big
|
||||||
|
select -module dffe_00 -assert-count 0 t:\\neg_small_tielo
|
||||||
|
select -module dffe_01 -assert-count 0 t:\\neg_small_tielo
|
||||||
|
select -module dffe_10 -assert-count 0 t:\\neg_small_tielo
|
||||||
|
select -module dffe_11 -assert-count 0 t:\\neg_small_tielo
|
||||||
|
|
||||||
|
# if necessary, EN is inverted, since the given ICG
|
||||||
|
# is assumed to have an active-high EN
|
||||||
|
select -module dffe_10 -assert-count 1 t:\$_NOT_
|
||||||
|
select -module dffe_11 -assert-count 0 t:\$_NOT_
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
design -load before
|
||||||
|
clockgate -liberty clockgate.lib -dont_use pos_small -dont_use neg_small
|
||||||
|
|
||||||
|
# rising edge ICGs
|
||||||
|
select -module dffe_10 -assert-count 1 t:\\pos_big
|
||||||
|
select -module dffe_11 -assert-count 1 t:\\pos_big
|
||||||
|
|
||||||
|
# falling edge ICGs
|
||||||
|
select -module dffe_00 -assert-count 1 t:\\neg_big
|
||||||
|
select -module dffe_01 -assert-count 1 t:\\neg_big
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
read_verilog <<EOF
|
||||||
|
(* blackbox *)
|
||||||
|
(* gate_cost_equivalent=150 *)
|
||||||
|
module macro;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module branch1;
|
||||||
|
macro inst1();
|
||||||
|
macro inst2();
|
||||||
|
macro inst3();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module branch2;
|
||||||
|
macro inst1();
|
||||||
|
macro inst2();
|
||||||
|
macro inst3();
|
||||||
|
macro inst4();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// branch3_submod on its own doesn't meet the threshold
|
||||||
|
module branch3_submod();
|
||||||
|
wire [2:0] y;
|
||||||
|
wire [2:0] a;
|
||||||
|
wire [2:0] b;
|
||||||
|
assign y = a * b;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// on the other hand four branch3_submods do
|
||||||
|
module branch3;
|
||||||
|
branch3_submod inst1();
|
||||||
|
branch3_submod inst2();
|
||||||
|
branch3_submod inst3();
|
||||||
|
branch3_submod inst4();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// wrapper should have zero cost when branch3 is marked
|
||||||
|
// keep_hierarchy
|
||||||
|
module branch3_wrapper;
|
||||||
|
branch3 inst();
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top;
|
||||||
|
branch1 inst1();
|
||||||
|
branch2 inst2();
|
||||||
|
branch3_wrapper wrapper();
|
||||||
|
endmodule
|
||||||
|
EOF
|
||||||
|
|
||||||
|
hierarchy -top top
|
||||||
|
keep_hierarchy -min_cost 500
|
||||||
|
select -assert-mod-count 2 A:keep_hierarchy
|
||||||
|
select -assert-any A:keep_hierarchy branch2 %i
|
||||||
|
select -assert-any A:keep_hierarchy branch3 %i
|
Loading…
Reference in New Issue