Compare commits

...

13 Commits

Author SHA1 Message Date
Emil J 279183fedf
Merge 1759609321 into 5b6baa3ef1 2024-11-20 15:18:25 +01:00
Emil J 5b6baa3ef1
Merge pull request #4744 from YosysHQ/emil/clockgate-liberty
clockgate: add -liberty
2024-11-20 15:04:00 +01:00
Martin Povišer 53a4ec375b
Merge pull request #4762 from georgerennie/george/fix_read_ilang_test
tests: replace read_ilang with read_rtlil
2024-11-20 14:58:16 +01:00
George Rennie 9043dc0ad6
tests: replace read_ilang with read_rtlil
* #4612 was written before read_ilang was deprecated but merged after so caused test failures. This switches read_ilang to read_rtlil
2024-11-20 14:54:23 +01:00
Emil J. Tywoniak 4d96cbec75 clockgate: reduce errors to warnings 2024-11-18 18:32:18 +01:00
Emil J. Tywoniak 983c54c75f clockgate: help string add -dont_use and -liberty 2024-11-18 13:57:49 +01:00
Emil J. Tywoniak a5bc36f77e clockgate: add -dont_use 2024-11-18 13:45:30 +01:00
Emil J. Tywoniak e6793da9a0 clockgate: refactor 2024-11-18 12:50:25 +01:00
Emil J. Tywoniak b08441d95c clockgate: shuffle test liberty to exercise comparison better 2024-11-18 12:48:50 +01:00
Emil J. Tywoniak 1e3f8cc630 clockgate: add test liberty file 2024-11-18 12:45:27 +01:00
Emil J. Tywoniak c921d85a85 clockgate: fix test comments 2024-11-18 12:33:09 +01:00
Emil J. Tywoniak 45880ea7f2 clockgate: add -liberty 2024-11-14 20:37:59 +01:00
Emil J. Tywoniak 1759609321 reduce OS ifdefs, refactor getting dirs and filenames from paths to files 2024-11-08 19:30:24 +01:00
9 changed files with 383 additions and 35 deletions

View File

@ -637,20 +637,6 @@ std::string escape_cxx_string(const std::string &input)
return output; return output;
} }
std::string basename(const std::string &filepath)
{
#ifdef _WIN32
const std::string dir_seps = "\\/";
#else
const std::string dir_seps = "/";
#endif
size_t sep_pos = filepath.find_last_of(dir_seps);
if (sep_pos != std::string::npos)
return filepath.substr(sep_pos + 1);
else
return filepath;
}
template<class T> template<class T>
std::string get_hdl_name(T *object) std::string get_hdl_name(T *object)
{ {
@ -2841,7 +2827,7 @@ struct CxxrtlWorker {
} }
if (split_intf) if (split_intf)
f << "#include \"" << basename(intf_filename) << "\"\n"; f << "#include \"" << name_from_file_path(intf_filename) << "\"\n";
else else
f << "#include <cxxrtl/cxxrtl.h>\n"; f << "#include <cxxrtl/cxxrtl.h>\n";
f << "\n"; f << "\n";

View File

@ -4440,12 +4440,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
std::ifstream f; std::ifstream f;
f.open(mem_filename.c_str()); f.open(mem_filename.c_str());
if (f.fail()) { if (f.fail()) {
#ifdef _WIN32 std::string path = parent_from_file_path(filename);
char slash = '\\';
#else
char slash = '/';
#endif
std::string path = filename.substr(0, filename.find_last_of(slash)+1);
f.open(path + mem_filename.c_str()); f.open(path + mem_filename.c_str());
yosys_input_files.insert(path + mem_filename); yosys_input_files.insert(path + mem_filename);
} else { } else {

View File

@ -892,11 +892,7 @@ frontend_verilog_preproc(std::istream &f,
// if the include file was not found, it is not given with an absolute path, and the // if the include file was not found, it is not given with an absolute path, and the
// currently read file is given with a path, then try again relative to its directory // currently read file is given with a path, then try again relative to its directory
ff.clear(); ff.clear();
#ifdef _WIN32 fixed_fn = parent_from_file_path(filename) + fn;
fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn;
#else
fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn;
#endif
ff.open(fixed_fn); ff.open(fixed_fn);
} }
if (ff.fail() && fn.size() > 0 && fn_relative) { if (ff.fail() && fn.size() > 0 && fn_relative) {

View File

@ -540,6 +540,25 @@ std::string escape_filename_spaces(const std::string& filename)
return out; return out;
} }
#ifdef _WIN32
const char* const OS_PATH_SEP = "/\\";
#else
const char* const OS_PATH_SEP = "/";
#endif
std::string name_from_file_path(std::string path) {
size_t sep_pos = path.find_last_of(OS_PATH_SEP);
if (sep_pos != std::string::npos)
return path.substr(sep_pos + 1);
else
return path;
}
// Includes OS_PATH_SEP at the end if present
std::string parent_from_file_path(std::string path) {
return path.substr(0, path.find_last_of(OS_PATH_SEP)+1);
}
bool already_setup = false; bool already_setup = false;
void yosys_setup() void yosys_setup()

View File

@ -343,6 +343,8 @@ bool is_absolute_path(std::string filename);
void remove_directory(std::string dirname); void remove_directory(std::string dirname);
bool create_directory(const std::string& dirname); bool create_directory(const std::string& dirname);
std::string escape_filename_spaces(const std::string& filename); std::string escape_filename_spaces(const std::string& filename);
std::string name_from_file_path(std::string path);
std::string parent_from_file_path(std::string path);
template<typename T> int GetSize(const T &obj) { return obj.size(); } template<typename T> int GetSize(const T &obj) { return obj.size(); }
inline int GetSize(RTLIL::Wire *wire); inline int GetSize(RTLIL::Wire *wire);

View File

@ -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) {

View File

@ -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

107
tests/techmap/clockgate.lib Normal file
View File

@ -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;
}
}
}

View File

@ -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