Rip out budgets
This commit is contained in:
parent
77afaf23a5
commit
914999673c
@ -116,7 +116,6 @@ template <typename R> struct ArchAPI : BaseCtx
|
||||
virtual float getDelayNS(delay_t v) const = 0;
|
||||
virtual delay_t getDelayFromNS(float ns) const = 0;
|
||||
virtual uint32_t getDelayChecksum(delay_t v) const = 0;
|
||||
virtual bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const = 0;
|
||||
virtual delay_t estimateDelay(WireId src, WireId dst) const = 0;
|
||||
virtual BoundingBox getRouteBoundingBox(WireId src, WireId dst) const = 0;
|
||||
virtual bool getArcDelayOverride(const NetInfo *net_info, const PortRef &sink, DelayQuad &delay) const = 0;
|
||||
|
@ -313,10 +313,6 @@ template <typename R> struct BaseArch : ArchAPI<R>
|
||||
};
|
||||
|
||||
// Delay methods
|
||||
virtual bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getArcDelayOverride(const NetInfo *net_info, const PortRef &sink, DelayQuad &delay) const override
|
||||
{
|
||||
return false;
|
||||
|
@ -171,7 +171,6 @@ po::options_description CommandHandler::getGeneralOptions()
|
||||
general.add_options()("slack_redist_iter", po::value<int>(), "number of iterations between slack redistribution");
|
||||
general.add_options()("cstrweight", po::value<float>(), "placer weighting for relative constraint satisfaction");
|
||||
general.add_options()("starttemp", po::value<float>(), "placer SA start temperature");
|
||||
general.add_options()("placer-budgets", "use budget rather than criticality in placer timing weights");
|
||||
|
||||
general.add_options()("pack-only", "pack design only without placement or routing");
|
||||
general.add_options()("no-route", "process design without routing");
|
||||
@ -318,9 +317,6 @@ void CommandHandler::setupContext(Context *ctx)
|
||||
ctx->settings[ctx->id("placer1/startTemp")] = std::to_string(vm["starttemp"].as<float>());
|
||||
}
|
||||
|
||||
if (vm.count("placer-budgets")) {
|
||||
ctx->settings[ctx->id("placer1/budgetBased")] = true;
|
||||
}
|
||||
if (vm.count("freq")) {
|
||||
auto freq = vm["freq"].as<double>();
|
||||
if (freq > 0)
|
||||
@ -457,7 +453,6 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
|
||||
if (!ctx->pack() && !ctx->force)
|
||||
log_error("Packing design failed.\n");
|
||||
}
|
||||
assign_budget(ctx.get());
|
||||
ctx->check();
|
||||
print_utilisation(ctx.get());
|
||||
|
||||
|
@ -236,13 +236,11 @@ uint32_t Context::checksum() const
|
||||
if (ni.driver.cell)
|
||||
x = xorshift32(x + xorshift32(ni.driver.cell->name.index));
|
||||
x = xorshift32(x + xorshift32(ni.driver.port.index));
|
||||
x = xorshift32(x + xorshift32(getDelayChecksum(ni.driver.budget)));
|
||||
|
||||
for (auto &u : ni.users) {
|
||||
if (u.cell)
|
||||
x = xorshift32(x + xorshift32(u.cell->name.index));
|
||||
x = xorshift32(x + xorshift32(u.port.index));
|
||||
x = xorshift32(x + xorshift32(getDelayChecksum(u.budget)));
|
||||
}
|
||||
|
||||
uint32_t attr_x_sum = 0;
|
||||
|
@ -75,7 +75,6 @@ struct PortRef
|
||||
{
|
||||
CellInfo *cell = nullptr;
|
||||
IdString port;
|
||||
delay_t budget = 0;
|
||||
};
|
||||
|
||||
// minimum and maximum delay
|
||||
@ -326,8 +325,6 @@ struct CriticalPath
|
||||
std::pair<IdString, IdString> to;
|
||||
// Segment delay
|
||||
delay_t delay;
|
||||
// Segment budget (routing only)
|
||||
delay_t budget;
|
||||
};
|
||||
|
||||
// Clock pair
|
||||
@ -349,8 +346,6 @@ struct NetSinkTiming
|
||||
std::pair<IdString, IdString> cell_port;
|
||||
// Delay
|
||||
delay_t delay;
|
||||
// Delay budget
|
||||
delay_t budget;
|
||||
};
|
||||
|
||||
struct TimingResult
|
||||
|
@ -239,8 +239,6 @@ PYBIND11_EMBEDDED_MODULE(MODULE_NAME, m)
|
||||
"cell");
|
||||
readonly_wrapper<PortRef, decltype(&PortRef::port), &PortRef::port, conv_to_str<IdString>>::def_wrap(pr_cls,
|
||||
"port");
|
||||
readonly_wrapper<PortRef, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>>::def_wrap(pr_cls,
|
||||
"budget");
|
||||
|
||||
auto pm_cls = py::class_<ContextualWrapper<PipMap &>>(m, "PipMap");
|
||||
readwrite_wrapper<PipMap &, decltype(&PipMap::pip), &PipMap::pip, conv_to_str<PipId>,
|
||||
|
@ -90,7 +90,6 @@ static Json::array report_critical_paths(const Context *ctx)
|
||||
} else if (segment.type == CriticalPath::Segment::Type::ROUTING) {
|
||||
segmentJson["type"] = "routing";
|
||||
segmentJson["net"] = segment.net.c_str(ctx);
|
||||
segmentJson["budget"] = ctx->getDelayNS(segment.budget);
|
||||
}
|
||||
|
||||
pathJson.push_back(segmentJson);
|
||||
@ -134,8 +133,7 @@ static Json::array report_detailed_net_timings(const Context *ctx)
|
||||
auto endpointJson = Json::object({{"cell", sink_timing.cell_port.first.c_str(ctx)},
|
||||
{"port", sink_timing.cell_port.second.c_str(ctx)},
|
||||
{"event", clock_event_name(ctx, sink_timing.clock_pair.end)},
|
||||
{"delay", ctx->getDelayNS(sink_timing.delay)},
|
||||
{"budget", ctx->getDelayNS(sink_timing.budget)}});
|
||||
{"delay", ctx->getDelayNS(sink_timing.delay)}});
|
||||
endpointsJson.push_back(endpointJson);
|
||||
}
|
||||
|
||||
@ -194,7 +192,6 @@ Report JSON structure:
|
||||
"type": <path segment type "clk-to-q", "source", "logic", "routing" or "setup">,
|
||||
"net": <net name (for routing only!)>,
|
||||
"delay": <segment delay [ns]>,
|
||||
"budget": <segment delay budget [ns] (for routing only!)>,
|
||||
}
|
||||
...
|
||||
]
|
||||
@ -213,7 +210,6 @@ Report JSON structure:
|
||||
"port": <sink cell port name>,
|
||||
"event": <destination clock event name>,
|
||||
"delay": <delay [ns]>,
|
||||
"budget": <delay budget [ns]>,
|
||||
}
|
||||
...
|
||||
]
|
||||
|
@ -501,7 +501,6 @@ void TimingAnalyser::reset_times()
|
||||
dp.second.hold_slack = std::numeric_limits<delay_t>::max();
|
||||
dp.second.max_path_length = 0;
|
||||
dp.second.criticality = 0;
|
||||
dp.second.budget = 0;
|
||||
}
|
||||
port.second.worst_crit = 0;
|
||||
port.second.worst_setup_slack = std::numeric_limits<delay_t>::max();
|
||||
@ -1060,7 +1059,6 @@ struct Timing
|
||||
if (portClass == TMG_ENDPOINT || portClass == TMG_IGNORE || portClass == TMG_CLOCK_INPUT) {
|
||||
// Skip
|
||||
} else {
|
||||
auto budget_override = ctx->getBudgetOverride(net, usr, net_delay);
|
||||
// Iterate over all output ports on the same cell as the sink
|
||||
for (auto port : usr.cell->ports) {
|
||||
if (port.second.type != PORT_OUT || !port.second.net)
|
||||
@ -1073,12 +1071,8 @@ struct Timing
|
||||
auto &data = net_data[port.second.net][start_clk];
|
||||
auto &arrival = data.max_arrival;
|
||||
arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay());
|
||||
if (!budget_override) { // Do not increment path length if budget overridden since it
|
||||
// doesn't
|
||||
// require a share of the slack
|
||||
auto &path_length = data.max_path_length;
|
||||
path_length = std::max(path_length, net_length_plus_one);
|
||||
}
|
||||
auto &path_length = data.max_path_length;
|
||||
path_length = std::max(path_length, net_length_plus_one);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1102,7 +1096,6 @@ struct Timing
|
||||
auto &net_min_remaining_budget = nd.min_remaining_budget;
|
||||
for (auto &usr : net->users) {
|
||||
auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t();
|
||||
auto budget_override = ctx->getBudgetOverride(net, usr, net_delay);
|
||||
int port_clocks;
|
||||
TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, port_clocks);
|
||||
if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT) {
|
||||
@ -1133,8 +1126,7 @@ struct Timing
|
||||
auto path_budget = period - endpoint_arrival;
|
||||
|
||||
if (update) {
|
||||
auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one;
|
||||
usr.budget = std::min(usr.budget, net_delay + budget_share);
|
||||
auto budget_share = path_budget / net_length_plus_one;
|
||||
net_min_remaining_budget =
|
||||
std::min(net_min_remaining_budget, path_budget - budget_share);
|
||||
}
|
||||
@ -1156,7 +1148,6 @@ struct Timing
|
||||
sink_timing.clock_pair = clockPair;
|
||||
sink_timing.cell_port = std::make_pair(usr.cell->name, usr.port);
|
||||
sink_timing.delay = endpoint_arrival;
|
||||
sink_timing.budget = period;
|
||||
|
||||
(*detailed_net_timings)[net->name].push_back(sink_timing);
|
||||
}
|
||||
@ -1196,8 +1187,7 @@ struct Timing
|
||||
net_data.at(port.second.net).count(startdomain.first)) {
|
||||
auto path_budget =
|
||||
net_data.at(port.second.net).at(startdomain.first).min_remaining_budget;
|
||||
auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one;
|
||||
usr.budget = std::min(usr.budget, net_delay + budget_share);
|
||||
auto budget_share = path_budget / net_length_plus_one;
|
||||
net_min_remaining_budget =
|
||||
std::min(net_min_remaining_budget, path_budget - budget_share);
|
||||
}
|
||||
@ -1265,64 +1255,8 @@ struct Timing
|
||||
}
|
||||
return min_slack;
|
||||
}
|
||||
|
||||
void assign_budget()
|
||||
{
|
||||
// Clear delays to a very high value first
|
||||
for (auto &net : ctx->nets) {
|
||||
for (auto &usr : net.second->users) {
|
||||
usr.budget = std::numeric_limits<delay_t>::max();
|
||||
}
|
||||
}
|
||||
|
||||
walk_paths();
|
||||
}
|
||||
};
|
||||
|
||||
void assign_budget(Context *ctx, bool quiet)
|
||||
{
|
||||
if (!quiet) {
|
||||
log_break();
|
||||
log_info("Annotating ports with timing budgets for target frequency %.2f MHz\n",
|
||||
ctx->setting<float>("target_freq") / 1e6);
|
||||
}
|
||||
|
||||
Timing timing(ctx, ctx->setting<int>("slack_redist_iter") > 0 /* net_delays */, true /* update */);
|
||||
timing.assign_budget();
|
||||
|
||||
if (!quiet || ctx->verbose) {
|
||||
for (auto &net : ctx->nets) {
|
||||
for (auto &user : net.second->users) {
|
||||
// Post-update check
|
||||
if (!ctx->setting<bool>("auto_freq") && user.budget < 0)
|
||||
log_info("port %s.%s, connected to net '%s', has negative "
|
||||
"timing budget of %fns\n",
|
||||
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
|
||||
ctx->getDelayNS(user.budget));
|
||||
else if (ctx->debug)
|
||||
log_info("port %s.%s, connected to net '%s', has "
|
||||
"timing budget of %fns\n",
|
||||
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
|
||||
ctx->getDelayNS(user.budget));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the
|
||||
// currently achieved maximum
|
||||
if (ctx->setting<bool>("auto_freq") && ctx->setting<int>("slack_redist_iter") > 0) {
|
||||
delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->setting<float>("target_freq"));
|
||||
ctx->settings[ctx->id("target_freq")] =
|
||||
std::to_string(1.0e9 / ctx->getDelayNS(default_slack - timing.min_slack));
|
||||
if (ctx->verbose)
|
||||
log_info("minimum slack for this assign = %.2f ns, target Fmax for next "
|
||||
"update = %.2f MHz\n",
|
||||
ctx->getDelayNS(timing.min_slack), ctx->setting<float>("target_freq") / 1e6);
|
||||
}
|
||||
|
||||
if (!quiet)
|
||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||
}
|
||||
|
||||
CriticalPath build_critical_path_report(Context *ctx, ClockPair &clocks, const PortRefVector &crit_path)
|
||||
{
|
||||
@ -1378,7 +1312,6 @@ CriticalPath build_critical_path_report(Context *ctx, ClockPair &clocks, const P
|
||||
}
|
||||
|
||||
seg_logic.delay = comb_delay.maxDelay();
|
||||
seg_logic.budget = 0;
|
||||
seg_logic.from = std::make_pair(last_cell->name, last_port);
|
||||
seg_logic.to = std::make_pair(driver_cell->name, driver.port);
|
||||
seg_logic.net = IdString();
|
||||
@ -1389,7 +1322,6 @@ CriticalPath build_critical_path_report(Context *ctx, ClockPair &clocks, const P
|
||||
CriticalPath::Segment seg_route;
|
||||
seg_route.type = CriticalPath::Segment::Type::ROUTING;
|
||||
seg_route.delay = net_delay;
|
||||
seg_route.budget = sink->budget;
|
||||
seg_route.from = std::make_pair(driver_cell->name, driver.port);
|
||||
seg_route.to = std::make_pair(sink_cell->name, sink->port);
|
||||
seg_route.net = net->name;
|
||||
@ -1408,7 +1340,6 @@ CriticalPath build_critical_path_report(Context *ctx, ClockPair &clocks, const P
|
||||
CriticalPath::Segment seg_logic;
|
||||
seg_logic.type = CriticalPath::Segment::Type::SETUP;
|
||||
seg_logic.delay = setup;
|
||||
seg_logic.budget = 0;
|
||||
seg_logic.from = std::make_pair(last_cell->name, last_port);
|
||||
seg_logic.to = seg_logic.from;
|
||||
seg_logic.net = IdString();
|
||||
@ -1582,8 +1513,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
||||
auto driver_loc = ctx->getBelLocation(driver->bel);
|
||||
auto sink_loc = ctx->getBelLocation(sink->bel);
|
||||
|
||||
log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(segment.delay),
|
||||
ctx->getDelayNS(total), segment.net.c_str(ctx), ctx->getDelayNS(segment.budget),
|
||||
log_info("%4.1f %4.1f Net %s (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(segment.delay),
|
||||
ctx->getDelayNS(total), segment.net.c_str(ctx),
|
||||
driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
|
||||
log_info(" Sink %s.%s\n", segment.to.first.c_str(ctx), segment.to.second.c_str(ctx));
|
||||
|
||||
@ -1594,7 +1525,6 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
||||
PortRef sink_ref;
|
||||
sink_ref.cell = sink.get();
|
||||
sink_ref.port = segment.to.second;
|
||||
sink_ref.budget = segment.budget;
|
||||
|
||||
auto driver_wire = ctx->getNetinfoSourceWire(net);
|
||||
auto sink_wire = ctx->getNetinfoSinkWire(net, sink_ref, 0);
|
||||
@ -1842,6 +1772,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
||||
|
||||
results.detailed_net_timings = std::move(detailed_net_timings);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -143,7 +143,6 @@ struct TimingAnalyser
|
||||
struct PortDomainPairData
|
||||
{
|
||||
delay_t setup_slack = std::numeric_limits<delay_t>::max(), hold_slack = std::numeric_limits<delay_t>::max();
|
||||
delay_t budget = std::numeric_limits<delay_t>::max();
|
||||
int max_path_length = 0;
|
||||
float criticality = 0;
|
||||
};
|
||||
@ -231,9 +230,6 @@ struct TimingAnalyser
|
||||
Context *ctx;
|
||||
};
|
||||
|
||||
// Evenly redistribute the total path slack amongst all sinks on each path
|
||||
void assign_budget(Context *ctx, bool quiet = false);
|
||||
|
||||
// Perform timing analysis and print out the fmax, and optionally the
|
||||
// critical path
|
||||
void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false,
|
||||
|
@ -51,7 +51,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
|
||||
continue;
|
||||
if (timing_driven) {
|
||||
delay_t net_delay = ctx->predictArcDelay(net, load);
|
||||
auto slack = load.budget - net_delay;
|
||||
auto slack = -net_delay;
|
||||
if (slack < 0)
|
||||
negative_slack += slack;
|
||||
worst_slack = std::min(slack, worst_slack);
|
||||
|
@ -211,8 +211,6 @@ class SAPlacer
|
||||
if ((placed_cells - constr_placed_cells) % 500 != 0)
|
||||
log_info(" initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells),
|
||||
int(autoplaced.size()));
|
||||
if (cfg.budgetBased && cfg.slack_redist_iter > 0)
|
||||
assign_budget(ctx);
|
||||
ctx->yield();
|
||||
auto iplace_end = std::chrono::high_resolution_clock::now();
|
||||
log_info("Initial placement time %.02fs\n",
|
||||
@ -240,8 +238,7 @@ class SAPlacer
|
||||
|
||||
// Invoke timing analysis to obtain criticalities
|
||||
tmg.setup_only = true;
|
||||
if (!cfg.budgetBased)
|
||||
tmg.setup();
|
||||
tmg.setup();
|
||||
|
||||
// Calculate costs after initial placement
|
||||
setup_costs();
|
||||
@ -368,18 +365,12 @@ class SAPlacer
|
||||
// temp = post_legalise_temp;
|
||||
// diameter = std::min<int>(M, diameter * post_legalise_dia_scale);
|
||||
ctx->shuffle(autoplaced);
|
||||
|
||||
// Legalisation is a big change so force a slack redistribution here
|
||||
if (cfg.slack_redist_iter > 0 && cfg.budgetBased)
|
||||
assign_budget(ctx, true /* quiet */);
|
||||
}
|
||||
require_legal = false;
|
||||
} else if (cfg.budgetBased && cfg.slack_redist_iter > 0 && iter % cfg.slack_redist_iter == 0) {
|
||||
assign_budget(ctx, true /* quiet */);
|
||||
}
|
||||
|
||||
// Invoke timing analysis to obtain criticalities
|
||||
if (!cfg.budgetBased && cfg.timing_driven)
|
||||
if (cfg.timing_driven)
|
||||
tmg.run();
|
||||
// Need to rebuild costs after criticalities change
|
||||
setup_costs();
|
||||
@ -870,14 +861,10 @@ class SAPlacer
|
||||
return 0;
|
||||
if (ctx->getPortTimingClass(net->driver.cell, net->driver.port, cc) == TMG_IGNORE)
|
||||
return 0;
|
||||
if (cfg.budgetBased) {
|
||||
double delay = ctx->getDelayNS(ctx->predictArcDelay(net, user));
|
||||
return std::min(10.0, std::exp(delay - ctx->getDelayNS(user.budget) / 10));
|
||||
} else {
|
||||
float crit = tmg.get_criticality(CellPortKey(user));
|
||||
double delay = ctx->getDelayNS(ctx->predictArcDelay(net, user));
|
||||
return delay * std::pow(crit, crit_exp);
|
||||
}
|
||||
|
||||
float crit = tmg.get_criticality(CellPortKey(user));
|
||||
double delay = ctx->getDelayNS(ctx->predictArcDelay(net, user));
|
||||
return delay * std::pow(crit, crit_exp);
|
||||
}
|
||||
|
||||
// Set up the cost maps
|
||||
@ -1267,7 +1254,6 @@ Placer1Cfg::Placer1Cfg(Context *ctx)
|
||||
constraintWeight = ctx->setting<float>("placer1/constraintWeight", 10);
|
||||
netShareWeight = ctx->setting<float>("placer1/netShareWeight", 0);
|
||||
minBelsForGridPick = ctx->setting<int>("placer1/minBelsForGridPick", 64);
|
||||
budgetBased = ctx->setting<bool>("placer1/budgetBased", false);
|
||||
startTemp = ctx->setting<float>("placer1/startTemp", 1);
|
||||
timingFanoutThresh = std::numeric_limits<int>::max();
|
||||
timing_driven = ctx->setting<bool>("timing_driven");
|
||||
|
@ -29,7 +29,6 @@ struct Placer1Cfg
|
||||
Placer1Cfg(Context *ctx);
|
||||
float constraintWeight, netShareWeight;
|
||||
int minBelsForGridPick;
|
||||
bool budgetBased;
|
||||
float startTemp;
|
||||
int timingFanoutThresh;
|
||||
bool timing_driven;
|
||||
|
@ -730,7 +730,6 @@ struct Router1
|
||||
log(" final route delay: %8.2f\n", ctx->getDelayNS(visited[dst_wire].delay));
|
||||
log(" final route penalty: %8.2f\n", ctx->getDelayNS(visited[dst_wire].penalty));
|
||||
log(" final route bonus: %8.2f\n", ctx->getDelayNS(visited[dst_wire].bonus));
|
||||
log(" arc budget: %12.2f\n", ctx->getDelayNS(net_info->users[user_idx].budget));
|
||||
}
|
||||
|
||||
// bind resulting route (and maybe unroute other nets)
|
||||
|
@ -545,12 +545,6 @@ Convert a real-world delay in nanoseconds to a `delay_t`.
|
||||
|
||||
Convert a `delay_t` to an integer for checksum calculations.
|
||||
|
||||
### bool getBudgetOverride(const NetInfo \*net\_info, const PortRef &sink, delay\_t &budget) const
|
||||
|
||||
Overwrite or modify (in-place) the timing budget for a given arc. Returns a bool to indicate whether this was done.
|
||||
|
||||
*BaseArch default: returns false*
|
||||
|
||||
### bool getArcDelayOverride(const NetInfo \*net_info, const PortRef &sink, DelayQuad &delay) const
|
||||
|
||||
This allows an arch to provide a more precise method for calculating the delay for a routed arc than
|
||||
@ -763,5 +757,3 @@ Returns `true` if the cell **must** be placed according to the cluster; for exam
|
||||
Gets an exact placement of the cluster, with the root cell placed on or near `root_bel` (and always within the same tile). Returns false if no placement is viable, otherwise returns `true` and populates `placement` with a list of cells inside the cluster and bels they should be placed at.
|
||||
|
||||
This approach of allowing architectures to define cluster placements enables easier handling of irregular fabrics than requiring strict and constant x, y and z offsets.
|
||||
|
||||
|
||||
|
@ -78,7 +78,6 @@ Placers might want to create their own indices of bels (for example, bels by typ
|
||||
As nextpnr allows arbitrary constraints on bels for more advanced packer-free flows and complex real-world architectures; placements must be checked for legality using `isBelLocationValid` (after placement) and the placement rejected if invalid. For analytical placement algorithms; after creating a spread-out AP solution the legality of placing each cell needs to be checked. In practice, the cost of this is fairly low as the architecture should ensure these functions are as fast as possible.
|
||||
|
||||
There are several routes for timing information in the placer:
|
||||
- sink `PortRef`s have a `budget` value annotated by calling `assign_budget` which is an estimate of the maximum delay that an arc may have
|
||||
- sink ports can have a criticality (value between 0 and 1 where 1 is the critical path) associated with them by using `get_criticalities` and a `NetCriticalityMap`
|
||||
- `predictDelay` and its derivative `predictArcDelay` returns an estimated delay for a sink port based on placement information
|
||||
|
||||
@ -98,4 +97,3 @@ together, see the "cellGroups" field.
|
||||
The job of the router is to ensure that the `wires` map for each net contains a complete routing tree; populated using the Arch functions to bind wires and pips. The ripup invariants in the [FAQ](faq.md) are important to bear in mind; as there may be complex constraints on the usage of wires and pips in some architectures.
|
||||
|
||||
`estimateDelay` is intended for use as an A* metric to guide routing.
|
||||
|
||||
|
@ -38,7 +38,6 @@ There is a dictionary `ctx.nets` that provides access to all of the nets in a de
|
||||
A `PortRef` has three fields:
|
||||
- `cell`: a reference to the cell the port is on
|
||||
- `port`: the name of the port
|
||||
- `budget`: the timing budget of the port
|
||||
|
||||
### Accessing cells
|
||||
|
||||
@ -84,4 +83,3 @@ The value given to `setParam` and `setAttr` should be a string of `[01xz]*` for
|
||||
## Constraints
|
||||
|
||||
See the [constraints documentation](constraints.md)
|
||||
|
||||
|
14
ecp5/arch.cc
14
ecp5/arch.cc
@ -582,19 +582,6 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr
|
||||
(3 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
|
||||
}
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const
|
||||
{
|
||||
if (net_info->driver.port == id_FCO && sink.port == id_FCI) {
|
||||
budget = 0;
|
||||
return true;
|
||||
} else if (sink.port.in(id_FXA, id_FXB)) {
|
||||
budget = 0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delay_t Arch::getRipupDelayPenalty() const { return 400; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@ -653,7 +640,6 @@ bool Arch::route()
|
||||
setup_wire_locations();
|
||||
route_ecp5_globals(getCtx());
|
||||
assignArchInfo();
|
||||
assign_budget(getCtx(), true);
|
||||
|
||||
bool result;
|
||||
if (router == "router1") {
|
||||
|
@ -975,7 +975,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
float getDelayNS(delay_t v) const override { return v * 0.001; }
|
||||
delay_t getDelayFromNS(float ns) const override { return delay_t(ns * 1000); }
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return v; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -763,8 +763,6 @@ BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
||||
return {x0, y0, x1, y1};
|
||||
}
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool Arch::pack()
|
||||
|
@ -707,7 +707,6 @@ struct Arch : ArchAPI<ArchRanges>
|
||||
float getDelayNS(delay_t v) const final { return v * 0.001; }
|
||||
delay_t getDelayFromNS(float ns) const final { return delay_t(ns * 1000); }
|
||||
uint32_t getDelayChecksum(delay_t v) const final { return v; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const final;
|
||||
|
||||
bool getArcDelayOverride(const NetInfo *net_info, const PortRef &sink, DelayQuad &delay) const final
|
||||
{
|
||||
|
@ -561,8 +561,6 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr
|
||||
return (dx + dy) * args.delayScale + args.delayOffset;
|
||||
}
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
|
||||
|
||||
BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
||||
{
|
||||
if (uarch)
|
||||
|
@ -323,7 +323,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
delay_t getDelayFromNS(float ns) const override { return ns; }
|
||||
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return 0; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override;
|
||||
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
||||
|
||||
|
@ -2150,8 +2150,6 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr
|
||||
return (dx + dy) * args.delayScale + args.delayOffset;
|
||||
}
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
|
||||
|
||||
BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
||||
{
|
||||
BoundingBox bb;
|
||||
|
@ -456,7 +456,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
delay_t getDelayFromNS(float ns) const override { return ns; }
|
||||
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return 0; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override;
|
||||
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
||||
|
||||
|
@ -91,7 +91,6 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, CommandHandler
|
||||
// Connect Worker
|
||||
connect(task, &TaskManager::log, this, &BaseMainWindow::writeInfo);
|
||||
connect(task, &TaskManager::pack_finished, this, &BaseMainWindow::pack_finished);
|
||||
connect(task, &TaskManager::budget_finish, this, &BaseMainWindow::budget_finish);
|
||||
connect(task, &TaskManager::place_finished, this, &BaseMainWindow::place_finished);
|
||||
connect(task, &TaskManager::route_finished, this, &BaseMainWindow::route_finished);
|
||||
connect(task, &TaskManager::taskCanceled, this, &BaseMainWindow::taskCanceled);
|
||||
@ -178,12 +177,6 @@ void BaseMainWindow::createMenusAndBars()
|
||||
actionPack->setEnabled(false);
|
||||
connect(actionPack, &QAction::triggered, task, &TaskManager::pack);
|
||||
|
||||
actionAssignBudget = new QAction("Assign Budget", this);
|
||||
actionAssignBudget->setIcon(QIcon(":/icons/resources/time_add.png"));
|
||||
actionAssignBudget->setStatusTip("Assign timing budget for current design");
|
||||
actionAssignBudget->setEnabled(false);
|
||||
connect(actionAssignBudget, &QAction::triggered, this, &BaseMainWindow::budget);
|
||||
|
||||
actionPlace = new QAction("Place", this);
|
||||
actionPlace->setIcon(QIcon(":/icons/resources/place.png"));
|
||||
actionPlace->setStatusTip("Place current design");
|
||||
@ -307,7 +300,6 @@ void BaseMainWindow::createMenusAndBars()
|
||||
|
||||
// Add Design menu actions
|
||||
menuDesign->addAction(actionPack);
|
||||
menuDesign->addAction(actionAssignBudget);
|
||||
menuDesign->addAction(actionPlace);
|
||||
menuDesign->addAction(actionRoute);
|
||||
menuDesign->addSeparator();
|
||||
@ -324,7 +316,6 @@ void BaseMainWindow::createMenusAndBars()
|
||||
mainActionBar->addAction(actionSaveJSON);
|
||||
mainActionBar->addSeparator();
|
||||
mainActionBar->addAction(actionPack);
|
||||
mainActionBar->addAction(actionAssignBudget);
|
||||
mainActionBar->addAction(actionPlace);
|
||||
mainActionBar->addAction(actionRoute);
|
||||
mainActionBar->addAction(actionExecutePy);
|
||||
@ -471,17 +462,6 @@ void BaseMainWindow::pack_finished(bool status)
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMainWindow::budget_finish(bool status)
|
||||
{
|
||||
disableActions();
|
||||
if (status) {
|
||||
log("Assigning timing budget successful.\n");
|
||||
updateActions();
|
||||
} else {
|
||||
log("Assigning timing budget failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMainWindow::place_finished(bool status)
|
||||
{
|
||||
disableActions();
|
||||
@ -524,24 +504,12 @@ void BaseMainWindow::taskPaused()
|
||||
actionStop->setEnabled(true);
|
||||
}
|
||||
|
||||
void BaseMainWindow::budget()
|
||||
{
|
||||
bool ok;
|
||||
double freq = QInputDialog::getDouble(this, "Assign timing budget", "Frequency [MHz]:", 50, 0, 250, 2, &ok);
|
||||
if (ok) {
|
||||
freq *= 1e6;
|
||||
timing_driven = true;
|
||||
Q_EMIT task->budget(freq);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseMainWindow::place() { Q_EMIT task->place(timing_driven); }
|
||||
|
||||
void BaseMainWindow::disableActions()
|
||||
{
|
||||
actionLoadJSON->setEnabled(true);
|
||||
actionPack->setEnabled(false);
|
||||
actionAssignBudget->setEnabled(false);
|
||||
actionPlace->setEnabled(false);
|
||||
actionRoute->setEnabled(false);
|
||||
|
||||
@ -559,7 +527,6 @@ void BaseMainWindow::updateActions()
|
||||
if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
|
||||
actionPack->setEnabled(true);
|
||||
else if (ctx->settings.find(ctx->id("place")) == ctx->settings.end()) {
|
||||
actionAssignBudget->setEnabled(true);
|
||||
actionPlace->setEnabled(true);
|
||||
} else if (ctx->settings.find(ctx->id("route")) == ctx->settings.end())
|
||||
actionRoute->setEnabled(true);
|
||||
|
@ -69,13 +69,11 @@ class BaseMainWindow : public QMainWindow
|
||||
|
||||
void open_json();
|
||||
void save_json();
|
||||
void budget();
|
||||
void place();
|
||||
|
||||
void execute_python();
|
||||
|
||||
void pack_finished(bool status);
|
||||
void budget_finish(bool status);
|
||||
void place_finished(bool status);
|
||||
void route_finished(bool status);
|
||||
|
||||
@ -120,7 +118,6 @@ class BaseMainWindow : public QMainWindow
|
||||
QAction *actionSaveJSON;
|
||||
|
||||
QAction *actionPack;
|
||||
QAction *actionAssignBudget;
|
||||
QAction *actionPlace;
|
||||
QAction *actionRoute;
|
||||
|
||||
|
@ -751,7 +751,6 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
|
||||
|
||||
QtProperty *driverItem = addSubGroup(topItem, "Driver");
|
||||
addProperty(driverItem, QVariant::String, "Port", net->driver.port.c_str(ctx));
|
||||
addProperty(driverItem, QVariant::Double, "Budget", net->driver.budget);
|
||||
if (net->driver.cell)
|
||||
addProperty(driverItem, QVariant::String, "Cell", net->driver.cell->name.c_str(ctx), ElementType::CELL);
|
||||
else
|
||||
@ -762,7 +761,6 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
|
||||
QtProperty *portItem = addSubGroup(usersItem, item.port.c_str(ctx));
|
||||
|
||||
addProperty(portItem, QVariant::String, "Port", item.port.c_str(ctx));
|
||||
addProperty(portItem, QVariant::Double, "Budget", item.budget);
|
||||
if (item.cell)
|
||||
addProperty(portItem, QVariant::String, "Cell", item.cell->name.c_str(ctx), ElementType::CELL);
|
||||
else
|
||||
|
@ -74,9 +74,6 @@ void MainWindow::createMenu()
|
||||
|
||||
menuDesign->addSeparator();
|
||||
menuDesign->addAction(actionLoadCST);
|
||||
|
||||
// XXX
|
||||
actionAssignBudget->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::new_proj() {}
|
||||
@ -102,7 +99,5 @@ void MainWindow::onUpdateActions()
|
||||
if (ctx->settings.find(ctx->id("pack")) != ctx->settings.end()) {
|
||||
actionLoadCST->setEnabled(false);
|
||||
}
|
||||
// XXX
|
||||
actionAssignBudget->setEnabled(false);
|
||||
}
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -64,18 +64,6 @@ void Worker::pack()
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::budget(double freq)
|
||||
{
|
||||
Q_EMIT taskStarted();
|
||||
try {
|
||||
ctx->settings[ctx->id("target_freq")] = std::to_string(freq);
|
||||
assign_budget(ctx);
|
||||
Q_EMIT budget_finish(true);
|
||||
} catch (WorkerInterruptionRequested) {
|
||||
Q_EMIT taskCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::place(bool timing_driven)
|
||||
{
|
||||
Q_EMIT taskStarted();
|
||||
@ -105,7 +93,6 @@ TaskManager::TaskManager() : toTerminate(false), toPause(false)
|
||||
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
|
||||
|
||||
connect(this, &TaskManager::pack, worker, &Worker::pack);
|
||||
connect(this, &TaskManager::budget, worker, &Worker::budget);
|
||||
connect(this, &TaskManager::place, worker, &Worker::place);
|
||||
connect(this, &TaskManager::route, worker, &Worker::route);
|
||||
|
||||
@ -113,7 +100,6 @@ TaskManager::TaskManager() : toTerminate(false), toPause(false)
|
||||
|
||||
connect(worker, &Worker::log, this, &TaskManager::info);
|
||||
connect(worker, &Worker::pack_finished, this, &TaskManager::pack_finished);
|
||||
connect(worker, &Worker::budget_finish, this, &TaskManager::budget_finish);
|
||||
connect(worker, &Worker::place_finished, this, &TaskManager::place_finished);
|
||||
connect(worker, &Worker::route_finished, this, &TaskManager::route_finished);
|
||||
|
||||
|
@ -36,13 +36,11 @@ class Worker : public QObject
|
||||
public Q_SLOTS:
|
||||
void newContext(Context *);
|
||||
void pack();
|
||||
void budget(double freq);
|
||||
void place(bool timing_driven);
|
||||
void route();
|
||||
Q_SIGNALS:
|
||||
void log(const std::string &text);
|
||||
void pack_finished(bool status);
|
||||
void budget_finish(bool status);
|
||||
void place_finished(bool status);
|
||||
void route_finished(bool status);
|
||||
void taskCanceled();
|
||||
@ -73,14 +71,12 @@ class TaskManager : public QObject
|
||||
void contextChanged(Context *ctx);
|
||||
void terminate();
|
||||
void pack();
|
||||
void budget(double freq);
|
||||
void place(bool timing_driven);
|
||||
void route();
|
||||
|
||||
// redirected signals
|
||||
void log(const std::string &text);
|
||||
void pack_finished(bool status);
|
||||
void budget_finish(bool status);
|
||||
void place_finished(bool status);
|
||||
void route_finished(bool status);
|
||||
void taskCanceled();
|
||||
|
@ -568,10 +568,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
float getDelayNS(delay_t v) const override { return v * 0.001; }
|
||||
delay_t getDelayFromNS(float ns) const override { return delay_t(ns * 1000); }
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return v; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override
|
||||
{
|
||||
return uarch->getRouteBoundingBox(src, dst);
|
||||
|
@ -639,47 +639,6 @@ std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const
|
||||
{
|
||||
const auto &driver = net_info->driver;
|
||||
if (driver.port == id_COUT) {
|
||||
NPNR_ASSERT(sink.port.in(id_CIN, id_I3));
|
||||
NPNR_ASSERT(driver.cell->constr_abs_z);
|
||||
bool cin = sink.port == id_CIN;
|
||||
bool same_y = driver.cell->constr_z < 7;
|
||||
if (cin && same_y)
|
||||
budget = 0;
|
||||
else {
|
||||
switch (args.type) {
|
||||
case ArchArgs::HX8K:
|
||||
case ArchArgs::HX4K:
|
||||
case ArchArgs::HX1K:
|
||||
budget = cin ? 190 : (same_y ? 260 : 560);
|
||||
break;
|
||||
case ArchArgs::LP384:
|
||||
case ArchArgs::LP1K:
|
||||
case ArchArgs::LP4K:
|
||||
case ArchArgs::LP8K:
|
||||
budget = cin ? 290 : (same_y ? 380 : 670);
|
||||
break;
|
||||
case ArchArgs::UP3K:
|
||||
case ArchArgs::UP5K:
|
||||
case ArchArgs::U1K:
|
||||
case ArchArgs::U2K:
|
||||
case ArchArgs::U4K:
|
||||
budget = cin ? 560 : (same_y ? 660 : 1220);
|
||||
break;
|
||||
default:
|
||||
log_error("Unsupported iCE40 chip type.\n");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool Arch::place()
|
||||
{
|
||||
std::string placer = str_or_default(settings, id_placer, defaultPlacer);
|
||||
|
@ -800,7 +800,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
float getDelayNS(delay_t v) const override { return v * 0.001; }
|
||||
delay_t getDelayFromNS(float ns) const override { return delay_t(ns * 1000); }
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return v; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override;
|
||||
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
||||
|
||||
|
@ -903,7 +903,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
float getDelayNS(delay_t v) const override { return v; }
|
||||
delay_t getDelayFromNS(float ns) const override { return ns; }
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return v; }
|
||||
// bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override;
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -484,8 +484,6 @@ bool Arch::place()
|
||||
|
||||
bool Arch::route()
|
||||
{
|
||||
assign_budget(getCtx(), true);
|
||||
|
||||
lab_pre_route();
|
||||
|
||||
route_globals();
|
||||
|
@ -700,8 +700,6 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr
|
||||
return 100 * dist_x + 100 * dist_y + 250;
|
||||
}
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
|
||||
|
||||
BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
||||
{
|
||||
BoundingBox bb;
|
||||
@ -811,8 +809,6 @@ float router2_base_cost(Context *ctx, WireId wire, PipId pip, float crit_weight)
|
||||
|
||||
bool Arch::route()
|
||||
{
|
||||
assign_budget(getCtx(), true);
|
||||
|
||||
pre_routing();
|
||||
|
||||
route_globals();
|
||||
|
@ -1299,7 +1299,6 @@ struct Arch : BaseArch<ArchRanges>
|
||||
float getDelayNS(delay_t v) const override { return v * 0.001; }
|
||||
delay_t getDelayFromNS(float ns) const override { return delay_t(ns * 1000); }
|
||||
uint32_t getDelayChecksum(delay_t v) const override { return v; }
|
||||
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override;
|
||||
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
||||
|
||||
// for better DSP bounding boxes
|
||||
|
Loading…
Reference in New Issue
Block a user