aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2021-05-06 13:58:08 +0100
committerGitHub <noreply@github.com>2021-05-06 13:58:08 +0100
commitc322cda3f875a5e5dd2575d3a390cbe1cee073e0 (patch)
treefcd843131002f8986decf8dcd9352cf3ebd54290 /common
parented17091e6ada98a55396186a22c748abf3fca310 (diff)
parent0d6be6f4749174f4a6938a675456cb663edc47cb (diff)
downloadnextpnr-c322cda3f875a5e5dd2575d3a390cbe1cee073e0.tar.gz
nextpnr-c322cda3f875a5e5dd2575d3a390cbe1cee073e0.tar.bz2
nextpnr-c322cda3f875a5e5dd2575d3a390cbe1cee073e0.zip
Merge pull request #688 from YosysHQ/gatecat/new-cluster-api
New cluster API
Diffstat (limited to 'common')
-rw-r--r--common/arch_api.h7
-rw-r--r--common/base_arch.h93
-rw-r--r--common/base_clusterinfo.h45
-rw-r--r--common/basectx.cc61
-rw-r--r--common/nextpnr_types.cc17
-rw-r--r--common/nextpnr_types.h17
-rw-r--r--common/place_common.cc208
-rw-r--r--common/placer1.cc65
-rw-r--r--common/placer_heap.cc121
-rw-r--r--common/timing_opt.cc7
10 files changed, 292 insertions, 349 deletions
diff --git a/common/arch_api.h b/common/arch_api.h
index 7ed81434..01c29a84 100644
--- a/common/arch_api.h
+++ b/common/arch_api.h
@@ -139,6 +139,13 @@ template <typename R> struct ArchAPI : BaseCtx
virtual typename R::CellTypeRangeT getCellTypes() const = 0;
virtual typename R::BelBucketRangeT getBelBuckets() const = 0;
virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const = 0;
+ // Cluster methods
+ virtual CellInfo *getClusterRootCell(ClusterId cluster) const = 0;
+ virtual ArcBounds getClusterBounds(ClusterId cluster) 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
virtual bool pack() = 0;
virtual bool place() = 0;
diff --git a/common/base_arch.h b/common/base_arch.h
index d4efe9ce..e9cc8cf0 100644
--- a/common/base_arch.h
+++ b/common/base_arch.h
@@ -25,6 +25,7 @@
#include <vector>
#include "arch_api.h"
+#include "base_clusterinfo.h"
#include "idstring.h"
#include "nextpnr_types.h"
@@ -80,6 +81,36 @@ typename std::enable_if<!std::is_same<Tret, Tc>::value, Tret>::type return_if_ma
"respective range types are 'const std::vector&'");
}
+// Default implementations of the clustering functions
+template <typename Tid>
+typename std::enable_if<std::is_same<Tid, IdString>::value, CellInfo *>::type get_cluster_root(const BaseCtx *ctx,
+ Tid cluster)
+{
+ return ctx->cells.at(cluster).get();
+}
+
+template <typename Tid>
+typename std::enable_if<!std::is_same<Tid, IdString>::value, CellInfo *>::type get_cluster_root(const BaseCtx *ctx,
+ Tid cluster)
+{
+ NPNR_ASSERT_FALSE("default implementation of getClusterRootCell requires ClusterId to be IdString");
+}
+
+// Executes the lambda with the base cluster data, only if the derivation works
+template <typename Tret, typename Tcell, typename Tfunc>
+typename std::enable_if<std::is_base_of<BaseClusterInfo, Tcell>::value, Tret>::type
+if_using_basecluster(const Tcell *cell, Tfunc func)
+{
+ return func(static_cast<const BaseClusterInfo *>(cell));
+}
+template <typename Tret, typename Tcell, typename Tfunc>
+typename std::enable_if<!std::is_base_of<BaseClusterInfo, Tcell>::value, Tret>::type
+if_using_basecluster(const Tcell *cell, Tfunc func)
+{
+ NPNR_ASSERT_FALSE(
+ "default implementation of cluster functions requires ArchCellInfo to derive from BaseClusterInfo");
+}
+
} // namespace
// This contains the relevant range types for the default implementations of Arch functions
@@ -343,6 +374,68 @@ template <typename R> struct BaseArch : ArchAPI<R>
return return_if_match<const std::vector<BelId> &, typename R::BucketBelRangeT>(bucket_bels.at(bucket));
}
+ // Cluster methods
+ virtual CellInfo *getClusterRootCell(ClusterId cluster) const override { return get_cluster_root(this, cluster); }
+
+ virtual ArcBounds getClusterBounds(ClusterId cluster) const override
+ {
+ return if_using_basecluster<ArcBounds>(get_cluster_root(this, cluster), [](const BaseClusterInfo *cluster) {
+ ArcBounds bounds(0, 0, 0, 0);
+ for (auto child : cluster->constr_children) {
+ if_using_basecluster<void>(child, [&](const BaseClusterInfo *child) {
+ bounds.x0 = std::min(bounds.x0, child->constr_x);
+ bounds.y0 = std::min(bounds.y0, child->constr_y);
+ bounds.x1 = std::max(bounds.x1, child->constr_x);
+ bounds.y1 = std::max(bounds.y1, child->constr_y);
+ });
+ }
+ return bounds;
+ });
+ }
+
+ 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(const CellInfo *cell) const override { return true; }
+
+ virtual bool getClusterPlacement(ClusterId cluster, BelId root_bel,
+ std::vector<std::pair<CellInfo *, BelId>> &placement) const override
+ {
+ CellInfo *root_cell = get_cluster_root(this, cluster);
+ return if_using_basecluster<bool>(root_cell, [&](const BaseClusterInfo *cluster) -> bool {
+ placement.clear();
+ NPNR_ASSERT(root_bel != BelId());
+ Loc root_loc = this->getBelLocation(root_bel);
+
+ if (cluster->constr_abs_z) {
+ // Coerce root to absolute z constraint
+ root_loc.z = cluster->constr_z;
+ root_bel = this->getBelByLocation(root_loc);
+ if (root_bel == BelId() || !this->isValidBelForCellType(root_cell->type, root_bel))
+ return false;
+ }
+ placement.emplace_back(root_cell, root_bel);
+
+ for (auto child : cluster->constr_children) {
+ Loc child_loc = if_using_basecluster<Loc>(child, [&](const BaseClusterInfo *child) {
+ Loc result;
+ result.x = root_loc.x + child->constr_x;
+ result.y = root_loc.y + child->constr_y;
+ result.z = child->constr_abs_z ? child->constr_z : (root_loc.z + child->constr_z);
+ return result;
+ });
+ BelId child_bel = this->getBelByLocation(child_loc);
+ if (child_bel == BelId() || !this->isValidBelForCellType(child->type, child_bel))
+ return false;
+ placement.emplace_back(child, child_bel);
+ }
+ return true;
+ });
+ }
+
// Flow methods
virtual void assignArchInfo() override{};
diff --git a/common/base_clusterinfo.h b/common/base_clusterinfo.h
new file mode 100644
index 00000000..65e8e6d4
--- /dev/null
+++ b/common/base_clusterinfo.h
@@ -0,0 +1,45 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2021 gatecat <gatecat@ds0.me>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef BASE_CLUSTERINFO_H
+#define BASE_CLUSTERINFO_H
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+
+#include <vector>
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct CellInfo;
+
+// The 'legacy' cluster data, used for existing arches and to provide a basic implementation for arches without complex
+// clustering requirements
+struct BaseClusterInfo
+{
+ std::vector<CellInfo *> constr_children;
+ int constr_x = 0; // this.x - parent.x
+ int constr_y = 0; // this.y - parent.y
+ int constr_z = 0; // this.z - parent.z
+ bool constr_abs_z = false; // parent.z := 0
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* BASE_ARCH_H */
diff --git a/common/basectx.cc b/common/basectx.cc
index da33eecc..34fb414c 100644
--- a/common/basectx.cc
+++ b/common/basectx.cc
@@ -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();
diff --git a/common/nextpnr_types.cc b/common/nextpnr_types.cc
index a76576de..f55b89e8 100644
--- a/common/nextpnr_types.cc
+++ b/common/nextpnr_types.cc
@@ -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
diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h
index 8b450297..67e60c50 100644
--- a/common/nextpnr_types.h
+++ b/common/nextpnr_types.h
@@ -165,15 +165,8 @@ struct CellInfo : ArchCellInfo
BelId bel;
PlaceStrength belStrength = STRENGTH_NONE;
- // placement constraints
- CellInfo *constr_parent = nullptr;
- std::vector<CellInfo *> constr_children;
- const int UNCONSTR = INT_MIN;
- int constr_x = UNCONSTR; // this.x - parent.x
- int constr_y = UNCONSTR; // this.y - parent.y
- int constr_z = UNCONSTR; // this.z - parent.z
- bool constr_abs_z = false; // parent.z := 0
- // parent.[xyz] := 0 when (constr_parent == nullptr)
+ // cell is part of a cluster if != ClusterId
+ ClusterId cluster;
Region *region = nullptr;
@@ -185,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
diff --git a/common/place_common.cc b/common/place_common.cc
index 31b93420..7cbeca65 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -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()) {
- return false;
- }
- if (!ctx->isValidBelForCellType(cell->type, locBel)) {
+ if (locBel == BelId())
return false;
- }
- if (!ctx->checkBelAvail(locBel)) {
- CellInfo *confCell = ctx->getConflictingBelCell(locBel);
- if (confCell->belStrength >= STRENGTH_STRONG) {
- 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)
+
+ if (cell->cluster == ClusterId()) {
+ if (!ctx->isValidBelForCellType(cell->type, locBel))
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);
-
- if (cell->constr_y == cell->UNCONSTR)
- yRootSearch = IncreasingDiameterSearch(currentLoc.y, 0, ctx->getGridDimY() - 1);
- else
- yRootSearch = IncreasingDiameterSearch(cell->constr_y);
+ 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_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;
}
diff --git a/common/placer1.cc b/common/placer1.cc
index 1f940dac..a3e7a696 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -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);
-
- 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);
+ if (!ctx->getClusterPlacement(cell->cluster, newBase, dest_bels))
+ return false;
+
+ 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)
diff --git a/common/placer_heap.cc b/common/placer_heap.cc
index 042f3046..2f7c7ccb 100644
--- a/common/placer_heap.cc
+++ b/common/placer_heap.cc
@@ -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))
- goto fail;
- CellInfo *bound;
- // Check that the target bel exists and is of a suitable type
- if (target == BelId() || !ctx->isValidBelForCellType(vc->type, target))
+ if (!target.first->testRegion(target.second))
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) {
diff --git a/common/timing_opt.cc b/common/timing_opt.cc
index dba96bf1..854cbc5b 100644
--- a/common/timing_opt.cc
+++ b/common/timing_opt.cc
@@ -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");