Merge pull request #688 from YosysHQ/gatecat/new-cluster-api
New cluster API
This commit is contained in:
commit
c322cda3f8
@ -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;
|
||||
|
@ -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{};
|
||||
|
||||
|
45
common/base_clusterinfo.h
Normal file
45
common/base_clusterinfo.h
Normal file
@ -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 */
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
|
42
ecp5/pack.cc
42
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));
|
||||
|
@ -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;
|
||||
|
@ -98,6 +98,8 @@ struct BelBucketId
|
||||
bool operator<(const BelBucketId &other) const { return name < other.name; }
|
||||
};
|
||||
|
||||
typedef IdString ClusterId;
|
||||
|
||||
struct SiteExpansionLoop;
|
||||
|
||||
struct ArchNetInfo
|
||||
|
@ -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;
|
||||
|
@ -34,6 +34,7 @@ typedef IdStringList PipId;
|
||||
typedef IdStringList GroupId;
|
||||
typedef IdStringList DecalId;
|
||||
typedef IdString BelBucketId;
|
||||
typedef IdString ClusterId;
|
||||
|
||||
struct ArchNetInfo
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user