-
Notifications
You must be signed in to change notification settings - Fork 890
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0fc5812
commit e64fcee
Showing
3 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
#include "kernel/yosys.h" | ||
#include "kernel/ff.h" | ||
#include <optional> | ||
|
||
USING_YOSYS_NAMESPACE | ||
PRIVATE_NAMESPACE_BEGIN | ||
|
||
struct ClockGateCell { | ||
IdString name; | ||
IdString ce_pin; | ||
IdString clk_in_pin; | ||
IdString clk_out_pin; | ||
}; | ||
|
||
ClockGateCell icg_from_arg(std::string& name, std::string& str) { | ||
ClockGateCell c; | ||
c.name = RTLIL::escape_id(name); | ||
char delimiter = ':'; | ||
size_t pos1 = str.find(delimiter); | ||
if (pos1 == std::string::npos) | ||
log_cmd_error("Not enough ports in descriptor string"); | ||
size_t pos2 = str.find(delimiter, pos1 + 1); | ||
if (pos2 == std::string::npos) | ||
log_cmd_error("Not enough ports in descriptor string"); | ||
size_t pos3 = str.find(delimiter, pos2 + 1); | ||
if (pos3 != std::string::npos) | ||
log_cmd_error("Too many ports in descriptor string"); | ||
|
||
std::string ce = str.substr(0, pos1); | ||
c.ce_pin = RTLIL::escape_id(ce); | ||
|
||
std::string clk_in = str.substr(pos1 + 1, pos2 - (pos1 + 1)); | ||
c.clk_in_pin = RTLIL::escape_id(clk_in); | ||
|
||
std::string clk_out = str.substr(pos2 + 1, str.size() - (pos2 + 1)); | ||
c.clk_out_pin = RTLIL::escape_id(clk_out); | ||
return c; | ||
} | ||
|
||
struct ClockgatePass : public Pass { | ||
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { } | ||
void help() override { | ||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | ||
log("\n"); | ||
log(" clockgate [options] [selection]\n"); | ||
log("\n"); | ||
log("Creates gated clock nets for sets of FFs with clock enable\n"); | ||
log("sharing a clock and replaces the FFs with versions without\n"); | ||
log("clock enable inputs. Intended to reduce power consumption\n"); | ||
log("in ASIC designs.\n"); | ||
log("\n"); | ||
log(" -pos <celltype> <ce>:<clk>:<gclk>\n"); | ||
log(" If specified, rising-edge FFs will have CE inputs\n"); | ||
log(" removed and a gated clock will be created by the\n"); | ||
log(" user-specified <celltype> ICG (integrated clock gating)\n"); | ||
log(" cell with ports named <ce>, <clk>, <gclk>.\n"); | ||
log(" The ICG's clock enable pin must be active high.\n"); | ||
log(" -neg <celltype> <ce>:<clk>:<gclk>\n"); | ||
log(" If specified, falling-edge FFs will have CE inputs\n"); | ||
log(" removed and a gated clock will be created by the\n"); | ||
log(" user-specified <celltype> ICG (integrated clock gating)\n"); | ||
log(" cell with ports named <ce>, <clk>, <gclk>.\n"); | ||
log(" The ICG's clock enable pin must be active high.\n"); | ||
log(" -tie_lo <port_name>\n"); | ||
log(" Port <port_name> of the ICG will be tied to zero.\n"); | ||
log(" Intended for DFT scan-enable pins.\n"); | ||
log(" -min_net_size <n>\n"); | ||
log(" Only work on clocks with at least <n> eligible FFs.\n"); | ||
// log(" \n"); | ||
} | ||
|
||
SigMap sigmap; | ||
FfInitVals initvals; | ||
|
||
// One ICG will be generated per ClkNetInfo | ||
// if the number of FFs associated with it is sufficent | ||
struct ClkNetInfo { | ||
// Original, ungated clock into enabled FF | ||
Wire* clk_net; | ||
// Original clock enable into enabled FF | ||
Wire* ce_net; | ||
bool pol_clk; | ||
bool pol_ce; | ||
unsigned int hash() const { | ||
auto t = std::make_tuple(clk_net, ce_net, pol_clk, pol_ce); | ||
unsigned int h = mkhash_init; | ||
h = mkhash(h, hash_ops<decltype(t)>::hash(t)); | ||
return h; | ||
} | ||
bool operator==(const ClkNetInfo& other) const { | ||
return (clk_net == other.clk_net) && | ||
(ce_net == other.ce_net) && | ||
(pol_clk == other.pol_clk) && | ||
(pol_ce == other.pol_ce); | ||
} | ||
}; | ||
|
||
struct GClkNetInfo { | ||
// How many CE FFs on this CLK net have we seen? | ||
int net_size; | ||
// After ICG generation, we have new gated CLK signals | ||
Wire* new_net; | ||
}; | ||
|
||
ClkNetInfo clk_info_from_ff(FfData& ff) { | ||
Wire* clk = ff.sig_clk.as_wire(); | ||
Wire* ce = ff.sig_ce.as_wire(); | ||
ClkNetInfo info{clk, ce, ff.pol_clk, ff.pol_ce}; | ||
return info; | ||
} | ||
|
||
void execute(std::vector<std::string> args, RTLIL::Design *design) override { | ||
log_header(design, "Executing CLOCK_GATE pass (extract clock gating out of flip flops).\n"); | ||
|
||
std::optional<ClockGateCell> pos_icg_desc; | ||
std::optional<ClockGateCell> neg_icg_desc; | ||
std::vector<std::string> tie_lo_ports; | ||
int min_net_size = 0; | ||
|
||
size_t argidx; | ||
for (argidx = 1; argidx < args.size(); argidx++) { | ||
if (args[argidx] == "-pos" && argidx+2 < args.size()) { | ||
auto name = args[++argidx]; | ||
auto rest = args[++argidx]; | ||
pos_icg_desc = icg_from_arg(name, rest); | ||
} | ||
if (args[argidx] == "-neg" && argidx+2 < args.size()) { | ||
auto name = args[++argidx]; | ||
auto rest = args[++argidx]; | ||
neg_icg_desc = icg_from_arg(name, rest); | ||
} | ||
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) { | ||
tie_lo_ports.push_back(RTLIL::escape_id(args[++argidx])); | ||
} | ||
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) { | ||
min_net_size = atoi(args[++argidx].c_str()); | ||
} | ||
} | ||
|
||
extra_args(args, argidx, design); | ||
|
||
pool<Cell*> ce_ffs; | ||
dict<ClkNetInfo, GClkNetInfo> clk_nets; | ||
|
||
int gated_flop_count = 0; | ||
for (auto module : design->selected_whole_modules()) { | ||
sigmap.set(module); | ||
initvals.set(&sigmap, module); | ||
for (auto cell : module->cells()) { | ||
if (!RTLIL::builtin_ff_cell_types().count(cell->type)) | ||
continue; | ||
|
||
FfData ff(&initvals, cell); | ||
if (ff.has_ce) { | ||
ce_ffs.insert(cell); | ||
|
||
ClkNetInfo info = clk_info_from_ff(ff); | ||
auto it = clk_nets.find(info); | ||
if (it == clk_nets.end()) | ||
clk_nets[info] = GClkNetInfo(); | ||
clk_nets[info].net_size++; | ||
} | ||
} | ||
|
||
for (auto& clk_net : clk_nets) { | ||
log_debug("checking clk net %s\n", clk_net.first.clk_net->name.c_str()); | ||
auto& clk = clk_net.first; | ||
auto& gclk = clk_net.second; | ||
|
||
if (gclk.net_size < min_net_size) | ||
continue; | ||
|
||
std::optional<ClockGateCell> matching_icg_desc; | ||
|
||
if (pos_icg_desc && clk.pol_clk) | ||
matching_icg_desc = pos_icg_desc; | ||
else if (neg_icg_desc && !clk.pol_clk) | ||
matching_icg_desc = neg_icg_desc; | ||
|
||
if (!matching_icg_desc) | ||
continue; | ||
|
||
log_debug("building ICG for clk net %s\n", clk_net.first.clk_net->name.c_str()); | ||
Cell* icg = module->addCell(NEW_ID, matching_icg_desc->name); | ||
icg->setPort(matching_icg_desc->ce_pin, clk.ce_net); | ||
icg->setPort(matching_icg_desc->clk_in_pin, clk.clk_net); | ||
gclk.new_net = module->addWire(NEW_ID); | ||
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net); | ||
// Tie low DFT ports like scan chain enable | ||
for (auto port : tie_lo_ports) | ||
icg->setPort(port, Const(0)); | ||
// Fix CE polarity if needed | ||
if (!clk.pol_ce) { | ||
SigBit ce_fixed_pol = module->NotGate(NEW_ID, clk.ce_net); | ||
icg->setPort(matching_icg_desc->ce_pin, ce_fixed_pol); | ||
} | ||
} | ||
|
||
for (auto cell : ce_ffs) { | ||
FfData ff(&initvals, cell); | ||
ClkNetInfo info = clk_info_from_ff(ff); | ||
auto it = clk_nets.find(info); | ||
log_assert(it != clk_nets.end() && "Bug: desync ce_ffs and clk_nets"); | ||
|
||
if (!it->second.new_net) | ||
continue; | ||
|
||
log_debug("Fix up FF %s\n", cell->name.c_str()); | ||
// Now we start messing with the design | ||
ff.has_ce = false; | ||
// Construct the clock gate | ||
// ICG = integrated clock gate, industry shorthand | ||
ff.sig_clk = (*it).second.new_net; | ||
|
||
// Rebuild the flop | ||
(void)ff.emit(); | ||
|
||
gated_flop_count++; | ||
} | ||
ce_ffs.clear(); | ||
clk_nets.clear(); | ||
} | ||
|
||
log("Converted %d FFs.\n", gated_flop_count); | ||
} | ||
} ClockgatePass; | ||
|
||
|
||
PRIVATE_NAMESPACE_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
read_verilog << EOT | ||
|
||
module dffe_00( input clk, en, | ||
input d1, output reg q1, | ||
); | ||
always @( negedge clk ) begin | ||
if ( ~en ) | ||
q1 <= d1; | ||
end | ||
endmodule | ||
|
||
module dffe_01( input clk, en, | ||
input d1, output reg q1, | ||
); | ||
always @( negedge clk ) begin | ||
if ( en ) | ||
q1 <= d1; | ||
end | ||
endmodule | ||
|
||
module dffe_10( input clk, en, | ||
input d1, output reg q1, | ||
); | ||
always @( posedge clk ) begin | ||
if ( ~en ) | ||
q1 <= d1; | ||
end | ||
endmodule | ||
|
||
module dffe_11( input clk, en, | ||
input d1, output reg q1, | ||
); | ||
always @( posedge clk ) begin | ||
if ( en ) | ||
q1 <= d1; | ||
end | ||
endmodule | ||
|
||
EOT | ||
|
||
proc | ||
opt | ||
|
||
design -save before | ||
|
||
#------------------------------------------------------------------------------ | ||
|
||
clockgate -pos pdk_icg ce:clkin:clkout -tie_lo scanen | ||
|
||
# falling edge clock flops don't get matched on -pos | ||
select -module dffe_00 -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 | ||
select -module dffe_10 -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 | ||
# 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 -min_net_size 1 -neg pdk_icg ce:clkin:clkout -tie_lo scanen | ||
|
||
# rising edge clock flops don't get matched on -neg | ||
select -module dffe_00 -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 | ||
select -module dffe_10 -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 | ||
# is assumed to have an active-high EN | ||
select -module dffe_00 -assert-count 1 t:\$_NOT_ | ||
select -module dffe_01 -assert-count 0 t:\$_NOT_ | ||
|
||
#------------------------------------------------------------------------------ | ||
|
||
design -load before | ||
clockgate -min_net_size 2 -neg pdk_icg ce:clkin:clkout -tie_lo scanen | ||
|
||
# No FF set sharing a (clock, clock enable) pair is large enough | ||
select -module dffe_00 -assert-count 0 t:\\pdk_icg | ||
select -module dffe_01 -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 | ||
|
||
# TODO test -tie_lo |