3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-20 06:05:50 +00:00

utils: add BitGrouper for shared bit-partition logic

This commit is contained in:
Emil J. Tywoniak 2026-05-30 12:42:38 +02:00
parent d952b04e54
commit ea41e61a36
4 changed files with 166 additions and 70 deletions

View file

@ -170,10 +170,11 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
std::vector<RTLIL::SigBit> bits_a = sig_a, bits_b = sig_b, bits_y = sig_y;
enum { GRP_DYN, GRP_CONST_A, GRP_CONST_B, GRP_CONST_AB, GRP_N };
std::map<std::pair<RTLIL::SigBit, RTLIL::SigBit>, std::set<RTLIL::SigBit>> grouped_bits[GRP_N];
for (int i = 0; i < GetSize(bits_y); i++)
{
// Two-level partition: outer key is the rewrite kind (GRP_*), inner key
// is the (a, b) pair that selects this bit's slot inside the new cell.
// Two original bits sharing (kind, a, b) collapse to the same output bit.
using Key = std::tuple<int, RTLIL::SigBit, RTLIL::SigBit>;
BitGrouper<Key> grouper(GetSize(bits_y), [&](int i) -> std::optional<Key> {
int group_idx = GRP_DYN;
RTLIL::SigBit bit_a = bits_a[i], bit_b = bits_b[i];
@ -208,11 +209,16 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
group_idx = GRP_CONST_B;
}
grouped_bits[group_idx][std::pair<RTLIL::SigBit, RTLIL::SigBit>(bit_a, bit_b)].insert(bits_y[i]);
}
return std::make_tuple(group_idx, bit_a, bit_b);
});
for (int i = 0; i < GRP_N; i++)
if (GetSize(grouped_bits[i]) == GetSize(bits_y))
// If every original bit ended up with its own unique (a, b) slot within
// some single kind, splitting would just rename without shrinking.
int slots_per_kind[GRP_N] = {};
for (auto &g : grouper.groups())
slots_per_kind[std::get<0>(g.key)]++;
for (int kind = 0; kind < GRP_N; kind++)
if (slots_per_kind[kind] == GetSize(bits_y))
return false;
log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
@ -223,23 +229,33 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
// Replacement bit for each original sig_y bit.
dict<SigBit, SigBit> bit_map;
for (int i = 0; i < GRP_N; i++)
// Pre-collect groups per kind, preserving BitGrouper's insertion order
// within each kind.
std::vector<const BitGrouper<Key>::Group*> per_kind[GRP_N];
for (auto &g : grouper.groups())
per_kind[std::get<0>(g.key)].push_back(&g);
for (int kind = 0; kind < GRP_N; kind++)
{
if (grouped_bits[i].empty())
if (per_kind[kind].empty())
continue;
int group_size = GetSize(grouped_bits[i]);
int group_size = GetSize(per_kind[kind]);
RTLIL::SigSpec new_y = patcher.addWire(NEW_ID, group_size);
RTLIL::SigSpec new_a, new_b;
for (auto &it : grouped_bits[i]) {
for (auto &bit : it.second)
bit_map[bit] = new_y[new_a.size()];
new_a.append(it.first.first);
new_b.append(it.first.second);
int slot = 0;
for (auto *g : per_kind[kind]) {
SigBit bit_a = std::get<1>(g->key);
SigBit bit_b = std::get<2>(g->key);
new_a.append(bit_a);
new_b.append(bit_b);
for (int i : g->indices)
bit_map[bits_y[i]] = new_y[slot];
slot++;
}
if (cell->type.in(ID($and), ID($or)) && i == GRP_CONST_A) {
if (cell->type.in(ID($and), ID($or)) && kind == GRP_CONST_A) {
if (!keepdc) {
if (cell->type == ID($and))
new_a.replace(dict<SigBit,SigBit>{{State::Sx, State::S0}, {State::Sz, State::S0}}, &new_b);
@ -259,34 +275,34 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
continue;
}
if (cell->type.in(ID($xor), ID($xnor)) && i == GRP_CONST_A) {
if (cell->type.in(ID($xor), ID($xnor)) && kind == GRP_CONST_A) {
SigSpec undef_a, undef_y, undef_b;
SigSpec def_y, def_a, def_b;
for (int i = 0; i < GetSize(new_y); i++) {
bool undef = new_a[i] == State::Sx || new_a[i] == State::Sz;
if (!keepdc && (undef || new_a[i] == new_b[i])) {
undef_a.append(new_a[i]);
for (int j = 0; j < GetSize(new_y); j++) {
bool undef = new_a[j] == State::Sx || new_a[j] == State::Sz;
if (!keepdc && (undef || new_a[j] == new_b[j])) {
undef_a.append(new_a[j]);
if (cell->type == ID($xor))
undef_b.append(State::S0);
// For consistency since simplemap does $xnor -> $_XOR_ + $_NOT_
else if (cell->type == ID($xnor))
undef_b.append(State::S1);
else log_abort();
undef_y.append(new_y[i]);
undef_y.append(new_y[j]);
}
else if (new_a[i] == State::S0 || new_a[i] == State::S1) {
undef_a.append(new_a[i]);
else if (new_a[j] == State::S0 || new_a[j] == State::S1) {
undef_a.append(new_a[j]);
if (cell->type == ID($xor))
undef_b.append(new_a[i] == State::S1 ? patcher.Not(NEW_ID, new_b[i]).as_bit() : new_b[i]);
undef_b.append(new_a[j] == State::S1 ? patcher.Not(NEW_ID, new_b[j]).as_bit() : new_b[j]);
else if (cell->type == ID($xnor))
undef_b.append(new_a[i] == State::S1 ? new_b[i] : patcher.Not(NEW_ID, new_b[i]).as_bit());
undef_b.append(new_a[j] == State::S1 ? new_b[j] : patcher.Not(NEW_ID, new_b[j]).as_bit());
else log_abort();
undef_y.append(new_y[i]);
undef_y.append(new_y[j]);
}
else {
def_a.append(new_a[i]);
def_b.append(new_b[i]);
def_y.append(new_y[i]);
def_a.append(new_a[j]);
def_b.append(new_b[j]);
def_y.append(new_y[j]);
}
}
if (!undef_y.empty()) {