aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergiusz Bazanski <q3k@q3k.org>2018-07-21 20:00:42 +0100
committerSergiusz Bazanski <q3k@q3k.org>2018-07-21 20:00:42 +0100
commit6588aafdb8038625e8ce0cc265a2851cbb16b1c9 (patch)
tree44bc45b6ae06d52c60d15431db6c47feff036aff
parentbe14e161ae1963203e380bfbe02cfaeda828f838 (diff)
parentf438fc615b829170679971110b5d1bb57fba6a86 (diff)
downloadnextpnr-6588aafdb8038625e8ce0cc265a2851cbb16b1c9.tar.gz
nextpnr-6588aafdb8038625e8ce0cc265a2851cbb16b1c9.tar.bz2
nextpnr-6588aafdb8038625e8ce0cc265a2851cbb16b1c9.zip
Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr into q3k/lock-2-electric-boogaloo
-rw-r--r--common/nextpnr.h24
-rw-r--r--common/place_common.cc5
-rw-r--r--common/router1.cc440
-rw-r--r--ecp5/arch.h6
-rw-r--r--ecp5/archdefs.h11
-rw-r--r--generic/arch.cc28
-rw-r--r--generic/arch.h14
-rw-r--r--generic/archdefs.h9
-rw-r--r--gui/basewindow.cc2
-rw-r--r--gui/basewindow.h1
-rw-r--r--gui/designwidget.cc33
-rw-r--r--gui/ice40/mainwindow.cc123
-rw-r--r--gui/ice40/mainwindow.h5
-rw-r--r--ice40/arch.cc43
-rw-r--r--ice40/arch.h28
-rw-r--r--ice40/archdefs.h13
-rw-r--r--ice40/bitstream.cc1317
-rw-r--r--ice40/bitstream.h1
-rw-r--r--ice40/blinky.proj3
-rw-r--r--ice40/main.cc26
-rw-r--r--ice40/picorv32.proj15
-rwxr-xr-xice40/picorv32.sh1
22 files changed, 1466 insertions, 682 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 8b8299bc..1f3e1d08 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -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
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 3e4416df..a171374f 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();
+
+ 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;
- ripup_net(ctx, net_name);
- ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
+ auto user_port_it = user_it.cell->pins.find(user_port);
- std::vector<PortRef> users_array = net_info->users;
- ctx->shuffle(users_array);
+ 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,95 +483,236 @@ struct Router
}
};
-} // namespace
+struct RouteJob
+{
+ IdString net;
+ int user_idx = -1;
+ delay_t slack = 0;
+ int randtag = 0;
-NEXTPNR_NAMESPACE_BEGIN
+ 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;
+ }
+ };
+};
-bool router1(Context *ctx)
+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)
{
- 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;
- ctx->lock();
- 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));
+
+ 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);
}
+ }
- log_info("found %d unrouted nets. starting routing procedure.\n", int(netsQueue.size()));
+ queue.push(job);
- delay_t estimatedTotalDelay = 0.0;
- int estimatedTotalDelayCnt = 0;
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
+ net_cache[user_idx] = true;
+}
- for (auto net_name : netsQueue) {
- auto net_info = ctx->nets.at(net_name).get();
+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();
- auto src_bel = net_info->driver.cell->bel;
+ if (net_info->driver.cell == nullptr)
+ return;
- if (src_bel == BelId())
- continue;
+ auto src_bel = net_info->driver.cell->bel;
- IdString driver_port = net_info->driver.port;
+ 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));
- 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;
+ IdString driver_port = net_info->driver.port;
- auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(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;
- if (src_wire == WireId())
- continue;
+ auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
- for (auto &user_it : net_info->users) {
- auto dst_bel = user_it.cell->bel;
+ 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));
- if (dst_bel == BelId())
- continue;
+ auto &net_cache = cache[net_name];
- IdString user_port = user_it.port;
+ if (net_cache.empty())
+ net_cache.resize(net_info->users.size());
- auto user_port_it = user_it.cell->pins.find(user_port);
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
+ {
+ if (net_cache[user_idx])
+ continue;
- if (user_port_it != user_it.cell->pins.end())
- user_port = user_port_it->second;
+ auto &user_info = net_info->users[user_idx];
+ auto dst_bel = user_info.cell->bel;
- auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
+ 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));
- if (dst_wire == WireId())
- continue;
+ 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));
- estimatedTotalDelay += ctx->estimateDelay(src_wire, dst_wire);
- estimatedTotalDelayCnt++;
+ 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
+
+bool router1(Context *ctx)
+{
+ try {
+ int totalVisitCnt = 0, totalRevisitCnt = 0, totalOvertimeRevisitCnt = 0;
+ delay_t ripup_penalty = ctx->getRipupDelayPenalty();
+ RipupScoreboard scores;
+
+ log_break();
+ log_info("Routing..\n");
+ ctx->lock();
+
+ std::unordered_map<IdString, std::vector<bool>> jobCache;
+ std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue;
+
+ 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;
}
- 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
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.cc b/generic/arch.cc
index 390830aa..b7ec847e 100644
--- a/generic/arch.cc
+++ b/generic/arch.cc
@@ -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;
}
diff --git a/generic/arch.h b/generic/arch.h
index 5d7ac540..e1516569 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -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;
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 4123bf30..a59307f0 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -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();
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.cc b/ice40/arch.cc
index e09f26be..7c183f6c 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -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;
diff --git a/ice40/arch.h b/ice40/arch.h
index f00d7f8a..b7e66414 100644
--- a/ice40/arch.h
+++ b/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; }
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 14b0d2be..8a7449a8 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -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
{
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index c12fa90e..7fd3f8ac 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -1,529 +1,788 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
- * Copyright (C) 2018 David Shah <david@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-#include "bitstream.h"
-#include <cctype>
-#include <vector>
-#include "log.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-inline TileType tile_at(const Context *ctx, int x, int y)
-{
- return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
-}
-
-const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
-{
- for (int i = 0; i < tile.num_config_entries; i++) {
- if (std::string(tile.entries[i].name.get()) == name) {
- return tile.entries[i];
- }
- }
- NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
-}
-
-std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
-{
- for (int i = 0; i < bi.num_ierens; i++) {
- auto ie = bi.ierens[i];
- if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
- return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
- }
- }
- // No pin at this location
- return std::make_tuple(-1, -1, -1);
-};
-
-void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
- int index = -1)
-{
- const ConfigEntryPOD &cfg = find_config(ti, name);
- if (index == -1) {
- for (int i = 0; i < cfg.num_bits; i++) {
- int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
- if (cbit && !value)
- log_error("clearing already set config bit %s", name.c_str());
- cbit = value;
- }
- } else {
- int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
- cbit = value;
- if (cbit && !value)
- log_error("clearing already set config bit %s[%d]", name.c_str(), index);
- }
-}
-
-int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
-{
- auto found = cell->params.find(param);
- if (found != cell->params.end())
- return std::stoi(found->second);
- else
- return defval;
-}
-
-std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
-{
- auto found = cell->params.find(param);
- if (found != cell->params.end())
- return found->second;
- else
- return defval;
-}
-
-char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
-
-static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
-{
- for (int i = 0; i < chip->num_belcfgs; i++) {
- if (chip->bel_config[i].bel_index == bel.index)
- return chip->bel_config[i];
- }
- NPNR_ASSERT_FALSE("failed to find bel config");
-}
-
-typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
-
-static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
- bool value)
-{
- const ChipInfoPOD *chip = ctx->chip_info;
-
- for (int i = 0; i < cell_cbits.num_entries; i++) {
- const auto &cbit = cell_cbits.entries[i];
- if (cbit.entry_name.get() == name) {
- const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
- set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value);
- return;
- }
- }
- NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
-}
-
-void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
- const std::vector<std::pair<std::string, int>> &params, bool string_style)
-{
- const ChipInfoPOD *chip = ctx->chip_info;
- const auto &bc = get_ec_config(chip, cell->bel);
- for (auto p : params) {
- std::vector<bool> value;
- if (string_style) {
- // Lattice's weird string style params, not sure if
- // prefixes other than 0b should be supported, only 0b features in docs
- std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
- assert(raw.substr(0, 2) == "0b");
- raw = raw.substr(2);
- value.resize(raw.length());
- for (int i = 0; i < (int)raw.length(); i++) {
- if (raw[i] == '1') {
- value[(raw.length() - 1) - i] = 1;
- } else {
- assert(raw[i] == '0');
- value[(raw.length() - 1) - i] = 0;
- }
- }
- } else {
- int ival = get_param_or_def(cell, ctx->id(p.first), 0);
-
- for (int i = 0; i < p.second; i++)
- value.push_back((ival >> i) & 0x1);
- }
-
- value.resize(p.second);
- if (p.second == 1) {
- set_ec_cbit(config, ctx, bc, p.first, value.at(0));
- } else {
- for (int i = 0; i < p.second; i++) {
- set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
- }
- }
- }
-}
-
-void write_asc(const Context *ctx, std::ostream &out)
-{
- // [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));
- }
- }
- out << ".comment from next-pnr" << std::endl;
-
- switch (ctx->args.type) {
- case ArchArgs::LP384:
- out << ".device 384" << std::endl;
- break;
- case ArchArgs::HX1K:
- case ArchArgs::LP1K:
- out << ".device 1k" << std::endl;
- break;
- case ArchArgs::HX8K:
- case ArchArgs::LP8K:
- out << ".device 8k" << std::endl;
- break;
- case ArchArgs::UP5K:
- out << ".device 5k" << std::endl;
- break;
- default:
- NPNR_ASSERT_FALSE("unsupported device type");
- }
- // Set pips
- for (auto pip : ctx->getPips()) {
- if (ctx->pip_to_net[pip.index] != IdString()) {
- const PipInfoPOD &pi = ci.pip_data[pip.index];
- const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
- 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);
- if (bool(cbit) != 0)
- NPNR_ASSERT(false);
- cbit = val;
- }
- }
- }
- // Set logic cell config
- for (auto &cell : ctx->cells) {
- BelId bel = cell.second.get()->bel;
- if (bel == BelId()) {
- std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
- continue;
- }
- 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"));
- bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
- bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
- bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
- bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
- std::vector<bool> lc(20, false);
- // From arachne-pnr
- static std::vector<int> lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
- for (int i = 0; i < 16; i++) {
- if ((lut_init >> i) & 0x1)
- lc.at(lut_perm.at(i)) = true;
- }
- lc.at(8) = carry_enable;
- lc.at(9) = dff_enable;
- lc.at(18) = set_noreset;
- lc.at(19) = async_sr;
-
- for (int i = 0; i < 20; i++)
- set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
- if (dff_enable)
- set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
-
- bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
- bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
- if (carry_const) {
- if (!ctx->force)
- NPNR_ASSERT(z == 0);
- set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
- }
- } else if (cell.second->type == ctx->id("SB_IO")) {
- 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"));
- bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
- for (int i = 0; i < 6; i++) {
- bool val = (pin_type >> i) & 0x01;
- set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
- }
- set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
- auto ieren = get_ieren(bi, x, y, z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- NPNR_ASSERT(iez != -1);
-
- bool input_en = false;
- if ((ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_0).index] != IdString()) ||
- (ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_1).index] != IdString())) {
- input_en = true;
- }
-
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
- } else {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
- }
-
- if (ctx->args.type == ArchArgs::UP5K) {
- if (iez == 0) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
- } else if (iez == 1) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
- }
- }
- } else if (cell.second->type == ctx->id("SB_GB")) {
- // no cell config bits
- } else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y;
- const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
- const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
- if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
- set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
- }
- bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
- bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
- int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
- int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
- set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
- set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
-
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
- set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
- } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
- // No config needed
- } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y, z = beli.z;
- NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
- if (x == 0 && y == 0) {
- const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
- if (z == 1) {
- set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
- } else if (z == 2) {
- set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
- } else {
- NPNR_ASSERT(false);
- }
- } else if (x == 25 && y == 0) {
- const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
- if (z == 3) {
- set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
- } else if (z == 4) {
- set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
- } else {
- NPNR_ASSERT(false);
- }
- }
- } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
- const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
- {"A_REG", 1},
- {"B_REG", 1},
- {"D_REG", 1},
- {"TOP_8x8_MULT_REG", 1},
- {"BOT_8x8_MULT_REG", 1},
- {"PIPELINE_16x16_MULT_REG1", 1},
- {"PIPELINE_16x16_MULT_REG2", 1},
- {"TOPOUTPUT_SELECT", 2},
- {"TOPADDSUB_LOWERINPUT", 2},
- {"TOPADDSUB_UPPERINPUT", 1},
- {"TOPADDSUB_CARRYSELECT", 2},
- {"BOTOUTPUT_SELECT", 2},
- {"BOTADDSUB_LOWERINPUT", 2},
- {"BOTADDSUB_UPPERINPUT", 1},
- {"BOTADDSUB_CARRYSELECT", 2},
- {"MODE_8x8", 1},
- {"A_SIGNED", 1},
- {"B_SIGNED", 1}};
- configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
- } else {
- NPNR_ASSERT(false);
- }
- }
- // Set config bits in unused IO and RAM
- for (auto bel : ctx->getBels()) {
- if (ctx->bel_to_cell[bel.index] == IdString() && 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;
- auto ieren = get_ieren(bi, x, y, z);
- int iex, iey, iez;
- std::tie(iex, iey, iez) = ieren;
- if (iez != -1) {
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
- set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
- }
- }
- } else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
- const BelInfoPOD &beli = ci.bel_data[bel.index];
- int x = beli.x, y = beli.y;
- const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
- if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
- set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
- }
- }
- }
-
- // Set other config bits
- for (int y = 0; y < ci.height; y++) {
- for (int x = 0; x < ci.width; x++) {
- TileType tile = tile_at(ctx, x, y);
- const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
-
- // set all ColBufCtrl bits (FIXME)
- bool setColBufCtrl = true;
- if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
- if (tile == TILE_RAMB || tile == TILE_RAMT) {
- setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
- } else {
- setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
- }
- } else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
- setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
- } else if (ctx->args.type == ArchArgs::UP5K) {
- setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
- }
- if (setColBufCtrl) {
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
- set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
- }
-
- // Weird UltraPlus bits
- if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
- tile == TILE_IPCON) {
- if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
- // Mystery bits not set in this one tile
- } else {
- for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
- static const std::vector<int> ip_dsp_lut_perm = {
- 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
- };
- for (int i = 0; i < 16; i++)
- set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
- ip_dsp_lut_perm.at(i));
- if (tile == TILE_IPCON)
- 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",
- true);
- }
- }
- }
- }
- }
-
- // Write config out
- for (int y = 0; y < ci.height; y++) {
- for (int x = 0; x < ci.width; x++) {
- 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 << " " << x << " " << y << std::endl;
- for (auto row : config.at(y).at(x)) {
- for (auto col : row) {
- if (col == 1)
- out << "1";
- else
- out << "0";
- }
- out << std::endl;
- }
- out << std::endl;
- }
- }
-
- // Write RAM init data
- for (auto &cell : ctx->cells) {
- if (cell.second->bel != BelId()) {
- if (cell.second->type == ctx->id("ICESTORM_RAM")) {
- const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
- int x = beli.x, y = beli.y;
- out << ".ram_data " << x << " " << y << std::endl;
- for (int w = 0; w < 16; w++) {
- std::vector<bool> bits(256);
- std::string init =
- get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
- NPNR_ASSERT(init != "");
- for (size_t i = 0; i < init.size(); i++) {
- bool val = (init.at((init.size() - 1) - i) == '1');
- bits.at(i) = val;
- }
- for (int i = bits.size() - 4; i >= 0; i -= 4) {
- int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
- out << char(std::tolower(get_hexdigit(c)));
- }
- out << std::endl;
- }
- out << std::endl;
- }
- }
- }
-
- // Write symbols
- // const bool write_symbols = 1;
- for (auto wire : ctx->getWires()) {
- IdString net = ctx->getBoundWireNet(wire);
- if (net != IdString())
- out << ".sym " << wire.index << " " << net.str(ctx) << std::endl;
- }
-}
-
-NEXTPNR_NAMESPACE_END
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#include "bitstream.h"
+#include <cctype>
+#include <vector>
+#include "cells.h"
+#include "log.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+inline TileType tile_at(const Context *ctx, int x, int y)
+{
+ return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
+}
+
+const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
+{
+ for (int i = 0; i < tile.num_config_entries; i++) {
+ if (std::string(tile.entries[i].name.get()) == name) {
+ return tile.entries[i];
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
+}
+
+std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
+{
+ for (int i = 0; i < bi.num_ierens; i++) {
+ auto ie = bi.ierens[i];
+ if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
+ return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
+ }
+ }
+ // No pin at this location
+ 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)
+{
+ const ConfigEntryPOD &cfg = find_config(ti, name);
+ if (index == -1) {
+ for (int i = 0; i < cfg.num_bits; i++) {
+ int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
+ if (cbit && !value)
+ log_error("clearing already set config bit %s", name.c_str());
+ cbit = value;
+ }
+ } else {
+ int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
+ cbit = value;
+ if (cbit && !value)
+ log_error("clearing already set config bit %s[%d]", name.c_str(), index);
+ }
+}
+
+int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
+{
+ auto found = cell->params.find(param);
+ if (found != cell->params.end())
+ return std::stoi(found->second);
+ else
+ return defval;
+}
+
+std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
+{
+ auto found = cell->params.find(param);
+ if (found != cell->params.end())
+ return found->second;
+ else
+ return defval;
+}
+
+char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
+
+static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
+{
+ for (int i = 0; i < chip->num_belcfgs; i++) {
+ if (chip->bel_config[i].bel_index == bel.index)
+ return chip->bel_config[i];
+ }
+ NPNR_ASSERT_FALSE("failed to find bel config");
+}
+
+typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
+
+static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
+ bool value)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+
+ for (int i = 0; i < cell_cbits.num_entries; i++) {
+ const auto &cbit = cell_cbits.entries[i];
+ if (cbit.entry_name.get() == name) {
+ const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
+ set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value);
+ return;
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
+}
+
+void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
+ const std::vector<std::pair<std::string, int>> &params, bool string_style)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+ const auto &bc = get_ec_config(chip, cell->bel);
+ for (auto p : params) {
+ std::vector<bool> value;
+ if (string_style) {
+ // Lattice's weird string style params, not sure if
+ // prefixes other than 0b should be supported, only 0b features in docs
+ std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
+ assert(raw.substr(0, 2) == "0b");
+ raw = raw.substr(2);
+ value.resize(raw.length());
+ for (int i = 0; i < (int)raw.length(); i++) {
+ if (raw[i] == '1') {
+ value[(raw.length() - 1) - i] = 1;
+ } else {
+ assert(raw[i] == '0');
+ value[(raw.length() - 1) - i] = 0;
+ }
+ }
+ } else {
+ int ival = get_param_or_def(cell, ctx->id(p.first), 0);
+
+ for (int i = 0; i < p.second; i++)
+ value.push_back((ival >> i) & 0x1);
+ }
+
+ value.resize(p.second);
+ if (p.second == 1) {
+ set_ec_cbit(config, ctx, bc, p.first, value.at(0));
+ } else {
+ for (int i = 0; i < p.second; i++) {
+ set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
+ }
+ }
+ }
+}
+
+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]
+ 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));
+ }
+ }
+ out << ".comment from next-pnr" << std::endl;
+
+ switch (ctx->args.type) {
+ case ArchArgs::LP384:
+ out << ".device 384" << std::endl;
+ break;
+ case ArchArgs::HX1K:
+ case ArchArgs::LP1K:
+ out << ".device 1k" << std::endl;
+ break;
+ case ArchArgs::HX8K:
+ case ArchArgs::LP8K:
+ out << ".device 8k" << std::endl;
+ break;
+ case ArchArgs::UP5K:
+ out << ".device 5k" << std::endl;
+ break;
+ default:
+ NPNR_ASSERT_FALSE("unsupported device type\n");
+ }
+ // Set pips
+ for (auto pip : ctx->getPips()) {
+ if (ctx->pip_to_net[pip.index] != IdString()) {
+ const PipInfoPOD &pi = ci.pip_data[pip.index];
+ const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
+ 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);
+ if (bool(cbit) != 0)
+ NPNR_ASSERT(false);
+ cbit = val;
+ }
+ }
+ }
+ // Set logic cell config
+ for (auto &cell : ctx->cells) {
+ BelId bel = cell.second.get()->bel;
+ if (bel == BelId()) {
+ 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;
+ 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"));
+ bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
+ bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
+ bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
+ bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
+ std::vector<bool> lc(20, false);
+ // From arachne-pnr
+ static std::vector<int> lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+ for (int i = 0; i < 16; i++) {
+ if ((lut_init >> i) & 0x1)
+ lc.at(lut_perm.at(i)) = true;
+ }
+ lc.at(8) = carry_enable;
+ lc.at(9) = dff_enable;
+ lc.at(18) = set_noreset;
+ lc.at(19) = async_sr;
+
+ for (int i = 0; i < 20; i++)
+ set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
+ if (dff_enable)
+ set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
+
+ bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
+ bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
+ if (carry_const) {
+ if (!ctx->force)
+ NPNR_ASSERT(z == 0);
+ 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"));
+ bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
+ for (int i = 0; i < 6; i++) {
+ bool val = (pin_type >> i) & 0x01;
+ set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
+ }
+ set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
+ auto ieren = get_ieren(bi, x, y, z);
+ int iex, iey, iez;
+ std::tie(iex, iey, iez) = ieren;
+ NPNR_ASSERT(iez != -1);
+
+ bool input_en = false;
+ if ((ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_0).index] != IdString()) ||
+ (ctx->wire_to_net[ctx->getWireBelPin(bel, PIN_D_IN_1).index] != IdString())) {
+ input_en = true;
+ }
+
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
+ } else {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
+ }
+
+ if (ctx->args.type == ArchArgs::UP5K) {
+ if (iez == 0) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
+ } else if (iez == 1) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
+ }
+ }
+ } else if (cell.second->type == ctx->id("SB_GB")) {
+ // no cell config bits
+ } else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y;
+ const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
+ const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
+ if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
+ set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
+ }
+ bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
+ bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
+ int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
+ int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
+ set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
+ set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
+
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
+ set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
+ } else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
+ // No config needed
+ } else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y, z = beli.z;
+ NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
+ if (x == 0 && y == 0) {
+ const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
+ if (z == 1) {
+ set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
+ } else if (z == 2) {
+ set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ } else if (x == 25 && y == 0) {
+ const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
+ if (z == 3) {
+ set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
+ } else if (z == 4) {
+ set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ }
+ } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
+ const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
+ {"A_REG", 1},
+ {"B_REG", 1},
+ {"D_REG", 1},
+ {"TOP_8x8_MULT_REG", 1},
+ {"BOT_8x8_MULT_REG", 1},
+ {"PIPELINE_16x16_MULT_REG1", 1},
+ {"PIPELINE_16x16_MULT_REG2", 1},
+ {"TOPOUTPUT_SELECT", 2},
+ {"TOPADDSUB_LOWERINPUT", 2},
+ {"TOPADDSUB_UPPERINPUT", 1},
+ {"TOPADDSUB_CARRYSELECT", 2},
+ {"BOTOUTPUT_SELECT", 2},
+ {"BOTADDSUB_LOWERINPUT", 2},
+ {"BOTADDSUB_UPPERINPUT", 1},
+ {"BOTADDSUB_CARRYSELECT", 2},
+ {"MODE_8x8", 1},
+ {"A_SIGNED", 1},
+ {"B_SIGNED", 1}};
+ configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ }
+ // Set config bits in unused IO and RAM
+ for (auto bel : ctx->getBels()) {
+ if (ctx->bel_to_cell[bel.index] == IdString() && 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;
+ auto ieren = get_ieren(bi, x, y, z);
+ int iex, iey, iez;
+ std::tie(iex, iey, iez) = ieren;
+ if (iez != -1) {
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
+ set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
+ }
+ }
+ } else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
+ const BelInfoPOD &beli = ci.bel_data[bel.index];
+ int x = beli.x, y = beli.y;
+ const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
+ if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
+ set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
+ }
+ }
+ }
+
+ // Set other config bits
+ for (int y = 0; y < ci.height; y++) {
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
+
+ // set all ColBufCtrl bits (FIXME)
+ bool setColBufCtrl = true;
+ if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
+ if (tile == TILE_RAMB || tile == TILE_RAMT) {
+ setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
+ } else {
+ setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
+ }
+ } else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
+ setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
+ } else if (ctx->args.type == ArchArgs::UP5K) {
+ setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
+ }
+ if (setColBufCtrl) {
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
+ set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
+ }
+
+ // Weird UltraPlus bits
+ if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
+ tile == TILE_IPCON) {
+ if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
+ // Mystery bits not set in this one tile
+ } else {
+ for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
+ static const std::vector<int> ip_dsp_lut_perm = {
+ 4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
+ };
+ for (int i = 0; i < 16; i++)
+ set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
+ ip_dsp_lut_perm.at(i));
+ if (tile == TILE_IPCON)
+ 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",
+ true);
+ }
+ }
+ }
+ }
+ }
+
+ // Write config out
+ for (int y = 0; y < ci.height; y++) {
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ if (tile == TILE_NONE)
+ continue;
+ out << tagTileType(tile);
+ out << " " << x << " " << y << std::endl;
+ for (auto row : config.at(y).at(x)) {
+ for (auto col : row) {
+ if (col == 1)
+ out << "1";
+ else
+ out << "0";
+ }
+ out << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+
+ // Write RAM init data
+ for (auto &cell : ctx->cells) {
+ if (cell.second->bel != BelId()) {
+ if (cell.second->type == ctx->id("ICESTORM_RAM")) {
+ const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
+ int x = beli.x, y = beli.y;
+ out << ".ram_data " << x << " " << y << std::endl;
+ for (int w = 0; w < 16; w++) {
+ std::vector<bool> bits(256);
+ std::string init =
+ get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
+ NPNR_ASSERT(init != "");
+ for (size_t i = 0; i < init.size(); i++) {
+ bool val = (init.at((init.size() - 1) - i) == '1');
+ bits.at(i) = val;
+ }
+ for (int i = bits.size() - 4; i >= 0; i -= 4) {
+ int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
+ out << char(std::tolower(get_hexdigit(c)));
+ }
+ out << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+ }
+
+ // Write symbols
+ // const bool write_symbols = 1;
+ for (auto wire : ctx->getWires()) {
+ IdString net = ctx->getBoundWireNet(wire);
+ if (net != IdString())
+ out << ".sym " << wire.index << " " << net.str(ctx) << std::endl;
+ }
+}
+
+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
diff --git a/ice40/bitstream.h b/ice40/bitstream.h
index 2b6cda1d..41a2ae68 100644
--- a/ice40/bitstream.h
+++ b/ice40/bitstream.h
@@ -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
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 e77bdd34..70324a91 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -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);
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