clangformat

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2018-12-02 13:15:39 +00:00
parent e1c74ad3db
commit 254c5ea359
3 changed files with 96 additions and 86 deletions

View File

@ -85,8 +85,6 @@ struct CriticalPath
delay_t path_period; delay_t path_period;
}; };
typedef std::unordered_map<ClockPair, CriticalPath> CriticalPathMap; typedef std::unordered_map<ClockPair, CriticalPath> CriticalPathMap;
typedef std::unordered_map<IdString, NetCriticalityInfo> NetCriticalityMap; typedef std::unordered_map<IdString, NetCriticalityInfo> NetCriticalityMap;
@ -914,7 +912,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
} }
} }
void get_criticalities(Context *ctx, NetCriticalityMap *net_crit) { void get_criticalities(Context *ctx, NetCriticalityMap *net_crit)
{
CriticalPathMap crit_paths; CriticalPathMap crit_paths;
net_crit->clear(); net_crit->clear();
Timing timing(ctx, true, true, &crit_paths, nullptr, net_crit); Timing timing(ctx, true, true, &crit_paths, nullptr, net_crit);

View File

@ -28,56 +28,60 @@
* and deal with the fact that not every cell on the crit path may be swappable. * and deal with the fact that not every cell on the crit path may be swappable.
*/ */
#include "timing.h"
#include "timing_opt.h" #include "timing_opt.h"
#include "nextpnr.h"
#include "util.h"
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <queue> #include <queue>
#include "nextpnr.h"
#include "timing.h"
#include "util.h"
namespace std { namespace std {
template <> struct hash<std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX IdString>> 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 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));
std::size_t seed = 0; boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.second));
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.first)); return seed;
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.second)); }
return seed; };
}
};
template <> struct hash<std::pair<int, NEXTPNR_NAMESPACE_PREFIX BelId>> 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 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));
std::size_t seed = 0; boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second));
boost::hash_combine(seed, hash<int>()(idp.first)); return seed;
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>> 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 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));
std::size_t seed = 0; boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second));
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(idp.first)); return seed;
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX BelId>()(idp.second)); }
return seed; };
} } // namespace std
};
}
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
class TimingOptimiser class TimingOptimiser
{ {
public: public:
TimingOptimiser(Context *ctx, TimingOptCfg cfg) : ctx(ctx), cfg(cfg) {}; TimingOptimiser(Context *ctx, TimingOptCfg cfg) : ctx(ctx), cfg(cfg){};
bool optimise() { bool optimise()
{
log_info("Running timing-driven placement optimisation...\n"); log_info("Running timing-driven placement optimisation...\n");
#if 1 #if 1
timing_analysis(ctx, false, true, false, false); timing_analysis(ctx, false, true, false, false);
@ -100,13 +104,13 @@ class TimingOptimiser
// 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(); max_net_delay.clear();
for (auto net : sorted(ctx->nets)) { for (auto net : sorted(ctx->nets)) {
NetInfo *ni = net.second; NetInfo *ni = net.second;
for (auto usr : ni->users) { for (auto usr : ni->users) {
max_net_delay[std::make_pair(usr.cell->name, usr.port)] max_net_delay[std::make_pair(usr.cell->name, usr.port)] = std::numeric_limits<delay_t>::max();
= std::numeric_limits<delay_t>::max();
} }
if (!net_crit.count(net.first)) if (!net_crit.count(net.first))
continue; continue;
@ -117,14 +121,15 @@ class TimingOptimiser
auto &usr = ni->users.at(i); auto &usr = ni->users.at(i);
delay_t net_delay = ctx->getNetinfoRouteDelay(ni, usr); delay_t net_delay = ctx->getNetinfoRouteDelay(ni, usr);
if (nc.max_path_length != 0) { if (nc.max_path_length != 0) {
max_net_delay[std::make_pair(usr.cell->name, usr.port)] 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); net_delay + ((nc.slack.at(i) - nc.cd_worst_slack) / nc.max_path_length);
} }
} }
} }
} }
bool check_cell_delay_limits(CellInfo *cell) { bool check_cell_delay_limits(CellInfo *cell)
{
for (const auto &port : cell->ports) { for (const auto &port : cell->ports) {
int nc; int nc;
if (ctx->getPortTimingClass(cell, port.first, nc) == TMG_IGNORE) if (ctx->getPortTimingClass(cell, port.first, nc) == TMG_IGNORE)
@ -137,7 +142,8 @@ class TimingOptimiser
continue; continue;
BelId srcBel = net->driver.cell->bel; BelId srcBel = net->driver.cell->bel;
if (ctx->estimateDelay(ctx->getBelPinWire(srcBel, net->driver.port), 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))) ctx->getBelPinWire(cell->bel, port.first)) >
max_net_delay.at(std::make_pair(cell->name, port.first)))
return false; return false;
} else if (port.second.type == PORT_OUT) { } else if (port.second.type == PORT_OUT) {
for (auto user : net->users) { for (auto user : net->users) {
@ -146,7 +152,8 @@ class TimingOptimiser
if (dstBel == BelId()) if (dstBel == BelId())
continue; continue;
if (ctx->estimateDelay(ctx->getBelPinWire(cell->bel, port.first), 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))) { ctx->getBelPinWire(dstBel, user.port)) >
max_net_delay.at(std::make_pair(user.cell->name, user.port))) {
#if 0 #if 0
if (ctx->debug) { if (ctx->debug) {
log_info(" est delay %.02fns exceeded maximum %.02fns\n", ctx->getDelayNS(ctx->estimateDelay(ctx->getBelPinWire(cell->bel, port.first), log_info(" est delay %.02fns exceeded maximum %.02fns\n", ctx->getDelayNS(ctx->estimateDelay(ctx->getBelPinWire(cell->bel, port.first),
@ -155,16 +162,15 @@ class TimingOptimiser
} }
#endif #endif
return false; return false;
} }
} }
} }
} }
return true; return true;
} }
BelId cell_swap_bel(CellInfo *cell, BelId newBel) { BelId cell_swap_bel(CellInfo *cell, BelId newBel)
{
BelId oldBel = cell->bel; BelId oldBel = cell->bel;
CellInfo *other_cell = ctx->getBoundBelCell(newBel); CellInfo *other_cell = ctx->getBoundBelCell(newBel);
NPNR_ASSERT(other_cell == nullptr || other_cell->belStrength <= STRENGTH_WEAK); NPNR_ASSERT(other_cell == nullptr || other_cell->belStrength <= STRENGTH_WEAK);
@ -179,7 +185,8 @@ class TimingOptimiser
// Check that a series of moves are both legal and remain within maximum delay bounds // Check that a series of moves are both legal and remain within maximum delay bounds
// Moves are specified as a vector of pairs <cell, oldBel> // Moves are specified as a vector of pairs <cell, oldBel>
bool acceptable_move(std::vector<std::pair<CellInfo *, BelId>> &move, bool check_delays = true) { bool acceptable_move(std::vector<std::pair<CellInfo *, BelId>> &move, bool check_delays = true)
{
for (auto &entry : move) { for (auto &entry : move) {
if (!ctx->isBelLocationValid(entry.first->bel)) if (!ctx->isBelLocationValid(entry.first->bel))
return false; return false;
@ -198,7 +205,8 @@ class TimingOptimiser
return true; return true;
} }
int find_neighbours(CellInfo *cell, IdString prev_cell, int d, bool allow_swap) { int find_neighbours(CellInfo *cell, IdString prev_cell, int d, bool allow_swap)
{
BelId curr = cell->bel; BelId curr = cell->bel;
Loc curr_loc = ctx->getBelLocation(curr); Loc curr_loc = ctx->getBelLocation(curr);
int found_count = 0; int found_count = 0;
@ -217,7 +225,8 @@ class TimingOptimiser
CellInfo *bound = ctx->getBoundBelCell(bel); CellInfo *bound = ctx->getBoundBelCell(bel);
if (bound == nullptr) { if (bound == nullptr) {
free_bels_at_loc.push_back(bel); free_bels_at_loc.push_back(bel);
} else if (bound->belStrength <= STRENGTH_WEAK || bound->constr_parent != nullptr || !bound->constr_children.empty()) { } else if (bound->belStrength <= STRENGTH_WEAK || bound->constr_parent != nullptr ||
!bound->constr_children.empty()) {
bound_bels_at_loc.push_back(bel); bound_bels_at_loc.push_back(bel);
} }
} }
@ -236,10 +245,11 @@ class TimingOptimiser
} }
if (bel_candidate_cells.count(try_bel) && !allow_swap) { if (bel_candidate_cells.count(try_bel) && !allow_swap) {
// Overlap is only allowed if it is with the previous cell (this is handled by removing those // Overlap is only allowed if it is with the previous cell (this is handled by removing those
// edges in the graph), or if allow_swap is true to deal with cases where overlap means few neighbours // edges in the graph), or if allow_swap is true to deal with cases where overlap means few
// are identified // neighbours are identified
if (bel_candidate_cells.at(try_bel).size() > 1 || (bel_candidate_cells.at(try_bel).size() == 0 || if (bel_candidate_cells.at(try_bel).size() > 1 ||
*(bel_candidate_cells.at(try_bel).begin()) != prev_cell)) (bel_candidate_cells.at(try_bel).size() == 0 ||
*(bel_candidate_cells.at(try_bel).begin()) != prev_cell))
continue; continue;
} }
// TODO: what else to check here? // TODO: what else to check here?
@ -267,24 +277,23 @@ class TimingOptimiser
return found_count; return found_count;
} }
std::vector<std::vector<PortRef*>> find_crit_paths(float crit_thresh, size_t 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<const 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);
for (auto net : netnames) { for (auto net : netnames) {
if (crit_nets.size() >= max_count) if (crit_nets.size() >= max_count)
break; break;
if (!net_crit.count(net)) if (!net_crit.count(net))
continue; continue;
auto crit_user = std::max_element(net_crit[net].criticality.begin(), auto crit_user = std::max_element(net_crit[net].criticality.begin(), net_crit[net].criticality.end());
net_crit[net].criticality.end());
if (*crit_user > crit_thresh) if (*crit_user > crit_thresh)
crit_nets.push_back(std::make_pair(ctx->nets[net].get(), crit_user - net_crit[net].criticality.begin())); crit_nets.push_back(
std::make_pair(ctx->nets[net].get(), crit_user - net_crit[net].criticality.begin()));
} }
auto port_user_index = [](CellInfo *cell, PortInfo &port) -> size_t { auto port_user_index = [](CellInfo *cell, PortInfo &port) -> size_t {
@ -296,15 +305,15 @@ class TimingOptimiser
} }
NPNR_ASSERT_FALSE("port user not found on net"); NPNR_ASSERT_FALSE("port user not found on net");
}; };
std::unordered_set<PortRef*> used_ports; std::unordered_set<PortRef *> used_ports;
for (auto crit_net : crit_nets) { for (auto crit_net : crit_nets) {
std::deque<PortRef*> crit_path; std::deque<PortRef *> crit_path;
// FIXME: This will fail badly on combinational loops // FIXME: This will fail badly on combinational loops
// Iterate backwards following greatest criticality // Iterate backwards following greatest criticality
NetInfo* back_cursor = crit_net.first; NetInfo *back_cursor = crit_net.first;
while (back_cursor != nullptr) { while (back_cursor != nullptr) {
float max_crit = 0; float max_crit = 0;
std::pair<NetInfo *, size_t> crit_sink{nullptr, 0}; std::pair<NetInfo *, size_t> crit_sink{nullptr, 0};
@ -372,7 +381,6 @@ class TimingOptimiser
crit_sink = std::make_pair(pn, i); crit_sink = std::make_pair(pn, i);
} }
} }
} }
if (crit_sink.first != nullptr) { if (crit_sink.first != nullptr) {
fwd_cursor = &(crit_sink.first->users.at(crit_sink.second)); fwd_cursor = &(crit_sink.first->users.at(crit_sink.second));
@ -382,7 +390,7 @@ class TimingOptimiser
} }
} }
std::vector<PortRef*> crit_path_vec; std::vector<PortRef *> crit_path_vec;
std::copy(crit_path.begin(), crit_path.end(), std::back_inserter(crit_path_vec)); std::copy(crit_path.begin(), crit_path.end(), std::back_inserter(crit_path_vec));
crit_paths.push_back(crit_path_vec); crit_paths.push_back(crit_path_vec);
} }
@ -390,7 +398,8 @@ class TimingOptimiser
return crit_paths; return crit_paths;
} }
void optimise_path(std::vector<PortRef*> &path) { void optimise_path(std::vector<PortRef *> &path)
{
path_cells.clear(); path_cells.clear();
cell_neighbour_bels.clear(); cell_neighbour_bels.clear();
bel_candidate_cells.clear(); bel_candidate_cells.clear();
@ -404,12 +413,13 @@ class TimingOptimiser
for (size_t i = 0; i < pn->users.size(); i++) for (size_t i = 0; i < pn->users.size(); i++)
if (pn->users.at(i).cell == port->cell && pn->users.at(i).port == port->port) if (pn->users.at(i).cell == port->cell && pn->users.at(i).port == port->port)
crit = net_crit.at(pn->name).criticality.at(i); crit = net_crit.at(pn->name).criticality.at(i);
log_info(" %s.%s at %s crit %0.02f\n", port->cell->name.c_str(ctx), port->port.c_str(ctx), ctx->getBelName(port->cell->bel).c_str(ctx), crit); log_info(" %s.%s at %s crit %0.02f\n", port->cell->name.c_str(ctx), port->port.c_str(ctx),
ctx->getBelName(port->cell->bel).c_str(ctx), crit);
} }
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) || port->cell->constr_parent != nullptr || !port->cell->constr_children.empty()) if (port->cell->belStrength > STRENGTH_WEAK || !cfg.cellTypes.count(port->cell->type) ||
port->cell->constr_parent != nullptr || !port->cell->constr_children.empty())
continue; continue;
if (ctx->debug) if (ctx->debug)
log_info(" can move\n"); log_info(" can move\n");
@ -452,7 +462,7 @@ class TimingOptimiser
// Swap for legality check // Swap for legality check
CellInfo *cell = ctx->cells.at(path_cells.front()).get(); CellInfo *cell = ctx->cells.at(path_cells.front()).get();
BelId origBel = cell_swap_bel(cell, startbel); BelId origBel = cell_swap_bel(cell, startbel);
std::vector<std::pair<CellInfo*,BelId>> move{std::make_pair(cell, origBel)}; std::vector<std::pair<CellInfo *, BelId>> move{std::make_pair(cell, origBel)};
if (acceptable_move(move)) { if (acceptable_move(move)) {
auto entry = std::make_pair(0, startbel); auto entry = std::make_pair(0, startbel);
visit.push(entry); visit.push(entry);
@ -462,7 +472,7 @@ class TimingOptimiser
cell_swap_bel(cell, origBel); cell_swap_bel(cell, origBel);
} }
while(!visit.empty()) { while (!visit.empty()) {
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);
@ -500,13 +510,14 @@ class TimingOptimiser
// Check the new cumulative delay // Check the new cumulative delay
auto port_pair = cost_ports.at(ncname); auto port_pair = cost_ports.at(ncname);
delay_t edge_delay = ctx->estimateDelay(ctx->getBelPinWire(port_pair.first->cell->bel, port_pair.first->port), delay_t edge_delay =
ctx->getBelPinWire(port_pair.second->cell->bel, port_pair.second->port)); ctx->estimateDelay(ctx->getBelPinWire(port_pair.first->cell->bel, port_pair.first->port),
ctx->getBelPinWire(port_pair.second->cell->bel, port_pair.second->port));
delay_t total_delay = cdelay + edge_delay; delay_t total_delay = cdelay + edge_delay;
// First, check if the move is actually worthwhile from a delay point of view before the expensive // First, check if the move is actually worthwhile from a delay point of view before the expensive
// legality check // legality check
if (!cumul_costs.count(ncname) || !cumul_costs.at(ncname).count(neighbour) if (!cumul_costs.count(ncname) || !cumul_costs.at(ncname).count(neighbour) ||
|| cumul_costs.at(ncname).at(neighbour) > total_delay) { cumul_costs.at(ncname).at(neighbour) > total_delay) {
// Now check that the swaps we have made to get here are legal and meet max delay requirements // Now check that the swaps we have made to get here are legal and meet max delay requirements
if (acceptable_move(move)) { if (acceptable_move(move)) {
cumul_costs[ncname][neighbour] = total_delay; cumul_costs[ncname][neighbour] = total_delay;
@ -531,10 +542,10 @@ class TimingOptimiser
if (cumul_costs.count(path_cells.back())) { if (cumul_costs.count(path_cells.back())) {
// Find the end position with the lowest total delay // Find the end position with the lowest total delay
auto &end_options = cumul_costs.at(path_cells.back()); 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, auto lowest = std::min_element(end_options.begin(), end_options.end(),
const std::pair<BelId, delay_t> &b) { [](const std::pair<BelId, delay_t> &a, const std::pair<BelId, delay_t> &b) {
return a.second < b.second; return a.second < b.second;
}); });
NPNR_ASSERT(lowest != end_options.end()); NPNR_ASSERT(lowest != end_options.end());
std::vector<std::pair<IdString, BelId>> route_to_solution; std::vector<std::pair<IdString, BelId>> route_to_solution;
@ -549,7 +560,7 @@ class TimingOptimiser
for (auto rt_entry : boost::adaptors::reverse(route_to_solution)) { for (auto rt_entry : boost::adaptors::reverse(route_to_solution)) {
CellInfo *cell = ctx->cells.at(rt_entry.first).get(); CellInfo *cell = ctx->cells.at(rt_entry.first).get();
cell_swap_bel(cell, rt_entry.second); cell_swap_bel(cell, rt_entry.second);
if(ctx->debug) if (ctx->debug)
log_info(" %s at %s\n", rt_entry.first.c_str(ctx), ctx->getBelName(rt_entry.second).c_str(ctx)); log_info(" %s at %s\n", rt_entry.first.c_str(ctx), ctx->getBelName(rt_entry.second).c_str(ctx));
} }
@ -571,7 +582,6 @@ class TimingOptimiser
NetCriticalityMap net_crit; NetCriticalityMap net_crit;
Context *ctx; Context *ctx;
TimingOptCfg cfg; TimingOptCfg cfg;
}; };
bool timing_opt(Context *ctx, TimingOptCfg cfg) { return TimingOptimiser(ctx, cfg).optimise(); } bool timing_opt(Context *ctx, TimingOptCfg cfg) { return TimingOptimiser(ctx, cfg).optimise(); }

View File

@ -26,8 +26,8 @@
#include "nextpnr.h" #include "nextpnr.h"
#include "placer1.h" #include "placer1.h"
#include "router1.h" #include "router1.h"
#include "util.h"
#include "timing_opt.h" #include "timing_opt.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -627,8 +627,9 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
bool Arch::place() { bool Arch::place()
if(!placer1(getCtx(), Placer1Cfg(getCtx()))) {
if (!placer1(getCtx(), Placer1Cfg(getCtx())))
return false; return false;
TimingOptCfg tocfg(getCtx()); TimingOptCfg tocfg(getCtx());
tocfg.cellTypes.insert(id_ICESTORM_LC); tocfg.cellTypes.insert(id_ICESTORM_LC);