From 33e06dfdf911e7eca837c43b3aa1038d0e934d1c Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Wed, 4 Sep 2024 23:55:14 +0200 Subject: [PATCH] wip --- passes/techmap/clock_gate.cc | 149 +++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 25 deletions(-) diff --git a/passes/techmap/clock_gate.cc b/passes/techmap/clock_gate.cc index 26179f99694..03ce9e034e2 100644 --- a/passes/techmap/clock_gate.cc +++ b/passes/techmap/clock_gate.cc @@ -1,9 +1,39 @@ #include "kernel/yosys.h" #include "kernel/ff.h" +#include 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 @@ -18,43 +48,112 @@ struct ClockgatePass : public Pass { { log_header(design, "Executing CLOCK_GATE pass (extract clock gating out of flip flops).\n"); - std::string pos_icg_cell; - std::string neg_icg_cell; + std::optional pos_icg_desc; + std::optional neg_icg_desc; + std::vector tie_lo_ports; + int net_size = 0; + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-pos" && argidx+1 < args.size()) { - pos_icg_cell = args[++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+1 < args.size()) { - neg_icg_cell = args[++argidx]; + 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] == "-net_size" && argidx+1 < args.size()) { + net_size = atoi(args[++argidx].c_str()); } - break; } - + // TODO worker? extra_args(args, argidx, design); + pool ce_ffs; + dict clk_net_sizes; + int gated_flop_count = 0; for (auto module : design->selected_whole_modules()) { - sigmap.set(module); + 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) { - - // TODO do stuff - - - bool ce_pol = ff.pol_ce; - if (!ce_pol) { - //TODO invert CE - } - } - } - } + 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); + + Wire* clk = ff.sig_clk.as_wire(); + auto it = clk_net_sizes.find(clk); + if (it != clk_net_sizes.end()) + it->second++; + else + clk_net_sizes[clk] = 1; + } + } + + for (auto cell : ce_ffs) { + FfData ff(&initvals, cell); + + Wire* clk = ff.sig_clk.as_wire(); + if (clk_net_sizes[clk] < net_size) + continue; + + std::string name = cell->name.str() + "_ICG"; + std::optional matching_icg_desc; + + if (pos_icg_desc && ff.pol_clk) + matching_icg_desc = pos_icg_desc; + else if (neg_icg_desc && !ff.pol_clk) + matching_icg_desc = neg_icg_desc; + + if (!matching_icg_desc) + continue; + + // Read CE polarity before doing anything + bool ce_pol = ff.pol_ce; + // Now we start messing with the design + ff.has_ce = false; + // Construct the clock gate + // ICG = integrated clock gate, industry shorthand + Cell* icg = module->addCell(name, matching_icg_desc->name); + icg->setPort(matching_icg_desc->ce_pin, ff.sig_ce); + icg->setPort(matching_icg_desc->clk_in_pin, ff.sig_clk); + + Wire *new_gclk = module->addWire(NEW_ID); + ff.sig_clk = new_gclk; + icg->setPort(matching_icg_desc->clk_out_pin, ff.sig_clk); + + // Tie low DFT ports like scan chain enable + for (auto port : tie_lo_ports) + icg->setPort(port, Const(0)); + + // Rebuild the flop + (void)ff.emit(); + + // Fix CE polarity if needed + if (!ce_pol) { + SigBit ce_fixed_pol = ff.module->NotGate(NEW_ID, ff.sig_ce); + icg->setPort(matching_icg_desc->ce_pin, ce_fixed_pol); + } + gated_flop_count++; + } + ce_ffs.clear(); + clk_net_sizes.clear(); + } + + // TODO add tests like tests/sim/dffe.v + // TODO we currently create one ICG per flop! + log("Converted %d FFs.\n", gated_flop_count); } } ClockgatePass;