2018-06-27 18:00:13 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "place_common.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include "log.h"
|
|
|
|
#include "util.h"
|
2018-06-29 19:12:44 +08:00
|
|
|
|
2018-06-27 18:00:13 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
2018-07-15 01:52:56 +08:00
|
|
|
// Get the total estimated wirelength for a net
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns)
|
2018-07-15 01:52:56 +08:00
|
|
|
{
|
|
|
|
wirelen_t wirelength = 0;
|
|
|
|
CellInfo *driver_cell = net->driver.cell;
|
|
|
|
if (!driver_cell)
|
|
|
|
return 0;
|
|
|
|
if (driver_cell->bel == BelId())
|
|
|
|
return 0;
|
2018-11-11 12:05:36 +08:00
|
|
|
bool driver_gb = ctx->getBelGlobalBuf(driver_cell->bel);
|
2018-07-15 01:52:56 +08:00
|
|
|
if (driver_gb)
|
|
|
|
return 0;
|
2018-11-11 12:05:36 +08:00
|
|
|
IdString clock_port;
|
2018-11-14 01:26:28 +08:00
|
|
|
bool timing_driven = ctx->timing_driven && type == MetricType::COST && ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port) != TMG_IGNORE;
|
2018-08-01 07:23:35 +08:00
|
|
|
delay_t negative_slack = 0;
|
2018-07-31 09:55:22 +08:00
|
|
|
delay_t worst_slack = std::numeric_limits<delay_t>::max();
|
2018-11-11 12:05:36 +08:00
|
|
|
Loc driver_loc = ctx->getBelLocation(driver_cell->bel);
|
2018-07-24 21:45:49 +08:00
|
|
|
int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y;
|
2018-07-15 01:52:56 +08:00
|
|
|
for (auto load : net->users) {
|
|
|
|
if (load.cell == nullptr)
|
|
|
|
continue;
|
|
|
|
CellInfo *load_cell = load.cell;
|
|
|
|
if (load_cell->bel == BelId())
|
|
|
|
continue;
|
2018-11-14 01:26:28 +08:00
|
|
|
if (timing_driven) {
|
2018-07-31 10:19:30 +08:00
|
|
|
delay_t net_delay = ctx->predictDelay(net, load);
|
|
|
|
auto slack = load.budget - net_delay;
|
2018-07-15 01:52:56 +08:00
|
|
|
if (slack < 0)
|
2018-08-01 07:23:35 +08:00
|
|
|
negative_slack += slack;
|
2018-07-15 01:52:56 +08:00
|
|
|
worst_slack = std::min(slack, worst_slack);
|
|
|
|
}
|
|
|
|
|
2018-07-24 21:45:49 +08:00
|
|
|
if (ctx->getBelGlobalBuf(load_cell->bel))
|
2018-07-15 01:52:56 +08:00
|
|
|
continue;
|
2018-07-24 21:45:49 +08:00
|
|
|
Loc load_loc = ctx->getBelLocation(load_cell->bel);
|
|
|
|
|
|
|
|
xmin = std::min(xmin, load_loc.x);
|
|
|
|
ymin = std::min(ymin, load_loc.y);
|
|
|
|
xmax = std::max(xmax, load_loc.x);
|
|
|
|
ymax = std::max(ymax, load_loc.y);
|
2018-07-15 01:52:56 +08:00
|
|
|
}
|
2018-11-14 01:26:28 +08:00
|
|
|
if (timing_driven) {
|
2018-08-01 11:57:36 +08:00
|
|
|
wirelength = wirelen_t(
|
|
|
|
(((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-ctx->getDelayNS(worst_slack) / 5)))));
|
2018-07-15 01:52:56 +08:00
|
|
|
} else {
|
|
|
|
wirelength = wirelen_t((ymax - ymin) + (xmax - xmin));
|
|
|
|
}
|
|
|
|
|
2018-08-01 07:23:35 +08:00
|
|
|
tns += ctx->getDelayNS(negative_slack);
|
2018-07-15 01:52:56 +08:00
|
|
|
return wirelength;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the total wirelength for a cell
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelen_t get_cell_metric(const Context *ctx, const CellInfo *cell, MetricType type)
|
2018-07-15 01:52:56 +08:00
|
|
|
{
|
|
|
|
std::set<IdString> nets;
|
|
|
|
for (auto p : cell->ports) {
|
|
|
|
if (p.second.net)
|
|
|
|
nets.insert(p.second.net->name);
|
|
|
|
}
|
|
|
|
wirelen_t wirelength = 0;
|
|
|
|
float tns = 0;
|
|
|
|
for (auto n : nets) {
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelength += get_net_metric(ctx, ctx->nets.at(n).get(), type, tns);
|
2018-07-15 01:52:56 +08:00
|
|
|
}
|
|
|
|
return wirelength;
|
|
|
|
}
|
|
|
|
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelen_t get_cell_metric_at_bel(const Context *ctx, CellInfo *cell, BelId bel, MetricType type)
|
2018-07-15 01:52:56 +08:00
|
|
|
{
|
|
|
|
BelId oldBel = cell->bel;
|
|
|
|
cell->bel = bel;
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelen_t wirelen = get_cell_metric(ctx, cell, type);
|
2018-07-15 01:52:56 +08:00
|
|
|
cell->bel = oldBel;
|
|
|
|
return wirelen;
|
|
|
|
}
|
|
|
|
|
2018-06-27 18:18:52 +08:00
|
|
|
// Placing a single cell
|
2018-07-15 01:52:56 +08:00
|
|
|
bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
|
2018-06-27 18:18:52 +08:00
|
|
|
{
|
|
|
|
bool all_placed = false;
|
|
|
|
int iters = 25;
|
|
|
|
while (!all_placed) {
|
|
|
|
BelId best_bel = BelId();
|
|
|
|
wirelen_t best_wirelen = std::numeric_limits<wirelen_t>::max(),
|
|
|
|
best_ripup_wirelen = std::numeric_limits<wirelen_t>::max();
|
|
|
|
CellInfo *ripup_target = nullptr;
|
|
|
|
BelId ripup_bel = BelId();
|
|
|
|
if (cell->bel != BelId()) {
|
2018-07-15 01:53:08 +08:00
|
|
|
ctx->unbindBel(cell->bel);
|
2018-06-27 18:18:52 +08:00
|
|
|
}
|
2018-08-08 23:01:18 +08:00
|
|
|
IdString targetType = cell->type;
|
2018-06-27 18:18:52 +08:00
|
|
|
for (auto bel : ctx->getBels()) {
|
2018-07-15 01:52:56 +08:00
|
|
|
if (ctx->getBelType(bel) == targetType && (!require_legality || ctx->isValidBelForCell(cell, bel))) {
|
2018-07-15 01:53:08 +08:00
|
|
|
if (ctx->checkBelAvail(bel)) {
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelen_t wirelen = get_cell_metric_at_bel(ctx, cell, bel, MetricType::COST);
|
2018-06-29 19:28:06 +08:00
|
|
|
if (iters >= 4)
|
|
|
|
wirelen += ctx->rng(25);
|
2018-06-27 18:18:52 +08:00
|
|
|
if (wirelen <= best_wirelen) {
|
|
|
|
best_wirelen = wirelen;
|
|
|
|
best_bel = bel;
|
|
|
|
}
|
|
|
|
} else {
|
2018-07-16 23:13:40 +08:00
|
|
|
wirelen_t wirelen = get_cell_metric_at_bel(ctx, cell, bel, MetricType::COST);
|
2018-06-29 19:28:06 +08:00
|
|
|
if (iters >= 4)
|
|
|
|
wirelen += ctx->rng(25);
|
2018-06-27 18:18:52 +08:00
|
|
|
if (wirelen <= best_ripup_wirelen) {
|
2018-08-05 21:25:42 +08:00
|
|
|
CellInfo *curr_cell = ctx->getBoundBelCell(bel);
|
2018-07-21 22:45:46 +08:00
|
|
|
if (curr_cell->belStrength < STRENGTH_STRONG) {
|
2018-06-27 18:18:52 +08:00
|
|
|
best_ripup_wirelen = wirelen;
|
|
|
|
ripup_bel = bel;
|
2018-07-21 22:45:46 +08:00
|
|
|
ripup_target = curr_cell;
|
2018-06-27 18:18:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (best_bel == BelId()) {
|
2018-06-29 19:12:44 +08:00
|
|
|
if (iters == 0) {
|
|
|
|
log_error("failed to place cell '%s' of type '%s' (ripup iteration limit exceeded)\n",
|
|
|
|
cell->name.c_str(ctx), cell->type.c_str(ctx));
|
|
|
|
}
|
|
|
|
if (ripup_bel == BelId()) {
|
2018-06-27 18:18:52 +08:00
|
|
|
log_error("failed to place cell '%s' of type '%s'\n", cell->name.c_str(ctx), cell->type.c_str(ctx));
|
|
|
|
}
|
|
|
|
--iters;
|
2018-07-15 01:53:08 +08:00
|
|
|
ctx->unbindBel(ripup_target->bel);
|
2018-06-27 18:18:52 +08:00
|
|
|
best_bel = ripup_bel;
|
|
|
|
} else {
|
|
|
|
all_placed = true;
|
|
|
|
}
|
2018-08-03 23:07:26 +08:00
|
|
|
if (ctx->verbose)
|
2018-08-04 14:18:04 +08:00
|
|
|
log_info(" placed single cell '%s' at '%s'\n", cell->name.c_str(ctx),
|
|
|
|
ctx->getBelName(best_bel).c_str(ctx));
|
2018-08-05 21:25:42 +08:00
|
|
|
ctx->bindBel(best_bel, cell, STRENGTH_WEAK);
|
2018-06-27 18:18:52 +08:00
|
|
|
|
|
|
|
cell = ripup_target;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-03 19:18:16 +08:00
|
|
|
class ConstraintLegaliseWorker
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
Context *ctx;
|
2018-08-03 23:07:26 +08:00
|
|
|
std::set<IdString> rippedCells;
|
2018-08-03 20:48:40 +08:00
|
|
|
std::unordered_map<IdString, Loc> oldLocations;
|
2018-08-03 19:18:16 +08:00
|
|
|
class IncreasingDiameterSearch
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
IncreasingDiameterSearch() : start(0), min(0), max(-1){};
|
|
|
|
IncreasingDiameterSearch(int x) : start(x), min(x), max(x){};
|
|
|
|
IncreasingDiameterSearch(int start, int min, int max) : start(start), min(min), max(max){};
|
2018-08-03 20:48:40 +08:00
|
|
|
bool done() const { return (diameter > (max - min)); };
|
|
|
|
int get() const
|
2018-08-03 19:18:16 +08:00
|
|
|
{
|
|
|
|
int val = start + sign * diameter;
|
|
|
|
val = std::max(val, min);
|
|
|
|
val = std::min(val, max);
|
2018-08-03 20:48:40 +08:00
|
|
|
return val;
|
|
|
|
}
|
2018-08-03 19:18:16 +08:00
|
|
|
|
2018-08-03 22:29:44 +08:00
|
|
|
void next()
|
|
|
|
{
|
2018-08-03 19:18:16 +08:00
|
|
|
if (sign == 0) {
|
|
|
|
sign = 1;
|
|
|
|
diameter = 1;
|
|
|
|
} else if (sign == -1) {
|
|
|
|
sign = 1;
|
2018-08-03 21:00:32 +08:00
|
|
|
if ((start + sign * diameter) > max)
|
|
|
|
sign = -1;
|
2018-08-03 19:18:16 +08:00
|
|
|
++diameter;
|
|
|
|
} else {
|
|
|
|
sign = -1;
|
2018-08-03 22:16:20 +08:00
|
|
|
if ((start + sign * diameter) < min) {
|
2018-08-03 21:00:32 +08:00
|
|
|
sign = 1;
|
|
|
|
++diameter;
|
|
|
|
}
|
2018-08-03 19:18:16 +08:00
|
|
|
}
|
2018-08-03 20:48:40 +08:00
|
|
|
}
|
2018-08-03 19:18:16 +08:00
|
|
|
|
2018-08-03 22:29:44 +08:00
|
|
|
void reset()
|
|
|
|
{
|
2018-08-03 20:48:40 +08:00
|
|
|
sign = 0;
|
|
|
|
diameter = 0;
|
2018-08-03 19:18:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int start, min, max;
|
|
|
|
int diameter = 0;
|
|
|
|
int sign = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
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
|
2018-08-03 20:48:40 +08:00
|
|
|
bool valid_loc_for(const CellInfo *cell, Loc loc, CellLocations &solution, std::unordered_set<Loc> &usedLocations)
|
2018-08-03 19:18:16 +08:00
|
|
|
{
|
|
|
|
BelId locBel = ctx->getBelByLocation(loc);
|
2018-08-03 22:29:44 +08:00
|
|
|
if (locBel == BelId()) {
|
2018-08-03 19:18:16 +08:00
|
|
|
return false;
|
2018-08-03 22:29:44 +08:00
|
|
|
}
|
2018-08-08 23:01:18 +08:00
|
|
|
if (ctx->getBelType(locBel) != cell->type) {
|
2018-08-03 19:18:16 +08:00
|
|
|
return false;
|
2018-08-03 22:29:44 +08:00
|
|
|
}
|
2018-08-03 20:48:40 +08:00
|
|
|
if (!ctx->checkBelAvail(locBel)) {
|
2018-08-05 21:25:42 +08:00
|
|
|
CellInfo *confCell = ctx->getConflictingBelCell(locBel);
|
|
|
|
if (confCell->belStrength >= STRENGTH_STRONG) {
|
2018-08-03 20:48:40 +08:00
|
|
|
return false;
|
2018-08-03 22:29:44 +08:00
|
|
|
}
|
2018-08-03 20:48:40 +08:00
|
|
|
}
|
2018-10-11 18:50:56 +08:00
|
|
|
// 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;
|
|
|
|
}
|
2018-08-03 20:48:40 +08:00
|
|
|
usedLocations.insert(loc);
|
2018-08-03 19:18:16 +08:00
|
|
|
for (auto child : cell->constr_children) {
|
|
|
|
IncreasingDiameterSearch xSearch, ySearch, zSearch;
|
|
|
|
if (child->constr_x == child->UNCONSTR) {
|
2018-08-03 22:29:44 +08:00
|
|
|
xSearch = IncreasingDiameterSearch(loc.x, 0, ctx->getGridDimX() - 1);
|
2018-08-03 19:18:16 +08:00
|
|
|
} else {
|
|
|
|
xSearch = IncreasingDiameterSearch(loc.x + child->constr_x);
|
|
|
|
}
|
|
|
|
if (child->constr_y == child->UNCONSTR) {
|
2018-08-03 22:29:44 +08:00
|
|
|
ySearch = IncreasingDiameterSearch(loc.y, 0, ctx->getGridDimY() - 1);
|
2018-08-03 19:18:16 +08:00
|
|
|
} else {
|
|
|
|
ySearch = IncreasingDiameterSearch(loc.y + child->constr_y);
|
|
|
|
}
|
|
|
|
if (child->constr_z == child->UNCONSTR) {
|
2018-08-08 16:27:08 +08:00
|
|
|
zSearch = IncreasingDiameterSearch(loc.z, 0, ctx->getTileBelDimZ(loc.x, loc.y));
|
2018-08-03 19:18:16 +08:00
|
|
|
} else {
|
|
|
|
if (child->constr_abs_z) {
|
|
|
|
zSearch = IncreasingDiameterSearch(child->constr_z);
|
|
|
|
} else {
|
|
|
|
zSearch = IncreasingDiameterSearch(loc.z + child->constr_z);
|
|
|
|
}
|
|
|
|
}
|
2018-08-03 21:25:43 +08:00
|
|
|
bool success = false;
|
2018-08-03 21:02:42 +08:00
|
|
|
while (!xSearch.done()) {
|
2018-08-03 19:18:16 +08:00
|
|
|
Loc cloc;
|
2018-08-03 20:48:40 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 21:02:42 +08:00
|
|
|
if (usedLocations.count(cloc))
|
2018-08-03 20:48:40 +08:00
|
|
|
continue;
|
2018-08-03 21:25:43 +08:00
|
|
|
if (valid_loc_for(child, cloc, solution, usedLocations)) {
|
|
|
|
success = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!success) {
|
|
|
|
usedLocations.erase(loc);
|
|
|
|
return false;
|
2018-08-03 19:18:16 +08:00
|
|
|
}
|
|
|
|
}
|
2018-08-03 20:48:40 +08:00
|
|
|
if (solution.count(cell->name))
|
|
|
|
usedLocations.erase(solution.at(cell->name));
|
2018-08-03 19:18:16 +08:00
|
|
|
solution[cell->name] = loc;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-03 20:48:40 +08:00
|
|
|
// Set the strength to locked on all cells in chain
|
2018-08-03 22:29:44 +08:00
|
|
|
void lockdown_chain(CellInfo *root)
|
|
|
|
{
|
2018-08-03 20:48:40 +08:00
|
|
|
root->belStrength = STRENGTH_LOCKED;
|
|
|
|
for (auto child : root->constr_children)
|
|
|
|
lockdown_chain(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Legalise placement constraints on a cell
|
2018-08-03 22:29:44 +08:00
|
|
|
bool legalise_cell(CellInfo *cell)
|
|
|
|
{
|
2018-08-03 20:48:40 +08:00
|
|
|
if (cell->constr_parent != nullptr)
|
|
|
|
return true; // Only process chain roots
|
|
|
|
if (constraints_satisfied(cell)) {
|
2018-08-03 22:29:44 +08:00
|
|
|
if (cell->constr_children.size() > 0 || cell->constr_x != cell->UNCONSTR ||
|
|
|
|
cell->constr_y != cell->UNCONSTR || cell->constr_z != cell->UNCONSTR)
|
|
|
|
lockdown_chain(cell);
|
2018-08-03 20:48:40 +08:00
|
|
|
} else {
|
|
|
|
IncreasingDiameterSearch xRootSearch, yRootSearch, zRootSearch;
|
|
|
|
Loc currentLoc;
|
|
|
|
if (cell->bel != BelId())
|
|
|
|
currentLoc = ctx->getBelLocation(cell->bel);
|
2018-08-03 22:16:20 +08:00
|
|
|
else
|
|
|
|
currentLoc = oldLocations[cell->name];
|
2018-08-03 20:48:40 +08:00
|
|
|
if (cell->constr_x == cell->UNCONSTR)
|
|
|
|
xRootSearch = IncreasingDiameterSearch(currentLoc.x, 0, ctx->getGridDimX() - 1);
|
2018-08-03 21:40:01 +08:00
|
|
|
else
|
|
|
|
xRootSearch = IncreasingDiameterSearch(cell->constr_x);
|
|
|
|
|
2018-08-03 20:48:40 +08:00
|
|
|
if (cell->constr_y == cell->UNCONSTR)
|
|
|
|
yRootSearch = IncreasingDiameterSearch(currentLoc.y, 0, ctx->getGridDimY() - 1);
|
2018-08-03 21:40:01 +08:00
|
|
|
else
|
|
|
|
yRootSearch = IncreasingDiameterSearch(cell->constr_y);
|
|
|
|
|
2018-08-03 20:48:40 +08:00
|
|
|
if (cell->constr_z == cell->UNCONSTR)
|
2018-09-30 22:13:18 +08:00
|
|
|
zRootSearch =
|
|
|
|
IncreasingDiameterSearch(currentLoc.z, 0, ctx->getTileBelDimZ(currentLoc.x, currentLoc.y));
|
2018-08-03 21:40:01 +08:00
|
|
|
else
|
|
|
|
zRootSearch = IncreasingDiameterSearch(cell->constr_z);
|
2018-08-03 21:02:42 +08:00
|
|
|
while (!xRootSearch.done()) {
|
2018-08-03 20:48:40 +08:00
|
|
|
Loc rootLoc;
|
2018-08-03 22:16:20 +08:00
|
|
|
|
2018-08-03 20:48:40 +08:00
|
|
|
rootLoc.x = xRootSearch.get();
|
|
|
|
rootLoc.y = yRootSearch.get();
|
|
|
|
rootLoc.z = zRootSearch.get();
|
|
|
|
zRootSearch.next();
|
|
|
|
if (zRootSearch.done()) {
|
|
|
|
zRootSearch.reset();
|
|
|
|
yRootSearch.next();
|
|
|
|
if (yRootSearch.done()) {
|
|
|
|
yRootSearch.reset();
|
|
|
|
xRootSearch.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CellLocations solution;
|
|
|
|
std::unordered_set<Loc> used;
|
|
|
|
if (valid_loc_for(cell, rootLoc, solution, used)) {
|
|
|
|
for (auto cp : solution) {
|
2018-08-03 21:25:43 +08:00
|
|
|
// First unbind all cells
|
|
|
|
if (ctx->cells.at(cp.first)->bel != BelId())
|
|
|
|
ctx->unbindBel(ctx->cells.at(cp.first)->bel);
|
|
|
|
}
|
|
|
|
for (auto cp : solution) {
|
|
|
|
if (ctx->verbose)
|
2018-08-03 22:29:44 +08:00
|
|
|
log_info(" placing '%s' at (%d, %d, %d)\n", cp.first.c_str(ctx), cp.second.x,
|
|
|
|
cp.second.y, cp.second.z);
|
2018-08-03 20:48:40 +08:00
|
|
|
BelId target = ctx->getBelByLocation(cp.second);
|
2018-08-03 21:25:43 +08:00
|
|
|
if (!ctx->checkBelAvail(target)) {
|
2018-08-05 21:25:42 +08:00
|
|
|
CellInfo *confl_cell = ctx->getConflictingBelCell(target);
|
|
|
|
if (confl_cell != nullptr) {
|
2018-08-03 21:25:43 +08:00
|
|
|
if (ctx->verbose)
|
2018-08-05 21:25:42 +08:00
|
|
|
log_info(" '%s' already placed at '%s'\n", ctx->nameOf(confl_cell),
|
2018-08-03 22:29:44 +08:00
|
|
|
ctx->getBelName(confl_cell->bel).c_str(ctx));
|
2018-08-03 21:25:43 +08:00
|
|
|
NPNR_ASSERT(confl_cell->belStrength < STRENGTH_STRONG);
|
|
|
|
ctx->unbindBel(target);
|
2018-08-05 21:25:42 +08:00
|
|
|
rippedCells.insert(confl_cell->name);
|
2018-08-03 21:25:43 +08:00
|
|
|
}
|
2018-08-03 20:48:40 +08:00
|
|
|
}
|
2018-08-05 21:25:42 +08:00
|
|
|
ctx->bindBel(target, ctx->cells.at(cp.first).get(), STRENGTH_LOCKED);
|
2018-08-03 23:07:26 +08:00
|
|
|
rippedCells.erase(cp.first);
|
2018-08-03 20:48:40 +08:00
|
|
|
}
|
2018-08-07 17:01:48 +08:00
|
|
|
for (auto cp : solution) {
|
|
|
|
for (auto bel : ctx->getBelsByTile(cp.second.x, cp.second.y)) {
|
|
|
|
CellInfo *belCell = ctx->getBoundBelCell(bel);
|
|
|
|
if (belCell != nullptr && !solution.count(belCell->name)) {
|
|
|
|
if (!ctx->isValidBelForCell(belCell, bel)) {
|
|
|
|
NPNR_ASSERT(belCell->belStrength < STRENGTH_STRONG);
|
|
|
|
ctx->unbindBel(bel);
|
|
|
|
rippedCells.insert(belCell->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-03 21:40:01 +08:00
|
|
|
NPNR_ASSERT(constraints_satisfied(cell));
|
2018-08-03 20:48:40 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-03 19:18:16 +08:00
|
|
|
// Check if constraints are currently satisfied on a cell and its children
|
|
|
|
bool constraints_satisfied(const CellInfo *cell) { return get_constraints_distance(ctx, cell) == 0; }
|
2018-08-03 20:48:40 +08:00
|
|
|
|
2018-08-03 22:29:44 +08:00
|
|
|
public:
|
|
|
|
ConstraintLegaliseWorker(Context *ctx) : ctx(ctx){};
|
2018-08-03 22:16:20 +08:00
|
|
|
|
2018-08-03 22:29:44 +08:00
|
|
|
void print_chain(CellInfo *cell, int depth = 0)
|
|
|
|
{
|
2018-08-03 22:16:20 +08:00
|
|
|
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)
|
2018-08-03 22:29:44 +08:00
|
|
|
print_chain(child, depth + 1);
|
2018-08-03 22:16:20 +08:00
|
|
|
}
|
|
|
|
|
2018-08-04 00:14:09 +08:00
|
|
|
void print_stats(const char *point)
|
|
|
|
{
|
|
|
|
float distance_sum = 0;
|
|
|
|
float max_distance = 0;
|
|
|
|
int moved_cells = 0;
|
|
|
|
int unplaced_cells = 0;
|
|
|
|
for (auto orig : oldLocations) {
|
|
|
|
if (ctx->cells.at(orig.first)->bel == BelId()) {
|
|
|
|
unplaced_cells++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Loc newLoc = ctx->getBelLocation(ctx->cells.at(orig.first)->bel);
|
|
|
|
if (newLoc != orig.second) {
|
|
|
|
float distance = std::sqrt(std::pow(newLoc.x - orig.second.x, 2) + pow(newLoc.y - orig.second.y, 2));
|
|
|
|
moved_cells++;
|
|
|
|
distance_sum += distance;
|
|
|
|
if (distance > max_distance)
|
|
|
|
max_distance = distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log_info(" moved %d cells, %d unplaced (after %s)\n", moved_cells, unplaced_cells, point);
|
|
|
|
if (moved_cells > 0) {
|
|
|
|
log_info(" average distance %f\n", (distance_sum / moved_cells));
|
|
|
|
log_info(" maximum distance %f\n", max_distance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-03 22:29:44 +08:00
|
|
|
bool legalise_constraints()
|
|
|
|
{
|
2018-08-03 20:48:40 +08:00
|
|
|
log_info("Legalising relative constraints...\n");
|
|
|
|
for (auto cell : sorted(ctx->cells)) {
|
|
|
|
oldLocations[cell.first] = ctx->getBelLocation(cell.second->bel);
|
|
|
|
}
|
|
|
|
for (auto cell : sorted(ctx->cells)) {
|
|
|
|
bool res = legalise_cell(cell.second);
|
|
|
|
if (!res) {
|
2018-08-03 22:29:44 +08:00
|
|
|
if (ctx->verbose)
|
2018-08-03 22:16:20 +08:00
|
|
|
print_chain(cell.second);
|
2018-08-03 20:48:40 +08:00
|
|
|
log_error("failed to place chain starting at cell '%s'\n", cell.first.c_str(ctx));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-08-04 00:14:09 +08:00
|
|
|
print_stats("after legalising chains");
|
2018-08-03 20:48:40 +08:00
|
|
|
for (auto rippedCell : rippedCells) {
|
2018-08-03 23:07:26 +08:00
|
|
|
bool res = place_single_cell(ctx, ctx->cells.at(rippedCell).get(), true);
|
2018-08-03 20:48:40 +08:00
|
|
|
if (!res) {
|
2018-08-04 14:18:04 +08:00
|
|
|
log_error("failed to place cell '%s' after relative constraint legalisation\n", rippedCell.c_str(ctx));
|
2018-08-03 20:48:40 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-08-04 00:14:09 +08:00
|
|
|
print_stats("after replacing ripped up cells");
|
2018-08-03 22:59:45 +08:00
|
|
|
for (auto cell : sorted(ctx->cells))
|
|
|
|
if (get_constraints_distance(ctx, cell.second) != 0)
|
|
|
|
log_error("constraint satisfaction check failed for cell '%s' at Bel '%s'\n", cell.first.c_str(ctx),
|
|
|
|
ctx->getBelName(cell.second->bel).c_str(ctx));
|
2018-08-03 20:48:40 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-08-03 19:18:16 +08:00
|
|
|
};
|
|
|
|
|
2018-08-03 22:29:44 +08:00
|
|
|
bool legalise_relative_constraints(Context *ctx) { return ConstraintLegaliseWorker(ctx).legalise_constraints(); }
|
2018-08-03 20:48:40 +08:00
|
|
|
|
2018-08-03 19:18:16 +08:00
|
|
|
// Get the total distance from satisfied constraints for a cell
|
|
|
|
int get_constraints_distance(const Context *ctx, const CellInfo *cell)
|
|
|
|
{
|
|
|
|
int dist = 0;
|
2018-08-03 22:29:44 +08:00
|
|
|
if (cell->bel == BelId())
|
2018-08-03 20:48:40 +08:00
|
|
|
return 100000;
|
2018-08-03 19:18:16 +08:00
|
|
|
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 {
|
2018-08-03 22:29:44 +08:00
|
|
|
if (cell->constr_parent->bel == BelId())
|
2018-08-03 20:48:40 +08:00
|
|
|
return 100000;
|
2018-08-03 19:18:16 +08:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto child : cell->constr_children)
|
|
|
|
dist += get_constraints_distance(ctx, child);
|
|
|
|
return dist;
|
|
|
|
}
|
|
|
|
|
2018-06-27 18:00:13 +08:00
|
|
|
NEXTPNR_NAMESPACE_END
|