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