Merge branch 'redist_slack' into 'redist_slack'

Redist slack

See merge request SymbioticEDA/nextpnr!14
This commit is contained in:
Eddie Hung 2018-07-21 20:08:53 +00:00
commit eeb93d6eda
22 changed files with 742 additions and 175 deletions

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;
ripup_net(ctx, net_name); if (user_idx < 0) {
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK); // route all users
users_array = net_info->users;
ctx->shuffle(users_array);
} else {
// route only the selected user
users_array.push_back(net_info->users[user_idx]);
}
std::vector<PortRef> users_array = net_info->users; if (reroute) {
ctx->shuffle(users_array); // complete ripup
ripup_net(ctx, net_name);
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
} else {
// re-use existing routes as much as possible
if (net_info->wires.count(src_wire) == 0)
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
for (auto &user_it : net_info->users) {
auto dst_bel = user_it.cell->bel;
if (dst_bel == BelId())
log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx),
user_it.cell->type.c_str(ctx));
if (ctx->debug)
log(" Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx));
IdString user_port = user_it.port;
auto user_port_it = user_it.cell->pins.find(user_port);
if (user_port_it != user_it.cell->pins.end())
user_port = user_port_it->second;
auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
if (dst_wire == WireId())
log_error("No wire found for port %s (pin %s) on destination "
"cell %s (bel %s).\n",
user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx),
ctx->getBelName(dst_bel).c_str(ctx));
std::function<delay_t(WireId)> register_existing_path = [ctx, net_info, &src_wires, &register_existing_path](WireId wire) -> delay_t {
auto it = src_wires.find(wire);
if (it != src_wires.end())
return it->second;
PipId pip = net_info->wires.at(wire).pip;
delay_t delay = register_existing_path(ctx->getPipSrcWire(pip));
delay += ctx->getPipDelay(pip).maxDelay();
delay += ctx->getWireDelay(wire).maxDelay();
delay -= 2*ctx->getDelayEpsilon();
src_wires[wire] = delay;
return delay;
};
WireId cursor = dst_wire;
while (src_wires.count(cursor) == 0) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end())
goto check_next_user_for_existing_path;
NPNR_ASSERT(it->second.pip != PipId());
cursor = ctx->getPipSrcWire(it->second.pip);
}
register_existing_path(dst_wire);
check_next_user_for_existing_path:;
}
std::vector<WireId> ripup_wires;
for (auto &it : net_info->wires)
if (src_wires.count(it.first) == 0)
ripup_wires.push_back(it.first);
for (auto &it : ripup_wires) {
if (ctx->debug)
log(" Unbind dangling wire for net %s: %s\n",
net_name.c_str(ctx), ctx->getWireName(it).c_str(ctx));
ctx->unbindWire(it);
}
}
for (auto &user_it : users_array) { for (auto &user_it : users_array) {
if (ctx->debug) if (ctx->debug)
@ -398,25 +483,210 @@ 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
static void prioritise_nets(Context *ctx, std::vector<IdString> &netsArray)
{
std::unordered_map<IdString, delay_t> netScores;
for (auto net_name : netsArray) {
delay_t score = std::numeric_limits<delay_t>::max();
for (const auto &sink : ctx->nets.at(net_name)->users) {
if (sink.budget < score)
score = sink.budget;
}
netScores[net_name] = score;
}
std::sort(netsArray.begin(), netsArray.end(),
[&netScores](IdString a, IdString b) { return netScores[a] < netScores[b]; });
}
bool router1(Context *ctx) bool router1(Context *ctx)
{ {
try { try {
@ -427,79 +697,22 @@ bool router1(Context *ctx)
log_break(); log_break();
log_info("Routing..\n"); log_info("Routing..\n");
std::unordered_set<IdString> netsQueue; std::unordered_map<IdString, std::vector<bool>> jobCache;
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue;
for (auto &net_it : ctx->nets) { for (auto &net_it : ctx->nets)
auto net_name = net_it.first; addNetRouteJobs(ctx, net_it.first, jobCache, jobQueue);
auto net_info = net_it.second.get();
if (net_info->driver.cell == nullptr) if (jobQueue.empty()) {
continue; log_info("found no unrouted source-sink pairs. no routing necessary.\n");
if (!net_info->wires.empty())
continue;
netsQueue.insert(net_name);
}
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++;
}
}
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());
@ -513,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()) {
prioritise_nets(ctx, 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;
@ -542,18 +762,20 @@ 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()));
} }
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()));
if (ctx->verbose) if (ctx->verbose)
log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime " log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime "
@ -571,18 +793,18 @@ 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());
prioritise_nets(ctx, ripupArray); ctx->sorted_shuffle(ripupArray);
for (auto net_name : ripupArray) { for (auto net_name : ripupArray) {
if (printNets) if (printNets)
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;
@ -593,7 +815,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) {
@ -619,16 +841,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;
@ -645,6 +867,25 @@ 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->check(); ctx->check();

View File

@ -215,24 +215,22 @@ void update_budget(Context *ctx)
auto pi = &user.cell->ports.at(user.port); auto pi = &user.cell->ports.at(user.port);
auto it = updates.find(pi); auto it = updates.find(pi);
if (it == updates.end()) continue; if (it == updates.end()) continue;
user.budget = delays.at(pi) + it->second; auto budget = delays.at(pi) + it->second;
user.budget = ctx->getBudgetOverride(net.second->driver, budget);
// HACK HACK HACK
if (net.second->driver.port == ctx->id("COUT"))
user.budget = 0;
// HACK HACK HACK
// Post-update check // Post-update check
// if (user.budget < 0) if (ctx->verbose) {
// log_warning("port %s.%s, connected to net '%s', has negative " if (user.budget < 0)
// "timing budget of %fns\n", log_warning("port %s.%s, connected to net '%s', has negative "
// user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx), "timing budget of %fns\n",
// ctx->getDelayNS(user.budget)); user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
// if (ctx->verbose) ctx->getDelayNS(user.budget));
// log_info("port %s.%s, connected to net '%s', has " else
// "timing budget of %fns\n", log_info("port %s.%s, connected to net '%s', has "
// user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx), "timing budget of %fns\n",
// ctx->getDelayNS(user.budget)); user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
ctx->getDelayNS(user.budget));
}
} }
} }
} }

View File

@ -328,6 +328,11 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
return abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y); return abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y);
} }
delay_t Arch::getBudgetOverride(const PortRef& pr, delay_t v) const
{
return v;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::place() { return placer1(getCtx()); } bool Arch::place() { return placer1(getCtx()); }

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;
@ -751,6 +757,7 @@ struct Arch : BaseCtx
delay_t getRipupDelayPenalty() const { return 200; } delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; } float getDelayNS(delay_t v) const { return v * 0.001; }
uint32_t getDelayChecksum(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return v; }
delay_t getBudgetOverride(const PortRef& pr, delay_t v) const;
// ------------------------------------------------- // -------------------------------------------------

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

@ -372,6 +372,11 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
return (dx + dy) * grid_distance_to_delay; return (dx + dy) * grid_distance_to_delay;
} }
delay_t Arch::getBudgetOverride(const PortRef& pr, delay_t v) const
{
return v;
}
// --------------------------------------------------------------- // ---------------------------------------------------------------
bool Arch::place() { return placer1(getCtx()); } bool Arch::place() { return placer1(getCtx()); }

View File

@ -157,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;
@ -189,6 +190,7 @@ struct Arch : BaseCtx
delay_t getRipupDelayPenalty() const { return 1.0; } delay_t getRipupDelayPenalty() const { return 1.0; }
float getDelayNS(delay_t v) const { return v; } float getDelayNS(delay_t v) const { return v; }
uint32_t getDelayChecksum(delay_t v) const { return 0; } uint32_t getDelayChecksum(delay_t v) const { return 0; }
delay_t getBudgetOverride(const PortRef& pr, delay_t v) const;
bool pack() { return true; } bool pack() { return true; }
bool place(); bool place();

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

@ -507,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())
@ -529,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++;
@ -540,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++;
@ -566,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"
@ -219,9 +222,11 @@ void MainWindow::new_proj()
QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type), QString package = QInputDialog::getItem(this, "Select package", "Package:", getSupportedPackages(chipArgs.type),
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()) {
std::string fn = fileName.toStdString(); try {
disableActions(); 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() 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

@ -488,6 +488,12 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
return xscale * abs(xd) + yscale * abs(yd) + offset; return xscale * abs(xd) + yscale * abs(yd) + offset;
} }
delay_t Arch::getBudgetOverride(const PortRef& pr, delay_t v) const
{
if (pr.port == id("COUT")) return 0;
return v;
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::place() { return placer1(getCtx()); } bool Arch::place() { return placer1(getCtx()); }

View File

@ -548,6 +548,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;
@ -695,6 +701,7 @@ struct Arch : BaseCtx
delay_t getRipupDelayPenalty() const { return 200; } delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; } float getDelayNS(delay_t v) const { return v * 0.001; }
uint32_t getDelayChecksum(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return v; }
delay_t getBudgetOverride(const PortRef& pr, delay_t v) const;
// ------------------------------------------------- // -------------------------------------------------

View File

@ -29,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)
{ {
@ -467,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);
} }
} }
@ -588,15 +602,18 @@ void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
std::tuple<int, int, int> key(b, x, y); std::tuple<int, int, int> key(b, x, y);
extra_bits.insert(key); extra_bits.insert(key);
*/ */
} else if (!strcmp(tok, ".sym")) { } else if (!strcmp(tok, ".sym")) {
int wireIndex = atoi(strtok(nullptr, " \t\r\n")); int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
const char *name = strtok(nullptr, " \t\r\n"); const char *name = strtok(nullptr, " \t\r\n");
std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
IdString netName = ctx->id(name); IdString netName = ctx->id(name);
created_net->name = netName;
ctx->nets[netName] = std::move(created_net); if (ctx->nets.find(netName) == ctx->nets.end()) {
std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
created_net->name = netName;
ctx->nets[netName] = std::move(created_net);
}
WireId wire; WireId wire;
wire.index = wireIndex; wire.index = wireIndex;
ctx->bindWire(wire, netName, STRENGTH_WEAK); ctx->bindWire(wire, netName, STRENGTH_WEAK);
@ -630,7 +647,139 @@ bool read_asc(Context *ctx, std::istream &in)
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols)); config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
} }
} }
read_config(ctx, in, config); read_config(ctx, in, config);
// Set pips
for (auto pip : ctx->getPips()) {
const PipInfoPOD &pi = ci.pip_data[pip.index];
const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
bool isUsed = true;
for (int i = 0; i < swi.num_bits; i++) {
bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
int8_t cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
isUsed &= !(bool(cbit) ^ val);
}
if (isUsed) {
IdString net = ctx->wire_to_net[pi.dst];
WireId wire;
wire.index = pi.dst;
ctx->unbindWire(wire);
ctx->bindPip(pip, net, STRENGTH_WEAK);
}
}
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == TYPE_ICESTORM_LC) {
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
std::vector<bool> lc(20, false);
bool isUsed = false;
for (int i = 0; i < 20; i++) {
lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i);
isUsed |= lc.at(i);
}
bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk");
isUsed |= neg_clk;
bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet");
isUsed |= carry_set;
if (isUsed) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets and assign values of properties
}
}
if (ctx->getBelType(bel) == TYPE_SB_IO) {
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
bool isUsed = false;
for (int i = 0; i < 6; i++) {
isUsed |= get_config(ti, config.at(y).at(x),
"IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i));
}
bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk");
isUsed |= neg_trigger;
if (isUsed) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets and assign values of properties
}
}
}
// Add cells that are without change in initial state of configuration
for (auto &net : ctx->nets) {
for (auto w : net.second->wires) {
if (w.second.pip == PipId()) {
WireId wire = w.first;
BelPin belpin = ctx->getBelPinUphill(wire);
if (ctx->checkBelAvail(belpin.bel)) {
if (ctx->getBelType(belpin.bel) == TYPE_ICESTORM_LC) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == TYPE_SB_IO) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == TYPE_SB_GB) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == TYPE_SB_WARMBOOT) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == TYPE_ICESTORM_LFOSC) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, name, STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
}
}
}
}
for (auto &cell : ctx->cells) {
if (cell.second->bel != BelId()) {
for (auto &port : cell.second->ports) {
PortPin pin = ctx->portPinFromId(port.first);
WireId wire = ctx->getWireBelPin(cell.second->bel, pin);
if (wire != WireId()) {
IdString name = ctx->getBoundWireNet(wire);
if (name != IdString()) {
port.second.net = ctx->nets[name].get();
PortRef ref;
ref.cell = cell.second.get();
ref.port = port.second.name;
if (port.second.type == PORT_OUT)
ctx->nets[name]->driver = ref;
else
ctx->nets[name]->users.push_back(ref);
}
}
}
}
}
return true; return true;
} catch (log_execution_error_exception) { } catch (log_execution_error_exception) {
return false; return false;

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

@ -68,8 +68,10 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2) 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()) if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted()) {
log_error((std::string("Conflicting options '") + opt1 + "' and '" + opt2 + "'.").c_str()); 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[])

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