ecp5: Proof-of-concept using IdStringList for bel names

This uses the new IdStringList API to store bel names for the ECP5. Note
that other arches and the GUI do not yet build with this
proof-of-concept patch.

getBelByName still uses the old implementation and could be more
efficiently implemented with further development.

Signed-off-by: D. Shah <dave@ds0.me>
This commit is contained in:
D. Shah 2021-01-29 12:12:12 +00:00
parent 0dbe7f96a3
commit 6d23461bcd
12 changed files with 101 additions and 61 deletions

View File

@ -36,10 +36,10 @@ void archcheck_names(const Context *ctx)
log_info("Checking bel names..\n");
for (BelId bel : ctx->getBels()) {
IdString name = ctx->getBelName(bel);
IdStringList name = ctx->getBelName(bel);
BelId bel2 = ctx->getBelByName(name);
if (bel != bel2) {
log_error("bel != bel2, name = %s\n", name.c_str(ctx));
log_error("bel != bel2, name = %s\n", ctx->nameOfBel(bel));
}
}

View File

@ -111,6 +111,14 @@ TimingConstrObjectId BaseCtx::timingWildcardObject()
return id;
}
std::string &StrRingBuffer::next()
{
std::string &s = buffer.at(index++);
if (index >= N)
index = 0;
return s;
}
TimingConstrObjectId BaseCtx::timingClockDomainObject(NetInfo *clockDomain)
{
NPNR_ASSERT(clockDomain->clkconstr != nullptr);
@ -283,7 +291,9 @@ void BaseCtx::removeConstraint(IdString constrName)
const char *BaseCtx::nameOfBel(BelId bel) const
{
const Context *ctx = getCtx();
return ctx->getBelName(bel).c_str(ctx);
std::string &s = ctx->log_strs.next();
ctx->getBelName(bel).build_str(ctx, s);
return s.c_str();
}
const char *BaseCtx::nameOfWire(WireId wire) const
@ -304,6 +314,12 @@ const char *BaseCtx::nameOfGroup(GroupId group) const
return ctx->getGroupName(group).c_str(ctx);
}
BelId BaseCtx::getBelByNameStr(const std::string &str)
{
Context *ctx = getCtx();
return ctx->getBelByName(IdStringList::parse(ctx, str));
}
WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
{
if (net_info->driver.cell == nullptr)
@ -655,7 +671,7 @@ void BaseCtx::archInfoToAttributes()
if (ci->attrs.find(id("BEL")) != ci->attrs.end()) {
ci->attrs.erase(ci->attrs.find(id("BEL")));
}
ci->attrs[id("NEXTPNR_BEL")] = getCtx()->getBelName(ci->bel).str(this);
ci->attrs[id("NEXTPNR_BEL")] = getCtx()->getBelName(ci->bel).str(getCtx());
ci->attrs[id("BEL_STRENGTH")] = (int)ci->belStrength;
}
if (ci->constr_x != ci->UNCONSTR)
@ -707,7 +723,7 @@ void BaseCtx::attributesToArchInfo()
if (str != ci->attrs.end())
strength = (PlaceStrength)str->second.as_int64();
BelId b = getCtx()->getBelByName(id(val->second.as_string()));
BelId b = getCtx()->getBelByNameStr(val->second.as_string());
getCtx()->bindBel(b, ci, strength);
}

View File

@ -148,14 +148,13 @@ NEXTPNR_NAMESPACE_BEGIN
// An small size optimised array that is statically allocated when the size is N or less; heap allocated otherwise
template <typename T, size_t N> class SSOArray
{
private:
union
{
T data_static[N];
T *data_heap;
};
size_t m_size;
private:
inline bool is_heap() const { return (m_size > N); }
void alloc()
{
@ -224,8 +223,8 @@ struct IdStringList
SSOArray<IdString, 4> ids;
IdStringList(){};
explicit IdStringList(size_t n) : ids(n, IdString()){};
explicit IdStringList(IdString id) : ids(1, id){};
IdStringList(size_t n) : ids(n, IdString()){};
IdStringList(IdString id) : ids(1, id){};
template <typename Tlist> IdStringList(const Tlist &list) : ids(list){};
static IdStringList parse(Context *ctx, const std::string &str);
@ -238,6 +237,19 @@ struct IdStringList
const IdString &operator[](size_t idx) const { return ids[idx]; }
};
// A ring buffer of strings, so we can return a simple const char * pointer for %s formatting - inspired by how logging
// in Yosys works Let's just hope noone tries to log more than 100 things in one call....
class StrRingBuffer
{
private:
static const size_t N = 100;
std::array<std::string, N> buffer;
size_t index = 0;
public:
std::string &next();
};
struct GraphicElement
{
enum type_t
@ -760,6 +772,9 @@ struct BaseCtx
mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
mutable std::vector<const std::string *> *idstring_idx_to_str;
// Temporary string backing store for logging
mutable StrRingBuffer log_strs;
// Project settings and config switches
std::unordered_map<IdString, Property> settings;
@ -875,6 +890,9 @@ struct BaseCtx
const char *nameOfPip(PipId pip) const;
const char *nameOfGroup(GroupId group) const;
// Overrides of arch functions that take a string and handle IdStringList parsing
BelId getBelByNameStr(const std::string &str);
// --------------------------------------------------------------
bool allUiReload = true;

View File

@ -158,8 +158,7 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
all_placed = true;
}
if (ctx->verbose)
log_info(" placed single cell '%s' at '%s'\n", cell->name.c_str(ctx),
ctx->getBelName(best_bel).c_str(ctx));
log_info(" placed single cell '%s' at '%s'\n", cell->name.c_str(ctx), ctx->nameOfBel(best_bel));
ctx->bindBel(best_bel, cell, STRENGTH_WEAK);
cell = ripup_target;
@ -375,7 +374,7 @@ class ConstraintLegaliseWorker
if (confl_cell != nullptr) {
if (ctx->verbose)
log_info(" '%s' already placed at '%s'\n", ctx->nameOf(confl_cell),
ctx->getBelName(confl_cell->bel).c_str(ctx));
ctx->nameOfBel(confl_cell->bel));
NPNR_ASSERT(confl_cell->belStrength < STRENGTH_STRONG);
ctx->unbindBel(target);
rippedCells.insert(confl_cell->name);
@ -489,7 +488,7 @@ class ConstraintLegaliseWorker
for (auto cell : sorted(ctx->cells))
if (get_constraints_distance(ctx, cell.second) != 0)
log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx),
ctx->getBelName(cell.second->bel).c_str(ctx));
ctx->nameOfBel(cell.second->bel));
return score;
}
};

View File

@ -154,7 +154,7 @@ class SAPlacer
auto loc = cell->attrs.find(ctx->id("BEL"));
if (loc != cell->attrs.end()) {
std::string loc_name = loc->second.as_string();
BelId bel = ctx->getBelByName(ctx->id(loc_name));
BelId bel = ctx->getBelByNameStr(loc_name);
if (bel == BelId()) {
log_error("No Bel named \'%s\' located for "
"this chip (processing BEL attribute on \'%s\')\n",
@ -409,18 +409,18 @@ class SAPlacer
if (ctx->force) {
log_warning("post-placement validity check failed for Bel '%s' "
"(%s)\n",
ctx->getBelName(bel).c_str(ctx), cell_text.c_str());
ctx->nameOfBel(bel), cell_text.c_str());
} else {
log_error("post-placement validity check failed for Bel '%s' "
"(%s)\n",
ctx->getBelName(bel).c_str(ctx), cell_text.c_str());
ctx->nameOfBel(bel), cell_text.c_str());
}
}
}
for (auto cell : sorted(ctx->cells))
if (get_constraints_distance(ctx, cell.second) != 0)
log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx),
ctx->getBelName(cell.second->bel).c_str(ctx));
ctx->nameOfBel(cell.second->bel));
timing_analysis(ctx);
ctx->unlock();
return true;

View File

@ -305,7 +305,7 @@ class HeAPPlacer
if (ctx->getBoundBelCell(cell.second->bel) != cell.second)
log_error("Found cell %s with mismatched binding\n", cell.first.c_str(ctx));
if (ctx->debug)
log_info("AP soln: %s -> %s\n", cell.first.c_str(ctx), ctx->getBelName(cell.second->bel).c_str(ctx));
log_info("AP soln: %s -> %s\n", cell.first.c_str(ctx), ctx->nameOfBel(cell.second->bel));
}
ctx->unlock();
@ -379,7 +379,7 @@ class HeAPPlacer
auto loc = cell->attrs.find(ctx->id("BEL"));
if (loc != cell->attrs.end()) {
std::string loc_name = loc->second.as_string();
BelId bel = ctx->getBelByName(ctx->id(loc_name));
BelId bel = ctx->getBelByNameStr(loc_name);
if (bel == BelId()) {
log_error("No Bel named \'%s\' located for "
"this chip (processing BEL attribute on \'%s\')\n",

View File

@ -427,7 +427,7 @@ class TimingOptimiser
if (pn->users.at(i).cell == port->cell && pn->users.at(i).port == port->port)
crit = net_crit.at(pn->name).criticality.at(i);
log_info(" %s.%s at %s crit %0.02f\n", port->cell->name.c_str(ctx), port->port.c_str(ctx),
ctx->getBelName(port->cell->bel).c_str(ctx), crit);
ctx->nameOfBel(port->cell->bel), crit);
}
if (std::find(path_cells.begin(), path_cells.end(), port->cell->name) != path_cells.end())
continue;
@ -472,10 +472,9 @@ class TimingOptimiser
if (ctx->debug) {
for (auto cell : path_cells) {
log_info("Candidate neighbours for %s (%s):\n", cell.c_str(ctx),
ctx->getBelName(ctx->cells[cell]->bel).c_str(ctx));
log_info("Candidate neighbours for %s (%s):\n", cell.c_str(ctx), ctx->nameOfBel(ctx->cells[cell]->bel));
for (auto neigh : cell_neighbour_bels.at(cell)) {
log_info(" %s\n", ctx->getBelName(neigh).c_str(ctx));
log_info(" %s\n", ctx->nameOfBel(neigh));
}
}
}
@ -597,7 +596,7 @@ class TimingOptimiser
CellInfo *cell = ctx->cells.at(rt_entry.first).get();
cell_swap_bel(cell, rt_entry.second);
if (ctx->debug)
log_info(" %s at %s\n", rt_entry.first.c_str(ctx), ctx->getBelName(rt_entry.second).c_str(ctx));
log_info(" %s at %s\n", rt_entry.first.c_str(ctx), ctx->nameOfBel(rt_entry.second));
}
} else {

View File

@ -128,6 +128,11 @@ Arch::Arch(ArchArgs args) : args(args)
bucket.name = bel_type;
buckets.push_back(bucket);
}
for (int i = 0; i < chip_info->width; i++)
x_ids.push_back(id(stringf("X%d", i)));
for (int i = 0; i < chip_info->height; i++)
y_ids.push_back(id(stringf("Y%d", i)));
}
// -----------------------------------------------------------------------
@ -208,16 +213,18 @@ IdString Arch::archArgsToId(ArchArgs args) const
// -----------------------------------------------------------------------
BelId Arch::getBelByName(IdString name) const
BelId Arch::getBelByName(IdStringList name) const
{
// TODO: take advantage of IdStringList for fast parsing
BelId ret;
#if 0
auto it = bel_by_name.find(name);
if (it != bel_by_name.end())
return it->second;
#endif
Location loc;
std::string basename;
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this));
std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(getCtx()));
ret.location = loc;
const LocationTypePOD *loci = locInfo(ret);
for (int i = 0; i < int(loci->bel_data.size()); i++) {
@ -226,8 +233,10 @@ BelId Arch::getBelByName(IdString name) const
break;
}
}
#if 0
if (ret.index >= 0)
bel_by_name[name] = ret;
#endif
return ret;
}

View File

@ -450,6 +450,9 @@ struct Arch : BaseCtx
std::unordered_map<PipId, NetInfo *> pip_to_net;
std::unordered_map<WireId, int> wire_fanout;
// fast access to X and Y IdStrings for building object names
std::vector<IdString> x_ids, y_ids;
ArchArgs args;
Arch(ArchArgs args);
@ -475,19 +478,19 @@ struct Arch : BaseCtx
// -------------------------------------------------
BelId getBelByName(IdString name) const;
BelId getBelByName(IdStringList name) const;
template <typename Id> const LocationTypePOD *locInfo(Id &id) const
{
return &(chip_info->locations[chip_info->location_type[id.location.y * chip_info->width + id.location.x]]);
}
IdString getBelName(BelId bel) const
IdStringList getBelName(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
std::stringstream name;
name << "X" << bel.location.x << "/Y" << bel.location.y << "/" << locInfo(bel)->bel_data[bel.index].name.get();
return id(name.str());
std::array<IdString, 3> ids{x_ids.at(bel.location.x), y_ids.at(bel.location.y),
id(locInfo(bel)->bel_data[bel.index].name.get())};
return IdStringList(ids);
}
uint32_t getBelChecksum(BelId bel) const { return bel.index; }

View File

@ -30,7 +30,7 @@ namespace PythonConversion {
template <> struct string_converter<BelId>
{
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByNameStr(name); }
std::string to_str(Context *ctx, BelId id)
{

View File

@ -176,8 +176,8 @@ class Ecp5GlobalRouter
}
}
if (upstream.size() > 30000) {
log_error("failed to route HPBX%02d00 to %s.%s\n", global_index,
ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx));
log_error("failed to route HPBX%02d00 to %s.%s\n", global_index, ctx->nameOfBel(user.cell->bel),
user.port.c_str(ctx));
}
}
// Set all the pips we found along the way
@ -300,7 +300,7 @@ class Ecp5GlobalRouter
if (drv.cell == nullptr) {
return 0;
} else if (drv.cell->attrs.count(ctx->id("BEL"))) {
drv_bel = ctx->getBelByName(ctx->id(drv.cell->attrs.at(ctx->id("BEL")).as_string()));
drv_bel = ctx->getBelByNameStr(drv.cell->attrs.at(ctx->id("BEL")).as_string());
} else {
// Check if driver is a singleton
BelId last_bel;

View File

@ -522,7 +522,7 @@ class Ecp5Packer
trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
} else {
log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
ctx->getBelName(pinBel).c_str(ctx));
ctx->nameOfBel(pinBel));
}
trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
}
@ -1657,7 +1657,7 @@ class Ecp5Packer
CellInfo *dcu = clki->driver.cell;
if (!dcu->attrs.count(ctx->id("BEL")))
log_error("DCU must be constrained to a Bel!\n");
BelId bel = ctx->getBelByName(ctx->id(dcu->attrs.at(ctx->id("BEL")).as_string()));
BelId bel = ctx->getBelByNameStr(dcu->attrs.at(ctx->id("BEL")).as_string());
if (bel == BelId())
log_error("Invalid DCU bel '%s'\n", dcu->attrs.at(ctx->id("BEL")).c_str());
Loc loc = ctx->getBelLocation(bel);
@ -1704,7 +1704,7 @@ class Ecp5Packer
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == id_EHXPLLL && ci->attrs.count(ctx->id("BEL")))
available_plls.erase(ctx->getBelByName(ctx->id(ci->attrs.at(ctx->id("BEL")).as_string())));
available_plls.erase(ctx->getBelByNameStr(ci->attrs.at(ctx->id("BEL")).as_string()));
}
// Place PLL connected to fixed drivers such as IO close to their source
for (auto cell : sorted(ctx->cells)) {
@ -1716,7 +1716,7 @@ class Ecp5Packer
const CellInfo *drivercell = drivernet->driver.cell;
if (!drivercell->attrs.count(ctx->id("BEL")))
continue;
BelId drvbel = ctx->getBelByName(ctx->id(drivercell->attrs.at(ctx->id("BEL")).as_string()));
BelId drvbel = ctx->getBelByNameStr(drivercell->attrs.at(ctx->id("BEL")).as_string());
Loc drvloc = ctx->getBelLocation(drvbel);
BelId closest_pll;
int closest_distance = std::numeric_limits<int>::max();
@ -1848,8 +1848,8 @@ class Ecp5Packer
WireId next;
while (true) {
if (upstream.empty() || upstream.size() > 30000)
log_error("failed to route bank %d ECLK%d to %s.%s\n", bank, found_eclk,
ctx->getBelName(usr_bel).c_str(ctx), usr_port.name.c_str(ctx));
log_error("failed to route bank %d ECLK%d to %s.%s\n", bank, found_eclk, ctx->nameOfBel(usr_bel),
usr_port.name.c_str(ctx));
next = upstream.front();
upstream.pop();
if (ctx->debug)
@ -1913,17 +1913,17 @@ class Ecp5Packer
log_error("DQSBUFM can only be used with a pin-constrained PIO connected to its DQSI input"
"(while processing '%s').\n",
ci->name.c_str(ctx));
BelId pio_bel = ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")).as_string()));
BelId pio_bel = ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string());
NPNR_ASSERT(pio_bel != BelId());
Loc pio_loc = ctx->getBelLocation(pio_bel);
if (pio_loc.z != 0)
log_error("PIO '%s' does not appear to be a DQS site (expecting an 'A' pin).\n",
ctx->getBelName(pio_bel).c_str(ctx));
ctx->nameOfBel(pio_bel));
pio_loc.z = 8;
BelId dqsbuf = ctx->getBelByLocation(pio_loc);
if (dqsbuf == BelId() || ctx->getBelType(dqsbuf) != id_DQSBUFM)
log_error("PIO '%s' does not appear to be a DQS site (didn't find a DQSBUFM).\n",
ctx->getBelName(pio_bel).c_str(ctx));
ctx->nameOfBel(pio_bel));
ci->attrs[ctx->id("BEL")] = ctx->getBelName(dqsbuf).str(ctx);
bool got_dqsg = ctx->getPIODQSGroup(pio_bel, dqsbuf_dqsg[ci->name].first, dqsbuf_dqsg[ci->name].second);
NPNR_ASSERT(got_dqsg);
@ -2078,15 +2078,14 @@ class Ecp5Packer
log_error("IOLOGIC functionality (DDR, DELAY, DQS, etc) can only be used with pin-constrained PIO "
"(while processing '%s').\n",
curr->name.c_str(ctx));
BelId bel = ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")).as_string()));
BelId bel = ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string());
NPNR_ASSERT(bel != BelId());
return bel;
};
auto create_pio_iologic = [&](CellInfo *pio, CellInfo *curr) {
BelId bel = get_pio_bel(pio, curr);
log_info("IOLOGIC component %s connected to PIO Bel %s\n", curr->name.c_str(ctx),
ctx->getBelName(bel).c_str(ctx));
log_info("IOLOGIC component %s connected to PIO Bel %s\n", curr->name.c_str(ctx), ctx->nameOfBel(bel));
Loc loc = ctx->getBelLocation(bel);
bool s = false;
if (loc.y == 0 || loc.y == (ctx->chip_info->height - 1))
@ -2292,8 +2291,7 @@ class Ecp5Packer
replace_port(ci, ctx->id("D2"), iol, id_TXDATA2);
replace_port(ci, ctx->id("D3"), iol, id_TXDATA3);
if (ci->type == ctx->id("ODDR71B")) {
Loc loc =
ctx->getBelLocation(ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")).as_string())));
Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string()));
if (loc.z % 2 == 1)
log_error("ODDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx));
replace_port(ci, ctx->id("D4"), iol, id_TXDATA4);
@ -2326,8 +2324,7 @@ class Ecp5Packer
replace_port(ci, ctx->id("Q2"), iol, id_RXDATA2);
replace_port(ci, ctx->id("Q3"), iol, id_RXDATA3);
if (ci->type == ctx->id("IDDR71B")) {
Loc loc =
ctx->getBelLocation(ctx->getBelByName(ctx->id(pio->attrs.at(ctx->id("BEL")).as_string())));
Loc loc = ctx->getBelLocation(ctx->getBelByNameStr(pio->attrs.at(ctx->id("BEL")).as_string()));
if (loc.z % 2 == 1)
log_error("IDDR71B '%s' can only be used at 'A' or 'C' locations\n", ci->name.c_str(ctx));
replace_port(ci, ctx->id("Q4"), iol, id_RXDATA4);
@ -2579,7 +2576,7 @@ class Ecp5Packer
if (!user.cell->attrs.count(ctx->id("BEL")))
continue;
Loc user_loc = ctx->getBelLocation(
ctx->getBelByName(ctx->id(user.cell->attrs.at(ctx->id("BEL")).as_string())));
ctx->getBelByNameStr(user.cell->attrs.at(ctx->id("BEL")).as_string()));
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ECLKBRIDGECS)
continue;
@ -2594,8 +2591,8 @@ class Ecp5Packer
CellInfo *drv = input->driver.cell;
if (!drv->attrs.count(ctx->id("BEL")))
continue;
Loc drv_loc = ctx->getBelLocation(
ctx->getBelByName(ctx->id(drv->attrs.at(ctx->id("BEL")).as_string())));
Loc drv_loc =
ctx->getBelLocation(ctx->getBelByNameStr(drv->attrs.at(ctx->id("BEL")).as_string()));
BelId closest;
int closest_x = -1; // aim for same side of chip
for (auto bel : ctx->getBels()) {
@ -2639,7 +2636,7 @@ class Ecp5Packer
if (ci->type == id_IOLOGIC || ci->type == id_DQSBUFM) {
if (!ci->ports.count(id_ECLK) || ci->ports.at(id_ECLK).net == nullptr)
continue;
BelId bel = ctx->getBelByName(ctx->id(str_or_default(ci->attrs, ctx->id("BEL"))));
BelId bel = ctx->getBelByNameStr(str_or_default(ci->attrs, ctx->id("BEL")));
NPNR_ASSERT(bel != BelId());
Loc pioLoc = ctx->getBelLocation(bel);
if (ci->type == id_DQSBUFM)
@ -2683,8 +2680,7 @@ class Ecp5Packer
const NetInfo *eclko = net_or_nullptr(ci, id_ECLKO);
if (eclki != nullptr && eclki->driver.cell != nullptr) {
if (eclki->driver.cell->type == id_ECLKBRIDGECS) {
BelId bel =
ctx->getBelByName(ctx->id(eclki->driver.cell->attrs.at(ctx->id("BEL")).as_string()));
BelId bel = ctx->getBelByNameStr(eclki->driver.cell->attrs.at(ctx->id("BEL")).as_string());
Loc loc = ctx->getBelLocation(bel);
ci->attrs[ctx->id("BEL")] =
ctx->getBelName(ctx->getBelByLocation(Loc(loc.x, loc.y, 15))).str(ctx);
@ -2697,7 +2693,7 @@ class Ecp5Packer
for (auto user : eclko->users) {
if (user.cell->type == id_TRELLIS_ECLKBUF) {
Loc eckbuf_loc = ctx->getBelLocation(
ctx->getBelByName(ctx->id(user.cell->attrs.at(ctx->id("BEL")).as_string())));
ctx->getBelByNameStr(user.cell->attrs.at(ctx->id("BEL")).as_string()));
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ECLKSYNCB)
continue;
@ -2726,7 +2722,7 @@ class Ecp5Packer
const CellInfo *uc = usr.cell;
if (uc->type != id_DQSBUFM || !uc->attrs.count(ctx->id("BEL")))
continue;
BelId dqsb_bel = ctx->getBelByName(ctx->id(uc->attrs.at(ctx->id("BEL")).as_string()));
BelId dqsb_bel = ctx->getBelByNameStr(uc->attrs.at(ctx->id("BEL")).as_string());
Loc dqsb_loc = ctx->getBelLocation(dqsb_bel);
if (dqsb_loc.x > 15)
right_bank_users = true;
@ -2809,7 +2805,7 @@ class Ecp5Packer
CellInfo *drv = clki->driver.cell;
if (drv->type != id_ECLKSYNCB || !drv->attrs.count(ctx->id("BEL")))
continue;
BelId bel = ctx->getBelByName(ctx->id(drv->attrs.at(ctx->id("BEL")).as_string()));
BelId bel = ctx->getBelByNameStr(drv->attrs.at(ctx->id("BEL")).as_string());
// Find a CLKDIVF that is routeable from the ECLKSYNC
std::queue<WireId> visit;
visit.push(ctx->getBelPinWire(bel, id_ECLKO));