13#include "llvm/ADT/MapVector.h"
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/SetVector.h"
16#include "llvm/Support/Debug.h"
17#include "llvm/Support/FormatVariadic.h"
26#define DEBUG_TYPE "aie-pathfinder"
32constexpr double overCapacityCoeff = 0.1;
33constexpr double usedCapacityCoeff = 0.02;
34constexpr double demandBase = 1.0;
37constexpr double demandCoeff = 1.1;
38constexpr double priorityDemandCoeff =
39 static_cast<double>(std::numeric_limits<int>::max());
40constexpr int maxCircuitStreamCapacity = 1;
41constexpr int maxPacketStreamCapacity = 32;
43constexpr int routingCheckPenalty = 5;
45constexpr int maxSteers = 16;
49constexpr int maxOverlayFaults = 16;
52constexpr int maxMeetFaults = 4;
54constexpr double conflictSharePenalty = 4;
58constexpr int packetFanoutCap = 2;
63constexpr double treeSeedFactor = 0.9;
65constexpr double rerouteMinSaving = 1e-6;
68constexpr int stallIterations = 50;
71constexpr int maxReorderedStall = 300;
75constexpr int maxOverusedStall = 400;
78constexpr int stallHistory = 4;
83 c.demand = (demandBase + overCapacityCoeff * c.overCapacity) *
84 (demandBase + usedCapacityCoeff * c.usedCapacity);
97 os <<
"Unable to find a legal routing";
103 LLVM_DEBUG(llvm::dbgs() <<
"\t---Begin DynamicTileAnalysis Constructor---\n");
105 maxCol = device.getTargetModel().columns();
106 maxRow = device.getTargetModel().rows();
108 pathfinder.initialize(maxCol, maxRow, device.getTargetModel());
114 for (PacketFlowOp pktFlowOp : device.getOps<PacketFlowOp>()) {
115 Region &r = pktFlowOp.getPorts();
116 Block &b = r.front();
117 SmallVector<std::pair<TileID, Port>, 4> sources;
119 for (Operation &Op : b.getOperations()) {
120 if (
auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
121 auto srcTile = cast<TileOp>(pktSource.getTile().getDefiningOp());
123 {{srcTile.colIndex(), srcTile.rowIndex()}, pktSource.port()});
127 bool priorityFlow = pktFlowOp.getPriorityRoute().value_or(
false);
130 for (Operation &Op : b.getOperations()) {
131 if (
auto pktDest = dyn_cast<PacketDestOp>(Op)) {
132 auto dstTile = cast<TileOp>(pktDest.getTile().getDefiningOp());
133 Port dstPort = pktDest.port();
134 TileID dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
135 for (
auto &[srcCoords, srcPort] : sources) {
136 LLVM_DEBUG(llvm::dbgs()
137 <<
"\tAdding Packet Flow: (" << srcCoords.col <<
", "
138 << srcCoords.row <<
")"
139 << stringifyWireBundle(srcPort.bundle) << srcPort.channel
140 <<
" -> (" << dstCoords.col <<
", " << dstCoords.row <<
")"
141 << stringifyWireBundle(dstPort.bundle) << dstPort.channel
143 pathfinder.addFlow(srcCoords, srcPort, dstCoords, dstPort,
144 pktFlowOp.IDInt(), priorityFlow,
152 for (FlowOp flowOp : device.getOps<FlowOp>()) {
153 TileOp srcTile = cast<TileOp>(flowOp.getSource().getDefiningOp());
154 TileOp dstTile = cast<TileOp>(flowOp.getDest().getDefiningOp());
155 TileID srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
156 TileID dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
157 Port srcPort = {flowOp.getSourceBundle(), flowOp.getSourceChannel()};
158 Port dstPort = {flowOp.getDestBundle(), flowOp.getDestChannel()};
159 LLVM_DEBUG(llvm::dbgs()
160 <<
"\tAdding Flow: (" << srcCoords.col <<
", " << srcCoords.row
161 <<
")" << stringifyWireBundle(srcPort.bundle) << srcPort.channel
162 <<
" -> (" << dstCoords.col <<
", " << dstCoords.row <<
")"
163 << stringifyWireBundle(dstPort.bundle) << dstPort.channel
165 pathfinder.addFlow(srcCoords, srcPort, dstCoords, dstPort,
174 if (device.getHasCtrlPktOverlay()) {
175 llvm::DenseSet<TileID> reached;
176 for (PacketFlowOp pktFlowOp : device.getOps<PacketFlowOp>()) {
177 if (!pktFlowOp.getPriorityRoute().value_or(
false))
179 for (
auto pktDest : pktFlowOp.getPorts().getOps<PacketDestOp>())
180 if (pktDest.getBundle() == WireBundle::TileControl)
182 cast<TileOp>(pktDest.getTile().getDefiningOp()).getTileID());
184 if (!reached.empty())
185 for (
int row = 0; row <= maxRow; row++)
186 for (
int col = 0; col <= maxCol; col++)
187 if (!reached.contains({col, row}))
193 for (SwitchboxOp switchboxOp : device.getOps<SwitchboxOp>()) {
194 if (failed(
pathfinder.addFixedConnection(switchboxOp)))
195 return llvm::make_error<RoutingFailure>(
196 llvm::formatv(
"cannot add the fixed connections of the switchbox "
197 "at tile ({0}, {1})",
198 switchboxOp.colIndex(), switchboxOp.rowIndex()),
205 llvm::Expected<Routing> found =
pathfinder.findPaths(maxIterations);
207 return found.takeError();
212 coordToSwitchbox.clear();
213 coordToShimMux.clear();
214 for (
auto tileOp : device.getOps<TileOp>()) {
215 [[maybe_unused]]
bool fresh =
216 coordToTile.try_emplace(tileOp.getTileID(), tileOp).second;
219 for (
auto switchboxOp : device.getOps<SwitchboxOp>()) {
220 [[maybe_unused]]
bool fresh =
222 .try_emplace({switchboxOp.colIndex(), switchboxOp.rowIndex()},
227 for (
auto shimmuxOp : device.getOps<ShimMuxOp>()) {
228 [[maybe_unused]]
bool fresh =
230 .try_emplace({shimmuxOp.colIndex(), shimmuxOp.rowIndex()},
236 LLVM_DEBUG(llvm::dbgs() <<
"\t---End DynamicTileAnalysis Constructor---\n");
237 return llvm::Error::success();
241 TileOp &tileOp = coordToTile[{col, row}];
243 tileOp = TileOp::create(
245 cast<DeviceOp>(builder.getInsertionBlock()->getParentOp()).getLoc(),
251 return getTile(builder, tileId.col, tileId.row);
260 auto tileOp =
getTile(builder, col, row);
261 auto switchboxOp = SwitchboxOp::create(builder, tileOp.getLoc(), tileOp);
262 SwitchboxOp::ensureTerminator(switchboxOp.getConnections(), builder,
264 coordToSwitchbox[{col, row}] = switchboxOp;
272 auto tileOp =
getTile(builder, col, 0);
273 assert(tileOp.isShimNOCorPLTile());
274 auto shimMuxOp = ShimMuxOp::create(builder, tileOp.getLoc(), tileOp);
275 ShimMuxOp::ensureTerminator(shimMuxOp.getConnections(), builder,
277 coordToShimMux[{col, 0}] = shimMuxOp;
299 wireEdgesBegin.clear();
306 std::map<WireBundle, int> maxChannels;
307 auto intraconnect = [&](
int col,
int row) {
308 TileID coords = {col, row};
310 graph.try_emplace({coords, coords}, coords).first->second;
312 for (
int i = 0, e = getMaxEnumValForWireBundle() + 1; i < e; ++i) {
313 WireBundle bundle = symbolizeWireBundle(i).value();
321 for (
int channel = 0; channel < channels; channel++) {
330 for (
int channel = 0; channel < channels; channel++) {
333 maxChannels[bundle] = channels;
337 auto isMuxed = [&](
Port p) {
340 {WireBundle::DMA, WireBundle::NOC, WireBundle::PLIO},
343 for (
auto [i, pIn] : llvm::enumerate(sb.
srcPorts))
344 for (
auto [j, pOut] : llvm::enumerate(sb.
dstPorts))
347 pOut.bundle, pOut.channel) ||
348 isMuxed(pIn) || isMuxed(pOut);
351 auto interconnect = [&](
int col,
int row,
int targetCol,
int targetRow,
352 WireBundle srcBundle, WireBundle dstBundle) {
353 TileID src = {col, row}, dst = {targetCol, targetRow};
355 graph.try_emplace({src, dst}, src, dst).first->second;
356 for (
int channel = 0; channel < maxChannels[srcBundle]; channel++) {
361 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
365 for (
int row = 0; row <= maxRow; row++) {
366 for (
int col = 0; col <= maxCol; col++) {
369 intraconnect(col, row);
374 interconnect(col, row, col, row - 1, WireBundle::South,
379 interconnect(col, row, col, row + 1, WireBundle::North,
384 interconnect(col, row, col - 1, row, WireBundle::West,
389 interconnect(col, row, col + 1, row, WireBundle::East,
399 Port dstPort, std::optional<int> packetId,
401 std::optional<mlir::Location> loc) {
402 isPriorityFlow &= constraints.prioritize ||
403 constraints.pinned.count({srcCoords, srcPort}) > 0;
405 flowLocs[{srcCoords, srcPort}].try_emplace({dstCoords, dstPort}, *loc);
407 auto &ids = packetIdsTo[{{srcCoords, srcPort}, {dstCoords, dstPort}}];
408 if (!llvm::is_contained(ids, *packetId))
409 ids.push_back(*packetId);
411 priorityIds[{srcCoords, srcPort}].insert(*packetId);
414 PathEndPoint src{srcCoords, srcPort}, dst{dstCoords, dstPort};
415 auto flow = llvm::find_if(flows, [&](
const Flow &f) {
return f.
src == src; });
416 if (flow == flows.end()) {
419 Flow{packetId ? 0 : -1, isPriorityFlow, src, {dst}, packetId});
420 }
else if (isPriorityFlow) {
421 flow->isPriorityFlow =
true;
422 flow->dsts.insert(flow->dsts.begin(), dst);
424 flow->dsts.push_back(dst);
430std::optional<mlir::Location>
432 auto locs = flowLocs.find(src);
433 if (locs == flowLocs.end())
435 auto it = dst ? locs->second.find(*dst) : locs->second.begin();
436 if (it == locs->second.end())
441bool Pathfinder::shareAllChannels() {
442 shareChannels =
true;
446bool Pathfinder::capCrowdedFanOut() {
447 size_t capped = cappedTiles.size();
448 cappedTiles.insert(crowdedTiles.begin(), crowdedTiles.end());
449 return cappedTiles.size() > capped;
452std::map<PathEndPoint, std::set<int>> Pathfinder::idsBySource()
const {
453 std::map<PathEndPoint, std::set<int>> ids;
454 for (
const auto &[ends, sent] : packetIdsTo)
455 ids[ends.first].insert(sent.begin(), sent.end());
459bool Pathfinder::routeIdsApart() {
463 shareChannels =
false;
465 return llvm::any_of(idsBySource(),
466 [](
const auto &s) {
return s.second.size() > 1; });
469bool Pathfinder::splitSharedIds() {
473 shareChannels =
false;
475 std::map<PathEndPoint, std::set<int>> ids = idsBySource();
476 for (
const auto &[src, srcIds] : ids)
477 for (
const auto &[other, otherIds] : ids) {
478 const std::set<int> &theirs = otherIds;
479 if (!(src == other) &&
480 llvm::any_of(srcIds, [&](
int id) {
return theirs.count(
id); }) &&
481 llvm::any_of(srcIds, [&](
int id) {
return !theirs.count(
id); }))
489bool Pathfinder::stopMeeting() {
490 if (meetApart || !treesMet)
493 shareChannels = idsApart = splitShared =
false;
500enum class RelaxStep {
509constexpr RelaxStep relaxLadder[] = {
510 RelaxStep::ShareChannels, RelaxStep::CapCrowdedTiles,
511 RelaxStep::RouteIdsApart, RelaxStep::ShareChannels,
512 RelaxStep::CapCrowdedTiles, RelaxStep::SplitSharedIds,
513 RelaxStep::ShareChannels, RelaxStep::CapCrowdedTiles,
514 RelaxStep::StopMeeting};
518 if (!packetsFailed) {
519 LLVM_DEBUG(llvm::dbgs() <<
"No packet stream crosses an overused link\n");
522 while (relaxStep < std::size(relaxLadder)) {
523 switch (relaxLadder[relaxStep++]) {
524 case RelaxStep::ShareChannels:
525 if (shareAllChannels()) {
526 LLVM_DEBUG(llvm::dbgs() <<
"Relax: share channels\n");
530 case RelaxStep::CapCrowdedTiles:
531 if (capCrowdedFanOut()) {
532 LLVM_DEBUG(llvm::dbgs() <<
"Relax: cap crowded tiles\n");
536 case RelaxStep::RouteIdsApart:
537 if (routeIdsApart()) {
538 LLVM_DEBUG(llvm::dbgs() <<
"Relax: route ids apart\n");
542 relaxStep = std::size(relaxLadder) - 1;
544 case RelaxStep::SplitSharedIds:
545 if (splitSharedIds()) {
546 LLVM_DEBUG(llvm::dbgs() <<
"Relax: split shared ids\n");
549 relaxStep = std::size(relaxLadder) - 1;
551 case RelaxStep::StopMeeting:
553 LLVM_DEBUG(llvm::dbgs() <<
"Relax: stop meeting\n");
565 for (
auto &flow : flows)
566 llvm::sort(flow.dsts);
570 llvm::IntEqClasses groups(flows.size());
571 std::map<PathEndPoint, unsigned> firstTo;
572 for (
auto [k, flow] : llvm::enumerate(flows)) {
573 if (flow.packetGroupId < 0)
576 auto [it, fresh] = firstTo.try_emplace(dst, k);
578 groups.join(k, it->second);
581 llvm::DenseMap<unsigned, int> groupOf;
582 for (
auto [k, flow] : llvm::enumerate(flows))
583 if (flow.packetGroupId >= 0)
587 : groupOf.try_emplace(groups.findLeader(k), groupOf.size())
590 auto flowRank = [](
const Flow &flow) {
591 if (flow.isPriorityFlow)
593 if (flow.packetGroupId >= 0)
597 llvm::sort(flows, [&](
const Flow &lhs,
const Flow &rhs) {
598 return std::make_pair(flowRank(lhs), lhs.
src) <
599 std::make_pair(flowRank(rhs), rhs.
src);
605 if (src.bundle == WireBundle::DMA)
606 return src.channel == 0 ? 3 : 7;
607 if (src.bundle == WireBundle::NOC)
608 return src.channel >= 2 ? src.channel + 4 : src.channel + 2;
614 if (dst.bundle == WireBundle::DMA)
615 return dst.channel == 0 ? 2 : 3;
616 if (dst.bundle == WireBundle::NOC)
617 return dst.channel + 2;
622 int col = switchboxOp.colIndex();
623 int row = switchboxOp.rowIndex();
624 TileID coords = {col, row};
625 auto &sb = graph[std::make_pair(coords, coords)];
630 llvm::SmallVector<std::pair<int, int>, 8> reserved;
631 llvm::SmallDenseSet<int, 8> claimedDsts;
632 for (ConnectOp connectOp : switchboxOp.getOps<ConnectOp>()) {
633 int srcIdx = sb.srcIndex(connectOp.sourcePort());
634 int dstIdx = sb.dstIndex(connectOp.destPort());
637 if (srcIdx < 0 || dstIdx < 0 || !sb.at(srcIdx, dstIdx).available ||
638 !claimedDsts.insert(dstIdx).second) {
641 reserved.emplace_back(srcIdx, dstIdx);
650 llvm::SmallVector<int, 8> reservedMasterDsts;
651 for (MasterSetOp masterSetOp : switchboxOp.getOps<MasterSetOp>()) {
652 int dstIdx = sb.dstIndex(masterSetOp.destPort());
655 if (dstIdx < 0 || !claimedDsts.insert(dstIdx).second) {
658 reservedMasterDsts.push_back(dstIdx);
665 for (
auto [srcIdx, dstIdx] : reserved) {
666 for (
size_t j = 0; j < sb.dstPorts.size(); j++)
667 sb.at(srcIdx, j).available =
false;
668 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
669 sb.at(i, dstIdx).available =
false;
673 for (
int dstIdx : reservedMasterDsts)
674 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
675 sb.at(i, dstIdx).available =
false;
678 if (switchboxOp.getTileOp().isShimNOCorPLTile()) {
679 auto isMuxed = [](
Port p) {
680 return llvm::is_contained(
681 {WireBundle::DMA, WireBundle::NOC, WireBundle::PLIO}, p.bundle);
683 llvm::SmallDenseSet<int, 8> southDsts, southSrcs;
684 for (
int dstIdx : claimedDsts)
685 if (sb.dstPorts[dstIdx].bundle == WireBundle::South)
686 southDsts.insert(sb.dstPorts[dstIdx].channel);
687 for (
auto [srcIdx, dstIdx] : reserved)
688 if (sb.srcPorts[srcIdx].bundle == WireBundle::South)
689 southSrcs.insert(sb.srcPorts[srcIdx].channel);
690 for (
size_t j = 0; j < sb.dstPorts.size(); j++)
691 if (isMuxed(sb.dstPorts[j]) &&
693 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
694 sb.at(i, j).available =
false;
695 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
696 if (isMuxed(sb.srcPorts[i]) &&
698 for (
size_t j = 0; j < sb.dstPorts.size(); j++)
699 sb.at(i, j).available =
false;
701 for (PacketRulesOp rulesOp : switchboxOp.getOps<PacketRulesOp>()) {
702 int srcIdx = sb.srcIndex(rulesOp.sourcePort());
705 sb.packetOnlySrc.set(srcIdx);
711 auto it = graph.find({coords, coords});
712 if (it == graph.end())
715 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
716 for (
size_t j = 0; j < sb.
dstPorts.size(); j++)
720static constexpr double INF = std::numeric_limits<double>::max();
723enum Color : int8_t { WHITE = 0, GRAY = 1, BLACK = 2 };
726int Pathfinder::getOrAddNodeId(
const PathEndPoint &pep) {
727 auto it = nodeIds.find(pep);
728 if (it != nodeIds.end())
730 int id =
static_cast<int>(nodes.size());
732 nodes.push_back(pep);
742void Pathfinder::buildRoutingGraph() {
746 for (
auto &f : flows) {
747 getOrAddNodeId(f.src);
748 for (
auto &d : f.dsts)
754 for (
size_t id = 0;
id < nodes.size();
id++) {
755 PathEndPoint src = nodes[id];
758 std::vector<PathEndPoint> dests;
760 if (intraIt != graph.end()) {
761 auto &sb = intraIt->second;
762 for (
auto [i, pIn] : llvm::enumerate(sb.srcPorts))
763 for (
auto [j, pOut] : llvm::enumerate(sb.dstPorts))
764 if (pIn == src.
port && sb.at(i, j).available)
765 dests.emplace_back(src.
coords, pOut);
767 std::pair<TileID, Port> neighbors[] = {
769 {WireBundle::North, src.
port.channel}},
771 {WireBundle::East, src.
port.channel}},
773 {WireBundle::South, src.
port.channel}},
775 {WireBundle::West, src.
port.channel}}};
776 for (
const auto &[neighborCoords, neighborPort] : neighbors) {
777 auto nIt = graph.find({src.
coords, neighborCoords});
778 if (nIt != graph.end() &&
780 llvm::is_contained(nIt->second.dstPorts, neighborPort))
781 dests.emplace_back(neighborCoords, neighborPort);
785 std::vector<Edge> edges;
786 edges.reserve(dests.size());
787 for (
auto &dest : dests) {
788 auto &sb = graph.at({src.
coords, dest.coords});
789 int i = sb.srcIndex(src.
port);
790 int j = sb.dstIndex(dest.port);
791 assert(i >= 0 && j >= 0);
792 int destId = getOrAddNodeId(dest);
793 edges.push_back(Edge{destId, &sb, i, j});
796 std::stable_partition(edges.begin(), edges.end(), [](
const Edge &e) {
797 return e.sb->srcCoords == e.sb->dstCoords;
799 if (wireEdgesBegin.size() < nodes.size())
800 wireEdgesBegin.resize(nodes.size());
801 wireEdgesBegin[id] = wires - edges.begin();
804 if (adjacency.size() < nodes.size())
805 adjacency.resize(nodes.size());
806 adjacency[id] = std::move(edges);
808 size_t n = nodes.size();
809 if (adjacency.size() < n)
811 wireEdgesBegin.resize(n);
815 distance.assign(2 * n, INF);
816 indexInHeap.assign(2 * n, 0);
817 colors.assign(2 * n, WHITE);
818 preds.assign(2 * n, -1);
819 predEdge.assign(2 * n, Edge{-1,
nullptr, 0, 0});
823double Pathfinder::edgeWeight(
const Edge &e, std::optional<int> packetId,
824 const llvm::BitVector *avoid,
825 const llvm::BitVector *avoidBranch)
const {
826 const SwitchboxConnect::Cell &cell = e.sb->at(e.i, e.j);
832 if (e.sb->srcCoords == e.sb->dstCoords)
833 for (
const llvm::BitVector *flows : {avoid, avoidBranch})
834 if (flows && llvm::any_of(e.sb->unitFlows(e.j),
835 [&](
int f) { return flows->test(f); }))
836 w += conflictSharePenalty;
843void Pathfinder::dijkstraShortestPaths(
844 ArrayRef<int> seeds, ArrayRef<double> seedCosts,
845 std::optional<int> packetId,
const llvm::BitVector *avoid,
846 const llvm::DenseMap<int, llvm::BitVector> *branchAvoid,
847 const llvm::DenseSet<int> *stops, ArrayRef<int> targets,
848 llvm::function_ref<
bool(
int,
int)> mayCross) {
849 llvm::fill(distance, INF);
850 llvm::fill(colors,
static_cast<int8_t
>(WHITE));
851 llvm::fill(preds, -1);
852 llvm::fill(indexInHeap, uint64_t{0});
854 using MutableQueue = d_ary_heap_indirect<
856 std::vector<uint64_t> &,
857 std::vector<double> &,
859 MutableQueue Q(distance, indexInHeap);
861 for (
auto [seed, cost] : llvm::zip_equal(seeds, seedCosts)) {
862 distance[seed] = cost;
867 SmallVector<int, 8> unsettled(targets);
871 if (llvm::is_contained(unsettled, s)) {
872 llvm::erase(unsettled, s);
873 if (unsettled.empty())
876 if (stops && stops->count(s)) {
884 const bool sIsOut = (s & 1) == Out;
885 const llvm::BitVector *avoidBranch =
nullptr;
887 if (
auto it = branchAvoid->find(s); it != branchAvoid->end())
888 avoidBranch = &it->second;
889 ArrayRef<Edge> out = adjacency[stateNode(s)];
890 size_t wires = wireEdgesBegin[stateNode(s)];
891 const bool isIntra = !sIsOut;
893 sIsOut ? out.drop_front(wires) : out.take_front(wires)) {
894 if ((isIntra && !packetId && e.sb->packetOnlySrc[e.i]) ||
895 (isIntra && packetId && e.sb->circuitOnlyDst[e.j]))
897 int dst = stateId(e.dst, isIntra ? Out : In);
898 if (colors[dst] == BLACK || (isIntra && mayCross && !mayCross(s, dst)))
900 double w = edgeWeight(e, packetId, avoid, avoidBranch);
901 if (distance[s] + w >= distance[dst])
903 distance[dst] = distance[s] + w;
906 if (colors[dst] == WHITE) {
922 SmallVector<int, 4>
idsTo(
int flow,
int dstState)
const;
924 int splitPart(
int flow,
const std::set<int> &ids);
935 std::map<PathEndPoint, SmallVector<int, 2>>
partsOf;
949 std::vector<llvm::DenseMap<int, std::pair<int, Edge>>>
treeOf;
956 std::vector<SmallVector<std::pair<int, Edge>, 8>>
joinedHops;
967 std::tie(other.
at, other.
a, other.
b, other.
apart);
988 std::set<std::pair<int, int>>
met;
998 bool mayLeave(ArrayRef<int> masters,
int next)
const;
999 bool reorder(
int later,
int earlier,
bool again =
false);
1008 for (
auto [k, f] : llvm::enumerate(
parts))
1010 for (
auto [k, f] : llvm::enumerate(
parts))
1012 for (
const auto &[ends, ids] :
pf.packetIdsTo)
1013 flowIds[
partsOf.at(ends.first).front()].insert(ids.begin(), ids.end());
1018 for (
size_t k = 0, n =
parts.size(); k < n; k++) {
1019 if (!isPinned(parts[k]))
1021 const PathEndPoint &src = parts[k].src;
1022 const std::set<int> &prioritized = pf.priorityIds.at(src);
1023 std::set<PathEndPoint> treeDsts;
1024 std::map<int, std::set<PathEndPoint>> dstsOf;
1025 for (const auto &[ends, ids] : pf.packetIdsTo)
1026 if (ends.first == src)
1028 (prioritized.count(id) ? treeDsts : dstsOf[id]).insert(ends.second);
1029 std::set<int> others;
1030 for (const auto &[id, dsts] : dstsOf)
1031 if (!prioritized.count(id) && dsts != treeDsts)
1033 if (!others.empty())
1034 splitPart(k, others);
1036 for (
const auto &[src, tree] :
pf.constraints.pinned) {
1037 std::map<PathEndPoint, SmallVector<int, 4>> sets;
1038 for (const TreeHop &h : tree) {
1039 auto to = pf.nodeIds.find(h.to);
1040 if (h.from.coords == h.to.coords && to != pf.nodeIds.end())
1041 sets[h.from].push_back(stateId(to->second, Out));
1043 for (
auto &[_, set] : sets) {
1054 if (!again && (
reordered.count({earlier, later}) ||
1055 !
reordered.insert({later, earlier}).second))
1058 if (!llvm::is_contained(group, earlier))
1060 group.erase(llvm::find(group, later));
1061 group.insert(llvm::find(group, earlier), later);
1069 llvm::none_of(masters, [&](
int m) { return overlayMasters.count(m); }))
1071 return llvm::any_of(
overlaySets, [&](
const SmallVector<int, 4> &set) {
1072 return llvm::is_contained(set, next) && llvm::all_of(masters, [&](
int m) {
1073 return llvm::is_contained(set, m);
1087 int dstState)
const {
1088 SmallVector<int, 4> ids;
1090 pf.packetIdsTo.find({
parts[flow].src,
pf.nodes[stateNode(dstState)]});
1091 if (it !=
pf.packetIdsTo.end())
1092 for (
int id : it->second)
1101 for (
size_t a = 0; a <
parts.size(); a++)
1102 for (
size_t b = 0; b <
parts.size(); b++) {
1104 bool same = llvm::any_of(
flowIds[a],
1105 [&](
int id) {
return flowIds[b].count(
id); });
1108 if (a < b &&
parts[a].packetId &&
parts[b].packetId &&
1111 (
pf.constraints.conflict &&
1121 int part =
parts.size();
1124 llvm::dbgs() <<
"\t\tRouting ids";
1126 llvm::dbgs() <<
' ' << id;
1134 auto it =
pf.packetIdsTo.find({f.
src, p});
1135 return it !=
pf.packetIdsTo.end() &&
1136 llvm::any_of(it->second, [&](
int i) { return flowIds[k].count(i); });
1138 llvm::erase_if(f.
dsts,
1139 [&](
const PathEndPoint &p) { return !carries(part, p); });
1140 llvm::erase_if(
parts[flow].dsts,
1141 [&](
const PathEndPoint &p) {
return !carries(flow, p); });
1143 std::optional<int> &flowId =
parts[flow].packetId;
1144 if (flowId && ids.count(*flowId))
1145 flowId = *
flowIds[flow].begin();
1146 auto prioritized = [&](
const std::set<int> &carried) {
1147 auto prio =
pf.priorityIds.find(f.
src);
1148 return prio !=
pf.priorityIds.end() && llvm::any_of(carried, [&](
int id) {
1149 return prio->second.count(
id);
1153 parts[flow].isPriorityFlow = prioritized(
flowIds[flow]);
1162 for (
auto [a, b] : std::set<std::pair<int, int>>(
noJoin))
1163 if (a == flow || b == flow)
1164 noJoin.insert({a == flow ? part : a, b == flow ? part : b});
1169 for (
auto [at, a, b, apart] :
splitsOf[flow])
1170 if (ids.count(a) != ids.count(b) &&
1171 flowIds[flow].count(ids.count(a) ? b : a)) {
1184 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
1185 for (
size_t j = 0; j < sb.
dstPorts.size(); j++) {
1189 if (xbar == graph.end())
1195 for (
size_t row = 0; row < x.
srcPorts.size(); row++)
1202llvm::Error Pathfinder::explainNoRouting(
const RouteState &st)
const {
1208 return c.usedCapacity > maxCircuitStreamCapacity;
1211 auto link = [](
const SwitchboxConnect &sb) -> std::string {
1212 return llvm::formatv(
"from tile ({0}, {1}) to ({2}, {3})", sb.
srcCoords.col,
1215 const Flow *prioritized =
nullptr;
1216 llvm::SetVector<const SwitchboxConnect *> held;
1217 for (
auto [k, f] : llvm::enumerate(st.parts)) {
1218 if (!st.isPinned(f))
1220 prioritized = prioritized ? prioritized : &f;
1221 for (
const auto &[_, hop] : st.treeOf[k]) {
1222 if (overused(*hop.second.sb)) {
1223 return llvm::make_error<PinnedRoutingFailure>(
1225 ", and it holds a channel " + link(*hop.second.sb) +
1226 " the other flows need.");
1228 if (hop.second.sb->srcCoords != hop.second.sb->dstCoords)
1229 held.insert(hop.second.sb);
1232 if (prioritized && llvm::any_of(graph, [&](
const auto &entry) {
1233 return overused(entry.second);
1235 return llvm::make_error<PinnedRoutingFailure>(
1238 ", and the router found no routing for the other flows around the "
1239 "channels it holds " +
1241 llvm::map_range(llvm::make_pointee_range(held), link))) +
1245 if (!checkReason.empty())
1246 return llvm::make_error<RoutingFailure>(checkReason);
1250 const SwitchboxConnect *worst =
nullptr;
1251 int worstI = 0, worstJ = 0;
1252 std::pair<bool, int> worstRank{
false, 0};
1253 for (
const auto &[_, sb] : graph) {
1254 std::optional<bool> roomless;
1255 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
1256 for (
size_t j = 0; j < sb.
dstPorts.size(); j++) {
1260 roomless = !hasRoom(sb);
1262 if (rank > worstRank) {
1271 return llvm::make_error<RoutingFailure>(
"");
1273 std::vector<std::string> users;
1274 for (
const auto &[src, settings] : st.routing.settings) {
1275 auto it = settings.find(worst->
srcCoords);
1276 if (it == settings.end())
1278 const SwitchSetting &s = it->second;
1280 for (
size_t k = 0; k < s.
dsts.size() && !uses; k++)
1289 crossbar ? llvm::formatv(
"the connection from {0} to {1} at tile ({2}, "
1295 :
"the links " + link(*worst);
1297 return llvm::make_error<RoutingFailure>(
1298 "the router found no routing that fits " + where +
".");
1299 return llvm::make_error<RoutingFailure>(
1300 "the flows from " +
joinNames(users) +
" need " + where +
1301 ", and the router found no routing that fits them.");
1304llvm::Error Pathfinder::cutOverflow()
const {
1310 auto onDevice = [&](
TileID t) {
return t.col < columns && t.row < rows; };
1311 std::map<std::tuple<bool, int, bool>,
size_t> channels;
1312 for (
const auto &[ends, sb] : graph) {
1313 const auto &[a, b] = ends;
1314 auto xbar = graph.find({a, a});
1315 if (a == b || !onDevice(a) || !onDevice(b) || xbar == graph.end())
1317 bool rows = a.col == b.col;
1318 int below = rows ? std::min(a.row, b.row) : std::min(a.col, b.col);
1319 bool up = rows ? b.row > a.row : b.col > a.col;
1320 const SwitchboxConnect &x = xbar->second;
1324 channels[{rows, below, up}] += 0;
1326 int k = x.dstIndex(port);
1328 llvm::any_of(llvm::seq<size_t>(0, x.srcPorts.size()),
1329 [&](size_t i) { return x.at(i, k).available; }))
1330 channels[{rows, below, up}]++;
1333 for (
const auto &[cut, available] : channels) {
1334 bool rows = std::get<0>(cut), up = std::get<2>(cut);
1335 int below = std::get<1>(cut);
1336 auto low = [&](
TileID t) {
return (rows ? t.row : t.col) <= below; };
1337 std::vector<std::string> circuits;
1338 bool packets =
false;
1339 for (
const Flow &f : flows) {
1340 if (low(f.src.coords) != up ||
1341 llvm::none_of(f.dsts, [&](
const PathEndPoint &d) {
1342 return low(d.coords) != up;
1351 if (packets && constraints.apartAcross && available > circuits.size())
1352 apart = constraints.apartAcross(low, up, available - circuits.size());
1354 circuits.size() + std::max<size_t>(packets, apart.
flows.size());
1355 LLVM_DEBUG(llvm::dbgs() <<
"Cut " << (rows ?
"row " :
"column ") << below
1356 << (up ?
" up" :
" down") <<
": " << needed
1357 <<
" of " << available <<
" channels\n");
1358 if (needed <= available)
1360 int from = up ? below : below + 1, to = up ? below + 1 : below;
1361 auto channel = [](
size_t n) {
return n == 1 ?
"channel" :
"channels"; };
1363 circuits.empty() ?
"" :
"the circuit flows from " +
joinNames(circuits);
1364 if (apart.
flows.size() > 1)
1366 ", no two of which can share an arbiter,";
1369 circuits.empty() ?
"the packet flows" : who +
", with packet flows,";
1371 apart.
flows.size() > 1 ?
" For example, " + apart.
why :
"";
1372 return llvm::make_error<RoutingFailure>(llvm::formatv(
1373 "{0} cross from {1} {2} to {1} {3}, which takes {4} {5} across, and "
1374 "it has {6} {7}.{8}",
1375 who, rows ?
"row" :
"column", from, to, needed, channel(needed),
1376 available, channel(available), why));
1378 return llvm::Error::success();
1388 return stateId(
pf.nodeIds.at(p), Out);
1390 bool isPending(
int state)
const;
1391 void reach(ArrayRef<int> dsts);
1392 bool joinable(
int at, ArrayRef<int> dsts)
const;
1395 void meet(
int owner);
1396 void search(
const llvm::DenseSet<int> &drop, ArrayRef<int> targets,
1397 const llvm::DenseSet<int> &off = {});
1398 bool trace(
int state);
1399 llvm::DenseSet<int> splitOff(
int dst)
const;
1400 llvm::DenseSet<int> trunkOff(
int dst)
const;
1401 llvm::Error placePinned();
1405 bool join(
int state,
int predId,
const Edge &e);
1415 const std::vector<TreeHop> *
pinned =
nullptr;
1424 llvm::MapVector<int, std::pair<int, Edge>>
planned;
1439 llvm::DenseMap<int, SmallVector<int, 4>>
joins;
1455 SmallVector<std::pair<int, const SmallVector<int, 4> *>, 2>
joinedAt;
1468 int srcId =
pf.nodeIds.at(
part.src);
1469 tree.push_back(stateId(srcId, In));
1471 st.processedStamp[
tree.front()] = ++
st.curStamp;
1479 int self = stateId(srcId, Out);
1480 if (endPoint == part.src && !st.overlayMasters.count(self) &&
1481 llvm::any_of(pf.adjacency[srcId],
1482 [&](const Edge &e) { return e.dst == srcId; })) {
1489 for (
auto it = st.
trunked.lower_bound({flow, 0});
1493 SmallVector<int, 4> shared;
1495 if (llvm::is_contained(part.dsts, dst))
1496 shared.push_back(dstState(dst));
1497 if (shared.size() > 1)
1498 trunkDsts.insert(shared.begin(), shared.end());
1501 for (
const auto &[state, _] : st.
treeOf[other])
1502 if ((state & 1) == Out && pf.nodes[stateNode(state)].coords == at)
1503 partOff.insert(state);
1515 return llvm::is_contained(dsts,
dstState(p));
1523 llvm::all_of(dsts, [&](
int dst) {
return isPending(dst); });
1527 auto shared = [&](ArrayRef<int> ids,
int other) {
1528 std::set<int> common;
1530 if (
st.flowIds[other].count(
id))
1534 for (
int other :
st.sameId[
flow]) {
1536 const Flow &o =
st.parts[other];
1538 if (!
st.treeDsts[other].empty() && llvm::is_contained(o.
dsts, o.
src) &&
1541 for (
int dst :
st.treeDsts[other]) {
1542 std::set<int> common = shared(
st.idsTo(other, dst),
flow);
1551 !(
pf.constraints.conflict &&
1552 pf.constraints.conflict(
part.src, common, o.
src,
1553 st.flowIds[other])) &&
1554 !
st.noJoin.count({
flow, other}) &&
1555 (!
part.isPriorityFlow ||
st.parts[other].isPriorityFlow);
1560 for (
auto it =
st.treeOf[other].find(dst); it !=
st.treeOf[other].end();
1561 it =
st.treeOf[other].find(it->second.first)) {
1562 int up = it->second.first;
1563 if ((up & 1) != Out)
1568 if (!llvm::is_contained(
joins[up], dst))
1569 joins[up].push_back(dst);
1570 if (!llvm::is_contained(
joinOwners[up], other))
1579 if (!
part.packetId ||
part.isPriorityFlow)
1581 llvm::DenseSet<int> sameIdDsts;
1582 for (
int other :
st.sameId[
flow])
1586 for (
auto [other, o] : llvm::enumerate(
st.parts)) {
1588 if (k ==
flow || o.src ==
part.src || !o.packetId || o.isPriorityFlow ||
1589 st.treeDsts[k].empty() || llvm::is_contained(
st.sameId[
flow], k) ||
1590 st.conflicting[
flow].test(k) ||
st.noJoin.count({flow, k}))
1592 SmallVector<int, 4> dsts;
1593 for (
int dst :
st.treeDsts[k])
1595 !sameIdDsts.count(dst) &&
st.treeOf[k].count(dst))
1596 dsts.push_back(dst);
1597 if (dsts.size() > 1) {
1616 auto spans = [&](
int k, ArrayRef<int> dsts) {
1617 return llvm::any_of(
st.flowIds[k], [&](
int id) {
1618 return llvm::count_if(dsts, [&](int dst) {
1619 return llvm::is_contained(st.idsTo(k, dst), id);
1623 auto mustMeet = [&](
int other) {
1624 return spans(flow, sharedDsts[other]) && spans(other, sharedDsts[other]);
1626 SmallVector<int, 4> owners;
1627 for (
int other :
llvm::make_first_range(sharedDsts))
1628 if (!disagree.count(other) && mustMeet(other))
1629 owners.push_back(other);
1630 llvm::stable_sort(owners, [&](
int a,
int b) {
1631 return sharedDsts[a].size() > sharedDsts[b].size();
1633 for (
int owner : owners) {
1634 llvm::erase_if(sharedDsts[owner], [&](
int dst) {
return !isPending(dst); });
1635 if (mustMeet(owner))
1641 st.unmet.insert({
flow, owner});
1643 bool other =
foreign.count(owner);
1644 const auto &t =
st.treeOf[owner];
1645 const Flow &o =
st.parts[owner];
1646 int root = stateId(
pf.nodeIds.at(o.
src), In);
1648 SmallVector<SmallVector<int, 16>, 4> paths;
1650 SmallVector<int, 16> &path = paths.emplace_back(1, dst);
1651 for (
auto it = t.find(dst); it != t.end(); it = t.find(it->second.first))
1652 path.push_back(it->second.first);
1653 if (path.size() == 1)
1654 path.push_back(root);
1655 if (path.back() != root)
1657 std::reverse(path.begin(), path.end());
1660 while (llvm::all_of(paths, [&](
const auto &path) {
1661 return trunk + 1 < path.size() && path[trunk] == paths[0][trunk];
1666 SmallVector<std::pair<int, Edge>, 4> hops;
1670 SmallVector<Meeting, 8> meetings;
1671 SmallVector<int, 8> targets;
1673 auto below = [&](
size_t k) {
1674 llvm::MapVector<int, std::pair<int, Edge>> hops;
1675 for (
const auto &path : paths)
1676 for (
size_t l = k + 1; l + 1 < path.size(); l++)
1677 hops.insert({path[l + 1], t.find(path[l + 1])->second});
1680 for (
size_t k = 0; k < trunk; k++) {
1681 int at = paths[0][k];
1684 SetVector<int> next;
1685 for (
const auto &path : paths)
1686 next.insert(path[k + 1]);
1687 if (!other && llvm::any_of(next, [&](
int s) {
1691 auto taken = [&](
int s) {
1692 return st.processedStamp[s] ==
st.curStamp ||
stops.count(s) ||
1695 if (other && (llvm::any_of(next, taken) ||
1696 llvm::any_of(below(k), [&](
const auto &hop) {
1697 const Edge &e = hop.second.second;
1698 return taken(hop.first) ||
1703 SmallVector<int, 4> leaving;
1704 if (llvm::any_of(next, [&](
int s) {
1705 bool ok =
st.mayLeave(leaving, s);
1706 leaving.push_back(s);
1710 TileID tile =
pf.nodes[stateNode(at)].coords;
1711 auto sb =
pf.graph.find({tile, tile});
1712 if (sb ==
pf.graph.end())
1715 auto node =
pf.nodeIds.find({tile, port});
1716 if (node ==
pf.nodeIds.end())
1718 int in = stateId(node->second, In);
1719 if (in == at || t.count(in))
1721 Meeting m{in, {}, 0, k};
1723 for (
const Edge &e :
pf.adjacency[node->second])
1724 if (e.dst == stateNode(s) && e.sb == &sb->second &&
1726 m.hops.push_back({s, e});
1727 m.cost +=
pf.edgeWeight(e,
part.packetId,
avoid,
nullptr);
1730 if (m.hops.size() == next.size()) {
1731 meetings.push_back(std::move(m));
1732 targets.push_back(in);
1736 if (meetings.empty())
1738 llvm::DenseSet<int> off;
1740 for (
const auto &[s, _] : below(0))
1742 search({}, targets, off);
1743 const Meeting *best =
nullptr;
1744 for (
const Meeting &m : meetings)
1745 if (
pf.distance[m.in] < INF &&
1747 pf.distance[m.in] + m.cost <
pf.distance[best->in] + best->cost))
1749 if (!best || !
trace(best->in))
1751 st.unmet.erase({
flow, owner});
1752 st.met.insert({
flow, owner});
1754 auto add = [&](
int s,
int from,
const Edge &e,
int depth) {
1755 planned.insert({s, {from, e}});
1757 st.processedStamp[s] =
st.curStamp;
1761 for (
const auto &[s, e] : best->hops) {
1762 add(s, best->in, e, hops);
1764 st.joinedHops[
flow].push_back({owner, e});
1772 const SmallVector<int, 4> &dsts =
joins[s];
1779 for (
const auto &[s, hop] : below(best->at)) {
1780 const auto &[from, e] = hop;
1782 st.joinedHops[
flow].push_back({owner, e});
1793 ArrayRef<int> targets,
1794 const llvm::DenseSet<int> &off) {
1796 llvm::DenseMap<int, llvm::BitVector> onArbiter;
1798 for (
const auto &[state, hop] :
planned) {
1799 const auto &[from, e] = hop;
1800 if (e.sb->srcCoords != e.sb->dstCoords || drop.count(state))
1802 llvm::BitVector &away =
1803 branchAvoid.try_emplace(from,
st.conflicting.size()).first->second;
1804 llvm::BitVector &on =
1805 onArbiter.try_emplace(from,
st.conflicting.size()).first->second;
1806 for (
int other : e.sb->unitFlows(e.j)) {
1809 away |=
st.conflicting[other];
1815 away.reset(onArbiter.find(from)->second);
1820 for (
auto [state, hops] : llvm::zip_equal(
tree,
treeHops))
1821 if (!drop.count(state) && !off.count(state)) {
1822 seeds.push_back(state);
1823 seedCosts.push_back(treeSeedFactor * hops);
1825 llvm::DenseSet<int> blocked;
1828 blocked.insert(off.begin(), off.end());
1830 llvm::DenseMap<int, SmallVector<int, 4>> masters;
1831 bool limited = !
st.overlayMasters.empty();
1833 for (
const auto &[state, hop] :
planned)
1834 if ((hop.first & 1) == In && !drop.count(state))
1835 masters[hop.first].push_back(state);
1836 auto mayCross = [&](
int from,
int to) {
1837 auto it = masters.find(from);
1838 return st.mayLeave(it == masters.end() ? ArrayRef<int>{} : it->second, to);
1840 pf.dijkstraShortestPaths(
1842 off.empty() ? &
stops : &blocked, targets,
1843 limited ? llvm::function_ref<
bool(
int,
int)>(mayCross) :
nullptr);
1848 size_t grown =
tree.size();
1849 while (
st.processedStamp[state] !=
st.curStamp) {
1852 int pred =
pf.preds[state];
1855 planned.insert({state, {pred,
pf.predEdge[state]}});
1857 st.processedStamp[state] =
st.curStamp;
1858 tree.push_back(state);
1863 static_cast<int>(
tree.size() - grown);
1874 llvm::DenseSet<int> off =
partOff;
1875 auto carries = [&](
int d,
int id) {
1876 return llvm::is_contained(
st.idsTo(
flow, d),
id);
1878 auto only = [&](
int d,
int id,
int other) {
1879 return carries(d,
id) && !carries(d, other);
1881 for (
auto [at, a, b, apart] :
st.splitsOf[
flow])
1883 bool splits = !apart ? (only(dst, a, b) && only(other, b, a)) ||
1884 (only(dst, b, a) && only(other, a, b))
1885 : (only(dst, a, b) && other == *apart) ||
1886 (dst == *apart && only(other, a, b));
1889 SmallVector<int, 8> below;
1891 auto entersAt = [&, tile = at](
int state) {
1892 return (state & 1) == In &&
pf.nodes[stateNode(state)].coords == tile;
1894 for (
auto *hop =
planned.find(s); !entersAt(s) && hop !=
planned.end();
1897 s = hop->second.first;
1901 off.insert(below.begin(), below.end());
1911 llvm::DenseSet<int> off;
1914 int root =
tree.front();
1915 auto exitOf = [&](
int s) {
1916 for (
const auto *it =
planned.find(s);
1917 it !=
planned.end() && it->second.first != root; it =
planned.find(s))
1918 s = it->second.first;
1921 const auto *other = llvm::find_if(
reached, [&](
int d) {
1926 int exit = exitOf(*other);
1928 if (s == root || exitOf(s) != exit)
1935 for (
const auto &[from, to, joined] : *
pinned) {
1936 if (
toSelf && from == to)
1938 bool intra = from.coords == to.coords;
1939 auto fromId =
pf.nodeIds.find(from), toId =
pf.nodeIds.find(to);
1940 const Edge *e =
nullptr;
1941 if (fromId !=
pf.nodeIds.end() && toId !=
pf.nodeIds.end())
1942 for (
const Edge &out :
pf.adjacency[fromId->second])
1943 if (out.dst == toId->second &&
1947 return llvm::make_error<RoutingFailure>(
1948 "the route packet flows from " +
1950 " take alone does not fit this design: it goes from " +
1953 ", which the design's own switchboxes leave no connection for.",
1955 std::pair<int, std::pair<int, Edge>> hop{
1956 stateId(toId->second, intra ? Out : In),
1957 {stateId(fromId->second, intra ? In : Out), *e}};
1972 return llvm::Error::success();
1987 SmallVector<int, 8> targets;
1990 for (
const auto &[at, dsts] :
joins)
1992 targets.push_back(at);
1999 : llvm::min_element(
2005 llvm::DenseSet<int> off =
splitOff(currId);
2006 llvm::DenseSet<int> trunk =
trunkOff(currId);
2007 off.insert(trunk.begin(), trunk.end());
2009 search({}, targets, off);
2010 if (
pf.distance[currId] == INF)
2013 const SmallVector<int, 4> *joined =
nullptr;
2014 for (
const auto &[at, dsts] :
joins)
2015 if (
pf.distance[at] <
pf.distance[currId] &&
joinable(at, dsts)) {
2021 joinedAt.push_back({currId, joined});
2029 return llvm::make_error<RoutingFailure>(
2030 "no path leads from " +
2033 " through the connections the switchboxes allow and existing "
2034 "routing leaves free.",
2041 return llvm::Error::success();
2049 llvm::DenseSet<int> branch;
2051 for (
int below = 0; top !=
tree.front() &&
children.lookup(top) == below;
2054 top =
planned.find(top)->second.first;
2059 off.insert(trunk.begin(), trunk.end());
2060 search(branch, dst, off);
2063 for (
int s = dst; s != top;) {
2064 const auto &[from, e] =
planned.find(s)->second;
2068 away ==
branchAvoid.end() ?
nullptr : &away->second);
2071 if (
pf.distance[dst] + rerouteMinSaving >= cost)
2073 for (
int s : branch) {
2074 st.processedStamp[s] = 0;
2079 for (
size_t k =
tree.size(); k-- > 0;)
2080 if (branch.count(
tree[k])) {
2084 [[maybe_unused]]
bool traced =
trace(dst);
2085 assert(traced &&
"a rerouted branch reaches its destination");
2095 st.routing.packetTrees[
part.src].push_back({
part.src,
part.src,
false});
2097 const std::optional<int> &packetId =
part.packetId;
2098 int packetGroupId =
part.packetGroupId;
2099 llvm::DenseMap<int, std::pair<SwitchboxConnect *, int>> branchPort;
2100 for (
const auto &[currId, hop] :
planned) {
2101 const auto &[predId, e] = hop;
2109 st.treeOf[
flow].try_emplace(currId, predId, e);
2111 st.circuitHops[
flow].push_back(e);
2123 auto mergeable = [&] {
2127 std::set<PathEndPoint> dsts;
2129 if (
auto ids =
pf.packetIdsTo.find({f.src, dst});
2130 ids !=
pf.packetIdsTo.end() &&
2131 llvm::is_contained(ids->second, *packetId))
2137 bool sameGroupUnseen =
2138 packetGroupId >= 0 && packetId.has_value() &&
2140 (seen == cell.
packetIds.end() || seen->second ==
flow || mergeable());
2141 if (sameGroupUnseen) {
2142 int packetIdValue = *packetId;
2148 for (
size_t k = 0; k < sb.
srcPorts.size(); k++)
2150 for (
size_t l = 0; l < sb.
dstPorts.size(); l++)
2151 if (l !=
static_cast<size_t>(j))
2169 auto [it,
first] = branchPort.try_emplace(predId, &sb, j);
2178 for (
const auto &[currId, hop] :
planned)
2179 st.routing.packetTrees[
part.src].push_back(
2180 {
pf.nodes[stateNode(hop.first)],
pf.nodes[stateNode(currId)],
false});
2186 if (!
st.treeOf[
flow].try_emplace(state, predId, e).second)
2190 st.routing.packetTrees[
part.src].push_back({pred, curr,
true});
2200 for (
const auto &[joinAt, joined] :
joinedAt) {
2202 st.joinedHops[
flow].push_back(
2203 {other,
planned.find(joinAt)->second.second});
2204 for (
int dst : *joined) {
2205 const auto &t =
st.treeOf[other];
2206 SmallVector<std::pair<int, std::pair<int, Edge>>, 8> path;
2207 auto it = t.find(dst);
2208 for (; it != t.end() && it->first != joinAt;
2209 it = t.find(it->second.first))
2210 path.push_back(*it);
2213 for (
const auto &[state, hop] : path)
2214 if (
join(state, hop.first, hop.second))
2215 st.joinedHops[
flow].push_back({other, hop.second});
2218 st.treeDsts[
flow].append(joined->begin(), joined->end());
2221 join(state, hop.first, hop.second);
2228 if (
st.partsOf.at(src).size() == 1) {
2232 for (
int id :
st.flowIds[
flow])
2236 for (
auto [in, out] : llvm::zip(setting.srcs, setting.dsts))
2237 if (!llvm::is_contained(llvm::zip(all.
srcs, all.
dsts),
2238 std::make_tuple(in, out))) {
2239 all.
srcs.push_back(in);
2240 all.
dsts.push_back(out);
2245llvm::Error Pathfinder::routePart(
RouteState &st,
int flow) {
2246 std::optional<TreeBuilder> tree;
2247 tree.emplace(*
this, st, flow);
2248 llvm::Error err = tree->pinned ? tree->placePinned() : tree->grow();
2253 for (
auto *pairs : {&st.
unmet, &st.
met})
2254 pairs->erase(pairs->lower_bound({flow, 0}),
2255 pairs->lower_bound({flow + 1, 0}));
2257 tree.emplace(*
this, st, flow);
2259 if (llvm::Error retry = tree->grow()) {
2260 llvm::consumeError(std::move(retry));
2263 llvm::consumeError(std::move(err));
2268 if (!tree->pinned && tree->reached.size() + tree->joinedAt.size() > 1)
2273 return llvm::Error::success();
2276int Pathfinder::applyRoutingFaults(
RouteState &st,
2277 const RoutingFaults &faults) {
2278 int illegalEdges = 0;
2279 for (
const TreeSplit &split : faults.
splits) {
2280 auto src = st.partsOf.find(split.
src);
2281 if (src == st.partsOf.end())
2283 auto partWith = [&](
int id) {
2284 for (
int k : src->second)
2285 if (st.flowIds[k].count(
id))
2289 int flow = partWith(split.
a), other = partWith(split.
b);
2290 if (flow < 0 || other < 0)
2292 std::optional<int> apart;
2294 auto it = nodeIds.find(*split.
apart);
2295 if (it == nodeIds.end())
2297 apart = stateId(it->second, Out);
2300 auto inseparable = [&] {
2301 const Flow &f = st.parts[flow];
2302 for (
const PathEndPoint &dst : f.
dsts) {
2303 auto ids = packetIdsTo.find({f.
src, dst});
2304 if (ids == packetIdsTo.end() ||
2305 !llvm::is_contained(ids->second, split.
a) ||
2306 !llvm::is_contained(ids->second, split.
b))
2308 const auto &tree = st.treeOf[flow];
2309 auto it = tree.find(stateId(nodeIds.at(dst), Out));
2310 if (it == tree.end() && dst == f.
src && dst.
coords == split.
at)
2312 for (; it != tree.end(); it = tree.find(it->second.first)) {
2313 int up = it->second.first;
2314 if ((up & 1) == In && nodes[stateNode(up)].coords == split.
at)
2320 if (idsApart && flow == other && !split.
apart &&
2321 !st.parts[flow].isPriorityFlow && inseparable())
2322 other = st.splitPart(flow, {split.
b});
2323 if (flow != other) {
2324 if (st.partSplits[flow].insert({split.at, other}).second)
2326 st.partSplits[other].insert({split.
at, flow});
2327 }
else if (st.splitsOf[flow]
2328 .insert({split.at, split.a, split.b, apart})
2330 LLVM_DEBUG(llvm::dbgs()
2331 <<
"Split ids " << split.
a <<
" and " << split.
b <<
" from "
2333 <<
" at tile (" << split.
at.col <<
", " << split.
at.row
2338 crowdedTiles.insert(faults.
crowded.begin(), faults.
crowded.end());
2341 auto splitOwnIds = [&](
const std::pair<PathEndPoint, PathEndPoint> &pair) {
2342 for (
auto [src, other] : {pair, std::pair{pair.second, pair.first}}) {
2343 auto srcParts = st.partsOf.find(src);
2344 auto otherParts = st.partsOf.find(other);
2345 if (srcParts == st.partsOf.end() || otherParts == st.partsOf.end())
2347 std::set<int> otherIds;
2348 for (
int k : otherParts->second)
2349 otherIds.insert(st.flowIds[k].begin(), st.flowIds[k].end());
2350 for (
int k : SmallVector<int, 2>(srcParts->second)) {
2352 for (
int id : st.flowIds[k])
2353 if (!otherIds.count(
id))
2355 if (!st.parts[k].isPriorityFlow && !own.empty() &&
2356 own.size() < st.flowIds[k].size())
2357 st.splitPart(k, own);
2361 bool related =
false;
2362 for (
const auto &pair : faults.
apart) {
2363 if (!st.apartSources.insert(pair).second)
2366 LLVM_DEBUG(llvm::dbgs()
2375 for (
const auto &pair : faults.
together) {
2376 if (!st.togetherSources.insert(pair).second)
2379 LLVM_DEBUG(llvm::dbgs()
2380 <<
"Route together: "
2391 for (
const PathEndPoint &src : faults.
untrunk)
2392 if (st.untrunked.insert(src).second) {
2393 LLVM_DEBUG(llvm::dbgs()
2398 std::set<std::pair<int, int>> faulted;
2399 bool unjoined =
false;
2400 for (
const auto &[tile, conn] : faults.
connections) {
2402 auto it = graph.find({tile, tile});
2403 if (it == graph.end())
2405 SwitchboxConnect &sb = it->second;
2411 bool joined =
false;
2412 for (
auto [flow, hops] : llvm::enumerate(st.joinedHops))
2413 for (
const auto &[other, e] : hops)
2414 if (e.sb == &sb && sb.
srcPorts[e.i] == conn.src &&
2416 faulted.insert({
static_cast<int>(flow), other});
2419 unjoined |= !joined;
2423 for (
auto [flow, other] : faulted)
2424 if (unjoined && st.spared.insert({flow, other}).second)
2426 else if (st.met.count({flow, other}) &&
2427 ++st.meetFaults[{std::min(flow, other), std::max(flow, other)}] <=
2429 st.reorder(flow, other,
true);
2431 st.noJoin.insert({flow, other});
2432 return illegalEdges;
2442 LLVM_DEBUG(llvm::dbgs() <<
"\t---Begin Pathfinder::findPaths---\n");
2443 checkReason.clear();
2444 packetsFailed =
true;
2448 buildRoutingGraph();
2449 if (llvm::Error err = cutOverflow()) {
2450 packetsFailed =
false;
2451 return std::move(err);
2453 crowdedTiles.clear();
2455 for (
auto &[_, sb] : graph) {
2457 for (
auto [j, port] : llvm::enumerate(sb.
dstPorts))
2459 port.channel >= packetFanoutCap &&
2470 int iterationCount = -1;
2471 int illegalEdges = 0;
2472 std::optional<Routing> usable;
2474 int fewestIllegal = std::numeric_limits<int>::max(), sinceFewest = 0;
2476 int reorderedStall = 0;
2477 int overlayFaults = 0;
2479 if (usable && (iterationCount + 1 >= maxIterations ||
2480 iterationCount - usableAt >= maxSteers)) {
2481 LLVM_DEBUG(llvm::dbgs() <<
"\t\tKeeping the usable routing\n");
2482 return std::move(*usable);
2487 if (++iterationCount >= maxIterations ||
2488 (fewestIllegal > 0 && sinceFewest >= maxOverusedStall) ||
2489 reorderedStall >= maxReorderedStall) {
2490 LLVM_DEBUG(llvm::dbgs()
2491 <<
"\t\tPathfinder: giving up after " << iterationCount
2493 << (iterationCount >= maxIterations ?
"the iteration limit"
2494 : reorderedStall >= maxReorderedStall
2495 ?
"circuit trees routed first stopped helping"
2496 :
"the overuse stalled")
2497 <<
")...unable to find routing for flows.\n");
2499 !checkReason.empty() || llvm::any_of(graph, [](
const auto &entry) {
2500 ArrayRef<SwitchboxConnect::Cell> cells = entry.second.cells;
2501 return llvm::any_of(cells,
2509 return explainNoRouting(st);
2512 LLVM_DEBUG(llvm::dbgs() <<
"\t\t---Begin findPaths iteration #"
2513 << iterationCount <<
"---\n");
2515 for (
auto &[_, sb] : graph) {
2522 for (
auto &[_, sb] : graph) {
2531 for (
auto &tree : st.
treeOf)
2546 for (
int flow : group)
2547 if (llvm::Error err = routePart(st, flow))
2548 return std::move(err);
2549 for (
auto &[_, sb] : graph) {
2564 for (
auto [later, earlier] : st.
unmet) {
2565 bool steered = st.
reorder(later, earlier);
2566 steered |= st.
trunked.insert({later, earlier}).second;
2567 steered |= st.
trunked.insert({earlier, later}).second;
2572 for (
auto &[_, sb] : graph) {
2573 for (
size_t i = 0; i < sb.
srcPorts.size(); i++) {
2574 for (
size_t j = 0; j < sb.
dstPorts.size(); j++) {
2583 LLVM_DEBUG(llvm::dbgs()
2584 <<
"\t\t\tToo much capacity on (" << sb.
srcCoords.col
2589 << sb.
dstPorts[j].channel <<
", used_capacity = "
2591 <<
", over_capacity_count = " << c.
overCapacity <<
"\n");
2600 if (illegalEdges < fewestIllegal) {
2601 fewestIllegal = illegalEdges;
2602 sinceFewest = reorderedStall = 0;
2606 if (illegalEdges && sinceFewest >= stallIterations) {
2607 auto losing = [&](
int flow) {
2608 return !st.
parts[flow].packetId &&
2609 llvm::any_of(st.
circuitHops[flow], [](
const Edge &e) {
2610 const SwitchboxConnect::Cell &c = e.sb->at(e.i, e.j);
2611 return c.usedCapacity > maxCircuitStreamCapacity &&
2612 c.packetGroupId < 0;
2617 moved |= llvm::any_of(group, losing);
2618 std::stable_partition(group.begin(), group.end(), losing);
2620 reorderedStall += moved;
2625 if (sinceFewest >= stallIterations && sinceFewest % stallIterations == 0) {
2626 LLVM_DEBUG(llvm::dbgs() <<
"Drop the id splits after " << sinceFewest
2627 <<
" stalled iterations\n");
2637 llvm::Error unmoved =
2638 illegalEdges != 0 || !constraints.check
2639 ? llvm::Error::success()
2640 : llvm::handleErrors(
2641 constraints.check(st.
routing),
2642 [&](std::unique_ptr<RoutingFailure> rejected) -> llvm::Error {
2643 LLVM_DEBUG(llvm::dbgs() <<
"Routing rejected: "
2644 << rejected->reason <<
'\n');
2645 illegalEdges += applyRoutingFaults(st, rejected->faults);
2646 overlayFaults = rejected->faults.onlyOverlayMasters
2649 stuck = illegalEdges == 0;
2650 if (rejected->isA(DeadlockProneRouting::classID())) {
2652 usable = st.routing;
2653 usableAt = iterationCount;
2655 return llvm::Error::success();
2658 return llvm::Error(std::move(rejected));
2659 checkReason = std::move(rejected->reason);
2660 return llvm::Error::success();
2665 llvm::dbgs() <<
"\t\t\tFlow starting at (" << src.
coords.col <<
","
2666 << src.
coords.row <<
"):\t" << settings;
2668 int totalPathLength = 0;
2669 for (
const auto &[_, sb] : graph)
2673 llvm::dbgs() <<
"\t\t---End findPaths iteration #" << iterationCount
2674 <<
" , illegal edges count = " << illegalEdges
2675 <<
", total path length = " << totalPathLength <<
"---\n";
2677 if (stuck && usable) {
2678 llvm::consumeError(std::move(unmoved));
2679 return std::move(*usable);
2682 return std::move(unmoved);
2683 if (overlayFaults >= maxOverlayFaults) {
2684 LLVM_DEBUG(llvm::dbgs() <<
"\t\tPathfinder: the routing check rejects "
2685 "only master sets of the prioritized flows ("
2686 << overlayFaults <<
" routings in a row)\n");
2688 return std::move(*usable);
2689 packetsFailed =
false;
2690 return explainNoRouting(st);
2693 }
while (illegalEdges > 0);
2695 LLVM_DEBUG(llvm::dbgs() <<
"\t---End Pathfinder::findPaths---\n");
2696 return std::move(st.routing);
virtual uint32_t getNumSourceShimMuxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a shimmux.
virtual bool isLegalTileConnection(int col, int row, WireBundle srcBundle, int srcChan, WireBundle dstBundle, int dstChan) const =0
bool isShimNOCorPLTile(int col, int row) const
Return true if the given tile is either a ShimNOC or ShimPL tile.
virtual uint32_t getNumDestShimMuxConnections(int col, int row, WireBundle bundle) const =0
Return the number of destinations of connections inside a shimmux.
virtual uint32_t getNumDestSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of destinations of connections inside a switchbox.
virtual uint32_t getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
ShimMuxOp getShimMux(mlir::OpBuilder &builder, int col)
SwitchboxOp getSwitchbox(mlir::OpBuilder &builder, int col, int row)
SwitchboxOp lookupSwitchbox(TileID tile) const
TileOp getTile(mlir::OpBuilder &builder, int col, int row)
llvm::Error runAnalysis(DeviceOp &device)
ShimMuxOp lookupShimMux(int col) const
llvm::Expected< Routing > findPaths(int maxIterations)
void excludeTile(TileID coords)
void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, Port dstPort, std::optional< int > packetId, bool isPriorityFlow, std::optional< mlir::Location > loc=std::nullopt)
void initialize(int maxCol, int maxRow, const AIETargetModel &targetModel)
mlir::LogicalResult addFixedConnection(SwitchboxOp switchboxOp)
void log(llvm::raw_ostream &os) const override
std::string joinNames(llvm::ArrayRef< std::string > names, size_t shown=4)
Lists names as "A, B and C", the first shown of them followed by "N more" if there are more.
int shimMuxChannelTo(Port dst)
std::string describePrioritized(llvm::ArrayRef< std::string > sources)
Says that the packet flows from sources keep the route they take alone, as a control-packet reload of...
struct TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")"; return os; } friend std::string to_string(const TileID &s) { std::ostringstream ss; ss<< s; return ss.str(); } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const TileID &s) { os<< to_string(s); return os; } inline bool operator<(const TileID &rhs) const { return std::tie(col, row)< std::tie(rhs.col, rhs.row); } bool operator==(const TileID &rhs) const { return std::tie(col, row)==std::tie(rhs.col, rhs.row); } bool operator!=(const TileID &rhs) const { return !(*this==rhs);} int col, row;} TileID
bool isDirectional(WireBundle bundle)
int shimMuxChannelFrom(Port src)
std::string describeTilePort(TileID tile, Port port)
Names a tile port the way diagnostics do, e.g. "(0, 2) DMA:1".
struct Port { WireBundle bundle; int channel; bool operator==(const Port &rhs) const { return std::tie(bundle, channel)==std::tie(rhs.bundle, rhs.channel); } bool operator!=(const Port &rhs) const { return !(*this==rhs);} bool operator<(const Port &rhs) const { return std::tie(bundle, channel)< std::tie(rhs.bundle, rhs.channel); } friend std::ostream &operator<<(std::ostream &os, const Port &port) { os<< "("; os<< stringifyWireBundle(port.bundle).str(); os<< ": "<< std::to_string(port.channel)<< ")"; return os; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Port &port) { os<< to_string(port); return os; } } Port
std::map< TileID, SwitchSetting > SwitchSettings
std::string describePort(Port port)
Names a port the way diagnostics do, e.g. "DMA:1".
WireBundle getConnectingBundle(WireBundle dir)
bool operator<(const IdSplit &other) const
std::optional< int > apart
std::set< std::pair< int, int > > noJoin
std::set< PathEndPoint > untrunked
std::vector< llvm::DenseMap< int, std::pair< int, Edge > > > treeOf
std::map< std::pair< int, int >, int > meetFaults
std::vector< SmallVector< Edge, 8 > > circuitHops
std::set< std::pair< int, int > > reordered
llvm::DenseSet< int > overlayMasters
std::vector< std::set< std::pair< TileID, int > > > partSplits
std::vector< uint32_t > processedStamp
std::vector< SmallVector< std::pair< int, Edge >, 8 > > joinedHops
std::set< std::pair< int, int > > spared
std::vector< llvm::BitVector > conflicting
std::vector< SmallVector< int, 4 > > treeDsts
std::vector< std::set< IdSplit > > splitsOf
std::set< std::pair< int, int > > met
std::vector< Flow > parts
RouteState(const Pathfinder &pf)
std::set< std::pair< PathEndPoint, PathEndPoint > > togetherSources
bool reorder(int later, int earlier, bool again=false)
std::vector< SmallVector< int, 4 > > overlaySets
std::vector< std::set< int > > flowIds
llvm::MapVector< int, SmallVector< int, 8 > > groupedFlows
bool isPinned(const Flow &f) const
std::set< std::pair< PathEndPoint, PathEndPoint > > apartSources
std::set< std::pair< int, int > > unmet
bool mayLeave(ArrayRef< int > masters, int next) const
std::set< std::pair< int, int > > trunked
int splitPart(int flow, const std::set< int > &ids)
std::vector< SmallVector< int, 4 > > sameId
SmallVector< int, 4 > idsTo(int flow, int dstState) const
std::map< PathEndPoint, SmallVector< int, 2 > > partsOf
int groupOf(const Flow &f) const
TreeBuilder(Pathfinder &pf, RouteState &st, int flow)
llvm::MapVector< int, SmallVector< int, 4 > > sharedDsts
bool isPending(int state) const
SmallVector< PathEndPoint, 4 > pending
SwitchSettings switchSettings
SmallVector< int, 4 > pinnedJoinDsts
llvm::DenseSet< int > foreign
llvm::DenseSet< int > stops
llvm::DenseSet< int > trunkOff(int dst) const
SmallVector< int, 16 > treeHops
SmallVector< int, 16 > seeds
SmallVector< int, 4 > met
void reach(ArrayRef< int > dsts)
void search(const llvm::DenseSet< int > &drop, ArrayRef< int > targets, const llvm::DenseSet< int > &off={})
const std::vector< TreeHop > * pinned
SmallVector< double, 16 > seedCosts
llvm::DenseSet< int > unjoinable
std::optional< PathEndPoint > first
SmallVector< std::pair< int, std::pair< int, Edge > >, 8 > pinnedJoins
int dstState(const PathEndPoint &p) const
llvm::MapVector< int, std::pair< int, Edge > > planned
SmallVector< int, 16 > tree
llvm::DenseSet< int > partOff
llvm::DenseMap< int, SmallVector< int, 2 > > joinOwners
SmallVector< int, 4 > reached
llvm::DenseMap< int, llvm::BitVector > branchAvoid
llvm::DenseSet< int > disagree
llvm::Error placePinned()
bool joinable(int at, ArrayRef< int > dsts) const
SmallVector< std::pair< int, const SmallVector< int, 4 > * >, 2 > joinedAt
bool join(int state, int predId, const Edge &e)
llvm::DenseSet< int > splitOff(int dst) const
llvm::DenseSet< int > trunkDsts
const llvm::BitVector * avoid
llvm::DenseMap< int, SmallVector< int, 4 > > joins
llvm::DenseMap< int, int > children
std::vector< PathEndPoint > dsts
std::optional< int > packetId
std::vector< std::string > flows
std::vector< std::pair< TileID, Connect > > connections
std::vector< PathEndPoint > untrunk
std::vector< std::pair< PathEndPoint, PathEndPoint > > together
std::vector< TreeSplit > splits
std::vector< TileID > crowded
std::vector< std::pair< PathEndPoint, PathEndPoint > > apart
std::map< PathEndPoint, SwitchSettings > settings
llvm::SmallDenseMap< int, int, 2 > packetIds
std::vector< Port > dstPorts
std::vector< Port > srcPorts
int srcIndex(Port p) const
static void bumpDemand(Cell &c)
void addToUnit(int j, int flow)
llvm::BitVector circuitOnlyDst
Cell & at(size_t i, size_t j)
std::vector< Cell > cells
int dstIndex(Port p) const
void joinUnits(int j, int k)
std::optional< PathEndPoint > apart