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

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

View File

@ -31,6 +31,8 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include <boost/functional/hash.hpp>
#ifndef NEXTPNR_H #ifndef NEXTPNR_H
#define NEXTPNR_H #define NEXTPNR_H
@ -162,8 +164,30 @@ struct GraphicElement
std::string text; 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 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" #include "archdefs.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN

View File

@ -130,10 +130,11 @@ bool place_single_cell(Context *ctx, CellInfo *cell, bool require_legality)
if (iters >= 4) if (iters >= 4)
wirelen += ctx->rng(25); wirelen += ctx->rng(25);
if (wirelen <= best_ripup_wirelen) { if (wirelen <= best_ripup_wirelen) {
ripup_target = ctx->cells.at(ctx->getBoundBelCell(bel)).get(); CellInfo *curr_cell = ctx->cells.at(ctx->getBoundBelCell(bel)).get();
if (ripup_target->belStrength < STRENGTH_STRONG) { if (curr_cell->belStrength < STRENGTH_STRONG) {
best_ripup_wirelen = wirelen; best_ripup_wirelen = wirelen;
ripup_bel = bel; ripup_bel = bel;
ripup_target = curr_cell;
} }
} }
} }

View File

@ -75,6 +75,9 @@ struct RipupScoreboard
void ripup_net(Context *ctx, IdString net_name) 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(); auto net_info = ctx->nets.at(net_name).get();
std::vector<PipId> pips; std::vector<PipId> pips;
std::vector<WireId> wires; std::vector<WireId> wires;
@ -143,11 +146,13 @@ struct Router
thisVisitCntLimit = (thisVisitCnt * 3) / 2; thisVisitCntLimit = (thisVisitCnt * 3) / 2;
for (auto pip : ctx->getPipsDownhill(qw.wire)) { 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); WireId next_wire = ctx->getPipDstWire(pip);
bool foundRipupNet = false; bool foundRipupNet = false;
thisVisitCnt++; thisVisitCnt++;
next_delay += ctx->getWireDelay(next_wire).maxDelay();
if (!ctx->checkWireAvail(next_wire)) { if (!ctx->checkWireAvail(next_wire)) {
if (!ripup) if (!ripup)
continue; continue;
@ -226,7 +231,7 @@ struct Router
: ctx(ctx), scores(scores), ripup(ripup), ripup_penalty(ripup_penalty) : ctx(ctx), scores(scores), ripup(ripup), ripup_penalty(ripup_penalty)
{ {
std::unordered_map<WireId, delay_t> src_wires; 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); route(src_wires, dst_wire);
routedOkay = visited.count(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) : ctx(ctx), scores(scores), net_name(net_name), ripup(ripup), ripup_penalty(ripup_penalty)
{ {
auto net_info = ctx->nets.at(net_name).get(); 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)); log(" Source wire: %s\n", ctx->getWireName(src_wire).c_str(ctx));
std::unordered_map<WireId, delay_t> src_wires; 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); ripup_net(ctx, net_name);
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK); ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
} else {
// re-use existing routes as much as possible
if (net_info->wires.count(src_wire) == 0)
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
std::vector<PortRef> users_array = net_info->users; for (auto &user_it : net_info->users) {
ctx->shuffle(users_array); auto dst_bel = user_it.cell->bel;
if (dst_bel == BelId())
log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx),
user_it.cell->type.c_str(ctx));
if (ctx->debug)
log(" Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx));
IdString user_port = user_it.port;
auto user_port_it = user_it.cell->pins.find(user_port);
if (user_port_it != user_it.cell->pins.end())
user_port = user_port_it->second;
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
if (dst_wire == WireId())
log_error("No wire found for port %s (pin %s) on destination "
"cell %s (bel %s).\n",
user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx),
ctx->getBelName(dst_bel).c_str(ctx));
std::function<delay_t(WireId)> register_existing_path = [ctx, net_info, &src_wires, &register_existing_path](WireId wire) -> delay_t {
auto it = src_wires.find(wire);
if (it != src_wires.end())
return it->second;
PipId pip = net_info->wires.at(wire).pip;
delay_t delay = register_existing_path(ctx->getPipSrcWire(pip));
delay += ctx->getPipDelay(pip).maxDelay();
delay += ctx->getWireDelay(wire).maxDelay();
delay -= 2*ctx->getDelayEpsilon();
src_wires[wire] = delay;
return delay;
};
WireId cursor = dst_wire;
while (src_wires.count(cursor) == 0) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end())
goto check_next_user_for_existing_path;
NPNR_ASSERT(it->second.pip != PipId());
cursor = ctx->getPipSrcWire(it->second.pip);
}
register_existing_path(dst_wire);
check_next_user_for_existing_path:;
}
std::vector<WireId> ripup_wires;
for (auto &it : net_info->wires)
if (src_wires.count(it.first) == 0)
ripup_wires.push_back(it.first);
for (auto &it : ripup_wires) {
if (ctx->debug)
log(" Unbind dangling wire for net %s: %s\n",
net_name.c_str(ctx), ctx->getWireName(it).c_str(ctx));
ctx->unbindWire(it);
}
}
for (auto &user_it : users_array) { for (auto &user_it : users_array) {
if (ctx->debug) if (ctx->debug)
@ -398,6 +483,205 @@ struct Router
} }
}; };
struct RouteJob
{
IdString net;
int user_idx = -1;
delay_t slack = 0;
int randtag = 0;
struct Greater
{
bool operator()(const RouteJob &lhs, const RouteJob &rhs) const noexcept
{
return lhs.slack == rhs.slack ? lhs.randtag > rhs.randtag : lhs.slack > rhs.slack;
}
};
};
void addFullNetRouteJob(Context *ctx, IdString net_name,
std::unordered_map<IdString, std::vector<bool>> &cache,
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
{
NetInfo *net_info = ctx->nets.at(net_name).get();
if (net_info->driver.cell == nullptr)
return;
auto src_bel = net_info->driver.cell->bel;
if (src_bel == BelId())
log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
net_info->driver.cell->type.c_str(ctx));
IdString driver_port = net_info->driver.port;
auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
if (driver_port_it != net_info->driver.cell->pins.end())
driver_port = driver_port_it->second;
auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
if (src_wire == WireId())
log_error("No wire found for port %s (pin %s) on source cell %s "
"(bel %s).\n",
net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
ctx->getBelName(src_bel).c_str(ctx));
auto &net_cache = cache[net_name];
if (net_cache.empty())
net_cache.resize(net_info->users.size());
RouteJob job;
job.net = net_name;
job.user_idx = -1;
job.slack = 0;
job.randtag = ctx->rng();
bool got_slack = false;
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
{
if (net_cache[user_idx])
continue;
auto &user_info = net_info->users[user_idx];
auto dst_bel = user_info.cell->bel;
if (dst_bel == BelId())
log_error("Destination cell %s (%s) is not mapped to a bel.\n",
user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx));
IdString user_port = user_info.port;
auto user_port_it = user_info.cell->pins.find(user_port);
if (user_port_it != user_info.cell->pins.end())
user_port = user_port_it->second;
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
if (dst_wire == WireId())
log_error("No wire found for port %s (pin %s) on destination "
"cell %s (bel %s).\n",
user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx),
ctx->getBelName(dst_bel).c_str(ctx));
if (user_idx == 0)
job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
else
job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire));
WireId cursor = dst_wire;
while (src_wire != cursor) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end()) {
if (!got_slack)
job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
else
job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire));
got_slack = true;
break;
}
NPNR_ASSERT(it->second.pip != PipId());
cursor = ctx->getPipSrcWire(it->second.pip);
}
}
queue.push(job);
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
net_cache[user_idx] = true;
}
void addNetRouteJobs(Context *ctx, IdString net_name,
std::unordered_map<IdString, std::vector<bool>> &cache,
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
{
NetInfo *net_info = ctx->nets.at(net_name).get();
if (net_info->driver.cell == nullptr)
return;
auto src_bel = net_info->driver.cell->bel;
if (src_bel == BelId())
log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
net_info->driver.cell->type.c_str(ctx));
IdString driver_port = net_info->driver.port;
auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
if (driver_port_it != net_info->driver.cell->pins.end())
driver_port = driver_port_it->second;
auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
if (src_wire == WireId())
log_error("No wire found for port %s (pin %s) on source cell %s "
"(bel %s).\n",
net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
ctx->getBelName(src_bel).c_str(ctx));
auto &net_cache = cache[net_name];
if (net_cache.empty())
net_cache.resize(net_info->users.size());
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
{
if (net_cache[user_idx])
continue;
auto &user_info = net_info->users[user_idx];
auto dst_bel = user_info.cell->bel;
if (dst_bel == BelId())
log_error("Destination cell %s (%s) is not mapped to a bel.\n",
user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx));
IdString user_port = user_info.port;
auto user_port_it = user_info.cell->pins.find(user_port);
if (user_port_it != user_info.cell->pins.end())
user_port = user_port_it->second;
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
if (dst_wire == WireId())
log_error("No wire found for port %s (pin %s) on destination "
"cell %s (bel %s).\n",
user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx),
ctx->getBelName(dst_bel).c_str(ctx));
WireId cursor = dst_wire;
while (src_wire != cursor) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end()) {
if (ctx->debug)
log("Adding job [%s %d]: %s %s (%s) -> %s %s (%s)\n",
net_name.c_str(ctx), user_idx,
ctx->getBelName(src_bel).c_str(ctx), driver_port.c_str(ctx),
ctx->getWireName(src_wire).c_str(ctx),
ctx->getBelName(dst_bel).c_str(ctx), user_port.c_str(ctx),
ctx->getWireName(dst_wire).c_str(ctx));
RouteJob job;
job.net = net_name;
job.user_idx = user_idx;
job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
job.randtag = ctx->rng();
queue.push(job);
net_cache[user_idx] = true;
break;
}
NPNR_ASSERT(it->second.pip != PipId());
cursor = ctx->getPipSrcWire(it->second.pip);
}
}
}
} // namespace } // namespace
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -411,82 +695,24 @@ bool router1(Context *ctx)
log_break(); log_break();
log_info("Routing..\n"); log_info("Routing..\n");
std::unordered_set<IdString> netsQueue;
ctx->lock(); ctx->lock();
for (auto &net_it : ctx->nets) {
auto net_name = net_it.first;
auto net_info = net_it.second.get();
if (net_info->driver.cell == nullptr) std::unordered_map<IdString, std::vector<bool>> jobCache;
continue; std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue;
if (!net_info->wires.empty()) for (auto &net_it : ctx->nets)
continue; addNetRouteJobs(ctx, net_it.first, jobCache, jobQueue);
netsQueue.insert(net_name); if (jobQueue.empty()) {
} log_info("found no unrouted source-sink pairs. no routing necessary.\n");
if (netsQueue.empty()) {
log_info("found no unrouted nets. no routing necessary.\n");
return true; return true;
} }
log_info("found %d unrouted nets. starting routing procedure.\n", int(netsQueue.size())); log_info("found %d unrouted source-sink pairs. starting routing procedure.\n", int(jobQueue.size()));
delay_t estimatedTotalDelay = 0.0;
int estimatedTotalDelayCnt = 0;
for (auto net_name : netsQueue) {
auto net_info = ctx->nets.at(net_name).get();
auto src_bel = net_info->driver.cell->bel;
if (src_bel == BelId())
continue;
IdString driver_port = net_info->driver.port;
auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
if (driver_port_it != net_info->driver.cell->pins.end())
driver_port = driver_port_it->second;
auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
if (src_wire == WireId())
continue;
for (auto &user_it : net_info->users) {
auto dst_bel = user_it.cell->bel;
if (dst_bel == BelId())
continue;
IdString user_port = user_it.port;
auto user_port_it = user_it.cell->pins.find(user_port);
if (user_port_it != user_it.cell->pins.end())
user_port = user_port_it->second;
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
if (dst_wire == WireId())
continue;
estimatedTotalDelay += ctx->estimateDelay(src_wire, dst_wire);
estimatedTotalDelayCnt++;
}
}
ctx->unlock();
log_info("estimated total wire delay: %.2f (avg %.2f)\n", float(estimatedTotalDelay),
float(estimatedTotalDelay) / estimatedTotalDelayCnt);
int iterCnt = 0; int iterCnt = 0;
while (!netsQueue.empty()) { while (!jobQueue.empty()) {
if (iterCnt == 200) { if (iterCnt == 200) {
log_warning("giving up after %d iterations.\n", iterCnt); log_warning("giving up after %d iterations.\n", iterCnt);
log_info("Checksum: 0x%08x\n", ctx->checksum()); log_info("Checksum: 0x%08x\n", ctx->checksum());
@ -500,27 +726,34 @@ bool router1(Context *ctx)
if (ctx->verbose) if (ctx->verbose)
log_info("-- %d --\n", iterCnt); 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) 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()); while (!jobQueue.empty()) {
ctx->sorted_shuffle(netsArray); if(ctx->debug)
netsQueue.clear(); log("Next job slack: %f\n", double(jobQueue.top().slack));
for (auto net_name : netsArray) { auto net_name = jobQueue.top().net;
if (printNets) auto user_idx = jobQueue.top().user_idx;
log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx), jobQueue.pop();
int(ctx->nets.at(net_name)->users.size()));
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; visitCnt += router.visitCnt;
revisitCnt += router.revisitCnt; revisitCnt += router.revisitCnt;
overtimeRevisitCnt += router.overtimeRevisitCnt; overtimeRevisitCnt += router.overtimeRevisitCnt;
@ -529,20 +762,24 @@ bool router1(Context *ctx)
if (printNets) if (printNets)
log_info(" failed to route to %s.\n", ctx->getWireName(router.failedDest).c_str(ctx)); log_info(" failed to route to %s.\n", ctx->getWireName(router.failedDest).c_str(ctx));
ripupQueue.insert(net_name); ripupQueue.insert(net_name);
failedCnt++;
} else {
normalRouteNets.insert(net_name);
} }
if ((ctx->verbose || iterCnt == 1) && !printNets && (netCnt % 100 == 0)) { if ((ctx->verbose || iterCnt == 1) && !printNets && (jobCnt % 100 == 0)) {
log_info(" processed %d nets. (%d routed, %d failed)\n", netCnt, netCnt - int(ripupQueue.size()), log_info(" processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt);
int(ripupQueue.size()));
ctx->yield(); ctx->yield();
} }
} }
int normalRouteCnt = netCnt - int(ripupQueue.size()); NPNR_ASSERT(jobQueue.empty());
jobCache.clear();
if ((ctx->verbose || iterCnt == 1) && (netCnt % 100 != 0)) if ((ctx->verbose || iterCnt == 1) && (jobCnt % 100 != 0)) {
log_info(" processed %d nets. (%d routed, %d failed)\n", netCnt, normalRouteCnt, log_info(" processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt);
int(ripupQueue.size())); ctx->yield();
}
if (ctx->verbose) if (ctx->verbose)
log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime " log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime "
@ -560,7 +797,7 @@ bool router1(Context *ctx)
visitCnt = 0; visitCnt = 0;
revisitCnt = 0; revisitCnt = 0;
overtimeRevisitCnt = 0; overtimeRevisitCnt = 0;
netCnt = 0; int netCnt = 0;
int ripCnt = 0; int ripCnt = 0;
std::vector<IdString> ripupArray(ripupQueue.begin(), ripupQueue.end()); 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), log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx),
int(ctx->nets.at(net_name)->users.size())); 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++; netCnt++;
visitCnt += router.visitCnt; 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)); log_error("Net %s is impossible to route.\n", net_name.c_str(ctx));
for (auto it : router.rippedNets) for (auto it : router.rippedNets)
netsQueue.insert(it); addFullNetRouteJob(ctx, it, jobCache, jobQueue);
if (printNets) { if (printNets) {
if (router.rippedNets.size() < 10) { if (router.rippedNets.size() < 10) {
@ -610,16 +847,16 @@ bool router1(Context *ctx)
"overtime revisits).\n", "overtime revisits).\n",
visitCnt, (100.0 * revisitCnt) / visitCnt, (100.0 * overtimeRevisitCnt) / visitCnt); 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 " log_info(" ripped up %d previously routed nets. continue "
"routing.\n", "routing.\n",
int(netsQueue.size())); int(jobQueue.size()));
} }
if (!ctx->verbose) if (!ctx->verbose)
log_info("iteration %d: routed %d nets without ripup, routed %d " log_info("iteration %d: routed %d nets without ripup, routed %d nets with ripup.\n",
"nets with ripup.\n", iterCnt, int(normalRouteNets.size()), int(ripupQueue.size()));
iterCnt, normalRouteCnt, int(ripupQueue.size()));
totalVisitCnt += visitCnt; totalVisitCnt += visitCnt;
totalRevisitCnt += revisitCnt; totalRevisitCnt += revisitCnt;
@ -638,16 +875,33 @@ bool router1(Context *ctx)
totalVisitCnt, (100.0 * totalRevisitCnt) / totalVisitCnt, totalVisitCnt, (100.0 * totalRevisitCnt) / totalVisitCnt,
(100.0 * totalOvertimeRevisitCnt) / 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()); log_info("Checksum: 0x%08x\n", ctx->checksum());
#ifndef NDEBUG #ifndef NDEBUG
ctx->lock();
ctx->check(); ctx->check();
ctx->unlock(); ctx->unlock();
#endif #endif
return true; return true;
} catch (log_execution_error_exception) { } catch (log_execution_error_exception) {
#ifndef NDEBUG #ifndef NDEBUG
ctx->lock();
ctx->check(); ctx->check();
ctx->unlock(); ctx->unlock();
#endif #endif

View File

@ -578,6 +578,12 @@ struct Arch : BaseCtx
return wire_to_net.at(wire); return wire_to_net.at(wire);
} }
DelayInfo getWireDelay(WireId wire) const
{
DelayInfo delay;
return delay;
}
WireRange getWires() const WireRange getWires() const
{ {
WireRange range; WireRange range;

View File

@ -32,9 +32,14 @@ struct DelayInfo
{ {
delay_t delay = 0; delay_t delay = 0;
delay_t raiseDelay() const { return delay; } delay_t minRaiseDelay() const { return delay; }
delay_t fallDelay() const { return delay; } delay_t maxRaiseDelay() const { return delay; }
delay_t avgDelay() 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 DelayInfo operator+(const DelayInfo &other) const
{ {

View File

@ -29,8 +29,8 @@ void Arch::addWire(IdString name, int x, int y)
NPNR_ASSERT(wires.count(name) == 0); NPNR_ASSERT(wires.count(name) == 0);
WireInfo &wi = wires[name]; WireInfo &wi = wires[name];
wi.name = name; wi.name = name;
wi.grid_x = x; wi.x = x;
wi.grid_y = y; wi.y = y;
wire_ids.push_back(name); wire_ids.push_back(name);
} }
@ -62,18 +62,28 @@ void Arch::addAlias(IdString name, IdString srcWire, IdString dstWire, DelayInfo
pip_ids.push_back(name); 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(bels.count(name) == 0);
NPNR_ASSERT(bel_by_loc.count(loc) == 0);
BelInfo &bi = bels[name]; BelInfo &bi = bels[name];
bi.name = name; bi.name = name;
bi.type = type; bi.type = type;
bi.grid_x = x; bi.x = x;
bi.grid_y = y; bi.y = y;
bi.z = z;
bi.gb = gb; bi.gb = gb;
bel_ids.push_back(name); bel_ids.push_back(name);
bel_ids_by_type[type].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) 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 void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
{ {
x = bels.at(bel).grid_x; x = bels.at(bel).x;
y = bels.at(bel).grid_y; y = bels.at(bel).y;
gb = bels.at(bel).gb; 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 &s = wires.at(src);
const WireInfo &d = wires.at(dst); const WireInfo &d = wires.at(dst);
int dx = abs(s.grid_x - d.grid_x); int dx = abs(s.x - d.x);
int dy = abs(s.grid_y - d.grid_y); int dy = abs(s.y - d.y);
return (dx + dy) * grid_distance_to_delay; return (dx + dy) * grid_distance_to_delay;
} }

View File

@ -44,7 +44,7 @@ struct WireInfo
BelPin uphill_bel_pin; BelPin uphill_bel_pin;
std::vector<BelPin> downhill_bel_pins; std::vector<BelPin> downhill_bel_pins;
DecalXY decalxy; DecalXY decalxy;
int grid_x, grid_y; int x, y;
}; };
struct PinInfo struct PinInfo
@ -59,7 +59,7 @@ struct BelInfo
IdString name, type, bound_cell; IdString name, type, bound_cell;
std::unordered_map<IdString, PinInfo> pins; std::unordered_map<IdString, PinInfo> pins;
DecalXY decalxy; DecalXY decalxy;
int grid_x, grid_y; int x, y, z;
bool gb; bool gb;
}; };
@ -85,6 +85,9 @@ struct Arch : BaseCtx
std::vector<IdString> bel_ids, wire_ids, pip_ids; std::vector<IdString> bel_ids, wire_ids, pip_ids;
std::unordered_map<IdString, std::vector<IdString>> bel_ids_by_type; 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; std::unordered_map<DecalId, std::vector<GraphicElement>> decal_graphics;
DecalXY frame_decalxy; DecalXY frame_decalxy;
@ -94,7 +97,7 @@ struct Arch : BaseCtx
void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay); void addPip(IdString name, IdString srcWire, IdString dstWire, DelayInfo delay);
void addAlias(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 addBelInput(IdString bel, IdString name, IdString wire);
void addBelOutput(IdString bel, IdString name, IdString wire); void addBelOutput(IdString bel, IdString name, IdString wire);
void addBelInout(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; BelId getBelByName(IdString name) const;
IdString getBelName(BelId bel) 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; uint32_t getBelChecksum(BelId bel) const;
void bindBel(BelId bel, IdString cell, PlaceStrength strength); void bindBel(BelId bel, IdString cell, PlaceStrength strength);
void unbindBel(BelId bel); void unbindBel(BelId bel);
@ -150,6 +157,7 @@ struct Arch : BaseCtx
bool checkWireAvail(WireId wire) const; bool checkWireAvail(WireId wire) const;
IdString getBoundWireNet(WireId wire) const; IdString getBoundWireNet(WireId wire) const;
IdString getConflictingWireNet(WireId wire) const; IdString getConflictingWireNet(WireId wire) const;
DelayInfo getWireDelay(WireId wire) const { return DelayInfo(); }
const std::vector<WireId> &getWires() const; const std::vector<WireId> &getWires() const;
PipId getPipByName(IdString name) const; PipId getPipByName(IdString name) const;

View File

@ -29,11 +29,14 @@ struct DelayInfo
{ {
delay_t delay = 0; 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 DelayInfo operator+(const DelayInfo &other) const
{ {

View File

@ -116,7 +116,7 @@ void BaseMainWindow::createMenusAndBars()
actionOpen->setStatusTip("Open an existing project file"); actionOpen->setStatusTip("Open an existing project file");
connect(actionOpen, SIGNAL(triggered()), this, SLOT(open_proj())); 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->setIcon(QIcon(":/icons/resources/save.png"));
actionSave->setShortcuts(QKeySequence::Save); actionSave->setShortcuts(QKeySequence::Save);
actionSave->setStatusTip("Save existing project to disk"); actionSave->setStatusTip("Save existing project to disk");

View File

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

View File

@ -230,6 +230,9 @@ void DesignWidget::addToHistory(QTreeWidgetItem *item)
void DesignWidget::newContext(Context *ctx) void DesignWidget::newContext(Context *ctx)
{ {
treeWidget->clear(); treeWidget->clear();
// reset pointers since they are not valid after clear
nets_root = nullptr;
cells_root = nullptr;
history_ignore = false; history_ignore = false;
history_index = -1; history_index = -1;
history.clear(); history.clear();
@ -334,16 +337,7 @@ void DesignWidget::newContext(Context *ctx)
for (auto pip : nameToItem[2].toStdMap()) { for (auto pip : nameToItem[2].toStdMap()) {
pip_root->addChild(pip.second); pip_root->addChild(pip.second);
} }
updateTree();
// 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);
} }
void DesignWidget::updateTree() void DesignWidget::updateTree()
@ -513,6 +507,14 @@ void DesignWidget::onItemSelectionChanged()
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx), addProperty(topItem, QVariant::String, "Conflicting Net", ctx->getConflictingWireNet(wire).c_str(ctx),
ElementType::NET); 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"); QtProperty *belpinItem = addSubGroup(topItem, "BelPin Uphill");
BelPin uphill = ctx->getBelPinUphill(wire); BelPin uphill = ctx->getBelPinUphill(wire);
if (uphill.bel != BelId()) if (uphill.bel != BelId())
@ -535,7 +537,7 @@ void DesignWidget::onItemSelectionChanged()
} }
int counter = 0; int counter = 0;
QtProperty *pipsDownItem = addSubGroup(downhillItem, "Pips Downhill"); QtProperty *pipsDownItem = addSubGroup(topItem, "Pips Downhill");
for (const auto &item : ctx->getPipsDownhill(wire)) { for (const auto &item : ctx->getPipsDownhill(wire)) {
addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP); addProperty(pipsDownItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
counter++; counter++;
@ -546,7 +548,7 @@ void DesignWidget::onItemSelectionChanged()
} }
counter = 0; counter = 0;
QtProperty *pipsUpItem = addSubGroup(downhillItem, "Pips Uphill"); QtProperty *pipsUpItem = addSubGroup(topItem, "Pips Uphill");
for (const auto &item : ctx->getPipsUphill(wire)) { for (const auto &item : ctx->getPipsUphill(wire)) {
addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP); addProperty(pipsUpItem, QVariant::String, "", ctx->getPipName(item).c_str(ctx), ElementType::PIP);
counter++; counter++;
@ -572,9 +574,10 @@ void DesignWidget::onItemSelectionChanged()
DelayInfo delay = ctx->getPipDelay(pip); DelayInfo delay = ctx->getPipDelay(pip);
QtProperty *delayItem = addSubGroup(topItem, "Delay"); QtProperty *delayItem = addSubGroup(topItem, "Delay");
addProperty(delayItem, QVariant::Double, "Raise", delay.raiseDelay()); addProperty(delayItem, QVariant::Double, "Min Raise", delay.minRaiseDelay());
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay()); addProperty(delayItem, QVariant::Double, "Max Raise", delay.maxRaiseDelay());
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay()); addProperty(delayItem, QVariant::Double, "Min Fall", delay.minFallDelay());
addProperty(delayItem, QVariant::Double, "Max Fall", delay.maxFallDelay());
} else if (type == ElementType::NET) { } else if (type == ElementType::NET) {
NetInfo *net = ctx->nets.at(c).get(); NetInfo *net = ctx->nets.at(c).get();

View File

@ -20,9 +20,12 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <QAction> #include <QAction>
#include <QFileDialog> #include <QFileDialog>
#include <QFileInfo>
#include <QIcon> #include <QIcon>
#include <QInputDialog> #include <QInputDialog>
#include <QLineEdit> #include <QLineEdit>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include "bitstream.h" #include "bitstream.h"
#include "design_utils.h" #include "design_utils.h"
#include "jsonparse.h" #include "jsonparse.h"
@ -220,8 +223,10 @@ void MainWindow::new_proj()
0, false, &ok); 0, false, &ok);
if (ok && !item.isEmpty()) { if (ok && !item.isEmpty()) {
currentProj = "";
currentJson = "";
currentPCF = "";
disableActions(); disableActions();
preload_pcf = "";
chipArgs.package = package.toStdString().c_str(); chipArgs.package = package.toStdString().c_str();
ctx = std::unique_ptr<Context>(new Context(chipArgs)); ctx = std::unique_ptr<Context>(new Context(chipArgs));
actionLoadJSON->setEnabled(true); actionLoadJSON->setEnabled(true);
@ -233,14 +238,16 @@ void MainWindow::new_proj()
void MainWindow::load_json(std::string filename, std::string pcf) void MainWindow::load_json(std::string filename, std::string pcf)
{ {
preload_pcf = pcf;
disableActions(); disableActions();
currentJson = filename;
currentPCF = pcf;
Q_EMIT task->loadfile(filename); Q_EMIT task->loadfile(filename);
} }
void MainWindow::load_pcf(std::string filename) void MainWindow::load_pcf(std::string filename)
{ {
disableActions(); disableActions();
currentPCF = filename;
Q_EMIT task->loadpcf(filename); Q_EMIT task->loadpcf(filename);
} }
@ -252,10 +259,79 @@ void MainWindow::newContext(Context *ctx)
void MainWindow::open_proj() 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")); QString fileName = QFileDialog::getOpenFileName(this, QString("Open Project"), QString(), QString("*.proj"));
if (!fileName.isEmpty()) { if (!fileName.isEmpty()) {
try {
namespace pt = boost::property_tree;
std::string fn = fileName.toStdString(); std::string fn = fileName.toStdString();
currentProj = fn;
disableActions(); 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() void MainWindow::save_asc()
{ {
@ -303,6 +407,7 @@ void MainWindow::disableActions()
actionNew->setEnabled(true); actionNew->setEnabled(true);
actionOpen->setEnabled(true); actionOpen->setEnabled(true);
actionSave->setEnabled(!currentJson.empty());
} }
void MainWindow::loadfile_finished(bool status) void MainWindow::loadfile_finished(bool status)
@ -312,12 +417,12 @@ void MainWindow::loadfile_finished(bool status)
log("Loading design successful.\n"); log("Loading design successful.\n");
actionLoadPCF->setEnabled(true); actionLoadPCF->setEnabled(true);
actionPack->setEnabled(true); actionPack->setEnabled(true);
if (!preload_pcf.empty()) if (!currentPCF.empty())
load_pcf(preload_pcf); load_pcf(currentPCF);
Q_EMIT updateTreeView(); Q_EMIT updateTreeView();
} else { } else {
log("Loading design failed.\n"); log("Loading design failed.\n");
preload_pcf = ""; currentPCF = "";
} }
} }

View File

@ -79,7 +79,10 @@ class MainWindow : public BaseMainWindow
bool timing_driven; bool timing_driven;
ArchArgs chipArgs; ArchArgs chipArgs;
std::string preload_pcf;
std::string currentProj;
std::string currentJson;
std::string currentPCF;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -255,11 +255,52 @@ BelId Arch::getBelByName(IdString name) const
return ret; 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 Arch::getBelsAtSameTile(BelId bel) const
{ {
BelRange br; BelRange br;
NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel != BelId());
// This requires Bels at the same tile are consecutive
int x = chip_info->bel_data[bel.index].x; int x = chip_info->bel_data[bel.index].x;
int y = chip_info->bel_data[bel.index].y; int y = chip_info->bel_data[bel.index].y;
int start = bel.index, end = bel.index; int start = bel.index, end = bel.index;

View File

@ -350,6 +350,7 @@ struct Arch : BaseCtx
mutable std::unordered_map<IdString, int> bel_by_name; mutable std::unordered_map<IdString, int> bel_by_name;
mutable std::unordered_map<IdString, int> wire_by_name; mutable std::unordered_map<IdString, int> wire_by_name;
mutable std::unordered_map<IdString, int> pip_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> bel_to_cell;
std::vector<IdString> wire_to_net; std::vector<IdString> wire_to_net;
@ -442,7 +443,24 @@ struct Arch : BaseCtx
return range; 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 BelType getBelType(BelId bel) const
{ {
@ -534,6 +552,12 @@ struct Arch : BaseCtx
return wire_to_net[wire.index]; return wire_to_net[wire.index];
} }
DelayInfo getWireDelay(WireId wire) const
{
DelayInfo delay;
return delay;
}
WireRange getWires() const WireRange getWires() const
{ {
WireRange range; 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 estimateDelay(WireId src, WireId dst) const;
delay_t getDelayEpsilon() const { return 20; } delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; } delay_t getRipupDelayPenalty() const { return 200; }

View File

@ -21,8 +21,6 @@
#error Include "archdefs.h" via "nextpnr.h" only. #error Include "archdefs.h" via "nextpnr.h" only.
#endif #endif
#include <boost/functional/hash.hpp>
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t; typedef int delay_t;
@ -31,9 +29,14 @@ struct DelayInfo
{ {
delay_t delay = 0; delay_t delay = 0;
delay_t raiseDelay() const { return delay; } delay_t minRaiseDelay() const { return delay; }
delay_t fallDelay() const { return delay; } delay_t maxRaiseDelay() const { return delay; }
delay_t avgDelay() 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 DelayInfo operator+(const DelayInfo &other) const
{ {

View File

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

View File

@ -27,6 +27,7 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
void write_asc(const Context *ctx, std::ostream &out); void write_asc(const Context *ctx, std::ostream &out);
bool read_asc(Context *ctx, std::istream &in);
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

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

View File

@ -66,6 +66,14 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
} }
} }
void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2)
{
if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted()) {
std::string msg = "Conflicting options '"+ std::string(opt1) + "' and '" + std::string(opt1) + "'.";
log_error("%s\n",msg.c_str());
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
try { 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()("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()("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()("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()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("version,V", "show version"); options.add_options()("version,V", "show version");
options.add_options()("tmfuzz", "run path delay estimate fuzzer"); options.add_options()("tmfuzz", "run path delay estimate fuzzer");
@ -121,13 +130,17 @@ int main(int argc, char *argv[])
po::store(parsed, vm); po::store(parsed, vm);
po::notify(vm); po::notify(vm);
} } catch (std::exception &e) {
catch (std::exception &e) {
std::cout << e.what() << "\n"; std::cout << e.what() << "\n";
return 1; 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) { if (vm.count("help") || argc == 1) {
help: help:
std::cout << boost::filesystem::basename(argv[0]) std::cout << boost::filesystem::basename(argv[0])
@ -353,6 +366,13 @@ int main(int argc, char *argv[])
if (vm.count("no-tmdriv")) if (vm.count("no-tmdriv"))
ctx->timing_driven = false; 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 #ifndef NO_GUI
if (vm.count("gui")) { if (vm.count("gui")) {
Application a(argc, argv); Application a(argc, argv);

15
ice40/picorv32.proj Normal file
View File

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

View File

@ -4,3 +4,4 @@ rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
yosys -p 'synth_ice40 -json picorv32.json -top top' picorv32.v picorv32_top.v yosys -p 'synth_ice40 -json picorv32.json -top top' picorv32.v picorv32_top.v
../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json ../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json
icetime -d hx8k -t picorv32.asc