ecp5: Fix placement of ECLKSYNCB driving PLL CLKFB (#1512)

* ecp5: Fix placement of ECLKSYNCB driving PLL CLKFB

Signed-off-by: gatecat <gatecat@ds0.me>

* ecp5: Improve PLL placement algorithm slightly

Signed-off-by: gatecat <gatecat@ds0.me>

---------

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
myrtle 2025-07-08 08:50:12 +02:00 committed by GitHub
parent 0ebd7afab9
commit 24785a3219
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 70 additions and 24 deletions

View File

@ -1498,6 +1498,9 @@ class Ecp5Packer
available_plls.erase(ctx->getBelByNameStr(ci->attrs.at(id_BEL).as_string())); available_plls.erase(ctx->getBelByNameStr(ci->attrs.at(id_BEL).as_string()));
} }
// Place PLL connected to fixed drivers such as IO close to their source // Place PLL connected to fixed drivers such as IO close to their source
bool did_something = false;
do {
did_something = false;
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get(); CellInfo *ci = cell.second.get();
if (ci->type == id_EHXPLLL && !ci->attrs.count(id_BEL)) { if (ci->type == id_EHXPLLL && !ci->attrs.count(id_BEL)) {
@ -1513,7 +1516,7 @@ class Ecp5Packer
int closest_distance = std::numeric_limits<int>::max(); int closest_distance = std::numeric_limits<int>::max();
for (auto bel : available_plls) { for (auto bel : available_plls) {
Loc pllloc = ctx->getBelLocation(bel); Loc pllloc = ctx->getBelLocation(bel);
int distance = std::abs(drvloc.x - pllloc.x) + std::abs(drvloc.y - pllloc.y); int distance = 2 * std::abs(drvloc.x - pllloc.x) + std::abs(drvloc.y - pllloc.y);
if (distance < closest_distance) { if (distance < closest_distance) {
closest_pll = bel; closest_pll = bel;
closest_distance = distance; closest_distance = distance;
@ -1523,8 +1526,10 @@ class Ecp5Packer
log_error("failed to place PLL '%s'\n", ci->name.c_str(ctx)); log_error("failed to place PLL '%s'\n", ci->name.c_str(ctx));
available_plls.erase(closest_pll); available_plls.erase(closest_pll);
ci->attrs[id_BEL] = ctx->getBelName(closest_pll).str(ctx); ci->attrs[id_BEL] = ctx->getBelName(closest_pll).str(ctx);
did_something = true;
} }
} }
} while (did_something);
// Place PLLs driven by logic, etc, randomly // Place PLLs driven by logic, etc, randomly
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get(); CellInfo *ci = cell.second.get();
@ -1775,6 +1780,28 @@ class Ecp5Packer
log_error("Unsupported DEL_MODE '%s'\n", del_mode.c_str()); log_error("Unsupported DEL_MODE '%s'\n", del_mode.c_str());
} }
std::vector<BelId> get_pll_eclkbs(WireId pll_clkfb)
{
std::vector<BelId> result;
std::queue<std::pair<WireId, int>> visit;
visit.emplace(pll_clkfb, 0);
while (!visit.empty()) {
auto top = visit.front();
visit.pop();
for (auto belpin : ctx->getWireBelPins(top.first)) {
if (ctx->getBelType(belpin.bel) == id_ECLKSYNCB && belpin.pin == id_ECLKO) {
result.push_back(belpin.bel);
}
}
if (top.second > 6) // depth limit
break;
for (auto pip : ctx->getPipsUphill(top.first)) {
visit.emplace(ctx->getPipSrcWire(pip), top.second + 1);
}
}
return result;
}
// Pack IOLOGIC // Pack IOLOGIC
void pack_iologic() void pack_iologic()
{ {
@ -2510,6 +2537,7 @@ class Ecp5Packer
} }
} }
} }
eclksync_done: eclksync_done:
continue; continue;
} else if (ci->type == id_DDRDLLA) { } else if (ci->type == id_DDRDLLA) {
@ -2578,6 +2606,24 @@ class Ecp5Packer
// Most will be dealt with above, but there might be some rogue cases // Most will be dealt with above, but there might be some rogue cases
if (ci->attrs.count(id_BEL)) if (ci->attrs.count(id_BEL))
continue; continue;
const NetInfo *eclko = ci->getPort(id_ECLKO);
if (eclko) {
// Look at the path to PLL CLKFB (apparently used by Lattice wizard for DDR71)
for (auto user : eclko->users) {
if (user.cell->type == id_EHXPLLL && user.port == id_CLKFB && user.cell->attrs.count(id_BEL)) {
BelId pll_bel = ctx->getBelByNameStr(user.cell->attrs.at(id_BEL).as_string());
WireId clkfb_wire = ctx->getBelPinWire(pll_bel, user.port);
for (auto bel : get_pll_eclkbs(clkfb_wire)) {
if (used_eclksyncb.count(bel))
continue;
log_info("Constraining ECLKSYNCB '%s' to bel '%s' based on CLKFB routing\n",
ctx->nameOf(ci), ctx->nameOfBel(bel));
ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx);
goto eclksync_ii_done;
}
}
}
}
for (BelId bel : ctx->getBels()) { for (BelId bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ECLKSYNCB) if (ctx->getBelType(bel) != id_ECLKSYNCB)
continue; continue;