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:
parent
0dbe7f96a3
commit
6d23461bcd
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
15
ecp5/arch.cc
15
ecp5/arch.cc
@ -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;
|
||||
}
|
||||
|
||||
|
13
ecp5/arch.h
13
ecp5/arch.h
@ -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; }
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
46
ecp5/pack.cc
46
ecp5/pack.cc
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user