aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2022-06-23 18:48:31 +0100
committergatecat <gatecat@ds0.me>2022-07-08 14:30:57 +0200
commit09e388f453d9cf998391495349c88e5478b62e34 (patch)
tree004f2b14ed5a3b0584c4998d9f0a5598cc52ab28 /common
parent86396c41d64d2583ec1dffca4298e83d927f0762 (diff)
downloadnextpnr-09e388f453d9cf998391495349c88e5478b62e34.tar.gz
nextpnr-09e388f453d9cf998391495349c88e5478b62e34.tar.bz2
nextpnr-09e388f453d9cf998391495349c88e5478b62e34.zip
netlist: Add PseudoCell API
When implementing concepts such as partition pins or deliberately split nets, there's a need for something that looks like a cell (starts/ends routing with pins on nets, has timing data) but isn't mapped to a fixed bel in the architecture, but instead can have pin mappings defined at runtime. The PseudoCell allows this by providing an alternate, virtual-function based API for such cells. When a cell has `pseudo_cell` used, instead of calling functions such as getBelPinWire, getBelLocation or getCellDelay in the Arch API; such data is provided by the cell itself, fully flexible at runtime regardless of arch, via methods on the PseudoCell implementation.
Diffstat (limited to 'common')
-rw-r--r--common/kernel/arch_pybindings_shared.h8
-rw-r--r--common/kernel/basectx.cc24
-rw-r--r--common/kernel/basectx.h4
-rw-r--r--common/kernel/context.cc5
-rw-r--r--common/kernel/context.h18
-rw-r--r--common/kernel/nextpnr_types.cc10
-rw-r--r--common/kernel/nextpnr_types.h86
-rw-r--r--common/place/detail_place_core.cc12
-rw-r--r--common/place/parallel_refine.cc2
-rw-r--r--common/place/place_common.cc6
-rw-r--r--common/place/placer1.cc20
-rw-r--r--common/place/placer_heap.cc24
-rw-r--r--common/route/router2.cc4
13 files changed, 179 insertions, 44 deletions
diff --git a/common/kernel/arch_pybindings_shared.h b/common/kernel/arch_pybindings_shared.h
index bfb58f11..d78d240c 100644
--- a/common/kernel/arch_pybindings_shared.h
+++ b/common/kernel/arch_pybindings_shared.h
@@ -150,4 +150,10 @@ fn_wrapper_1a<Context, decltype(&Context::getDelayFromNS), &Context::getDelayFro
pass_through<double>>::def_wrap(ctx_cls, "getDelayFromNS");
fn_wrapper_1a<Context, decltype(&Context::getDelayNS), &Context::getDelayNS, pass_through<double>,
- pass_through<delay_t>>::def_wrap(ctx_cls, "getDelayNS"); \ No newline at end of file
+ pass_through<delay_t>>::def_wrap(ctx_cls, "getDelayNS");
+
+fn_wrapper_3a_v<Context, decltype(&Context::createRegionPlug), &Context::createRegionPlug, conv_from_str<IdString>,
+ conv_from_str<IdString>, pass_through<Loc>>::def_wrap(ctx_cls, "createRegionPlug");
+fn_wrapper_4a_v<Context, decltype(&Context::addPlugPin), &Context::addPlugPin, conv_from_str<IdString>,
+ conv_from_str<IdString>, pass_through<PortType>, conv_from_str<WireId>>::def_wrap(ctx_cls,
+ "addPlugPin");
diff --git a/common/kernel/basectx.cc b/common/kernel/basectx.cc
index 83a2deea..82cdd835 100644
--- a/common/kernel/basectx.cc
+++ b/common/kernel/basectx.cc
@@ -131,6 +131,30 @@ void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name)
if (!matched)
log_warning("No cell matched '%s' when constraining to region '%s'\n", nameOf(cell), nameOf(region_name));
}
+
+void BaseCtx::createRegionPlug(IdString name, IdString type, Loc approx_loc)
+{
+ CellInfo *cell = nullptr;
+ if (cells.count(name))
+ cell = cells.at(name).get();
+ else
+ cell = createCell(name, type);
+ cell->pseudo_cell = std::make_unique<RegionPlug>(approx_loc);
+}
+
+void BaseCtx::addPlugPin(IdString plug, IdString pin, PortType dir, WireId wire)
+{
+ if (!cells.count(plug))
+ log_error("no cell named '%s' found\n", plug.c_str(this));
+ CellInfo *ci = cells.at(plug).get();
+ RegionPlug *rplug = dynamic_cast<RegionPlug *>(ci->pseudo_cell.get());
+ if (!rplug)
+ log_error("cell '%s' is not a RegionPlug\n", plug.c_str(this));
+ rplug->port_wires[pin] = wire;
+ ci->ports[pin].name = pin;
+ ci->ports[pin].type = dir;
+}
+
DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y)
{
DecalXY dxy;
diff --git a/common/kernel/basectx.h b/common/kernel/basectx.h
index 21d6d63a..5775e47f 100644
--- a/common/kernel/basectx.h
+++ b/common/kernel/basectx.h
@@ -220,6 +220,10 @@ struct BaseCtx
void addBelToRegion(IdString name, BelId bel);
void constrainCellToRegion(IdString cell, IdString region_name);
+ // Helper functions for the partial reconfiguration plug API using PseudoCells
+ void createRegionPlug(IdString name, IdString type, Loc approx_loc);
+ void addPlugPin(IdString plug, IdString pin, PortType dir, WireId wire);
+
// Helper functions for Python bindings
NetInfo *createNet(IdString name);
void connectPort(IdString net, IdString cell, IdString port);
diff --git a/common/kernel/context.cc b/common/kernel/context.cc
index e35d3e49..014394a6 100644
--- a/common/kernel/context.cc
+++ b/common/kernel/context.cc
@@ -30,6 +30,9 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
if (net_info->driver.cell == nullptr)
return WireId();
+ if (net_info->driver.cell->isPseudo())
+ return net_info->driver.cell->pseudo_cell->getPortWire(net_info->driver.port);
+
auto src_bel = net_info->driver.cell->bel;
if (src_bel == BelId())
@@ -47,6 +50,8 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
SSOArray<WireId, 2> Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const
{
+ if (user_info.cell->isPseudo())
+ return SSOArray<WireId, 2>(1, user_info.cell->pseudo_cell->getPortWire(user_info.port));
auto dst_bel = user_info.cell->bel;
if (dst_bel == BelId())
return SSOArray<WireId, 2>(0, WireId());
diff --git a/common/kernel/context.h b/common/kernel/context.h
index cb8fd257..4a5667d0 100644
--- a/common/kernel/context.h
+++ b/common/kernel/context.h
@@ -65,6 +65,24 @@ struct Context : Arch, DeterministicRNG
dict<WireId, PipId> *route = nullptr, bool useEstimate = true);
// --------------------------------------------------------------
+ // Dispatch to the Arch API or pseudo-cell API accordingly
+ bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const override
+ {
+ return cell->pseudo_cell ? cell->pseudo_cell->getDelay(fromPort, toPort, delay)
+ : Arch::getCellDelay(cell, fromPort, toPort, delay);
+ }
+ TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const override
+ {
+ return cell->pseudo_cell ? cell->pseudo_cell->getPortTimingClass(port, clockInfoCount)
+ : Arch::getPortTimingClass(cell, port, clockInfoCount);
+ }
+ TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override
+ {
+ return cell->pseudo_cell ? cell->pseudo_cell->getPortClockingInfo(port, index)
+ : Arch::getPortClockingInfo(cell, port, index);
+ }
+
+ // --------------------------------------------------------------
// call after changing hierpath or adding/removing nets and cells
void fixupHierarchy();
diff --git a/common/kernel/nextpnr_types.cc b/common/kernel/nextpnr_types.cc
index 82725d6f..8563eb27 100644
--- a/common/kernel/nextpnr_types.cc
+++ b/common/kernel/nextpnr_types.cc
@@ -177,4 +177,14 @@ void CellInfo::copyPortBusTo(IdString old_name, int old_offset, bool old_bracket
}
}
+Loc CellInfo::getLocation() const
+{
+ if (pseudo_cell) {
+ return pseudo_cell->getLocation();
+ } else {
+ NPNR_ASSERT(bel != BelId());
+ return ctx->getBelLocation(bel);
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/common/kernel/nextpnr_types.h b/common/kernel/nextpnr_types.h
index c21182cc..e4042aec 100644
--- a/common/kernel/nextpnr_types.h
+++ b/common/kernel/nextpnr_types.h
@@ -160,6 +160,59 @@ struct PortInfo
struct Context;
+enum TimingPortClass
+{
+ TMG_CLOCK_INPUT, // Clock input to a sequential cell
+ TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc)
+ TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too)
+ TMG_REGISTER_OUTPUT, // Output from a register
+ TMG_COMB_INPUT, // Combinational input, no paths end here
+ TMG_COMB_OUTPUT, // Combinational output, no paths start here
+ TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output
+ TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input
+ TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis
+};
+
+enum ClockEdge
+{
+ RISING_EDGE,
+ FALLING_EDGE
+};
+
+struct TimingClockingInfo
+{
+ IdString clock_port; // Port name of clock domain
+ ClockEdge edge;
+ DelayPair setup, hold; // Input timing checks
+ DelayQuad clockToQ; // Output clock-to-Q time
+};
+
+struct PseudoCell
+{
+ virtual Loc getLocation() const = 0;
+ virtual WireId getPortWire(IdString port) const = 0;
+
+ virtual bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const = 0;
+ virtual TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const = 0;
+ virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const = 0;
+ virtual ~PseudoCell(){};
+};
+
+struct RegionPlug : PseudoCell
+{
+ RegionPlug(Loc loc) : loc(loc){}; // 'loc' is a notional location for the placer only
+ Loc getLocation() const override { return loc; }
+ WireId getPortWire(IdString port) const override { return port_wires.at(port); }
+
+ // TODO: partial reconfiguration region timing
+ bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const { return false; }
+ TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const { return TMG_IGNORE; }
+ virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const { return TimingClockingInfo{}; }
+
+ dict<IdString, WireId> port_wires;
+ Loc loc;
+};
+
struct CellInfo : ArchCellInfo
{
CellInfo(Context *ctx, IdString name, IdString type) : ctx(ctx), name(name), type(type){};
@@ -179,6 +232,8 @@ struct CellInfo : ArchCellInfo
Region *region = nullptr;
+ std::unique_ptr<PseudoCell> pseudo_cell{};
+
void addInput(IdString name);
void addOutput(IdString name);
void addInout(IdString name);
@@ -190,6 +245,10 @@ struct CellInfo : ArchCellInfo
// check whether a bel complies with the cell's region constraint
bool testRegion(BelId bel) const;
+ bool isPseudo() const { return bool(pseudo_cell); }
+
+ Loc getLocation() const;
+
NetInfo *getPort(IdString name)
{
auto found = ports.find(name);
@@ -212,33 +271,6 @@ struct CellInfo : ArchCellInfo
int new_offset, bool new_brackets, int width);
};
-enum TimingPortClass
-{
- TMG_CLOCK_INPUT, // Clock input to a sequential cell
- TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc)
- TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too)
- TMG_REGISTER_OUTPUT, // Output from a register
- TMG_COMB_INPUT, // Combinational input, no paths end here
- TMG_COMB_OUTPUT, // Combinational output, no paths start here
- TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output
- TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input
- TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis
-};
-
-enum ClockEdge
-{
- RISING_EDGE,
- FALLING_EDGE
-};
-
-struct TimingClockingInfo
-{
- IdString clock_port; // Port name of clock domain
- ClockEdge edge;
- DelayPair setup, hold; // Input timing checks
- DelayQuad clockToQ; // Output clock-to-Q time
-};
-
struct ClockConstraint
{
DelayPair high;
diff --git a/common/place/detail_place_core.cc b/common/place/detail_place_core.cc
index 7e629f24..18118fc8 100644
--- a/common/place/detail_place_core.cc
+++ b/common/place/detail_place_core.cc
@@ -37,6 +37,8 @@ PlacePartition::PlacePartition(Context *ctx)
x1 = 0;
y1 = 0;
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
Loc l = ctx->getBelLocation(cell.second->bel);
x0 = std::min(x0, l.x);
x1 = std::max(x1, l.x);
@@ -110,6 +112,8 @@ NetBB NetBB::compute(const Context *ctx, const NetInfo *net, const dict<IdString
if (!net->driver.cell)
return result;
auto bel_loc = [&](const CellInfo *cell) {
+ if (cell->isPseudo())
+ return cell->getLocation();
BelId bel = cell2bel ? cell2bel->at(cell->name) : cell->bel;
return ctx->getBelLocation(bel);
};
@@ -176,10 +180,12 @@ void DetailPlacerThreadState::set_partition(const PlacePartition &part)
// Set up the original cell-bel map for all nets inside the thread
local_cell2bel.clear();
for (NetInfo *net : thread_nets) {
- if (net->driver.cell)
+ if (net->driver.cell && !net->driver.cell->isPseudo())
local_cell2bel[net->driver.cell->name] = net->driver.cell->bel;
- for (auto &usr : net->users)
- local_cell2bel[usr.cell->name] = usr.cell->bel;
+ for (auto &usr : net->users) {
+ if (!usr.cell->isPseudo())
+ local_cell2bel[usr.cell->name] = usr.cell->bel;
+ }
}
}
diff --git a/common/place/parallel_refine.cc b/common/place/parallel_refine.cc
index de71b8e0..0fb99be5 100644
--- a/common/place/parallel_refine.cc
+++ b/common/place/parallel_refine.cc
@@ -390,6 +390,8 @@ struct ParallelRefine
// Setup fast bels map
pool<IdString> cell_types_in_use;
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
IdString cell_type = cell.second->type;
cell_types_in_use.insert(cell_type);
if (cell.second->cluster != ClusterId())
diff --git a/common/place/place_common.cc b/common/place/place_common.cc
index e03fca55..c2fc3b7d 100644
--- a/common/place/place_common.cc
+++ b/common/place/place_common.cc
@@ -293,6 +293,8 @@ class ConstraintLegaliseWorker
{
if (cell->cluster != ClusterId() && ctx->getClusterRootCell(cell->cluster) != cell)
return true; // Only process chain roots
+ if (cell->isPseudo())
+ return true;
if (constraints_satisfied(cell)) {
if (cell->cluster != ClusterId())
lockdown_chain(cell);
@@ -415,7 +417,7 @@ class ConstraintLegaliseWorker
{
log_info("Legalising relative constraints...\n");
for (auto &cell : ctx->cells) {
- oldLocations[cell.first] = ctx->getBelLocation(cell.second->bel);
+ oldLocations[cell.first] = cell.second->getLocation();
}
for (auto &cell : ctx->cells) {
bool res = legalise_cell(cell.second.get());
@@ -448,6 +450,8 @@ bool legalise_relative_constraints(Context *ctx) { return ConstraintLegaliseWork
int get_constraints_distance(const Context *ctx, const CellInfo *cell)
{
int dist = 0;
+ if (cell->isPseudo())
+ return 0;
if (cell->bel == BelId())
return 100000;
Loc loc = ctx->getBelLocation(cell->bel);
diff --git a/common/place/placer1.cc b/common/place/placer1.cc
index a6ba3895..23264ce2 100644
--- a/common/place/placer1.cc
+++ b/common/place/placer1.cc
@@ -76,6 +76,8 @@ class SAPlacer
pool<IdString> cell_types_in_use;
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
IdString cell_type = cell.second->type;
cell_types_in_use.insert(cell_type);
}
@@ -120,7 +122,7 @@ class SAPlacer
}
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
- if (ci->cluster == ClusterId())
+ if (ci->isPseudo() || ci->cluster == ClusterId())
continue;
cluster2cell[ci->cluster].push_back(ci);
}
@@ -145,6 +147,8 @@ class SAPlacer
// Initial constraints placer
for (auto &cell_entry : ctx->cells) {
CellInfo *cell = cell_entry.second.get();
+ if (cell->isPseudo())
+ continue;
auto loc = cell->attrs.find(ctx->id("BEL"));
if (loc != cell->attrs.end()) {
std::string loc_name = loc->second.as_string();
@@ -187,7 +191,7 @@ class SAPlacer
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
- if (ci->bel == BelId()) {
+ if (!ci->isPseudo() && (ci->bel == BelId())) {
autoplaced.push_back(cell.second.get());
}
}
@@ -217,7 +221,7 @@ class SAPlacer
} else {
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
- if (ci->belStrength > STRENGTH_STRONG) {
+ if (ci->isPseudo() || ci->belStrength > STRENGTH_STRONG) {
continue;
} else if (ci->cluster != ClusterId()) {
if (ctx->getClusterRootCell(ci->cluster) == ci)
@@ -353,6 +357,8 @@ class SAPlacer
autoplaced.clear();
chain_basis.clear();
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
if (cell.second->belStrength <= STRENGTH_STRONG && cell.second->cluster != ClusterId() &&
ctx->getClusterRootCell(cell.second->cluster) == cell.second.get())
chain_basis.push_back(cell.second.get());
@@ -814,7 +820,7 @@ class SAPlacer
{
BoundingBox bb;
NPNR_ASSERT(net->driver.cell != nullptr);
- Loc dloc = ctx->getBelLocation(net->driver.cell->bel);
+ Loc dloc = net->driver.cell->getLocation();
bb.x0 = dloc.x;
bb.x1 = dloc.x;
bb.y0 = dloc.y;
@@ -824,9 +830,9 @@ class SAPlacer
bb.ny0 = 1;
bb.ny1 = 1;
for (auto user : net->users) {
- if (user.cell->bel == BelId())
+ if (!user.cell->isPseudo() && user.cell->bel == BelId())
continue;
- Loc uloc = ctx->getBelLocation(user.cell->bel);
+ Loc uloc = user.cell->getLocation();
if (bb.x0 == uloc.x)
++bb.nx0;
else if (uloc.x < bb.x0) {
@@ -1173,7 +1179,7 @@ class SAPlacer
nets_by_tile.resize(max_x + 1, std::vector<dict<IdString, int>>(max_y + 1));
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
- if (int(ci->ports.size()) > large_cell_thresh)
+ if (ci->isPseudo() || (int(ci->ports.size()) > large_cell_thresh))
continue;
Loc loc = ctx->getBelLocation(ci->bel);
auto &nbt = nets_by_tile.at(loc.x).at(loc.y);
diff --git a/common/place/placer_heap.cc b/common/place/placer_heap.cc
index 4c9ffb23..bd8cd37d 100644
--- a/common/place/placer_heap.cc
+++ b/common/place/placer_heap.cc
@@ -147,7 +147,7 @@ class HeAPPlacer
tmg.setup();
for (auto &cell : ctx->cells)
- if (cell.second->cluster != ClusterId())
+ if (!cell.second->isPseudo() && cell.second->cluster != ClusterId())
cluster2cells[cell.second->cluster].push_back(cell.second.get());
}
@@ -284,6 +284,8 @@ class HeAPPlacer
// Save solution
solution.clear();
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
solution.emplace_back(cell.second.get(), cell.second->bel, cell.second->belStrength);
}
} else {
@@ -312,6 +314,8 @@ class HeAPPlacer
}
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
if (cell.second->bel == BelId())
log_error("Found unbound cell %s\n", cell.first.c_str(ctx));
if (ctx->getBoundBelCell(cell.second->bel) != cell.second.get())
@@ -411,7 +415,8 @@ class HeAPPlacer
// Initial constraints placer
for (auto &cell_entry : ctx->cells) {
CellInfo *cell = cell_entry.second.get();
-
+ if (cell->isPseudo())
+ continue;
auto loc = cell->attrs.find(ctx->id("BEL"));
if (loc != cell->attrs.end()) {
std::string loc_name = loc->second.as_string();
@@ -461,6 +466,8 @@ class HeAPPlacer
pool<IdString> cell_types_in_use;
pool<BelBucketId> buckets_in_use;
for (auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
IdString cell_type = cell.second->type;
cell_types_in_use.insert(cell_type);
BelBucketId bucket = ctx->getBelBucketForCellType(cell_type);
@@ -527,6 +534,8 @@ class HeAPPlacer
{
pool<IdString> cell_types;
for (const auto &cell : ctx->cells) {
+ if (cell.second->isPseudo())
+ continue;
cell_types.insert(cell.second->type);
}
@@ -551,6 +560,14 @@ class HeAPPlacer
for (auto &cell : ctx->cells) {
CellInfo *ci = cell.second.get();
+ if (ci->isPseudo()) {
+ Loc loc = ci->pseudo_cell->getLocation();
+ cell_locs[cell.first].x = loc.x;
+ cell_locs[cell.first].y = loc.y;
+ cell_locs[cell.first].locked = true;
+ cell_locs[cell.first].global = false;
+ continue;
+ }
if (ci->bel != BelId()) {
Loc loc = ctx->getBelLocation(ci->bel);
cell_locs[cell.first].x = loc.x;
@@ -627,8 +644,9 @@ class HeAPPlacer
int row = 0;
solve_cells.clear();
// First clear the udata of all cells
- for (auto &cell : ctx->cells)
+ for (auto &cell : ctx->cells) {
cell.second->udata = dont_solve;
+ }
// Then update cells to be placed, which excludes cell children
for (auto cell : place_cells) {
if (buckets && !buckets->count(ctx->getBelBucketForCellType(cell->type)))
diff --git a/common/route/router2.cc b/common/route/router2.cc
index e943e493..1153f054 100644
--- a/common/route/router2.cc
+++ b/common/route/router2.cc
@@ -128,7 +128,7 @@ struct Router2
nets.at(i).cy = 0;
if (ni->driver.cell != nullptr) {
- Loc drv_loc = ctx->getBelLocation(ni->driver.cell->bel);
+ Loc drv_loc = ni->driver.cell->getLocation();
nets.at(i).cx += drv_loc.x;
nets.at(i).cy += drv_loc.y;
}
@@ -159,7 +159,7 @@ struct Router2
nets.at(i).bb.y1 = std::max(nets.at(i).bb.y1, ad.bb.y1);
}
// Add location to centroid sum
- Loc usr_loc = ctx->getBelLocation(usr.value.cell->bel);
+ Loc usr_loc = usr.value.cell->getLocation();
nets.at(i).cx += usr_loc.x;
nets.at(i).cy += usr_loc.y;
}