|
| 1 | +/* |
| 2 | + * nextpnr -- Next Generation Place and Route |
| 3 | + * |
| 4 | + * Copyright (C) 2020-23 gatecat <[email protected]> |
| 5 | + * |
| 6 | + * |
| 7 | + * Permission to use, copy, modify, and/or distribute this software for any |
| 8 | + * purpose with or without fee is hereby granted, provided that the above |
| 9 | + * copyright notice and this permission notice appear in all copies. |
| 10 | + * |
| 11 | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 12 | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 13 | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 14 | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 15 | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 16 | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 17 | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 18 | + * |
| 19 | + */ |
| 20 | + |
| 21 | +#include "log.h" |
| 22 | +#include "nextpnr.h" |
| 23 | +#include "util.h" |
| 24 | + |
| 25 | +#include <queue> |
| 26 | + |
| 27 | +NEXTPNR_NAMESPACE_BEGIN |
| 28 | + |
| 29 | +struct MachxoGlobalRouter |
| 30 | +{ |
| 31 | + Context *ctx; |
| 32 | + |
| 33 | + MachxoGlobalRouter(Context *ctx) : ctx(ctx){}; |
| 34 | + |
| 35 | + // When routing globals; we allow global->local for some tricky cases but never local->local |
| 36 | + bool global_pip_filter(PipId pip) const |
| 37 | + { |
| 38 | + WireId src = ctx->getPipSrcWire(pip); |
| 39 | + const char *s = ctx->tile_info(src)->wire_data[src.index].name.get(); |
| 40 | + if ((s[0] == 'H' || s[0] == 'V') && s[1] == '0') |
| 41 | + return false; |
| 42 | + return true; |
| 43 | + } |
| 44 | + |
| 45 | + // Dedicated backwards BFS routing for global networks |
| 46 | + template <typename Tfilt> |
| 47 | + bool backwards_bfs_route(NetInfo *net, store_index<PortRef> user_idx, int iter_limit, bool strict, Tfilt pip_filter) |
| 48 | + { |
| 49 | + // Queue of wires to visit |
| 50 | + std::queue<WireId> visit; |
| 51 | + // Wire -> upstream pip |
| 52 | + dict<WireId, PipId> backtrace; |
| 53 | + |
| 54 | + // Lookup source and destination wires |
| 55 | + WireId src = ctx->getNetinfoSourceWire(net); |
| 56 | + WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx), 0); |
| 57 | + |
| 58 | + if (src == WireId()) |
| 59 | + log_error("Net '%s' has an invalid source port %s.%s\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell), |
| 60 | + ctx->nameOf(net->driver.port)); |
| 61 | + |
| 62 | + if (dst == WireId()) |
| 63 | + log_error("Net '%s' has an invalid sink port %s.%s\n", ctx->nameOf(net), |
| 64 | + ctx->nameOf(net->users.at(user_idx).cell), ctx->nameOf(net->users.at(user_idx).port)); |
| 65 | + |
| 66 | + if (ctx->getBoundWireNet(src) != net) |
| 67 | + ctx->bindWire(src, net, STRENGTH_LOCKED); |
| 68 | + |
| 69 | + if (src == dst) { |
| 70 | + // Nothing more to do |
| 71 | + return true; |
| 72 | + } |
| 73 | + |
| 74 | + visit.push(dst); |
| 75 | + backtrace[dst] = PipId(); |
| 76 | + |
| 77 | + int iter = 0; |
| 78 | + |
| 79 | + while (!visit.empty() && (iter++ < iter_limit)) { |
| 80 | + WireId cursor = visit.front(); |
| 81 | + visit.pop(); |
| 82 | + // Search uphill pips |
| 83 | + for (PipId pip : ctx->getPipsUphill(cursor)) { |
| 84 | + // Skip pip if unavailable, and not because it's already used for this net |
| 85 | + if (!ctx->checkPipAvail(pip) && ctx->getBoundPipNet(pip) != net) |
| 86 | + continue; |
| 87 | + WireId prev = ctx->getPipSrcWire(pip); |
| 88 | + // Ditto for the upstream wire |
| 89 | + if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net) |
| 90 | + continue; |
| 91 | + // Skip already visited wires |
| 92 | + if (backtrace.count(prev)) |
| 93 | + continue; |
| 94 | + // Apply our custom pip filter |
| 95 | + if (!pip_filter(pip)) |
| 96 | + continue; |
| 97 | + // Add to the queue |
| 98 | + visit.push(prev); |
| 99 | + backtrace[prev] = pip; |
| 100 | + // Check if we are done yet |
| 101 | + if (prev == src) |
| 102 | + goto done; |
| 103 | + } |
| 104 | + if (false) { |
| 105 | + done: |
| 106 | + break; |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + if (backtrace.count(src)) { |
| 111 | + WireId cursor = src; |
| 112 | + std::vector<PipId> pips; |
| 113 | + // Create a list of pips on the routed path |
| 114 | + while (true) { |
| 115 | + PipId pip = backtrace.at(cursor); |
| 116 | + if (pip == PipId()) |
| 117 | + break; |
| 118 | + pips.push_back(pip); |
| 119 | + cursor = ctx->getPipDstWire(pip); |
| 120 | + } |
| 121 | + // Reverse that list |
| 122 | + std::reverse(pips.begin(), pips.end()); |
| 123 | + // Bind pips until we hit already-bound routing |
| 124 | + for (PipId pip : pips) { |
| 125 | + WireId dst = ctx->getPipDstWire(pip); |
| 126 | + if (ctx->getBoundWireNet(dst) == net) |
| 127 | + break; |
| 128 | + ctx->bindPip(pip, net, STRENGTH_LOCKED); |
| 129 | + } |
| 130 | + return true; |
| 131 | + } else { |
| 132 | + if (strict) |
| 133 | + log_error("Failed to route net '%s' from %s to %s using dedicated routing.\n", ctx->nameOf(net), |
| 134 | + ctx->nameOfWire(src), ctx->nameOfWire(dst)); |
| 135 | + return false; |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + bool is_relaxed_sink(const PortRef &sink) const |
| 140 | + { |
| 141 | + // Cases where global clocks are driving fabric |
| 142 | + if ((sink.cell->type == id_TRELLIS_COMB && sink.port != id_WCK) || |
| 143 | + (sink.cell->type == id_TRELLIS_FF && sink.port != id_CLK)) |
| 144 | + return true; |
| 145 | + return false; |
| 146 | + } |
| 147 | + |
| 148 | + void route_clk_net(NetInfo *net) |
| 149 | + { |
| 150 | + for (auto usr : net->users.enumerate()) |
| 151 | + backwards_bfs_route(net, usr.index, 1000000, true, |
| 152 | + [&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); }); |
| 153 | + log_info(" routed net '%s' using global resources\n", ctx->nameOf(net)); |
| 154 | + } |
| 155 | + |
| 156 | + void operator()() |
| 157 | + { |
| 158 | + log_info("Routing globals...\n"); |
| 159 | + for (auto &net : ctx->nets) { |
| 160 | + NetInfo *ni = net.second.get(); |
| 161 | + CellInfo *drv = ni->driver.cell; |
| 162 | + if (drv == nullptr) |
| 163 | + continue; |
| 164 | + if (drv->type.in(id_DCCA, id_DCMA)) { |
| 165 | + route_clk_net(ni); |
| 166 | + continue; |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | +}; |
| 171 | + |
| 172 | +void Arch::route_globals() |
| 173 | +{ |
| 174 | + MachxoGlobalRouter glb_router(getCtx()); |
| 175 | + glb_router(); |
| 176 | +} |
| 177 | + |
| 178 | +NEXTPNR_NAMESPACE_END |
0 commit comments