Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr into q3k/lock-2-electric-boogaloo

This commit is contained in:
Sergiusz Bazanski 2018-07-21 20:00:42 +01:00
commit 6588aafdb8
22 changed files with 1477 additions and 693 deletions

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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, &register_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

View File

@ -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;

View File

@ -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
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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
{

View File

@ -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");

View File

@ -73,6 +73,7 @@ class BaseMainWindow : public QMainWindow
QStatusBar *statusBar;
QAction *actionNew;
QAction *actionOpen;
QAction *actionSave;
QProgressBar *progressBar;
DesignWidget *designview;
};

View File

@ -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();

View File

@ -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 = "";
}
}

View File

@ -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

View File

@ -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;

View File

@ -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; }

View File

@ -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
{

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -10,9 +10,6 @@
"input": {
"json": "blinky.json",
"pcf": "blinky.pcf"
},
"params": {
"freq": "50"
}
}
}

View File

@ -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
View File

@ -0,0 +1,15 @@
{
"project": {
"version": "1",
"name": "picorv32",
"arch": {
"name": "ice40",
"type": "hx8k",
"package": "ct256"
},
"input": {
"json": "picorv32.json",
"pcf": "icebreaker.pcf"
}
}
}

View File

@ -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