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