mirror of https://github.com/YosysHQ/yosys.git
Removed simulation and isValidGatingSignal function
This commit is contained in:
parent
fa9e7a77d7
commit
a8e4fccc56
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/ff.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/consteval.h"
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
|
||||
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<SigBit, Cell*> sig_to_driver;
|
||||
|
|
@ -120,17 +116,13 @@ struct SatClockgateWorker
|
|||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
|
||||
// Cached simulation results: [iteration][SigBit] = evaluated State
|
||||
std::vector<dict<SigBit, State>> 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<SigBit> &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<int> d_vec = satgen.importSigSpec(sig_d);
|
||||
std::vector<int> 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<int> assumptions = {query};
|
||||
std::vector<int> dummy_exprs;
|
||||
std::vector<bool> 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<SigBit> &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<int> d_vec = satgen.importSigSpec(sig_d);
|
||||
std::vector<int> q_vec = satgen.importSigSpec(sig_q);
|
||||
|
||||
|
|
@ -447,12 +316,12 @@ struct SatClockgateWorker
|
|||
FfData ff(nullptr, reg);
|
||||
|
||||
// Get candidate signals downstream of this register
|
||||
pool<SigBit> downstream = getDownstreamSignals(reg, max_cover);
|
||||
// pool<SigBit> 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<SigBit> d_inputs;
|
||||
|
|
@ -463,11 +332,16 @@ struct SatClockgateWorker
|
|||
|
||||
// Combine and limit candidates
|
||||
std::vector<SigBit> 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<std::string> 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
|
||||
|
|
@ -328,6 +328,8 @@ struct ClockgatePass : public Pass {
|
|||
pool<Cell*> ce_ffs;
|
||||
dict<ClkNetInfo, GClkNetInfo> 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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue