diff --git a/passes/silimate/Makefile.inc b/passes/silimate/Makefile.inc index dfb1a4ffe..bfdc21ce8 100644 --- a/passes/silimate/Makefile.inc +++ b/passes/silimate/Makefile.inc @@ -10,7 +10,7 @@ OBJS += passes/silimate/mux_push.o OBJS += passes/silimate/obs_clean.o OBJS += passes/silimate/segv.o OBJS += passes/silimate/reg_rename.o -OBJS += passes/silimate/sat_clockgate.o +OBJS += passes/silimate/infer_ce.o OBJS += passes/silimate/splitfanout.o OBJS += passes/silimate/splitlarge.o OBJS += passes/silimate/splitnetlist.o diff --git a/passes/silimate/sat_clockgate.cc b/passes/silimate/infer_ce.cc similarity index 76% rename from passes/silimate/sat_clockgate.cc rename to passes/silimate/infer_ce.cc index 0a704b8f7..b2d393ce6 100644 --- a/passes/silimate/sat_clockgate.cc +++ b/passes/silimate/infer_ce.cc @@ -20,11 +20,9 @@ #include "kernel/sigtools.h" #include "kernel/ff.h" #include "kernel/satgen.h" -#include "kernel/consteval.h" #include #include #include -#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -98,9 +96,8 @@ void profileFlipFlops(Module *module, const std::string &filename, const std::st // Configuration static const int DEFAULT_MAX_COVER = 100; // Max candidate signals to consider static const int DEFAULT_MIN_REGS = 10; // Min registers per clock gate -static const int DEFAULT_SIM_ITERATIONS = 1000; // Random simulation iterations for pruning -struct SatClockgateWorker +struct InferCeWorker { Module *module; SigMap sigmap; @@ -108,7 +105,6 @@ struct SatClockgateWorker // Configuration int max_cover; int min_regs; - int sim_iterations; // Maps output signal bits to their driver cells dict sig_to_driver; @@ -120,17 +116,13 @@ struct SatClockgateWorker ezSatPtr ez; SatGen satgen; - // Cached simulation results: [iteration][SigBit] = evaluated State - std::vector> cached_sim_results; - // Statistics int accepted_count = 0; - int rejected_sim_count = 0; int rejected_sat_count = 0; - SatClockgateWorker(Module *module, int max_cover, int min_regs, int sim_iterations) + InferCeWorker(Module *module, int max_cover, int min_regs) : module(module), sigmap(module), - max_cover(max_cover), min_regs(min_regs), sim_iterations(sim_iterations), + max_cover(max_cover), min_regs(min_regs), ez(), satgen(ez.get(), &sigmap) { // Build driver and sink maps @@ -156,95 +148,6 @@ struct SatClockgateWorker ID($dffsre), ID($_DFF_P_), ID($_DFF_N_), ID($_DFFE_PP_), ID($_DFFE_PN_), ID($_DFFE_NP_), ID($_DFFE_NN_))) satgen.importCell(cell); - - // Pre-run simulations and cache all signal values - std::mt19937 rng(42); - cached_sim_results.resize(sim_iterations); - for (int iter = 0; iter < sim_iterations; iter++) { - ConstEval ce(module); - - // Set random values for input ports - for (auto wire : module->wires()) { - if (wire->port_input) { - Const rand_val(State::S0, wire->width); - for (int i = 0; i < wire->width; i++) - rand_val.bits()[i] = (rng() & 1) ? State::S1 : State::S0; - ce.set(SigSpec(wire), rand_val); - } - } - // Set random values for FF Q outputs - for (auto cell : module->cells()) { - if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($adff), ID($adffe), - ID($sdff), ID($sdffe), ID($sdffce), ID($dffsr), - ID($dffsre), ID($_DFF_P_), ID($_DFF_N_), ID($_DFFE_PP_), - ID($_DFFE_PN_), ID($_DFFE_NP_), ID($_DFFE_NN_))) { - FfData ff(nullptr, cell); - Const rand_val(State::S0, ff.width); - for (int i = 0; i < ff.width; i++) - rand_val.bits()[i] = (rng() & 1) ? State::S1 : State::S0; - ce.set(ff.sig_q, rand_val); - } - } - - // Evaluate and cache ALL wire signals - for (auto wire : module->wires()) { - for (int i = 0; i < wire->width; i++) { - SigBit bit(wire, i); - SigSpec sig(bit); - if (ce.eval(sig)) - cached_sim_results[iter][sigmap(bit)] = sig[0].data; - } - } - } - } - - // Check cached simulation results for counterexamples - // Returns false if counterexample found (candidate is definitely invalid) - bool simulationTest(const std::vector &conds, SigSpec sig_d, SigSpec sig_q, bool as_enable) - { - for (int iter = 0; iter < sim_iterations; iter++) { - auto &cache = cached_sim_results[iter]; - - // Lookup gating condition from cache - bool combined_cond; - if (as_enable) { - combined_cond = false; - for (auto bit : conds) { - SigBit mapped = sigmap(bit); - if (cache.count(mapped) && cache[mapped] == State::S1) - combined_cond = true; - } - } else { - combined_cond = true; - for (auto bit : conds) { - SigBit mapped = sigmap(bit); - if (!cache.count(mapped) || cache[mapped] != State::S1) - combined_cond = false; - } - } - - bool gating_active = as_enable ? !combined_cond : combined_cond; - - // Lookup D and Q from cache, check if D != Q - bool d_ne_q = false; - for (int i = 0; i < sig_d.size(); i++) { - SigBit d_bit = sigmap(sig_d[i]); - SigBit q_bit = sigmap(sig_q[i]); - State d_val = cache.count(d_bit) ? cache[d_bit] : State::S0; - State q_val = cache.count(q_bit) ? cache[q_bit] : State::S0; - if (d_val != q_val) { - d_ne_q = true; - break; - } - } - - // If gating is active and D != Q, this is a counterexample - if (gating_active && d_ne_q) { - rejected_sim_count++; - return false; - } - } - return true; // No counterexample found } // Get downstream signals from a register (BFS forward through combinational logic) @@ -334,32 +237,6 @@ struct SatClockgateWorker return visited; } - // Check if a candidate signal is a valid gating condition using SAT - // Safe gating check: sig=1 → D==Q (i.e., (sig ∧ (D≠Q)) is UNSAT) - bool isValidGatingSignal(SigBit candidate, SigSpec sig_d, SigSpec sig_q, bool as_enable) - { - std::vector d_vec = satgen.importSigSpec(sig_d); - std::vector q_vec = satgen.importSigSpec(sig_q); - int cand_var = satgen.importSigSpec(SigSpec(candidate))[0]; - - // D != Q - int d_ne_q = ez->vec_ne(d_vec, q_vec); - - // For clock enable (active high): when enable=0, D must equal Q - // Check: (!enable ∧ (D≠Q)) is UNSAT - // For clock disable (active low): when disable=1, D must equal Q - // Check: (disable ∧ (D≠Q)) is UNSAT - - int gating_active = as_enable ? ez->NOT(cand_var) : cand_var; - int query = ez->AND(gating_active, d_ne_q); - - std::vector assumptions = {query}; - std::vector dummy_exprs; - std::vector dummy_vals; - - return !ez->solve(dummy_exprs, dummy_vals, assumptions); - } - // Binary search to minimize the gating condition set // Tries to remove half of the signals at a time void minimizeGatingCondition( @@ -393,20 +270,12 @@ struct SatClockgateWorker } } - // Check if OR/AND of signals forms a valid gating condition - // Uses simulation first to quickly prune invalid candidates, then SAT to prove + // Check if OR/AND of signals forms a valid gating condition using SAT bool isValidGatingSet(const std::vector &conds, SigSpec sig_d, SigSpec sig_q, bool as_enable) { if (conds.empty()) return false; - // Quick simulation filter first - catches most invalid candidates fast - // if (!simulationTest(conds, sig_d, sig_q, as_enable)) { - // log_debug(" Rejected by simulation\n"); - // return false; - // } - - // SAT only if simulation passes std::vector d_vec = satgen.importSigSpec(sig_d); std::vector q_vec = satgen.importSigSpec(sig_q); @@ -447,12 +316,12 @@ struct SatClockgateWorker FfData ff(nullptr, reg); // Get candidate signals downstream of this register - pool downstream = getDownstreamSignals(reg, max_cover); + // pool downstream = getDownstreamSignals(reg, max_cover); - if (downstream.empty()) { - log_debug(" No downstream candidates for %s\n", log_id(reg)); - return {{}, false}; - } + // if (downstream.empty()) { + // log_debug(" No downstream candidates for %s\n", log_id(reg)); + // return {{}, false}; + // } // Also include upstream signals that could affect D pool d_inputs; @@ -463,11 +332,16 @@ struct SatClockgateWorker // Combine and limit candidates std::vector candidates; - for (auto bit : downstream) - candidates.push_back(bit); + // for (auto bit : downstream) + // candidates.push_back(bit); + // for (auto bit : upstream) + // if (!downstream.count(bit)) + // candidates.push_back(bit); + for (auto bit : upstream) - if (!downstream.count(bit)) - candidates.push_back(bit); + candidates.push_back(bit); + + if ((int)candidates.size() > max_cover) candidates.resize(max_cover); @@ -678,22 +552,22 @@ struct SatClockgateWorker } log(" Inserted %d clock gates\n", gates_inserted); - log(" Statistics: accepted=%d, rejected_sim=%d, rejected_sat=%d\n", - accepted_count, rejected_sim_count, rejected_sat_count); + log(" Statistics: accepted=%d, rejected_sat=%d\n", + accepted_count, rejected_sat_count); log(" SAT stats: literals=%d, expressions=%d\n", ez->numLiterals(), ez->numExpressions()); } }; -struct SatClockgatePass : public Pass { - SatClockgatePass() : Pass("sat_clockgate", "SAT-based automatic clock gating") { } +struct InferCePass : public Pass { + InferCePass() : Pass("infer_ce", "Infer clock enable signals from conditional logic") { } void help() override { log("\n"); - log(" sat_clockgate [options] [selection]\n"); + log(" infer_ce [options] [selection]\n"); log("\n"); - log("This command performs SAT-based automatic clock gating insertion.\n"); + log("This command infers clock enable (CE) signals from conditional logic.\n"); log("It analyzes registers and uses SAT solving to find signals that can\n"); log("serve as clock enable conditions (when the signal is low, D==Q).\n"); log("\n"); @@ -713,11 +587,10 @@ struct SatClockgatePass : public Pass { void execute(std::vector args, RTLIL::Design *design) override { - log_header(design, "Executing SAT_CLOCKGATE pass.\n"); + log_header(design, "Executing INFER_CE pass.\n"); int max_cover = DEFAULT_MAX_COVER; int min_regs = DEFAULT_MIN_REGS; - int sim_iterations = DEFAULT_SIM_ITERATIONS; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -745,18 +618,18 @@ struct SatClockgatePass : public Pass { for (auto module : design->selected_modules()) { // Profile BEFORE clock gating - profileFlipFlops(module, "ff_profile.txt", "BEFORE sat_clockgate"); + profileFlipFlops(module, "ff_profile.txt", "BEFORE infer_ce"); - SatClockgateWorker worker(module, max_cover, min_regs, sim_iterations); + InferCeWorker worker(module, max_cover, min_regs); worker.run(); total_gates += worker.accepted_count; // Profile AFTER clock gating - profileFlipFlops(module, "ff_profile.txt", "AFTER sat_clockgate"); + profileFlipFlops(module, "ff_profile.txt", "AFTER infer_ce"); } log("Total clock gates inserted: %d\n", total_gates); } -} SatClockgatePass; +} InferCePass; PRIVATE_NAMESPACE_END diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index 58b7520ac..b112d7811 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -328,6 +328,8 @@ struct ClockgatePass : public Pass { pool ce_ffs; dict clk_nets; + + log("Found %zu CE FFs\n", ce_ffs.size()); int gated_flop_count = 0; for (auto module : design->selected_unboxed_whole_modules()) { for (auto cell : module->cells()) { @@ -337,11 +339,14 @@ struct ClockgatePass : public Pass { FfData ff(nullptr, cell); // It would be odd to get constants, but we better handle it if (ff.has_ce) { + log("FF %s has CE\n", cell->name); if (!ff.sig_clk.is_bit() || !ff.sig_ce.is_bit()) continue; if (!ff.sig_clk[0].is_wire() || !ff.sig_ce[0].is_wire()) continue; + log("FF %s has valid CE and CLK\n", cell->name); + ce_ffs.insert(cell); ClkNetInfo info = clk_info_from_ff(ff); @@ -352,6 +357,8 @@ struct ClockgatePass : public Pass { } } + log("Found %zu clk_nets\n", clk_nets.size()); + for (auto& clk_net : clk_nets) { auto& clk = clk_net.first; auto& gclk = clk_net.second; @@ -385,15 +392,20 @@ struct ClockgatePass : public Pass { } } + log("Found %zu clk_nets\n", clk_nets.size()); + for (auto cell : ce_ffs) { FfData ff(nullptr, 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"); + log("Found new_net for %s\n", cell->name); if (!it->second.new_net) continue; + log("Tryuing to fix up FF %s\n", cell->name); + log_debug("Fix up FF %s\n", cell->name); // Now we start messing with the design ff.has_ce = false;