3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-15 19:55:41 +00:00

opt_prienc: require ConstEval fingerprint inputs to be a valid cut

The round-robin (and PE/CLZ/CTZ) fingerprints pin candidate request/
start/select signals as free ConstEval inputs and evaluate the encoder
output cone. ConstEval::eval() re-computes and re-set()s the FULL output
of every combinational cell it needs. If a pinned bit is a combinational
cell output and a sibling output bit of that same cell is pulled into the
cone, evaluating the sibling re-sets the pinned bit to the cell's real
value, contradicting the free value we pinned and tripping the assertion
`current_val[i].wire != NULL || current_val[i] == value[i]` in
kernel/consteval.h.

The earlier clean_set_signals() guard only rejected constant/aliased
bits; it did not ensure the pinned signals form a valid cut. Candidates
are gathered purely by width, so an internal combinational wire (e.g. a
slice of a wider arithmetic result) can be pinned, which is exactly what
crashed on veer_speed1/picorv32/murax/raygentop.

Add is_valid_consteval_cut(): a pinned bit is a safe leaf when it is a
primary input, sequential-cell output or undriven (absent from
bit_to_driver, which holds combinational drivers only); a combinational
output is safe only if that cell's entire output lies within the pinned
cut. Apply it in both fingerprint() and fingerprint_rr(). Declining an
unclean cut only forgoes a possible rewrite, never yields a wrong one,
and the intended arbiter inputs (request ports, idx_last flop outputs)
remain valid cuts so real round-robin patterns still rewrite.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Akash Levy 2026-07-07 00:00:24 -07:00
parent a4fa9c31e8
commit 0a69890e3f

View file

@ -260,6 +260,39 @@ struct OptPriEncWorker {
return true;
}
// A set of signals is a valid ConstEval "cut" to pin as free inputs only if
// pinning them can never collide with a value ConstEval derives while
// evaluating the cone. ConstEval::eval() re-computes and re-set()s the FULL
// output of any combinational cell it needs: so if a pinned bit is a
// combinational-cell output and a *sibling* output bit of that same cell
// lies outside the cut (and is pulled into the cone), evaluating the sibling
// re-sets the pinned bit to the cell's real value, which contradicts the
// free value we pinned -> the ConstEval assertion in set() fires.
//
// A bit is a safe leaf when it is a primary input, sequential-cell output or
// undriven (all absent from bit_to_driver, which holds combinational drivers
// only). A combinational-cell output is safe only if that cell's entire
// output lies within the cut. `cut` must be the union of every signal pinned
// together before a shared eval.
bool is_valid_consteval_cut(const SigSpec& cut) {
pool<SigBit> cut_bits;
for (auto bit : cut)
if (bit.wire) cut_bits.insert(bit);
for (auto bit : cut) {
if (bit.wire == nullptr) return false;
auto it = bit_to_driver.find(bit);
if (it == bit_to_driver.end()) continue; // safe leaf
Cell* d = it->second;
for (auto& conn : d->connections()) {
if (!d->output(conn.first)) continue;
for (auto ob : sigmap(conn.second))
if (ob.wire && !cut_bits.count(ob))
return false;
}
}
return true;
}
// Run all candidate test vectors through ConstEval and try to match each of
// the four PE variants against the recorded outputs. Returns the matched
// variant, or NONE.
@ -277,6 +310,9 @@ struct OptPriEncWorker {
if (!clean_set_signals({&T_sig}))
return PEVariant::NONE;
if (!is_valid_consteval_cut(T_sig))
return PEVariant::NONE;
auto vs = gen_test_vectors(N);
for (auto& v : vs) {
ce.push();
@ -462,6 +498,10 @@ struct OptPriEncWorker {
ConstEval ce(module);
if (!clean_set_signals({&req_sig, &start_sig}))
return -1;
SigSpec cut = req_sig;
cut.append(start_sig);
if (!is_valid_consteval_cut(cut))
return -1;
bool ok0 = true, ok1 = true;
auto deck = gen_test_vectors(N);
int checks = 0;