mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-25 00:22:34 +00:00
Implement constbit gather.
This commit is contained in:
parent
0bf4afc023
commit
7bdcc97aed
1 changed files with 200 additions and 44 deletions
|
|
@ -248,6 +248,9 @@ struct OptDffWorker
|
||||||
OptDffWorker(const OptDffOptions &opt, Module *mod)
|
OptDffWorker(const OptDffOptions &opt, Module *mod)
|
||||||
: opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod)
|
: opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod)
|
||||||
{
|
{
|
||||||
|
sat_effort = (int64_t)module->design->scratchpad_get_int("opt_dff.sat_effort", 1000000) * 1000;
|
||||||
|
sat_effort_left = sat_effort;
|
||||||
|
|
||||||
// Gathering two kinds of information here for every sigmapped SigBit:
|
// Gathering two kinds of information here for every sigmapped SigBit:
|
||||||
// - bitusers: how many users it has (muxes will only be merged into FFs if the FF is the only user)
|
// - bitusers: how many users it has (muxes will only be merged into FFs if the FF is the only user)
|
||||||
// - bit2mux: the mux cell and bit index that drives it, if any
|
// - bit2mux: the mux cell and bit index that drives it, if any
|
||||||
|
|
@ -970,16 +973,46 @@ struct OptDffWorker
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// One FF bit whose constness needs SAT proofs; each target is a non-const input (D and/or AD)
|
||||||
|
// that must be shown eq to val
|
||||||
|
struct ConstTarget {
|
||||||
|
SigBit sig;
|
||||||
|
int lit = -1;
|
||||||
|
bool proven = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstObligation {
|
||||||
|
Cell *cell;
|
||||||
|
int idx;
|
||||||
|
State val;
|
||||||
|
SigBit q;
|
||||||
|
int q_lit = -1;
|
||||||
|
bool dropped = false;
|
||||||
|
std::vector<ConstTarget> targets;
|
||||||
|
};
|
||||||
|
|
||||||
bool run_constbits()
|
bool run_constbits()
|
||||||
{
|
{
|
||||||
// Find FFs that are provably constant
|
// Find FFs that are provably constant
|
||||||
ModWalker modwalker(module->design, module);
|
ModWalker modwalker(module->design, module);
|
||||||
QuickConeSat qcsat(modwalker);
|
QuickConeSat qcsat(modwalker);
|
||||||
|
|
||||||
std::vector<RTLIL::Cell*> cells_to_remove;
|
dict<Cell *, pool<int>> const_bits;
|
||||||
std::vector<FfData> ffs_to_emit;
|
|
||||||
bool did_something = false;
|
bool did_something = false;
|
||||||
|
|
||||||
|
auto commit_const = [&](Cell *cell, int i, SigBit q, State val) {
|
||||||
|
log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n",
|
||||||
|
val == State::S1 ? 1 : 0, i, cell, cell->type.unescape(), module);
|
||||||
|
initvals.remove_init(q);
|
||||||
|
module->connect(q, val);
|
||||||
|
const_bits[cell].insert(i);
|
||||||
|
did_something = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fold constant D/AD inputs into the tested value directly bits whose remaining inputs are
|
||||||
|
// wires become SAT proof obligations
|
||||||
|
std::vector<ConstObligation> obligations;
|
||||||
|
|
||||||
for (auto cell : module->selected_cells()) {
|
for (auto cell : module->selected_cells()) {
|
||||||
if (!cell->is_builtin_ff())
|
if (!cell->is_builtin_ff())
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -992,58 +1025,181 @@ struct OptDffWorker
|
||||||
if (val == State::Sm)
|
if (val == State::Sm)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check Synchronous input D
|
// Fold all const inputs first, so the SAT targets are checked against the final const
|
||||||
if (ff.has_clk || ff.has_gclk) {
|
if ((ff.has_clk || ff.has_gclk) && !ff.sig_d[i].wire) {
|
||||||
if (!ff.sig_d[i].wire) {
|
val = combine_const(val, ff.sig_d[i].data);
|
||||||
// D is already a constant
|
if (val == State::Sm) continue;
|
||||||
val = combine_const(val, ff.sig_d[i].data);
|
}
|
||||||
if (val == State::Sm) continue;
|
if (ff.has_aload && !ff.sig_ad[i].wire) {
|
||||||
} else if (opt.sat) {
|
val = combine_const(val, ff.sig_ad[i].data);
|
||||||
// Try SAT proof for non-constant D wires
|
if (val == State::Sm) continue;
|
||||||
if (!prove_const_with_sat(qcsat, modwalker, ff.sig_q[i], ff.sig_d[i], val))
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Async Load input AD
|
ConstObligation ob;
|
||||||
if (ff.has_aload) {
|
ob.cell = cell;
|
||||||
if (!ff.sig_ad[i].wire) {
|
ob.idx = i;
|
||||||
val = combine_const(val, ff.sig_ad[i].data);
|
ob.val = val;
|
||||||
if (val == State::Sm) continue;
|
ob.q = ff.sig_q[i];
|
||||||
} else if (opt.sat) {
|
|
||||||
if (!prove_const_with_sat(qcsat, modwalker, ff.sig_q[i], ff.sig_ad[i], val))
|
bool feasible = true;
|
||||||
continue;
|
auto add_target = [&](SigBit sig) {
|
||||||
} else {
|
if (!opt.sat || (val != State::S0 && val != State::S1) ||
|
||||||
continue;
|
!modwalker.has_drivers(sig)) {
|
||||||
|
feasible = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
ConstTarget t;
|
||||||
|
t.sig = sig;
|
||||||
|
ob.targets.push_back(t);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((ff.has_clk || ff.has_gclk) && ff.sig_d[i].wire)
|
||||||
|
add_target(ff.sig_d[i]);
|
||||||
|
if (feasible && ff.has_aload && ff.sig_ad[i].wire)
|
||||||
|
add_target(ff.sig_ad[i]);
|
||||||
|
if (!feasible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ob.targets.empty()) {
|
||||||
|
commit_const(cell, i, ff.sig_q[i], val);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
obligations.push_back(ob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n",
|
int64_t screen_cap = 0;
|
||||||
val ? 1 : 0, i, cell, cell->type.unescape(), module);
|
if (sat_effort > 0 && !obligations.empty()) {
|
||||||
|
// Screening cap, scaled down when the budget cannot afford a full-price screening round
|
||||||
|
int64_t num_queries = 0;
|
||||||
|
for (auto &ob : obligations)
|
||||||
|
num_queries += GetSize(ob.targets);
|
||||||
|
screen_cap = max((int64_t)20000, min((int64_t)200000, sat_effort / (4 * num_queries)));
|
||||||
|
}
|
||||||
|
|
||||||
// Replace the Q output with the constant value
|
for (int batch_begin = 0; batch_begin < GetSize(obligations) && !sat_budget_spent(); ) {
|
||||||
initvals.remove_init(ff.sig_q[i]);
|
QuickConeSat qcsat(modwalker);
|
||||||
module->connect(ff.sig_q[i], val);
|
int64_t cells_charged = 0;
|
||||||
removed_sigbits.insert(i);
|
int batch_end = batch_begin;
|
||||||
|
|
||||||
|
while (batch_end < GetSize(obligations) && !sat_budget_spent()) {
|
||||||
|
if (batch_end > batch_begin && GetSize(qcsat.imported_cells) >= sat_batch_cells)
|
||||||
|
break;
|
||||||
|
auto &ob = obligations[batch_end];
|
||||||
|
ob.q_lit = qcsat.importSigBit(ob.q);
|
||||||
|
for (auto &t : ob.targets)
|
||||||
|
t.lit = qcsat.importSigBit(t.sig);
|
||||||
|
qcsat.prepare();
|
||||||
|
charge_import(qcsat, cells_charged);
|
||||||
|
batch_end++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct FF with constant bits removed
|
int64_t cap = screen_cap;
|
||||||
if (!removed_sigbits.empty()) {
|
bool out_of_budget = false;
|
||||||
std::vector<int> keep_bits;
|
|
||||||
for (int i = 0; i < ff.width; i++)
|
|
||||||
if (!removed_sigbits.count(i))
|
|
||||||
keep_bits.push_back(i);
|
|
||||||
|
|
||||||
if (keep_bits.empty()) {
|
while (!out_of_budget) {
|
||||||
cells_to_remove.push_back(cell);
|
bool all_resolved = true;
|
||||||
} else {
|
|
||||||
ff = ff.slice(keep_bits);
|
for (int obi = batch_begin; obi < batch_end; obi++) {
|
||||||
ff.cell = cell;
|
auto &ob = obligations[obi];
|
||||||
ffs_to_emit.push_back(ff);
|
if (ob.dropped)
|
||||||
|
continue;
|
||||||
|
for (auto &t : ob.targets) {
|
||||||
|
if (t.proven || ob.dropped)
|
||||||
|
continue;
|
||||||
|
if (sat_budget_spent()) {
|
||||||
|
out_of_budget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> modelExprs;
|
||||||
|
std::vector<ConstObligation *> model_obs;
|
||||||
|
for (int obi2 = batch_begin; obi2 < batch_end; obi2++) {
|
||||||
|
auto &ob2 = obligations[obi2];
|
||||||
|
if (ob2.dropped)
|
||||||
|
continue;
|
||||||
|
for (auto &t2 : ob2.targets)
|
||||||
|
if (!t2.proven) {
|
||||||
|
modelExprs.push_back(t2.lit);
|
||||||
|
modelExprs.push_back(ob2.q_lit);
|
||||||
|
model_obs.push_back(&ob2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prove that the next value equals the constant in every state where Q already holds it
|
||||||
|
int vlit = qcsat.ez->value(ob.val == State::S1);
|
||||||
|
std::vector<int> assumptions;
|
||||||
|
assumptions.push_back(qcsat.ez->IFF(ob.q_lit, vlit));
|
||||||
|
assumptions.push_back(qcsat.ez->NOT(qcsat.ez->IFF(t.lit, vlit)));
|
||||||
|
|
||||||
|
std::vector<bool> modelVals;
|
||||||
|
int res = solve_budgeted(qcsat, cap, modelExprs, modelVals, assumptions);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
all_resolved = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
t.proven = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Counterexample: this bit is not constant
|
||||||
|
// Any other pending bit whose Q holds its constant while its next value differs is
|
||||||
|
// disproven by the same state
|
||||||
|
for (int k = 0; k < GetSize(model_obs); k++) {
|
||||||
|
ConstObligation *ob2 = model_obs[k];
|
||||||
|
if (ob2->dropped)
|
||||||
|
continue;
|
||||||
|
bool want = (ob2->val == State::S1);
|
||||||
|
bool t_val = modelVals[2*k];
|
||||||
|
bool q_val = modelVals[2*k + 1];
|
||||||
|
if (q_val == want && t_val != want)
|
||||||
|
ob2->dropped = true;
|
||||||
|
}
|
||||||
|
ob.dropped = true;
|
||||||
|
}
|
||||||
|
if (out_of_budget)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
did_something = true;
|
|
||||||
|
if (out_of_budget || all_resolved)
|
||||||
|
break;
|
||||||
|
cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
batch_begin = batch_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &ob : obligations) {
|
||||||
|
if (ob.dropped)
|
||||||
|
continue;
|
||||||
|
bool all_proven = true;
|
||||||
|
for (auto &t : ob.targets)
|
||||||
|
all_proven &= t.proven;
|
||||||
|
if (all_proven)
|
||||||
|
commit_const(ob.cell, ob.idx, ob.q, ob.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct FF with constant bits removed
|
||||||
|
std::vector<RTLIL::Cell*> cells_to_remove;
|
||||||
|
std::vector<FfData> ffs_to_emit;
|
||||||
|
|
||||||
|
for (auto &kv : const_bits) {
|
||||||
|
Cell *cell = kv.first;
|
||||||
|
FfData ff(&initvals, cell);
|
||||||
|
std::vector<int> keep_bits;
|
||||||
|
for (int i = 0; i < ff.width; i++)
|
||||||
|
if (!kv.second.count(i))
|
||||||
|
keep_bits.push_back(i);
|
||||||
|
|
||||||
|
if (keep_bits.empty()) {
|
||||||
|
cells_to_remove.push_back(cell);
|
||||||
|
} else {
|
||||||
|
ff = ff.slice(keep_bits);
|
||||||
|
ff.cell = cell;
|
||||||
|
ffs_to_emit.push_back(ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue