Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr into q3k/lock-2-electric-boogaloo
This commit is contained in:
commit
6588aafdb8
@ -31,6 +31,8 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#ifndef NEXTPNR_H
|
||||
#define NEXTPNR_H
|
||||
|
||||
@ -162,8 +164,30 @@ struct GraphicElement
|
||||
std::string text;
|
||||
};
|
||||
|
||||
struct Loc
|
||||
{
|
||||
int x = -1, y = -1, z = -1;
|
||||
|
||||
bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
|
||||
bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); }
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX Loc>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, hash<int>()(obj.x));
|
||||
boost::hash_combine(seed, hash<int>()(obj.y));
|
||||
boost::hash_combine(seed, hash<int>()(obj.z));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
#include "archdefs.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
@ -130,10 +130,11 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
|
||||
if (iters >= 4)
|
||||
wirelen += ctx->rng(25);
|
||||
if (wirelen <= best_ripup_wirelen) {
|
||||
ripup_target = ctx->cells.at(ctx->getBoundBelCell(bel)).get();
|
||||
if (ripup_target->belStrength < STRENGTH_STRONG) {
|
||||
CellInfo *curr_cell = ctx->cells.at(ctx->getBoundBelCell(bel)).get();
|
||||
if (curr_cell->belStrength < STRENGTH_STRONG) {
|
||||
best_ripup_wirelen = wirelen;
|
||||
ripup_bel = bel;
|
||||
ripup_target = curr_cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,9 @@ struct RipupScoreboard
|
||||
|
||||
void ripup_net(Context *ctx, IdString net_name)
|
||||
{
|
||||
if (ctx->debug)
|
||||
log("Ripping up all routing for net %s.\n", net_name.c_str(ctx));
|
||||
|
||||
auto net_info = ctx->nets.at(net_name).get();
|
||||
std::vector<PipId> pips;
|
||||
std::vector<WireId> wires;
|
||||
@ -143,11 +146,13 @@ struct Router
|
||||
thisVisitCntLimit = (thisVisitCnt * 3) / 2;
|
||||
|
||||
for (auto pip : ctx->getPipsDownhill(qw.wire)) {
|
||||
delay_t next_delay = qw.delay + ctx->getPipDelay(pip).avgDelay();
|
||||
delay_t next_delay = qw.delay + ctx->getPipDelay(pip).maxDelay();
|
||||
WireId next_wire = ctx->getPipDstWire(pip);
|
||||
bool foundRipupNet = false;
|
||||
thisVisitCnt++;
|
||||
|
||||
next_delay += ctx->getWireDelay(next_wire).maxDelay();
|
||||
|
||||
if (!ctx->checkWireAvail(next_wire)) {
|
||||
if (!ripup)
|
||||
continue;
|
||||
@ -226,7 +231,7 @@ struct Router
|
||||
: ctx(ctx), scores(scores), ripup(ripup), ripup_penalty(ripup_penalty)
|
||||
{
|
||||
std::unordered_map<WireId, delay_t> src_wires;
|
||||
src_wires[src_wire] = 0;
|
||||
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
|
||||
route(src_wires, dst_wire);
|
||||
routedOkay = visited.count(dst_wire);
|
||||
|
||||
@ -246,7 +251,7 @@ struct Router
|
||||
}
|
||||
}
|
||||
|
||||
Router(Context *ctx, RipupScoreboard &scores, IdString net_name, bool ripup = false, delay_t ripup_penalty = 0)
|
||||
Router(Context *ctx, RipupScoreboard &scores, IdString net_name, int user_idx = -1, bool reroute = false, bool ripup = false, delay_t ripup_penalty = 0)
|
||||
: ctx(ctx), scores(scores), net_name(net_name), ripup(ripup), ripup_penalty(ripup_penalty)
|
||||
{
|
||||
auto net_info = ctx->nets.at(net_name).get();
|
||||
@ -284,13 +289,93 @@ struct Router
|
||||
log(" Source wire: %s\n", ctx->getWireName(src_wire).c_str(ctx));
|
||||
|
||||
std::unordered_map<WireId, delay_t> src_wires;
|
||||
src_wires[src_wire] = 0;
|
||||
std::vector<PortRef> users_array;
|
||||
|
||||
ripup_net(ctx, net_name);
|
||||
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
|
||||
if (user_idx < 0) {
|
||||
// route all users
|
||||
users_array = net_info->users;
|
||||
ctx->shuffle(users_array);
|
||||
} else {
|
||||
// route only the selected user
|
||||
users_array.push_back(net_info->users[user_idx]);
|
||||
}
|
||||
|
||||
std::vector<PortRef> users_array = net_info->users;
|
||||
ctx->shuffle(users_array);
|
||||
if (reroute) {
|
||||
// complete ripup
|
||||
ripup_net(ctx, net_name);
|
||||
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
|
||||
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
|
||||
} else {
|
||||
// re-use existing routes as much as possible
|
||||
if (net_info->wires.count(src_wire) == 0)
|
||||
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
|
||||
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
|
||||
|
||||
for (auto &user_it : net_info->users) {
|
||||
auto dst_bel = user_it.cell->bel;
|
||||
|
||||
if (dst_bel == BelId())
|
||||
log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx),
|
||||
user_it.cell->type.c_str(ctx));
|
||||
|
||||
if (ctx->debug)
|
||||
log(" Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx));
|
||||
|
||||
IdString user_port = user_it.port;
|
||||
|
||||
auto user_port_it = user_it.cell->pins.find(user_port);
|
||||
|
||||
if (user_port_it != user_it.cell->pins.end())
|
||||
user_port = user_port_it->second;
|
||||
|
||||
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
|
||||
|
||||
if (dst_wire == WireId())
|
||||
log_error("No wire found for port %s (pin %s) on destination "
|
||||
"cell %s (bel %s).\n",
|
||||
user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx),
|
||||
ctx->getBelName(dst_bel).c_str(ctx));
|
||||
|
||||
std::function<delay_t(WireId)> register_existing_path = [ctx, net_info, &src_wires, ®ister_existing_path](WireId wire) -> delay_t {
|
||||
auto it = src_wires.find(wire);
|
||||
if (it != src_wires.end())
|
||||
return it->second;
|
||||
|
||||
PipId pip = net_info->wires.at(wire).pip;
|
||||
delay_t delay = register_existing_path(ctx->getPipSrcWire(pip));
|
||||
delay += ctx->getPipDelay(pip).maxDelay();
|
||||
delay += ctx->getWireDelay(wire).maxDelay();
|
||||
delay -= 2*ctx->getDelayEpsilon();
|
||||
src_wires[wire] = delay;
|
||||
|
||||
return delay;
|
||||
};
|
||||
|
||||
WireId cursor = dst_wire;
|
||||
while (src_wires.count(cursor) == 0) {
|
||||
auto it = net_info->wires.find(cursor);
|
||||
if (it == net_info->wires.end())
|
||||
goto check_next_user_for_existing_path;
|
||||
NPNR_ASSERT(it->second.pip != PipId());
|
||||
cursor = ctx->getPipSrcWire(it->second.pip);
|
||||
}
|
||||
|
||||
register_existing_path(dst_wire);
|
||||
check_next_user_for_existing_path:;
|
||||
}
|
||||
|
||||
std::vector<WireId> ripup_wires;
|
||||
for (auto &it : net_info->wires)
|
||||
if (src_wires.count(it.first) == 0)
|
||||
ripup_wires.push_back(it.first);
|
||||
|
||||
for (auto &it : ripup_wires) {
|
||||
if (ctx->debug)
|
||||
log(" Unbind dangling wire for net %s: %s\n",
|
||||
net_name.c_str(ctx), ctx->getWireName(it).c_str(ctx));
|
||||
ctx->unbindWire(it);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &user_it : users_array) {
|
||||
if (ctx->debug)
|
||||
@ -398,6 +483,205 @@ struct Router
|
||||
}
|
||||
};
|
||||
|
||||
struct RouteJob
|
||||
{
|
||||
IdString net;
|
||||
int user_idx = -1;
|
||||
delay_t slack = 0;
|
||||
int randtag = 0;
|
||||
|
||||
struct Greater
|
||||
{
|
||||
bool operator()(const RouteJob &lhs, const RouteJob &rhs) const noexcept
|
||||
{
|
||||
return lhs.slack == rhs.slack ? lhs.randtag > rhs.randtag : lhs.slack > rhs.slack;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void addFullNetRouteJob(Context *ctx, IdString net_name,
|
||||
std::unordered_map<IdString, std::vector<bool>> &cache,
|
||||
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
|
||||
{
|
||||
NetInfo *net_info = ctx->nets.at(net_name).get();
|
||||
|
||||
if (net_info->driver.cell == nullptr)
|
||||
return;
|
||||
|
||||
auto src_bel = net_info->driver.cell->bel;
|
||||
|
||||
if (src_bel == BelId())
|
||||
log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
|
||||
net_info->driver.cell->type.c_str(ctx));
|
||||
|
||||
IdString driver_port = net_info->driver.port;
|
||||
|
||||
auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
|
||||
if (driver_port_it != net_info->driver.cell->pins.end())
|
||||
driver_port = driver_port_it->second;
|
||||
|
||||
auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
|
||||
|
||||
if (src_wire == WireId())
|
||||
log_error("No wire found for port %s (pin %s) on source cell %s "
|
||||
"(bel %s).\n",
|
||||
net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
|
||||
ctx->getBelName(src_bel).c_str(ctx));
|
||||
|
||||
auto &net_cache = cache[net_name];
|
||||
|
||||
if (net_cache.empty())
|
||||
net_cache.resize(net_info->users.size());
|
||||
|
||||
RouteJob job;
|
||||
job.net = net_name;
|
||||
job.user_idx = -1;
|
||||
job.slack = 0;
|
||||
job.randtag = ctx->rng();
|
||||
|
||||
bool got_slack = false;
|
||||
|
||||
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
|
||||
{
|
||||
if (net_cache[user_idx])
|
||||
continue;
|
||||
|
||||
auto &user_info = net_info->users[user_idx];
|
||||
auto dst_bel = user_info.cell->bel;
|
||||
|
||||
if (dst_bel == BelId())
|
||||
log_error("Destination cell %s (%s) is not mapped to a bel.\n",
|
||||
user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx));
|
||||
|
||||
IdString user_port = user_info.port;
|
||||
|
||||
auto user_port_it = user_info.cell->pins.find(user_port);
|
||||
|
||||
if (user_port_it != user_info.cell->pins.end())
|
||||
user_port = user_port_it->second;
|
||||
|
||||
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
|
||||
|
||||
if (dst_wire == WireId())
|
||||
log_error("No wire found for port %s (pin %s) on destination "
|
||||
"cell %s (bel %s).\n",
|
||||
user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx),
|
||||
ctx->getBelName(dst_bel).c_str(ctx));
|
||||
|
||||
if (user_idx == 0)
|
||||
job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
|
||||
else
|
||||
job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire));
|
||||
|
||||
WireId cursor = dst_wire;
|
||||
while (src_wire != cursor) {
|
||||
auto it = net_info->wires.find(cursor);
|
||||
if (it == net_info->wires.end()) {
|
||||
if (!got_slack)
|
||||
job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
|
||||
else
|
||||
job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire));
|
||||
got_slack = true;
|
||||
break;
|
||||
}
|
||||
NPNR_ASSERT(it->second.pip != PipId());
|
||||
cursor = ctx->getPipSrcWire(it->second.pip);
|
||||
}
|
||||
}
|
||||
|
||||
queue.push(job);
|
||||
|
||||
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
|
||||
net_cache[user_idx] = true;
|
||||
}
|
||||
|
||||
void addNetRouteJobs(Context *ctx, IdString net_name,
|
||||
std::unordered_map<IdString, std::vector<bool>> &cache,
|
||||
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
|
||||
{
|
||||
NetInfo *net_info = ctx->nets.at(net_name).get();
|
||||
|
||||
if (net_info->driver.cell == nullptr)
|
||||
return;
|
||||
|
||||
auto src_bel = net_info->driver.cell->bel;
|
||||
|
||||
if (src_bel == BelId())
|
||||
log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
|
||||
net_info->driver.cell->type.c_str(ctx));
|
||||
|
||||
IdString driver_port = net_info->driver.port;
|
||||
|
||||
auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
|
||||
if (driver_port_it != net_info->driver.cell->pins.end())
|
||||
driver_port = driver_port_it->second;
|
||||
|
||||
auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
|
||||
|
||||
if (src_wire == WireId())
|
||||
log_error("No wire found for port %s (pin %s) on source cell %s "
|
||||
"(bel %s).\n",
|
||||
net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
|
||||
ctx->getBelName(src_bel).c_str(ctx));
|
||||
|
||||
auto &net_cache = cache[net_name];
|
||||
|
||||
if (net_cache.empty())
|
||||
net_cache.resize(net_info->users.size());
|
||||
|
||||
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
|
||||
{
|
||||
if (net_cache[user_idx])
|
||||
continue;
|
||||
|
||||
auto &user_info = net_info->users[user_idx];
|
||||
auto dst_bel = user_info.cell->bel;
|
||||
|
||||
if (dst_bel == BelId())
|
||||
log_error("Destination cell %s (%s) is not mapped to a bel.\n",
|
||||
user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx));
|
||||
|
||||
IdString user_port = user_info.port;
|
||||
|
||||
auto user_port_it = user_info.cell->pins.find(user_port);
|
||||
|
||||
if (user_port_it != user_info.cell->pins.end())
|
||||
user_port = user_port_it->second;
|
||||
|
||||
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
|
||||
|
||||
if (dst_wire == WireId())
|
||||
log_error("No wire found for port %s (pin %s) on destination "
|
||||
"cell %s (bel %s).\n",
|
||||
user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx),
|
||||
ctx->getBelName(dst_bel).c_str(ctx));
|
||||
|
||||
WireId cursor = dst_wire;
|
||||
while (src_wire != cursor) {
|
||||
auto it = net_info->wires.find(cursor);
|
||||
if (it == net_info->wires.end()) {
|
||||
if (ctx->debug)
|
||||
log("Adding job [%s %d]: %s %s (%s) -> %s %s (%s)\n",
|
||||
net_name.c_str(ctx), user_idx,
|
||||
ctx->getBelName(src_bel).c_str(ctx), driver_port.c_str(ctx),
|
||||
ctx->getWireName(src_wire).c_str(ctx),
|
||||
ctx->getBelName(dst_bel).c_str(ctx), user_port.c_str(ctx),
|
||||
ctx->getWireName(dst_wire).c_str(ctx));
|
||||
RouteJob job;
|
||||
job.net = net_name;
|
||||
job.user_idx = user_idx;
|
||||
job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
|
||||
job.randtag = ctx->rng();
|
||||
queue.push(job);
|
||||
net_cache[user_idx] = true;
|
||||
break;
|
||||
}
|
||||
NPNR_ASSERT(it->second.pip != PipId());
|
||||
cursor = ctx->getPipSrcWire(it->second.pip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
@ -411,82 +695,24 @@ bool router1(Context *ctx)
|
||||
|
||||
log_break();
|
||||
log_info("Routing..\n");
|
||||
|
||||
std::unordered_set<IdString> netsQueue;
|
||||
|
||||
ctx->lock();
|
||||
for (auto &net_it : ctx->nets) {
|
||||
auto net_name = net_it.first;
|
||||
auto net_info = net_it.second.get();
|
||||
|
||||
if (net_info->driver.cell == nullptr)
|
||||
continue;
|
||||
std::unordered_map<IdString, std::vector<bool>> jobCache;
|
||||
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue;
|
||||
|
||||
if (!net_info->wires.empty())
|
||||
continue;
|
||||
for (auto &net_it : ctx->nets)
|
||||
addNetRouteJobs(ctx, net_it.first, jobCache, jobQueue);
|
||||
|
||||
netsQueue.insert(net_name);
|
||||
}
|
||||
|
||||
if (netsQueue.empty()) {
|
||||
log_info("found no unrouted nets. no routing necessary.\n");
|
||||
if (jobQueue.empty()) {
|
||||
log_info("found no unrouted source-sink pairs. no routing necessary.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
log_info("found %d unrouted nets. starting routing procedure.\n", int(netsQueue.size()));
|
||||
|
||||
delay_t estimatedTotalDelay = 0.0;
|
||||
int estimatedTotalDelayCnt = 0;
|
||||
|
||||
for (auto net_name : netsQueue) {
|
||||
auto net_info = ctx->nets.at(net_name).get();
|
||||
|
||||
auto src_bel = net_info->driver.cell->bel;
|
||||
|
||||
if (src_bel == BelId())
|
||||
continue;
|
||||
|
||||
IdString driver_port = net_info->driver.port;
|
||||
|
||||
auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
|
||||
if (driver_port_it != net_info->driver.cell->pins.end())
|
||||
driver_port = driver_port_it->second;
|
||||
|
||||
auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
|
||||
|
||||
if (src_wire == WireId())
|
||||
continue;
|
||||
|
||||
for (auto &user_it : net_info->users) {
|
||||
auto dst_bel = user_it.cell->bel;
|
||||
|
||||
if (dst_bel == BelId())
|
||||
continue;
|
||||
|
||||
IdString user_port = user_it.port;
|
||||
|
||||
auto user_port_it = user_it.cell->pins.find(user_port);
|
||||
|
||||
if (user_port_it != user_it.cell->pins.end())
|
||||
user_port = user_port_it->second;
|
||||
|
||||
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
|
||||
|
||||
if (dst_wire == WireId())
|
||||
continue;
|
||||
|
||||
estimatedTotalDelay += ctx->estimateDelay(src_wire, dst_wire);
|
||||
estimatedTotalDelayCnt++;
|
||||
}
|
||||
}
|
||||
ctx->unlock();
|
||||
|
||||
log_info("estimated total wire delay: %.2f (avg %.2f)\n", float(estimatedTotalDelay),
|
||||
float(estimatedTotalDelay) / estimatedTotalDelayCnt);
|
||||
log_info("found %d unrouted source-sink pairs. starting routing procedure.\n", int(jobQueue.size()));
|
||||
|
||||
int iterCnt = 0;
|
||||
|
||||
while (!netsQueue.empty()) {
|
||||
while (!jobQueue.empty()) {
|
||||
if (iterCnt == 200) {
|
||||
log_warning("giving up after %d iterations.\n", iterCnt);
|
||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||
@ -500,27 +726,34 @@ bool router1(Context *ctx)
|
||||
if (ctx->verbose)
|
||||
log_info("-- %d --\n", iterCnt);
|
||||
|
||||
int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0, netCnt = 0;
|
||||
int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0, jobCnt = 0, failedCnt = 0;
|
||||
|
||||
std::unordered_set<IdString> ripupQueue;
|
||||
std::unordered_set<IdString> normalRouteNets, ripupQueue;
|
||||
|
||||
if (ctx->verbose || iterCnt == 1)
|
||||
log_info("routing queue contains %d nets.\n", int(netsQueue.size()));
|
||||
log_info("routing queue contains %d jobs.\n", int(jobQueue.size()));
|
||||
|
||||
bool printNets = ctx->verbose && (netsQueue.size() < 10);
|
||||
bool printNets = ctx->verbose && (jobQueue.size() < 10);
|
||||
|
||||
std::vector<IdString> netsArray(netsQueue.begin(), netsQueue.end());
|
||||
ctx->sorted_shuffle(netsArray);
|
||||
netsQueue.clear();
|
||||
while (!jobQueue.empty()) {
|
||||
if(ctx->debug)
|
||||
log("Next job slack: %f\n", double(jobQueue.top().slack));
|
||||
|
||||
for (auto net_name : netsArray) {
|
||||
if (printNets)
|
||||
log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx),
|
||||
int(ctx->nets.at(net_name)->users.size()));
|
||||
auto net_name = jobQueue.top().net;
|
||||
auto user_idx = jobQueue.top().user_idx;
|
||||
jobQueue.pop();
|
||||
|
||||
Router router(ctx, scores, net_name, false);
|
||||
if (printNets) {
|
||||
if (user_idx < 0)
|
||||
log_info(" routing all %d users of net %s\n",
|
||||
int(ctx->nets.at(net_name)->users.size()), net_name.c_str(ctx));
|
||||
else
|
||||
log_info(" routing user %d of net %s\n", user_idx, net_name.c_str(ctx));
|
||||
}
|
||||
|
||||
netCnt++;
|
||||
Router router(ctx, scores, net_name, user_idx, false, false);
|
||||
|
||||
jobCnt++;
|
||||
visitCnt += router.visitCnt;
|
||||
revisitCnt += router.revisitCnt;
|
||||
overtimeRevisitCnt += router.overtimeRevisitCnt;
|
||||
@ -529,20 +762,24 @@ bool router1(Context *ctx)
|
||||
if (printNets)
|
||||
log_info(" failed to route to %s.\n", ctx->getWireName(router.failedDest).c_str(ctx));
|
||||
ripupQueue.insert(net_name);
|
||||
failedCnt++;
|
||||
} else {
|
||||
normalRouteNets.insert(net_name);
|
||||
}
|
||||
|
||||
if ((ctx->verbose || iterCnt == 1) && !printNets && (netCnt % 100 == 0)) {
|
||||
log_info(" processed %d nets. (%d routed, %d failed)\n", netCnt, netCnt - int(ripupQueue.size()),
|
||||
int(ripupQueue.size()));
|
||||
if ((ctx->verbose || iterCnt == 1) && !printNets && (jobCnt % 100 == 0)) {
|
||||
log_info(" processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt);
|
||||
ctx->yield();
|
||||
}
|
||||
}
|
||||
|
||||
int normalRouteCnt = netCnt - int(ripupQueue.size());
|
||||
NPNR_ASSERT(jobQueue.empty());
|
||||
jobCache.clear();
|
||||
|
||||
if ((ctx->verbose || iterCnt == 1) && (netCnt % 100 != 0))
|
||||
log_info(" processed %d nets. (%d routed, %d failed)\n", netCnt, normalRouteCnt,
|
||||
int(ripupQueue.size()));
|
||||
if ((ctx->verbose || iterCnt == 1) && (jobCnt % 100 != 0)) {
|
||||
log_info(" processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt);
|
||||
ctx->yield();
|
||||
}
|
||||
|
||||
if (ctx->verbose)
|
||||
log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime "
|
||||
@ -560,7 +797,7 @@ bool router1(Context *ctx)
|
||||
visitCnt = 0;
|
||||
revisitCnt = 0;
|
||||
overtimeRevisitCnt = 0;
|
||||
netCnt = 0;
|
||||
int netCnt = 0;
|
||||
int ripCnt = 0;
|
||||
|
||||
std::vector<IdString> ripupArray(ripupQueue.begin(), ripupQueue.end());
|
||||
@ -571,7 +808,7 @@ bool router1(Context *ctx)
|
||||
log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx),
|
||||
int(ctx->nets.at(net_name)->users.size()));
|
||||
|
||||
Router router(ctx, scores, net_name, true, ripup_penalty);
|
||||
Router router(ctx, scores, net_name, -1, false, true, ripup_penalty);
|
||||
|
||||
netCnt++;
|
||||
visitCnt += router.visitCnt;
|
||||
@ -582,7 +819,7 @@ bool router1(Context *ctx)
|
||||
log_error("Net %s is impossible to route.\n", net_name.c_str(ctx));
|
||||
|
||||
for (auto it : router.rippedNets)
|
||||
netsQueue.insert(it);
|
||||
addFullNetRouteJob(ctx, it, jobCache, jobQueue);
|
||||
|
||||
if (printNets) {
|
||||
if (router.rippedNets.size() < 10) {
|
||||
@ -610,16 +847,16 @@ bool router1(Context *ctx)
|
||||
"overtime revisits).\n",
|
||||
visitCnt, (100.0 * revisitCnt) / visitCnt, (100.0 * overtimeRevisitCnt) / visitCnt);
|
||||
|
||||
if (ctx->verbose && !netsQueue.empty())
|
||||
if (ctx->verbose && !jobQueue.empty())
|
||||
log_info(" ripped up %d previously routed nets. continue "
|
||||
"routing.\n",
|
||||
int(netsQueue.size()));
|
||||
int(jobQueue.size()));
|
||||
}
|
||||
|
||||
if (!ctx->verbose)
|
||||
log_info("iteration %d: routed %d nets without ripup, routed %d "
|
||||
"nets with ripup.\n",
|
||||
iterCnt, normalRouteCnt, int(ripupQueue.size()));
|
||||
log_info("iteration %d: routed %d nets without ripup, routed %d nets with ripup.\n",
|
||||
iterCnt, int(normalRouteNets.size()), int(ripupQueue.size()));
|
||||
|
||||
|
||||
totalVisitCnt += visitCnt;
|
||||
totalRevisitCnt += revisitCnt;
|
||||
@ -638,16 +875,33 @@ bool router1(Context *ctx)
|
||||
totalVisitCnt, (100.0 * totalRevisitCnt) / totalVisitCnt,
|
||||
(100.0 * totalOvertimeRevisitCnt) / totalVisitCnt);
|
||||
|
||||
NPNR_ASSERT(jobQueue.empty());
|
||||
jobCache.clear();
|
||||
|
||||
for (auto &net_it : ctx->nets)
|
||||
addNetRouteJobs(ctx, net_it.first, jobCache, jobQueue);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!jobQueue.empty()) {
|
||||
log_info("Design strangely still contains unrouted source-sink pairs:\n");
|
||||
while (!jobQueue.empty()) {
|
||||
log_info(" user %d on net %s.\n", jobQueue.top().user_idx, jobQueue.top().net.c_str(ctx));
|
||||
jobQueue.pop();
|
||||
}
|
||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||
ctx->check();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||
#ifndef NDEBUG
|
||||
ctx->lock();
|
||||
ctx->check();
|
||||
ctx->unlock();
|
||||
#endif
|
||||
return true;
|
||||
} catch (log_execution_error_exception) {
|
||||
#ifndef NDEBUG
|
||||
ctx->lock();
|
||||
ctx->check();
|
||||
ctx->unlock();
|
||||
#endif
|
||||
|
@ -578,6 +578,12 @@ struct Arch : BaseCtx
|
||||
return wire_to_net.at(wire);
|
||||
}
|
||||
|
||||
DelayInfo getWireDelay(WireId wire) const
|
||||
{
|
||||
DelayInfo delay;
|
||||
return delay;
|
||||
}
|
||||
|
||||
WireRange getWires() const
|
||||
{
|
||||
WireRange range;
|
||||
|
@ -32,9 +32,14 @@ struct DelayInfo
|
||||
{
|
||||
delay_t delay = 0;
|
||||
|
||||
delay_t raiseDelay() const { return delay; }
|
||||
delay_t fallDelay() const { return delay; }
|
||||
delay_t avgDelay() const { return delay; }
|
||||
delay_t minRaiseDelay() const { return delay; }
|
||||
delay_t maxRaiseDelay() const { return delay; }
|
||||
|
||||
delay_t minFallDelay() const { return delay; }
|
||||
delay_t maxFallDelay() const { return delay; }
|
||||
|
||||
delay_t minDelay() const { return delay; }
|
||||
delay_t maxDelay() const { return delay; }
|
||||
|
||||
DelayInfo operator+(const DelayInfo &other) const
|
||||
{
|
||||
|
@ -29,8 +29,8 @@ void Arch::addWire(IdString name, int x, int y)
|
||||
NPNR_ASSERT(wires.count(name) == 0);
|
||||
WireInfo &wi = wires[name];
|
||||
wi.name = name;
|
||||
wi.grid_x = x;
|
||||
wi.grid_y = y;
|
||||
wi.x = x;
|
||||
wi.y = y;
|
||||
|
||||
wire_ids.push_back(name);
|
||||
}
|
||||
@ -62,18 +62,28 @@ void Arch::addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo
|
||||
pip_ids.push_back(name);
|
||||
}
|
||||
|
||||
void Arch::addBel(IdString name, IdString type, int x, int y, bool gb)
|
||||
void Arch::addBel(IdString name, IdString type, int x, int y, int z, bool gb)
|
||||
{
|
||||
Loc loc;
|
||||
loc.x = x;
|
||||
loc.y = y;
|
||||
loc.z = z;
|
||||
|
||||
NPNR_ASSERT(bels.count(name) == 0);
|
||||
NPNR_ASSERT(bel_by_loc.count(loc) == 0);
|
||||
BelInfo &bi = bels[name];
|
||||
bi.name = name;
|
||||
bi.type = type;
|
||||
bi.grid_x = x;
|
||||
bi.grid_y = y;
|
||||
bi.x = x;
|
||||
bi.y = y;
|
||||
bi.z = z;
|
||||
bi.gb = gb;
|
||||
|
||||
bel_ids.push_back(name);
|
||||
bel_ids_by_type[type].push_back(name);
|
||||
|
||||
bel_by_loc[loc] = name;
|
||||
bels_by_tile[x][y].push_back(name);
|
||||
}
|
||||
|
||||
void Arch::addBelInput(IdString bel, IdString name, IdString wire)
|
||||
@ -348,8 +358,8 @@ const std::vector<GroupId> &Arch::getGroupGroups(GroupId group) const { return g
|
||||
|
||||
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
|
||||
{
|
||||
x = bels.at(bel).grid_x;
|
||||
y = bels.at(bel).grid_y;
|
||||
x = bels.at(bel).x;
|
||||
y = bels.at(bel).y;
|
||||
gb = bels.at(bel).gb;
|
||||
}
|
||||
|
||||
@ -357,8 +367,8 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||
{
|
||||
const WireInfo &s = wires.at(src);
|
||||
const WireInfo &d = wires.at(dst);
|
||||
int dx = abs(s.grid_x - d.grid_x);
|
||||
int dy = abs(s.grid_y - d.grid_y);
|
||||
int dx = abs(s.x - d.x);
|
||||
int dy = abs(s.y - d.y);
|
||||
return (dx + dy) * grid_distance_to_delay;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ struct WireInfo
|
||||
BelPin uphill_bel_pin;
|
||||
std::vector<BelPin> downhill_bel_pins;
|
||||
DecalXY decalxy;
|
||||
int grid_x, grid_y;
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct PinInfo
|
||||
@ -59,7 +59,7 @@ struct BelInfo
|
||||
IdString name, type, bound_cell;
|
||||
std::unordered_map<IdString, PinInfo> pins;
|
||||
DecalXY decalxy;
|
||||
int grid_x, grid_y;
|
||||
int x, y, z;
|
||||
bool gb;
|
||||
};
|
||||
|
||||
@ -85,6 +85,9 @@ struct Arch : BaseCtx
|
||||
std::vector<IdString> bel_ids, wire_ids, pip_ids;
|
||||
std::unordered_map<IdString, std::vector<IdString>> bel_ids_by_type;
|
||||
|
||||
std::unordered_map<Loc, BelId> bel_by_loc;
|
||||
std::unordered_map<int, std::unordered_map<int, std::vector<BelId>>> bels_by_tile;
|
||||
|
||||
std::unordered_map<DecalId, std::vector<GraphicElement>> decal_graphics;
|
||||
DecalXY frame_decalxy;
|
||||
|
||||
@ -94,7 +97,7 @@ struct Arch : BaseCtx
|
||||
void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
|
||||
void addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
|
||||
|
||||
void addBel(IdString name, IdString type, int x, int y, bool gb);
|
||||
void addBel(IdString name, IdString type, int x, int y, int z, bool gb);
|
||||
void addBelInput(IdString bel, IdString name, IdString wire);
|
||||
void addBelOutput(IdString bel, IdString name, IdString wire);
|
||||
void addBelInout(IdString bel, IdString name, IdString wire);
|
||||
@ -129,6 +132,10 @@ struct Arch : BaseCtx
|
||||
|
||||
BelId getBelByName(IdString name) const;
|
||||
IdString getBelName(BelId bel) const;
|
||||
Loc getBelLocation(BelId bel) const;
|
||||
BelId getBelByLocation(Loc loc) const;
|
||||
std::vector<BelId> getBelsByTile(int x, int y) const;
|
||||
bool getBelGlobalBuf(BelId bel) const;
|
||||
uint32_t getBelChecksum(BelId bel) const;
|
||||
void bindBel(BelId bel, IdString cell, PlaceStrength strength);
|
||||
void unbindBel(BelId bel);
|
||||
@ -150,6 +157,7 @@ struct Arch : BaseCtx
|
||||
bool checkWireAvail(WireId wire) const;
|
||||
IdString getBoundWireNet(WireId wire) const;
|
||||
IdString getConflictingWireNet(WireId wire) const;
|
||||
DelayInfo getWireDelay(WireId wire) const { return DelayInfo(); }
|
||||
const std::vector<WireId> &getWires() const;
|
||||
|
||||
PipId getPipByName(IdString name) const;
|
||||
|
@ -29,11 +29,14 @@ struct DelayInfo
|
||||
{
|
||||
delay_t delay = 0;
|
||||
|
||||
delay_t raiseDelay() const { return delay; }
|
||||
delay_t minRaiseDelay() const { return delay; }
|
||||
delay_t maxRaiseDelay() const { return delay; }
|
||||
|
||||
delay_t fallDelay() const { return delay; }
|
||||
delay_t minFallDelay() const { return delay; }
|
||||
delay_t maxFallDelay() const { return delay; }
|
||||
|
||||
delay_t avgDelay() const { return delay; }
|
||||
delay_t minDelay() const { return delay; }
|
||||
delay_t maxDelay() const { return delay; }
|
||||
|
||||
DelayInfo operator+(const DelayInfo &other) const
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ void BaseMainWindow::createMenusAndBars()
|
||||
actionOpen->setStatusTip("Open an existing project file");
|
||||
connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj()));
|
||||
|
||||
QAction *actionSave = new QAction("Save", this);
|
||||
actionSave = new QAction("Save", this);
|
||||
actionSave->setIcon(QIcon(":/icons/resources/save.png"));
|
||||
actionSave->setShortcuts(QKeySequence::Save);
|
||||
actionSave->setStatusTip("Save existing project to disk");
|
||||
|
@ -73,6 +73,7 @@ class BaseMainWindow : public QMainWindow
|
||||
QStatusBar *statusBar;
|
||||
QAction *actionNew;
|
||||
QAction *actionOpen;
|
||||
QAction *actionSave;
|
||||
QProgressBar *progressBar;
|
||||
DesignWidget *designview;
|
||||
};
|
||||
|
@ -230,6 +230,9 @@ void DesignWidget::addToHistory(QTreeWidgetItem *item)
|
||||
void DesignWidget::newContext(Context *ctx)
|
||||
{
|
||||
treeWidget->clear();
|
||||
// reset pointers since they are not valid after clear
|
||||
nets_root = nullptr;
|
||||
cells_root = nullptr;
|
||||
history_ignore = false;
|
||||
history_index = -1;
|
||||
history.clear();
|
||||
@ -334,16 +337,7 @@ void DesignWidget::newContext(Context *ctx)
|
||||
for (auto pip : nameToItem[2].toStdMap()) {
|
||||
pip_root->addChild(pip.second);
|
||||
}
|
||||
|
||||
// Add nets to tree
|
||||
nets_root = new QTreeWidgetItem(treeWidget);
|
||||
nets_root->setText(0, "Nets");
|
||||
treeWidget->insertTopLevelItem(0, nets_root);
|
||||
|
||||
// Add cells to tree
|
||||
cells_root = new QTreeWidgetItem(treeWidget);
|
||||
cells_root->setText(0, "Cells");
|
||||
treeWidget->insertTopLevelItem(0, cells_root);
|
||||
updateTree();
|
||||
}
|
||||
|
||||
void DesignWidget::updateTree()
|
||||
@ -513,6 +507,14 @@ void DesignWidget::onItemSelectionChanged()
|
||||
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
|
||||
ElementType::NET);
|
||||
|
||||
DelayInfo delay = ctx->getWireDelay(wire);
|
||||
|
||||
QtProperty *delayItem = addSubGroup(topItem, "Delay");
|
||||
addProperty(delayItem, QVariant::Double, "Min Raise", delay.minRaiseDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Max Raise", delay.maxRaiseDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Min Fall", delay.minFallDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Max Fall", delay.maxFallDelay());
|
||||
|
||||
QtProperty *belpinItem = addSubGroup(topItem, "BelPin Uphill");
|
||||
BelPin uphill = ctx->getBelPinUphill(wire);
|
||||
if (uphill.bel != BelId())
|
||||
@ -535,7 +537,7 @@ void DesignWidget::onItemSelectionChanged()
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
QtProperty *pipsDownItem = addSubGroup(downhillItem, "Pips Downhill");
|
||||
QtProperty *pipsDownItem = addSubGroup(topItem, "Pips Downhill");
|
||||
for (const auto &item : ctx->getPipsDownhill(wire)) {
|
||||
addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
|
||||
counter++;
|
||||
@ -546,7 +548,7 @@ void DesignWidget::onItemSelectionChanged()
|
||||
}
|
||||
|
||||
counter = 0;
|
||||
QtProperty *pipsUpItem = addSubGroup(downhillItem, "Pips Uphill");
|
||||
QtProperty *pipsUpItem = addSubGroup(topItem, "Pips Uphill");
|
||||
for (const auto &item : ctx->getPipsUphill(wire)) {
|
||||
addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
|
||||
counter++;
|
||||
@ -572,9 +574,10 @@ void DesignWidget::onItemSelectionChanged()
|
||||
DelayInfo delay = ctx->getPipDelay(pip);
|
||||
|
||||
QtProperty *delayItem = addSubGroup(topItem, "Delay");
|
||||
addProperty(delayItem, QVariant::Double, "Raise", delay.raiseDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Min Raise", delay.minRaiseDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Max Raise", delay.maxRaiseDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Min Fall", delay.minFallDelay());
|
||||
addProperty(delayItem, QVariant::Double, "Max Fall", delay.maxFallDelay());
|
||||
} else if (type == ElementType::NET) {
|
||||
NetInfo *net = ctx->nets.at(c).get();
|
||||
|
||||
|
@ -20,9 +20,12 @@
|
||||
#include "mainwindow.h"
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QIcon>
|
||||
#include <QInputDialog>
|
||||
#include <QLineEdit>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include "bitstream.h"
|
||||
#include "design_utils.h"
|
||||
#include "jsonparse.h"
|
||||
@ -219,9 +222,11 @@ void MainWindow::new_proj()
|
||||
QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type),
|
||||
0, false, &ok);
|
||||
|
||||
if (ok && !item.isEmpty()) {
|
||||
if (ok && !item.isEmpty()) {
|
||||
currentProj = "";
|
||||
currentJson = "";
|
||||
currentPCF = "";
|
||||
disableActions();
|
||||
preload_pcf = "";
|
||||
chipArgs.package = package.toStdString().c_str();
|
||||
ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||
actionLoadJSON->setEnabled(true);
|
||||
@ -233,14 +238,16 @@ void MainWindow::new_proj()
|
||||
|
||||
void MainWindow::load_json(std::string filename, std::string pcf)
|
||||
{
|
||||
preload_pcf = pcf;
|
||||
disableActions();
|
||||
currentJson = filename;
|
||||
currentPCF = pcf;
|
||||
Q_EMIT task->loadfile(filename);
|
||||
}
|
||||
|
||||
void MainWindow::load_pcf(std::string filename)
|
||||
{
|
||||
disableActions();
|
||||
currentPCF = filename;
|
||||
Q_EMIT task->loadpcf(filename);
|
||||
}
|
||||
|
||||
@ -252,10 +259,79 @@ void MainWindow::newContext(Context *ctx)
|
||||
|
||||
void MainWindow::open_proj()
|
||||
{
|
||||
QMap<std::string, int> arch;
|
||||
#ifdef ICE40_HX1K_ONLY
|
||||
arch.insert("hx1k", ArchArgs::HX1K);
|
||||
#else
|
||||
arch.insert("lp384", ArchArgs::LP384);
|
||||
arch.insert("lp1k", ArchArgs::LP1K);
|
||||
arch.insert("hx1k", ArchArgs::HX1K);
|
||||
arch.insert("up5k", ArchArgs::UP5K);
|
||||
arch.insert("lp8k", ArchArgs::LP8K);
|
||||
arch.insert("hx8k", ArchArgs::HX8K);
|
||||
#endif
|
||||
|
||||
QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
|
||||
if (!fileName.isEmpty()) {
|
||||
std::string fn = fileName.toStdString();
|
||||
disableActions();
|
||||
try {
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
std::string fn = fileName.toStdString();
|
||||
currentProj = fn;
|
||||
disableActions();
|
||||
|
||||
pt::ptree root;
|
||||
std::string filename = fileName.toStdString();
|
||||
pt::read_json(filename, root);
|
||||
log_info("Loading project %s...\n", filename.c_str());
|
||||
log_break();
|
||||
|
||||
int version = root.get<int>("project.version");
|
||||
if (version != 1)
|
||||
log_error("Wrong project format version.\n");
|
||||
|
||||
std::string arch_name = root.get<std::string>("project.arch.name");
|
||||
if (arch_name != "ice40")
|
||||
log_error("Unsuported project architecture.\n");
|
||||
|
||||
std::string arch_type = root.get<std::string>("project.arch.type");
|
||||
std::string arch_package = root.get<std::string>("project.arch.package");
|
||||
|
||||
chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(arch_type);
|
||||
chipArgs.package = arch_package;
|
||||
ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||
Q_EMIT contextChanged(ctx.get());
|
||||
|
||||
QFileInfo fi(fileName);
|
||||
QDir::setCurrent(fi.absoluteDir().absolutePath());
|
||||
log_info("Setting current dir to %s...\n", fi.absoluteDir().absolutePath().toStdString().c_str());
|
||||
log_info("Loading project %s...\n", filename.c_str());
|
||||
log_info("Context changed to %s (%s)\n", arch_type.c_str(), arch_package.c_str());
|
||||
|
||||
auto project = root.get_child("project");
|
||||
std::string json;
|
||||
std::string pcf;
|
||||
if (project.count("input")) {
|
||||
auto input = project.get_child("input");
|
||||
if (input.count("json"))
|
||||
json = input.get<std::string>("json");
|
||||
if (input.count("pcf"))
|
||||
pcf = input.get<std::string>("pcf");
|
||||
}
|
||||
|
||||
if (!(QFileInfo::exists(json.c_str()) && QFileInfo(json.c_str()).isFile())) {
|
||||
log_error("Json file does not exist.\n");
|
||||
}
|
||||
if (!pcf.empty()) {
|
||||
if (!(QFileInfo::exists(pcf.c_str()) && QFileInfo(pcf.c_str()).isFile())) {
|
||||
log_error("PCF file does not exist.\n");
|
||||
}
|
||||
}
|
||||
|
||||
log_info("Loading json: %s...\n", json.c_str());
|
||||
load_json(json, pcf);
|
||||
} catch (log_execution_error_exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,7 +351,35 @@ void MainWindow::open_pcf()
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::save_proj() { return false; }
|
||||
bool MainWindow::save_proj()
|
||||
{
|
||||
if (currentProj.empty()) {
|
||||
QString fileName = QFileDialog::getSaveFileName(this, QString("Save Project"), QString(), QString("*.proj"));
|
||||
if (fileName.isEmpty())
|
||||
return false;
|
||||
currentProj = fileName.toStdString();
|
||||
}
|
||||
if (!currentProj.empty()) {
|
||||
namespace pt = boost::property_tree;
|
||||
QFileInfo fi(currentProj.c_str());
|
||||
QDir dir(fi.absoluteDir().absolutePath());
|
||||
std::ofstream f(currentProj);
|
||||
pt::ptree root;
|
||||
root.put("project.version", 1);
|
||||
root.put("project.name", fi.baseName().toStdString());
|
||||
root.put("project.arch.name", ctx->archId().c_str(ctx.get()));
|
||||
root.put("project.arch.type", ctx->archArgsToId(chipArgs).c_str(ctx.get()));
|
||||
root.put("project.arch.package", chipArgs.package);
|
||||
if (!currentJson.empty())
|
||||
root.put("project.input.json", dir.relativeFilePath(currentJson.c_str()).toStdString());
|
||||
if (!currentPCF.empty())
|
||||
root.put("project.input.pcf", dir.relativeFilePath(currentPCF.c_str()).toStdString());
|
||||
pt::write_json(f, root);
|
||||
log_info("Project %s saved...\n", fi.baseName().toStdString().c_str());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::save_asc()
|
||||
{
|
||||
@ -303,6 +407,7 @@ void MainWindow::disableActions()
|
||||
|
||||
actionNew->setEnabled(true);
|
||||
actionOpen->setEnabled(true);
|
||||
actionSave->setEnabled(!currentJson.empty());
|
||||
}
|
||||
|
||||
void MainWindow::loadfile_finished(bool status)
|
||||
@ -312,12 +417,12 @@ void MainWindow::loadfile_finished(bool status)
|
||||
log("Loading design successful.\n");
|
||||
actionLoadPCF->setEnabled(true);
|
||||
actionPack->setEnabled(true);
|
||||
if (!preload_pcf.empty())
|
||||
load_pcf(preload_pcf);
|
||||
if (!currentPCF.empty())
|
||||
load_pcf(currentPCF);
|
||||
Q_EMIT updateTreeView();
|
||||
} else {
|
||||
log("Loading design failed.\n");
|
||||
preload_pcf = "";
|
||||
currentPCF = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,10 @@ class MainWindow : public BaseMainWindow
|
||||
|
||||
bool timing_driven;
|
||||
ArchArgs chipArgs;
|
||||
std::string preload_pcf;
|
||||
|
||||
std::string currentProj;
|
||||
std::string currentJson;
|
||||
std::string currentPCF;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -255,11 +255,52 @@ BelId Arch::getBelByName(IdString name) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
BelId Arch::getBelByLocation(Loc loc) const
|
||||
{
|
||||
BelId bel;
|
||||
|
||||
if (bel_by_loc.empty()) {
|
||||
for (int i = 0; i < chip_info->num_bels; i++) {
|
||||
BelId b;
|
||||
b.index = i;
|
||||
bel_by_loc[getBelLocation(b)] = i;
|
||||
}
|
||||
}
|
||||
|
||||
auto it = bel_by_loc.find(loc);
|
||||
if (it != bel_by_loc.end())
|
||||
bel.index = it->second;
|
||||
|
||||
return bel;
|
||||
}
|
||||
|
||||
BelRange Arch::getBelsByTile(int x, int y) const
|
||||
{
|
||||
// In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates are used
|
||||
BelRange br;
|
||||
|
||||
Loc loc;
|
||||
loc.x = x;
|
||||
loc.y = y;
|
||||
loc.z = 0;
|
||||
|
||||
br.b.cursor = Arch::getBelByLocation(loc).index;
|
||||
br.e.cursor = br.b.cursor;
|
||||
|
||||
if (br.e.cursor != -1) {
|
||||
while (br.e.cursor < chip_info->num_bels &&
|
||||
chip_info->bel_data[br.e.cursor].x == x &&
|
||||
chip_info->bel_data[br.e.cursor].y == y)
|
||||
br.e.cursor++;
|
||||
}
|
||||
|
||||
return br;
|
||||
}
|
||||
|
||||
BelRange Arch::getBelsAtSameTile(BelId bel) const
|
||||
{
|
||||
BelRange br;
|
||||
NPNR_ASSERT(bel != BelId());
|
||||
// This requires Bels at the same tile are consecutive
|
||||
int x = chip_info->bel_data[bel.index].x;
|
||||
int y = chip_info->bel_data[bel.index].y;
|
||||
int start = bel.index, end = bel.index;
|
||||
|
28
ice40/arch.h
28
ice40/arch.h
@ -350,6 +350,7 @@ struct Arch : BaseCtx
|
||||
mutable std::unordered_map<IdString, int> bel_by_name;
|
||||
mutable std::unordered_map<IdString, int> wire_by_name;
|
||||
mutable std::unordered_map<IdString, int> pip_by_name;
|
||||
mutable std::unordered_map<Loc, int> bel_by_loc;
|
||||
|
||||
std::vector<IdString> bel_to_cell;
|
||||
std::vector<IdString> wire_to_net;
|
||||
@ -442,7 +443,24 @@ struct Arch : BaseCtx
|
||||
return range;
|
||||
}
|
||||
|
||||
BelRange getBelsAtSameTile(BelId bel) const;
|
||||
Loc getBelLocation(BelId bel) const
|
||||
{
|
||||
Loc loc;
|
||||
loc.x = chip_info->bel_data[bel.index].x;
|
||||
loc.y = chip_info->bel_data[bel.index].y;
|
||||
loc.z = chip_info->bel_data[bel.index].z;
|
||||
return loc;
|
||||
}
|
||||
|
||||
BelId getBelByLocation(Loc loc) const;
|
||||
BelRange getBelsByTile(int x, int y) const;
|
||||
|
||||
bool getBelGlobalBuf(BelId bel) const
|
||||
{
|
||||
return chip_info->bel_data[bel.index].type == TYPE_SB_GB;
|
||||
}
|
||||
|
||||
BelRange getBelsAtSameTile(BelId bel) const NPNR_DEPRECATED;
|
||||
|
||||
BelType getBelType(BelId bel) const
|
||||
{
|
||||
@ -534,6 +552,12 @@ struct Arch : BaseCtx
|
||||
return wire_to_net[wire.index];
|
||||
}
|
||||
|
||||
DelayInfo getWireDelay(WireId wire) const
|
||||
{
|
||||
DelayInfo delay;
|
||||
return delay;
|
||||
}
|
||||
|
||||
WireRange getWires() const
|
||||
{
|
||||
WireRange range;
|
||||
@ -679,7 +703,7 @@ struct Arch : BaseCtx
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const;
|
||||
void estimatePosition(BelId bel, int &x, int &y, bool &gb) const NPNR_DEPRECATED;
|
||||
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||
delay_t getDelayEpsilon() const { return 20; }
|
||||
delay_t getRipupDelayPenalty() const { return 200; }
|
||||
|
@ -21,8 +21,6 @@
|
||||
#error Include "archdefs.h" via "nextpnr.h" only.
|
||||
#endif
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
typedef int delay_t;
|
||||
@ -31,9 +29,14 @@ struct DelayInfo
|
||||
{
|
||||
delay_t delay = 0;
|
||||
|
||||
delay_t raiseDelay() const { return delay; }
|
||||
delay_t fallDelay() const { return delay; }
|
||||
delay_t avgDelay() const { return delay; }
|
||||
delay_t minRaiseDelay() const { return delay; }
|
||||
delay_t maxRaiseDelay() const { return delay; }
|
||||
|
||||
delay_t minFallDelay() const { return delay; }
|
||||
delay_t maxFallDelay() const { return delay; }
|
||||
|
||||
delay_t minDelay() const { return delay; }
|
||||
delay_t maxDelay() const { return delay; }
|
||||
|
||||
DelayInfo operator+(const DelayInfo &other) const
|
||||
{
|
||||
|
1317
ice40/bitstream.cc
1317
ice40/bitstream.cc
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void write_asc(const Context *ctx, std::ostream &out);
|
||||
bool read_asc(Context *ctx, std::istream &in);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
|
@ -10,9 +10,6 @@
|
||||
"input": {
|
||||
"json": "blinky.json",
|
||||
"pcf": "blinky.pcf"
|
||||
},
|
||||
"params": {
|
||||
"freq": "50"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,14 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
|
||||
}
|
||||
}
|
||||
|
||||
void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2)
|
||||
{
|
||||
if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted()) {
|
||||
std::string msg = "Conflicting options '"+ std::string(opt1) + "' and '" + std::string(opt1) + "'.";
|
||||
log_error("%s\n",msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
@ -95,6 +103,7 @@ int main(int argc, char *argv[])
|
||||
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
|
||||
options.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
|
||||
options.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
|
||||
options.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
|
||||
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
|
||||
options.add_options()("version,V", "show version");
|
||||
options.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
||||
@ -121,13 +130,17 @@ int main(int argc, char *argv[])
|
||||
po::store(parsed, vm);
|
||||
|
||||
po::notify(vm);
|
||||
}
|
||||
|
||||
catch (std::exception &e) {
|
||||
} catch (std::exception &e) {
|
||||
std::cout << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
conflicting_options(vm, "read", "json");
|
||||
#ifndef ICE40_HX1K_ONLY
|
||||
if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
|
||||
vm.count("up5k")) > 1)
|
||||
log_error("Only one device type can be set\n");
|
||||
#endif
|
||||
if (vm.count("help") || argc == 1) {
|
||||
help:
|
||||
std::cout << boost::filesystem::basename(argv[0])
|
||||
@ -353,6 +366,13 @@ int main(int argc, char *argv[])
|
||||
if (vm.count("no-tmdriv"))
|
||||
ctx->timing_driven = false;
|
||||
|
||||
if (vm.count("read")) {
|
||||
std::string filename = vm["read"].as<std::string>();
|
||||
std::ifstream f(filename);
|
||||
if (!read_asc(ctx.get(), f))
|
||||
log_error("Loading ASC failed.\n");
|
||||
}
|
||||
|
||||
#ifndef NO_GUI
|
||||
if (vm.count("gui")) {
|
||||
Application a(argc, argv);
|
||||
|
15
ice40/picorv32.proj
Normal file
15
ice40/picorv32.proj
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"project": {
|
||||
"version": "1",
|
||||
"name": "picorv32",
|
||||
"arch": {
|
||||
"name": "ice40",
|
||||
"type": "hx8k",
|
||||
"package": "ct256"
|
||||
},
|
||||
"input": {
|
||||
"json": "picorv32.json",
|
||||
"pcf": "icebreaker.pcf"
|
||||
}
|
||||
}
|
||||
}
|
@ -4,3 +4,4 @@ rm -f picorv32.v
|
||||
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
|
||||
yosys -p 'synth_ice40 -json picorv32.json -top top' picorv32.v picorv32_top.v
|
||||
../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json
|
||||
icetime -d hx8k -t picorv32.asc
|
||||
|
Loading…
Reference in New Issue
Block a user