timing_opt: Improve heuristics

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2018-12-02 15:49:24 +00:00
parent a990a1576c
commit f53dc8d3c9
3 changed files with 50 additions and 22 deletions

View File

@ -566,16 +566,20 @@ struct Timing
auto &nc = (*net_crit)[net->name];
if (nc.slack.empty())
nc.slack.resize(net->users.size(), std::numeric_limits<delay_t>::max());
#if 0
if (ctx->debug)
log_info("Net %s cd %s\n", net->name.c_str(ctx), startdomain.first.clock.c_str(ctx));
#endif
for (size_t i = 0; i < net->users.size(); i++) {
delay_t slack = nd.min_required.at(i) -
(nd.max_arrival + ctx->getNetinfoRouteDelay(net, net->users.at(i)));
#if 0
if (ctx->debug)
log_info(" user %s.%s required %.02fns arrival %.02f route %.02f slack %.02f\n",
net->users.at(i).cell->name.c_str(ctx), net->users.at(i).port.c_str(ctx),
ctx->getDelayNS(nd.min_required.at(i)), ctx->getDelayNS(nd.max_arrival),
ctx->getDelayNS(ctx->getNetinfoRouteDelay(net, net->users.at(i))), ctx->getDelayNS(slack));
#endif
if (worst_slack.count(startdomain.first))
worst_slack.at(startdomain.first) = std::min(worst_slack.at(startdomain.first), slack);
else
@ -612,7 +616,7 @@ struct Timing
nc.cd_worst_slack = std::min(nc.cd_worst_slack, worst_slack.at(startdomain.first));
}
}
#if 0
if (ctx->debug) {
for (auto &nc : *net_crit) {
NetInfo *net = ctx->nets.at(nc.first).get();
@ -628,6 +632,7 @@ struct Timing
log_break();
}
}
#endif
}
return min_slack;
}

View File

@ -90,7 +90,7 @@ class TimingOptimiser
log_info(" Iteration %d...\n", i);
get_criticalities(ctx, &net_crit);
setup_delay_limits();
auto crit_paths = find_crit_paths(0.95, 1000);
auto crit_paths = find_crit_paths(0.98, 1000);
for (auto &path : crit_paths)
optimise_path(path);
#if 1
@ -140,27 +140,23 @@ class TimingOptimiser
if (port.second.type == PORT_IN) {
if (net->driver.cell == nullptr || net->driver.cell->bel == BelId())
continue;
BelId srcBel = net->driver.cell->bel;
if (ctx->estimateDelay(ctx->getBelPinWire(srcBel, net->driver.port),
ctx->getBelPinWire(cell->bel, port.first)) >
max_net_delay.at(std::make_pair(cell->name, port.first)))
return false;
for (auto user : net->users) {
if (user.cell == cell && user.port == port.first) {
if (ctx->predictDelay(net, user) >
1.1 * max_net_delay.at(std::make_pair(cell->name, port.first)))
return false;
}
}
} else if (port.second.type == PORT_OUT) {
for (auto user : net->users) {
// This could get expensive for high-fanout nets??
BelId dstBel = user.cell->bel;
if (dstBel == BelId())
continue;
if (ctx->estimateDelay(ctx->getBelPinWire(cell->bel, port.first),
ctx->getBelPinWire(dstBel, user.port)) >
max_net_delay.at(std::make_pair(user.cell->name, user.port))) {
#if 0
if (ctx->debug) {
log_info(" est delay %.02fns exceeded maximum %.02fns\n", ctx->getDelayNS(ctx->estimateDelay(ctx->getBelPinWire(cell->bel, port.first),
ctx->getBelPinWire(dstBel, user.port))),
ctx->getDelayNS(max_net_delay.at(std::make_pair(user.cell->name, user.port))));
}
#endif
if (ctx->predictDelay(net, user) >
1.1 * max_net_delay.at(std::make_pair(user.cell->name, user.port))) {
return false;
}
}
@ -370,7 +366,7 @@ class TimingOptimiser
int ccount;
DelayInfo combDelay;
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
if (tpclass != TMG_COMB_OUTPUT)
if (tpclass != TMG_COMB_OUTPUT && tpclass != TMG_REGISTER_OUTPUT)
continue;
bool is_path = ctx->getCellDelay(cell, fwd_cursor->port, port.first, combDelay);
if (!is_path)
@ -408,6 +404,17 @@ class TimingOptimiser
bel_candidate_cells.clear();
if (ctx->debug)
log_info("Optimising the following path: \n");
auto front_port = path.front();
NetInfo *front_net = front_port->cell->ports.at(front_port->port).net;
if (front_net != nullptr && front_net->driver.cell != nullptr) {
auto front_cell = front_net->driver.cell;
if (front_cell->belStrength <= STRENGTH_WEAK && cfg.cellTypes.count(front_cell->type) &&
front_cell->constr_parent == nullptr && front_cell->constr_children.empty()) {
path_cells.push_back(front_cell->name);
}
}
for (auto port : path) {
if (ctx->debug) {
float crit = 0;
@ -429,7 +436,7 @@ class TimingOptimiser
path_cells.push_back(port->cell->name);
}
if (path_cells.size() < 3) {
if (path_cells.size() < 2) {
if (ctx->debug) {
log_info("Too few moveable cells; skipping path\n");
log_break();
@ -437,8 +444,23 @@ class TimingOptimiser
return;
}
// Calculate original delay before touching anything
delay_t original_delay = 0;
for (size_t i = 0; i < path.size(); i++) {
NetInfo *pn = path.at(i)->cell->ports.at(path.at(i)->port).net;
for (size_t j = 0; j < pn->users.size(); j++) {
auto & usr = pn->users.at(j);
if (usr.cell == path.at(i)->cell && usr.port == path.at(i)->port) {
original_delay += ctx->predictDelay(pn, usr);
break;
}
}
}
IdString last_cell;
const int d = 4; // FIXME: how to best determine d
const int d = 3; // FIXME: how to best determine d
for (auto cell : path_cells) {
// FIXME: when should we allow swapping due to a lack of candidates
find_neighbours(ctx->cells[cell].get(), last_cell, d, false);
@ -556,7 +578,8 @@ class TimingOptimiser
route_to_solution.push_back(cursor);
}
if (ctx->debug)
log_info("Found a solution with cost %.02f ns\n", ctx->getDelayNS(lowest->second));
log_info("Found a solution with cost %.02f ns (existing path %.02f ns)\n", ctx->getDelayNS(lowest->second),
ctx->getDelayNS(original_delay));
for (auto rt_entry : boost::adaptors::reverse(route_to_solution)) {
CellInfo *cell = ctx->cells.at(rt_entry.first).get();
cell_swap_bel(cell, rt_entry.second);

View File

@ -22,7 +22,7 @@ for i in range(num_runs):
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
if path.exists(ascfile):
os.remove(ascfile)
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "40", "--opt-timing"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
if result.returncode != 0:
print("Run {} failed!".format(run))
else: