From 7aab4925b4573613e9b39c4f141c3e3f1a9e6f57 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 5 Aug 2018 22:31:59 -0700 Subject: Change getBudgetOverride() signature to return bool and modify budget in place --- ice40/arch.cc | 10 ++++++---- ice40/arch.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 0b168383..b8bb13ea 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -637,17 +637,19 @@ std::vector Arch::getGroupGroups(GroupId group) const // ----------------------------------------------------------------------- -delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) 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) { auto driver_loc = getBelLocation(driver.cell->bel); auto sink_loc = getBelLocation(sink.cell->bel); if (driver_loc.y == sink_loc.y) - return 0; - return 250; + budget = 0; + else + budget = 190; + return true; } - return budget; + return false; } // ----------------------------------------------------------------------- diff --git a/ice40/arch.h b/ice40/arch.h index 236f73f1..328950df 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -766,7 +766,7 @@ struct Arch : BaseCtx delay_t getRipupDelayPenalty() const { return 200; } float getDelayNS(delay_t v) const { return v * 0.001; } uint32_t getDelayChecksum(delay_t v) const { return v; } - delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const; + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; // ------------------------------------------------- -- cgit v1.2.3 From 8a6ff4b261b23d8bce3efb0236b6a74621121918 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 5 Aug 2018 22:33:14 -0700 Subject: Modify getBudgetOverride for generic and ecp5 too --- ecp5/arch.cc | 2 +- ecp5/arch.h | 2 +- generic/arch.cc | 2 +- generic/arch.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d72b0085..d2d62241 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -422,7 +422,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const return 200 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y)); } -delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { return budget; } +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } // ----------------------------------------------------------------------- diff --git a/ecp5/arch.h b/ecp5/arch.h index 74f0c4e1..e00e111a 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -805,7 +805,7 @@ struct Arch : BaseCtx delay_t getRipupDelayPenalty() const { return 200; } float getDelayNS(delay_t v) const { return v * 0.001; } uint32_t getDelayChecksum(delay_t v) const { return v; } - delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const; + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; // ------------------------------------------------- diff --git a/generic/arch.cc b/generic/arch.cc index 7e65d411..0fa93da8 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -408,7 +408,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const return (dx + dy) * grid_distance_to_delay; } -delay_t Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const { return budget; } +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } // --------------------------------------------------------------- diff --git a/generic/arch.h b/generic/arch.h index a5b3470f..59fe8d05 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -200,7 +200,7 @@ struct Arch : BaseCtx delay_t getRipupDelayPenalty() const { return 1.0; } float getDelayNS(delay_t v) const { return v; } uint32_t getDelayChecksum(delay_t v) const { return 0; } - delay_t getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t budget) const; + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; bool pack() { return true; } bool place(); -- cgit v1.2.3 From e314ea761abd8f55b3341f733c01d13ce2d4fae5 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 5 Aug 2018 22:38:54 -0700 Subject: WIP for new assign_budget() using topographical ordering --- common/timing.cc | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 6 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index d37a0f59..23fbd9f9 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -23,6 +23,7 @@ #include #include "log.h" #include "util.h" +#include NEXTPNR_NAMESPACE_BEGIN @@ -38,6 +39,14 @@ struct Timing PortRefVector *crit_path; DelayFrequency *slack_histogram; + struct TimingData { + TimingData() : max_arrival(), max_path_length(), min_remaining_budget() {} + TimingData(delay_t max_arrival) : max_arrival(max_arrival), max_path_length(), min_remaining_budget() {} + delay_t max_arrival; + unsigned max_path_length = 0; + delay_t min_remaining_budget; + }; + Timing(Context *ctx, bool update, PortRefVector *crit_path = nullptr, DelayFrequency *slack_histogram = nullptr) : ctx(ctx), update(update), min_slack(1.0e12 / ctx->target_freq), crit_path(crit_path), slack_histogram(slack_histogram) @@ -53,8 +62,8 @@ struct Timing // If budget override is less than existing budget, then do not increment // path length int pl = path_length + 1; - auto budget = ctx->getBudgetOverride(net, usr, net_budget); - if (budget < net_budget) { + auto budget = net_budget; + if (ctx->getBudgetOverride(net, usr, budget) && budget < net_budget) { net_budget = budget; pl = std::max(1, path_length); } @@ -109,16 +118,17 @@ struct Timing delay_t walk_paths() { - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); + const auto clk_period = delay_t(1.0e12 / ctx->target_freq); // Go through all clocked drivers and distribute the available path // slack evenly into the budget of every sink on the path +#if 0 for (auto &cell : ctx->cells) { for (auto port : cell.second->ports) { if (port.second.type == PORT_OUT) { IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); if (clock_domain != IdString()) { - delay_t slack = default_slack; // TODO: clock constraints + delay_t slack = clk_period; // TODO: clock constraints DelayInfo clkToQ; if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ)) slack -= clkToQ.maxDelay(); @@ -128,16 +138,165 @@ struct Timing } } } + +#else + std::vector topographical_order; + std::unordered_map port_fanin; + std::unordered_map net_data; + + std::vector input_ports; + std::vector output_ports; + for (auto &cell : ctx->cells) { + input_ports.clear(); + output_ports.clear(); + bool is_io = cell.second->type == ctx->id_sb_io; + for (auto& port : cell.second->ports) { + if (!port.second.net) continue; + if (port.second.type == PORT_OUT) + output_ports.push_back(&port.second); + else + input_ports.push_back(port.first); + } + + for (auto o : output_ports) { + IdString clock_domain = ctx->getPortClock(cell.second.get(), o->name); + if (clock_domain != IdString()) { + DelayInfo clkToQ; + ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); + topographical_order.emplace_back(o->net); + net_data.emplace(o->net, TimingData{ clkToQ.maxDelay() }); + } + else { + if (is_io) { + topographical_order.emplace_back(o->net); + net_data.emplace(o->net, TimingData{}); + } + for (auto i : input_ports) { + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); + if (is_path) + port_fanin[o]++; + } + } + } + } + + std::deque queue(topographical_order.begin(), topographical_order.end()); + + while (!queue.empty()) { + const auto net = queue.front(); + queue.pop_front(); + + for (auto &usr : net->users) { + if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + } else { + // Follow outputs of the user + for (auto& port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto it = port_fanin.find(&port.second); + NPNR_ASSERT(it != port_fanin.end()); + if (--it->second == 0) { + topographical_order.emplace_back(port.second.net); + queue.emplace_back(port.second.net); + port_fanin.erase(it); + } + } + } + } + } + } + } + + // Find the maximum arrival time and max path length for each net + for (auto net : topographical_order) { + auto &nd = net_data.at(net); + const auto net_arrival = nd.max_arrival; + const auto net_length_plus_one = nd.max_path_length + 1; + nd.min_remaining_budget = clk_period; + for (auto &usr : net->users) { + if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + } else { + auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + delay_t budget; + auto budget_override = ctx->getBudgetOverride(net, usr, budget); + auto usr_arrival = net_arrival + net_delay; + // Follow outputs of the user + for (auto port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + DelayInfo comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto& data = net_data[port.second.net]; + auto& arrival = data.max_arrival; + arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); + if (!budget_override) { + auto& path_length = data.max_path_length; + path_length = std::max(path_length, net_length_plus_one); + } + } + } + } + } + } + } + + for (auto net : boost::adaptors::reverse(topographical_order)) { + auto &nd = net_data.at(net); + const delay_t net_length_plus_one = nd.max_path_length + 1; + auto& net_min_remaining_budget = nd.min_remaining_budget; + for (auto &usr : net->users) { + const auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + auto budget_override = ctx->getBudgetOverride(net, usr, usr.budget); + if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + const auto net_arrival = nd.max_arrival; + auto path_budget = clk_period - (net_arrival + net_delay); + auto budget_share = path_budget / net_length_plus_one; + if (budget_override) + budget_share = 0; + else + usr.budget = std::min(usr.budget, net_delay + budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + + min_slack = std::min(min_slack, path_budget); + if (slack_histogram) { + int slack_ps = ctx->getDelayNS(path_budget) * 1000; + (*slack_histogram)[slack_ps]++; + } + } else { + // Follow outputs of the user + for (auto port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + DelayInfo comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto path_budget = net_data.at(port.second.net).min_remaining_budget; + auto budget_share = path_budget / net_length_plus_one; + if (budget_override) + budget_share = 0; + else + usr.budget = std::min(usr.budget, net_delay + budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + } + } + } + } + } + } +#endif return min_slack; } void assign_budget() { // Clear delays to a very high value first - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); for (auto &net : ctx->nets) { for (auto &usr : net.second->users) { - usr.budget = default_slack; + usr.budget = std::numeric_limits::max(); } } -- cgit v1.2.3 From 3f5c0373a5eb77cf4cd40f1d280452bfbe6f42f8 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 12:03:58 -0700 Subject: Consider clocked cells with COUT, consider constant nets --- common/timing.cc | 58 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 72093183..12a4940a 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -155,7 +155,7 @@ struct Timing for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - bool is_io = cell.second->type == ctx->id_sb_io; + bool is_io = cell.second->type == ctx->id_sb_io; // HACK HACK HACK for (auto& port : cell.second->ports) { if (!port.second.net) continue; if (port.second.type == PORT_OUT) @@ -187,28 +187,41 @@ struct Timing } } + auto it = ctx->nets.find(ctx->id("$PACKER_VCC_NET")); + if (it != ctx->nets.end()) { + topographical_order.emplace_back(it->second.get()); + net_data.emplace(it->second.get(), TimingData{}); + } + it = ctx->nets.find(ctx->id("$PACKER_GND_NET")); + if (it != ctx->nets.end()) { + topographical_order.emplace_back(it->second.get()); + net_data.emplace(it->second.get(), TimingData{}); + } + std::deque queue(topographical_order.begin(), topographical_order.end()); while (!queue.empty()) { const auto net = queue.front(); queue.pop_front(); + DelayInfo clkToQ; for (auto &usr : net->users) { - if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { - } else { - // Follow outputs of the user - for (auto& port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - auto it = port_fanin.find(&port.second); - NPNR_ASSERT(it != port_fanin.end()); - if (--it->second == 0) { - topographical_order.emplace_back(port.second.net); - queue.emplace_back(port.second.net); - port_fanin.erase(it); - } + auto clock_domain = ctx->getPortClock(usr.cell, usr.port); + // Follow outputs of the user + for (auto& port : usr.cell->ports) { + if (port.second.type == PORT_OUT && port.second.net) { + // Skip if this is a clocked output + if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) + continue; + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (is_path) { + auto it = port_fanin.find(&port.second); + NPNR_ASSERT(it != port_fanin.end()); + if (--it->second == 0) { + topographical_order.emplace_back(port.second.net); + queue.emplace_back(port.second.net); + port_fanin.erase(it); } } } @@ -216,6 +229,9 @@ struct Timing } } + NPNR_ASSERT(port_fanin.empty()); + port_fanin.clear(); + // Find the maximum arrival time and max path length for each net for (auto net : topographical_order) { auto &nd = net_data.at(net); @@ -225,7 +241,7 @@ struct Timing for (auto &usr : net->users) { if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { } else { - auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); auto usr_arrival = net_arrival + net_delay; // Follow outputs of the user @@ -254,13 +270,14 @@ struct Timing const delay_t net_length_plus_one = nd.max_path_length + 1; auto& net_min_remaining_budget = nd.min_remaining_budget; for (auto &usr : net->users) { - const auto net_delay = ctx->getNetinfoRouteDelay(net, usr); + auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { const auto net_arrival = nd.max_arrival; auto path_budget = clk_period - (net_arrival + net_delay); auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - usr.budget = std::min(usr.budget, net_delay + budget_share); + if (update) + usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); min_slack = std::min(min_slack, path_budget); @@ -278,7 +295,8 @@ struct Timing if (is_path) { auto path_budget = net_data.at(port.second.net).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); + if (update) + usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); } } -- cgit v1.2.3 From 21cd1d7dd6c1d033c933006a96b5eebfef220a9d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 12:11:47 -0700 Subject: Add new Arch::isIOCell() API function --- ice40/arch.cc | 5 +++++ ice40/arch.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/ice40/arch.cc b/ice40/arch.cc index 1e7a383b..b142ae8b 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -898,6 +898,11 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out; } +bool Arch::isIOCell(const CellInfo *cell) const +{ + return cell->type == id_sb_io; +} + // Assign arch arg info void Arch::assignArchInfo() { diff --git a/ice40/arch.h b/ice40/arch.h index 328950df..2b785079 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -794,6 +794,8 @@ struct Arch : BaseCtx bool isClockPort(const CellInfo *cell, IdString port) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; + // Return true if a cell is an IO + bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- -- cgit v1.2.3 From daedf732911bc6d149e7c6db14f4fd976c79bbac Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 12:12:03 -0700 Subject: Use new Arch::isIOCell() function in Timing --- common/timing.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index 12a4940a..eb3cdc55 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -155,7 +155,7 @@ struct Timing for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - bool is_io = cell.second->type == ctx->id_sb_io; // HACK HACK HACK + bool is_io = ctx->isIOCell(cell.second.get()); for (auto& port : cell.second->ports) { if (!port.second.net) continue; if (port.second.type == PORT_OUT) -- cgit v1.2.3 From 519b755acb9155caef0346fd478a6bc02ef9ae8d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 13:12:24 -0700 Subject: Add comments --- common/timing.cc | 54 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 11383cee..49e8a5ef 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -127,9 +127,9 @@ struct Timing { const auto clk_period = delay_t(1.0e12 / ctx->target_freq); +#if 0 // Go through all clocked drivers and distribute the available path // slack evenly into the budget of every sink on the path -#if 0 for (auto &cell : ctx->cells) { for (auto port : cell.second->ports) { if (port.second.type == PORT_OUT) { @@ -147,16 +147,20 @@ struct Timing } #else + // First, compute the topographical order of nets to walk through + // the circuit, assuming it is a _acyclic_ graph + // TODO: Handle the case where it is cyclic, e.g. combinatorial loops std::vector topographical_order; - std::unordered_map port_fanin; std::unordered_map net_data; + // In lieu of deleting edges from the graph, simply count + // the number of fanins to each output port + std::unordered_map port_fanin; std::vector input_ports; std::vector output_ports; for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - bool is_io = ctx->isIOCell(cell.second.get()); for (auto& port : cell.second->ports) { if (!port.second.net) continue; if (port.second.type == PORT_OUT) @@ -165,8 +169,11 @@ struct Timing input_ports.push_back(port.first); } + bool is_io = ctx->isIOCell(cell.second.get()); for (auto o : output_ports) { IdString clock_domain = ctx->getPortClock(cell.second.get(), o->name); + // If output port is influenced by a clock (e.g. FF output) + // then add it to the ordering as a timing start-point if (clock_domain != IdString()) { DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); @@ -174,10 +181,14 @@ struct Timing net_data.emplace(o->net, TimingData{ clkToQ.maxDelay() }); } else { + // Also add I/O cells too if (is_io) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } + // Otherwise, for all driven input ports on this cell, + // if a timing arch exists between the input and + // the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); @@ -188,6 +199,7 @@ struct Timing } } + // If these constant nets exist, add them to the topographical ordering too auto it = ctx->nets.find(ctx->id("$PACKER_VCC_NET")); if (it != ctx->nets.end()) { topographical_order.emplace_back(it->second.get()); @@ -201,6 +213,8 @@ struct Timing std::deque queue(topographical_order.begin(), topographical_order.end()); + // Now walk the design, from the start points identified previously, building + // up a topographical order while (!queue.empty()) { const auto net = queue.front(); queue.pop_front(); @@ -208,15 +222,16 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { auto clock_domain = ctx->getPortClock(usr.cell, usr.port); - // Follow outputs of the user for (auto& port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { - // Skip if this is a clocked output + // Skip if this is a clocked output (but allow non-clocked ones) if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) continue; DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (is_path) { + // Decrement the fanin count, and only add to topographical + // order if all its fanins have already been visited auto it = port_fanin.find(&port.second); NPNR_ASSERT(it != port_fanin.end()); if (--it->second == 0) { @@ -230,10 +245,25 @@ struct Timing } } +#if 0 + // Sanity check to ensure that all ports where fanins were recorded + // were indeed visited + log_info("port_fanin = %d\n", port_fanin.size()); + for (auto i : port_fanin) { + log_info("%s %s.%s has %d fanins left\n", i.first->net->name.c_str(ctx),i.first->net->driver.cell->name.c_str(ctx), i.first->name.c_str(ctx), i.second); + auto cell = i.first->net->driver.cell; + for (auto& port : cell->ports) { + if (!port.second.net) continue; + if (port.second.type == PORT_IN) + log_info(" %s connected to %s\n", port.second.name.c_str(ctx), port.second.net->name.c_str(ctx)); + } + } NPNR_ASSERT(port_fanin.empty()); +#endif port_fanin.clear(); - // Find the maximum arrival time and max path length for each net + // Go forwards topographically to find the maximum arrival time + // and max path length for each net for (auto net : topographical_order) { auto &nd = net_data.at(net); const auto net_arrival = nd.max_arrival; @@ -245,7 +275,7 @@ struct Timing auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); auto usr_arrival = net_arrival + net_delay; - // Follow outputs of the user + // 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) { DelayInfo comb_delay; @@ -255,7 +285,8 @@ struct Timing auto& data = net_data[port.second.net]; auto& arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { + if (!budget_override) { // Do not increment path length if budget overriden + // 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); } @@ -266,6 +297,8 @@ struct Timing } } + // Now go backwards topographically to determine the minimum path slack, + // and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; @@ -287,11 +320,10 @@ struct Timing (*slack_histogram)[slack_ps]++; } } else { - // Follow outputs of the user - for (auto port : usr.cell->ports) { + // Iterate over all output ports on the same cell as the sink + for (const auto& port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { DelayInfo comb_delay; - // Look up delay through this path bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (is_path) { auto path_budget = net_data.at(port.second.net).min_remaining_budget; -- cgit v1.2.3 From 06584f2e74bff6bf5a179e7f4a0dbebda7ef6caf Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 14:14:41 -0700 Subject: Compute critical path report --- common/timing.cc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 49e8a5ef..8fe5cd49 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -145,7 +145,6 @@ struct Timing } } } - #else // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph @@ -297,6 +296,8 @@ struct Timing } } + const NetInfo* crit_net = nullptr; + // Now go backwards topographically to determine the minimum path slack, // and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { @@ -314,7 +315,14 @@ struct Timing usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); - min_slack = std::min(min_slack, path_budget); + if (path_budget < min_slack) { + min_slack = path_budget; + if (crit_path) { + crit_path->clear(); + crit_path->push_back(&usr); + crit_net = net; + } + } if (slack_histogram) { int slack_ps = ctx->getDelayNS(path_budget) * 1000; (*slack_histogram)[slack_ps]++; @@ -337,6 +345,45 @@ struct Timing } } } + + if (crit_path) { + // Walk backwards from the most critical net + while (crit_net) { + const PortInfo* crit_ipin = nullptr; + delay_t max_arrival = std::numeric_limits::min(); + + // Look at all input ports on its driving cell + for (const auto& port : crit_net->driver.cell->ports) { + if (port.second.type == PORT_IN && port.second.net) { + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); + if (is_path) { + // If input port is influenced by a clock, skip + if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) + continue; + + // And find the fanin net with the latest arrival time + const auto net_arrival = net_data.at(port.second.net).max_arrival; + if (net_arrival > max_arrival) { + max_arrival = net_arrival; + crit_ipin = &port.second; + } + } + } + } + + if (!crit_ipin) break; + + for (auto &usr : crit_ipin->net->users) { + if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) { + crit_path->push_back(&usr); + break; + } + } + crit_net = crit_ipin->net; + } + std::reverse(crit_path->begin(), crit_path->end()); + } #endif return min_slack; } -- cgit v1.2.3 From f3e46df7095582b23ffcd4726e1b33cd77df6ba0 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 16:09:17 -0700 Subject: Remove old timing code --- common/timing.cc | 88 -------------------------------------------------------- 1 file changed, 88 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 8fe5cd49..b1be8dad 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -37,7 +37,6 @@ struct Timing bool net_delays; bool update; delay_t min_slack; - PortRefVector current_path; PortRefVector *crit_path; DelayFrequency *slack_histogram; @@ -56,96 +55,10 @@ struct Timing { } - delay_t follow_net(NetInfo *net, int path_length, delay_t slack) - { - const delay_t default_budget = slack / (path_length + 1); - delay_t net_budget = default_budget; - for (auto &usr : net->users) { - auto delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); - if (crit_path) - current_path.push_back(&usr); - // If budget override exists, use that value and do not increment path_length - auto budget = default_budget; - if (ctx->getBudgetOverride(net, usr, budget)) { - if (update) - usr.budget = std::min(usr.budget, budget); - budget = follow_user_port(usr, path_length, slack - budget); - net_budget = std::min(net_budget, budget); - } - else { - budget = follow_user_port(usr, path_length + 1, slack - delay); - net_budget = std::min(net_budget, budget); - if (update) - usr.budget = std::min(usr.budget, delay + budget); - } - if (crit_path) - current_path.pop_back(); - } - return net_budget; - } - - // Follow a path, returning budget to annotate - delay_t follow_user_port(PortRef &user, int path_length, delay_t slack) - { - delay_t value; - if (ctx->getPortClock(user.cell, user.port) != IdString()) { - // At the end of a timing path (arguably, should check setup time - // here too) - value = slack / path_length; - if (slack < min_slack) { - min_slack = slack; - if (crit_path) - *crit_path = current_path; - } - if (slack_histogram) { - int slack_ps = ctx->getDelayNS(slack) * 1000; - (*slack_histogram)[slack_ps]++; - } - } else { - // Default to the path ending here, if no further paths found - value = slack / path_length; - // Follow outputs of the user - for (auto port : user.cell->ports) { - if (port.second.type == PORT_OUT) { - DelayInfo comb_delay; - // Look up delay through this path - bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay); - if (is_path) { - NetInfo *net = port.second.net; - if (net) { - delay_t path_budget = follow_net(net, path_length, slack - comb_delay.maxDelay()); - value = std::min(value, path_budget); - } - } - } - } - } - return value; - } - delay_t walk_paths() { const auto clk_period = delay_t(1.0e12 / ctx->target_freq); -#if 0 - // Go through all clocked drivers and distribute the available path - // slack evenly into the budget of every sink on the path - for (auto &cell : ctx->cells) { - for (auto port : cell.second->ports) { - if (port.second.type == PORT_OUT) { - IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first); - if (clock_domain != IdString()) { - delay_t slack = clk_period; // TODO: clock constraints - DelayInfo clkToQ; - if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ)) - slack -= clkToQ.maxDelay(); - if (port.second.net) - follow_net(port.second.net, 0, slack); - } - } - } - } -#else // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph // TODO: Handle the case where it is cyclic, e.g. combinatorial loops @@ -384,7 +297,6 @@ struct Timing } std::reverse(crit_path->begin(), crit_path->end()); } -#endif return min_slack; } -- cgit v1.2.3 From 6768a5c03e59e75ac20c666419b4e42cc16118e1 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:17:39 -0700 Subject: Add Arch::isIOCell() to ecp5 and generic --- ecp5/arch.cc | 6 ++++++ ecp5/arch.h | 2 ++ generic/arch.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d2d62241..4a3e8ef3 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -500,6 +500,12 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } +bool Arch::isIOCell(const CellInfo *cell) const +{ + return cell->type == id("TRELLIS_IO"); +} + + std::vector> Arch::getTilesAtLocation(int row, int col) { std::vector> ret; diff --git a/ecp5/arch.h b/ecp5/arch.h index e00e111a..fd8d0a13 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -833,6 +833,8 @@ struct Arch : BaseCtx bool isClockPort(const CellInfo *cell, IdString port) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; + // Return true if a cell is an IO + bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- // Placement validity checks diff --git a/generic/arch.h b/generic/arch.h index 59fe8d05..e7010885 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -215,6 +215,8 @@ struct Arch : BaseCtx bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; IdString getPortClock(const CellInfo *cell, IdString port) const; bool isClockPort(const CellInfo *cell, IdString port) const; + // Return true if a cell is an IO + bool isIOCell(const CellInfo *cell) const; bool isValidBelForCell(CellInfo *cell, BelId bel) const; bool isBelLocationValid(BelId bel) const; -- cgit v1.2.3 From 483f8631068086d7f0bdf10f60205567e03d943e Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:20:29 -0700 Subject: Also add PLL outputs as timing startpoints --- common/timing.cc | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index b1be8dad..3a4f0f9c 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -61,7 +61,7 @@ struct Timing // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph - // TODO: Handle the case where it is cyclic, e.g. combinatorial loops + // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops std::vector topographical_order; std::unordered_map net_data; // In lieu of deleting edges from the graph, simply count @@ -94,7 +94,8 @@ struct Timing } else { // Also add I/O cells too - if (is_io) { + // TODO(eddieh): More generic way of detecting PLLs + if (is_io || cell.second->type == ctx->id("ICESTORM_PLL")) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } @@ -157,22 +158,9 @@ struct Timing } } -#if 0 // Sanity check to ensure that all ports where fanins were recorded // were indeed visited - log_info("port_fanin = %d\n", port_fanin.size()); - for (auto i : port_fanin) { - log_info("%s %s.%s has %d fanins left\n", i.first->net->name.c_str(ctx),i.first->net->driver.cell->name.c_str(ctx), i.first->name.c_str(ctx), i.second); - auto cell = i.first->net->driver.cell; - for (auto& port : cell->ports) { - if (!port.second.net) continue; - if (port.second.type == PORT_IN) - log_info(" %s connected to %s\n", port.second.name.c_str(ctx), port.second.net->name.c_str(ctx)); - } - } NPNR_ASSERT(port_fanin.empty()); -#endif - port_fanin.clear(); // Go forwards topographically to find the maximum arrival time // and max path length for each net -- cgit v1.2.3 From f44a5fb904e6e52e8383e8f13d9b3f4ab9d7ce48 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:35:23 -0700 Subject: clangformat --- common/timing.cc | 65 +++++++++++++++++++++++++++++++------------------------- ecp5/arch.cc | 7 +----- ice40/arch.cc | 22 ++++++++++--------- ice40/arch.h | 14 ++++++------ 4 files changed, 57 insertions(+), 51 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 3a4f0f9c..e524cd19 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -20,11 +20,11 @@ #include "timing.h" #include +#include #include #include #include "log.h" #include "util.h" -#include NEXTPNR_NAMESPACE_BEGIN @@ -40,7 +40,8 @@ struct Timing PortRefVector *crit_path; DelayFrequency *slack_histogram; - struct TimingData { + struct TimingData + { TimingData() : max_arrival(), max_path_length(), min_remaining_budget() {} TimingData(delay_t max_arrival) : max_arrival(max_arrival), max_path_length(), min_remaining_budget() {} delay_t max_arrival; @@ -61,20 +62,22 @@ struct Timing // First, compute the topographical order of nets to walk through // the circuit, assuming it is a _acyclic_ graph - // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops - std::vector topographical_order; - std::unordered_map net_data; + // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial + // loops + std::vector topographical_order; + std::unordered_map net_data; // In lieu of deleting edges from the graph, simply count // the number of fanins to each output port - std::unordered_map port_fanin; + std::unordered_map port_fanin; std::vector input_ports; - std::vector output_ports; + std::vector output_ports; for (auto &cell : ctx->cells) { input_ports.clear(); output_ports.clear(); - for (auto& port : cell.second->ports) { - if (!port.second.net) continue; + for (auto &port : cell.second->ports) { + if (!port.second.net) + continue; if (port.second.type == PORT_OUT) output_ports.push_back(&port.second); else @@ -90,11 +93,10 @@ struct Timing DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); topographical_order.emplace_back(o->net); - net_data.emplace(o->net, TimingData{ clkToQ.maxDelay() }); - } - else { + net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); + } else { // Also add I/O cells too - // TODO(eddieh): More generic way of detecting PLLs + // TODO(eddieh): More generic way of detecting PLLs if (is_io || cell.second->type == ctx->id("ICESTORM_PLL")) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); @@ -124,9 +126,10 @@ struct Timing net_data.emplace(it->second.get(), TimingData{}); } - std::deque queue(topographical_order.begin(), topographical_order.end()); + std::deque queue(topographical_order.begin(), topographical_order.end()); - // Now walk the design, from the start points identified previously, building + // Now walk the design, from the start points identified previously, + // building // up a topographical order while (!queue.empty()) { const auto net = queue.front(); @@ -135,7 +138,7 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { auto clock_domain = ctx->getPortClock(usr.cell, usr.port); - for (auto& port : usr.cell->ports) { + for (auto &port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { // Skip if this is a clocked output (but allow non-clocked ones) if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) @@ -182,12 +185,13 @@ struct Timing // Look up delay through this path bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (is_path) { - auto& data = net_data[port.second.net]; - auto& arrival = data.max_arrival; + auto &data = net_data[port.second.net]; + auto &arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if budget overriden - // since it doesn't require a share of the slack - auto& path_length = data.max_path_length; + if (!budget_override) { // Do not increment path length if + // budget overriden + // 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); } } @@ -197,14 +201,14 @@ struct Timing } } - const NetInfo* crit_net = nullptr; + const NetInfo *crit_net = nullptr; // Now go backwards topographically to determine the minimum path slack, // and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; - auto& net_min_remaining_budget = nd.min_remaining_budget; + 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); @@ -230,7 +234,7 @@ struct Timing } } else { // Iterate over all output ports on the same cell as the sink - for (const auto& port : usr.cell->ports) { + for (const auto &port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); @@ -239,7 +243,8 @@ struct Timing auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; if (update) usr.budget = std::min(usr.budget, net_delay + budget_share); - net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + net_min_remaining_budget = + std::min(net_min_remaining_budget, path_budget - budget_share); } } } @@ -250,14 +255,15 @@ struct Timing if (crit_path) { // Walk backwards from the most critical net while (crit_net) { - const PortInfo* crit_ipin = nullptr; + const PortInfo *crit_ipin = nullptr; delay_t max_arrival = std::numeric_limits::min(); // Look at all input ports on its driving cell - for (const auto& port : crit_net->driver.cell->ports) { + for (const auto &port : crit_net->driver.cell->ports) { if (port.second.type == PORT_IN && port.second.net) { DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); + bool is_path = + ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); if (is_path) { // If input port is influenced by a clock, skip if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) @@ -273,7 +279,8 @@ struct Timing } } - if (!crit_ipin) break; + if (!crit_ipin) + break; for (auto &usr : crit_ipin->net->users) { if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) { diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4a3e8ef3..de3abd44 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -375,7 +375,6 @@ BelId Arch::getPioByFunctionName(const std::string &name) const } std::vector Arch::getBelPins(BelId bel) const - { std::vector ret; NPNR_ASSERT(bel != BelId()); @@ -500,11 +499,7 @@ IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } -bool Arch::isIOCell(const CellInfo *cell) const -{ - return cell->type == id("TRELLIS_IO"); -} - +bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id("TRELLIS_IO"); } std::vector> Arch::getTilesAtLocation(int row, int col) { diff --git a/ice40/arch.cc b/ice40/arch.cc index 324934d0..16104033 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -291,7 +291,8 @@ BelId Arch::getBelByLocation(Loc loc) const BelRange Arch::getBelsByTile(int x, int y) const { - // In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates are used + // In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates + // are used BelRange br; br.b.cursor = Arch::getBelByLocation(Loc(x, y, 0)).index; @@ -645,23 +646,27 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay auto sink_loc = getBelLocation(sink.cell->bel); if (driver_loc.y == sink_loc.y) budget = 0; - else switch (args.type) { + else + switch (args.type) { #ifndef ICE40_HX1K_ONLY case ArchArgs::HX8K: #endif case ArchArgs::HX1K: - budget = 190; break; + budget = 190; + break; #ifndef ICE40_HX1K_ONLY case ArchArgs::LP384: case ArchArgs::LP1K: case ArchArgs::LP8K: - budget = 290; break; + budget = 290; + break; case ArchArgs::UP5K: - budget = 560; break; + budget = 560; + break; #endif default: log_error("Unsupported iCE40 chip type.\n"); - } + } return true; } return false; @@ -913,10 +918,7 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out; } -bool Arch::isIOCell(const CellInfo *cell) const -{ - return cell->type == id_sb_io; -} +bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id_sb_io; } // Assign arch arg info void Arch::assignArchInfo() diff --git a/ice40/arch.h b/ice40/arch.h index 2b785079..a5be7e33 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -400,10 +400,10 @@ struct Arch : BaseCtx mutable std::unordered_map bel_by_loc; std::vector bel_carry; - std::vector bel_to_cell; - std::vector wire_to_net; - std::vector pip_to_net; - std::vector switches_locked; + std::vector bel_to_cell; + std::vector wire_to_net; + std::vector pip_to_net; + std::vector switches_locked; ArchArgs args; Arch(ArchArgs args); @@ -799,7 +799,8 @@ struct Arch : BaseCtx // ------------------------------------------------- - // Perform placement validity checks, returning false on failure (all implemented in arch_place.cc) + // Perform placement validity checks, returning false on failure (all + // implemented in arch_place.cc) // Whether or not a given cell can be placed at a given Bel // This is not intended for Bel type checks, but finer-grained constraints @@ -813,7 +814,8 @@ struct Arch : BaseCtx bool logicCellsCompatible(const std::vector &cells) const; // ------------------------------------------------- - // Assign architecure-specific arguments to nets and cells, which must be called between packing or further + // Assign architecure-specific arguments to nets and cells, which must be + // called between packing or further // netlist modifications, and validity checks void assignArchInfo(); void assignCellInfo(CellInfo *cell); -- cgit v1.2.3 From 676500b83fade3c2bc422a20f503ee1987cdb00f Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 17:42:44 -0700 Subject: Do less work if update flag is false --- common/timing.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index e524cd19..bb3f680d 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -215,10 +215,11 @@ struct Timing if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { const auto net_arrival = nd.max_arrival; auto path_budget = clk_period - (net_arrival + net_delay); - auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - if (update) + if (update) { + auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; usr.budget = std::min(usr.budget, net_delay + budget_share); - net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); + } if (path_budget < min_slack) { min_slack = path_budget; @@ -232,7 +233,7 @@ struct Timing int slack_ps = ctx->getDelayNS(path_budget) * 1000; (*slack_histogram)[slack_ps]++; } - } else { + } else if (update) { // Iterate over all output ports on the same cell as the sink for (const auto &port : usr.cell->ports) { if (port.second.type == PORT_OUT && port.second.net) { @@ -241,8 +242,7 @@ struct Timing if (is_path) { auto path_budget = net_data.at(port.second.net).min_remaining_budget; auto budget_share = budget_override ? 0 : path_budget / net_length_plus_one; - if (update) - usr.budget = std::min(usr.budget, net_delay + budget_share); + usr.budget = std::min(usr.budget, net_delay + budget_share); net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); } -- cgit v1.2.3 From a1d626469f46d112c2dfea680beb30ebdebc65fe Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Mon, 6 Aug 2018 19:53:42 -0700 Subject: Cleanup nesting --- common/timing.cc | 118 +++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index bb3f680d..841f8e92 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -139,23 +139,23 @@ struct Timing for (auto &usr : net->users) { auto clock_domain = ctx->getPortClock(usr.cell, usr.port); for (auto &port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - // Skip if this is a clocked output (but allow non-clocked ones) - if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) - continue; - DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - // Decrement the fanin count, and only add to topographical - // order if all its fanins have already been visited - auto it = port_fanin.find(&port.second); - NPNR_ASSERT(it != port_fanin.end()); - if (--it->second == 0) { - topographical_order.emplace_back(port.second.net); - queue.emplace_back(port.second.net); - port_fanin.erase(it); - } - } + if (port.second.type != PORT_OUT || !port.second.net) + continue; + // Skip if this is a clocked output (but allow non-clocked ones) + if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) + continue; + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (!is_path) + continue; + // Decrement the fanin count, and only add to topographical + // order if all its fanins have already been visited + auto it = port_fanin.find(&port.second); + NPNR_ASSERT(it != port_fanin.end()); + if (--it->second == 0) { + topographical_order.emplace_back(port.second.net); + queue.emplace_back(port.second.net); + port_fanin.erase(it); } } } @@ -180,21 +180,21 @@ struct Timing auto usr_arrival = net_arrival + 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) { - DelayInfo comb_delay; - // Look up delay through this path - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - auto &data = net_data[port.second.net]; - auto &arrival = data.max_arrival; - arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if - // budget overriden - // 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); - } - } + if (port.second.type != PORT_OUT || !port.second.net) + continue; + DelayInfo comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (!is_path) + continue; + auto &data = net_data[port.second.net]; + auto &arrival = data.max_arrival; + arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); + if (!budget_override) { // Do not increment path length if + // budget overriden + // 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); } } } @@ -236,17 +236,16 @@ struct Timing } else if (update) { // Iterate over all output ports on the same cell as the sink for (const auto &port : usr.cell->ports) { - if (port.second.type == PORT_OUT && port.second.net) { - DelayInfo comb_delay; - bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); - if (is_path) { - auto path_budget = net_data.at(port.second.net).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); - net_min_remaining_budget = - std::min(net_min_remaining_budget, path_budget - budget_share); - } - } + if (port.second.type != PORT_OUT || !port.second.net) + continue; + DelayInfo comb_delay; + bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); + if (!is_path) + continue; + auto path_budget = net_data.at(port.second.net).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); + net_min_remaining_budget = std::min(net_min_remaining_budget, path_budget - budget_share); } } } @@ -260,28 +259,29 @@ struct Timing // Look at all input ports on its driving cell for (const auto &port : crit_net->driver.cell->ports) { - if (port.second.type == PORT_IN && port.second.net) { - DelayInfo comb_delay; - bool is_path = - ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); - if (is_path) { - // If input port is influenced by a clock, skip - if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) - continue; - - // And find the fanin net with the latest arrival time - const auto net_arrival = net_data.at(port.second.net).max_arrival; - if (net_arrival > max_arrival) { - max_arrival = net_arrival; - crit_ipin = &port.second; - } - } + if (port.second.type != PORT_IN || !port.second.net) + continue; + DelayInfo comb_delay; + bool is_path = + ctx->getCellDelay(crit_net->driver.cell, port.first, crit_net->driver.port, comb_delay); + if (!is_path) + continue; + // If input port is influenced by a clock, skip + if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) + continue; + + // And find the fanin net with the latest arrival time + const auto net_arrival = net_data.at(port.second.net).max_arrival; + if (net_arrival > max_arrival) { + max_arrival = net_arrival; + crit_ipin = &port.second; } } if (!crit_ipin) break; + // Now convert PortInfo* into a PortRef* for (auto &usr : crit_ipin->net->users) { if (usr.cell->name == crit_net->driver.cell->name && usr.port == crit_ipin->name) { crit_path->push_back(&usr); -- cgit v1.2.3 From a0994d515454a696c98602980b298ee61aa03f4e Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 10:44:42 +0200 Subject: common: Add TimingPortClass Signed-off-by: David Shah --- common/nextpnr.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 3d9a66ca..d4925a16 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -291,6 +291,19 @@ struct CellInfo : ArchCellInfo // parent.[xyz] := 0 when (constr_parent == nullptr) }; +enum TimingPortClass +{ + TMG_CLOCK_INPUT, // Clock input to a sequential cell + TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc) + TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too) + TMG_REGISTER_OUTPUT, // Output from a register + TMG_COMB_INPUT, // Combinational input, no paths end here + TMG_COMB_OUTPUT, // Combinational output, no paths start here + TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output + TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input + TMG_ASYNC, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis +}; + struct DeterministicRNG { uint64_t rngstate; @@ -437,7 +450,7 @@ struct BaseCtx const Context *getCtx() const { return reinterpret_cast(this); } - template const char *nameOf(const T *obj) + template const char *nameOf(const T *obj) { if (obj == nullptr) return ""; -- cgit v1.2.3 From bf42e525cb7ab6ae071b16dfeca55194878be69c Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 14:37:59 +0200 Subject: Arch API: New specification for timing port classes Signed-off-by: David Shah --- common/nextpnr.h | 2 +- ecp5/arch.cc | 7 +++--- ecp5/arch.h | 6 ++--- generic/arch.cc | 8 ++++--- generic/arch.h | 4 ++-- ice40/arch.cc | 68 +++++++++++++++++++++++++++++++++++++++++--------------- ice40/arch.h | 6 ++--- 7 files changed, 66 insertions(+), 35 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index d4925a16..938f4f95 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -301,7 +301,7 @@ enum TimingPortClass TMG_COMB_OUTPUT, // Combinational output, no paths start here TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input - TMG_ASYNC, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis + TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis }; struct DeterministicRNG diff --git a/ecp5/arch.cc b/ecp5/arch.cc index de3abd44..12707a03 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -495,9 +495,10 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; } -IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return IdString(); } - -bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const +{ + return TMG_IGNORE; +} bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id("TRELLIS_IO"); } diff --git a/ecp5/arch.h b/ecp5/arch.h index fd8d0a13..7bbb9da5 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -827,10 +827,8 @@ struct Arch : BaseCtx // Get the delay through a cell from one port to another, returning false // if no path exists bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - // Get the associated clock to a port, or empty if the port is combinational - IdString getPortClock(const CellInfo *cell, IdString port) const; - // Return true if a port is a clock - bool isClockPort(const CellInfo *cell, IdString port) const; + // Get the port class, also setting clockPort if applicable + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; // Return true if a cell is an IO diff --git a/generic/arch.cc b/generic/arch.cc index 0fa93da8..25e4d08c 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -435,9 +435,11 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; } -IdString Arch::getPortClock(const CellInfo *cell, IdString port) const { return IdString(); } - -bool Arch::isClockPort(const CellInfo *cell, IdString port) const { return false; } +// Get the port class, also setting clockPort if applicable +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const +{ + return TMG_IGNORE; +} bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } bool Arch::isBelLocationValid(BelId bel) const { return true; } diff --git a/generic/arch.h b/generic/arch.h index e7010885..fb4f3660 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -213,8 +213,8 @@ struct Arch : BaseCtx DecalXY getGroupDecal(GroupId group) const; bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - IdString getPortClock(const CellInfo *cell, IdString port) const; - bool isClockPort(const CellInfo *cell, IdString port) const; + // Get the port class, also setting clockPort if applicable + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; // Return true if a cell is an IO bool isIOCell(const CellInfo *cell) const; diff --git a/ice40/arch.cc b/ice40/arch.cc index 16104033..0f81bfea 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -27,6 +27,7 @@ #include "placer1.h" #include "router1.h" #include "util.h" + NEXTPNR_NAMESPACE_BEGIN // ----------------------------------------------------------------------- @@ -106,7 +107,9 @@ BelType Arch::belTypeFromId(IdString type) const void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, PIN_##t); + #include "portpins.inc" + #undef X } @@ -888,27 +891,56 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return false; } -IdString Arch::getPortClock(const CellInfo *cell, IdString port) const +// Get the port class, also setting clockPort to associated clock if applicable +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const { - if (cell->type == id_icestorm_lc && cell->lcInfo.dffEnable) { - if (port != id_lo && port != id_cin && port != id_cout) - return id_clk; - } else if (cell->type == id_icestorm_ram) { - if (port.str(this)[0] == 'R') - return id_rclk; + if (cell->type == id_icestorm_lc) { + if (port == id_clk) + return TMG_CLOCK_INPUT; + if (port == id_cin) + return TMG_COMB_INPUT; + if (port == id_cout || port == id_lo) + return TMG_COMB_OUTPUT; + if (cell->lcInfo.dffEnable) { + clockPort = id_clk; + if (port == id_o) + return TMG_REGISTER_OUTPUT; + else + return TMG_REGISTER_INPUT; + } else { + if (port == id_o) + return TMG_COMB_OUTPUT; + else + return TMG_COMB_INPUT; + } + } else if (cell->type == id_icestorm_ram || cell->type == id("ICESTORM_DSP") || + cell->type == id("ICESTORM_SPRAM")) { + if (port == id_clk) + return TMG_CLOCK_INPUT; + else if (cell->ports.at(port).type == PORT_OUT) + return TMG_REGISTER_OUTPUT; else - return id_wclk; + return TMG_REGISTER_INPUT; + } else if (cell->type == id_sb_io) { + if (port == id("D_IN_0") || port == id("D_IN_1")) + return TMG_STARTPOINT; + if (port == id("D_OUT_0") || port == id("D_OUT_1")) + return TMG_STARTPOINT; + return TMG_IGNORE; + } else if (cell->type == id("ICESTORM_PLL")) { + if (port == id("PLLOUT_A") || port == id("PLLOUT_B")) + return TMG_GEN_CLOCK; + return TMG_IGNORE; + } else if (cell->type == id("ICESTORM_LFOSC")) { + if (port == id("CLKLF")) + return TMG_GEN_CLOCK; + return TMG_IGNORE; + } else if (cell->type == id("ICESTORM_HFOSC")) { + if (port == id("CLKHF")) + return TMG_GEN_CLOCK; + return TMG_IGNORE; } - return IdString(); -} - -bool Arch::isClockPort(const CellInfo *cell, IdString port) const -{ - if (cell->type == id("ICESTORM_LC") && port == id("CLK")) - return true; - if (cell->type == id("ICESTORM_RAM") && (port == id("RCLK") || (port == id("WCLK")))) - return true; - return false; + return TMG_IGNORE; } bool Arch::isGlobalNet(const NetInfo *net) const diff --git a/ice40/arch.h b/ice40/arch.h index a5be7e33..7cc8495d 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -788,10 +788,8 @@ struct Arch : BaseCtx // Get the delay through a cell from one port to another, returning false // if no path exists bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; - // Get the associated clock to a port, or empty if the port is combinational - IdString getPortClock(const CellInfo *cell, IdString port) const; - // Return true if a port is a clock - bool isClockPort(const CellInfo *cell, IdString port) const; + // Get the port class, also setting clockDomain if applicable + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; // Return true if a cell is an IO -- cgit v1.2.3 From d8b383003194f45cae8486905e50575d792a4640 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 14:58:43 +0200 Subject: timing: Update to new use API (currently broken) Signed-off-by: David Shah --- common/timing.cc | 32 +++++++++++++++++++++----------- gui/ecp5/mainwindow.cc | 12 +++++++----- gui/ecp5/mainwindow.h | 1 + gui/generic/mainwindow.cc | 3 ++- ice40/arch.cc | 4 ++-- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 841f8e92..15a4d126 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -84,20 +84,20 @@ struct Timing input_ports.push_back(port.first); } - bool is_io = ctx->isIOCell(cell.second.get()); for (auto o : output_ports) { - IdString clock_domain = ctx->getPortClock(cell.second.get(), o->name); + IdString clockPort; + TimingPortClass portClass = ctx->getPortTimingClass(cell.second.get(), o->name, clockPort); // If output port is influenced by a clock (e.g. FF output) // then add it to the ordering as a timing start-point - if (clock_domain != IdString()) { + if (portClass == TMG_REGISTER_OUTPUT) { DelayInfo clkToQ; - ctx->getCellDelay(cell.second.get(), clock_domain, o->name, clkToQ); + ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ); topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { // Also add I/O cells too // TODO(eddieh): More generic way of detecting PLLs - if (is_io || cell.second->type == ctx->id("ICESTORM_PLL")) { + if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { // IGNORE: ???? topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } @@ -137,12 +137,13 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { - auto clock_domain = ctx->getPortClock(usr.cell, usr.port); + IdString clockPort; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); for (auto &port : usr.cell->ports) { if (port.second.type != PORT_OUT || !port.second.net) continue; // Skip if this is a clocked output (but allow non-clocked ones) - if (clock_domain != IdString() && ctx->getCellDelay(usr.cell, clock_domain, port.first, clkToQ)) + if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) continue; DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); @@ -173,7 +174,9 @@ struct Timing const auto net_length_plus_one = nd.max_path_length + 1; nd.min_remaining_budget = clk_period; for (auto &usr : net->users) { - if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + IdString clockPort; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); + if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) { } else { auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t(); auto budget_override = ctx->getBudgetOverride(net, usr, net_delay); @@ -212,7 +215,9 @@ struct Timing 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); - if (ctx->getPortClock(usr.cell, usr.port) != IdString()) { + IdString associatedClock; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, associatedClock); + if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT) { const auto net_arrival = nd.max_arrival; auto path_budget = clk_period - (net_arrival + net_delay); if (update) { @@ -267,7 +272,10 @@ struct Timing if (!is_path) continue; // If input port is influenced by a clock, skip - if (ctx->getPortClock(crit_net->driver.cell, port.first) != IdString()) + IdString portClock; + TimingPortClass portClass = ctx->getPortTimingClass(crit_net->driver.cell, port.first, portClock); + if (portClass == TMG_REGISTER_INPUT || portClass == TMG_CLOCK_INPUT || portClass == TMG_ENDPOINT || + portClass == TMG_IGNORE) continue; // And find the fanin net with the latest arrival time @@ -373,7 +381,9 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) auto &front = crit_path.front(); auto &front_port = front->cell->ports.at(front->port); auto &front_driver = front_port.net->driver; - auto last_port = ctx->getPortClock(front_driver.cell, front_driver.port); + + IdString last_port; + ctx->getPortTimingClass(front_driver.cell, front_driver.port, last_port); for (auto sink : crit_path) { auto sink_cell = sink->cell; auto &port = sink_cell->ports.at(sink->port); diff --git a/gui/ecp5/mainwindow.cc b/gui/ecp5/mainwindow.cc index efaad364..e5a5f1ba 100644 --- a/gui/ecp5/mainwindow.cc +++ b/gui/ecp5/mainwindow.cc @@ -29,7 +29,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) { initMainResource(); @@ -50,7 +51,8 @@ void MainWindow::newContext(Context *ctx) setWindowTitle(title.c_str()); } -void MainWindow::createMenu() { +void MainWindow::createMenu() +{ // Add arch specific actions actionLoadBase = new QAction("Open Base Config", this); actionLoadBase->setIcon(QIcon(":/icons/resources/open_base.png")); @@ -71,7 +73,7 @@ void MainWindow::createMenu() { menuDesign->addSeparator(); menuDesign->addAction(actionLoadBase); - menuDesign->addAction(actionSaveConfig); + menuDesign->addAction(actionSaveConfig); } static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return ptr->get(); } @@ -96,8 +98,8 @@ static QStringList getSupportedPackages(ArchArgs::ArchArgsTypes chip) return packages; } - -void MainWindow::new_proj() { +void MainWindow::new_proj() +{ QMap arch; arch.insert("Lattice ECP5 25K", ArchArgs::LFE5U_25F); arch.insert("Lattice ECP5 45K", ArchArgs::LFE5U_45F); diff --git a/gui/ecp5/mainwindow.h b/gui/ecp5/mainwindow.h index d1d5a5a2..9460913c 100644 --- a/gui/ecp5/mainwindow.h +++ b/gui/ecp5/mainwindow.h @@ -47,6 +47,7 @@ class MainWindow : public BaseMainWindow void newContext(Context *ctx); void open_base(); void save_config(); + private: QAction *actionLoadBase; QAction *actionSaveConfig; diff --git a/gui/generic/mainwindow.cc b/gui/generic/mainwindow.cc index 050c0fb8..01eeb4ef 100644 --- a/gui/generic/mainwindow.cc +++ b/gui/generic/mainwindow.cc @@ -23,7 +23,8 @@ static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } NEXTPNR_NAMESPACE_BEGIN -MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) : BaseMainWindow(std::move(context), args, parent) +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) { initMainResource(); diff --git a/ice40/arch.cc b/ice40/arch.cc index 0f81bfea..fddfabfd 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -915,7 +915,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id } } else if (cell->type == id_icestorm_ram || cell->type == id("ICESTORM_DSP") || cell->type == id("ICESTORM_SPRAM")) { - if (port == id_clk) + if (port == id_clk || port == id_rclk || port == id_wclk) return TMG_CLOCK_INPUT; else if (cell->ports.at(port).type == PORT_OUT) return TMG_REGISTER_OUTPUT; @@ -925,7 +925,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id("D_IN_0") || port == id("D_IN_1")) return TMG_STARTPOINT; if (port == id("D_OUT_0") || port == id("D_OUT_1")) - return TMG_STARTPOINT; + return TMG_ENDPOINT; return TMG_IGNORE; } else if (cell->type == id("ICESTORM_PLL")) { if (port == id("PLLOUT_A") || port == id("PLLOUT_B")) -- cgit v1.2.3 From 787fe5661cedf42a55f6aeb285d164e6cdf56404 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 15:00:39 +0200 Subject: ice40: Timing arch fix Signed-off-by: David Shah --- ice40/arch.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index fddfabfd..bfab91a1 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -913,9 +913,23 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id else return TMG_COMB_INPUT; } - } else if (cell->type == id_icestorm_ram || cell->type == id("ICESTORM_DSP") || - cell->type == id("ICESTORM_SPRAM")) { - if (port == id_clk || port == id_rclk || port == id_wclk) + } else if (cell->type == id_icestorm_ram) { + + if (port == id_rclk || port == id_wclk) + return TMG_CLOCK_INPUT; + + if (port.str(this)[0] == 'R') + clockPort = id_rclk; + else + clockPort = id_wclk; + + if (cell->ports.at(port).type == PORT_OUT) + return TMG_REGISTER_OUTPUT; + else + return TMG_REGISTER_INPUT; + } else if (cell->type == id("ICESTORM_DSP") || cell->type == id("ICESTORM_SPRAM")) { + clockPort = id_clk; + if (port == id_clk) return TMG_CLOCK_INPUT; else if (cell->ports.at(port).type == PORT_OUT) return TMG_REGISTER_OUTPUT; -- cgit v1.2.3 From d173ddba367f7082f122df75dffb59577afaf0b4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 15:07:41 +0200 Subject: timing: Debugging implementation of new timing API Signed-off-by: David Shah --- common/timing.cc | 13 +++++++++---- ice40/arch.cc | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 15a4d126..0028fd21 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -96,8 +96,8 @@ struct Timing net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { // Also add I/O cells too - // TODO(eddieh): More generic way of detecting PLLs - if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { // IGNORE: ???? + // TODO: how to process ignore here + if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } @@ -138,12 +138,17 @@ struct Timing DelayInfo clkToQ; for (auto &usr : net->users) { IdString clockPort; - TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); + TimingPortClass usrClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); + if (usrClass == TMG_IGNORE || usrClass == TMG_CLOCK_INPUT) + continue; for (auto &port : usr.cell->ports) { if (port.second.type != PORT_OUT || !port.second.net) continue; + TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, port.first, clockPort); + // Skip if this is a clocked output (but allow non-clocked ones) - if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) + if (portClass == TMG_REGISTER_OUTPUT || portClass == TMG_STARTPOINT || portClass == TMG_IGNORE || + portClass == TMG_GEN_CLOCK) continue; DelayInfo comb_delay; bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); diff --git a/ice40/arch.cc b/ice40/arch.cc index bfab91a1..abce0aba 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -938,7 +938,7 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id } else if (cell->type == id_sb_io) { if (port == id("D_IN_0") || port == id("D_IN_1")) return TMG_STARTPOINT; - if (port == id("D_OUT_0") || port == id("D_OUT_1")) + if (port == id("D_OUT_0") || port == id("D_OUT_1") || port == id("OUTPUT_ENABLE")) return TMG_ENDPOINT; return TMG_IGNORE; } else if (cell->type == id("ICESTORM_PLL")) { -- cgit v1.2.3 From e6eb2038683fd3c81570b8c8e5307678bca5f77e Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 16:34:41 +0200 Subject: ice40: Add timing arcs through global buffers Signed-off-by: David Shah --- ice40/arch.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ice40/arch.cc b/ice40/arch.cc index abce0aba..3d31f980 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -953,6 +953,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id("CLKHF")) return TMG_GEN_CLOCK; return TMG_IGNORE; + } else if (cell->type == id_sb_gb) { + if (port == id_glb_buf_out) + return TMG_COMB_OUTPUT; + return TMG_COMB_INPUT; } return TMG_IGNORE; } -- cgit v1.2.3 From fca01f5447d8ca0ba2e0075cb214f3a19933e6c1 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 07:49:07 -0700 Subject: Also include TMG_GEN_CLOCK as a timing startpoint --- common/timing.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 0028fd21..fae9ca53 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -95,14 +95,13 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { - // Also add I/O cells too // TODO: how to process ignore here - if (portClass == TMG_STARTPOINT || portClass == TMG_IGNORE) { + if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } // Otherwise, for all driven input ports on this cell, - // if a timing arch exists between the input and + // if a timing arc exists between the input and // the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; -- cgit v1.2.3 From d21e5a4b10c2eb57ae13e4622aa436339a96bcb9 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 07:58:01 -0700 Subject: Disable assign_budget() after placement legalisation, unless slack redist --- common/placer1.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/placer1.cc b/common/placer1.cc index 1d00e77a..51ef271a 100644 --- a/common/placer1.cc +++ b/common/placer1.cc @@ -236,7 +236,10 @@ class SAPlacer temp = post_legalise_temp; diameter *= post_legalise_dia_scale; ctx->shuffle(autoplaced); - assign_budget(ctx); + + // Legalisation is a big change so force a slack redistribution here + if (ctx->slack_redist_iter > 0) + assign_budget(ctx, true /* quiet */); } else if (ctx->slack_redist_iter > 0 && iter % ctx->slack_redist_iter == 0) { assign_budget(ctx, true /* quiet */); } -- cgit v1.2.3 From 936b52eafc6da47fdd9c5e56d2e5ae667f000944 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 08:01:24 -0700 Subject: Unfurl comments for clangformat --- common/timing.cc | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index fae9ca53..47091c27 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -60,14 +60,11 @@ struct Timing { const auto clk_period = delay_t(1.0e12 / ctx->target_freq); - // First, compute the topographical order of nets to walk through - // the circuit, assuming it is a _acyclic_ graph - // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial - // loops + // First, compute the topographical order of nets to walk through the circuit, assuming it is a _acyclic_ graph + // TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops std::vector topographical_order; std::unordered_map net_data; - // In lieu of deleting edges from the graph, simply count - // the number of fanins to each output port + // In lieu of deleting edges from the graph, simply count the number of fanins to each output port std::unordered_map port_fanin; std::vector input_ports; @@ -87,8 +84,7 @@ struct Timing for (auto o : output_ports) { IdString clockPort; TimingPortClass portClass = ctx->getPortTimingClass(cell.second.get(), o->name, clockPort); - // If output port is influenced by a clock (e.g. FF output) - // then add it to the ordering as a timing start-point + // If output port is influenced by a clock (e.g. FF output) then add it to the ordering as a timing start-point if (portClass == TMG_REGISTER_OUTPUT) { DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ); @@ -100,9 +96,7 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } - // Otherwise, for all driven input ports on this cell, - // if a timing arc exists between the input and - // the current output port, increment fanin counter + // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); @@ -127,9 +121,7 @@ struct Timing std::deque queue(topographical_order.begin(), topographical_order.end()); - // Now walk the design, from the start points identified previously, - // building - // up a topographical order + // Now walk the design, from the start points identified previously, building up a topographical order while (!queue.empty()) { const auto net = queue.front(); queue.pop_front(); @@ -153,8 +145,7 @@ struct Timing bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (!is_path) continue; - // Decrement the fanin count, and only add to topographical - // order if all its fanins have already been visited + // Decrement the fanin count, and only add to topographical order if all its fanins have already been visited auto it = port_fanin.find(&port.second); NPNR_ASSERT(it != port_fanin.end()); if (--it->second == 0) { @@ -166,12 +157,10 @@ struct Timing } } - // Sanity check to ensure that all ports where fanins were recorded - // were indeed visited + // Sanity check to ensure that all ports where fanins were recorded were indeed visited NPNR_ASSERT(port_fanin.empty()); - // Go forwards topographically to find the maximum arrival time - // and max path length for each net + // Go forwards topographically to find the maximum arrival time and max path length for each net for (auto net : topographical_order) { auto &nd = net_data.at(net); const auto net_arrival = nd.max_arrival; @@ -197,9 +186,7 @@ struct Timing auto &data = net_data[port.second.net]; auto &arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if - // budget overriden - // since it doesn't require a share of the slack + if (!budget_override) { // Do not increment path length if budget overriden 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); } @@ -210,8 +197,7 @@ struct Timing const NetInfo *crit_net = nullptr; - // Now go backwards topographically to determine the minimum path slack, - // and to distribute all path slack evenly between all nets on the path + // Now go backwards topographically to determine the minimum path slack, and to distribute all path slack evenly between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; @@ -348,9 +334,7 @@ void assign_budget(Context *ctx, bool quiet) } } - // For slack redistribution, if user has not specified a frequency - // dynamically adjust the target frequency to be the currently - // achieved maximum + // For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the currently achieved maximum if (ctx->auto_freq && ctx->slack_redist_iter > 0) { delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); ctx->target_freq = 1e12 / (default_slack - timing.min_slack); -- cgit v1.2.3 From 433ad6462e401e722fbdd033c2b4b1c9a3537947 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:06:59 +0200 Subject: Arch API: Removing Arch::isIOCell Signed-off-by: David Shah --- ecp5/arch.cc | 2 -- ecp5/arch.h | 2 -- generic/arch.h | 2 -- ice40/arch.cc | 2 -- ice40/arch.h | 2 -- 5 files changed, 10 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 12707a03..9dac70d9 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -500,8 +500,6 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id return TMG_IGNORE; } -bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id("TRELLIS_IO"); } - std::vector> Arch::getTilesAtLocation(int row, int col) { std::vector> ret; diff --git a/ecp5/arch.h b/ecp5/arch.h index 7bbb9da5..17f8c58f 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -831,8 +831,6 @@ struct Arch : BaseCtx TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; - // Return true if a cell is an IO - bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- // Placement validity checks diff --git a/generic/arch.h b/generic/arch.h index fb4f3660..46801372 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -215,8 +215,6 @@ struct Arch : BaseCtx bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; // Get the port class, also setting clockPort if applicable TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const; - // Return true if a cell is an IO - bool isIOCell(const CellInfo *cell) const; bool isValidBelForCell(CellInfo *cell, BelId bel) const; bool isBelLocationValid(BelId bel) const; diff --git a/ice40/arch.cc b/ice40/arch.cc index 3d31f980..888a0dee 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -968,8 +968,6 @@ bool Arch::isGlobalNet(const NetInfo *net) const return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out; } -bool Arch::isIOCell(const CellInfo *cell) const { return cell->type == id_sb_io; } - // Assign arch arg info void Arch::assignArchInfo() { diff --git a/ice40/arch.h b/ice40/arch.h index 7cc8495d..cdac1e96 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -792,8 +792,6 @@ struct Arch : BaseCtx TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const; // Return true if a port is a net bool isGlobalNet(const NetInfo *net) const; - // Return true if a cell is an IO - bool isIOCell(const CellInfo *cell) const; // ------------------------------------------------- -- cgit v1.2.3 From 90e3db324eb016b6c5ae4168db6ac00f65c2af4e Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:07:20 +0200 Subject: clangformat Signed-off-by: David Shah --- common/timing.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 47091c27..a3b5235a 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -84,7 +84,8 @@ struct Timing for (auto o : output_ports) { IdString clockPort; TimingPortClass portClass = ctx->getPortTimingClass(cell.second.get(), o->name, clockPort); - // If output port is influenced by a clock (e.g. FF output) then add it to the ordering as a timing start-point + // If output port is influenced by a clock (e.g. FF output) then add it to the ordering as a timing + // start-point if (portClass == TMG_REGISTER_OUTPUT) { DelayInfo clkToQ; ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ); @@ -96,7 +97,8 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); } - // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and the current output port, increment fanin counter + // Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and + // the current output port, increment fanin counter for (auto i : input_ports) { DelayInfo comb_delay; bool is_path = ctx->getCellDelay(cell.second.get(), i, o->name, comb_delay); @@ -145,7 +147,8 @@ struct Timing bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay); if (!is_path) continue; - // Decrement the fanin count, and only add to topographical order if all its fanins have already been visited + // Decrement the fanin count, and only add to topographical order if all its fanins have already + // been visited auto it = port_fanin.find(&port.second); NPNR_ASSERT(it != port_fanin.end()); if (--it->second == 0) { @@ -186,7 +189,8 @@ struct Timing auto &data = net_data[port.second.net]; auto &arrival = data.max_arrival; arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay()); - if (!budget_override) { // Do not increment path length if budget overriden since it doesn't require a share of the slack + if (!budget_override) { // Do not increment path length if budget overriden 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); } @@ -197,7 +201,8 @@ struct Timing const NetInfo *crit_net = nullptr; - // Now go backwards topographically to determine the minimum path slack, and to distribute all path slack evenly between all nets on the path + // Now go backwards topographically to determine the minimum path slack, and to distribute all path slack evenly + // between all nets on the path for (auto net : boost::adaptors::reverse(topographical_order)) { auto &nd = net_data.at(net); const delay_t net_length_plus_one = nd.max_path_length + 1; @@ -334,7 +339,8 @@ void assign_budget(Context *ctx, bool quiet) } } - // For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the currently achieved maximum + // For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the + // currently achieved maximum if (ctx->auto_freq && ctx->slack_redist_iter > 0) { delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); ctx->target_freq = 1e12 / (default_slack - timing.min_slack); -- cgit v1.2.3 From 91023d2a0e6c4d15b640f9c913565b8298f0a19c Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 08:31:08 -0700 Subject: Leave comment behind about removing false paths --- common/timing.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index a3b5235a..c1be083d 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -92,7 +92,7 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { - // TODO: how to process ignore here + // TODO(eddieh): Generated clocks and ignored ports are currently added into the ordering as if it was a regular timing start point in order to enable the full topographical order to be computed, however these false nets (and their downstream paths) should not be in the final ordering if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); -- cgit v1.2.3 From 03575a2a7a07dd4798bf7acde6bd49258c2ec236 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Wed, 8 Aug 2018 08:32:17 -0700 Subject: One more breadcrumb --- common/timing.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/common/timing.cc b/common/timing.cc index c1be083d..3c430739 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -110,6 +110,7 @@ struct Timing } // If these constant nets exist, add them to the topographical ordering too + // TODO(eddieh): Also false paths and should be removed from ordering auto it = ctx->nets.find(ctx->id("$PACKER_VCC_NET")); if (it != ctx->nets.end()) { topographical_order.emplace_back(it->second.get()); -- cgit v1.2.3 From 8e593fb47147ca7b4cd73d3cafc789d520871cb4 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:54:25 +0200 Subject: timing: Update to use getDelayNS Signed-off-by: David Shah --- common/timing.cc | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/common/timing.cc b/common/timing.cc index 3c430739..601311a8 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -92,7 +92,9 @@ struct Timing topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{clkToQ.maxDelay()}); } else { - // TODO(eddieh): Generated clocks and ignored ports are currently added into the ordering as if it was a regular timing start point in order to enable the full topographical order to be computed, however these false nets (and their downstream paths) should not be in the final ordering + // TODO(eddieh): Generated clocks and ignored ports are currently added into the ordering as if it + // was a regular timing start point in order to enable the full topographical order to be computed, + // however these false nets (and their downstream paths) should not be in the final ordering if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) { topographical_order.emplace_back(o->net); net_data.emplace(o->net, TimingData{}); @@ -343,12 +345,12 @@ void assign_budget(Context *ctx, bool quiet) // For slack redistribution, if user has not specified a frequency dynamically adjust the target frequency to be the // currently achieved maximum if (ctx->auto_freq && ctx->slack_redist_iter > 0) { - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); - ctx->target_freq = 1e12 / (default_slack - timing.min_slack); + delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq); + ctx->target_freq = 1.0e9 / ctx->getDelayNS(default_slack - timing.min_slack); if (ctx->verbose) - log_info("minimum slack for this assign = %d, target Fmax for next " + log_info("minimum slack for this assign = %.2f ns, target Fmax for next " "update = %.2f MHz\n", - timing.min_slack, ctx->target_freq / 1e6); + ctx->getDelayNS(timing.min_slack), ctx->target_freq / 1e6); } if (!quiet) @@ -388,14 +390,15 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) DelayInfo comb_delay; ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay); total += comb_delay.maxDelay(); - log_info("%4d %4d Source %s.%s\n", comb_delay.maxDelay(), total, driver_cell->name.c_str(ctx), - driver.port.c_str(ctx)); + log_info("%4.1f %4.1f Source %s.%s\n", ctx->getDelayNS(comb_delay.maxDelay()), ctx->getDelayNS(total), + driver_cell->name.c_str(ctx), driver.port.c_str(ctx)); auto net_delay = ctx->getNetinfoRouteDelay(net, *sink); total += net_delay; auto driver_loc = ctx->getBelLocation(driver_cell->bel); auto sink_loc = ctx->getBelLocation(sink_cell->bel); - log_info("%4d %4d Net %s budget %d (%d,%d) -> (%d,%d)\n", net_delay, total, net->name.c_str(ctx), - sink->budget, driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y); + log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay), + ctx->getDelayNS(total), net->name.c_str(ctx), ctx->getDelayNS(sink->budget), driver_loc.x, + driver_loc.y, sink_loc.x, sink_loc.y); log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx)); last_port = sink->port; } @@ -403,8 +406,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) } } - delay_t default_slack = delay_t(1.0e12 / ctx->target_freq); - log_info("estimated Fmax = %.2f MHz\n", 1e6 / (default_slack - min_slack)); + delay_t default_slack = delay_t((1.0e9 / ctx->getDelayNS(1)) / ctx->target_freq); + log_info("estimated Fmax = %.2f MHz\n", 1e3 / ctx->getDelayNS(default_slack - min_slack)); if (print_histogram && slack_histogram.size() > 0) { constexpr unsigned num_bins = 20; -- cgit v1.2.3 From 3e11ba8afb332909ee40760a222b2d2b56bc3232 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:55:54 +0200 Subject: timing: Remove unused variable Signed-off-by: David Shah --- common/timing.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index 601311a8..aadd8381 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -131,7 +131,6 @@ struct Timing const auto net = queue.front(); queue.pop_front(); - DelayInfo clkToQ; for (auto &usr : net->users) { IdString clockPort; TimingPortClass usrClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort); -- cgit v1.2.3 From 674cabb6bea05032c8bd7e638684ac7f6e448a6b Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 17:58:25 +0200 Subject: docs: Update Arch API Cell Timing docs Signed-off-by: David Shah --- docs/archapi.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/archapi.md b/docs/archapi.md index 222b9f78..3a2b5efb 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -455,13 +455,11 @@ Cell Delay Methods Returns the delay for the specified path through a cell in the `&delay` argument. The method returns false if there is no timing relationship from `fromPort` to `toPort`. -### IdString getPortClock(const CellInfo \*cell, IdString port) const +### TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const -Returns the clock input port for the specified output port. - -### bool isClockPort(const CellInfo \*cell, IdString port) const - -Returns true if the specified port is a clock input. +Return the _timing port class_ of a port. This can be a register or combinational input or output; clock input or +output; general startpoint or endpoint; or a port ignored for timing purposes. For register ports, clockPort is set +to the associated clock port. Placer Methods -------------- -- cgit v1.2.3 From 751335977fe7d69b23f6110ec4938408ec7a7ff8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 8 Aug 2018 18:07:34 +0200 Subject: ice40: Add error for unknown cell type when getting timing info Signed-off-by: David Shah --- ice40/arch.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ice40/arch.cc b/ice40/arch.cc index 888a0dee..5e56ea10 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -957,8 +957,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id_glb_buf_out) return TMG_COMB_OUTPUT; return TMG_COMB_INPUT; + } else if (cell->type == id("SB_WARMBOOT")) { + return TMG_ENDPOINT; } - return TMG_IGNORE; + log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this)); } bool Arch::isGlobalNet(const NetInfo *net) const -- cgit v1.2.3