Merge branch 'redist_slack' into 'redist_slack'
Redist slack See merge request SymbioticEDA/nextpnr!14
This commit is contained in:
commit
eeb93d6eda
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, ®ister_existing_path](WireId wire) -> delay_t {
|
||||||
|
auto it = src_wires.find(wire);
|
||||||
|
if (it != src_wires.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
PipId pip = net_info->wires.at(wire).pip;
|
||||||
|
delay_t delay = register_existing_path(ctx->getPipSrcWire(pip));
|
||||||
|
delay += ctx->getPipDelay(pip).maxDelay();
|
||||||
|
delay += ctx->getWireDelay(wire).maxDelay();
|
||||||
|
delay -= 2*ctx->getDelayEpsilon();
|
||||||
|
src_wires[wire] = delay;
|
||||||
|
|
||||||
|
return delay;
|
||||||
|
};
|
||||||
|
|
||||||
|
WireId cursor = dst_wire;
|
||||||
|
while (src_wires.count(cursor) == 0) {
|
||||||
|
auto it = net_info->wires.find(cursor);
|
||||||
|
if (it == net_info->wires.end())
|
||||||
|
goto check_next_user_for_existing_path;
|
||||||
|
NPNR_ASSERT(it->second.pip != PipId());
|
||||||
|
cursor = ctx->getPipSrcWire(it->second.pip);
|
||||||
|
}
|
||||||
|
|
||||||
|
register_existing_path(dst_wire);
|
||||||
|
check_next_user_for_existing_path:;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WireId> ripup_wires;
|
||||||
|
for (auto &it : net_info->wires)
|
||||||
|
if (src_wires.count(it.first) == 0)
|
||||||
|
ripup_wires.push_back(it.first);
|
||||||
|
|
||||||
|
for (auto &it : ripup_wires) {
|
||||||
|
if (ctx->debug)
|
||||||
|
log(" Unbind dangling wire for net %s: %s\n",
|
||||||
|
net_name.c_str(ctx), ctx->getWireName(it).c_str(ctx));
|
||||||
|
ctx->unbindWire(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto &user_it : users_array) {
|
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();
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()); }
|
||||||
|
@ -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;
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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()); }
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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 = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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()); }
|
||||||
|
@ -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;
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
"input": {
|
"input": {
|
||||||
"json": "blinky.json",
|
"json": "blinky.json",
|
||||||
"pcf": "blinky.pcf"
|
"pcf": "blinky.pcf"
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"freq": "50"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
15
ice40/picorv32.proj
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"version": "1",
|
||||||
|
"name": "picorv32",
|
||||||
|
"arch": {
|
||||||
|
"name": "ice40",
|
||||||
|
"type": "hx8k",
|
||||||
|
"package": "ct256"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"json": "picorv32.json",
|
||||||
|
"pcf": "icebreaker.pcf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,3 +4,4 @@ rm -f picorv32.v
|
|||||||
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user