-
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 7294ef2
Showing
3 changed files
with
234 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,205 @@ | ||
#include "kernel/yosys.h" | ||
#include "kernel/ff.h" | ||
#include <optional> | ||
|
||
USING_YOSYS_NAMESPACE | ||
PRIVATE_NAMESPACE_BEGIN | ||
|
||
struct ClockGateCell { | ||
std::string name; | ||
std::string ce_pin; | ||
std::string clk_in_pin; | ||
std::string clk_out_pin; | ||
|
||
ClockGateCell(std::string& name, std::string& str) : name(RTLIL::escape_id(name)) { | ||
char delimiter = ':'; | ||
size_t pos1 = str.find(delimiter); | ||
if (pos1 == std::string::npos) | ||
log_assert(false && "Not enough ports in descriptor string"); | ||
size_t pos2 = str.find(delimiter, pos1 + 1); | ||
if (pos2 == std::string::npos) | ||
log_assert(false && "Not enough ports in descriptor string"); | ||
size_t pos3 = str.find(delimiter, pos2 + 1); | ||
if (pos3 != std::string::npos) | ||
log_assert(false && "Too many ports in descriptor string"); | ||
|
||
ce_pin = str.substr(0, pos1); | ||
ce_pin = RTLIL::escape_id(ce_pin); | ||
|
||
clk_in_pin = str.substr(pos1 + 1, pos2 - (pos1 + 1)); | ||
clk_in_pin = RTLIL::escape_id(clk_in_pin); | ||
|
||
clk_out_pin = str.substr(pos2 + 1, str.size() - (pos2 + 1)); | ||
clk_out_pin = RTLIL::escape_id(clk_out_pin); | ||
} | ||
}; | ||
|
||
struct ClockgatePass : public Pass { | ||
ClockgatePass() : Pass("clock_gate", "extract clock gating out of flip flops") { } | ||
void help() override { | ||
// TODO | ||
} | ||
|
||
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 { | ||
unsigned int h = mkhash_init; | ||
h = mkhash(h, hash_ptr_ops::hash(clk_net)); | ||
h = mkhash(h, hash_ptr_ops::hash(ce_net)); | ||
h = mkhash(h, pol_clk); | ||
h = mkhash(h, pol_ce); | ||
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 = ClockGateCell(name, rest); | ||
} | ||
if (args[argidx] == "-neg" && argidx+2 < args.size()) { | ||
auto name = args[++argidx]; | ||
auto rest = args[++argidx]; | ||
neg_icg_desc = ClockGateCell(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()) | ||
it->second.net_size++; | ||
else | ||
clk_nets[info] = GClkNetInfo(); | ||
} | ||
} | ||
|
||
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(); | ||
} | ||
|
||
// TODO add tests like tests/sim/dffe.v | ||
|
||
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,28 @@ | ||
read_verilog << EOT | ||
|
||
module dffe( input clk, en, | ||
input d1, output reg q1, | ||
input d2, output reg q2, | ||
input d3, output reg q3, | ||
input d4, output reg q4, | ||
); | ||
always @( posedge clk ) begin | ||
if ( en ) | ||
q1 <= d1; | ||
if ( ~en ) | ||
q2 <= d2; | ||
end | ||
always @( negedge clk ) begin | ||
if ( en ) | ||
q3 <= d3; | ||
if ( ~en ) | ||
q4 <= d4; | ||
end | ||
endmodule | ||
|
||
EOT | ||
|
||
proc | ||
opt | ||
clock_gate -pos pdk_icg ce:clkin:clkout -tie_lo scanen | ||
# show |