aboutsummaryrefslogtreecommitdiffstats
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
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
-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
-rw-r--r--docs/archapi.md33
-rw-r--r--docs/netlist.md6
-rw-r--r--ecp5/archdefs.h5
-rw-r--r--ecp5/bitstream.cc2
-rw-r--r--ecp5/pack.cc42
-rw-r--r--fpga_interchange/arch.h13
-rw-r--r--fpga_interchange/archdefs.h2
-rw-r--r--generic/arch.h11
-rw-r--r--generic/archdefs.h1
-rw-r--r--gowin/archdefs.h4
-rw-r--r--ice40/archdefs.h4
-rw-r--r--ice40/chains.cc4
-rw-r--r--machxo2/archdefs.h4
-rw-r--r--nexus/archdefs.h5
-rw-r--r--nexus/pack.cc20
-rw-r--r--nexus/post_place.cc5
26 files changed, 411 insertions, 391 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");
diff --git a/docs/archapi.md b/docs/archapi.md
index d164e61c..6d17f01a 100644
--- a/docs/archapi.md
+++ b/docs/archapi.md
@@ -67,6 +67,10 @@ A type representing a group name. `GroupId()` must construct a unique null-value
A type representing a reference to a graphical decal. `DecalId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<DecalId>`.
+### ClusterId
+
+A type representing a reference to a constrained cluster of cells. `ClusterId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<ClusterId>`.
+
### ArchNetInfo
The global `NetInfo` type derives from this one. Can be used to add arch-specific data (caches of information derived from wire attributes, bound wires and pips, and other net state). Must be declared as empty struct if unused.
@@ -720,3 +724,32 @@ Name of the default router algorithm for the architecture, if
Name of available router algorithms for the architecture, used
to provide help for and validate `--router`.
+
+Cluster Methods
+---------------
+
+### CellInfo *getClusterRootCell(ClusterId cluster) const
+
+Gets the root cell of a cluster, which is used as a datum point when placing the cluster.
+
+### ArcBounds getClusterBounds(ClusterId cluster) const
+
+Gets an approximate bounding box of the cluster. This is intended for area allocation in the placer and is permitted to occasionally give incorrect estimates, for example due to irregularities in the fabric depending on cluster placement. `getClusterPlacement` should always be used to get exact locations.
+
+### Loc getClusterOffset(const CellInfo \*cell) const
+
+Gets the approximate offset of a cell within its cluster, relative to the root cell. This is intended for global placement usage and is permitted to occasionally give incorrect estimates, for example due to irregularities in the fabric depending on cluster placement. `getClusterPlacement` should always be used to get exact locations.
+
+The returned x and y coordinates, when added to the root location of the cluster, should give an approximate location where `cell` will end up placed at.
+
+### bool isClusterStrict(const CellInfo *cell) const
+
+Returns `true` if the cell **must** be placed according to the cluster; for example typical carry chains, and dedicated IO routing. Returns `false` if the cell can be split from the cluster if placement desires, at the expense of a less optimal result (for example dedicated LUT-FF paths where general routing can also be used).
+
+### bool getClusterPlacement(ClusterId cluster, BelId root\_bel, std::vector\<std::pair\<CellInfo \*, BelId\>\> &placement) const
+
+Gets an exact placement of the cluster, with the root cell placed on or near `root_bel` (and always within the same tile). Returns false if no placement is viable, otherwise returns `true` and populates `placement` with a list of cells inside the cluster and bels they should be placed at.
+
+This approach of allowing architectures to define cluster placements enables easier handling of irregular fabrics than requiring strict and constant x, y and z offsets.
+
+
diff --git a/docs/netlist.md b/docs/netlist.md
index 2e989a33..5d8ca572 100644
--- a/docs/netlist.md
+++ b/docs/netlist.md
@@ -23,11 +23,7 @@ Other structures used by these basic structures include:
- `ports` is a map from port name `IdString` to `PortInfo` structures for each cell port
- `bel` and `belStrength` contain the ID of the Bel the cell is placed onto; and placement strength of the cell; if placed. Placement/ripup should always be done by `Arch::bindBel` and `Arch::unbindBel` rather than by manipulating these fields.
- `params` and `attrs` store parameters and attributes - from the input JSON or assigned in flows to add metadata - by mapping from parameter name `IdString` to `Property`.
- - The `constr_` fields are for relative constraints:
- - `constr_parent` is a reference to the cell this cell is constrained with respect to; or `nullptr` if not relatively constrained. If not `nullptr`, this cell should be in the parent's `constr_children`.
- - `constr_children` is a list of cells relatively constrained to this one. All children should have `constr_parent == this`.
- - `constr_x` and `constr_y` are absolute (`constr_parent == nullptr`) or relative (`constr_parent != nullptr`) tile coordinate constraints. If set to `UNCONSTR` then the cell is not constrained in this axis (defaults to `UNCONSTR`)
- - `constr_z` is an absolute (`constr_abs_z`) or relative (`!constr_abs_z`) 'Z-axis' (index inside tile, e.g. logic cell) constraint
+ - `cluster` is used to specify that the cell is inside a placement cluster, with the details of the placement within the cluster provided by the architecture.
- `region` is a reference to a `Region` if the cell is constrained to a placement region (e.g. for partial reconfiguration or out-of-context flows) or `nullptr` otherwise.
## NetInfo
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index 6a149264..2b4590e5 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -23,6 +23,7 @@
#include <boost/functional/hash.hpp>
+#include "base_clusterinfo.h"
#include "idstring.h"
#include "nextpnr_namespaces.h"
@@ -148,7 +149,9 @@ struct ArchNetInfo
bool is_global = false;
};
-struct ArchCellInfo
+typedef IdString ClusterId;
+
+struct ArchCellInfo : BaseClusterInfo
{
struct
{
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index 7db0e020..f8d11d39 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -1171,7 +1171,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC"));
tg.config.add_enum(dsp + ".MODE", "MULT18X18D");
- if (str_or_default(ci->params, ctx->id("REG_OUTPUT_CLK"), "NONE") == "NONE" && ci->constr_parent == nullptr)
+ if (str_or_default(ci->params, ctx->id("REG_OUTPUT_CLK"), "NONE") == "NONE" && ci->cluster == ClusterId())
tg.config.add_enum(dsp + ".CIBOUT_BYP", "ON");
if (loc.z < 4)
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 029ed028..bdf84bcf 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -631,10 +631,11 @@ class Ecp5Packer
slice0->constr_z = 1;
slice0->constr_x = 0;
slice0->constr_y = 0;
- slice0->constr_parent = slice1;
+ slice0->cluster = slice1->name;
slice1->constr_z = 0;
slice1->constr_abs_z = true;
slice1->constr_children.push_back(slice0);
+ slice1->cluster = slice1->name;
if (lutffPairs.find(ci->name) != lutffPairs.end()) {
CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get();
@@ -696,21 +697,22 @@ class Ecp5Packer
for (auto slice : {slice0, slice1, slice2, slice3}) {
slice->constr_children.clear();
slice->constr_abs_z = false;
- slice->constr_x = slice->UNCONSTR;
- slice->constr_y = slice->UNCONSTR;
- slice->constr_z = slice->UNCONSTR;
- slice->constr_parent = nullptr;
+ slice->constr_x = 0;
+ slice->constr_y = 0;
+ slice->constr_z = 0;
+ slice->cluster = ClusterId();
}
slice3->constr_children.clear();
slice3->constr_abs_z = true;
slice3->constr_z = 0;
+ slice3->cluster = slice3->name;
slice2->constr_children.clear();
slice2->constr_abs_z = true;
slice2->constr_z = 1;
slice2->constr_x = 0;
slice2->constr_y = 0;
- slice2->constr_parent = slice3;
+ slice2->cluster = slice3->name;
slice3->constr_children.push_back(slice2);
slice1->constr_children.clear();
@@ -718,7 +720,7 @@ class Ecp5Packer
slice1->constr_z = 2;
slice1->constr_x = 0;
slice1->constr_y = 0;
- slice1->constr_parent = slice3;
+ slice1->cluster = slice3->name;
slice3->constr_children.push_back(slice1);
slice0->constr_children.clear();
@@ -726,7 +728,7 @@ class Ecp5Packer
slice0->constr_z = 3;
slice0->constr_x = 0;
slice0->constr_y = 0;
- slice0->constr_parent = slice3;
+ slice0->cluster = slice3->name;
slice3->constr_children.push_back(slice0);
if (lutffPairs.find(ci->name) != lutffPairs.end()) {
@@ -956,12 +958,13 @@ class Ecp5Packer
for (auto &chain : packed_chains) {
chain.at(0)->constr_abs_z = true;
chain.at(0)->constr_z = 0;
+ chain.at(0)->cluster = chain.at(0)->name;
for (int i = 1; i < int(chain.size()); i++) {
chain.at(i)->constr_x = (i / 4);
chain.at(i)->constr_y = 0;
chain.at(i)->constr_z = i % 4;
chain.at(i)->constr_abs_z = true;
- chain.at(i)->constr_parent = chain.at(0);
+ chain.at(i)->cluster = chain.at(0)->name;
chain.at(0)->constr_children.push_back(chain.at(i));
}
}
@@ -1037,15 +1040,16 @@ class Ecp5Packer
// Setup placement constraints
ram0_slice->constr_abs_z = true;
ram0_slice->constr_z = 0;
+ ram0_slice->cluster = ram0_slice->name;
- ram1_slice->constr_parent = ram0_slice.get();
+ ram1_slice->cluster = ram0_slice->name;
ram1_slice->constr_abs_z = true;
ram1_slice->constr_x = 0;
ram1_slice->constr_y = 0;
ram1_slice->constr_z = 1;
ram0_slice->constr_children.push_back(ram1_slice.get());
- ramw_slice->constr_parent = ram0_slice.get();
+ ramw_slice->cluster = ram0_slice->name;
ramw_slice->constr_abs_z = true;
ramw_slice->constr_x = 0;
ramw_slice->constr_y = 0;
@@ -1189,17 +1193,15 @@ class Ecp5Packer
CellInfo *target = find_nearby_cell(ci, [&](CellInfo *cursor) {
if (cursor->type != id_TRELLIS_SLICE)
return false;
- if (!cursor->constr_children.empty() || cursor->constr_parent != nullptr) {
- auto &constr_children = (cursor->constr_parent != nullptr)
- ? cursor->constr_parent->constr_children
- : cursor->constr_children;
+ if (cursor->cluster != ClusterId()) {
+ auto &constr_children = ctx->cells.at(cursor->cluster)->constr_children;
// Skip big chains for performance
if (constr_children.size() > 8)
return false;
// Have to check the whole of the tile for legality when dealing with chains, not just slice
ltile.clear();
- if (cursor->constr_parent != nullptr)
- ltile.push_back(cursor->constr_parent);
+ if (cursor->cluster != cursor->name)
+ ltile.push_back(ctx->cells.at(cursor->cluster).get());
else
ltile.push_back(cursor);
for (auto c : constr_children)
@@ -1596,7 +1598,11 @@ class Ecp5Packer
mult_b = mult;
}
mult->constr_y = 0;
- mult->constr_parent = ci;
+ mult->cluster = ci->name;
+ ci->constr_x = 0;
+ ci->constr_y = 0;
+ ci->constr_z = 0;
+ ci->cluster = ci->name;
ci->constr_children.push_back(mult);
log_info("DSP: Constraining MULT18X18D '%s' to ALU54B '%s' port %s\n", mult->name.c_str(ctx),
cell.first.c_str(ctx), ctx->nameOf(port));
diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h
index da620699..c7d2544f 100644
--- a/fpga_interchange/arch.h
+++ b/fpga_interchange/arch.h
@@ -835,6 +835,19 @@ struct Arch : ArchAPI<ArchRanges>
return get_site_status(tile_status, bel_data).checkSiteRouting(getCtx(), tile_status);
}
+ // -------------------------------------------------
+
+ // TODO
+ CellInfo *getClusterRootCell(ClusterId cluster) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ ArcBounds getClusterBounds(ClusterId cluster) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ Loc getClusterOffset(const CellInfo *cell) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ bool isClusterStrict(const CellInfo *cell) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ bool getClusterPlacement(ClusterId cluster, BelId root_bel,
+ std::vector<std::pair<CellInfo *, BelId>> &placement) const override
+ {
+ NPNR_ASSERT_FALSE("unimplemented");
+ }
+
IdString get_bel_tiletype(BelId bel) const { return IdString(loc_info(chip_info, bel).name); }
std::unordered_map<WireId, Loc> sink_locs, source_locs;
diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h
index 23bff4f3..c145b893 100644
--- a/fpga_interchange/archdefs.h
+++ b/fpga_interchange/archdefs.h
@@ -98,6 +98,8 @@ struct BelBucketId
bool operator<(const BelBucketId &other) const { return name < other.name; }
};
+typedef IdString ClusterId;
+
struct SiteExpansionLoop;
struct ArchNetInfo
diff --git a/generic/arch.h b/generic/arch.h
index 92260ce0..50d2731c 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -362,6 +362,17 @@ struct Arch : ArchAPI<ArchRanges>
bool isValidBelForCellType(IdString cell_type, BelId bel) const override { return cell_type == getBelType(bel); }
bool isBelLocationValid(BelId bel) const override;
+ // TODO
+ CellInfo *getClusterRootCell(ClusterId cluster) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ ArcBounds getClusterBounds(ClusterId cluster) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ Loc getClusterOffset(const CellInfo *cell) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ bool isClusterStrict(const CellInfo *cell) const override { NPNR_ASSERT_FALSE("unimplemented"); }
+ bool getClusterPlacement(ClusterId cluster, BelId root_bel,
+ std::vector<std::pair<CellInfo *, BelId>> &placement) const override
+ {
+ NPNR_ASSERT_FALSE("unimplemented");
+ }
+
static const std::string defaultPlacer;
static const std::vector<std::string> availablePlacers;
static const std::string defaultRouter;
diff --git a/generic/archdefs.h b/generic/archdefs.h
index bdd97dde..0489ab04 100644
--- a/generic/archdefs.h
+++ b/generic/archdefs.h
@@ -34,6 +34,7 @@ typedef IdStringList PipId;
typedef IdStringList GroupId;
typedef IdStringList DecalId;
typedef IdString BelBucketId;
+typedef IdString ClusterId;
struct ArchNetInfo
{
diff --git a/gowin/archdefs.h b/gowin/archdefs.h
index ef297d41..fd7e0b0b 100644
--- a/gowin/archdefs.h
+++ b/gowin/archdefs.h
@@ -21,6 +21,7 @@
#ifndef GOWIN_ARCHDEFS_H
#define GOWIN_ARCHDEFS_H
+#include "base_clusterinfo.h"
#include "idstring.h"
#include "nextpnr_namespaces.h"
@@ -48,6 +49,7 @@ typedef IdString PipId;
typedef IdString GroupId;
typedef IdString DecalId;
typedef IdString BelBucketId;
+typedef IdString ClusterId;
struct ArchNetInfo
{
@@ -55,7 +57,7 @@ struct ArchNetInfo
struct NetInfo;
-struct ArchCellInfo
+struct ArchCellInfo : BaseClusterInfo
{
// Is the flip-flop of this slice used
bool ff_used;
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 038a3131..c2b7019c 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -22,6 +22,7 @@
#include <boost/functional/hash.hpp>
+#include "base_clusterinfo.h"
#include "idstring.h"
#include "nextpnr_namespaces.h"
@@ -122,7 +123,7 @@ struct ArchNetInfo
struct NetInfo;
-struct ArchCellInfo
+struct ArchCellInfo : BaseClusterInfo
{
union
{
@@ -154,6 +155,7 @@ struct ArchCellInfo
};
typedef IdString BelBucketId;
+typedef IdString ClusterId;
NEXTPNR_NAMESPACE_END
diff --git a/ice40/chains.cc b/ice40/chains.cc
index 7eb03105..2607959a 100644
--- a/ice40/chains.cc
+++ b/ice40/chains.cc
@@ -292,12 +292,14 @@ class ChainConstrainer
// Place carry chain
chain.cells.at(0)->constr_abs_z = true;
chain.cells.at(0)->constr_z = 0;
+ chain.cells.at(0)->cluster = chain.cells.at(0)->name;
+
for (int i = 1; i < int(chain.cells.size()); i++) {
chain.cells.at(i)->constr_x = 0;
chain.cells.at(i)->constr_y = (i / 8);
chain.cells.at(i)->constr_z = i % 8;
chain.cells.at(i)->constr_abs_z = true;
- chain.cells.at(i)->constr_parent = chain.cells.at(0);
+ chain.cells.at(i)->cluster = chain.cells.at(0)->name;
chain.cells.at(0)->constr_children.push_back(chain.cells.at(i));
}
}
diff --git a/machxo2/archdefs.h b/machxo2/archdefs.h
index 11791d3c..2d50dddb 100644
--- a/machxo2/archdefs.h
+++ b/machxo2/archdefs.h
@@ -21,6 +21,7 @@
#ifndef MACHXO2_ARCHDEFS_H
#define MACHXO2_ARCHDEFS_H
+#include "base_clusterinfo.h"
#include "idstring.h"
#include "nextpnr_namespaces.h"
@@ -104,6 +105,7 @@ struct PipId
typedef IdString GroupId;
typedef IdString DecalId;
typedef IdString BelBucketId;
+typedef IdString ClusterId;
struct ArchNetInfo
{
@@ -111,7 +113,7 @@ struct ArchNetInfo
struct NetInfo;
-struct ArchCellInfo
+struct ArchCellInfo : BaseClusterInfo
{
};
diff --git a/nexus/archdefs.h b/nexus/archdefs.h
index b9ac3c77..660f166c 100644
--- a/nexus/archdefs.h
+++ b/nexus/archdefs.h
@@ -23,6 +23,7 @@
#include <boost/functional/hash.hpp>
#include <unordered_map>
+#include "base_clusterinfo.h"
#include "idstring.h"
#include "nextpnr_namespaces.h"
@@ -157,7 +158,9 @@ inline bool operator!=(const FFControlSet &a, const FFControlSet &b)
(a.ce != b.ce);
}
-struct ArchCellInfo
+typedef IdString ClusterId;
+
+struct ArchCellInfo : BaseClusterInfo
{
union
{
diff --git a/nexus/pack.cc b/nexus/pack.cc
index ffec29bd..66ab4b09 100644
--- a/nexus/pack.cc
+++ b/nexus/pack.cc
@@ -1000,12 +1000,13 @@ struct NexusPacker
// Setup relative constraints
combs[0]->constr_z = 0;
combs[0]->constr_abs_z = true;
+ combs[0]->cluster = combs[0]->name;
for (int i = 1; i < 4; i++) {
combs[i]->constr_x = 0;
combs[i]->constr_y = 0;
combs[i]->constr_z = ((i / 2) << 3) | (i % 2);
combs[i]->constr_abs_z = true;
- combs[i]->constr_parent = combs[0];
+ combs[i]->cluster = combs[0]->name;
combs[0]->constr_children.push_back(combs[i]);
}
@@ -1013,7 +1014,7 @@ struct NexusPacker
ramw->constr_y = 0;
ramw->constr_z = (2 << 3) | Arch::BEL_RAMW;
ramw->constr_abs_z = true;
- ramw->constr_parent = combs[0];
+ ramw->cluster = combs[0]->name;
combs[0]->constr_children.push_back(ramw);
// Remove now-packed cell
ctx->cells.erase(ci->name);
@@ -1182,11 +1183,12 @@ struct NexusPacker
combs[0]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT0, 16, 0);
combs[1]->params[id_INIT] = ctx->parse_lattice_param(ci, id_INIT1, 16, 0);
- combs[1]->constr_parent = combs[0];
+ combs[1]->cluster = combs[0]->name;
combs[1]->constr_x = 0;
combs[1]->constr_y = 0;
combs[1]->constr_z = 1;
combs[1]->constr_abs_z = false;
+ combs[0]->cluster = combs[0]->name;
combs[0]->constr_children.push_back(combs[1]);
ctx->cells.erase(ci->name);
@@ -1253,10 +1255,11 @@ struct NexusPacker
if (constr_base == nullptr) {
// This is the very first cell in the chain
constr_base = combs[i];
+ constr_base->cluster = constr_base->name;
} else {
combs[i]->constr_x = (idx / 8);
combs[i]->constr_y = 0;
- combs[i]->constr_parent = constr_base;
+ combs[i]->cluster = constr_base->name;
constr_base->constr_children.push_back(combs[i]);
}
@@ -1455,19 +1458,20 @@ struct NexusPacker
CellInfo *cell = ctx->createCell(name, type);
if (constr_base != nullptr) {
// We might be constraining against an already-constrained cell
- if (constr_base->constr_parent != nullptr) {
+ if (constr_base->cluster != ClusterId() && constr_base->cluster != constr_base->name) {
cell->constr_x = dx + constr_base->constr_x;
cell->constr_y = constr_base->constr_y;
cell->constr_z = dz + constr_base->constr_z;
cell->constr_abs_z = false;
- cell->constr_parent = constr_base->constr_parent;
- constr_base->constr_parent->constr_children.push_back(cell);
+ cell->cluster = constr_base->cluster;
+ ctx->cells.at(constr_base->cluster)->constr_children.push_back(cell);
} else {
cell->constr_x = dx;
cell->constr_y = 0;
cell->constr_z = dz;
cell->constr_abs_z = false;
- cell->constr_parent = constr_base;
+ cell->cluster = constr_base->name;
+ constr_base->cluster = constr_base->name;
constr_base->constr_children.push_back(cell);
}
}
diff --git a/nexus/post_place.cc b/nexus/post_place.cc
index b712aea3..b6817b57 100644
--- a/nexus/post_place.cc
+++ b/nexus/post_place.cc
@@ -32,10 +32,7 @@ struct NexusPostPlaceOpt
NexusPostPlaceOpt(Context *ctx) : ctx(ctx), tmg(ctx){};
- inline bool is_constrained(CellInfo *cell)
- {
- return cell->constr_parent != nullptr || !cell->constr_children.empty();
- }
+ inline bool is_constrained(CellInfo *cell) { return cell->cluster != ClusterId(); }
bool swap_cell_placement(CellInfo *cell, BelId new_bel)
{