1
0
mirror of https://github.com/YosysHQ/nextpnr.git synced 2026-05-04 15:15:34 +00:00

Update placers to use new cluster APIs

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
gatecat
2021-04-28 15:43:02 +01:00
parent 6a3eacddd6
commit 14863bc04e
10 changed files with 150 additions and 345 deletions

View File

@@ -142,8 +142,8 @@ template <typename R> struct ArchAPI : BaseCtx
// Cluster methods
virtual CellInfo *getClusterRootCell(ClusterId cluster) const = 0;
virtual ArcBounds getClusterBounds(ClusterId cluster) const = 0;
virtual Loc getClusterOffset(CellInfo *cell) const = 0;
virtual bool isClusterStrict(CellInfo *cell) const = 0;
virtual Loc getClusterOffset(const CellInfo *cell) const = 0;
virtual bool isClusterStrict(const CellInfo *cell) const = 0;
virtual bool getClusterPlacement(ClusterId cluster, BelId root_bel,
std::vector<std::pair<CellInfo *, BelId>> &placement) const = 0;
// Flow methods

View File

@@ -393,13 +393,13 @@ template <typename R> struct BaseArch : ArchAPI<R>
});
}
virtual Loc getClusterOffset(CellInfo *cell) const override
virtual Loc getClusterOffset(const CellInfo *cell) const override
{
return if_using_basecluster<Loc>(cell,
[](const BaseClusterInfo *c) { return Loc(c->constr_x, c->constr_y, 0); });
}
virtual bool isClusterStrict(CellInfo *cell) const override { return true; }
virtual bool isClusterStrict(const CellInfo *cell) const override { return true; }
virtual bool getClusterPlacement(ClusterId cluster, BelId root_bel,
std::vector<std::pair<CellInfo *, BelId>> &placement) const override

View File

@@ -152,25 +152,6 @@ void BaseCtx::archInfoToAttributes()
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)
ci->attrs[id("CONSTR_X")] = ci->constr_x;
if (ci->constr_y != ci->UNCONSTR)
ci->attrs[id("CONSTR_Y")] = ci->constr_y;
if (ci->constr_z != ci->UNCONSTR) {
ci->attrs[id("CONSTR_Z")] = ci->constr_z;
ci->attrs[id("CONSTR_ABS_Z")] = ci->constr_abs_z ? 1 : 0;
}
if (ci->constr_parent != nullptr)
ci->attrs[id("CONSTR_PARENT")] = ci->constr_parent->name.str(this);
if (!ci->constr_children.empty()) {
std::string constr = "";
for (auto &item : ci->constr_children) {
if (!constr.empty())
constr += std::string(";");
constr += item->name.c_str(this);
}
ci->attrs[id("CONSTR_CHILDREN")] = constr;
}
}
for (auto &net : getCtx()->nets) {
auto ni = net.second.get();
@@ -204,48 +185,6 @@ void BaseCtx::attributesToArchInfo()
BelId b = getCtx()->getBelByNameStr(val->second.as_string());
getCtx()->bindBel(b, ci, strength);
}
val = ci->attrs.find(id("CONSTR_PARENT"));
if (val != ci->attrs.end()) {
auto parent = cells.find(id(val->second.str));
if (parent != cells.end())
ci->constr_parent = parent->second.get();
else
continue;
}
val = ci->attrs.find(id("CONSTR_X"));
if (val != ci->attrs.end())
ci->constr_x = val->second.as_int64();
val = ci->attrs.find(id("CONSTR_Y"));
if (val != ci->attrs.end())
ci->constr_y = val->second.as_int64();
val = ci->attrs.find(id("CONSTR_Z"));
if (val != ci->attrs.end())
ci->constr_z = val->second.as_int64();
val = ci->attrs.find(id("CONSTR_ABS_Z"));
if (val != ci->attrs.end())
ci->constr_abs_z = val->second.as_int64() == 1;
val = ci->attrs.find(id("CONSTR_PARENT"));
if (val != ci->attrs.end()) {
auto parent = cells.find(id(val->second.as_string()));
if (parent != cells.end())
ci->constr_parent = parent->second.get();
}
val = ci->attrs.find(id("CONSTR_CHILDREN"));
if (val != ci->attrs.end()) {
std::vector<std::string> strs;
auto children = val->second.as_string();
boost::split(strs, children, boost::is_any_of(";"));
for (auto val : strs) {
if (cells.count(id(val.c_str())))
ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get());
}
}
}
for (auto &net : getCtx()->nets) {
auto ni = net.second.get();

View File

@@ -44,26 +44,9 @@ void CellInfo::unsetParam(IdString name) { params.erase(name); }
void CellInfo::setAttr(IdString name, Property value) { attrs[name] = value; }
void CellInfo::unsetAttr(IdString name) { attrs.erase(name); }
bool CellInfo::isConstrained(bool include_abs_z_constr) const
{
return constr_parent != nullptr || !constr_children.empty() || (include_abs_z_constr && constr_abs_z);
}
bool CellInfo::testRegion(BelId bel) const
{
return region == nullptr || !region->constr_bels || region->bels.count(bel);
}
Loc CellInfo::getConstrainedLoc(Loc parent_loc) const
{
NPNR_ASSERT(constr_parent != nullptr);
Loc cloc = parent_loc;
if (constr_x != UNCONSTR)
cloc.x += constr_x;
if (constr_y != UNCONSTR)
cloc.y += constr_y;
if (constr_z != UNCONSTR)
cloc.z = constr_abs_z ? constr_z : (parent_loc.z + constr_z);
return cloc;
}
NEXTPNR_NAMESPACE_END

View File

@@ -178,14 +178,8 @@ struct CellInfo : ArchCellInfo
void unsetParam(IdString name);
void setAttr(IdString name, Property value);
void unsetAttr(IdString name);
// return true if the cell has placement constraints (optionally excluding the case where the only case is an
// absolute z constraint)
bool isConstrained(bool include_abs_z_constr = true) const;
// check whether a bel complies with the cell's region constraint
bool testRegion(BelId bel) const;
// get the constrained location for this cell given a provisional location for its parent
Loc getConstrainedLoc(Loc parent_loc) const;
};
enum TimingPortClass

View File

@@ -179,6 +179,8 @@ class ConstraintLegaliseWorker
Context *ctx;
std::set<IdString> rippedCells;
std::unordered_map<IdString, Loc> oldLocations;
std::unordered_map<ClusterId, std::vector<CellInfo *>> cluster2cells;
class IncreasingDiameterSearch
{
public:
@@ -228,83 +230,52 @@ class ConstraintLegaliseWorker
typedef std::unordered_map<IdString, Loc> CellLocations;
// Check if a location would be suitable for a cell and all its constrained children
// This also makes a crude attempt to "solve" unconstrained constraints, that is slow and horrible
// and will need to be reworked if mixed constrained/unconstrained chains become common
bool valid_loc_for(const CellInfo *cell, Loc loc, CellLocations &solution, std::unordered_set<Loc> &usedLocations)
{
BelId locBel = ctx->getBelByLocation(loc);
if (locBel == BelId()) {
if (locBel == BelId())
return false;
}
if (!ctx->isValidBelForCellType(cell->type, locBel)) {
return false;
}
if (!ctx->checkBelAvail(locBel)) {
CellInfo *confCell = ctx->getConflictingBelCell(locBel);
if (confCell->belStrength >= STRENGTH_STRONG) {
if (cell->cluster == ClusterId()) {
if (!ctx->isValidBelForCellType(cell->type, locBel))
return false;
}
}
// Don't place at tiles where any strongly bound Bels exist, as we might need to rip them up later
for (auto tilebel : ctx->getBelsByTile(loc.x, loc.y)) {
CellInfo *tcell = ctx->getBoundBelCell(tilebel);
if (tcell && tcell->belStrength >= STRENGTH_STRONG)
return false;
}
usedLocations.insert(loc);
for (auto child : cell->constr_children) {
IncreasingDiameterSearch xSearch, ySearch, zSearch;
if (child->constr_x == child->UNCONSTR) {
xSearch = IncreasingDiameterSearch(loc.x, 0, ctx->getGridDimX() - 1);
} else {
xSearch = IncreasingDiameterSearch(loc.x + child->constr_x);
}
if (child->constr_y == child->UNCONSTR) {
ySearch = IncreasingDiameterSearch(loc.y, 0, ctx->getGridDimY() - 1);
} else {
ySearch = IncreasingDiameterSearch(loc.y + child->constr_y);
}
if (child->constr_z == child->UNCONSTR) {
zSearch = IncreasingDiameterSearch(loc.z, 0, ctx->getTileBelDimZ(loc.x, loc.y));
} else {
if (child->constr_abs_z) {
zSearch = IncreasingDiameterSearch(child->constr_z);
} else {
zSearch = IncreasingDiameterSearch(loc.z + child->constr_z);
if (!ctx->checkBelAvail(locBel)) {
CellInfo *confCell = ctx->getConflictingBelCell(locBel);
if (confCell->belStrength >= STRENGTH_STRONG) {
return false;
}
}
bool success = false;
while (!xSearch.done()) {
Loc cloc;
cloc.x = xSearch.get();
cloc.y = ySearch.get();
cloc.z = zSearch.get();
zSearch.next();
if (zSearch.done()) {
zSearch.reset();
ySearch.next();
if (ySearch.done()) {
ySearch.reset();
xSearch.next();
// Don't place at tiles where any strongly bound Bels exist, as we might need to rip them up later
for (auto tilebel : ctx->getBelsByTile(loc.x, loc.y)) {
CellInfo *tcell = ctx->getBoundBelCell(tilebel);
if (tcell && tcell->belStrength >= STRENGTH_STRONG)
return false;
}
usedLocations.insert(loc);
solution[cell->name] = loc;
} else {
std::vector<std::pair<CellInfo *, BelId>> placement;
if (!ctx->getClusterPlacement(cell->cluster, locBel, placement))
return false;
for (auto &p : placement) {
Loc p_loc = ctx->getBelLocation(p.second);
if (!ctx->checkBelAvail(p.second)) {
CellInfo *confCell = ctx->getConflictingBelCell(p.second);
if (confCell->belStrength >= STRENGTH_STRONG) {
return false;
}
}
if (usedLocations.count(cloc))
continue;
if (valid_loc_for(child, cloc, solution, usedLocations)) {
success = true;
break;
// Don't place at tiles where any strongly bound Bels exist, as we might need to rip them up later
for (auto tilebel : ctx->getBelsByTile(p_loc.x, p_loc.y)) {
CellInfo *tcell = ctx->getBoundBelCell(tilebel);
if (tcell && tcell->belStrength >= STRENGTH_STRONG)
return false;
}
}
if (!success) {
usedLocations.erase(loc);
return false;
usedLocations.insert(p_loc);
solution[p.first->name] = p_loc;
}
}
if (solution.count(cell->name))
usedLocations.erase(solution.at(cell->name));
solution[cell->name] = loc;
return true;
}
@@ -312,18 +283,18 @@ class ConstraintLegaliseWorker
void lockdown_chain(CellInfo *root)
{
root->belStrength = STRENGTH_STRONG;
for (auto child : root->constr_children)
lockdown_chain(child);
if (root->cluster != ClusterId())
for (auto child : cluster2cells.at(root->cluster))
child->belStrength = STRENGTH_STRONG;
}
// Legalise placement constraints on a cell
bool legalise_cell(CellInfo *cell)
{
if (cell->constr_parent != nullptr)
if (cell->cluster != ClusterId() && ctx->getClusterRootCell(cell->cluster) != cell)
return true; // Only process chain roots
if (constraints_satisfied(cell)) {
if (cell->constr_children.size() > 0 || cell->constr_x != cell->UNCONSTR ||
cell->constr_y != cell->UNCONSTR || cell->constr_z != cell->UNCONSTR)
if (cell->cluster != ClusterId())
lockdown_chain(cell);
} else {
IncreasingDiameterSearch xRootSearch, yRootSearch, zRootSearch;
@@ -332,21 +303,10 @@ class ConstraintLegaliseWorker
currentLoc = ctx->getBelLocation(cell->bel);
else
currentLoc = oldLocations[cell->name];
if (cell->constr_x == cell->UNCONSTR)
xRootSearch = IncreasingDiameterSearch(currentLoc.x, 0, ctx->getGridDimX() - 1);
else
xRootSearch = IncreasingDiameterSearch(cell->constr_x);
xRootSearch = IncreasingDiameterSearch(currentLoc.x, 0, ctx->getGridDimX() - 1);
yRootSearch = IncreasingDiameterSearch(currentLoc.y, 0, ctx->getGridDimY() - 1);
zRootSearch = IncreasingDiameterSearch(currentLoc.z, 0, ctx->getTileBelDimZ(currentLoc.x, currentLoc.y));
if (cell->constr_y == cell->UNCONSTR)
yRootSearch = IncreasingDiameterSearch(currentLoc.y, 0, ctx->getGridDimY() - 1);
else
yRootSearch = IncreasingDiameterSearch(cell->constr_y);
if (cell->constr_z == cell->UNCONSTR)
zRootSearch =
IncreasingDiameterSearch(currentLoc.z, 0, ctx->getTileBelDimZ(currentLoc.x, currentLoc.y));
else
zRootSearch = IncreasingDiameterSearch(cell->constr_z);
while (!xRootSearch.done()) {
Loc rootLoc;
@@ -415,29 +375,13 @@ class ConstraintLegaliseWorker
bool constraints_satisfied(const CellInfo *cell) { return get_constraints_distance(ctx, cell) == 0; }
public:
ConstraintLegaliseWorker(Context *ctx) : ctx(ctx){};
void print_chain(CellInfo *cell, int depth = 0)
ConstraintLegaliseWorker(Context *ctx) : ctx(ctx)
{
for (int i = 0; i < depth; i++)
log(" ");
log("'%s' (", cell->name.c_str(ctx));
if (cell->constr_x != cell->UNCONSTR)
log("%d, ", cell->constr_x);
else
log("*, ");
if (cell->constr_y != cell->UNCONSTR)
log("%d, ", cell->constr_y);
else
log("*, ");
if (cell->constr_z != cell->UNCONSTR)
log("%d", cell->constr_z);
else
log("*");
log(")\n");
for (auto child : cell->constr_children)
print_chain(child, depth + 1);
}
for (auto cell : sorted(ctx->cells)) {
if (cell.second->cluster != ClusterId())
cluster2cells[cell.second->cluster].push_back(cell.second);
}
};
unsigned print_stats(const char *point)
{
@@ -476,8 +420,6 @@ class ConstraintLegaliseWorker
for (auto cell : sorted(ctx->cells)) {
bool res = legalise_cell(cell.second);
if (!res) {
if (ctx->verbose)
print_chain(cell.second);
log_error("failed to place chain starting at cell '%s'\n", cell.first.c_str(ctx));
return -1;
}
@@ -509,30 +451,36 @@ int get_constraints_distance(const Context *ctx, const CellInfo *cell)
if (cell->bel == BelId())
return 100000;
Loc loc = ctx->getBelLocation(cell->bel);
if (cell->constr_parent == nullptr) {
if (cell->constr_x != cell->UNCONSTR)
dist += std::abs(cell->constr_x - loc.x);
if (cell->constr_y != cell->UNCONSTR)
dist += std::abs(cell->constr_y - loc.y);
if (cell->constr_z != cell->UNCONSTR)
dist += std::abs(cell->constr_z - loc.z);
} else {
if (cell->constr_parent->bel == BelId())
return 100000;
Loc parent_loc = ctx->getBelLocation(cell->constr_parent->bel);
if (cell->constr_x != cell->UNCONSTR)
dist += std::abs(cell->constr_x - (loc.x - parent_loc.x));
if (cell->constr_y != cell->UNCONSTR)
dist += std::abs(cell->constr_y - (loc.y - parent_loc.y));
if (cell->constr_z != cell->UNCONSTR) {
if (cell->constr_abs_z)
dist += std::abs(cell->constr_z - loc.z);
else
dist += std::abs(cell->constr_z - (loc.z - parent_loc.z));
if (cell->cluster != ClusterId()) {
CellInfo *root = ctx->getClusterRootCell(cell->cluster);
if (root == cell) {
// parent
std::vector<std::pair<CellInfo *, BelId>> placement;
if (!ctx->getClusterPlacement(cell->cluster, cell->bel, placement)) {
return 100000;
} else {
for (const auto &p : placement) {
if (p.first->bel == BelId())
return 100000;
Loc c_loc = ctx->getBelLocation(p.first->bel);
Loc p_loc = ctx->getBelLocation(p.second);
dist += std::abs(c_loc.x - p_loc.x);
dist += std::abs(c_loc.y - p_loc.y);
dist += std::abs(c_loc.z - p_loc.z);
}
}
} else {
// child
if (root->bel == BelId())
return 100000;
Loc root_loc = ctx->getBelLocation(root->bel);
Loc offset = ctx->getClusterOffset(cell);
dist += std::abs((root_loc.x + offset.x) - loc.x);
dist += std::abs((root_loc.y + offset.y) - loc.y);
}
}
for (auto child : cell->constr_children)
dist += get_constraints_distance(ctx, child);
return dist;
}

View File

@@ -225,14 +225,16 @@ class SAPlacer
} else {
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
if (ci->belStrength > STRENGTH_STRONG)
if (ci->belStrength > STRENGTH_STRONG) {
continue;
else if (ci->constr_parent != nullptr)
continue;
else if (!ci->constr_children.empty() || ci->constr_z != ci->UNCONSTR)
chain_basis.push_back(ci);
else
} else if (ci->cluster != ClusterId()) {
if (ctx->getClusterRootCell(ci->cluster) == ci)
chain_basis.push_back(ci);
else
continue;
} else {
autoplaced.push_back(ci);
}
}
require_legal = false;
diameter = 3;
@@ -359,8 +361,8 @@ class SAPlacer
autoplaced.clear();
chain_basis.clear();
for (auto cell : sorted(ctx->cells)) {
if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->constr_parent == nullptr &&
!cell.second->constr_children.empty())
if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() &&
ctx->getClusterRootCell(cell.second->cluster) == cell.second)
chain_basis.push_back(cell.second);
else if (cell.second->belStrength < STRENGTH_STRONG)
autoplaced.push_back(cell.second);
@@ -507,12 +509,12 @@ class SAPlacer
{
static const double epsilon = 1e-20;
moveChange.reset(this);
if (!require_legal && cell->isConstrained(false))
if (!require_legal && cell->cluster != ClusterId())
return false;
BelId oldBel = cell->bel;
CellInfo *other_cell = ctx->getBoundBelCell(newBel);
if (!require_legal && other_cell != nullptr &&
(other_cell->isConstrained(false) || other_cell->belStrength > STRENGTH_WEAK)) {
(other_cell->cluster != ClusterId() || other_cell->belStrength > STRENGTH_WEAK)) {
return false;
}
int old_dist = get_constraints_distance(ctx, cell);
@@ -612,9 +614,9 @@ class SAPlacer
if (bound != nullptr)
ctx->unbindBel(newBel);
ctx->unbindBel(oldBel);
ctx->bindBel(newBel, cell, cell->isConstrained(false) ? STRENGTH_STRONG : STRENGTH_WEAK);
ctx->bindBel(newBel, cell, (cell->cluster != ClusterId()) ? STRENGTH_STRONG : STRENGTH_WEAK);
if (bound != nullptr) {
ctx->bindBel(oldBel, bound, bound->isConstrained(false) ? STRENGTH_STRONG : STRENGTH_WEAK);
ctx->bindBel(oldBel, bound, (bound->cluster != ClusterId()) ? STRENGTH_STRONG : STRENGTH_WEAK);
if (cfg.netShareWeight > 0)
update_nets_by_tile(bound, ctx->getBelLocation(newBel), ctx->getBelLocation(oldBel));
}
@@ -623,16 +625,6 @@ class SAPlacer
return oldBel;
}
// Discover the relative positions of all cells in a chain
void discover_chain(Loc baseLoc, CellInfo *cell, std::vector<std::pair<CellInfo *, Loc>> &cell_rel)
{
Loc cellLoc = ctx->getBelLocation(cell->bel);
Loc rel{cellLoc.x - baseLoc.x, cellLoc.y - baseLoc.y, cellLoc.z};
cell_rel.emplace_back(std::make_pair(cell, rel));
for (auto child : cell->constr_children)
discover_chain(baseLoc, child, cell_rel);
}
// Attempt to swap a chain with a non-chain
bool try_swap_chain(CellInfo *cell, BelId newBase)
{
@@ -647,32 +639,23 @@ class SAPlacer
if (ctx->debug)
log_info("finding cells for chain swap %s\n", cell->name.c_str(ctx));
#endif
Loc baseLoc = ctx->getBelLocation(cell->bel);
discover_chain(baseLoc, cell, cell_rel);
Loc newBaseLoc = ctx->getBelLocation(newBase);
NPNR_ASSERT(newBaseLoc.z == baseLoc.z);
for (const auto &cr : cell_rel)
cells.insert(cr.first->name);
if (!ctx->getClusterPlacement(cell->cluster, newBase, dest_bels))
return false;
for (const auto &cr : cell_rel) {
Loc targetLoc = {newBaseLoc.x + cr.second.x, newBaseLoc.y + cr.second.y, cr.second.z};
BelId targetBel = ctx->getBelByLocation(targetLoc);
if (targetBel == BelId())
return false;
if (!ctx->isValidBelForCellType(cell->type, targetBel))
return false;
CellInfo *bound = ctx->getBoundBelCell(targetBel);
for (const auto &db : dest_bels)
cells.insert(db.first->name);
for (const auto &db : dest_bels) {
CellInfo *bound = ctx->getBoundBelCell(db.second);
// We don't consider swapping chains with other chains, at least for the time being - unless it is
// part of this chain
if (bound != nullptr && !cells.count(bound->name) &&
(bound->belStrength >= STRENGTH_STRONG || bound->isConstrained(false)))
(bound->belStrength >= STRENGTH_STRONG || bound->cluster != ClusterId()))
return false;
if (bound != nullptr)
if (!ctx->isValidBelForCellType(bound->type, cr.first->bel))
if (!ctx->isValidBelForCellType(bound->type, db.first->bel))
return false;
dest_bels.emplace_back(std::make_pair(cr.first, targetBel));
}
#if 0
if (ctx->debug)

View File

@@ -145,6 +145,10 @@ class HeAPPlacer
Eigen::initParallel();
tmg.setup_only = true;
tmg.setup();
for (auto cell : sorted(ctx->cells))
if (cell.second->cluster != ClusterId())
cluster2cells[cell.second->cluster].push_back(cell.second);
}
bool place()
@@ -386,14 +390,8 @@ class HeAPPlacer
// cells of a certain type)
std::vector<CellInfo *> solve_cells;
// For cells in a chain, this is the ultimate root cell of the chain (sometimes this is not constr_parent
// where chains are within chains
std::unordered_map<IdString, CellInfo *> chain_root;
std::unordered_map<IdString, int> chain_size;
// The offset from chain_root to a cell in the chain
std::unordered_map<IdString, std::pair<int, int>> cell_offsets;
std::unordered_map<ClusterId, std::vector<CellInfo *>> cluster2cells;
std::unordered_map<ClusterId, int> chain_size;
// Performance counting
double solve_time = 0, cl_time = 0, sl_time = 0;
@@ -549,7 +547,7 @@ class HeAPPlacer
cell_locs[cell.first].y = loc.y;
cell_locs[cell.first].locked = true;
cell_locs[cell.first].global = ctx->getBelGlobalBuf(ci->bel);
} else if (ci->constr_parent == nullptr) {
} else if (ci->cluster == ClusterId() || ctx->getClusterRootCell(ci->cluster) == ci) {
bool placed = false;
int attempt_count = 0;
while (!placed) {
@@ -629,40 +627,27 @@ class HeAPPlacer
solve_cells.push_back(cell);
}
// Finally, update the udata of children
for (auto chained : chain_root)
ctx->cells.at(chained.first)->udata = chained.second->udata;
for (auto &cluster : cluster2cells)
for (auto child : cluster.second)
child->udata = ctx->getClusterRootCell(cluster.first)->udata;
return row;
}
// Update the location of all children of a chain
void update_chain(CellInfo *cell, CellInfo *root)
{
const auto &base = cell_locs[cell->name];
for (auto child : cell->constr_children) {
// FIXME: Improve handling of heterogeneous chains
if (child->type == root->type)
chain_size[root->name]++;
if (child->constr_x != child->UNCONSTR)
cell_locs[child->name].x = std::max(0, std::min(max_x, base.x + child->constr_x));
else
cell_locs[child->name].x = base.x; // better handling of UNCONSTR?
if (child->constr_y != child->UNCONSTR)
cell_locs[child->name].y = std::max(0, std::min(max_y, base.y + child->constr_y));
else
cell_locs[child->name].y = base.y; // better handling of UNCONSTR?
chain_root[child->name] = root;
if (!child->constr_children.empty())
update_chain(child, root);
}
}
// Update all chains
void update_all_chains()
{
for (auto cell : place_cells) {
chain_size[cell->name] = 1;
if (!cell->constr_children.empty())
update_chain(cell, cell);
if (cell->cluster != ClusterId()) {
const auto &base = cell_locs[cell->name];
for (auto child : cluster2cells.at(cell->cluster)) {
if (child->type == cell->type && child != cell)
chain_size[cell->name]++;
Loc offset = ctx->getClusterOffset(child);
cell_locs[child->name].x = std::max(0, std::min(max_x, base.x + offset.x));
cell_locs[child->name].y = std::max(0, std::min(max_y, base.y + offset.y));
}
}
}
}
@@ -721,10 +706,9 @@ class HeAPPlacer
} else {
es.add_rhs(row, -v_pos * weight);
}
if (cell_offsets.count(var.cell->name)) {
es.add_rhs(row, -(yaxis ? cell_offsets.at(var.cell->name).second
: cell_offsets.at(var.cell->name).first) *
weight);
if (var.cell->cluster != ClusterId()) {
Loc offset = ctx->getClusterOffset(var.cell);
es.add_rhs(row, -(yaxis ? offset.y : offset.x) * weight);
}
};
@@ -827,8 +811,9 @@ class HeAPPlacer
// Unbind all cells placed in this solution
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->bel != BelId() && (ci->udata != dont_solve ||
(chain_root.count(ci->name) && chain_root.at(ci->name)->udata != dont_solve)))
if (ci->bel != BelId() &&
(ci->udata != dont_solve ||
(ci->cluster != ClusterId() && ctx->getClusterRootCell(ci->cluster)->udata != dont_solve)))
ctx->unbindBel(ci->bel);
}
@@ -955,7 +940,7 @@ class HeAPPlacer
break;
}
if (ci->constr_children.empty() && !ci->constr_abs_z) {
if (ci->cluster == ClusterId()) {
// The case where we have no relative constraints
for (auto sz : fb->at(nx).at(ny)) {
// Look through all bels in this tile; checking region constraint if applicable
@@ -967,7 +952,7 @@ class HeAPPlacer
CellInfo *bound = ctx->getBoundBelCell(sz);
if (bound != nullptr) {
// Only rip up cells without constraints
if (bound->isConstrained())
if (bound->cluster != ClusterId())
continue;
ctx->unbindBel(bound->bel);
}
@@ -1019,45 +1004,23 @@ class HeAPPlacer
} else {
// We do have relative constraints
for (auto sz : fb->at(nx).at(ny)) {
Loc loc = ctx->getBelLocation(sz);
// Check that the absolute-z constraint is satisfied if applicable
if (ci->constr_abs_z && loc.z != ci->constr_z)
continue;
// List of cells and their destination
std::vector<std::pair<CellInfo *, BelId>> targets;
// List of bels we placed things at; and the cell that was there before if applicable
std::vector<std::pair<BelId, CellInfo *>> swaps_made;
// List of (cell, new location) pairs to check
std::queue<std::pair<CellInfo *, Loc>> visit;
// FIXME: this approach of having a visit queue is designed to deal with recursively chained
// cells. But is this a case we really want to care about given the complexity it adds? Start by
// considering the root cell at the root location
visit.emplace(ci, loc);
while (!visit.empty()) {
CellInfo *vc = visit.front().first;
NPNR_ASSERT(vc->bel == BelId());
Loc ploc = visit.front().second;
visit.pop();
// Get the bel we're going to place this cell at
BelId target = ctx->getBelByLocation(ploc);
if (!ctx->getClusterPlacement(ci->cluster, sz, targets))
continue;
for (auto &target : targets) {
// Check it satisfies the region constraint if applicable
if (!vc->testRegion(target))
if (!target.first->testRegion(target.second))
goto fail;
CellInfo *bound;
// Check that the target bel exists and is of a suitable type
if (target == BelId() || !ctx->isValidBelForCellType(vc->type, target))
goto fail;
bound = ctx->getBoundBelCell(target);
CellInfo *bound = ctx->getBoundBelCell(target.second);
// Chains cannot overlap; so if we have to ripup a cell make sure it isn't part of a chain
if (bound != nullptr)
if (bound->isConstrained() || bound->belStrength > STRENGTH_WEAK)
if (bound->cluster != ClusterId() || bound->belStrength > STRENGTH_WEAK)
goto fail;
targets.emplace_back(vc, target);
for (auto child : vc->constr_children) {
// For all the constrained children; compute the location we need to place them at and
// add them to the queue
visit.emplace(child, child->getConstrainedLoc(ploc));
}
}
// Actually perform the move; keeping track of the moves we make so we can revert them if needed
for (auto &target : targets) {
@@ -1307,10 +1270,8 @@ class HeAPPlacer
occupancy.at(cell_loc.second.x).at(cell_loc.second.y).at(cell_index(cell))++;
// Compute ultimate extent of each chain root
if (p->chain_root.count(cell_name)) {
set_chain_ext(p->chain_root.at(cell_name)->name, loc.x, loc.y);
} else if (!ctx->cells.at(cell_name)->constr_children.empty()) {
set_chain_ext(cell_name, loc.x, loc.y);
if (cell.cluster != ClusterId()) {
set_chain_ext(ctx->getClusterRootCell(cell.cluster)->name, loc.x, loc.y);
}
}
@@ -1328,10 +1289,8 @@ class HeAPPlacer
// Transfer chain extents to the actual chains structure
ChainExtent *ce = nullptr;
if (p->chain_root.count(cell_name)) {
ce = &(cell_extents.at(p->chain_root.at(cell_name)->name));
} else if (!ctx->cells.at(cell_name)->constr_children.empty()) {
ce = &(cell_extents.at(cell_name));
if (cell.cluster != ClusterId()) {
ce = &(cell_extents.at(ctx->getClusterRootCell(cell.cluster)->name));
}
if (ce) {

View File

@@ -182,8 +182,7 @@ class TimingOptimiser
CellInfo *bound = ctx->getBoundBelCell(bel);
if (bound == nullptr) {
free_bels_at_loc.push_back(bel);
} else if (bound->belStrength <= STRENGTH_WEAK && bound->constr_parent == nullptr &&
bound->constr_children.empty()) {
} else if (bound->belStrength <= STRENGTH_WEAK && bound->cluster == ClusterId()) {
bound_bels_at_loc.push_back(bel);
}
}
@@ -378,7 +377,7 @@ class TimingOptimiser
if (front_net != nullptr && front_net->driver.cell != nullptr) {
auto front_cell = front_net->driver.cell;
if (front_cell->belStrength <= STRENGTH_WEAK && cfg.cellTypes.count(front_cell->type) &&
front_cell->constr_parent == nullptr && front_cell->constr_children.empty()) {
front_cell->cluster == ClusterId()) {
path_cells.push_back(front_cell->name);
}
}
@@ -392,7 +391,7 @@ class TimingOptimiser
if (std::find(path_cells.begin(), path_cells.end(), port->cell->name) != path_cells.end())
continue;
if (port->cell->belStrength > STRENGTH_WEAK || !cfg.cellTypes.count(port->cell->type) ||
port->cell->constr_parent != nullptr || !port->cell->constr_children.empty())
port->cell->cluster != ClusterId())
continue;
if (ctx->debug)
log_info(" can move\n");