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"); log_info("Checking bel names..\n");
for (BelId bel : ctx->getBels()) { for (BelId bel : ctx->getBels()) {
IdString name = ctx->getBelName(bel); IdStringList name = ctx->getBelName(bel);
BelId bel2 = ctx->getBelByName(name); BelId bel2 = ctx->getBelByName(name);
if (bel != bel2) { 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; return id;
} }
std::string &StrRingBuffer::next()
{
std::string &s = buffer.at(index++);
if (index >= N)
index = 0;
return s;
}
TimingConstrObjectId BaseCtx::timingClockDomainObject(NetInfo *clockDomain) TimingConstrObjectId BaseCtx::timingClockDomainObject(NetInfo *clockDomain)
{ {
NPNR_ASSERT(clockDomain->clkconstr != nullptr); NPNR_ASSERT(clockDomain->clkconstr != nullptr);
@ -283,7 +291,9 @@ void BaseCtx::removeConstraint(IdString constrName)
const char *BaseCtx::nameOfBel(BelId bel) const const char *BaseCtx::nameOfBel(BelId bel) const
{ {
const Context *ctx = getCtx(); 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 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); 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 WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
{ {
if (net_info->driver.cell == nullptr) if (net_info->driver.cell == nullptr)
@ -655,7 +671,7 @@ void BaseCtx::archInfoToAttributes()
if (ci->attrs.find(id("BEL")) != ci->attrs.end()) { if (ci->attrs.find(id("BEL")) != ci->attrs.end()) {
ci->attrs.erase(ci->attrs.find(id("BEL"))); 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; ci->attrs[id("BEL_STRENGTH")] = (int)ci->belStrength;
} }
if (ci->constr_x != ci->UNCONSTR) if (ci->constr_x != ci->UNCONSTR)
@ -707,7 +723,7 @@ void BaseCtx::attributesToArchInfo()
if (str != ci->attrs.end()) if (str != ci->attrs.end())
strength = (PlaceStrength)str->second.as_int64(); 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); 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 // 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 template <typename T, size_t N> class SSOArray
{ {
private:
union union
{ {
T data_static[N]; T data_static[N];
T *data_heap; T *data_heap;
}; };
size_t m_size; size_t m_size;
private:
inline bool is_heap() const { return (m_size > N); } inline bool is_heap() const { return (m_size > N); }
void alloc() void alloc()
{ {
@ -224,8 +223,8 @@ struct IdStringList
SSOArray<IdString, 4> ids; SSOArray<IdString, 4> ids;
IdStringList(){}; IdStringList(){};
explicit IdStringList(size_t n) : ids(n, IdString()){}; IdStringList(size_t n) : ids(n, IdString()){};
explicit IdStringList(IdString id) : ids(1, id){}; IdStringList(IdString id) : ids(1, id){};
template <typename Tlist> IdStringList(const Tlist &list) : ids(list){}; template <typename Tlist> IdStringList(const Tlist &list) : ids(list){};
static IdStringList parse(Context *ctx, const std::string &str); 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]; } 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 struct GraphicElement
{ {
enum type_t enum type_t
@ -760,6 +772,9 @@ struct BaseCtx
mutable std::unordered_map<std::string, int> *idstring_str_to_idx; mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
mutable std::vector<const std::string *> *idstring_idx_to_str; 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 // Project settings and config switches
std::unordered_map<IdString, Property> settings; std::unordered_map<IdString, Property> settings;
@ -875,6 +890,9 @@ struct BaseCtx
const char *nameOfPip(PipId pip) const; const char *nameOfPip(PipId pip) const;
const char *nameOfGroup(GroupId group) 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; bool allUiReload = true;

View File

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

View File

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

View File

@ -305,7 +305,7 @@ class HeAPPlacer
if (ctx->getBoundBelCell(cell.second->bel) != cell.second) if (ctx->getBoundBelCell(cell.second->bel) != cell.second)
log_error("Found cell %s with mismatched binding\n", cell.first.c_str(ctx)); log_error("Found cell %s with mismatched binding\n", cell.first.c_str(ctx));
if (ctx->debug) 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(); ctx->unlock();
@ -379,7 +379,7 @@ class HeAPPlacer
auto loc = cell->attrs.find(ctx->id("BEL")); auto loc = cell->attrs.find(ctx->id("BEL"));
if (loc != cell->attrs.end()) { if (loc != cell->attrs.end()) {
std::string loc_name = loc->second.as_string(); 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()) { if (bel == BelId()) {
log_error("No Bel named \'%s\' located for " log_error("No Bel named \'%s\' located for "
"this chip (processing BEL attribute on \'%s\')\n", "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) if (pn->users.at(i).cell == port->cell && pn->users.at(i).port == port->port)
crit = net_crit.at(pn->name).criticality.at(i); 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), 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()) if (std::find(path_cells.begin(), path_cells.end(), port->cell->name) != path_cells.end())
continue; continue;
@ -472,10 +472,9 @@ class TimingOptimiser
if (ctx->debug) { if (ctx->debug) {
for (auto cell : path_cells) { for (auto cell : path_cells) {
log_info("Candidate neighbours for %s (%s):\n", cell.c_str(ctx), log_info("Candidate neighbours for %s (%s):\n", cell.c_str(ctx), ctx->nameOfBel(ctx->cells[cell]->bel));
ctx->getBelName(ctx->cells[cell]->bel).c_str(ctx));
for (auto neigh : cell_neighbour_bels.at(cell)) { 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(); CellInfo *cell = ctx->cells.at(rt_entry.first).get();
cell_swap_bel(cell, rt_entry.second); cell_swap_bel(cell, rt_entry.second);
if (ctx->debug) 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 { } else {

View File

@ -128,6 +128,11 @@ Arch::Arch(ArchArgs args) : args(args)
bucket.name = bel_type; bucket.name = bel_type;
buckets.push_back(bucket); 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; BelId ret;
#if 0
auto it = bel_by_name.find(name); auto it = bel_by_name.find(name);
if (it != bel_by_name.end()) if (it != bel_by_name.end())
return it->second; return it->second;
#endif
Location loc; Location loc;
std::string basename; 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; ret.location = loc;
const LocationTypePOD *loci = locInfo(ret); const LocationTypePOD *loci = locInfo(ret);
for (int i = 0; i < int(loci->bel_data.size()); i++) { for (int i = 0; i < int(loci->bel_data.size()); i++) {
@ -226,8 +233,10 @@ BelId Arch::getBelByName(IdString name) const
break; break;
} }
} }
#if 0
if (ret.index >= 0) if (ret.index >= 0)
bel_by_name[name] = ret; bel_by_name[name] = ret;
#endif
return ret; return ret;
} }

View File

@ -450,6 +450,9 @@ struct Arch : BaseCtx
std::unordered_map<PipId, NetInfo *> pip_to_net; std::unordered_map<PipId, NetInfo *> pip_to_net;
std::unordered_map<WireId, int> wire_fanout; 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; ArchArgs args;
Arch(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 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]]); 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()); NPNR_ASSERT(bel != BelId());
std::stringstream name; std::array<IdString, 3> ids{x_ids.at(bel.location.x), y_ids.at(bel.location.y),
name << "X" << bel.location.x << "/Y" << bel.location.y << "/" << locInfo(bel)->bel_data[bel.index].name.get(); id(locInfo(bel)->bel_data[bel.index].name.get())};
return id(name.str()); return IdStringList(ids);
} }
uint32_t getBelChecksum(BelId bel) const { return bel.index; } uint32_t getBelChecksum(BelId bel) const { return bel.index; }

View File

@ -30,7 +30,7 @@ namespace PythonConversion {
template <> struct string_converter<BelId> 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) std::string to_str(Context *ctx, BelId id)
{ {

View File

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

View File

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