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;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
std::vector<PortRef> users_array = net_info->users;
|
||||
ctx->shuffle(users_array);
|
||||
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"
|
||||
@ -220,8 +223,10 @@ void MainWindow::new_proj()
|
||||
0, false, &ok);
|
||||
|
||||
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()) {
|
||||
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
|
||||
{
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "bitstream.h"
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include "cells.h"
|
||||
#include "log.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
@ -51,6 +52,20 @@ std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t
|
||||
return std::make_tuple(-1, -1, -1);
|
||||
};
|
||||
|
||||
bool get_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name,
|
||||
int index = -1)
|
||||
{
|
||||
const ConfigEntryPOD &cfg = find_config(ti, name);
|
||||
if (index == -1) {
|
||||
for (int i = 0; i < cfg.num_bits; i++) {
|
||||
return tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
|
||||
}
|
||||
} else {
|
||||
return tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
|
||||
int index = -1)
|
||||
{
|
||||
@ -157,6 +172,42 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
|
||||
}
|
||||
}
|
||||
|
||||
std::string tagTileType(TileType &tile)
|
||||
{
|
||||
if (tile == TILE_NONE)
|
||||
return "";
|
||||
switch (tile) {
|
||||
case TILE_LOGIC:
|
||||
return ".logic_tile";
|
||||
break;
|
||||
case TILE_IO:
|
||||
return ".io_tile";
|
||||
break;
|
||||
case TILE_RAMB:
|
||||
return ".ramb_tile";
|
||||
break;
|
||||
case TILE_RAMT:
|
||||
return ".ramt_tile";
|
||||
break;
|
||||
case TILE_DSP0:
|
||||
return ".dsp0_tile";
|
||||
break;
|
||||
case TILE_DSP1:
|
||||
return ".dsp1_tile";
|
||||
break;
|
||||
case TILE_DSP2:
|
||||
return ".dsp2_tile";
|
||||
break;
|
||||
case TILE_DSP3:
|
||||
return ".dsp3_tile";
|
||||
break;
|
||||
case TILE_IPCON:
|
||||
return ".ipcon_tile";
|
||||
break;
|
||||
default:
|
||||
NPNR_ASSERT(false);
|
||||
}
|
||||
}
|
||||
void write_asc(const Context *ctx, std::ostream &out)
|
||||
{
|
||||
// [y][x][row][col]
|
||||
@ -191,7 +242,7 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
out << ".device 5k" << std::endl;
|
||||
break;
|
||||
default:
|
||||
NPNR_ASSERT_FALSE("unsupported device type");
|
||||
NPNR_ASSERT_FALSE("unsupported device type\n");
|
||||
}
|
||||
// Set pips
|
||||
for (auto pip : ctx->getPips()) {
|
||||
@ -214,9 +265,9 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (cell.second->type == ctx->id("ICESTORM_LC")) {
|
||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||
int x = beli.x, y = beli.y, z = beli.z;
|
||||
if (cell.second->type == ctx->id("ICESTORM_LC")) {
|
||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
|
||||
unsigned lut_init = get_param_or_def(cell.second.get(), ctx->id("LUT_INIT"));
|
||||
bool neg_clk = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK"));
|
||||
@ -251,6 +302,8 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
|
||||
}
|
||||
} else if (cell.second->type == ctx->id("SB_IO")) {
|
||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||
int x = beli.x, y = beli.y, z = beli.z;
|
||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
||||
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
||||
@ -429,9 +482,8 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
set_config(ti, config.at(y).at(x),
|
||||
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
|
||||
else
|
||||
set_config(ti, config.at(y).at(x),
|
||||
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
|
||||
std::to_string(lc_idx) + "_inmux02_5",
|
||||
set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
|
||||
"_LC0" + std::to_string(lc_idx) + "_inmux02_5",
|
||||
true);
|
||||
}
|
||||
}
|
||||
@ -445,37 +497,7 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
TileType tile = tile_at(ctx, x, y);
|
||||
if (tile == TILE_NONE)
|
||||
continue;
|
||||
switch (tile) {
|
||||
case TILE_LOGIC:
|
||||
out << ".logic_tile";
|
||||
break;
|
||||
case TILE_IO:
|
||||
out << ".io_tile";
|
||||
break;
|
||||
case TILE_RAMB:
|
||||
out << ".ramb_tile";
|
||||
break;
|
||||
case TILE_RAMT:
|
||||
out << ".ramt_tile";
|
||||
break;
|
||||
case TILE_DSP0:
|
||||
out << ".dsp0_tile";
|
||||
break;
|
||||
case TILE_DSP1:
|
||||
out << ".dsp1_tile";
|
||||
break;
|
||||
case TILE_DSP2:
|
||||
out << ".dsp2_tile";
|
||||
break;
|
||||
case TILE_DSP3:
|
||||
out << ".dsp3_tile";
|
||||
break;
|
||||
case TILE_IPCON:
|
||||
out << ".ipcon_tile";
|
||||
break;
|
||||
default:
|
||||
NPNR_ASSERT(false);
|
||||
}
|
||||
out << tagTileType(tile);
|
||||
out << " " << x << " " << y << std::endl;
|
||||
for (auto row : config.at(y).at(x)) {
|
||||
for (auto col : row) {
|
||||
@ -526,4 +548,241 @@ void write_asc(const Context *ctx, std::ostream &out)
|
||||
}
|
||||
}
|
||||
|
||||
void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
|
||||
{
|
||||
constexpr size_t line_buf_size = 65536;
|
||||
char buffer[line_buf_size];
|
||||
int tile_x = -1, tile_y = -1, line_nr = -1;
|
||||
|
||||
while (1) {
|
||||
in.getline(buffer, line_buf_size);
|
||||
if (buffer[0] == '.') {
|
||||
line_nr = -1;
|
||||
const char *tok = strtok(buffer, " \t\r\n");
|
||||
|
||||
if (!strcmp(tok, ".device")) {
|
||||
std::string config_device = strtok(nullptr, " \t\r\n");
|
||||
std::string expected;
|
||||
switch (ctx->args.type) {
|
||||
case ArchArgs::LP384:
|
||||
expected = "384";
|
||||
break;
|
||||
case ArchArgs::HX1K:
|
||||
case ArchArgs::LP1K:
|
||||
expected = "1k";
|
||||
break;
|
||||
case ArchArgs::HX8K:
|
||||
case ArchArgs::LP8K:
|
||||
expected = "8k";
|
||||
break;
|
||||
case ArchArgs::UP5K:
|
||||
expected = "5k";
|
||||
break;
|
||||
default:
|
||||
log_error("unsupported device type\n");
|
||||
}
|
||||
if (expected != config_device)
|
||||
log_error("device type does not match\n");
|
||||
} else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
|
||||
!strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
|
||||
!strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
|
||||
line_nr = 0;
|
||||
tile_x = atoi(strtok(nullptr, " \t\r\n"));
|
||||
tile_y = atoi(strtok(nullptr, " \t\r\n"));
|
||||
|
||||
TileType tile = tile_at(ctx, tile_x, tile_y);
|
||||
if (tok != tagTileType(tile))
|
||||
log_error("Wrong tile type for specified position\n");
|
||||
|
||||
} else if (!strcmp(tok, ".extra_bit")) {
|
||||
/*
|
||||
int b = atoi(strtok(nullptr, " \t\r\n"));
|
||||
int x = atoi(strtok(nullptr, " \t\r\n"));
|
||||
int y = atoi(strtok(nullptr, " \t\r\n"));
|
||||
std::tuple<int, int, int> key(b, x, y);
|
||||
extra_bits.insert(key);
|
||||
*/
|
||||
} else if (!strcmp(tok, ".sym")) {
|
||||
int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
|
||||
const char *name = strtok(nullptr, " \t\r\n");
|
||||
|
||||
IdString netName = ctx->id(name);
|
||||
|
||||
if (ctx->nets.find(netName) == ctx->nets.end()) {
|
||||
std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||
created_net->name = netName;
|
||||
ctx->nets[netName] = std::move(created_net);
|
||||
}
|
||||
|
||||
WireId wire;
|
||||
wire.index = wireIndex;
|
||||
ctx->bindWire(wire, netName, STRENGTH_WEAK);
|
||||
}
|
||||
} else if (line_nr >= 0 && strlen(buffer) > 0) {
|
||||
if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
|
||||
log_error("Invalid data in input asc file");
|
||||
for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
|
||||
config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
|
||||
line_nr++;
|
||||
}
|
||||
if (in.eof())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_asc(Context *ctx, std::istream &in)
|
||||
{
|
||||
try {
|
||||
// [y][x][row][col]
|
||||
const ChipInfoPOD &ci = *ctx->chip_info;
|
||||
const BitstreamInfoPOD &bi = *ci.bits_info;
|
||||
chipconfig_t config;
|
||||
config.resize(ci.height);
|
||||
for (int y = 0; y < ci.height; y++) {
|
||||
config.at(y).resize(ci.width);
|
||||
for (int x = 0; x < ci.width; x++) {
|
||||
TileType tile = tile_at(ctx, x, y);
|
||||
int rows = bi.tiles_nonrouting[tile].rows;
|
||||
int cols = bi.tiles_nonrouting[tile].cols;
|
||||
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
|
||||
}
|
||||
}
|
||||
read_config(ctx, in, config);
|
||||
|
||||
// Set pips
|
||||
for (auto pip : ctx->getPips()) {
|
||||
const PipInfoPOD &pi = ci.pip_data[pip.index];
|
||||
const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
|
||||
bool isUsed = true;
|
||||
for (int i = 0; i < swi.num_bits; i++) {
|
||||
bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
|
||||
int8_t cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
|
||||
isUsed &= !(bool(cbit) ^ val);
|
||||
}
|
||||
if (isUsed) {
|
||||
IdString net = ctx->wire_to_net[pi.dst];
|
||||
WireId wire;
|
||||
wire.index = pi.dst;
|
||||
ctx->unbindWire(wire);
|
||||
ctx->bindPip(pip, net, STRENGTH_WEAK);
|
||||
}
|
||||
}
|
||||
for (auto bel : ctx->getBels()) {
|
||||
if (ctx->getBelType(bel) == TYPE_ICESTORM_LC) {
|
||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
|
||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||
int x = beli.x, y = beli.y, z = beli.z;
|
||||
std::vector<bool> lc(20, false);
|
||||
bool isUsed = false;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i);
|
||||
isUsed |= lc.at(i);
|
||||
}
|
||||
bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk");
|
||||
isUsed |= neg_clk;
|
||||
bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet");
|
||||
isUsed |= carry_set;
|
||||
|
||||
if (isUsed) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets and assign values of properties
|
||||
}
|
||||
}
|
||||
if (ctx->getBelType(bel) == TYPE_SB_IO) {
|
||||
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
|
||||
const BelInfoPOD &beli = ci.bel_data[bel.index];
|
||||
int x = beli.x, y = beli.y, z = beli.z;
|
||||
bool isUsed = false;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
isUsed |= get_config(ti, config.at(y).at(x),
|
||||
"IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i));
|
||||
}
|
||||
bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk");
|
||||
isUsed |= neg_trigger;
|
||||
|
||||
if (isUsed) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets and assign values of properties
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add cells that are without change in initial state of configuration
|
||||
for (auto &net : ctx->nets) {
|
||||
for (auto w : net.second->wires) {
|
||||
if (w.second.pip == PipId()) {
|
||||
WireId wire = w.first;
|
||||
BelPin belpin = ctx->getBelPinUphill(wire);
|
||||
if (ctx->checkBelAvail(belpin.bel)) {
|
||||
if (ctx->getBelType(belpin.bel) == TYPE_ICESTORM_LC) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets
|
||||
}
|
||||
if (ctx->getBelType(belpin.bel) == TYPE_SB_IO) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets
|
||||
}
|
||||
if (ctx->getBelType(belpin.bel) == TYPE_SB_GB) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets
|
||||
}
|
||||
if (ctx->getBelType(belpin.bel) == TYPE_SB_WARMBOOT) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets
|
||||
}
|
||||
if (ctx->getBelType(belpin.bel) == TYPE_ICESTORM_LFOSC) {
|
||||
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"));
|
||||
IdString name = created->name;
|
||||
ctx->cells[name] = std::move(created);
|
||||
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
|
||||
// TODO: Add port mapping to nets
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &cell : ctx->cells) {
|
||||
if (cell.second->bel != BelId()) {
|
||||
for (auto &port : cell.second->ports) {
|
||||
PortPin pin = ctx->portPinFromId(port.first);
|
||||
WireId wire = ctx->getWireBelPin(cell.second->bel, pin);
|
||||
if (wire != WireId()) {
|
||||
IdString name = ctx->getBoundWireNet(wire);
|
||||
if (name != IdString()) {
|
||||
port.second.net = ctx->nets[name].get();
|
||||
PortRef ref;
|
||||
ref.cell = cell.second.get();
|
||||
ref.port = port.second.name;
|
||||
|
||||
if (port.second.type == PORT_OUT)
|
||||
ctx->nets[name]->driver = ref;
|
||||
else
|
||||
ctx->nets[name]->users.push_back(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (log_execution_error_exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -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