MLIR-AIE
AIEPathFinder.cpp
Go to the documentation of this file.
1//===- AIEPathfinder.cpp ----------------------------------------*- C++ -*-===//
2//
3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7// (c) Copyright 2021 Xilinx Inc.
8//
9//===----------------------------------------------------------------------===//
10
12#include "d_ary_heap.h"
13
14#include "llvm/Support/Debug.h"
15#include "llvm/Support/raw_os_ostream.h"
16
17#include "llvm/ADT/MapVector.h"
18
19using namespace mlir;
20using namespace xilinx;
21using namespace xilinx::AIE;
22
23#define DEBUG_TYPE "aie-pathfinder"
24
25LogicalResult DynamicTileAnalysis::runAnalysis(DeviceOp &device) {
26 LLVM_DEBUG(llvm::dbgs() << "\t---Begin DynamicTileAnalysis Constructor---\n");
27 // find the maxCol and maxRow
28 maxCol = device.getTargetModel().columns();
29 maxRow = device.getTargetModel().rows();
30
31 pathfinder->initialize(maxCol, maxRow, device.getTargetModel());
32
33 // For each flow (circuit + packet) in the device, add it to pathfinder. Each
34 // source can map to multiple different destinations (fanout). Control packet
35 // flows to be routed (as prioritized routings). Then followed by normal
36 // packet flows.
37 for (PacketFlowOp pktFlowOp : device.getOps<PacketFlowOp>()) {
38 Region &r = pktFlowOp.getPorts();
39 Block &b = r.front();
40 Port srcPort, dstPort;
41 TileOp srcTile, dstTile;
43 for (Operation &Op : b.getOperations()) {
44 if (auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
45 srcTile = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
46 srcPort = pktSource.port();
47 srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
48 } else if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
49 dstTile = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
50 dstPort = pktDest.port();
51 dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
52 LLVM_DEBUG(llvm::dbgs()
53 << "\tAdding Packet Flow: (" << srcCoords.col << ", "
54 << srcCoords.row << ")"
55 << stringifyWireBundle(srcPort.bundle) << srcPort.channel
56 << " -> (" << dstCoords.col << ", " << dstCoords.row << ")"
57 << stringifyWireBundle(dstPort.bundle) << dstPort.channel
58 << "\n");
59 // todo: support many-to-one & many-to-many?
60 bool priorityFlow =
61 pktFlowOp.getPriorityRoute()
62 ? *pktFlowOp.getPriorityRoute()
63 : false; // Flows such as control packet flows are routed in
64 // priority, to ensure routing consistency.
65 pathfinder->addFlow(srcCoords, srcPort, dstCoords, dstPort,
66 /*isPktFlow*/ true, priorityFlow);
67 }
68 }
69 }
70
71 // Sort ctrlPktFlows into a deterministic order; concat ctrlPktFlows to flows
72 pathfinder->sortFlows(device.getTargetModel().columns(),
73 device.getTargetModel().rows());
74
75 // Add circuit flows.
76 for (FlowOp flowOp : device.getOps<FlowOp>()) {
77 TileOp srcTile = cast<TileOp>(flowOp.getSource().getDefiningOp());
78 TileOp dstTile = cast<TileOp>(flowOp.getDest().getDefiningOp());
79 TileID srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
80 TileID dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
81 Port srcPort = {flowOp.getSourceBundle(), flowOp.getSourceChannel()};
82 Port dstPort = {flowOp.getDestBundle(), flowOp.getDestChannel()};
83 LLVM_DEBUG(llvm::dbgs()
84 << "\tAdding Flow: (" << srcCoords.col << ", " << srcCoords.row
85 << ")" << stringifyWireBundle(srcPort.bundle) << srcPort.channel
86 << " -> (" << dstCoords.col << ", " << dstCoords.row << ")"
87 << stringifyWireBundle(dstPort.bundle) << dstPort.channel
88 << "\n");
89 pathfinder->addFlow(srcCoords, srcPort, dstCoords, dstPort,
90 /*isPktFlow*/ false, /*isPriorityFlow*/ false);
91 }
92
93 // add existing connections so Pathfinder knows which resources are
94 // available search all existing SwitchBoxOps for exising connections
95 for (SwitchboxOp switchboxOp : device.getOps<SwitchboxOp>()) {
96 if (!pathfinder->addFixedConnection(switchboxOp))
97 return switchboxOp.emitOpError() << "Unable to add fixed connections";
98 }
99
100 // all flows are now populated, call the congestion-aware pathfinder
101 // algorithm
102 // check whether the pathfinder algorithm creates a legal routing
103 if (auto maybeFlowSolutions = pathfinder->findPaths(maxIterations))
104 flowSolutions = maybeFlowSolutions.value();
105 else
106 return device.emitError("Unable to find a legal routing");
107
108 // initialize all flows as unprocessed to prep for rewrite
109 for (const auto &[PathEndPoint, switchSetting] : flowSolutions) {
111 }
112
113 // fill in coords to TileOps, SwitchboxOps, and ShimMuxOps
114 for (auto tileOp : device.getOps<TileOp>()) {
115 int col, row;
116 col = tileOp.colIndex();
117 row = tileOp.rowIndex();
118 assert(coordToTile.count({col, row}) == 0);
119 coordToTile[{col, row}] = tileOp;
120 }
121 for (auto switchboxOp : device.getOps<SwitchboxOp>()) {
122 int col = switchboxOp.colIndex();
123 int row = switchboxOp.rowIndex();
124 assert(coordToSwitchbox.count({col, row}) == 0);
125 coordToSwitchbox[{col, row}] = switchboxOp;
126 }
127 for (auto shimmuxOp : device.getOps<ShimMuxOp>()) {
128 int col = shimmuxOp.colIndex();
129 int row = shimmuxOp.rowIndex();
130 assert(coordToShimMux.count({col, row}) == 0);
131 coordToShimMux[{col, row}] = shimmuxOp;
132 }
133
134 LLVM_DEBUG(llvm::dbgs() << "\t---End DynamicTileAnalysis Constructor---\n");
135 return success();
136}
137
138TileOp DynamicTileAnalysis::getTile(OpBuilder &builder, int col, int row) {
139 if (coordToTile.count({col, row})) {
140 return coordToTile[{col, row}];
141 }
142 auto tileOp = builder.create<TileOp>(builder.getUnknownLoc(), col, row);
143 coordToTile[{col, row}] = tileOp;
144 return tileOp;
145}
146
147SwitchboxOp DynamicTileAnalysis::getSwitchbox(OpBuilder &builder, int col,
148 int row) {
149 assert(col >= 0);
150 assert(row >= 0);
151 if (coordToSwitchbox.count({col, row})) {
152 return coordToSwitchbox[{col, row}];
153 }
154 auto switchboxOp = builder.create<SwitchboxOp>(builder.getUnknownLoc(),
155 getTile(builder, col, row));
156 SwitchboxOp::ensureTerminator(switchboxOp.getConnections(), builder,
157 builder.getUnknownLoc());
158 coordToSwitchbox[{col, row}] = switchboxOp;
159 return switchboxOp;
160}
161
162ShimMuxOp DynamicTileAnalysis::getShimMux(OpBuilder &builder, int col) {
163 assert(col >= 0);
164 int row = 0;
165 if (coordToShimMux.count({col, row})) {
166 return coordToShimMux[{col, row}];
167 }
168 assert(getTile(builder, col, row).isShimNOCorPLTile());
169 auto switchboxOp = builder.create<ShimMuxOp>(builder.getUnknownLoc(),
170 getTile(builder, col, row));
171 SwitchboxOp::ensureTerminator(switchboxOp.getConnections(), builder,
172 builder.getUnknownLoc());
173 coordToShimMux[{col, row}] = switchboxOp;
174 return switchboxOp;
175}
176
177void Pathfinder::initialize(int maxCol, int maxRow,
178 const AIETargetModel &targetModel) {
179
180 std::map<WireBundle, int> maxChannels;
181 auto intraconnect = [&](int col, int row) {
182 TileID coords = {col, row};
184
185 for (int i = 0, e = getMaxEnumValForWireBundle() + 1; i < e; ++i) {
186 WireBundle bundle = symbolizeWireBundle(i).value();
187 // get all ports into current switchbox
188 int channels =
189 targetModel.getNumSourceSwitchboxConnections(col, row, bundle);
190 if (channels == 0 && targetModel.isShimNOCorPLTile(col, row)) {
191 // wordaround for shimMux
192 channels = targetModel.getNumSourceShimMuxConnections(col, row, bundle);
193 }
194 for (int channel = 0; channel < channels; channel++) {
195 sb.srcPorts.push_back(Port{bundle, channel});
196 }
197 // get all ports out of current switchbox
198 channels = targetModel.getNumDestSwitchboxConnections(col, row, bundle);
199 if (channels == 0 && targetModel.isShimNOCorPLTile(col, row)) {
200 // wordaround for shimMux
201 channels = targetModel.getNumDestShimMuxConnections(col, row, bundle);
202 }
203 for (int channel = 0; channel < channels; channel++) {
204 sb.dstPorts.push_back(Port{bundle, channel});
205 }
206 maxChannels[bundle] = channels;
207 }
208 // initialize matrices
209 sb.resize();
210 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
211 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
212 auto &pIn = sb.srcPorts[i];
213 auto &pOut = sb.dstPorts[j];
214 if (targetModel.isLegalTileConnection(col, row, pIn.bundle, pIn.channel,
215 pOut.bundle, pOut.channel))
216 sb.connectivity[i][j] = Connectivity::AVAILABLE;
217 else {
218 sb.connectivity[i][j] = Connectivity::INVALID;
219 if (targetModel.isShimNOCorPLTile(col, row)) {
220 // wordaround for shimMux
221 auto isBundleInList = [](WireBundle bundle,
222 std::vector<WireBundle> bundles) {
223 return std::find(bundles.begin(), bundles.end(), bundle) !=
224 bundles.end();
225 };
226 const std::vector<WireBundle> bundles = {
227 WireBundle::DMA, WireBundle::NOC, WireBundle::PLIO};
228 if (isBundleInList(pIn.bundle, bundles) ||
229 isBundleInList(pOut.bundle, bundles))
230 sb.connectivity[i][j] = Connectivity::AVAILABLE;
231 }
232 }
233 }
234 }
235 graph[std::make_pair(coords, coords)] = sb;
236 };
237
238 auto interconnect = [&](int col, int row, int targetCol, int targetRow,
239 WireBundle srcBundle, WireBundle dstBundle) {
240 SwitchboxConnect sb = {{col, row}, {targetCol, targetRow}};
241 for (int channel = 0; channel < maxChannels[srcBundle]; channel++) {
242 sb.srcPorts.push_back(Port{srcBundle, channel});
243 sb.dstPorts.push_back(Port{dstBundle, channel});
244 }
245 sb.resize();
246 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
247 sb.connectivity[i][i] = Connectivity::AVAILABLE;
248 }
249 graph[std::make_pair(TileID{col, row}, TileID{targetCol, targetRow})] = sb;
250 };
251
252 for (int row = 0; row <= maxRow; row++) {
253 for (int col = 0; col <= maxCol; col++) {
254 maxChannels.clear();
255 // connections within the same switchbox
256 intraconnect(col, row);
257
258 // connections between switchboxes
259 if (row > 0) {
260 // from south to north
261 interconnect(col, row, col, row - 1, WireBundle::South,
262 WireBundle::North);
263 }
264 if (row < maxRow) {
265 // from north to south
266 interconnect(col, row, col, row + 1, WireBundle::North,
267 WireBundle::South);
268 }
269 if (col > 0) {
270 // from east to west
271 interconnect(col, row, col - 1, row, WireBundle::West,
272 WireBundle::East);
273 }
274 if (col < maxCol) {
275 // from west to east
276 interconnect(col, row, col + 1, row, WireBundle::East,
277 WireBundle::West);
278 }
279 }
280 }
281}
282
283// Add a flow from src to dst can have an arbitrary number of dst locations
284// due to fanout.
286 Port dstPort, bool isPacketFlow, bool isPriorityFlow) {
287 // check if a flow with this source already exists
288 for (auto &[_, prioritized, src, dsts] : flows) {
289 if (src.coords == srcCoords && src.port == srcPort) {
290 if (isPriorityFlow) {
291 prioritized = true;
292 dsts.emplace(dsts.begin(), PathEndPoint{dstCoords, dstPort});
293 } else
294 dsts.emplace_back(PathEndPoint{dstCoords, dstPort});
295 return;
296 }
297 }
298
299 // Assign a group ID for packet flows
300 // any overlapping in source/destination will lead to the same group ID
301 // channel sharing will happen within the same group ID
302 // for circuit flows, group ID is always -1, and no channel sharing
303 int packetGroupId = -1;
304 if (isPacketFlow) {
305 bool found = false;
306 for (auto &[existingId, _, src, dsts] : flows) {
307 if (src.coords == srcCoords && src.port == srcPort) {
308 packetGroupId = existingId;
309 found = true;
310 break;
311 }
312 for (auto &dst : dsts) {
313 if (dst.coords == dstCoords && dst.port == dstPort) {
314 packetGroupId = existingId;
315 found = true;
316 break;
317 }
318 }
319 packetGroupId = std::max(packetGroupId, existingId);
320 }
321 if (!found) {
323 }
324 }
325 // If no existing flow was found with this source, create a new flow.
326 flows.push_back(
328 std::vector<PathEndPoint>{PathEndPoint{dstCoords, dstPort}}});
329}
330
331// Sort flows to (1) get deterministic routing, and (2) perform routings on
332// prioritized flows before others, for routing consistency on those flows.
333void Pathfinder::sortFlows(const int maxCol, const int maxRow) {
334 std::vector<Flow> priorityFlows;
335 std::vector<Flow> normalFlows;
336 for (auto f : flows) {
337 if (f.isPriorityFlow)
338 priorityFlows.push_back(f);
339 else
340 normalFlows.push_back(f);
341 }
342 // Get unique int identifier from a vector if pairs of int properties and
343 // their maximums.
344 auto getUniqueIdFromVecOfProperties =
345 [](std::vector<std::pair<int, int>> propertiesAndLimits) {
346 int uniqueId = 0;
347 int multiplier = 1;
348 for (auto pair : propertiesAndLimits) {
349 uniqueId += pair.first * multiplier;
350 multiplier *= pair.second;
351 }
352 return uniqueId;
353 };
354 std::sort(priorityFlows.begin(), priorityFlows.end(),
355 [maxCol, maxRow, getUniqueIdFromVecOfProperties](const auto &lhs,
356 const auto &rhs) {
357 // List of properties used in sorting: src col, src row, src
358 // wirebundle and src channel.
359 std::vector<std::pair<int, int>> lhsProperties = {
360 {lhs.src.coords.col, maxCol},
361 {lhs.src.coords.row, maxRow},
362 {getWireBundleAsInt(lhs.src.port.bundle),
363 AIE::getMaxEnumValForWireBundle()},
364 {lhs.src.port.channel, /*don't care*/ 0}};
365 int lhsUniqueID = getUniqueIdFromVecOfProperties(lhsProperties);
366 std::vector<std::pair<int, int>> rhsProperties = {
367 {rhs.src.coords.col, maxCol},
368 {rhs.src.coords.row, maxRow},
369 {getWireBundleAsInt(rhs.src.port.bundle),
370 AIE::getMaxEnumValForWireBundle()},
371 {rhs.src.port.channel, /*don't care*/ 0}};
372 int rhsUniqueID = getUniqueIdFromVecOfProperties(rhsProperties);
373 return lhsUniqueID < rhsUniqueID;
374 });
375 flows = priorityFlows;
376 flows.insert(flows.end(), normalFlows.begin(), normalFlows.end());
377}
378
379// Keep track of connections already used in the AIE; Pathfinder algorithm
380// will avoid using these.
381bool Pathfinder::addFixedConnection(SwitchboxOp switchboxOp) {
382 int col = switchboxOp.colIndex();
383 int row = switchboxOp.rowIndex();
384 TileID coords = {col, row};
385 auto &sb = graph[std::make_pair(coords, coords)];
386 for (ConnectOp connectOp : switchboxOp.getOps<ConnectOp>()) {
387 bool found = false;
388 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
389 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
390 if (sb.srcPorts[i] == connectOp.sourcePort() &&
391 sb.dstPorts[j] == connectOp.destPort() &&
392 sb.connectivity[i][j] == Connectivity::AVAILABLE) {
393 sb.connectivity[i][j] = Connectivity::INVALID;
394 found = true;
395 }
396 }
397 }
398 if (!found) {
399 // could not add such a fixed connection
400 return false;
401 }
402 }
403 return true;
404}
405
406static constexpr double INF = std::numeric_limits<double>::max();
407
408std::map<PathEndPoint, PathEndPoint>
410 // Use std::map instead of DenseMap because DenseMap doesn't let you
411 // overwrite tombstones.
412 std::map<PathEndPoint, double> distance;
413 std::map<PathEndPoint, PathEndPoint> preds;
414 std::map<PathEndPoint, uint64_t> indexInHeap;
415 enum Color { WHITE, GRAY, BLACK };
416 std::map<PathEndPoint, Color> colors;
417 typedef d_ary_heap_indirect<
418 /*Value=*/PathEndPoint, /*Arity=*/4,
419 /*IndexInHeapPropertyMap=*/std::map<PathEndPoint, uint64_t>,
420 /*DistanceMap=*/std::map<PathEndPoint, double> &,
421 /*Compare=*/std::less<>>
422 MutableQueue;
423 MutableQueue Q(distance, indexInHeap);
424
425 distance[src] = 0.0;
426 Q.push(src);
427 while (!Q.empty()) {
428 src = Q.top();
429 Q.pop();
430
431 // get all channels src connects to
432 if (channels.count(src) == 0) {
433 auto &sb = graph[std::make_pair(src.coords, src.coords)];
434 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
435 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
436 if (sb.srcPorts[i] == src.port &&
437 sb.connectivity[i][j] == Connectivity::AVAILABLE) {
438 // connections within the same switchbox
439 channels[src].push_back(PathEndPoint{src.coords, sb.dstPorts[j]});
440 }
441 }
442 }
443 // connections to neighboring switchboxes
444 std::vector<std::pair<TileID, Port>> neighbors = {
445 {{src.coords.col, src.coords.row - 1},
446 {WireBundle::North, src.port.channel}},
447 {{src.coords.col - 1, src.coords.row},
448 {WireBundle::East, src.port.channel}},
449 {{src.coords.col, src.coords.row + 1},
450 {WireBundle::South, src.port.channel}},
451 {{src.coords.col + 1, src.coords.row},
452 {WireBundle::West, src.port.channel}}};
453
454 for (const auto &[neighborCoords, neighborPort] : neighbors) {
455 if (graph.count(std::make_pair(src.coords, neighborCoords)) > 0 &&
456 src.port.bundle == getConnectingBundle(neighborPort.bundle)) {
457 auto &sb = graph[std::make_pair(src.coords, neighborCoords)];
458 if (std::find(sb.dstPorts.begin(), sb.dstPorts.end(), neighborPort) !=
459 sb.dstPorts.end())
460 channels[src].push_back({neighborCoords, neighborPort});
461 }
462 }
463 std::sort(channels[src].begin(), channels[src].end());
464 }
465
466 for (auto &dest : channels[src]) {
467 if (distance.count(dest) == 0)
468 distance[dest] = INF;
469 auto &sb = graph[std::make_pair(src.coords, dest.coords)];
470 size_t i = std::distance(
471 sb.srcPorts.begin(),
472 std::find(sb.srcPorts.begin(), sb.srcPorts.end(), src.port));
473 size_t j = std::distance(
474 sb.dstPorts.begin(),
475 std::find(sb.dstPorts.begin(), sb.dstPorts.end(), dest.port));
476 assert(i < sb.srcPorts.size());
477 assert(j < sb.dstPorts.size());
478 bool relax = distance[src] + sb.demand[i][j] < distance[dest];
479 if (colors.count(dest) == 0) {
480 // was WHITE
481 if (relax) {
482 distance[dest] = distance[src] + sb.demand[i][j];
483 preds[dest] = src;
484 colors[dest] = GRAY;
485 }
486 Q.push(dest);
487 } else if (colors[dest] == GRAY && relax) {
488 distance[dest] = distance[src] + sb.demand[i][j];
489 preds[dest] = src;
490 }
491 }
492 colors[src] = BLACK;
493 }
494
495 return preds;
496}
497
498// Perform congestion-aware routing for all flows which have been added.
499// Use Dijkstra's shortest path to find routes, and use "demand" as the
500// weights. If the routing finds too much congestion, update the demand
501// weights and repeat the process until a valid solution is found. Returns a
502// map specifying switchbox settings for all flows. If no legal routing can be
503// found after maxIterations, returns empty vector.
504std::optional<std::map<PathEndPoint, SwitchSettings>>
505Pathfinder::findPaths(const int maxIterations) {
506 LLVM_DEBUG(llvm::dbgs() << "\t---Begin Pathfinder::findPaths---\n");
507 std::map<PathEndPoint, SwitchSettings> routingSolution;
508 // initialize all Channel histories to 0
509 for (auto &[_, sb] : graph) {
510 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
511 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
512 sb.usedCapacity[i][j] = 0;
513 sb.overCapacity[i][j] = 0;
514 sb.isPriority[i][j] = false;
515 }
516 }
517 }
518
519 // group flows based on packetGroupId
520 llvm::MapVector<int, std::vector<Flow>> groupedFlows;
521 for (auto &f : flows) {
522 if (groupedFlows.count(f.packetGroupId) == 0) {
523 groupedFlows[f.packetGroupId] = std::vector<Flow>();
524 }
525 groupedFlows[f.packetGroupId].push_back(f);
526 }
527
528 int iterationCount = -1;
529 int illegalEdges = 0;
530#ifndef NDEBUG
531 int totalPathLength = 0;
532#endif
533 do {
534 // if reach maxIterations, throw an error since no routing can be found
535 if (++iterationCount >= maxIterations) {
536 LLVM_DEBUG(llvm::dbgs()
537 << "\t\tPathfinder: maxIterations has been exceeded ("
538 << maxIterations
539 << " iterations)...unable to find routing for flows.\n");
540 return std::nullopt;
541 }
542
543 LLVM_DEBUG(llvm::dbgs() << "\t\t---Begin findPaths iteration #"
544 << iterationCount << "---\n");
545 // update demand at the beginning of each iteration
546 for (auto &[_, sb] : graph) {
547 sb.updateDemand();
548 }
549
550 // "rip up" all routes
551 illegalEdges = 0;
552#ifndef NDEBUG
553 totalPathLength = 0;
554#endif
555 routingSolution.clear();
556 for (auto &[_, sb] : graph) {
557 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
558 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
559 sb.usedCapacity[i][j] = 0;
560 sb.packetFlowCount[i][j] = 0;
561 sb.packetGroupId[i][j] = -1;
562 }
563 }
564 }
565
566 // for each flow, find the shortest path from source to destination
567 // update used_capacity for the path between them
568
569 for (const auto &[_, flows] : groupedFlows) {
570 for (const auto &[packetGroupId, isPriority, src, dsts] : flows) {
571 // Use dijkstra to find path given current demand from the start
572 // switchbox; find the shortest paths to each other switchbox. Output is
573 // in the predecessor map, which must then be processed to get
574 // individual switchbox settings
575 std::set<PathEndPoint> processed;
576 std::map<PathEndPoint, PathEndPoint> preds = dijkstraShortestPaths(src);
577
578 // trace the path of the flow backwards via predecessors
579 // increment used_capacity for the associated channels
580 SwitchSettings switchSettings;
581 processed.insert(src);
582 for (auto endPoint : dsts) {
583 if (endPoint == src) {
584 // route to self
585 switchSettings[src.coords].srcs.push_back(src.port);
586 switchSettings[src.coords].dsts.push_back(src.port);
587 }
588 auto curr = endPoint;
589 // trace backwards until a vertex already processed is reached
590 while (!processed.count(curr)) {
591 auto &sb = graph[std::make_pair(preds[curr].coords, curr.coords)];
592 size_t i =
593 std::distance(sb.srcPorts.begin(),
594 std::find(sb.srcPorts.begin(), sb.srcPorts.end(),
595 preds[curr].port));
596 size_t j = std::distance(
597 sb.dstPorts.begin(),
598 std::find(sb.dstPorts.begin(), sb.dstPorts.end(), curr.port));
599 assert(i < sb.srcPorts.size());
600 assert(j < sb.dstPorts.size());
601 sb.isPriority[i][j] = isPriority;
602 if (packetGroupId >= 0 &&
603 (sb.packetGroupId[i][j] == -1 ||
604 sb.packetGroupId[i][j] == packetGroupId)) {
605 for (size_t k = 0; k < sb.srcPorts.size(); k++) {
606 for (size_t l = 0; l < sb.dstPorts.size(); l++) {
607 if (k == i || l == j) {
608 sb.packetGroupId[k][l] = packetGroupId;
609 }
610 }
611 }
612 sb.packetFlowCount[i][j]++;
613 // maximum packet stream sharing per channel
614 if (sb.packetFlowCount[i][j] >= MAX_PACKET_STREAM_CAPACITY) {
615 sb.packetFlowCount[i][j] = 0;
616 sb.usedCapacity[i][j]++;
617 }
618 } else {
619 sb.usedCapacity[i][j]++;
620 }
621 // if at capacity, bump demand to discourage using this Channel
622 // this means the order matters!
623 sb.bumpDemand(i, j);
624 if (preds[curr].coords == curr.coords) {
625 switchSettings[preds[curr].coords].srcs.push_back(
626 preds[curr].port);
627 switchSettings[curr.coords].dsts.push_back(curr.port);
628 }
629 processed.insert(curr);
630 curr = preds[curr];
631 }
632 }
633 // add this flow to the proposed solution
634 routingSolution[src] = switchSettings;
635 }
636 for (auto &[_, sb] : graph) {
637 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
638 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
639 // fix used capacity for packet flows
640 if (sb.packetFlowCount[i][j] > 0) {
641 sb.packetFlowCount[i][j] = 0;
642 sb.usedCapacity[i][j]++;
643 }
644 sb.bumpDemand(i, j);
645 }
646 }
647 }
648 }
649
650 for (auto &[_, sb] : graph) {
651 for (size_t i = 0; i < sb.srcPorts.size(); i++) {
652 for (size_t j = 0; j < sb.dstPorts.size(); j++) {
653 // check that every channel does not exceed max capacity
654 if (sb.usedCapacity[i][j] > MAX_CIRCUIT_STREAM_CAPACITY) {
655 sb.overCapacity[i][j]++;
656 illegalEdges++;
657 LLVM_DEBUG(
658 llvm::dbgs()
659 << "\t\t\tToo much capacity on (" << sb.srcCoords.col << ","
660 << sb.srcCoords.row << ") " << sb.srcPorts[i].bundle
661 << sb.srcPorts[i].channel << " -> (" << sb.dstCoords.col << ","
662 << sb.dstCoords.row << ") " << sb.dstPorts[j].bundle
663 << sb.dstPorts[j].channel << ", used_capacity = "
664 << sb.usedCapacity[i][j] << ", demand = " << sb.demand[i][j]
665 << ", over_capacity_count = " << sb.overCapacity[i][j] << "\n");
666 }
667#ifndef NDEBUG
668 // calculate total path length (across switchboxes)
669 if (sb.srcCoords != sb.dstCoords) {
670 totalPathLength += sb.usedCapacity[i][j];
671 }
672#endif
673 }
674 }
675 }
676
677#ifndef NDEBUG
678 for (const auto &[PathEndPoint, switchSetting] : routingSolution) {
679 LLVM_DEBUG(llvm::dbgs()
680 << "\t\t\tFlow starting at (" << PathEndPoint.coords.col << ","
681 << PathEndPoint.coords.row << "):\t");
682 LLVM_DEBUG(llvm::dbgs() << switchSetting);
683 }
684 LLVM_DEBUG(llvm::dbgs()
685 << "\t\t---End findPaths iteration #" << iterationCount
686 << " , illegal edges count = " << illegalEdges
687 << ", total path length = " << totalPathLength << "---\n");
688#endif
689 } while (illegalEdges >
690 0); // continue iterations until a legal routing is found
691
692 LLVM_DEBUG(llvm::dbgs() << "\t---End Pathfinder::findPaths---\n");
693 return routingSolution;
694}
695
696// Get enum int value from WireBundle.
697int AIE::getWireBundleAsInt(WireBundle bundle) {
698 return static_cast<typename std::underlying_type<WireBundle>::type>(bundle);
699}
#define MAX_CIRCUIT_STREAM_CAPACITY
#define MAX_PACKET_STREAM_CAPACITY
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
virtual bool isShimNOCorPLTile(int col, int row) const =0
Return true if the given tile is either a Shim NOC or a Shim PL interface 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)
llvm::DenseMap< TileID, SwitchboxOp > coordToSwitchbox
TileOp getTile(mlir::OpBuilder &builder, int col, int row)
llvm::DenseMap< TileID, ShimMuxOp > coordToShimMux
std::shared_ptr< Router > pathfinder
std::map< PathEndPoint, bool > processedFlows
mlir::LogicalResult runAnalysis(DeviceOp &device)
llvm::DenseMap< TileID, TileOp > coordToTile
std::map< PathEndPoint, SwitchSettings > flowSolutions
std::map< PathEndPoint, PathEndPoint > dijkstraShortestPaths(PathEndPoint src)
bool addFixedConnection(SwitchboxOp switchboxOp) override
void initialize(int maxCol, int maxRow, const AIETargetModel &targetModel) override
std::optional< std::map< PathEndPoint, SwitchSettings > > findPaths(int maxIterations) override
void sortFlows(const int maxCol, const int maxRow) override
void addFlow(TileID srcCoords, Port srcPort, TileID dstCoords, Port dstPort, bool isPacketFlow, bool isPriorityFlow) override
Include the generated interface declarations.
int getWireBundleAsInt(WireBundle bundle)
SwitchboxConnect { SwitchboxConnect()=default SwitchboxConnect
std::vector< PathEndPoint > dsts
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
std::vector< std::vector< int > > packetGroupId
std::map< TileID, SwitchSetting > SwitchSettings
TileID dstCoords
Port { WireBundle bundle Port
Definition AIEDialect.h:119
PathEndPoint src
std::vector< std::vector< bool > > isPriority
Flow { int packetGroupId Flow
TileID srcCoords
PathEndPoint { PathEndPoint()=default PathEndPoint
WireBundle getConnectingBundle(WireBundle dir)