timing_opt: Debugging and integration
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
1b7214a18a
commit
b51308708b
@ -34,31 +34,92 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <> struct hash<std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX IdString>>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX IdString> &idp) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.first));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<std::pair<int, NEXTPNR_NAMESPACE_PREFIX BelId>>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const std::pair<int, NEXTPNR_NAMESPACE_PREFIX BelId> &idp) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<int>()(idp.first));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX BelId>>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX BelId> &idp) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.first));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class TimingOptimiser
|
class TimingOptimiser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TimingOptimiser(Context *ctx) : ctx(ctx){};
|
TimingOptimiser(Context *ctx, TimingOptCfg cfg) : ctx(ctx), cfg(cfg) {};
|
||||||
bool optimise() {}
|
bool optimise() {
|
||||||
|
log_info("Running timing-driven placement optimisation...\n");
|
||||||
|
#if 1
|
||||||
|
timing_analysis(ctx, false, true, ctx->debug, false);
|
||||||
|
#endif
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
log_info(" Iteration %d...\n", i);
|
||||||
|
get_criticalities(ctx, &net_crit);
|
||||||
|
setup_delay_limits();
|
||||||
|
auto crit_paths = find_crit_paths(0.98, 1000);
|
||||||
|
for (auto &path : crit_paths)
|
||||||
|
optimise_path(path);
|
||||||
|
#if 1
|
||||||
|
timing_analysis(ctx, false, true, ctx->debug, false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Ratio of available to already-candidates to begin borrowing
|
// Ratio of available to already-candidates to begin borrowing
|
||||||
const float borrow_thresh = 0.2;
|
const float borrow_thresh = 0.2;
|
||||||
|
|
||||||
void setup_delay_limits() {
|
void setup_delay_limits() {
|
||||||
|
max_net_delay.clear();
|
||||||
for (auto net : sorted(ctx->nets)) {
|
for (auto net : sorted(ctx->nets)) {
|
||||||
NetInfo *ni = net.second;
|
NetInfo *ni = net.second;
|
||||||
max_net_delay[ni].clear();
|
for (auto usr : ni->users) {
|
||||||
max_net_delay[ni].resize(ni->users.size(), std::numeric_limits<delay_t>::max());
|
max_net_delay[std::make_pair(usr.cell->name, usr.port)]
|
||||||
|
= std::numeric_limits<delay_t>::max();
|
||||||
|
}
|
||||||
if (!net_crit.count(net.first))
|
if (!net_crit.count(net.first))
|
||||||
continue;
|
continue;
|
||||||
auto &nc = net_crit.at(net.first);
|
auto &nc = net_crit.at(net.first);
|
||||||
if (nc.slack.empty())
|
if (nc.slack.empty())
|
||||||
continue;
|
continue;
|
||||||
for (size_t i = 0; i < ni->users.size(); i++) {
|
for (size_t i = 0; i < ni->users.size(); i++) {
|
||||||
delay_t net_delay = ctx->getNetinfoRouteDelay(ni, ni->users.at(i));
|
auto &usr = ni->users.at(i);
|
||||||
max_net_delay[ni].at(i) = net_delay + ((nc.slack.at(i) - nc.cd_worst_slack) / nc.max_path_length);
|
delay_t net_delay = ctx->getNetinfoRouteDelay(ni, usr);
|
||||||
|
if (nc.max_path_length != 0) {
|
||||||
|
max_net_delay[std::make_pair(usr.cell->name, usr.port)]
|
||||||
|
= net_delay + ((nc.slack.at(i) - nc.cd_worst_slack) / nc.max_path_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,12 +257,12 @@ class TimingOptimiser
|
|||||||
return found_count;
|
return found_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<PortRef*>> find_crit_paths(float crit_thresh, int max_count) {
|
std::vector<std::vector<PortRef*>> find_crit_paths(float crit_thresh, size_t max_count) {
|
||||||
std::vector<std::vector<PortRef*>> crit_paths;
|
std::vector<std::vector<PortRef*>> crit_paths;
|
||||||
std::vector<std::pair<NetInfo *, int>> crit_nets;
|
std::vector<std::pair<NetInfo *, int>> crit_nets;
|
||||||
std::vector<IdString> netnames;
|
std::vector<IdString> netnames;
|
||||||
std::transform(ctx->nets.begin(), ctx->nets.end(), std::back_inserter(netnames),
|
std::transform(ctx->nets.begin(), ctx->nets.end(), std::back_inserter(netnames),
|
||||||
[](const std::pair<IdString, std::unique_ptr<NetInfo>> &kv){
|
[](const std::pair<const IdString, std::unique_ptr<NetInfo>> &kv){
|
||||||
return kv.first;
|
return kv.first;
|
||||||
});
|
});
|
||||||
ctx->sorted_shuffle(netnames);
|
ctx->sorted_shuffle(netnames);
|
||||||
@ -250,7 +311,7 @@ class TimingOptimiser
|
|||||||
int ccount;
|
int ccount;
|
||||||
DelayInfo combDelay;
|
DelayInfo combDelay;
|
||||||
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
|
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
|
||||||
if (tpclass != TMG_COMB_INPUT && tpclass != TMG_REGISTER_INPUT)
|
if (tpclass != TMG_COMB_INPUT)
|
||||||
continue;
|
continue;
|
||||||
bool is_path = ctx->getCellDelay(cell, port.first, back_cursor->driver.port, combDelay);
|
bool is_path = ctx->getCellDelay(cell, port.first, back_cursor->driver.port, combDelay);
|
||||||
if (!is_path)
|
if (!is_path)
|
||||||
@ -286,7 +347,7 @@ class TimingOptimiser
|
|||||||
int ccount;
|
int ccount;
|
||||||
DelayInfo combDelay;
|
DelayInfo combDelay;
|
||||||
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
|
TimingPortClass tpclass = ctx->getPortTimingClass(cell, port.first, ccount);
|
||||||
if (tpclass != TMG_COMB_OUTPUT && tpclass != TMG_REGISTER_OUTPUT)
|
if (tpclass != TMG_COMB_OUTPUT)
|
||||||
continue;
|
continue;
|
||||||
auto &crits = net_crit.at(pn->name).criticality;
|
auto &crits = net_crit.at(pn->name).criticality;
|
||||||
auto most_crit_usr = std::max_element(crits.begin(), crits.end());
|
auto most_crit_usr = std::max_element(crits.begin(), crits.end());
|
||||||
@ -314,11 +375,17 @@ class TimingOptimiser
|
|||||||
path_cells.clear();
|
path_cells.clear();
|
||||||
cell_neighbour_bels.clear();
|
cell_neighbour_bels.clear();
|
||||||
bel_candidate_cells.clear();
|
bel_candidate_cells.clear();
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Optimising the following path: \n");
|
||||||
for (auto port : path) {
|
for (auto port : path) {
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" %s.%s at %s\n", port->cell->name.c_str(ctx), port->port.c_str(ctx), ctx->getBelName(port->cell->bel).c_str(ctx));
|
||||||
if (std::find(path_cells.begin(), path_cells.end(), port->cell->name) != path_cells.end())
|
if (std::find(path_cells.begin(), path_cells.end(), port->cell->name) != path_cells.end())
|
||||||
continue;
|
continue;
|
||||||
if (port->cell->belStrength > STRENGTH_WEAK || !cfg.cellTypes.count(port->cell->type))
|
if (port->cell->belStrength > STRENGTH_WEAK || !cfg.cellTypes.count(port->cell->type))
|
||||||
continue;
|
continue;
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info(" can move\n");
|
||||||
path_cells.push_back(port->cell->name);
|
path_cells.push_back(port->cell->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +429,7 @@ class TimingOptimiser
|
|||||||
auto entry = visit.front();
|
auto entry = visit.front();
|
||||||
visit.pop();
|
visit.pop();
|
||||||
auto cellname = path_cells.at(entry.first);
|
auto cellname = path_cells.at(entry.first);
|
||||||
if (entry.first == path_cells.size() - 1)
|
if (entry.first == int(path_cells.size()) - 1)
|
||||||
continue;
|
continue;
|
||||||
std::vector<std::pair<CellInfo *, BelId>> move;
|
std::vector<std::pair<CellInfo *, BelId>> move;
|
||||||
// Apply the entire backtrace for accurate legality and delay checks
|
// Apply the entire backtrace for accurate legality and delay checks
|
||||||
@ -422,6 +489,39 @@ class TimingOptimiser
|
|||||||
cell_swap_bel(move_entry.first, move_entry.second);
|
cell_swap_bel(move_entry.first, move_entry.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Did we find a solution??
|
||||||
|
if (cumul_costs.count(path_cells.back())) {
|
||||||
|
// Find the end position with the lowest total delay
|
||||||
|
auto &end_options = cumul_costs.at(path_cells.back());
|
||||||
|
auto lowest = std::min_element(end_options.begin(), end_options.end(), [](const std::pair<BelId, delay_t> &a,
|
||||||
|
const std::pair<BelId, delay_t> &b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
NPNR_ASSERT(lowest != end_options.end());
|
||||||
|
|
||||||
|
std::vector<std::pair<IdString, BelId>> route_to_solution;
|
||||||
|
auto cursor = std::make_pair(path_cells.back(), lowest->first);
|
||||||
|
route_to_solution.push_back(cursor);
|
||||||
|
while (backtrace.count(cursor)) {
|
||||||
|
cursor = backtrace.at(cursor);
|
||||||
|
route_to_solution.push_back(cursor);
|
||||||
|
}
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Found a solution with cost %.02f ns\n", ctx->getDelayNS(lowest->second));
|
||||||
|
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);
|
||||||
|
if(ctx->debug)
|
||||||
|
log_info(" %s at %s\n", rt_entry.first.c_str(ctx), ctx->getBelName(rt_entry.second).c_str(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (ctx->debug)
|
||||||
|
log_info("Solution was not found\n");
|
||||||
|
}
|
||||||
|
if (ctx->debug)
|
||||||
|
log_break();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current candidate Bels for cells (linked in both direction>
|
// Current candidate Bels for cells (linked in both direction>
|
||||||
@ -432,12 +532,11 @@ class TimingOptimiser
|
|||||||
std::unordered_map<std::pair<IdString, IdString>, delay_t> max_net_delay;
|
std::unordered_map<std::pair<IdString, IdString>, delay_t> max_net_delay;
|
||||||
// Criticality data from timing analysis
|
// Criticality data from timing analysis
|
||||||
NetCriticalityMap net_crit;
|
NetCriticalityMap net_crit;
|
||||||
|
Context *ctx;
|
||||||
TimingOptCfg cfg;
|
TimingOptCfg cfg;
|
||||||
|
|
||||||
Context *ctx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool timing_opt(Context *ctx, TimingOptCfg cfg) { return TimingOptimiser(ctx).optimise(); }
|
bool timing_opt(Context *ctx, TimingOptCfg cfg) { return TimingOptimiser(ctx, cfg).optimise(); }
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -24,6 +24,8 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
struct TimingOptCfg : public Settings
|
struct TimingOptCfg : public Settings
|
||||||
{
|
{
|
||||||
|
TimingOptCfg(Context *ctx) : Settings(ctx) {}
|
||||||
|
|
||||||
// The timing optimiser will *only* optimise cells of these types
|
// The timing optimiser will *only* optimise cells of these types
|
||||||
// Normally these would only be logic cells (or tiles if applicable), the algorithm makes little sense
|
// Normally these would only be logic cells (or tiles if applicable), the algorithm makes little sense
|
||||||
// for other cell types
|
// for other cell types
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "placer1.h"
|
#include "placer1.h"
|
||||||
#include "router1.h"
|
#include "router1.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "timing_opt.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -626,7 +627,13 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
|
bool Arch::place() {
|
||||||
|
if(!placer1(getCtx(), Placer1Cfg(getCtx())))
|
||||||
|
return false;
|
||||||
|
TimingOptCfg tocfg(getCtx());
|
||||||
|
tocfg.cellTypes.insert(id_ICESTORM_LC);
|
||||||
|
return timing_opt(getCtx(), tocfg);
|
||||||
|
}
|
||||||
|
|
||||||
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
|
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user