Skip to content

Commit

Permalink
widelut support for xo2/xo3/xo3d
Browse files Browse the repository at this point in the history
  • Loading branch information
mmicko authored and gatecat committed Aug 29, 2023
1 parent e08471d commit 688f1ba
Showing 1 changed file with 187 additions and 0 deletions.
187 changes: 187 additions & 0 deletions machxo2/pack.cc
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,192 @@ class MachXO2Packer
return false;
}

// Pass to pack LUT5s into a newly created slice
void pack_lut5xs()
{
log_info("Packing LUT5-7s...\n");

// Gets the "COMB1" side of a LUT5, where we pack a LUT[67] into
auto get_comb1_from_lut5 = [&](CellInfo *lut5) {
NetInfo *f1 = lut5->getPort(id_F1);
NPNR_ASSERT(f1 != nullptr);
NPNR_ASSERT(f1->driver.cell != nullptr);
return f1->driver.cell;
};

dict<IdString, std::pair<CellInfo *, CellInfo *>> lut5_roots, lut6_roots, lut7_roots;
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (is_pfumx(ctx, ci)) {
NetInfo *f0 = ci->ports.at(id_BLUT).net;

if (f0 == nullptr)
log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx));
NetInfo *f1 = ci->ports.at(id_ALUT).net;
if (f1 == nullptr)
log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx));

CellInfo *lut0 =
(f0->driver.cell && f0->driver.cell->type == id_TRELLIS_COMB && f0->driver.port == id_F)
? f0->driver.cell
: nullptr;
CellInfo *lut1 =
(f1->driver.cell && f1->driver.cell->type == id_TRELLIS_COMB && f1->driver.port == id_F)
? f1->driver.cell
: nullptr;
if (lut0 == nullptr || lut0->cluster != ClusterId())
log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
if (lut1 == nullptr || lut1->cluster != ClusterId())
log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
lut0->addInput(id_F1);
lut0->addInput(id_M);
lut0->addOutput(id_OFX);

ci->movePortTo(id_Z, lut0, id_OFX);
ci->movePortTo(id_ALUT, lut0, id_F1);
ci->movePortTo(id_C0, lut0, id_M);
ci->disconnectPort(id_BLUT);

lut5_roots[lut0->name] = {lut0, lut1};
packed_cells.insert(ci->name);
}
}
flush_cells();
// Pack LUT6s
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (is_l6mux(ctx, ci)) {
NetInfo *ofx0_0 = ci->ports.at(id_D0).net;
if (ofx0_0 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
NetInfo *ofx0_1 = ci->ports.at(id_D1).net;
if (ofx0_1 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
CellInfo *comb0 = (ofx0_0->driver.cell && ofx0_0->driver.cell->type == id_TRELLIS_COMB &&
ofx0_0->driver.port == id_OFX)
? ofx0_0->driver.cell
: nullptr;
CellInfo *comb1 = (ofx0_1->driver.cell && ofx0_1->driver.cell->type == id_TRELLIS_COMB &&
ofx0_1->driver.port == id_OFX)
? ofx0_1->driver.cell
: nullptr;
if (comb0 == nullptr) {
if (!net_driven_by(ctx, ofx0_0, is_l6mux, id_Z))
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
"('%s.%s')\n",
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
ofx0_0->driver.port.c_str(ctx));
continue;
}
if (lut6_roots.count(comb0->name))
continue;

if (comb1 == nullptr) {
if (!net_driven_by(ctx, ofx0_1, is_l6mux, id_Z))
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
"('%s.%s')\n",
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
ofx0_0->driver.port.c_str(ctx));
continue;
}
if (lut6_roots.count(comb1->name))
continue;
if (ctx->verbose)
log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx));
comb0 = get_comb1_from_lut5(comb0);
comb1 = get_comb1_from_lut5(comb1);

comb1->addInput(id_FXA);
comb1->addInput(id_FXB);
comb1->addInput(id_M);
comb1->addOutput(id_OFX);
ci->movePortTo(id_D0, comb1, id_FXA);
ci->movePortTo(id_D1, comb1, id_FXB);
ci->movePortTo(id_SD, comb1, id_M);
ci->movePortTo(id_Z, comb1, id_OFX);
lut6_roots[comb1->name] = {comb0, comb1};
packed_cells.insert(ci->name);
}
}
flush_cells();
// Pack LUT7s
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (is_l6mux(ctx, ci)) {
NetInfo *ofx1_0 = ci->ports.at(id_D0).net;
if (ofx1_0 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
NetInfo *ofx1_1 = ci->ports.at(id_D1).net;
if (ofx1_1 == nullptr)
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
CellInfo *comb1 = (ofx1_0->driver.cell && ofx1_0->driver.cell->type == id_TRELLIS_COMB &&
ofx1_0->driver.port == id_OFX)
? ofx1_0->driver.cell
: nullptr;
CellInfo *comb3 = (ofx1_1->driver.cell && ofx1_1->driver.cell->type == id_TRELLIS_COMB &&
ofx1_1->driver.port == id_OFX)
? ofx1_1->driver.cell
: nullptr;
if (comb1 == nullptr)
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n",
ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx),
ofx1_0->driver.port.c_str(ctx));
if (comb3 == nullptr)
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n",
ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx),
ofx1_1->driver.port.c_str(ctx));

NetInfo *fxa_0 = comb1->ports.at(id_FXA).net;
if (fxa_0 == nullptr)
log_error("SLICE '%s' has disconnected port 'FXA'\n", comb1->name.c_str(ctx));
NetInfo *fxa_1 = comb3->ports.at(id_FXA).net;
if (fxa_1 == nullptr)
log_error("SLICE '%s' has disconnected port 'FXA'\n", comb3->name.c_str(ctx));

CellInfo *comb2 = net_driven_by(
ctx, fxa_1,
[](const Context *ctx, const CellInfo *ci) {
(void)ctx;
return ci->type == id_TRELLIS_COMB;
},
id_OFX);
if (comb2 == nullptr)
log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n",
comb3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx),
fxa_1->driver.port.c_str(ctx));
comb2 = get_comb1_from_lut5(comb2);
comb2->addInput(id_FXA);
comb2->addInput(id_FXB);
comb2->addInput(id_M);
comb2->addOutput(id_OFX);
ci->movePortTo(id_D0, comb2, id_FXA);
ci->movePortTo(id_D1, comb2, id_FXB);
ci->movePortTo(id_SD, comb2, id_M);
ci->movePortTo(id_Z, comb2, id_OFX);

lut7_roots[comb2->name] = {comb1, comb3};
packed_cells.insert(ci->name);
}
}

for (auto &root : lut7_roots) {
auto &cells = root.second;
cells.second->cluster = cells.second->name;
cells.second->constr_abs_z = true;
cells.second->constr_z = (1 << Arch::lc_idx_shift) | Arch::BEL_COMB;
rel_constr_cells(cells.second, cells.first, (4 << Arch::lc_idx_shift));
}
for (auto &root : lut6_roots) {
auto &cells = root.second;
rel_constr_cells(cells.second, cells.first, (2 << Arch::lc_idx_shift));
}
for (auto &root : lut5_roots) {
auto &cells = root.second;
rel_constr_cells(cells.first, cells.second, (1 << Arch::lc_idx_shift));
}
flush_cells();
}

// Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated
void pack_io()
{
Expand Down Expand Up @@ -1442,6 +1628,7 @@ class MachXO2Packer
pack_dram();
pack_carries();
pack_luts();
pack_lut5xs();
pack_ffs();
promote_globals();
place_globals();
Expand Down

0 comments on commit 688f1ba

Please sign in to comment.