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;
44constexpr double avoidedPortDemand = 100.0;
46constexpr int routingCheckPenalty = 5;
48constexpr int maxSteers = 16;
52constexpr int maxPriorityFaults = 16;
55constexpr int maxMeetFaults = 4;
57constexpr double conflictSharePenalty = 4;
61constexpr int packetFanoutCap = 2;
66constexpr double treeSeedFactor = 0.9;
68constexpr double rerouteMinSaving = 1e-6;
71constexpr int stallIterations = 50;
74constexpr int maxReorderedStall = 300;
78constexpr int maxOverusedStall = 400;
81constexpr int stallHistory = 4;
86 c.demand = (demandBase + overCapacityCoeff * c.overCapacity) *
87 (demandBase + usedCapacityCoeff * c.usedCapacity) +
101 os <<
"Unable to find a legal routing";
107 LLVM_DEBUG(llvm::dbgs() <<
"\t---Begin DynamicTileAnalysis Constructor---\n");
109 maxCol = device.getTargetModel().columns();
110 maxRow = device.getTargetModel().rows();
112 pathfinder.initialize(maxCol, maxRow, device.getTargetModel());
120 for (PacketFlowOp pktFlowOp : device.getOps<PacketFlowOp>()) {
121 Region &r = pktFlowOp.getPorts();
122 Block &b = r.front();
123 SmallVector<std::pair<TileID, Port>, 4> sources;
125 for (Operation &Op : b.getOperations()) {
126 if (
auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
127 auto srcTile = cast<TileOp>(pktSource.getTile().getDefiningOp());
129 {{srcTile.colIndex(), srcTile.rowIndex()}, pktSource.port()});
133 bool priorityFlow = pktFlowOp.getPriorityRoute().value_or(
false);
136 for (Operation &Op : b.getOperations()) {
137 if (
auto pktDest = dyn_cast<PacketDestOp>(Op)) {
138 auto dstTile = cast<TileOp>(pktDest.getTile().getDefiningOp());
139 Port dstPort = pktDest.port();
140 TileID dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
141 for (
auto &[srcCoords, srcPort] : sources) {
142 LLVM_DEBUG(llvm::dbgs()
143 <<
"\tAdding Packet Flow: (" << srcCoords.col <<
", "
144 << srcCoords.row <<
")"
145 << stringifyWireBundle(srcPort.bundle) << srcPort.channel
146 <<
" -> (" << dstCoords.col <<
", " << dstCoords.row <<
")"
147 << stringifyWireBundle(dstPort.bundle) << dstPort.channel
149 pathfinder.addFlow(srcCoords, srcPort, dstCoords, dstPort,
150 pktFlowOp.IDInt(), priorityFlow,
158 for (FlowOp flowOp : device.getOps<FlowOp>()) {
159 TileOp srcTile = cast<TileOp>(flowOp.getSource().getDefiningOp());
160 TileOp dstTile = cast<TileOp>(flowOp.getDest().getDefiningOp());
161 TileID srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
162 TileID dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
163 Port srcPort = {flowOp.getSourceBundle(), flowOp.getSourceChannel()};
164 Port dstPort = {flowOp.getDestBundle(), flowOp.getDestChannel()};
165 LLVM_DEBUG(llvm::dbgs()
166 <<
"\tAdding Flow: (" << srcCoords.col <<
", " << srcCoords.row
167 <<
")" << stringifyWireBundle(srcPort.bundle) << srcPort.channel
168 <<
" -> (" << dstCoords.col <<
", " << dstCoords.row <<
")"
169 << stringifyWireBundle(dstPort.bundle) << dstPort.channel
171 pathfinder.addFlow(srcCoords, srcPort, dstCoords, dstPort,
181 for (SwitchboxOp switchboxOp : device.getOps<SwitchboxOp>()) {
182 if (failed(
pathfinder.addFixedConnection(switchboxOp)))
183 return llvm::make_error<RoutingFailure>(
184 llvm::formatv(
"cannot add the fixed connections of the switchbox "
185 "at tile ({0}, {1})",
186 switchboxOp.colIndex(), switchboxOp.rowIndex()),
193 llvm::Expected<Routing> found =
pathfinder.findPaths(maxIterations);
195 return found.takeError();
200 coordToSwitchbox.clear();
201 coordToShimMux.clear();
202 for (
auto tileOp : device.getOps<TileOp>()) {
203 [[maybe_unused]]
bool fresh =
204 coordToTile.try_emplace(tileOp.getTileID(), tileOp).second;
207 for (
auto switchboxOp : device.getOps<SwitchboxOp>()) {
208 [[maybe_unused]]
bool fresh =
210 .try_emplace({switchboxOp.colIndex(), switchboxOp.rowIndex()},
215 for (
auto shimmuxOp : device.getOps<ShimMuxOp>()) {
216 [[maybe_unused]]
bool fresh =
218 .try_emplace({shimmuxOp.colIndex(), shimmuxOp.rowIndex()},
224 LLVM_DEBUG(llvm::dbgs() <<
"\t---End DynamicTileAnalysis Constructor---\n");
225 return llvm::Error::success();
229 TileOp &tileOp = coordToTile[{col, row}];
231 tileOp = TileOp::create(
233 cast<DeviceOp>(builder.getInsertionBlock()->getParentOp()).getLoc(),
239 return getTile(builder, tileId.col, tileId.row);
248 auto tileOp =
getTile(builder, col, row);
249 auto switchboxOp = SwitchboxOp::create(builder, tileOp.getLoc(), tileOp);
250 SwitchboxOp::ensureTerminator(switchboxOp.getConnections(), builder,
252 coordToSwitchbox[{col, row}] = switchboxOp;
260 auto tileOp =
getTile(builder, col, 0);
261 assert(tileOp.isShimNOCorPLTile());
262 auto shimMuxOp = ShimMuxOp::create(builder, tileOp.getLoc(), tileOp);
263 ShimMuxOp::ensureTerminator(shimMuxOp.getConnections(), builder,
265 coordToShimMux[{col, 0}] = shimMuxOp;
287 wireEdgesBegin.clear();
294 std::map<WireBundle, int> maxChannels;
295 auto intraconnect = [&](
int col,
int row) {
296 TileID coords = {col, row};
298 graph.try_emplace({coords, coords}, coords).first->second;
300 for (
int i = 0, e = getMaxEnumValForWireBundle() + 1; i < e; ++i) {
301 WireBundle bundle = symbolizeWireBundle(i).value();
309 for (
int channel = 0; channel < channels; channel++) {
318 for (
int channel = 0; channel < channels; channel++) {
321 maxChannels[bundle] = channels;
325 auto isMuxed = [&](
Port p) {
328 {WireBundle::DMA, WireBundle::NOC, WireBundle::PLIO},
331 for (
auto [i, pIn] : llvm::enumerate(sb.
srcPorts))
332 for (
auto [j, pOut] : llvm::enumerate(sb.
dstPorts))
335 pOut.bundle, pOut.channel) ||
336 isMuxed(pIn) || isMuxed(pOut);
339 auto interconnect = [&](
int col,
int row,
int targetCol,
int targetRow,
340 WireBundle srcBundle, WireBundle dstBundle) {
341 TileID src = {col, row}, dst = {targetCol, targetRow};
343 graph.try_emplace({src, dst}, src, dst).first->second;
344 for (
int channel = 0; channel < maxChannels[srcBundle]; channel++) {
349 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
353 for (
int row = 0; row <= maxRow; row++) {
354 for (
int col = 0; col <= maxCol; col++) {
357 intraconnect(col, row);
362 interconnect(col, row, col, row - 1, WireBundle::South,
367 interconnect(col, row, col, row + 1, WireBundle::North,
372 interconnect(col, row, col - 1, row, WireBundle::West,
377 interconnect(col, row, col + 1, row, WireBundle::East,
387 Port dstPort, std::optional<int> packetId,
389 std::optional<mlir::Location> loc) {
390 auto pinnedIds = constraints.pinnedIds.find({srcCoords, srcPort});
391 if (constraints.pinned.count({srcCoords, srcPort}))
392 isPriorityFlow &= packetId && pinnedIds != constraints.pinnedIds.end() &&
393 pinnedIds->second.count(*packetId);
395 isPriorityFlow &= constraints.prioritize;
397 flowLocs[{srcCoords, srcPort}].try_emplace({dstCoords, dstPort}, *loc);
399 auto &ids = packetIdsTo[{{srcCoords, srcPort}, {dstCoords, dstPort}}];
400 if (!llvm::is_contained(ids, *packetId))
401 ids.push_back(*packetId);
403 priorityIds[{srcCoords, srcPort}].insert(*packetId);
406 PathEndPoint src{srcCoords, srcPort}, dst{dstCoords, dstPort};
407 auto flow = llvm::find_if(flows, [&](
const Flow &f) {
return f.
src == src; });
408 if (flow == flows.end()) {
411 Flow{packetId ? 0 : -1, isPriorityFlow, src, {dst}, packetId});
412 }
else if (isPriorityFlow) {
413 flow->isPriorityFlow =
true;
414 flow->dsts.insert(flow->dsts.begin(), dst);
416 flow->dsts.push_back(dst);
422std::optional<mlir::Location>
424 auto locs = flowLocs.find(src);
425 if (locs == flowLocs.end())
427 auto it = dst ? locs->second.find(*dst) : locs->second.begin();
428 if (it == locs->second.end())
433bool Pathfinder::shareAllChannels() {
434 shareChannels =
true;
438bool Pathfinder::capCrowdedFanOut() {
439 size_t capped = cappedTiles.size();
440 cappedTiles.insert(crowdedTiles.begin(), crowdedTiles.end());
441 return cappedTiles.size() > capped;
444std::map<PathEndPoint, std::set<int>> Pathfinder::idsBySource()
const {
445 std::map<PathEndPoint, std::set<int>> ids;
446 for (
const auto &[ends, sent] : packetIdsTo)
447 ids[ends.first].insert(sent.begin(), sent.end());
451bool Pathfinder::routeIdsApart() {
455 shareChannels =
false;
457 return llvm::any_of(idsBySource(),
458 [](
const auto &s) {
return s.second.size() > 1; });
461bool Pathfinder::splitSharedIds() {
465 shareChannels =
false;
467 std::map<PathEndPoint, std::set<int>> ids = idsBySource();
468 for (
const auto &[src, srcIds] : ids)
469 for (
const auto &[other, otherIds] : ids) {
470 const std::set<int> &theirs = otherIds;
471 if (!(src == other) &&
472 llvm::any_of(srcIds, [&](
int id) {
return theirs.count(
id); }) &&
473 llvm::any_of(srcIds, [&](
int id) {
return !theirs.count(
id); }))
481bool Pathfinder::stopMeeting() {
482 if (meetApart || !treesMet)
485 shareChannels = idsApart = splitShared =
false;
492enum class RelaxStep {
501constexpr RelaxStep relaxLadder[] = {
502 RelaxStep::ShareChannels, RelaxStep::CapCrowdedTiles,
503 RelaxStep::RouteIdsApart, RelaxStep::ShareChannels,
504 RelaxStep::CapCrowdedTiles, RelaxStep::SplitSharedIds,
505 RelaxStep::ShareChannels, RelaxStep::CapCrowdedTiles,
506 RelaxStep::StopMeeting};
510 if (!packetsFailed) {
511 LLVM_DEBUG(llvm::dbgs() <<
"No packet stream crosses an overused link\n");
514 while (relaxStep < std::size(relaxLadder)) {
515 switch (relaxLadder[relaxStep++]) {
516 case RelaxStep::ShareChannels:
517 if (shareAllChannels()) {
518 LLVM_DEBUG(llvm::dbgs() <<
"Relax: share channels\n");
522 case RelaxStep::CapCrowdedTiles:
523 if (capCrowdedFanOut()) {
524 LLVM_DEBUG(llvm::dbgs() <<
"Relax: cap crowded tiles\n");
528 case RelaxStep::RouteIdsApart:
529 if (routeIdsApart()) {
530 LLVM_DEBUG(llvm::dbgs() <<
"Relax: route ids apart\n");
534 relaxStep = std::size(relaxLadder) - 1;
536 case RelaxStep::SplitSharedIds:
537 if (splitSharedIds()) {
538 LLVM_DEBUG(llvm::dbgs() <<
"Relax: split shared ids\n");
541 relaxStep = std::size(relaxLadder) - 1;
543 case RelaxStep::StopMeeting:
545 LLVM_DEBUG(llvm::dbgs() <<
"Relax: stop meeting\n");
557 for (
auto &flow : flows)
558 llvm::sort(flow.dsts);
562 llvm::IntEqClasses groups(flows.size());
563 std::map<PathEndPoint, unsigned> firstTo;
564 for (
auto [k, flow] : llvm::enumerate(flows)) {
565 if (flow.packetGroupId < 0)
568 auto [it, fresh] = firstTo.try_emplace(dst, k);
570 groups.join(k, it->second);
573 llvm::DenseMap<unsigned, int> groupOf;
574 for (
auto [k, flow] : llvm::enumerate(flows))
575 if (flow.packetGroupId >= 0)
579 : groupOf.try_emplace(groups.findLeader(k), groupOf.size())
582 auto flowRank = [](
const Flow &flow) {
583 if (flow.isPriorityFlow)
585 if (flow.packetGroupId >= 0)
589 llvm::sort(flows, [&](
const Flow &lhs,
const Flow &rhs) {
590 return std::make_pair(flowRank(lhs), lhs.
src) <
591 std::make_pair(flowRank(rhs), rhs.
src);
597 if (src.bundle == WireBundle::DMA)
598 return src.channel == 0 ? 3 : 7;
599 if (src.bundle == WireBundle::NOC)
600 return src.channel >= 2 ? src.channel + 4 : src.channel + 2;
606 if (dst.bundle == WireBundle::DMA)
607 return dst.channel == 0 ? 2 : 3;
608 if (dst.bundle == WireBundle::NOC)
609 return dst.channel + 2;
614 int col = switchboxOp.colIndex();
615 int row = switchboxOp.rowIndex();
616 TileID coords = {col, row};
617 auto &sb = graph[std::make_pair(coords, coords)];
622 llvm::SmallVector<std::pair<int, int>, 8> reserved;
623 llvm::SmallDenseSet<int, 8> claimedDsts;
624 for (ConnectOp connectOp : switchboxOp.getOps<ConnectOp>()) {
625 int srcIdx = sb.srcIndex(connectOp.sourcePort());
626 int dstIdx = sb.dstIndex(connectOp.destPort());
629 if (srcIdx < 0 || dstIdx < 0 || !sb.at(srcIdx, dstIdx).available ||
630 !claimedDsts.insert(dstIdx).second) {
633 reserved.emplace_back(srcIdx, dstIdx);
642 llvm::SmallVector<int, 8> reservedMasterDsts;
643 for (MasterSetOp masterSetOp : switchboxOp.getOps<MasterSetOp>()) {
644 int dstIdx = sb.dstIndex(masterSetOp.destPort());
647 if (dstIdx < 0 || !claimedDsts.insert(dstIdx).second) {
650 reservedMasterDsts.push_back(dstIdx);
657 for (
auto [srcIdx, dstIdx] : reserved) {
658 for (
size_t j = 0; j < sb.dstPorts.size(); j++)
659 sb.at(srcIdx, j).available =
false;
660 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
661 sb.at(i, dstIdx).available =
false;
665 for (
int dstIdx : reservedMasterDsts)
666 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
667 sb.at(i, dstIdx).available =
false;
670 if (switchboxOp.getTileOp().isShimNOCorPLTile()) {
671 auto isMuxed = [](
Port p) {
672 return llvm::is_contained(
673 {WireBundle::DMA, WireBundle::NOC, WireBundle::PLIO}, p.bundle);
675 llvm::SmallDenseSet<int, 8> southDsts, southSrcs;
676 for (
int dstIdx : claimedDsts)
677 if (sb.dstPorts[dstIdx].bundle == WireBundle::South)
678 southDsts.insert(sb.dstPorts[dstIdx].channel);
679 for (
auto [srcIdx, dstIdx] : reserved)
680 if (sb.srcPorts[srcIdx].bundle == WireBundle::South)
681 southSrcs.insert(sb.srcPorts[srcIdx].channel);
682 for (
size_t j = 0; j < sb.dstPorts.size(); j++)
683 if (isMuxed(sb.dstPorts[j]) &&
685 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
686 sb.at(i, j).available =
false;
687 for (
size_t i = 0; i < sb.srcPorts.size(); i++)
688 if (isMuxed(sb.srcPorts[i]) &&
690 for (
size_t j = 0; j < sb.dstPorts.size(); j++)
691 sb.at(i, j).available =
false;
693 for (PacketRulesOp rulesOp : switchboxOp.getOps<PacketRulesOp>()) {
694 int srcIdx = sb.srcIndex(rulesOp.sourcePort());
697 sb.packetOnlySrc.set(srcIdx);
703 auto it = graph.find({coords, coords});
704 if (it == graph.end())
707 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
708 for (
size_t j = 0; j < sb.
dstPorts.size(); j++)
713 auto it = graph.find({coords, coords});
714 if (it == graph.end())
720 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
724static constexpr double INF = std::numeric_limits<double>::max();
727enum Color : int8_t { WHITE = 0, GRAY = 1, BLACK = 2 };
730int Pathfinder::getOrAddNodeId(
const PathEndPoint &pep) {
731 auto it = nodeIds.find(pep);
732 if (it != nodeIds.end())
734 int id =
static_cast<int>(nodes.size());
736 nodes.push_back(pep);
746void Pathfinder::buildRoutingGraph() {
750 for (
auto &f : flows) {
751 getOrAddNodeId(f.src);
752 for (
auto &d : f.dsts)
758 for (
size_t id = 0;
id < nodes.size();
id++) {
759 PathEndPoint src = nodes[id];
762 std::vector<PathEndPoint> dests;
764 if (intraIt != graph.end()) {
765 auto &sb = intraIt->second;
766 for (
auto [i, pIn] : llvm::enumerate(sb.srcPorts))
767 for (
auto [j, pOut] : llvm::enumerate(sb.dstPorts))
768 if (pIn == src.
port && sb.at(i, j).available)
769 dests.emplace_back(src.
coords, pOut);
771 std::pair<TileID, Port> neighbors[] = {
773 {WireBundle::North, src.
port.channel}},
775 {WireBundle::East, src.
port.channel}},
777 {WireBundle::South, src.
port.channel}},
779 {WireBundle::West, src.
port.channel}}};
780 for (
const auto &[neighborCoords, neighborPort] : neighbors) {
781 auto nIt = graph.find({src.
coords, neighborCoords});
782 if (nIt != graph.end() &&
784 llvm::is_contained(nIt->second.dstPorts, neighborPort))
785 dests.emplace_back(neighborCoords, neighborPort);
789 std::vector<Edge> edges;
790 edges.reserve(dests.size());
791 for (
auto &dest : dests) {
792 auto &sb = graph.at({src.
coords, dest.coords});
793 int i = sb.srcIndex(src.
port);
794 int j = sb.dstIndex(dest.port);
795 assert(i >= 0 && j >= 0);
796 int destId = getOrAddNodeId(dest);
797 edges.push_back(Edge{destId, &sb, i, j});
800 std::stable_partition(edges.begin(), edges.end(), [](
const Edge &e) {
801 return e.sb->srcCoords == e.sb->dstCoords;
803 if (wireEdgesBegin.size() < nodes.size())
804 wireEdgesBegin.resize(nodes.size());
805 wireEdgesBegin[id] = wires - edges.begin();
808 if (adjacency.size() < nodes.size())
809 adjacency.resize(nodes.size());
810 adjacency[id] = std::move(edges);
812 size_t n = nodes.size();
813 if (adjacency.size() < n)
815 wireEdgesBegin.resize(n);
819 distance.assign(2 * n, INF);
820 indexInHeap.assign(2 * n, 0);
821 colors.assign(2 * n, WHITE);
822 preds.assign(2 * n, -1);
823 predEdge.assign(2 * n, Edge{-1,
nullptr, 0, 0});
827double Pathfinder::edgeWeight(
const Edge &e, std::optional<int> packetId,
828 const llvm::BitVector *avoid,
829 const llvm::BitVector *avoidBranch)
const {
830 const SwitchboxConnect::Cell &cell = e.sb->at(e.i, e.j);
836 if (e.sb->srcCoords == e.sb->dstCoords)
837 for (
const llvm::BitVector *flows : {avoid, avoidBranch})
838 if (flows && llvm::any_of(e.sb->unitFlows(e.j),
839 [&](
int f) { return flows->test(f); }))
840 w += conflictSharePenalty;
847void Pathfinder::dijkstraShortestPaths(
848 ArrayRef<int> seeds, ArrayRef<double> seedCosts,
849 std::optional<int> packetId,
const llvm::BitVector *avoid,
850 const llvm::DenseMap<int, llvm::BitVector> *branchAvoid,
851 const llvm::DenseSet<int> *stops, ArrayRef<int> targets,
852 llvm::function_ref<
bool(
int,
int)> mayCross) {
853 llvm::fill(distance, INF);
854 llvm::fill(colors,
static_cast<int8_t
>(WHITE));
855 llvm::fill(preds, -1);
856 llvm::fill(indexInHeap, uint64_t{0});
858 using MutableQueue = d_ary_heap_indirect<
860 std::vector<uint64_t> &,
861 std::vector<double> &,
863 MutableQueue Q(distance, indexInHeap);
865 for (
auto [seed, cost] : llvm::zip_equal(seeds, seedCosts)) {
866 distance[seed] = cost;
871 SmallVector<int, 8> unsettled(targets);
875 if (llvm::is_contained(unsettled, s)) {
876 llvm::erase(unsettled, s);
877 if (unsettled.empty())
880 if (stops && stops->count(s)) {
888 const bool sIsOut = (s & 1) == Out;
889 const llvm::BitVector *avoidBranch =
nullptr;
891 if (
auto it = branchAvoid->find(s); it != branchAvoid->end())
892 avoidBranch = &it->second;
893 ArrayRef<Edge> out = adjacency[stateNode(s)];
894 size_t wires = wireEdgesBegin[stateNode(s)];
895 const bool isIntra = !sIsOut;
897 sIsOut ? out.drop_front(wires) : out.take_front(wires)) {
898 if ((isIntra && !packetId && e.sb->packetOnlySrc[e.i]) ||
899 (isIntra && packetId && e.sb->circuitOnlyDst[e.j]))
901 int dst = stateId(e.dst, isIntra ? Out : In);
902 if (colors[dst] == BLACK || (isIntra && mayCross && !mayCross(s, dst)))
904 double w = edgeWeight(e, packetId, avoid, avoidBranch);
905 if (distance[s] + w >= distance[dst])
907 distance[dst] = distance[s] + w;
910 if (colors[dst] == WHITE) {
926 SmallVector<int, 4>
idsTo(
int flow,
int dstState)
const;
928 int splitPart(
int flow,
const std::set<int> &ids);
939 std::map<PathEndPoint, SmallVector<int, 2>>
partsOf;
953 std::vector<llvm::DenseMap<int, std::pair<int, Edge>>>
treeOf;
960 std::vector<SmallVector<std::pair<int, Edge>, 8>>
joinedHops;
971 std::tie(other.
at, other.
a, other.
b, other.
apart);
992 std::set<std::pair<int, int>>
met;
1002 bool mayLeave(ArrayRef<int> masters,
int next)
const;
1003 bool reorder(
int later,
int earlier,
bool again =
false);
1012 for (
auto [k, f] : llvm::enumerate(
parts))
1014 for (
auto [k, f] : llvm::enumerate(
parts))
1016 for (
const auto &[ends, ids] :
pf.packetIdsTo)
1017 flowIds[
partsOf.at(ends.first).front()].insert(ids.begin(), ids.end());
1022 for (
size_t k = 0, n =
parts.size(); k < n; k++) {
1023 if (!isPinned(parts[k]))
1025 const PathEndPoint &src = parts[k].src;
1026 const std::set<int> &prioritized = pf.priorityIds.at(src);
1027 std::set<PathEndPoint> treeDsts;
1028 std::map<int, std::set<PathEndPoint>> dstsOf;
1029 for (const auto &[ends, ids] : pf.packetIdsTo)
1030 if (ends.first == src)
1032 (prioritized.count(id) ? treeDsts : dstsOf[id]).insert(ends.second);
1033 std::set<int> others;
1034 for (const auto &[id, dsts] : dstsOf)
1035 if (!prioritized.count(id) && dsts != treeDsts)
1037 if (!others.empty())
1038 splitPart(k, others);
1040 for (
const auto &[src, tree] :
pf.constraints.pinned) {
1041 std::map<PathEndPoint, SmallVector<int, 4>> sets;
1042 for (const TreeHop &h : tree) {
1043 auto to = pf.nodeIds.find(h.to);
1044 if (h.from.coords == h.to.coords && to != pf.nodeIds.end())
1045 sets[h.from].push_back(stateId(to->second, Out));
1047 for (
auto &[_, set] : sets) {
1058 if (!again && (
reordered.count({earlier, later}) ||
1059 !
reordered.insert({later, earlier}).second))
1062 if (!llvm::is_contained(group, earlier))
1064 group.erase(llvm::find(group, later));
1065 group.insert(llvm::find(group, earlier), later);
1073 llvm::none_of(masters, [&](
int m) { return priorityMasters.count(m); }))
1075 return llvm::any_of(
prioritySets, [&](
const SmallVector<int, 4> &set) {
1076 return llvm::is_contained(set, next) && llvm::all_of(masters, [&](
int m) {
1077 return llvm::is_contained(set, m);
1091 int dstState)
const {
1092 SmallVector<int, 4> ids;
1094 pf.packetIdsTo.find({
parts[flow].src,
pf.nodes[stateNode(dstState)]});
1095 if (it !=
pf.packetIdsTo.end())
1096 for (
int id : it->second)
1105 for (
size_t a = 0; a <
parts.size(); a++)
1106 for (
size_t b = 0; b <
parts.size(); b++) {
1108 bool same = llvm::any_of(
flowIds[a],
1109 [&](
int id) {
return flowIds[b].count(
id); });
1112 if (a < b &&
parts[a].packetId &&
parts[b].packetId &&
1115 (
pf.constraints.conflict &&
1125 int part =
parts.size();
1128 llvm::dbgs() <<
"\t\tRouting ids";
1130 llvm::dbgs() <<
' ' << id;
1138 auto it =
pf.packetIdsTo.find({f.
src, p});
1139 return it !=
pf.packetIdsTo.end() &&
1140 llvm::any_of(it->second, [&](
int i) { return flowIds[k].count(i); });
1142 llvm::erase_if(f.
dsts,
1143 [&](
const PathEndPoint &p) { return !carries(part, p); });
1144 llvm::erase_if(
parts[flow].dsts,
1145 [&](
const PathEndPoint &p) {
return !carries(flow, p); });
1147 std::optional<int> &flowId =
parts[flow].packetId;
1148 if (flowId && ids.count(*flowId))
1149 flowId = *
flowIds[flow].begin();
1150 auto prioritized = [&](
const std::set<int> &carried) {
1151 auto prio =
pf.priorityIds.find(f.
src);
1152 return prio !=
pf.priorityIds.end() && llvm::any_of(carried, [&](
int id) {
1153 return prio->second.count(
id);
1157 parts[flow].isPriorityFlow = prioritized(
flowIds[flow]);
1166 for (
auto [a, b] : std::set<std::pair<int, int>>(
noJoin))
1167 if (a == flow || b == flow)
1168 noJoin.insert({a == flow ? part : a, b == flow ? part : b});
1173 for (
auto [at, a, b, apart] :
splitsOf[flow])
1174 if (ids.count(a) != ids.count(b) &&
1175 flowIds[flow].count(ids.count(a) ? b : a)) {
1188 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
1189 for (
size_t j = 0; j < sb.
dstPorts.size(); j++) {
1193 if (xbar == graph.end())
1199 for (
size_t row = 0; row < x.
srcPorts.size(); row++)
1206llvm::Error Pathfinder::explainNoRouting(
const RouteState &st)
const {
1212 return c.usedCapacity > maxCircuitStreamCapacity;
1215 auto link = [](
const SwitchboxConnect &sb) -> std::string {
1216 return llvm::formatv(
"from tile ({0}, {1}) to ({2}, {3})", sb.
srcCoords.col,
1219 const Flow *prioritized =
nullptr;
1220 llvm::SetVector<const SwitchboxConnect *> held;
1221 for (
auto [k, f] : llvm::enumerate(st.parts)) {
1222 if (!st.isPinned(f))
1224 prioritized = prioritized ? prioritized : &f;
1225 for (
const auto &[_, hop] : st.treeOf[k]) {
1226 if (overused(*hop.second.sb)) {
1227 return llvm::make_error<PinnedRoutingFailure>(
1229 ", and it holds a channel " + link(*hop.second.sb) +
1230 " the other flows need.");
1232 if (hop.second.sb->srcCoords != hop.second.sb->dstCoords)
1233 held.insert(hop.second.sb);
1236 if (prioritized && llvm::any_of(graph, [&](
const auto &entry) {
1237 return overused(entry.second);
1239 return llvm::make_error<PinnedRoutingFailure>(
1242 ", and the router found no routing for the other flows around the "
1243 "channels it holds " +
1245 llvm::map_range(llvm::make_pointee_range(held), link))) +
1249 if (!checkReason.empty())
1250 return llvm::make_error<RoutingFailure>(checkReason);
1254 const SwitchboxConnect *worst =
nullptr;
1255 int worstI = 0, worstJ = 0;
1256 std::pair<bool, int> worstRank{
false, 0};
1257 for (
const auto &[_, sb] : graph) {
1258 std::optional<bool> roomless;
1259 for (
size_t i = 0; i < sb.
srcPorts.size(); i++)
1260 for (
size_t j = 0; j < sb.
dstPorts.size(); j++) {
1264 roomless = !hasRoom(sb);
1266 if (rank > worstRank) {
1275 return llvm::make_error<RoutingFailure>(
"");
1277 std::vector<std::string> users;
1278 for (
const auto &[src, settings] : st.routing.settings) {
1279 auto it = settings.find(worst->
srcCoords);
1280 if (it == settings.end())
1282 const SwitchSetting &s = it->second;
1284 for (
size_t k = 0; k < s.
dsts.size() && !uses; k++)
1293 crossbar ? llvm::formatv(
"the connection from {0} to {1} at tile ({2}, "
1299 :
"the links " + link(*worst);
1301 return llvm::make_error<RoutingFailure>(
1302 "the router found no routing that fits " + where +
".");
1303 return llvm::make_error<RoutingFailure>(
1304 "the flows from " +
joinNames(users) +
" need " + where +
1305 ", and the router found no routing that fits them.");
1308llvm::Error Pathfinder::cutOverflow()
const {
1314 auto onDevice = [&](
TileID t) {
return t.col < columns && t.row < rows; };
1315 std::map<std::tuple<bool, int, bool>,
size_t> channels;
1316 for (
const auto &[ends, sb] : graph) {
1317 const auto &[a, b] = ends;
1318 auto xbar = graph.find({a, a});
1319 if (a == b || !onDevice(a) || !onDevice(b) || xbar == graph.end())
1321 bool rows = a.col == b.col;
1322 int below = rows ? std::min(a.row, b.row) : std::min(a.col, b.col);
1323 bool up = rows ? b.row > a.row : b.col > a.col;
1324 const SwitchboxConnect &x = xbar->second;
1328 channels[{rows, below, up}] += 0;
1330 int k = x.dstIndex(port);
1332 llvm::any_of(llvm::seq<size_t>(0, x.srcPorts.size()),
1333 [&](size_t i) { return x.at(i, k).available; }))
1334 channels[{rows, below, up}]++;
1337 for (
const auto &[cut, available] : channels) {
1338 bool rows = std::get<0>(cut), up = std::get<2>(cut);
1339 int below = std::get<1>(cut);
1340 auto low = [&](
TileID t) {
return (rows ? t.row : t.col) <= below; };
1341 std::vector<std::string> circuits;
1342 bool packets =
false;
1343 for (
const Flow &f : flows) {
1344 if (low(f.src.coords) != up ||
1345 llvm::none_of(f.dsts, [&](
const PathEndPoint &d) {
1346 return low(d.coords) != up;
1355 if (packets && constraints.apartAcross && available > circuits.size())
1356 apart = constraints.apartAcross(low, up, available - circuits.size());
1358 circuits.size() + std::max<size_t>(packets, apart.
flows.size());
1359 LLVM_DEBUG(llvm::dbgs() <<
"Cut " << (rows ?
"row " :
"column ") << below
1360 << (up ?
" up" :
" down") <<
": " << needed
1361 <<
" of " << available <<
" channels\n");
1362 if (needed <= available)
1364 int from = up ? below : below + 1, to = up ? below + 1 : below;
1365 auto channel = [](
size_t n) {
return n == 1 ?
"channel" :
"channels"; };
1367 circuits.empty() ?
"" :
"the circuit flows from " +
joinNames(circuits);
1368 if (apart.
flows.size() > 1)
1370 ", no two of which can share an arbiter,";
1373 circuits.empty() ?
"the packet flows" : who +
", with packet flows,";
1375 apart.
flows.size() > 1 ?
" For example, " + apart.
why :
"";
1376 return llvm::make_error<RoutingFailure>(llvm::formatv(
1377 "{0} cross from {1} {2} to {1} {3}, which takes {4} {5} across, and "
1378 "it has {6} {7}.{8}",
1379 who, rows ?
"row" :
"column", from, to, needed, channel(needed),
1380 available, channel(available), why));
1382 return llvm::Error::success();
1392 return stateId(
pf.nodeIds.at(p), Out);
1394 bool isPending(
int state)
const;
1395 void reach(ArrayRef<int> dsts);
1396 bool joinable(
int at, ArrayRef<int> dsts)
const;
1399 void meet(
int owner);
1400 void search(
const llvm::DenseSet<int> &drop, ArrayRef<int> targets,
1401 const llvm::DenseSet<int> &off = {});
1402 bool trace(
int state);
1403 llvm::DenseSet<int> splitOff(
int dst)
const;
1404 llvm::DenseSet<int> trunkOff(
int dst)
const;
1405 llvm::Error placePinned();
1409 bool join(
int state,
int predId,
const Edge &e);
1419 const std::vector<TreeHop> *
pinned =
nullptr;
1428 llvm::MapVector<int, std::pair<int, Edge>>
planned;
1443 llvm::DenseMap<int, SmallVector<int, 4>>
joins;
1459 SmallVector<std::pair<int, const SmallVector<int, 4> *>, 2>
joinedAt;
1472 int srcId =
pf.nodeIds.at(
part.src);
1473 tree.push_back(stateId(srcId, In));
1475 st.processedStamp[
tree.front()] = ++
st.curStamp;
1483 int self = stateId(srcId, Out);
1484 if (endPoint == part.src && !st.priorityMasters.count(self) &&
1485 llvm::any_of(pf.adjacency[srcId],
1486 [&](const Edge &e) { return e.dst == srcId; })) {
1493 for (
auto it = st.
trunked.lower_bound({flow, 0});
1497 SmallVector<int, 4> shared;
1499 if (llvm::is_contained(part.dsts, dst))
1500 shared.push_back(dstState(dst));
1501 if (shared.size() > 1)
1502 trunkDsts.insert(shared.begin(), shared.end());
1505 for (
const auto &[state, _] : st.
treeOf[other])
1506 if ((state & 1) == Out && pf.nodes[stateNode(state)].coords == at)
1507 partOff.insert(state);
1519 return llvm::is_contained(dsts,
dstState(p));
1527 llvm::all_of(dsts, [&](
int dst) {
return isPending(dst); });
1531 auto shared = [&](ArrayRef<int> ids,
int other) {
1532 std::set<int> common;
1534 if (
st.flowIds[other].count(
id))
1538 for (
int other :
st.sameId[
flow]) {
1540 const Flow &o =
st.parts[other];
1542 if (!
st.treeDsts[other].empty() && llvm::is_contained(o.
dsts, o.
src) &&
1545 for (
int dst :
st.treeDsts[other]) {
1546 std::set<int> common = shared(
st.idsTo(other, dst),
flow);
1555 !(
pf.constraints.conflict &&
1556 pf.constraints.conflict(
part.src, common, o.
src,
1557 st.flowIds[other])) &&
1558 !
st.noJoin.count({
flow, other}) &&
1559 (!
part.isPriorityFlow ||
st.parts[other].isPriorityFlow);
1564 for (
auto it =
st.treeOf[other].find(dst); it !=
st.treeOf[other].end();
1565 it =
st.treeOf[other].find(it->second.first)) {
1566 int up = it->second.first;
1567 if ((up & 1) != Out)
1572 if (!llvm::is_contained(
joins[up], dst))
1573 joins[up].push_back(dst);
1574 if (!llvm::is_contained(
joinOwners[up], other))
1583 if (!
part.packetId ||
part.isPriorityFlow)
1585 llvm::DenseSet<int> sameIdDsts;
1586 for (
int other :
st.sameId[
flow])
1590 for (
auto [other, o] : llvm::enumerate(
st.parts)) {
1592 if (k ==
flow || o.src ==
part.src || !o.packetId || o.isPriorityFlow ||
1593 st.treeDsts[k].empty() || llvm::is_contained(
st.sameId[
flow], k) ||
1594 st.conflicting[
flow].test(k) ||
st.noJoin.count({flow, k}))
1596 SmallVector<int, 4> dsts;
1597 for (
int dst :
st.treeDsts[k])
1599 !sameIdDsts.count(dst) &&
st.treeOf[k].count(dst))
1600 dsts.push_back(dst);
1601 if (dsts.size() > 1) {
1620 auto spans = [&](
int k, ArrayRef<int> dsts) {
1621 return llvm::any_of(
st.flowIds[k], [&](
int id) {
1622 return llvm::count_if(dsts, [&](int dst) {
1623 return llvm::is_contained(st.idsTo(k, dst), id);
1627 auto mustMeet = [&](
int other) {
1628 return spans(flow, sharedDsts[other]) && spans(other, sharedDsts[other]);
1630 SmallVector<int, 4> owners;
1631 for (
int other :
llvm::make_first_range(sharedDsts))
1632 if (!disagree.count(other) && mustMeet(other))
1633 owners.push_back(other);
1634 llvm::stable_sort(owners, [&](
int a,
int b) {
1635 return sharedDsts[a].size() > sharedDsts[b].size();
1637 for (
int owner : owners) {
1638 llvm::erase_if(sharedDsts[owner], [&](
int dst) {
return !isPending(dst); });
1639 if (mustMeet(owner))
1645 st.unmet.insert({
flow, owner});
1647 bool other =
foreign.count(owner);
1648 const auto &t =
st.treeOf[owner];
1649 const Flow &o =
st.parts[owner];
1650 int root = stateId(
pf.nodeIds.at(o.
src), In);
1652 SmallVector<SmallVector<int, 16>, 4> paths;
1654 SmallVector<int, 16> &path = paths.emplace_back(1, dst);
1655 for (
auto it = t.find(dst); it != t.end(); it = t.find(it->second.first))
1656 path.push_back(it->second.first);
1657 if (path.size() == 1)
1658 path.push_back(root);
1659 if (path.back() != root)
1661 std::reverse(path.begin(), path.end());
1664 while (llvm::all_of(paths, [&](
const auto &path) {
1665 return trunk + 1 < path.size() && path[trunk] == paths[0][trunk];
1670 SmallVector<std::pair<int, Edge>, 4> hops;
1674 SmallVector<Meeting, 8> meetings;
1675 SmallVector<int, 8> targets;
1677 auto below = [&](
size_t k) {
1678 llvm::MapVector<int, std::pair<int, Edge>> hops;
1679 for (
const auto &path : paths)
1680 for (
size_t l = k + 1; l + 1 < path.size(); l++)
1681 hops.insert({path[l + 1], t.find(path[l + 1])->second});
1684 for (
size_t k = 0; k < trunk; k++) {
1685 int at = paths[0][k];
1688 SetVector<int> next;
1689 for (
const auto &path : paths)
1690 next.insert(path[k + 1]);
1691 if (!other && llvm::any_of(next, [&](
int s) {
1695 auto taken = [&](
int s) {
1696 return st.processedStamp[s] ==
st.curStamp ||
stops.count(s) ||
1699 if (other && (llvm::any_of(next, taken) ||
1700 llvm::any_of(below(k), [&](
const auto &hop) {
1701 const Edge &e = hop.second.second;
1702 return taken(hop.first) ||
1707 SmallVector<int, 4> leaving;
1708 if (llvm::any_of(next, [&](
int s) {
1709 bool ok =
st.mayLeave(leaving, s);
1710 leaving.push_back(s);
1714 TileID tile =
pf.nodes[stateNode(at)].coords;
1715 auto sb =
pf.graph.find({tile, tile});
1716 if (sb ==
pf.graph.end())
1719 auto node =
pf.nodeIds.find({tile, port});
1720 if (node ==
pf.nodeIds.end())
1722 int in = stateId(node->second, In);
1723 if (in == at || t.count(in))
1725 Meeting m{in, {}, 0, k};
1727 for (
const Edge &e :
pf.adjacency[node->second])
1728 if (e.dst == stateNode(s) && e.sb == &sb->second &&
1730 m.hops.push_back({s, e});
1731 m.cost +=
pf.edgeWeight(e,
part.packetId,
avoid,
nullptr);
1734 if (m.hops.size() == next.size()) {
1735 meetings.push_back(std::move(m));
1736 targets.push_back(in);
1740 if (meetings.empty())
1742 llvm::DenseSet<int> off;
1744 for (
const auto &[s, _] : below(0))
1746 search({}, targets, off);
1747 const Meeting *best =
nullptr;
1748 for (
const Meeting &m : meetings)
1749 if (
pf.distance[m.in] < INF &&
1751 pf.distance[m.in] + m.cost <
pf.distance[best->in] + best->cost))
1753 if (!best || !
trace(best->in))
1755 st.unmet.erase({
flow, owner});
1756 st.met.insert({
flow, owner});
1758 auto add = [&](
int s,
int from,
const Edge &e,
int depth) {
1759 planned.insert({s, {from, e}});
1761 st.processedStamp[s] =
st.curStamp;
1765 for (
const auto &[s, e] : best->hops) {
1766 add(s, best->in, e, hops);
1768 st.joinedHops[
flow].push_back({owner, e});
1776 const SmallVector<int, 4> &dsts =
joins[s];
1783 for (
const auto &[s, hop] : below(best->at)) {
1784 const auto &[from, e] = hop;
1786 st.joinedHops[
flow].push_back({owner, e});
1797 ArrayRef<int> targets,
1798 const llvm::DenseSet<int> &off) {
1800 llvm::DenseMap<int, llvm::BitVector> onArbiter;
1802 for (
const auto &[state, hop] :
planned) {
1803 const auto &[from, e] = hop;
1804 if (e.sb->srcCoords != e.sb->dstCoords || drop.count(state))
1806 llvm::BitVector &away =
1807 branchAvoid.try_emplace(from,
st.conflicting.size()).first->second;
1808 llvm::BitVector &on =
1809 onArbiter.try_emplace(from,
st.conflicting.size()).first->second;
1810 for (
int other : e.sb->unitFlows(e.j)) {
1813 away |=
st.conflicting[other];
1819 away.reset(onArbiter.find(from)->second);
1824 for (
auto [state, hops] : llvm::zip_equal(
tree,
treeHops))
1825 if (!drop.count(state) && !off.count(state)) {
1826 seeds.push_back(state);
1827 seedCosts.push_back(treeSeedFactor * hops);
1829 llvm::DenseSet<int> blocked;
1832 blocked.insert(off.begin(), off.end());
1834 llvm::DenseMap<int, SmallVector<int, 4>> masters;
1835 bool limited = !
st.priorityMasters.empty();
1837 for (
const auto &[state, hop] :
planned)
1838 if ((hop.first & 1) == In && !drop.count(state))
1839 masters[hop.first].push_back(state);
1840 auto mayCross = [&](
int from,
int to) {
1841 auto it = masters.find(from);
1842 return st.mayLeave(it == masters.end() ? ArrayRef<int>{} : it->second, to);
1844 pf.dijkstraShortestPaths(
1846 off.empty() ? &
stops : &blocked, targets,
1847 limited ? llvm::function_ref<
bool(
int,
int)>(mayCross) :
nullptr);
1852 size_t grown =
tree.size();
1853 while (
st.processedStamp[state] !=
st.curStamp) {
1856 int pred =
pf.preds[state];
1859 planned.insert({state, {pred,
pf.predEdge[state]}});
1861 st.processedStamp[state] =
st.curStamp;
1862 tree.push_back(state);
1867 static_cast<int>(
tree.size() - grown);
1878 llvm::DenseSet<int> off =
partOff;
1879 auto carries = [&](
int d,
int id) {
1880 return llvm::is_contained(
st.idsTo(
flow, d),
id);
1882 auto only = [&](
int d,
int id,
int other) {
1883 return carries(d,
id) && !carries(d, other);
1885 for (
auto [at, a, b, apart] :
st.splitsOf[
flow])
1887 bool splits = !apart ? (only(dst, a, b) && only(other, b, a)) ||
1888 (only(dst, b, a) && only(other, a, b))
1889 : (only(dst, a, b) && other == *apart) ||
1890 (dst == *apart && only(other, a, b));
1893 SmallVector<int, 8> below;
1895 auto entersAt = [&, tile = at](
int state) {
1896 return (state & 1) == In &&
pf.nodes[stateNode(state)].coords == tile;
1898 for (
auto *hop =
planned.find(s); !entersAt(s) && hop !=
planned.end();
1901 s = hop->second.first;
1905 off.insert(below.begin(), below.end());
1915 llvm::DenseSet<int> off;
1918 int root =
tree.front();
1919 auto exitOf = [&](
int s) {
1920 for (
const auto *it =
planned.find(s);
1921 it !=
planned.end() && it->second.first != root; it =
planned.find(s))
1922 s = it->second.first;
1925 const auto *other = llvm::find_if(
reached, [&](
int d) {
1930 int exit = exitOf(*other);
1932 if (s == root || exitOf(s) != exit)
1939 for (
const auto &[from, to, joined] : *
pinned) {
1940 if (
toSelf && from == to)
1942 bool intra = from.coords == to.coords;
1943 auto fromId =
pf.nodeIds.find(from), toId =
pf.nodeIds.find(to);
1944 const Edge *e =
nullptr;
1945 if (fromId !=
pf.nodeIds.end() && toId !=
pf.nodeIds.end())
1946 for (
const Edge &out :
pf.adjacency[fromId->second])
1947 if (out.dst == toId->second &&
1951 return llvm::make_error<RoutingFailure>(
1952 "the route packet flows from " +
1954 " take alone does not fit this design: it goes from " +
1957 ", which the design's own switchboxes leave no connection for.",
1959 std::pair<int, std::pair<int, Edge>> hop{
1960 stateId(toId->second, intra ? Out : In),
1961 {stateId(fromId->second, intra ? In : Out), *e}};
1976 return llvm::Error::success();
1991 SmallVector<int, 8> targets;
1994 for (
const auto &[at, dsts] :
joins)
1996 targets.push_back(at);
2003 : llvm::min_element(
2009 llvm::DenseSet<int> off =
splitOff(currId);
2010 llvm::DenseSet<int> trunk =
trunkOff(currId);
2011 off.insert(trunk.begin(), trunk.end());
2013 search({}, targets, off);
2014 if (
pf.distance[currId] == INF)
2017 const SmallVector<int, 4> *joined =
nullptr;
2018 for (
const auto &[at, dsts] :
joins)
2019 if (
pf.distance[at] <
pf.distance[currId] &&
joinable(at, dsts)) {
2025 joinedAt.push_back({currId, joined});
2033 return llvm::make_error<RoutingFailure>(
2034 "no path leads from " +
2037 " through the connections the switchboxes allow and existing "
2038 "routing leaves free.",
2045 return llvm::Error::success();
2053 llvm::DenseSet<int> branch;
2055 for (
int below = 0; top !=
tree.front() &&
children.lookup(top) == below;
2058 top =
planned.find(top)->second.first;
2063 off.insert(trunk.begin(), trunk.end());
2064 search(branch, dst, off);
2067 for (
int s = dst; s != top;) {
2068 const auto &[from, e] =
planned.find(s)->second;
2072 away ==
branchAvoid.end() ?
nullptr : &away->second);
2075 if (
pf.distance[dst] + rerouteMinSaving >= cost)
2077 for (
int s : branch) {
2078 st.processedStamp[s] = 0;
2083 for (
size_t k =
tree.size(); k-- > 0;)
2084 if (branch.count(
tree[k])) {
2088 [[maybe_unused]]
bool traced =
trace(dst);
2089 assert(traced &&
"a rerouted branch reaches its destination");
2099 st.routing.packetTrees[
part.src].push_back({
part.src,
part.src,
false});
2101 const std::optional<int> &packetId =
part.packetId;
2102 int packetGroupId =
part.packetGroupId;
2103 llvm::DenseMap<int, std::pair<SwitchboxConnect *, int>> branchPort;
2104 for (
const auto &[currId, hop] :
planned) {
2105 const auto &[predId, e] = hop;
2113 st.treeOf[
flow].try_emplace(currId, predId, e);
2115 st.circuitHops[
flow].push_back(e);
2127 auto mergeable = [&] {
2131 std::set<PathEndPoint> dsts;
2133 if (
auto ids =
pf.packetIdsTo.find({f.src, dst});
2134 ids !=
pf.packetIdsTo.end() &&
2135 llvm::is_contained(ids->second, *packetId))
2141 bool sameGroupUnseen =
2142 packetGroupId >= 0 && packetId.has_value() &&
2144 (seen == cell.
packetIds.end() || seen->second ==
flow || mergeable());
2145 if (sameGroupUnseen) {
2146 int packetIdValue = *packetId;
2152 for (
size_t k = 0; k < sb.
srcPorts.size(); k++)
2154 for (
size_t l = 0; l < sb.
dstPorts.size(); l++)
2155 if (l !=
static_cast<size_t>(j))
2173 auto [it,
first] = branchPort.try_emplace(predId, &sb, j);
2182 for (
const auto &[currId, hop] :
planned)
2183 st.routing.packetTrees[
part.src].push_back(
2184 {
pf.nodes[stateNode(hop.first)],
pf.nodes[stateNode(currId)],
false});
2190 if (!
st.treeOf[
flow].try_emplace(state, predId, e).second)
2194 st.routing.packetTrees[
part.src].push_back({pred, curr,
true});
2204 for (
const auto &[joinAt, joined] :
joinedAt) {
2206 st.joinedHops[
flow].push_back(
2207 {other,
planned.find(joinAt)->second.second});
2208 for (
int dst : *joined) {
2209 const auto &t =
st.treeOf[other];
2210 SmallVector<std::pair<int, std::pair<int, Edge>>, 8> path;
2211 auto it = t.find(dst);
2212 for (; it != t.end() && it->first != joinAt;
2213 it = t.find(it->second.first))
2214 path.push_back(*it);
2217 for (
const auto &[state, hop] : path)
2218 if (
join(state, hop.first, hop.second))
2219 st.joinedHops[
flow].push_back({other, hop.second});
2222 st.treeDsts[
flow].append(joined->begin(), joined->end());
2225 join(state, hop.first, hop.second);
2232 if (
st.partsOf.at(src).size() == 1) {
2236 for (
int id :
st.flowIds[
flow])
2240 for (
auto [in, out] : llvm::zip(setting.srcs, setting.dsts))
2241 if (!llvm::is_contained(llvm::zip(all.
srcs, all.
dsts),
2242 std::make_tuple(in, out))) {
2243 all.
srcs.push_back(in);
2244 all.
dsts.push_back(out);
2249llvm::Error Pathfinder::routePart(
RouteState &st,
int flow) {
2250 std::optional<TreeBuilder> tree;
2251 tree.emplace(*
this, st, flow);
2252 llvm::Error err = tree->pinned ? tree->placePinned() : tree->grow();
2257 for (
auto *pairs : {&st.
unmet, &st.
met})
2258 pairs->erase(pairs->lower_bound({flow, 0}),
2259 pairs->lower_bound({flow + 1, 0}));
2261 tree.emplace(*
this, st, flow);
2263 if (llvm::Error retry = tree->grow()) {
2264 llvm::consumeError(std::move(retry));
2267 llvm::consumeError(std::move(err));
2272 if (!tree->pinned && tree->reached.size() + tree->joinedAt.size() > 1)
2277 return llvm::Error::success();
2280int Pathfinder::applyRoutingFaults(
RouteState &st,
2281 const RoutingFaults &faults) {
2282 int illegalEdges = 0;
2283 for (
const TreeSplit &split : faults.
splits) {
2284 auto src = st.partsOf.find(split.
src);
2285 if (src == st.partsOf.end())
2287 auto partWith = [&](
int id) {
2288 for (
int k : src->second)
2289 if (st.flowIds[k].count(
id))
2293 int flow = partWith(split.
a), other = partWith(split.
b);
2294 if (flow < 0 || other < 0)
2296 std::optional<int> apart;
2298 auto it = nodeIds.find(*split.
apart);
2299 if (it == nodeIds.end())
2301 apart = stateId(it->second, Out);
2304 auto inseparable = [&] {
2305 const Flow &f = st.parts[flow];
2306 for (
const PathEndPoint &dst : f.
dsts) {
2307 auto ids = packetIdsTo.find({f.
src, dst});
2308 if (ids == packetIdsTo.end() ||
2309 !llvm::is_contained(ids->second, split.
a) ||
2310 !llvm::is_contained(ids->second, split.
b))
2312 const auto &tree = st.treeOf[flow];
2313 auto it = tree.find(stateId(nodeIds.at(dst), Out));
2314 if (it == tree.end() && dst == f.
src && dst.
coords == split.
at)
2316 for (; it != tree.end(); it = tree.find(it->second.first)) {
2317 int up = it->second.first;
2318 if ((up & 1) == In && nodes[stateNode(up)].coords == split.
at)
2324 if (idsApart && flow == other && !split.
apart &&
2325 !st.parts[flow].isPriorityFlow && inseparable())
2326 other = st.splitPart(flow, {split.
b});
2327 if (flow != other) {
2328 if (st.partSplits[flow].insert({split.at, other}).second)
2330 st.partSplits[other].insert({split.
at, flow});
2331 }
else if (st.splitsOf[flow]
2332 .insert({split.at, split.a, split.b, apart})
2334 LLVM_DEBUG(llvm::dbgs()
2335 <<
"Split ids " << split.
a <<
" and " << split.
b <<
" from "
2337 <<
" at tile (" << split.
at.col <<
", " << split.
at.row
2342 crowdedTiles.insert(faults.
crowded.begin(), faults.
crowded.end());
2345 auto splitOwnIds = [&](
const std::pair<PathEndPoint, PathEndPoint> &pair) {
2346 for (
auto [src, other] : {pair, std::pair{pair.second, pair.first}}) {
2347 auto srcParts = st.partsOf.find(src);
2348 auto otherParts = st.partsOf.find(other);
2349 if (srcParts == st.partsOf.end() || otherParts == st.partsOf.end())
2351 std::set<int> otherIds;
2352 for (
int k : otherParts->second)
2353 otherIds.insert(st.flowIds[k].begin(), st.flowIds[k].end());
2354 for (
int k : SmallVector<int, 2>(srcParts->second)) {
2356 for (
int id : st.flowIds[k])
2357 if (!otherIds.count(
id))
2359 if (!st.parts[k].isPriorityFlow && !own.empty() &&
2360 own.size() < st.flowIds[k].size())
2361 st.splitPart(k, own);
2365 bool related =
false;
2366 for (
const auto &pair : faults.
apart) {
2367 if (!st.apartSources.insert(pair).second)
2370 LLVM_DEBUG(llvm::dbgs()
2379 for (
const auto &pair : faults.
together) {
2380 if (!st.togetherSources.insert(pair).second)
2383 LLVM_DEBUG(llvm::dbgs()
2384 <<
"Route together: "
2395 for (
const PathEndPoint &src : faults.
untrunk)
2396 if (st.untrunked.insert(src).second) {
2397 LLVM_DEBUG(llvm::dbgs()
2402 std::set<std::pair<int, int>> faulted;
2403 bool unjoined =
false;
2404 for (
const auto &[tile, conn] : faults.
connections) {
2406 auto it = graph.find({tile, tile});
2407 if (it == graph.end())
2409 SwitchboxConnect &sb = it->second;
2415 bool joined =
false;
2416 for (
auto [flow, hops] : llvm::enumerate(st.joinedHops))
2417 for (
const auto &[other, e] : hops)
2418 if (e.sb == &sb && sb.
srcPorts[e.i] == conn.src &&
2420 faulted.insert({
static_cast<int>(flow), other});
2423 unjoined |= !joined;
2427 for (
auto [flow, other] : faulted)
2428 if (unjoined && st.spared.insert({flow, other}).second)
2430 else if (st.met.count({flow, other}) &&
2431 ++st.meetFaults[{std::min(flow, other), std::max(flow, other)}] <=
2433 st.reorder(flow, other,
true);
2435 st.noJoin.insert({flow, other});
2436 return illegalEdges;
2446 LLVM_DEBUG(llvm::dbgs() <<
"\t---Begin Pathfinder::findPaths---\n");
2447 checkReason.clear();
2448 packetsFailed =
true;
2452 buildRoutingGraph();
2453 if (llvm::Error err = cutOverflow()) {
2454 packetsFailed =
false;
2455 return std::move(err);
2457 crowdedTiles.clear();
2459 for (
auto &[_, sb] : graph) {
2461 for (
auto [j, port] : llvm::enumerate(sb.
dstPorts))
2463 port.channel >= packetFanoutCap &&
2474 int iterationCount = -1;
2475 int illegalEdges = 0;
2476 std::optional<Routing> usable;
2478 int fewestIllegal = std::numeric_limits<int>::max(), sinceFewest = 0;
2480 int reorderedStall = 0;
2481 int priorityFaults = 0;
2483 if (usable && (iterationCount + 1 >= maxIterations ||
2484 iterationCount - usableAt >= maxSteers)) {
2485 LLVM_DEBUG(llvm::dbgs() <<
"\t\tKeeping the usable routing\n");
2486 return std::move(*usable);
2491 if (++iterationCount >= maxIterations ||
2492 (fewestIllegal > 0 && sinceFewest >= maxOverusedStall) ||
2493 reorderedStall >= maxReorderedStall) {
2494 LLVM_DEBUG(llvm::dbgs()
2495 <<
"\t\tPathfinder: giving up after " << iterationCount
2497 << (iterationCount >= maxIterations ?
"the iteration limit"
2498 : reorderedStall >= maxReorderedStall
2499 ?
"circuit trees routed first stopped helping"
2500 :
"the overuse stalled")
2501 <<
")...unable to find routing for flows.\n");
2503 !checkReason.empty() || llvm::any_of(graph, [](
const auto &entry) {
2504 ArrayRef<SwitchboxConnect::Cell> cells = entry.second.cells;
2505 return llvm::any_of(cells,
2513 return explainNoRouting(st);
2516 LLVM_DEBUG(llvm::dbgs() <<
"\t\t---Begin findPaths iteration #"
2517 << iterationCount <<
"---\n");
2519 for (
auto &[_, sb] : graph) {
2526 for (
auto &[_, sb] : graph) {
2535 for (
auto &tree : st.
treeOf)
2550 for (
int flow : group)
2551 if (llvm::Error err = routePart(st, flow))
2552 return std::move(err);
2553 for (
auto &[_, sb] : graph) {
2568 for (
auto [later, earlier] : st.
unmet) {
2569 bool steered = st.
reorder(later, earlier);
2570 steered |= st.
trunked.insert({later, earlier}).second;
2571 steered |= st.
trunked.insert({earlier, later}).second;
2576 for (
auto &[_, sb] : graph) {
2577 for (
size_t i = 0; i < sb.
srcPorts.size(); i++) {
2578 for (
size_t j = 0; j < sb.
dstPorts.size(); j++) {
2587 LLVM_DEBUG(llvm::dbgs()
2588 <<
"\t\t\tToo much capacity on (" << sb.
srcCoords.col
2593 << sb.
dstPorts[j].channel <<
", used_capacity = "
2595 <<
", over_capacity_count = " << c.
overCapacity <<
"\n");
2604 if (illegalEdges < fewestIllegal) {
2605 fewestIllegal = illegalEdges;
2606 sinceFewest = reorderedStall = 0;
2610 if (illegalEdges && sinceFewest >= stallIterations) {
2611 auto losing = [&](
int flow) {
2612 return !st.
parts[flow].packetId &&
2613 llvm::any_of(st.
circuitHops[flow], [](
const Edge &e) {
2614 const SwitchboxConnect::Cell &c = e.sb->at(e.i, e.j);
2615 return c.usedCapacity > maxCircuitStreamCapacity &&
2616 c.packetGroupId < 0;
2621 moved |= llvm::any_of(group, losing);
2622 std::stable_partition(group.begin(), group.end(), losing);
2624 reorderedStall += moved;
2629 if (sinceFewest >= stallIterations && sinceFewest % stallIterations == 0) {
2630 LLVM_DEBUG(llvm::dbgs() <<
"Drop the id splits after " << sinceFewest
2631 <<
" stalled iterations\n");
2641 llvm::Error unmoved =
2642 illegalEdges != 0 || !constraints.check
2643 ? llvm::Error::success()
2644 : llvm::handleErrors(
2645 constraints.check(st.
routing),
2646 [&](std::unique_ptr<RoutingFailure> rejected) -> llvm::Error {
2647 LLVM_DEBUG(llvm::dbgs() <<
"Routing rejected: "
2648 << rejected->reason <<
'\n');
2649 illegalEdges += applyRoutingFaults(st, rejected->faults);
2650 priorityFaults = rejected->faults.onlyPriorityMasters
2651 ? priorityFaults + 1
2653 stuck = illegalEdges == 0;
2654 if (rejected->isA(DeadlockProneRouting::classID())) {
2656 usable = st.routing;
2657 usableAt = iterationCount;
2659 return llvm::Error::success();
2662 return llvm::Error(std::move(rejected));
2663 checkReason = std::move(rejected->reason);
2664 return llvm::Error::success();
2669 llvm::dbgs() <<
"\t\t\tFlow starting at (" << src.
coords.col <<
","
2670 << src.
coords.row <<
"):\t" << settings;
2672 int totalPathLength = 0;
2673 for (
const auto &[_, sb] : graph)
2677 llvm::dbgs() <<
"\t\t---End findPaths iteration #" << iterationCount
2678 <<
" , illegal edges count = " << illegalEdges
2679 <<
", total path length = " << totalPathLength <<
"---\n";
2681 if (stuck && usable) {
2682 llvm::consumeError(std::move(unmoved));
2683 return std::move(*usable);
2686 return std::move(unmoved);
2687 if (priorityFaults >= maxPriorityFaults) {
2688 LLVM_DEBUG(llvm::dbgs() <<
"\t\tPathfinder: the routing check rejects "
2689 "only master sets of the prioritized flows ("
2690 << priorityFaults <<
" routings in a row)\n");
2692 return std::move(*usable);
2693 packetsFailed =
false;
2694 return explainNoRouting(st);
2697 }
while (illegalEdges > 0);
2699 LLVM_DEBUG(llvm::dbgs() <<
"\t---End Pathfinder::findPaths---\n");
2700 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
std::vector< std::pair< TileID, Port > > avoidedPorts
void avoidPort(TileID coords, Port port)
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::vector< SmallVector< int, 4 > > prioritySets
std::set< std::pair< int, int > > noJoin
std::set< PathEndPoint > untrunked
std::vector< llvm::DenseMap< int, std::pair< int, Edge > > > treeOf
llvm::DenseSet< int > priorityMasters
std::map< std::pair< int, int >, int > meetFaults
std::vector< SmallVector< Edge, 8 > > circuitHops
std::set< std::pair< int, int > > reordered
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< 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