aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEddie Hung <e.hung@imperial.ac.uk>2018-07-21 12:27:44 -0700
committerEddie Hung <e.hung@imperial.ac.uk>2018-07-21 12:27:44 -0700
commit31c9fd28fe59902311d61519537eae06f0f915d0 (patch)
treebdf5cb28dda96dadd92f62f88f8b0ecf1b2cb887
parentf176ee48cdd6b508f6f26e7b2ccca97a680929cf (diff)
parentdfdeb21690181044a95c2b2d64c197c999507b0c (diff)
downloadnextpnr-31c9fd28fe59902311d61519537eae06f0f915d0.tar.gz
nextpnr-31c9fd28fe59902311d61519537eae06f0f915d0.tar.bz2
nextpnr-31c9fd28fe59902311d61519537eae06f0f915d0.zip
Merge remote-tracking branch 'origin/master' into redist_slack
-rw-r--r--common/place_common.cc5
-rw-r--r--common/router1.cc445
-rw-r--r--ecp5/arch.h6
-rw-r--r--ecp5/archdefs.h11
-rw-r--r--generic/arch.h1
-rw-r--r--generic/archdefs.h9
-rw-r--r--gui/basewindow.cc2
-rw-r--r--gui/basewindow.h1
-rw-r--r--gui/designwidget.cc19
-rw-r--r--gui/ice40/mainwindow.cc123
-rw-r--r--gui/ice40/mainwindow.h5
-rw-r--r--ice40/arch.h6
-rw-r--r--ice40/archdefs.h11
-rw-r--r--ice40/bitstream.cc169
-rw-r--r--ice40/blinky.proj3
-rw-r--r--ice40/main.cc6
-rw-r--r--ice40/picorv32.proj15
-rwxr-xr-xice40/picorv32.sh1
18 files changed, 694 insertions, 144 deletions
diff --git a/common/place_common.cc b/common/place_common.cc
index b2f0e849..370eff23 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -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;
}
}
}
diff --git a/common/router1.cc b/common/router1.cc
index 50022169..86fb1a44 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -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();
- ripup_net(ctx, net_name);
- ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
+ for (auto &user_it : net_info->users) {
+ auto dst_bel = user_it.cell->bel;
- std::vector<PortRef> users_array = net_info->users;
- ctx->shuffle(users_array);
+ 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,108 +483,236 @@ struct Router
}
};
-} // namespace
+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;
+ }
+ };
+};
-NEXTPNR_NAMESPACE_BEGIN
-static void prioritise_nets(Context *ctx, std::vector<IdString> &netsArray)
+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)
{
- std::unordered_map<IdString, delay_t> netScores;
- for (auto net_name : netsArray) {
- delay_t score = std::numeric_limits<delay_t>::max();
- for (const auto &sink : ctx->nets.at(net_name)->users) {
- if (sink.budget < score)
- score = sink.budget;
+ 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);
}
- netScores[net_name] = score;
}
- std::sort(netsArray.begin(), netsArray.end(),
- [&netScores](IdString a, IdString b) { return netScores[a] < netScores[b]; });
+
+ queue.push(job);
+
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
+ net_cache[user_idx] = true;
}
-bool router1(Context *ctx)
+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)
{
- try {
- int totalVisitCnt = 0, totalRevisitCnt = 0, totalOvertimeRevisitCnt = 0;
- delay_t ripup_penalty = ctx->getRipupDelayPenalty();
- RipupScoreboard scores;
+ NetInfo *net_info = ctx->nets.at(net_name).get();
- log_break();
- log_info("Routing..\n");
+ if (net_info->driver.cell == nullptr)
+ return;
- std::unordered_set<IdString> netsQueue;
+ auto src_bel = net_info->driver.cell->bel;
- for (auto &net_it : ctx->nets) {
- auto net_name = net_it.first;
- auto net_info = net_it.second.get();
+ 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));
- if (net_info->driver.cell == nullptr)
- continue;
+ IdString driver_port = net_info->driver.port;
- if (!net_info->wires.empty())
- continue;
+ 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;
- netsQueue.insert(net_name);
- }
+ auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
- if (netsQueue.empty()) {
- log_info("found no unrouted nets. no routing necessary.\n");
- return true;
- }
+ 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));
- log_info("found %d unrouted nets. starting routing procedure.\n", int(netsQueue.size()));
+ auto &net_cache = cache[net_name];
- delay_t estimatedTotalDelay = 0.0;
- int estimatedTotalDelayCnt = 0;
+ if (net_cache.empty())
+ net_cache.resize(net_info->users.size());
- for (auto net_name : netsQueue) {
- auto net_info = ctx->nets.at(net_name).get();
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
+ {
+ if (net_cache[user_idx])
+ continue;
- auto src_bel = net_info->driver.cell->bel;
+ auto &user_info = net_info->users[user_idx];
+ auto dst_bel = user_info.cell->bel;
- if (src_bel == BelId())
- continue;
+ 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 driver_port = net_info->driver.port;
+ IdString user_port = user_info.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 user_port_it = user_info.cell->pins.find(user_port);
- auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
+ if (user_port_it != user_info.cell->pins.end())
+ user_port = user_port_it->second;
- if (src_wire == WireId())
- continue;
+ auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
- for (auto &user_it : net_info->users) {
- auto dst_bel = user_it.cell->bel;
+ 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 (dst_bel == BelId())
- continue;
+ 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);
+ }
+ }
+}
- IdString user_port = user_it.port;
+} // namespace
- auto user_port_it = user_it.cell->pins.find(user_port);
+NEXTPNR_NAMESPACE_BEGIN
- if (user_port_it != user_it.cell->pins.end())
- user_port = user_port_it->second;
+bool router1(Context *ctx)
+{
+ try {
+ int totalVisitCnt = 0, totalRevisitCnt = 0, totalOvertimeRevisitCnt = 0;
+ delay_t ripup_penalty = ctx->getRipupDelayPenalty();
+ RipupScoreboard scores;
- auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
+ log_break();
+ log_info("Routing..\n");
- if (dst_wire == WireId())
- continue;
+ std::unordered_map<IdString, std::vector<bool>> jobCache;
+ std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue;
- estimatedTotalDelay += ctx->estimateDelay(src_wire, dst_wire);
- estimatedTotalDelayCnt++;
- }
+ for (auto &net_it : ctx->nets)
+ addNetRouteJobs(ctx, net_it.first, jobCache, jobQueue);
+
+ if (jobQueue.empty()) {
+ log_info("found no unrouted source-sink pairs. no routing necessary.\n");
+ return true;
}
- 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());
@@ -513,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());
- prioritise_nets(ctx, 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;
@@ -542,18 +762,20 @@ 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);
}
- 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);
if (ctx->verbose)
log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime "
@@ -571,18 +793,18 @@ 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());
- prioritise_nets(ctx, ripupArray);
+ ctx->sorted_shuffle(ripupArray);
for (auto net_name : ripupArray) {
if (printNets)
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;
@@ -593,7 +815,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) {
@@ -619,16 +841,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;
@@ -645,6 +867,25 @@ 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->check();
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 944aedea..bf36ef2f 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -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;
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index 941607ba..40442e1b 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -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
{
diff --git a/generic/arch.h b/generic/arch.h
index ea4bb565..e1516569 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -157,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;
diff --git a/generic/archdefs.h b/generic/archdefs.h
index 06d4ec6e..b318d5af 100644
--- a/generic/archdefs.h
+++ b/generic/archdefs.h
@@ -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
{
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index 4a225bd6..78c2fe3a 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -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");
diff --git a/gui/basewindow.h b/gui/basewindow.h
index eee426c7..1184fa80 100644
--- a/gui/basewindow.h
+++ b/gui/basewindow.h
@@ -73,6 +73,7 @@ class BaseMainWindow : public QMainWindow
QStatusBar *statusBar;
QAction *actionNew;
QAction *actionOpen;
+ QAction *actionSave;
QProgressBar *progressBar;
DesignWidget *designview;
};
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index f7ae82f5..a59307f0 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -507,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())
@@ -529,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++;
@@ -540,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++;
@@ -566,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();
diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc
index 28792ed3..847698c5 100644
--- a/gui/ice40/mainwindow.cc
+++ b/gui/ice40/mainwindow.cc
@@ -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 = "";
}
}
diff --git a/gui/ice40/mainwindow.h b/gui/ice40/mainwindow.h
index cfd938f8..4600d1da 100644
--- a/gui/ice40/mainwindow.h
+++ b/gui/ice40/mainwindow.h
@@ -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
diff --git a/ice40/arch.h b/ice40/arch.h
index beba2ccf..21169298 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -548,6 +548,12 @@ struct Arch : BaseCtx
return wire_to_net[wire.index];
}
+ DelayInfo getWireDelay(WireId wire) const
+ {
+ DelayInfo delay;
+ return delay;
+ }
+
WireRange getWires() const
{
WireRange range;
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index dec6f702..8a7449a8 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -29,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
{
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index 924868b5..7fd3f8ac 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -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)
{
@@ -467,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);
}
}
@@ -588,15 +602,18 @@ void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
std::tuple<int, int, int> key(b, x, y);
extra_bits.insert(key);
*/
- } else if (!strcmp(tok, ".sym")) {
+ } else if (!strcmp(tok, ".sym")) {
int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
const char *name = strtok(nullptr, " \t\r\n");
-
- std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
+
IdString netName = ctx->id(name);
- created_net->name = netName;
- ctx->nets[netName] = std::move(created_net);
-
+
+ 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);
@@ -630,7 +647,139 @@ bool read_asc(Context *ctx, std::istream &in)
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
}
}
- read_config(ctx, in, config);
+ 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;
diff --git a/ice40/blinky.proj b/ice40/blinky.proj
index 6789a27a..f5bb9f88 100644
--- a/ice40/blinky.proj
+++ b/ice40/blinky.proj
@@ -10,9 +10,6 @@
"input": {
"json": "blinky.json",
"pcf": "blinky.pcf"
- },
- "params": {
- "freq": "50"
}
}
}
diff --git a/ice40/main.cc b/ice40/main.cc
index 652196a1..70324a91 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -68,8 +68,10 @@ 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())
- log_error((std::string("Conflicting options '") + opt1 + "' and '" + opt2 + "'.").c_str());
+ 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[])
diff --git a/ice40/picorv32.proj b/ice40/picorv32.proj
new file mode 100644
index 00000000..a8c83bd9
--- /dev/null
+++ b/ice40/picorv32.proj
@@ -0,0 +1,15 @@
+{
+ "project": {
+ "version": "1",
+ "name": "picorv32",
+ "arch": {
+ "name": "ice40",
+ "type": "hx8k",
+ "package": "ct256"
+ },
+ "input": {
+ "json": "picorv32.json",
+ "pcf": "icebreaker.pcf"
+ }
+ }
+}
diff --git a/ice40/picorv32.sh b/ice40/picorv32.sh
index 87426cde..d06786c5 100755
--- a/ice40/picorv32.sh
+++ b/ice40/picorv32.sh
@@ -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