xilinx: Improve LUT/CARRY->FF packing (#1683)

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
myrtle 2026-04-01 11:22:00 +02:00 committed by GitHub
parent 497d685139
commit 10c5997007
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 117 additions and 17 deletions

View File

@ -222,6 +222,66 @@ void XilinxPacker::pack_ffs()
generic_xform(ff_rules, true);
}
bool XilinxPacker::can_add_ff_to_cluster(const CellInfo *lut, const CellInfo *ff)
{
bool found_wclk = false;
bool found_ff = false;
const NetInfo *clk = nullptr, *ce = nullptr, *sr = nullptr;
bool is_clkinv = false, is_srinv = false, is_latch = false, is_ffsync = false;
CellInfo *base = ctx->cells.at(lut->cluster).get();
auto process_cell = [&](const CellInfo *cell) {
if (cell->constr_x != lut->constr_x || cell->constr_y != lut->constr_y)
return;
if (cell->type == id_SLICE_LUTX && cell->getPort(id_CLK)) {
found_wclk = true;
clk = cell->getPort(id_CLK);
}
if (cell->type == id_SLICE_FFX) {
found_ff = true;
clk = cell->getPort(id_CK);
ce = cell->getPort(id_CE);
sr = cell->getPort(id_SR);
is_clkinv = bool_or_default(cell->params, id_IS_CLK_INVERTED, false);
is_srinv = bool_or_default(cell->params, id_IS_R_INVERTED, false) ||
bool_or_default(cell->params, id_IS_S_INVERTED, false) ||
bool_or_default(cell->params, id_IS_CLR_INVERTED, false) ||
bool_or_default(cell->params, id_IS_PRE_INVERTED, false);
is_latch = cell->attrs.count(id_X_FF_AS_LATCH);
is_ffsync = cell->attrs.count(id_X_FFSYNC);
}
};
process_cell(base);
for (auto child : base->constr_children)
process_cell(child);
if (!found_wclk && !found_ff)
return true;
if (ff->getPort(id_CK) != clk)
return false;
if (!found_ff)
return true;
if (ff->getPort(id_CE) != ce)
return false;
if (ff->getPort(id_SR) != sr)
return false;
if (bool_or_default(ff->params, id_IS_CLK_INVERTED, false) != is_clkinv)
return false;
if ((bool_or_default(ff->params, id_IS_R_INVERTED, false) || bool_or_default(ff->params, id_IS_S_INVERTED, false) ||
bool_or_default(ff->params, id_IS_CLR_INVERTED, false) ||
bool_or_default(ff->params, id_IS_PRE_INVERTED, false)) != is_srinv)
return false;
if (ff->attrs.count(id_X_FF_AS_LATCH) != is_latch)
return false;
if (ff->attrs.count(id_X_FFSYNC) != is_ffsync)
return false;
return true;
}
void XilinxPacker::pack_lutffs()
{
int pairs = 0;
@ -232,18 +292,53 @@ void XilinxPacker::pack_lutffs()
if (ci->type != id_SLICE_FFX)
continue;
NetInfo *d = ci->getPort(id_D);
if (d->driver.cell == nullptr || d->driver.cell->type != id_SLICE_LUTX || d->driver.port != id_O6)
if (d->driver.cell == nullptr)
continue;
CellInfo *lut = d->driver.cell;
if (lut->cluster != ClusterId() || !lut->constr_children.empty())
continue;
lut->constr_children.push_back(ci);
lut->cluster = lut->name;
ci->cluster = lut->name;
ci->constr_x = 0;
ci->constr_y = 0;
ci->constr_z = (BEL_FF - BEL_6LUT);
++pairs;
if (d->driver.cell->type == id_SLICE_LUTX && d->driver.port == id_O6) {
CellInfo *lut = d->driver.cell;
if (lut->cluster != ClusterId() || !lut->constr_children.empty()) {
if (d->users.entries() != 1)
continue; // fanout might not be packable
if (!can_add_ff_to_cluster(lut, ci))
continue;
CellInfo *base = ctx->cells.at(lut->cluster).get();
base->constr_children.push_back(ci);
ci->cluster = lut->cluster;
ci->constr_x = lut->constr_x;
ci->constr_y = lut->constr_y;
ci->constr_abs_z = lut->constr_abs_z;
ci->constr_z = lut->constr_z + (BEL_FF - BEL_6LUT);
++pairs;
} else {
lut->constr_children.push_back(ci);
lut->cluster = lut->name;
ci->cluster = lut->name;
ci->constr_x = 0;
ci->constr_y = 0;
ci->constr_z = (BEL_FF - BEL_6LUT);
++pairs;
}
} else if (d->driver.cell->type == id_CARRY4) {
CellInfo *carry = d->driver.cell;
if (carry->cluster != ClusterId() || !carry->constr_children.empty()) {
auto port = d->driver.port.str(ctx);
if (port.size() != 2 || port[0] != 'O')
continue;
int z = std::stoi(port.substr(1));
if (d->users.entries() != 1)
continue; // fanout might not be packable
if (!can_add_ff_to_cluster(carry, ci))
continue;
CellInfo *base = ctx->cells.at(carry->cluster).get();
base->constr_children.push_back(ci);
ci->cluster = carry->cluster;
ci->constr_x = carry->constr_x;
ci->constr_y = carry->constr_y;
ci->constr_abs_z = carry->constr_abs_z;
ci->constr_z = carry->constr_z + ((BEL_FF | (z << 4)) - BEL_CARRY4);
++pairs;
}
}
}
log_info("Constrained %d LUTFF pairs.\n", pairs);
}

View File

@ -120,6 +120,7 @@ struct XilinxPacker
void pack_inverters();
void pack_luts();
void pack_ffs();
bool can_add_ff_to_cluster(const CellInfo *lut, const CellInfo *ff);
void pack_lutffs();
bool is_constrained(const CellInfo *cell);

View File

@ -173,7 +173,9 @@ bool XilinxImpl::xc7_logic_tile_valid(IdString tile_type, const LogicTileStatus
if (ff1 != nullptr && ff1->ff.d != nullptr && ff1->ff.d->driver.cell != nullptr) {
auto &drv = ff1->ff.d->driver;
if ((drv.cell == lts.cells[(i << 4) | BEL_6LUT] && drv.port != id_MC31) ||
drv.cell == lts.cells[(i << 4) | BEL_5LUT] || drv.cell == out_fmux_cell) {
drv.cell == lts.cells[(i << 4) | BEL_5LUT] || drv.cell == out_fmux_cell ||
((carry4 && drv.cell == lts.cells[((i / 4) << 6) | BEL_CARRY4] &&
carry4->carry.out_sigs[i % 4] == ff1->ff.d))) {
// Direct, OK
} else {
// Indirect, must use X input
@ -230,12 +232,14 @@ bool XilinxImpl::xc7_logic_tile_valid(IdString tile_type, const LogicTileStatus
}
if (carry4 != nullptr && carry4->carry.out_sigs[i % 4] != nullptr) {
// FIXME: direct connections to FF
if (mux_output_used) {
DBG();
return false; // Memory and SRLs only valid in SLICEMs
NetInfo *o = carry4->carry.out_sigs[i % 4];
if (o->users.entries() > 1 || (ff1 == nullptr || o != ff1->ff.d)) {
if (mux_output_used) {
DBG();
return false; // Memory and SRLs only valid in SLICEMs
}
mux_output_used = true;
}
mux_output_used = true;
}
if (out_fmux_cell != nullptr) {
auto out_fmux = get_tags(out_fmux_cell);