From 85bb108ba40f9573571de0a785f9fbc91c4e1dd0 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Feb 2021 11:54:54 +0000 Subject: [PATCH 1/4] Add getBelPinsForCellPin to Arch API This is a basic implementation, without considering "M of N" arrangements (e.g. for LUT permuation where you only want to route to 1 out of 4/6 sinks) or using a type other than IdString to identify bel pins. But this is also enough to start working out where in nextpnr will break due to removing the 1:1 cell:bel pin cardinality, as a next step. Signed-off-by: gatecat --- common/nextpnr.h | 8 ++++++++ docs/archapi.md | 7 +++++++ fpga_interchange/arch.h | 3 +++ generic/arch.cc | 2 ++ generic/arch.h | 2 ++ gowin/arch.cc | 2 ++ gowin/arch.h | 2 ++ 7 files changed, 26 insertions(+) diff --git a/common/nextpnr.h b/common/nextpnr.h index c43b9dc4..cf04f831 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -1093,6 +1093,7 @@ template struct ArchAPI : BaseCtx virtual WireId getBelPinWire(BelId bel, IdString pin) const = 0; virtual PortType getBelPinType(BelId bel, IdString pin) const = 0; virtual typename R::BelPinsRangeT getBelPins(BelId bel) const = 0; + virtual typename R::CellBelPinRangeT getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const = 0; // Wire methods virtual typename R::AllWiresRangeT getWires() const = 0; virtual WireId getWireByName(IdStringList name) const = 0; @@ -1176,6 +1177,8 @@ template struct ArchAPI : BaseCtx // This contains the relevant range types for the default implementations of Arch functions struct BaseArchRanges { + // Bels + using CellBelPinRangeT = std::array; // Attributes using BelAttrsRangeT = std::vector>; using WireAttrsRangeT = std::vector>; @@ -1241,6 +1244,11 @@ template struct BaseArch : ArchAPI return empty_if_possible(); } + virtual typename R::CellBelPinRangeT getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const override + { + return return_if_match, typename R::CellBelPinRangeT>({pin}); + } + // Wire methods virtual IdString getWireType(WireId wire) const override { return IdString(); } virtual typename R::WireAttrsRangeT getWireAttrs(WireId) const override diff --git a/docs/archapi.md b/docs/archapi.md index f6f184e0..2a38502c 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -13,6 +13,7 @@ The contents of `ArchRanges` is as follows: |`TileBelsRangeT` | `BelId` | |`BelAttrsRangeT` | std::pair | |`BelPinsRangeT` | `IdString` | +|`CellBelPinRangeT` | `IdString` | |`AllWiresRangeT` | `WireId` | |`DownhillPipRangeT` | `PipId` | |`UphillPipRangeT` | `PipId` | @@ -244,6 +245,12 @@ Return the type (input/output/inout) of the given bel pin. Return a list of all pins on that bel. +### CellBelPinRangeT getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const + +Return the list of bel pin names that a given cell pin should be routed to. In most cases there will be a single bel pin for each cell pin; and output pins must _always_ have only one bel pin associated with them. + +*BaseArch default: returns a one-element array containing `pin`* + Wire Methods ------------ diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index fd2d16a2..e5c7551b 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -660,6 +660,7 @@ struct ArchRanges using TileBelsRangeT = BelRange; using BelAttrsRangeT = std::vector>; using BelPinsRangeT = IdStringRange; + using CellBelPinRangeT = std::array; // Wires using AllWiresRangeT = WireRange; using DownhillPipRangeT = DownhillPipRange; @@ -866,6 +867,8 @@ struct Arch : ArchAPI return str_range; } + std::array getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const override { return {pin}; } + // ------------------------------------------------- WireId getWireByName(IdStringList name) const override; diff --git a/generic/arch.cc b/generic/arch.cc index 912f8a53..c51fbb84 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -339,6 +339,8 @@ std::vector Arch::getBelPins(BelId bel) const return ret; } +std::array Arch::getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const { return {pin}; } + // --------------------------------------------------------------- WireId Arch::getWireByName(IdStringList name) const diff --git a/generic/arch.h b/generic/arch.h index 09fd8e34..e7d204ef 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -124,6 +124,7 @@ struct ArchRanges using TileBelsRangeT = const std::vector &; using BelAttrsRangeT = const std::map &; using BelPinsRangeT = std::vector; + using CellBelPinRangeT = std::array; // Wires using AllWiresRangeT = const std::vector &; using DownhillPipRangeT = const std::vector &; @@ -241,6 +242,7 @@ struct Arch : ArchAPI WireId getBelPinWire(BelId bel, IdString pin) const override; PortType getBelPinType(BelId bel, IdString pin) const override; std::vector getBelPins(BelId bel) const override; + std::array getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const override; WireId getWireByName(IdStringList name) const override; IdStringList getWireName(WireId wire) const override; diff --git a/gowin/arch.cc b/gowin/arch.cc index d4e17a8a..8aeccfba 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -822,6 +822,8 @@ std::vector Arch::getBelPins(BelId bel) const return ret; } +std::array Arch::getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const { return {pin}; } + // --------------------------------------------------------------- WireId Arch::getWireByName(IdStringList name) const diff --git a/gowin/arch.h b/gowin/arch.h index c5ef0a2e..eab75899 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -251,6 +251,7 @@ struct ArchRanges using TileBelsRangeT = const std::vector &; using BelAttrsRangeT = const std::map &; using BelPinsRangeT = std::vector; + using CellBelPinRangeT = std::array; // Wires using AllWiresRangeT = const std::vector &; using DownhillPipRangeT = const std::vector &; @@ -375,6 +376,7 @@ struct Arch : BaseArch WireId getBelPinWire(BelId bel, IdString pin) const override; PortType getBelPinType(BelId bel, IdString pin) const override; std::vector getBelPins(BelId bel) const override; + std::array getBelPinsForCellPin(CellInfo *cell_info, IdString pin) const override; WireId getWireByName(IdStringList name) const override; IdStringList getWireName(WireId wire) const override; From 535723f414a77eb4aa43b794627bbd4aca56f6f0 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Feb 2021 12:28:40 +0000 Subject: [PATCH 2/4] Start making use of getBelPinsForCellPin API This replaces getNetinfoSinkWire with 3 new functions for different use cases. At the moment all existing code has been moved to getNetinfoSinkWire with phys_idx=0 so the build doesn't break; but this won't yet function properly with more than one sink. But it provides a base on which to work on refactoring the routers to support this case. Signed-off-by: gatecat --- common/nextpnr.cc | 94 ++++++++++++++++++++++++++++++++++------------- common/nextpnr.h | 4 +- common/router1.cc | 10 ++--- common/router2.cc | 12 +++--- common/timing.cc | 2 +- nexus/global.cc | 2 +- 6 files changed, 85 insertions(+), 39 deletions(-) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 880e0344..11acf991 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -354,19 +354,59 @@ WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const if (src_bel == BelId()) return WireId(); - IdString driver_port = net_info->driver.port; - return getBelPinWire(src_bel, driver_port); + auto bel_pins = getBelPinsForCellPin(net_info->driver.cell, net_info->driver.port); + auto iter = bel_pins.begin(); + if (iter == bel_pins.end()) + return WireId(); + WireId driver = getBelPinWire(src_bel, *iter); + ++iter; + NPNR_ASSERT(iter == bel_pins.end()); // assert there is only one driver bel pin; + return driver; } -WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &user_info) const +SSOArray Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const { auto dst_bel = user_info.cell->bel; - if (dst_bel == BelId()) - return WireId(); + return SSOArray(0, WireId()); + size_t bel_pin_count = 0; + // We use an SSOArray here because it avoids any heap allocation for the 99.9% case of 1 or 2 sink wires + // but as SSOArray doesn't (currently) support resizing to keep things simple it does mean we have to do + // two loops + for (auto s : getBelPinsForCellPin(user_info.cell, user_info.port)) { + (void)s; // unused + ++bel_pin_count; + } + SSOArray result(bel_pin_count, WireId()); + bel_pin_count = 0; + for (auto pin : getBelPinsForCellPin(user_info.cell, user_info.port)) { + result[bel_pin_count++] = getBelPinWire(dst_bel, pin); + } + return result; +} - IdString user_port = user_info.port; - return getBelPinWire(dst_bel, user_port); +size_t Context::getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const +{ + size_t count = 0; + for (auto s : getNetinfoSinkWires(net_info, sink)) { + (void)s; // unused + ++count; + } + return count; +} + +WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const +{ + size_t count = 0; + for (auto s : getNetinfoSinkWires(net_info, sink)) { + if (count == phys_idx) + return s; + ++count; + } + /* TODO: This should be an assertion failure, but for the zero-wire case of unplaced sinks; legacy code currently + assumes WireId Remove once the refactoring process is complete. + */ + return WireId(); } delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const @@ -383,29 +423,33 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us if (src_wire == WireId()) return 0; - WireId dst_wire = getNetinfoSinkWire(net_info, user_info); - WireId cursor = dst_wire; - delay_t delay = 0; + delay_t max_delay = 0; - while (cursor != WireId() && cursor != src_wire) { - auto it = net_info->wires.find(cursor); + for (auto dst_wire : getNetinfoSinkWires(net_info, user_info)) { + WireId cursor = dst_wire; + delay_t delay = 0; - if (it == net_info->wires.end()) - break; + while (cursor != WireId() && cursor != src_wire) { + auto it = net_info->wires.find(cursor); - PipId pip = it->second.pip; - if (pip == PipId()) - break; + if (it == net_info->wires.end()) + break; - delay += getPipDelay(pip).maxDelay(); - delay += getWireDelay(cursor).maxDelay(); - cursor = getPipSrcWire(pip); + PipId pip = it->second.pip; + if (pip == PipId()) + break; + + delay += getPipDelay(pip).maxDelay(); + delay += getWireDelay(cursor).maxDelay(); + cursor = getPipSrcWire(pip); + } + + if (cursor == src_wire) + max_delay = std::max(max_delay, delay + getWireDelay(src_wire).maxDelay()); // routed + else + max_delay = std::max(max_delay, predictDelay(net_info, user_info)); // unrouted } - - if (cursor == src_wire) - return delay + getWireDelay(src_wire).maxDelay(); - - return predictDelay(net_info, user_info); + return max_delay; } static uint32_t xorshift32(uint32_t x) diff --git a/common/nextpnr.h b/common/nextpnr.h index cf04f831..cb4dbc28 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -1494,7 +1494,9 @@ struct Context : Arch, DeterministicRNG // -------------------------------------------------------------- WireId getNetinfoSourceWire(const NetInfo *net_info) const; - WireId getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink) const; + SSOArray getNetinfoSinkWires(const NetInfo *net_info, const PortRef &sink) const; + size_t getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const; + WireId getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const; delay_t getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &sink) const; // provided by router1.cc diff --git a/common/router1.cc b/common/router1.cc index d2816c1e..51a356bc 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -144,7 +144,7 @@ struct Router1 int user_idx = arc.user_idx; auto src_wire = ctx->getNetinfoSourceWire(net_info); - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); arc_queue_insert(arc, src_wire, dst_wire); } @@ -302,7 +302,7 @@ struct Router1 log_assert(src_wire != WireId()); for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); log_assert(dst_wire != WireId()); arc_key arc; @@ -375,7 +375,7 @@ struct Router1 ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx); for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); if (dst_wire == WireId()) log_error("No wire found for port %s on destination cell %s.\n", @@ -443,7 +443,7 @@ struct Router1 int user_idx = arc.user_idx; auto src_wire = ctx->getNetinfoSourceWire(net_info); - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); ripup_flag = false; if (ctx->debug) { @@ -934,7 +934,7 @@ bool Context::checkRoutedDesign() const std::unordered_map dest_wires; for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); log_assert(dst_wire != WireId()); dest_wires[dst_wire] = user_idx; diff --git a/common/router2.cc b/common/router2.cc index 49d5fdec..d1e4e347 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -150,7 +150,7 @@ struct Router2 for (size_t j = 0; j < ni->users.size(); j++) { auto &usr = ni->users.at(j); - WireId src_wire = ctx->getNetinfoSourceWire(ni), dst_wire = ctx->getNetinfoSinkWire(ni, usr); + WireId src_wire = ctx->getNetinfoSourceWire(ni), dst_wire = ctx->getNetinfoSinkWire(ni, usr, 0); nets.at(i).src_wire = src_wire; if (ni->driver.cell == nullptr) src_wire = dst_wire; @@ -405,7 +405,7 @@ struct Router2 void reserve_wires_for_arc(NetInfo *net, size_t i) { WireId src = ctx->getNetinfoSourceWire(net); - WireId sink = ctx->getNetinfoSinkWire(net, net->users.at(i)); + WireId sink = ctx->getNetinfoSinkWire(net, net->users.at(i), 0); if (sink == WireId()) return; std::unordered_set rsv; @@ -479,7 +479,7 @@ struct Router2 auto &usr = net->users.at(i); ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", int(i), ctx->nameOf(net), ad.bb.x0, ad.bb.y0, ad.bb.x1, ad.bb.y1); - WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr); + WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, 0); if (src_wire == WireId()) ARC_LOG_ERR("No wire found for port %s on source cell %s.\n", ctx->nameOf(net->driver.port), ctx->nameOf(net->driver.cell)); @@ -726,7 +726,7 @@ struct Router2 if (check_arc_routing(net, i)) continue; auto &usr = net->users.at(i); - WireId dst_wire = ctx->getNetinfoSinkWire(net, usr); + WireId dst_wire = ctx->getNetinfoSinkWire(net, usr, 0); // Case of arcs that were pre-routed strongly (e.g. clocks) if (net->wires.count(dst_wire) && net->wires.at(dst_wire).strength > STRENGTH_STRONG) return ARC_SUCCESS; @@ -751,7 +751,7 @@ struct Router2 if (res2 != ARC_SUCCESS) log_error("Failed to route arc %d of net '%s', from %s to %s.\n", int(i), ctx->nameOf(net), ctx->nameOfWire(ctx->getNetinfoSourceWire(net)), - ctx->nameOfWire(ctx->getNetinfoSinkWire(net, net->users.at(i)))); + ctx->nameOfWire(ctx->getNetinfoSinkWire(net, net->users.at(i), 0))); } } } @@ -803,7 +803,7 @@ struct Router2 // Skip routes with no source if (src == WireId()) return true; - WireId dst = ctx->getNetinfoSinkWire(net, usr); + WireId dst = ctx->getNetinfoSinkWire(net, usr, 0); // Skip routes where the destination is already bound if (dst == WireId() || ctx->getBoundWireNet(dst) == net) return true; diff --git a/common/timing.cc b/common/timing.cc index 9fb14a33..a741c6ee 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -869,7 +869,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx)); if (ctx->verbose) { auto driver_wire = ctx->getNetinfoSourceWire(net); - auto sink_wire = ctx->getNetinfoSinkWire(net, *sink); + auto sink_wire = ctx->getNetinfoSinkWire(net, *sink, 0); log_info(" prediction: %f ns estimate: %f ns\n", ctx->getDelayNS(ctx->predictDelay(net, *sink)), ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire))); diff --git a/nexus/global.cc b/nexus/global.cc index f7abb399..62633df9 100644 --- a/nexus/global.cc +++ b/nexus/global.cc @@ -53,7 +53,7 @@ struct NexusGlobalRouter // Lookup source and destination wires WireId src = ctx->getNetinfoSourceWire(net); - WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx)); + WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx), 0); if (src == WireId()) log_error("Net '%s' has an invalid source port %s.%s\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell), From 7dafc64f78270ec37e2155c609da7cbf2ae18028 Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Feb 2021 14:31:16 +0000 Subject: [PATCH 3/4] router1: Support for multiple bel pins per cell pin Signed-off-by: gatecat --- common/router1.cc | 144 +++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 64 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 51a356bc..efc06b06 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -32,12 +32,20 @@ USING_NEXTPNR_NAMESPACE struct arc_key { NetInfo *net_info; + // logical user cell port index int user_idx; + // physical index into cell->bel pin mapping (usually 0) + unsigned phys_idx; - bool operator==(const arc_key &other) const { return (net_info == other.net_info) && (user_idx == other.user_idx); } + bool operator==(const arc_key &other) const + { + return (net_info == other.net_info) && (user_idx == other.user_idx) && (phys_idx == other.phys_idx); + } bool operator<(const arc_key &other) const { - return net_info == other.net_info ? user_idx < other.user_idx : net_info->name < other.net_info->name; + return net_info == other.net_info + ? (user_idx == other.user_idx ? phys_idx < other.phys_idx : user_idx < other.user_idx) + : net_info->name < other.net_info->name; } struct Hash @@ -46,6 +54,7 @@ struct arc_key { std::size_t seed = std::hash()(arg.net_info); seed ^= std::hash()(arg.user_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= std::hash()(arg.phys_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } }; @@ -142,9 +151,10 @@ struct Router1 NetInfo *net_info = arc.net_info; int user_idx = arc.user_idx; + unsigned phys_idx = arc.phys_idx; auto src_wire = ctx->getNetinfoSourceWire(net_info); - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], phys_idx); arc_queue_insert(arc, src_wire, dst_wire); } @@ -302,27 +312,29 @@ struct Router1 log_assert(src_wire != WireId()); for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); - log_assert(dst_wire != WireId()); + unsigned phys_idx = 0; + for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, net_info->users[user_idx])) { + log_assert(dst_wire != WireId()); - arc_key arc; - arc.net_info = net_info; - arc.user_idx = user_idx; - - valid_arcs.insert(arc); + arc_key arc; + arc.net_info = net_info; + arc.user_idx = user_idx; + arc.phys_idx = phys_idx++; + valid_arcs.insert(arc); #if 0 - if (ctx->debug) + if (ctx->debug) log("[check] arc: %s %s\n", ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire)); #endif - for (WireId wire : arc_to_wires[arc]) { + for (WireId wire : arc_to_wires[arc]) { #if 0 - if (ctx->debug) + if (ctx->debug) log("[check] wire: %s\n", ctx->nameOfWire(wire)); #endif - valid_wires_for_net.insert(wire); - log_assert(wire_to_arcs[wire].count(arc)); - log_assert(net_info->wires.count(wire)); + valid_wires_for_net.insert(wire); + log_assert(wire_to_arcs[wire].count(arc)); + log_assert(net_info->wires.count(wire)); + } } } @@ -375,52 +387,55 @@ struct Router1 ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx); for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); + unsigned phys_idx = 0; + for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, net_info->users[user_idx])) { + arc_key arc; + arc.net_info = net_info; + arc.user_idx = user_idx; + arc.phys_idx = phys_idx++; - if (dst_wire == WireId()) - log_error("No wire found for port %s on destination cell %s.\n", - ctx->nameOf(net_info->users[user_idx].port), ctx->nameOf(net_info->users[user_idx].cell)); - - if (dst_to_arc.count(dst_wire)) { - if (dst_to_arc.at(dst_wire).net_info == net_info) - continue; - log_error("Found two arcs with same sink wire %s: %s (%d) vs %s (%d)\n", ctx->nameOfWire(dst_wire), - ctx->nameOf(net_info), user_idx, ctx->nameOf(dst_to_arc.at(dst_wire).net_info), - dst_to_arc.at(dst_wire).user_idx); - } - - if (src_to_net.count(dst_wire)) - log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n", - ctx->nameOfWire(dst_wire), ctx->nameOf(src_to_net.at(dst_wire)), ctx->nameOf(net_info), - user_idx); - - arc_key arc; - arc.net_info = net_info; - arc.user_idx = user_idx; - - dst_to_arc[dst_wire] = arc; - - if (net_info->wires.count(dst_wire) == 0) { - arc_queue_insert(arc, src_wire, dst_wire); - continue; - } - - WireId cursor = dst_wire; - wire_to_arcs[cursor].insert(arc); - arc_to_wires[arc].insert(cursor); - - while (src_wire != cursor) { - auto it = net_info->wires.find(cursor); - if (it == net_info->wires.end()) { - arc_queue_insert(arc, src_wire, dst_wire); - break; + if (dst_to_arc.count(dst_wire)) { + if (dst_to_arc.at(dst_wire).net_info == net_info) + continue; + log_error("Found two arcs with same sink wire %s: %s (%d) vs %s (%d)\n", + ctx->nameOfWire(dst_wire), ctx->nameOf(net_info), user_idx, + ctx->nameOf(dst_to_arc.at(dst_wire).net_info), dst_to_arc.at(dst_wire).user_idx); } - NPNR_ASSERT(it->second.pip != PipId()); - cursor = ctx->getPipSrcWire(it->second.pip); + if (src_to_net.count(dst_wire)) + log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n", + ctx->nameOfWire(dst_wire), ctx->nameOf(src_to_net.at(dst_wire)), + ctx->nameOf(net_info), user_idx); + + dst_to_arc[dst_wire] = arc; + + if (net_info->wires.count(dst_wire) == 0) { + arc_queue_insert(arc, src_wire, dst_wire); + continue; + } + + WireId cursor = dst_wire; wire_to_arcs[cursor].insert(arc); arc_to_wires[arc].insert(cursor); + + while (src_wire != cursor) { + auto it = net_info->wires.find(cursor); + if (it == net_info->wires.end()) { + arc_queue_insert(arc, src_wire, dst_wire); + break; + } + + NPNR_ASSERT(it->second.pip != PipId()); + cursor = ctx->getPipSrcWire(it->second.pip); + wire_to_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); + } } + // TODO: this matches the situation before supporting multiple cell->bel pins, but do we want to keep + // this invariant? + if (phys_idx == 0) + log_error("No wires found for port %s on destination cell %s.\n", + ctx->nameOf(net_info->users[user_idx].port), ctx->nameOf(net_info->users[user_idx].cell)); } src_to_net[src_wire] = net_info; @@ -443,7 +458,7 @@ struct Router1 int user_idx = arc.user_idx; auto src_wire = ctx->getNetinfoSourceWire(net_info); - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], arc.phys_idx); ripup_flag = false; if (ctx->debug) { @@ -934,14 +949,15 @@ bool Context::checkRoutedDesign() const std::unordered_map dest_wires; for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx], 0); - log_assert(dst_wire != WireId()); - dest_wires[dst_wire] = user_idx; + for (auto dst_wire : ctx->getNetinfoSinkWires(net_info, net_info->users[user_idx])) { + log_assert(dst_wire != WireId()); + dest_wires[dst_wire] = user_idx; - if (net_info->wires.count(dst_wire) == 0) { - if (ctx->debug) - log(" sink %d (%s) not bound to net\n", user_idx, ctx->nameOfWire(dst_wire)); - found_unrouted = true; + if (net_info->wires.count(dst_wire) == 0) { + if (ctx->debug) + log(" sink %d (%s) not bound to net\n", user_idx, ctx->nameOfWire(dst_wire)); + found_unrouted = true; + } } } From 7c7d69e1d2030dc983d0ddbc0e04f6765d51bbbc Mon Sep 17 00:00:00 2001 From: gatecat Date: Wed, 10 Feb 2021 14:43:49 +0000 Subject: [PATCH 4/4] router2: Support for multiple bel pins per cell pin Signed-off-by: gatecat --- common/router2.cc | 180 ++++++++++++++++++++++++---------------------- 1 file changed, 95 insertions(+), 85 deletions(-) diff --git a/common/router2.cc b/common/router2.cc index d1e4e347..a2023f47 100644 --- a/common/router2.cc +++ b/common/router2.cc @@ -58,7 +58,7 @@ struct Router2 struct PerNetData { WireId src_wire; - std::vector arcs; + std::vector> arcs; ArcBounds bb; // Coordinates of the center of the net, used for the weight-to-average int cx, cy, hpwl; @@ -150,26 +150,30 @@ struct Router2 for (size_t j = 0; j < ni->users.size(); j++) { auto &usr = ni->users.at(j); - WireId src_wire = ctx->getNetinfoSourceWire(ni), dst_wire = ctx->getNetinfoSinkWire(ni, usr, 0); - nets.at(i).src_wire = src_wire; - if (ni->driver.cell == nullptr) - src_wire = dst_wire; - if (ni->driver.cell == nullptr && dst_wire == WireId()) - continue; - if (src_wire == WireId()) - log_error("No wire found for port %s on source cell %s.\n", ctx->nameOf(ni->driver.port), - ctx->nameOf(ni->driver.cell)); - if (dst_wire == WireId()) - log_error("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.port), - ctx->nameOf(usr.cell)); - nets.at(i).arcs.at(j).sink_wire = dst_wire; - // Set bounding box for this arc - nets.at(i).arcs.at(j).bb = ctx->getRouteBoundingBox(src_wire, dst_wire); - // Expand net bounding box to include this arc - nets.at(i).bb.x0 = std::min(nets.at(i).bb.x0, nets.at(i).arcs.at(j).bb.x0); - nets.at(i).bb.x1 = std::max(nets.at(i).bb.x1, nets.at(i).arcs.at(j).bb.x1); - nets.at(i).bb.y0 = std::min(nets.at(i).bb.y0, nets.at(i).arcs.at(j).bb.y0); - nets.at(i).bb.y1 = std::max(nets.at(i).bb.y1, nets.at(i).arcs.at(j).bb.y1); + WireId src_wire = ctx->getNetinfoSourceWire(ni); + for (auto &dst_wire : ctx->getNetinfoSinkWires(ni, usr)) { + nets.at(i).src_wire = src_wire; + if (ni->driver.cell == nullptr) + src_wire = dst_wire; + if (ni->driver.cell == nullptr && dst_wire == WireId()) + continue; + if (src_wire == WireId()) + log_error("No wire found for port %s on source cell %s.\n", ctx->nameOf(ni->driver.port), + ctx->nameOf(ni->driver.cell)); + if (dst_wire == WireId()) + log_error("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.port), + ctx->nameOf(usr.cell)); + nets.at(i).arcs.at(j).emplace_back(); + auto &ad = nets.at(i).arcs.at(j).back(); + ad.sink_wire = dst_wire; + // Set bounding box for this arc + ad.bb = ctx->getRouteBoundingBox(src_wire, dst_wire); + // Expand net bounding box to include this arc + nets.at(i).bb.x0 = std::min(nets.at(i).bb.x0, ad.bb.x0); + nets.at(i).bb.x1 = std::max(nets.at(i).bb.x1, ad.bb.x1); + nets.at(i).bb.y0 = std::min(nets.at(i).bb.y0, ad.bb.y0); + nets.at(i).bb.y1 = std::max(nets.at(i).bb.x1, ad.bb.y1); + } // Add location to centroid sum Loc usr_loc = ctx->getBelLocation(usr.cell->bel); nets.at(i).cx += usr_loc.x; @@ -254,7 +258,7 @@ struct Router2 // Nets that failed routing std::vector failed_nets; - std::vector route_arcs; + std::vector> route_arcs; std::priority_queue, QueuedWire::Greater> queue; // Special case where one net has multiple logical arcs to the same physical sink @@ -317,9 +321,9 @@ struct Router2 } } - void ripup_arc(NetInfo *net, size_t user) + void ripup_arc(NetInfo *net, size_t user, size_t phys_pin) { - auto &ad = nets.at(net->udata).arcs.at(user); + auto &ad = nets.at(net->udata).arcs.at(user).at(phys_pin); if (!ad.routed) return; WireId src = nets.at(net->udata).src_wire; @@ -333,7 +337,7 @@ struct Router2 ad.routed = false; } - float score_wire_for_arc(NetInfo *net, size_t user, WireId wire, PipId pip) + float score_wire_for_arc(NetInfo *net, size_t user, size_t phys_pin, WireId wire, PipId pip) { auto &wd = wire_data(wire); auto &nd = nets.at(net->udata); @@ -350,7 +354,7 @@ struct Router2 for (auto &bound : wd.bound_nets) if (bound.first != net->udata) max_bound_crit = std::max(max_bound_crit, nets.at(bound.first).max_crit); - if (max_bound_crit >= 0.8 && nd.arcs.at(user).arc_crit < (max_bound_crit + 0.01)) { + if (max_bound_crit >= 0.8 && nd.arcs.at(user).at(phys_pin).arc_crit < (max_bound_crit + 0.01)) { present_cost *= 1.5; } } @@ -372,9 +376,9 @@ struct Router2 return (ctx->getDelayNS(ctx->estimateDelay(wd.w, sink)) / (1 + source_uses)) + cfg.ipin_cost_adder; } - bool check_arc_routing(NetInfo *net, size_t usr) + bool check_arc_routing(NetInfo *net, size_t usr, size_t phys_pin) { - auto &ad = nets.at(net->udata).arcs.at(usr); + auto &ad = nets.at(net->udata).arcs.at(usr).at(phys_pin); WireId src_wire = nets.at(net->udata).src_wire; WireId cursor = ad.sink_wire; while (wire_data(cursor).bound_nets.count(net->udata)) { @@ -405,35 +409,34 @@ struct Router2 void reserve_wires_for_arc(NetInfo *net, size_t i) { WireId src = ctx->getNetinfoSourceWire(net); - WireId sink = ctx->getNetinfoSinkWire(net, net->users.at(i), 0); - if (sink == WireId()) - return; - std::unordered_set rsv; - WireId cursor = sink; - bool done = false; - if (ctx->debug) - log("reserving wires for arc %d of net %s\n", int(i), ctx->nameOf(net)); - while (!done) { - auto &wd = wire_data(cursor); + for (auto sink : ctx->getNetinfoSinkWires(net, net->users.at(i))) { + std::unordered_set rsv; + WireId cursor = sink; + bool done = false; if (ctx->debug) - log(" %s\n", ctx->nameOfWire(cursor)); - wd.reserved_net = net->udata; - if (cursor == src) - break; - WireId next_cursor; - for (auto uh : ctx->getPipsUphill(cursor)) { - WireId w = ctx->getPipSrcWire(uh); - if (is_wire_undriveable(w)) - continue; - if (next_cursor != WireId()) { - done = true; + log("reserving wires for arc %d of net %s\n", int(i), ctx->nameOf(net)); + while (!done) { + auto &wd = wire_data(cursor); + if (ctx->debug) + log(" %s\n", ctx->nameOfWire(cursor)); + wd.reserved_net = net->udata; + if (cursor == src) break; + WireId next_cursor; + for (auto uh : ctx->getPipsUphill(cursor)) { + WireId w = ctx->getPipSrcWire(uh); + if (is_wire_undriveable(w)) + continue; + if (next_cursor != WireId()) { + done = true; + break; + } + next_cursor = w; } - next_cursor = w; + if (next_cursor == WireId()) + break; + cursor = next_cursor; } - if (next_cursor == WireId()) - break; - cursor = next_cursor; } } @@ -471,15 +474,15 @@ struct Router2 } bool was_visited(int wire) { return flat_wires.at(wire).visit.visited; } - ArcRouteResult route_arc(ThreadContext &t, NetInfo *net, size_t i, bool is_mt, bool is_bb = true) + ArcRouteResult route_arc(ThreadContext &t, NetInfo *net, size_t i, size_t phys_pin, bool is_mt, bool is_bb = true) { auto &nd = nets[net->udata]; - auto &ad = nd.arcs[i]; + auto &ad = nd.arcs.at(i).at(phys_pin); auto &usr = net->users.at(i); ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", int(i), ctx->nameOf(net), ad.bb.x0, ad.bb.y0, ad.bb.x1, ad.bb.y1); - WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, 0); + WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, phys_pin); if (src_wire == WireId()) ARC_LOG_ERR("No wire found for port %s on source cell %s.\n", ctx->nameOf(net->driver.port), ctx->nameOf(net->driver.cell)); @@ -650,7 +653,7 @@ struct Router2 if (!thread_test_wire(t, nwd)) continue; // thread safety issue WireScore next_score; - next_score.cost = curr.score.cost + score_wire_for_arc(net, i, next, dh); + next_score.cost = curr.score.cost + score_wire_for_arc(net, i, phys_pin, next, dh); next_score.delay = curr.score.delay + ctx->getPipDelay(dh).maxDelay() + ctx->getWireDelay(next).maxDelay(); next_score.togo_cost = cfg.estimate_weight * get_togo_cost(net, i, next_idx, dst_wire); @@ -720,22 +723,26 @@ struct Router2 bool have_failures = false; t.processed_sinks.clear(); t.route_arcs.clear(); + auto &nd = nets.at(net->udata); for (size_t i = 0; i < net->users.size(); i++) { - // Ripup failed arcs to start with - // Check if arc is already legally routed - if (check_arc_routing(net, i)) - continue; - auto &usr = net->users.at(i); - WireId dst_wire = ctx->getNetinfoSinkWire(net, usr, 0); - // Case of arcs that were pre-routed strongly (e.g. clocks) - if (net->wires.count(dst_wire) && net->wires.at(dst_wire).strength > STRENGTH_STRONG) - return ARC_SUCCESS; - // Ripup arc to start with - ripup_arc(net, i); - t.route_arcs.push_back(i); + auto &ad = nd.arcs.at(i); + for (size_t j = 0; j < ad.size(); j++) { + // Ripup failed arcs to start with + // Check if arc is already legally routed + if (check_arc_routing(net, i, j)) + continue; + auto &usr = net->users.at(i); + WireId dst_wire = ctx->getNetinfoSinkWire(net, usr, j); + // Case of arcs that were pre-routed strongly (e.g. clocks) + if (net->wires.count(dst_wire) && net->wires.at(dst_wire).strength > STRENGTH_STRONG) + return ARC_SUCCESS; + // Ripup arc to start with + ripup_arc(net, i, j); + t.route_arcs.emplace_back(i, j); + } } - for (auto i : t.route_arcs) { - auto res1 = route_arc(t, net, i, is_mt, true); + for (auto a : t.route_arcs) { + auto res1 = route_arc(t, net, a.first, a.second, is_mt, true); if (res1 == ARC_FATAL) return false; // Arc failed irrecoverably else if (res1 == ARC_RETRY_WITHOUT_BB) { @@ -744,14 +751,14 @@ struct Router2 have_failures = true; } else { // Attempt a re-route without the bounding box constraint - ROUTE_LOG_DBG("Rerouting arc %d of net '%s' without bounding box, possible tricky routing...\n", - int(i), ctx->nameOf(net)); - auto res2 = route_arc(t, net, i, is_mt, false); + ROUTE_LOG_DBG("Rerouting arc %d.%d of net '%s' without bounding box, possible tricky routing...\n", + int(a.first), int(a.second), ctx->nameOf(net)); + auto res2 = route_arc(t, net, a.first, a.second, is_mt, false); // If this also fails, no choice but to give up if (res2 != ARC_SUCCESS) - log_error("Failed to route arc %d of net '%s', from %s to %s.\n", int(i), ctx->nameOf(net), - ctx->nameOfWire(ctx->getNetinfoSourceWire(net)), - ctx->nameOfWire(ctx->getNetinfoSinkWire(net, net->users.at(i), 0))); + log_error("Failed to route arc %d.%d of net '%s', from %s to %s.\n", int(a.first), + int(a.second), ctx->nameOf(net), ctx->nameOfWire(ctx->getNetinfoSourceWire(net)), + ctx->nameOfWire(ctx->getNetinfoSinkWire(net, net->users.at(a.first), a.second))); } } } @@ -789,7 +796,7 @@ struct Router2 } } - bool bind_and_check(NetInfo *net, int usr_idx) + bool bind_and_check(NetInfo *net, int usr_idx, int phys_pin) { #ifdef ARCH_ECP5 if (net->is_global) @@ -797,13 +804,13 @@ struct Router2 #endif bool success = true; auto &nd = nets.at(net->udata); - auto &ad = nd.arcs.at(usr_idx); + auto &ad = nd.arcs.at(usr_idx).at(phys_pin); auto &usr = net->users.at(usr_idx); WireId src = ctx->getNetinfoSourceWire(net); // Skip routes with no source if (src == WireId()) return true; - WireId dst = ctx->getNetinfoSinkWire(net, usr, 0); + WireId dst = ctx->getNetinfoSinkWire(net, usr, phys_pin); // Skip routes where the destination is already bound if (dst == WireId() || ctx->getBoundWireNet(dst) == net) return true; @@ -849,7 +856,7 @@ struct Router2 for (auto tb : to_bind) ctx->bindPip(tb, net, STRENGTH_WEAK); } else { - ripup_arc(net, usr_idx); + ripup_arc(net, usr_idx, phys_pin); failed_nets.insert(net->udata); } return success; @@ -875,9 +882,11 @@ struct Router2 ctx->unbindWire(w); // Bind the arcs using the routes we have discovered for (size_t i = 0; i < net->users.size(); i++) { - if (!bind_and_check(net, i)) { - ++arch_fail; - success = false; + for (size_t phys_pin = 0; phys_pin < nets.at(net->udata).arcs.at(i).size(); phys_pin++) { + if (!bind_and_check(net, i, phys_pin)) { + ++arch_fail; + success = false; + } } } } @@ -1123,7 +1132,8 @@ struct Router2 continue; for (int i = 0; i < int(fnd->second.criticality.size()); i++) { float c = fnd->second.criticality.at(i); - net.arcs.at(i).arc_crit = c; + for (auto &a : net.arcs.at(i)) + a.arc_crit = c; net.max_crit = std::max(net.max_crit, c); } }