MLIR-AIE
AIECreatePathFindFlows.cpp
Go to the documentation of this file.
1//===- AIECreatePathfindFlows.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
14
15#include "mlir/IR/IRMapping.h"
16#include "mlir/IR/PatternMatch.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
19#include "mlir/Transforms/DialectConversion.h"
20#include "llvm/Support/Debug.h"
21
22using namespace mlir;
23using namespace xilinx;
24using namespace xilinx::AIE;
25
26#define DEBUG_TYPE "aie-create-pathfinder-flows"
27
28namespace {
29// allocates channels between switchboxes ( but does not assign them)
30// instantiates shim-muxes AND allocates channels ( no need to rip these up in )
31struct ConvertFlowsToInterconnect : OpConversionPattern<FlowOp> {
32 using OpConversionPattern::OpConversionPattern;
33 DeviceOp &device;
34 DynamicTileAnalysis &analyzer;
35 ConvertFlowsToInterconnect(MLIRContext *context, DeviceOp &d,
36 DynamicTileAnalysis &a, PatternBenefit benefit = 1)
37 : OpConversionPattern(context, benefit), device(d), analyzer(a) {}
38
39 void addConnection(ConversionPatternRewriter &rewriter,
40 // could be a shim-mux or a switchbox.
41 Interconnect op, FlowOp flowOp, WireBundle inBundle,
42 int inIndex, WireBundle outBundle, int outIndex) const {
43
44 Region &r = op.getConnections();
45 Block &b = r.front();
46 auto point = rewriter.saveInsertionPoint();
47 rewriter.setInsertionPoint(b.getTerminator());
48
49 ConnectOp::create(rewriter, rewriter.getUnknownLoc(), inBundle, inIndex,
50 outBundle, outIndex);
51
52 rewriter.restoreInsertionPoint(point);
53
54 LLVM_DEBUG(llvm::dbgs()
55 << "\t\taddConnection() (" << op.colIndex() << ","
56 << op.rowIndex() << ") " << stringifyWireBundle(inBundle)
57 << inIndex << " -> " << stringifyWireBundle(outBundle)
58 << outIndex << "\n");
59 }
60
61 mlir::LogicalResult
62 matchAndRewrite(FlowOp flowOp, OpAdaptor adaptor,
63 ConversionPatternRewriter &rewriter) const override {
64 Operation *Op = flowOp.getOperation();
65 DeviceOp d = flowOp->getParentOfType<DeviceOp>();
66 if (!d) {
67 flowOp->emitOpError("This operation must be contained within a device");
68 return failure();
69 }
70 rewriter.setInsertionPoint(d.getBody()->getTerminator());
71
72 auto srcTile = cast<TileOp>(flowOp.getSource().getDefiningOp());
73 TileID srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
74 auto srcBundle = flowOp.getSourceBundle();
75 auto srcChannel = flowOp.getSourceChannel();
76 Port srcPort = {srcBundle, srcChannel};
77
78#ifndef NDEBUG
79 auto dstTile = cast<TileOp>(flowOp.getDest().getDefiningOp());
80 TileID dstCoords = {dstTile.colIndex(), dstTile.rowIndex()};
81 auto dstBundle = flowOp.getDestBundle();
82 auto dstChannel = flowOp.getDestChannel();
83 LLVM_DEBUG(llvm::dbgs()
84 << "\n\t---Begin rewrite() for flowOp: (" << srcCoords.col
85 << ", " << srcCoords.row << ")" << stringifyWireBundle(srcBundle)
86 << srcChannel << " -> (" << dstCoords.col << ", "
87 << dstCoords.row << ")" << stringifyWireBundle(dstBundle)
88 << dstChannel << "\n\t");
89#endif
90
91 // if the flow (aka "net") for this FlowOp hasn't been processed yet,
92 // add all switchbox connections to implement the flow
93 TileID srcSbId = {srcCoords.col, srcCoords.row};
94 PathEndPoint srcPoint = {srcSbId, srcPort};
95 if (analyzer.processedFlows[srcPoint]) {
96 LLVM_DEBUG(llvm::dbgs() << "Flow already processed!\n");
97 rewriter.eraseOp(Op);
98 return failure();
99 }
100 // std::map<TileID, SwitchSetting>
101 SwitchSettings settings = analyzer.flowSolutions[srcPoint];
102 // add connections for all the Switchboxes in SwitchSettings
103 for (const auto &[tileId, setting] : settings) {
104 int col = tileId.col;
105 int row = tileId.row;
106 SwitchboxOp swOp = analyzer.getSwitchbox(rewriter, col, row);
107 int shimCh = srcChannel;
108 bool isShim = analyzer.getTile(rewriter, tileId).isShimNOCorPLTile();
109
110 // TODO: must reserve N3, N7, S2, S3 for DMA connections
111 if (isShim && tileId == srcSbId) {
112
113 // shim DMAs at start of flows
114 if (srcBundle == WireBundle::DMA)
115 // must be either DMA0 -> N3 or DMA1 -> N7
116 shimCh = srcChannel == 0 ? 3 : 7;
117 else if (srcBundle == WireBundle::NOC)
118 // must be NOC0/NOC1 -> N2/N3 or NOC2/NOC3 -> N6/N7
119 shimCh = srcChannel >= 2 ? srcChannel + 4 : srcChannel + 2;
120 else if (srcBundle == WireBundle::PLIO)
121 shimCh = srcChannel;
122
123 ShimMuxOp shimMuxOp = analyzer.getShimMux(rewriter, col);
124 addConnection(rewriter, cast<Interconnect>(shimMuxOp.getOperation()),
125 flowOp, srcBundle, srcChannel, WireBundle::North, shimCh);
126 }
127 assert(setting.srcs.size() == setting.dsts.size());
128 for (size_t i = 0; i < setting.srcs.size(); i++) {
129 Port src = setting.srcs[i];
130 Port dest = setting.dsts[i];
131
132 // handle special shim connectivity
133 if (isShim && tileId == srcSbId) {
134 addConnection(rewriter, cast<Interconnect>(swOp.getOperation()),
135 flowOp, WireBundle::South, shimCh, dest.bundle,
136 dest.channel);
137 } else if (isShim && (dest.bundle == WireBundle::DMA ||
138 dest.bundle == WireBundle::PLIO ||
139 dest.bundle == WireBundle::NOC)) {
140
141 // shim DMAs at end of flows
142 if (dest.bundle == WireBundle::DMA)
143 // must be either N2 -> DMA0 or N3 -> DMA1
144 shimCh = dest.channel == 0 ? 2 : 3;
145 else if (dest.bundle == WireBundle::NOC)
146 // must be either N2/3/4/5 -> NOC0/1/2/3
147 shimCh = dest.channel + 2;
148 else if (dest.bundle == WireBundle::PLIO)
149 shimCh = dest.channel;
150
151 ShimMuxOp shimMuxOp = analyzer.getShimMux(rewriter, col);
152 addConnection(rewriter, cast<Interconnect>(shimMuxOp.getOperation()),
153 flowOp, WireBundle::North, shimCh, dest.bundle,
154 dest.channel);
155 addConnection(rewriter, cast<Interconnect>(swOp.getOperation()),
156 flowOp, src.bundle, src.channel, WireBundle::South,
157 shimCh);
158 } else {
159 // otherwise, regular switchbox connection
160 addConnection(rewriter, cast<Interconnect>(swOp.getOperation()),
161 flowOp, src.bundle, src.channel, dest.bundle,
162 dest.channel);
163 }
164 }
165
166 LLVM_DEBUG(llvm::dbgs() << tileId << ": " << setting << " | "
167 << "\n");
168 }
169
170 LLVM_DEBUG(llvm::dbgs()
171 << "\n\t\tFinished adding ConnectOps to implement flowOp.\n");
172
173 analyzer.processedFlows[srcPoint] = true;
174 rewriter.eraseOp(Op);
175 return success();
176 }
177};
178
179} // namespace
180
181namespace xilinx::AIE {
182
183LogicalResult AIEPathfinderPass::runOnFlow(DeviceOp d,
184 DynamicTileAnalysis &analyzer) {
185 // Apply rewrite rule to switchboxes to add assignments to every 'connect'
186 // operation inside
187 ConversionTarget target(getContext());
188 target.addLegalOp<TileOp>();
189 target.addLegalOp<ConnectOp>();
190 target.addLegalOp<SwitchboxOp>();
191 target.addLegalOp<ShimMuxOp>();
192 target.addLegalOp<EndOp>();
193
194 RewritePatternSet patterns(&getContext());
195 patterns.insert<ConvertFlowsToInterconnect>(d.getContext(), d, analyzer);
196 if (failed(applyPartialConversion(d, target, std::move(patterns))))
197 return failure();
198 return success();
199}
200
201template <typename MyOp>
204 using OpAdaptor = typename MyOp::Adaptor;
205
206 explicit AIEOpRemoval(MLIRContext *context, PatternBenefit benefit = 1)
207 : OpConversionPattern<MyOp>(context, benefit) {}
208
209 LogicalResult
210 matchAndRewrite(MyOp op, OpAdaptor adaptor,
211 ConversionPatternRewriter &rewriter) const override {
212 Operation *Op = op.getOperation();
213
214 rewriter.eraseOp(Op);
215 return success();
216 }
217};
218
220 WireBundle currDestBundle,
221 int currDestChannel, TileID finalTile,
222 WireBundle finalDestBundle,
223 int finalDestChannel) {
224
225 if ((currTile == finalTile) && (currDestBundle == finalDestBundle) &&
226 (currDestChannel == finalDestChannel)) {
227 return true;
228 }
229
230 WireBundle neighbourSourceBundle;
231 TileID neighbourTile;
232 if (currDestBundle == WireBundle::East) {
233 neighbourSourceBundle = WireBundle::West;
234 neighbourTile = {currTile.col + 1, currTile.row};
235 } else if (currDestBundle == WireBundle::West) {
236 neighbourSourceBundle = WireBundle::East;
237 neighbourTile = {currTile.col - 1, currTile.row};
238 } else if (currDestBundle == WireBundle::North) {
239 neighbourSourceBundle = WireBundle::South;
240 neighbourTile = {currTile.col, currTile.row + 1};
241 } else if (currDestBundle == WireBundle::South) {
242 neighbourSourceBundle = WireBundle::North;
243 neighbourTile = {currTile.col, currTile.row - 1};
244 } else {
245 return false;
246 }
247
248 int neighbourSourceChannel = currDestChannel;
249 for (const auto &[sbNode, setting] : settings) {
250 TileID tile = {sbNode.col, sbNode.row};
251 if (tile == neighbourTile) {
252 assert(setting.srcs.size() == setting.dsts.size());
253 for (size_t i = 0; i < setting.srcs.size(); i++) {
254 Port src = setting.srcs[i];
255 Port dest = setting.dsts[i];
256 if ((src.bundle == neighbourSourceBundle) &&
257 (src.channel == neighbourSourceChannel)) {
258 if (findPathToDest(settings, neighbourTile, dest.bundle, dest.channel,
259 finalTile, finalDestBundle, finalDestChannel)) {
260 return true;
261 }
262 }
263 }
264 }
265 }
266
267 return false;
268}
269
270LogicalResult
271AIEPathfinderPass::runOnPacketFlow(DeviceOp device, OpBuilder &builder,
272 DynamicTileAnalysis &analyzer) {
273
274 ConversionTarget target(getContext());
275
276 std::map<TileID, mlir::Operation *> tiles;
277
278 // Map from a port and flowID to
279 std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> packetFlows;
280 std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> ctrlPacketFlows;
281 SmallVector<std::pair<PhysPort, int>, 4> slavePorts;
282 DenseMap<std::pair<PhysPort, int>, int> slaveAMSels;
283 // Flag to keep packet header at packet flow destination
284 DenseMap<PhysPort, BoolAttr> keepPktHeaderAttr;
285 // Map from tileID and master ports to flags labelling control packet flows
286 DenseMap<std::pair<PhysPort, int>, bool> ctrlPktFlows;
287
288 for (auto tileOp : device.getOps<TileOp>()) {
289 int col = tileOp.colIndex();
290 int row = tileOp.rowIndex();
291 tiles[{col, row}] = tileOp;
292 }
293
294 // The logical model of all the switchboxes.
295 std::map<TileID, SmallVector<std::pair<Connect, int>, 8>> switchboxes;
296 for (PacketFlowOp pktFlowOp : device.getOps<PacketFlowOp>()) {
297 Region &r = pktFlowOp.getPorts();
298 Block &b = r.front();
299 int flowID = pktFlowOp.IDInt();
300 Port srcPort, destPort;
301 TileOp srcTile, destTile;
302 TileID srcCoords, destCoords;
303
304 // Pass 1: extract source (order-independent: dest may appear before source)
305 for (Operation &Op : b.getOperations()) {
306 if (auto pktSource = dyn_cast<PacketSourceOp>(Op)) {
307 srcTile = dyn_cast<TileOp>(pktSource.getTile().getDefiningOp());
308 srcPort = pktSource.port();
309 srcCoords = {srcTile.colIndex(), srcTile.rowIndex()};
310 }
311 }
312 if (!srcTile)
313 return pktFlowOp.emitOpError("packet_flow has no packet_source");
314 // Pass 2: process each destination using the source extracted above
315 for (Operation &Op : b.getOperations()) {
316 if (auto pktDest = dyn_cast<PacketDestOp>(Op)) {
317 destTile = dyn_cast<TileOp>(pktDest.getTile().getDefiningOp());
318 destPort = pktDest.port();
319 destCoords = {destTile.colIndex(), destTile.rowIndex()};
320 // Assign "keep_pkt_header flag"
321 auto keep = pktFlowOp.getKeepPktHeader();
322 keepPktHeaderAttr[{destTile.getTileID(), destPort}] =
323 keep ? BoolAttr::get(Op.getContext(), *keep) : nullptr;
324
325 TileID srcSB = {srcCoords.col, srcCoords.row};
326 if (PathEndPoint srcPoint = {srcSB, srcPort};
327 !analyzer.processedFlows[srcPoint]) {
328 SwitchSettings settings = analyzer.flowSolutions[srcPoint];
329 // add connections for all the Switchboxes in SwitchSettings
330 for (const auto &[curr, setting] : settings) {
331 assert(setting.srcs.size() == setting.dsts.size());
332 TileID currTile = {curr.col, curr.row};
333 for (size_t i = 0; i < setting.srcs.size(); i++) {
334 Port src = setting.srcs[i];
335 Port dest = setting.dsts[i];
336 // reject false broadcast
337 if (!findPathToDest(settings, currTile, dest.bundle, dest.channel,
338 destCoords, destPort.bundle,
339 destPort.channel))
340 continue;
341 Connect connect = {{src.bundle, src.channel},
342 {dest.bundle, dest.channel}};
343 if (std::find(switchboxes[currTile].begin(),
344 switchboxes[currTile].end(),
345 std::pair{connect, flowID}) ==
346 switchboxes[currTile].end())
347 switchboxes[currTile].push_back({connect, flowID});
348 // Assign "control packet flows" flag per switchbox, based on
349 // packet flow op attribute
350 auto ctrlPkt = pktFlowOp.getPriorityRoute();
351 ctrlPktFlows[{{currTile, dest}, flowID}] =
352 ctrlPkt ? *ctrlPkt : false;
353 }
354 }
355 }
356 }
357 }
358 }
359
360 LLVM_DEBUG(llvm::dbgs() << "Check switchboxes\n");
361
362 for (const auto &[tileId, connects] : switchboxes) {
363 LLVM_DEBUG(llvm::dbgs() << "***switchbox*** " << tileId.col << " "
364 << tileId.row << '\n');
365 for (const auto &[conn, flowID] : connects) {
366 Port sourcePort = conn.src;
367 Port destPort = conn.dst;
368 auto sourceFlow =
369 std::make_pair(std::make_pair(tileId, sourcePort), flowID);
370 if (ctrlPktFlows[{{tileId, destPort}, flowID}])
371 ctrlPacketFlows[sourceFlow].push_back({tileId, destPort});
372 else
373 packetFlows[sourceFlow].push_back({tileId, destPort});
374 slavePorts.push_back(sourceFlow);
375 LLVM_DEBUG(llvm::dbgs() << "flowID " << flowID << ':'
376 << stringifyWireBundle(sourcePort.bundle) << " "
377 << sourcePort.channel << " -> "
378 << stringifyWireBundle(destPort.bundle) << " "
379 << destPort.channel << "\n");
380 }
381 }
382
383 // amsel()
384 // masterset()
385 // packetrules()
386 // rule()
387
388 // Compute arbiter assignments. Each arbiter has four msels.
389 // Therefore, the number of "logical" arbiters is 6 x 4 = 24
390 // A master port can only be associated with one arbiter
391
392 // Constants for arbiter configuration
393 constexpr int INVALID_AMSEL_VALUE = -1;
394 constexpr int INVALID_ARBITER_VALUE = -1;
395
396 // A map from Tile and master selectValue to the ports targetted by that
397 // master select.
398 std::map<std::pair<TileID, int>, SmallVector<Port, 4>> masterAMSels;
399
400 // Track which arbiter each port is assigned to (to prevent conflicts)
401 std::map<PhysPort, int> portToArbiter;
402
403 // Count of currently used logical arbiters for each tile.
404 DenseMap<Operation *, int> amselValues;
405 int numMselsPerArbiter = 4;
406 int numArbiters = 6;
407
408 // Get arbiter id from amsel
409 auto getArbiterIDFromAmsel = [numArbiters](int amsel) {
410 return amsel % numArbiters;
411 };
412 // Get amsel from arbiter id and msel
413 auto getAmselFromArbiterIDAndMsel = [numArbiters](int arbiter, int msel) {
414 return arbiter + msel * numArbiters;
415 };
416 // Get a new unique amsel from masterAMSels on tile op. Prioritize on
417 // incrementing arbiter id, before incrementing msel
418 auto getNewUniqueAmsel =
419 [&](const std::map<std::pair<TileID, int>, SmallVector<Port, 4>>
420 &masterAMSels,
421 TileOp tileOp, bool isCtrlPkt) {
422 if (isCtrlPkt) { // Higher AMsel first
423 for (int i = numMselsPerArbiter - 1; i >= 0; i--)
424 for (int a = numArbiters - 1; a >= 0; a--)
425 if (!masterAMSels.count(
426 {tileOp.getTileID(), getAmselFromArbiterIDAndMsel(a, i)}))
427 return getAmselFromArbiterIDAndMsel(a, i);
428 } else { // Lower AMsel first
429 for (int i = 0; i < numMselsPerArbiter; i++)
430 for (int a = 0; a < numArbiters; a++)
431 if (!masterAMSels.count(
432 {tileOp.getTileID(), getAmselFromArbiterIDAndMsel(a, i)}))
433 return getAmselFromArbiterIDAndMsel(a, i);
434 }
435 tileOp->emitOpError(
436 "tile op has used up all arbiter-msel combinations");
437 return INVALID_AMSEL_VALUE;
438 };
439 // Get a new unique amsel from masterAMSels on tile op with given arbiter id
440 auto getNewUniqueAmselPerArbiterID =
441 [&](const std::map<std::pair<TileID, int>, SmallVector<Port, 4>>
442 &masterAMSels,
443 TileOp tileOp, int arbiter) {
444 for (int i = 0; i < numMselsPerArbiter; i++)
445 if (!masterAMSels.count({tileOp.getTileID(),
446 getAmselFromArbiterIDAndMsel(arbiter, i)}))
447 return getAmselFromArbiterIDAndMsel(arbiter, i);
448 tileOp->emitOpError("tile op arbiter ")
449 << std::to_string(arbiter) << " has used up all its msels";
450 return INVALID_AMSEL_VALUE;
451 };
452
453 // Order the packet flows in order to get determinsitic amsel allocation;
454 // allocate amsels for control packet flows before others to ensure
455 // consistency in control packet flow overlay.
456 auto getSortedPacketFlows =
457 [&](std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>> pktFlows,
458 std::map<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>
459 ctrlPktFlows) {
460 std::vector<
461 std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
462 sortedpktFlows(pktFlows.begin(), pktFlows.end());
463 std::vector<
464 std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
465 sortedctrlpktFlows(ctrlPktFlows.begin(), ctrlPktFlows.end());
466 sortedctrlpktFlows.insert(sortedctrlpktFlows.end(),
467 sortedpktFlows.begin(), sortedpktFlows.end());
468 return sortedctrlpktFlows;
469 };
470
471 std::vector<std::pair<std::pair<PhysPort, int>, SmallVector<PhysPort, 4>>>
472 sortedPacketFlows = getSortedPacketFlows(packetFlows, ctrlPacketFlows);
473
474 packetFlows.insert(ctrlPacketFlows.begin(), ctrlPacketFlows.end());
475
476 // Helper function to assign ports to a given amsel value
477 // Updates masterAMSels and portToArbiter, skipping ports already on different
478 // arbiters
479 auto assignPortsToAmsel = [&](TileID tileId, int amselValue,
480 const SmallVector<PhysPort, 4> &destinations) {
481 int targetArbiter = getArbiterIDFromAmsel(amselValue);
482 for (auto dest : destinations) {
483 Port port = dest.second;
484 PhysPort physPort = {tileId, port};
485
486 // Skip this port if it's already assigned to a different arbiter
487 if (portToArbiter.count(physPort) &&
488 portToArbiter[physPort] != targetArbiter) {
489 LLVM_DEBUG(llvm::dbgs()
490 << "Skipping port " << stringifyWireBundle(port.bundle)
491 << ":" << port.channel << " - already on arbiter "
492 << portToArbiter[physPort] << ", target is " << targetArbiter
493 << "\n");
494 continue;
495 }
496
497 masterAMSels[{tileId, amselValue}].push_back(port);
498 portToArbiter[physPort] = targetArbiter;
499 }
500 };
501
502 // Helper function to find existing arbiter assignment for ports in a flow
503 // Returns -1 if no existing assignment found
504 auto findExistingArbiter =
505 [&](TileID tileId, const SmallVector<PhysPort, 4> &destinations) -> int {
506 for (auto dest : destinations) {
507 Port port = dest.second;
508 PhysPort physPort = {tileId, port};
509 if (portToArbiter.count(physPort)) {
510 return portToArbiter[physPort];
511 }
512 }
513 return INVALID_ARBITER_VALUE;
514 };
515
516 // Check all multi-cast flows (same source, same ID). They should be
517 // assigned the same arbiter and msel so that the flow can reach all the
518 // destination ports at the same time For destination ports that appear in
519 // different (multicast) flows, it should have a different <arbiterID, msel>
520 // value pair for each flow
521 for (const auto &packetFlow : sortedPacketFlows) {
522 // The Source Tile of the flow
523 TileID tileId = packetFlow.first.first.first;
524 TileOp tileOp = analyzer.getTile(builder, tileId);
525 if (amselValues.count(tileOp) == 0)
526 amselValues[tileOp] = 0;
527
528 // arb0: 6*0, 6*1, 6*2, 6*3
529 // arb1: 6*0+1, 6*1+1, 6*2+1, 6*3+1
530 // arb2: 6*0+2, 6*1+2, 6*2+2, 6*3+2
531 // arb3: 6*0+3, 6*1+3, 6*2+3, 6*3+3
532 // arb4: 6*0+4, 6*1+4, 6*2+4, 6*3+4
533 // arb5: 6*0+5, 6*1+5, 6*2+5, 6*3+5
534
535 int amselValue = amselValues[tileOp];
536 assert(amselValue < numArbiters && "Could not allocate new arbiter!");
537
538 // Find existing arbiter and amsel assignments for this flow
539 // Strategy: Look for existing amsel entries that match the flow's
540 // destinations
541 // - Complete match: reuse existing amsel
542 // - Partial match: create new amsel on same arbiter
543 // - No match: create new amsel (on existing arbiter if ports already
544 // assigned)
545 bool hasMatchingAmselEntry = false;
546 int partialMatchArbiterID = INVALID_ARBITER_VALUE;
547
548 // Check if any ports in this flow already have arbiter assignments
549 int existingArbiter = findExistingArbiter(tileId, packetFlow.second);
550
551 // Search for matching amsel entries, prioritizing those that match the
552 // existing arbiter
553 for (const auto &amselEntry : masterAMSels) {
554 if (amselEntry.first.first != tileId)
555 continue;
556 amselValue = amselEntry.first.second;
557 int thisArbiter = getArbiterIDFromAmsel(amselValue);
558
559 // If we have an existing arbiter assignment, only consider amsels from
560 // that arbiter
561 if (existingArbiter != INVALID_ARBITER_VALUE &&
562 existingArbiter != thisArbiter) {
563 continue;
564 }
565
566 // Check if destinations match (completely or partially)
567 const SmallVector<Port, 4> &existingPorts =
568 masterAMSels[{tileId, amselValue}];
569 bool hasOverlap = false;
570 bool hasNonOverlap = false;
571
572 for (auto dest : packetFlow.second) {
573 Port port = dest.second;
574 if (std::find(existingPorts.begin(), existingPorts.end(), port) ==
575 existingPorts.end())
576 hasNonOverlap = true;
577 else
578 hasOverlap = true;
579 }
580
581 if (hasOverlap) {
582 hasMatchingAmselEntry = true;
583 // Partial match if some ports don't overlap or sizes differ
584 if (hasNonOverlap || existingPorts.size() != packetFlow.second.size())
585 partialMatchArbiterID = thisArbiter;
586 break;
587 }
588 }
589
590 if (!hasMatchingAmselEntry) {
591 // This packet flow switchbox's output ports completely mismatches with
592 // any existing amsel. Creating a new amsel.
593
594 // Determine target arbiter: use existing if available, otherwise allocate
595 // based on priority
596 int targetArbiter = existingArbiter;
597
598 if (targetArbiter == INVALID_ARBITER_VALUE) {
599 // No existing assignment, choose based on control packet priority
600 bool isCtrlPkt =
601 llvm::any_of(packetFlow.second, [&](PhysPort destPhysPort) {
602 Port port = destPhysPort.second;
603 return ctrlPktFlows[{{tileId, port}, packetFlow.first.second}];
604 });
605
606 amselValue = getNewUniqueAmsel(masterAMSels, tileOp, isCtrlPkt);
607 if (amselValue == INVALID_AMSEL_VALUE)
608 return failure();
609 } else {
610 // Use existing arbiter to maintain consistency
611 amselValue =
612 getNewUniqueAmselPerArbiterID(masterAMSels, tileOp, targetArbiter);
613 if (amselValue == INVALID_AMSEL_VALUE) {
614 // No more msels available on this arbiter - this is a routing
615 // conflict
616 tileOp->emitOpError("cannot assign flow: arbiter ")
617 << targetArbiter
618 << " has no free msels, but flow requires this arbiter due to "
619 "existing port assignments";
620 return failure();
621 }
622 }
623
624 // Update masterAMSels with new amsel, skipping ports already assigned to
625 // different arbiters
626 assignPortsToAmsel(tileId, amselValue, packetFlow.second);
627 } else if (partialMatchArbiterID != INVALID_ARBITER_VALUE) {
628 // This packet flow switchbox's output ports partially overlaps with
629 // some existing amsel. Create a NEW amsel with the SAME arbiter for this
630 // flow. The comment states: "destination ports that appear in different
631 // (multicast) flows should have a different <arbiterID, msel> value pair
632 // for each flow" but use the same arbiter to maintain the constraint.
633
634 // Use the arbiter we found (which should match existingArbiter if set)
635 int targetArbiter = partialMatchArbiterID;
636 if (existingArbiter != INVALID_ARBITER_VALUE &&
637 existingArbiter != targetArbiter) {
638 // Conflict detected - should have been caught earlier, but add safety
639 // check
640 tileOp->emitOpError(
641 "internal error: arbiter conflict in partial match");
642 return failure();
643 }
644
645 amselValue =
646 getNewUniqueAmselPerArbiterID(masterAMSels, tileOp, targetArbiter);
647
648 // Update masterAMSels with new amsel, skipping ports already assigned to
649 // different arbiters
650 assignPortsToAmsel(tileId, amselValue, packetFlow.second);
651 } else {
652 // Complete match - reuse the existing amsel
653 // Track arbiter assignments for all ports in this flow
654 int arbiter = getArbiterIDFromAmsel(amselValue);
655 for (auto dest : packetFlow.second) {
656 Port port = dest.second;
657 PhysPort physPort = {tileId, port};
658 // Update tracking even for reused amsels
659 if (!portToArbiter.count(physPort)) {
660 portToArbiter[physPort] = arbiter;
661 }
662 }
663 }
664
665 slaveAMSels[packetFlow.first] = amselValue;
666 amselValues[tileOp] = getArbiterIDFromAmsel(amselValue);
667 }
668
669 // Compute the master set IDs
670 // A map from a switchbox output port to its associated amsel values
671 std::map<PhysPort, SmallVector<int, 4>> mastersets;
672 for (const auto &[physPort, ports] : masterAMSels) {
673 TileID tileId = physPort.first;
674 int amselValue = physPort.second;
675 for (auto port : ports) {
676 PhysPort physPort = {tileId, port};
677 mastersets[physPort].push_back(amselValue);
678 }
679 }
680
681 // Validate that each port only has amsels from a single arbiter
682 for (const auto &[physPort, amselList] : mastersets) {
683 int assignedArbiter = INVALID_ARBITER_VALUE;
684 for (auto amsel : amselList) {
685 int thisArbiter = getArbiterIDFromAmsel(amsel);
686 if (assignedArbiter != INVALID_ARBITER_VALUE &&
687 assignedArbiter != thisArbiter) {
688 TileID tileId = physPort.first;
689 Port port = physPort.second;
690 TileOp tileOp = analyzer.getTile(builder, tileId);
691 tileOp->emitOpError("port ")
692 << stringifyWireBundle(port.bundle) << ":" << port.channel
693 << " assigned to multiple arbiters: " << assignedArbiter << " and "
694 << thisArbiter << " (amsels: " << amselList[0];
695 for (size_t i = 1; i < amselList.size(); i++) {
696 llvm::errs() << ", " << amselList[i];
697 }
698 llvm::errs() << ")\n";
699 return failure();
700 }
701 assignedArbiter = thisArbiter;
702 }
703 }
704
705 LLVM_DEBUG(llvm::dbgs() << "CHECK mastersets\n");
706#ifndef NDEBUG
707 for (const auto &[physPort, values] : mastersets) {
708 TileID tileId = physPort.first;
709 WireBundle bundle = physPort.second.bundle;
710 int channel = physPort.second.channel;
711 LLVM_DEBUG(llvm::dbgs()
712 << "master " << tileId << " " << stringifyWireBundle(bundle)
713 << " : " << channel << '\n');
714 for (auto value : values)
715 LLVM_DEBUG(llvm::dbgs() << "amsel: " << value << '\n');
716 }
717#endif
718
719 // Compute mask values
720 // Merging as many stream flows as possible
721 // The flows must originate from the same source port and have different IDs
722 // Two flows can be merged if they share the same destinations
723 SmallVector<SmallVector<std::pair<PhysPort, int>, 4>, 4> slaveGroups;
724 SmallVector<std::pair<PhysPort, int>, 4> workList(slavePorts);
725 while (!workList.empty()) {
726 auto slave1 = workList.pop_back_val();
727 Port slavePort1 = slave1.first.second;
728
729 bool foundgroup = false;
730 for (auto &group : slaveGroups) {
731 auto slave2 = group.front();
732 if (Port slavePort2 = slave2.first.second; slavePort1 != slavePort2)
733 continue;
734
735 bool matched = true;
736 auto dests1 = packetFlows[slave1];
737 auto dests2 = packetFlows[slave2];
738 if (dests1.size() != dests2.size())
739 continue;
740
741 for (auto dest1 : dests1) {
742 if (std::find(dests2.begin(), dests2.end(), dest1) == dests2.end()) {
743 matched = false;
744 break;
745 }
746 }
747
748 if (matched) {
749 group.push_back(slave1);
750 foundgroup = true;
751 break;
752 }
753 }
754
755 if (!foundgroup) {
756 SmallVector<std::pair<PhysPort, int>, 4> group({slave1});
757 slaveGroups.push_back(group);
758 }
759 }
760
761 std::map<std::pair<PhysPort, int>, int> slaveMasks;
762 for (const auto &group : slaveGroups) {
763 // Iterate over all the ID values in a group
764 // If bit n-th (n <= 5) of an ID value differs from bit n-th of another ID
765 // value, the bit position should be "don't care", and we will set the
766 // mask bit of that position to 0
767 int mask[5] = {-1, -1, -1, -1, -1};
768 for (auto port : group) {
769 int ID = port.second;
770 for (int i = 0; i < 5; i++) {
771 if (mask[i] == -1)
772 mask[i] = ID >> i & 0x1;
773 else if (mask[i] != (ID >> i & 0x1))
774 mask[i] = 2; // found bit difference --> mark as "don't care"
775 }
776 }
777
778 int maskValue = 0;
779 for (int i = 4; i >= 0; i--) {
780 if (mask[i] == 2) // don't care
781 mask[i] = 0;
782 else
783 mask[i] = 1;
784 maskValue = (maskValue << 1) + mask[i];
785 }
786 for (auto port : group)
787 slaveMasks[port] = maskValue;
788 }
789
790#ifndef NDEBUG
791 LLVM_DEBUG(llvm::dbgs() << "CHECK Slave Masks\n");
792 for (auto map : slaveMasks) {
793 PhysPort port = map.first.first;
794 TileOp tile = analyzer.getTile(builder, port.first);
795 WireBundle bundle = port.second.bundle;
796 int channel = port.second.channel;
797 int ID = map.first.second;
798 int mask = map.second;
799
800 LLVM_DEBUG(llvm::dbgs()
801 << "Port " << tile << " " << stringifyWireBundle(bundle) << " "
802 << channel << '\n');
803 LLVM_DEBUG(llvm::dbgs() << "Mask "
804 << "0x" << llvm::Twine::utohexstr(mask) << '\n');
805 LLVM_DEBUG(llvm::dbgs() << "ID "
806 << "0x" << llvm::Twine::utohexstr(ID) << '\n');
807 for (int i = 0; i < 31; i++) {
808 if ((i & mask) == (ID & mask))
809 LLVM_DEBUG(llvm::dbgs() << "matches flow ID "
810 << "0x" << llvm::Twine::utohexstr(i) << '\n');
811 }
812 }
813#endif
814
815 // Realize the routes in MLIR
816
817 // Update tiles map if any new tile op declaration is needed for constructing
818 // the flow.
819 for (const auto &swMap : mastersets) {
820 TileID tileId = swMap.first.first;
821 TileOp tileOp = analyzer.getTile(builder, tileId);
822 if (std::none_of(tiles.begin(), tiles.end(),
823 [&tileOp](const std::pair<const xilinx::AIE::TileID,
824 Operation *> &tileMapEntry) {
825 return tileMapEntry.second == tileOp.getOperation();
826 })) {
827 tiles[{tileOp.colIndex(), tileOp.rowIndex()}] = tileOp;
828 }
829 }
830
831 for (auto map : tiles) {
832 Operation *tileOp = map.second;
833 TileOp tile = cast<TileOp>(map.second);
834 TileID tileId = tile.getTileID();
835
836 // Create a switchbox for the routes and insert inside it.
837 builder.setInsertionPointAfter(tileOp);
838 SwitchboxOp swbox =
839 analyzer.getSwitchbox(builder, tile.colIndex(), tile.rowIndex());
840 SwitchboxOp::ensureTerminator(swbox.getConnections(), builder,
841 builder.getUnknownLoc());
842 Block &b = swbox.getConnections().front();
843 builder.setInsertionPoint(b.getTerminator());
844
845 std::vector<bool> amselOpNeededVector(numMselsPerArbiter * numArbiters);
846 for (const auto &map : mastersets) {
847 if (tileId != map.first.first)
848 continue;
849
850 for (auto value : map.second) {
851 amselOpNeededVector[value] = true;
852 }
853 }
854 // Create all the amsel Ops
855 std::map<int, AMSelOp> amselOps;
856 for (int i = 0; i < numMselsPerArbiter; i++) {
857 for (int a = 0; a < numArbiters; a++) {
858 auto amselValue = getAmselFromArbiterIDAndMsel(a, i);
859 if (amselOpNeededVector[amselValue]) {
860 int arbiterID = a;
861 int msel = i;
862 auto amsel = AMSelOp::create(builder, builder.getUnknownLoc(),
863 arbiterID, msel);
864 amselOps[amselValue] = amsel;
865 }
866 }
867 }
868 // Create all the master set Ops
869 // First collect the master sets for this tile.
870 SmallVector<Port, 4> tileMasters;
871 for (const auto &map : mastersets) {
872 if (tileId != map.first.first)
873 continue;
874 tileMasters.push_back(map.first.second);
875 }
876 // Sort them so we get a reasonable order
877 std::sort(tileMasters.begin(), tileMasters.end());
878 for (auto tileMaster : tileMasters) {
879 WireBundle bundle = tileMaster.bundle;
880 int channel = tileMaster.channel;
881 SmallVector<int, 4> msels = mastersets[{tileId, tileMaster}];
882 SmallVector<Value, 4> amsels;
883 for (auto msel : msels) {
884 assert(amselOps.count(msel) == 1);
885 amsels.push_back(amselOps[msel]);
886 }
887
888 MasterSetOp::create(builder, builder.getUnknownLoc(),
889 builder.getIndexType(), bundle, channel, amsels,
890 keepPktHeaderAttr[{tileId, tileMaster}]);
891 }
892
893 // Generate the packet rules
894 DenseMap<Port, PacketRulesOp> slaveRules;
895 for (auto group : slaveGroups) {
896 builder.setInsertionPoint(b.getTerminator());
897
898 auto port = group.front().first;
899 if (tileId != port.first)
900 continue;
901
902 WireBundle bundle = port.second.bundle;
903 int channel = port.second.channel;
904 auto slave = port.second;
905
906 int mask = slaveMasks[group.front()];
907 int ID = group.front().second & mask;
908
909 // Verify that we actually map all the ID's correctly.
910#ifndef NDEBUG
911 for (auto slave : group)
912 assert((slave.second & mask) == ID);
913#endif
914 Value amsel = amselOps[slaveAMSels[group.front()]];
915
916 PacketRulesOp packetrules;
917 if (slaveRules.count(slave) == 0) {
918 packetrules = PacketRulesOp::create(builder, builder.getUnknownLoc(),
919 bundle, channel);
920 PacketRulesOp::ensureTerminator(packetrules.getRules(), builder,
921 builder.getUnknownLoc());
922 slaveRules[slave] = packetrules;
923 } else
924 packetrules = slaveRules[slave];
925
926 Block &rules = packetrules.getRules().front();
927
928 // Verify ID mapping against all other rules of the same slave.
929 for (auto rule : rules.getOps<PacketRuleOp>()) {
930 auto verifyMask = rule.maskInt();
931 auto verifyValue = rule.valueInt();
932 if ((group.front().second & verifyMask) == verifyValue) {
933 rule->emitOpError("can lead to false packet id match for id ")
934 << ID << ", which is not supposed to pass through this port.";
935 rule->emitRemark("Please consider changing all uses of packet id ")
936 << ID << " to avoid deadlock.";
937 return failure();
938 }
939 }
940
941 builder.setInsertionPoint(rules.getTerminator());
942 PacketRuleOp::create(builder, builder.getUnknownLoc(), mask, ID, amsel);
943 }
944 }
945
946 // Add support for shimDMA
947 // From shimDMA to BLI: 1) shimDMA 0 --> North 3
948 // 2) shimDMA 1 --> North 7
949 // From BLI to shimDMA: 1) North 2 --> shimDMA 0
950 // 2) North 3 --> shimDMA 1
951
952 for (auto switchbox : make_early_inc_range(device.getOps<SwitchboxOp>())) {
953 auto retVal = switchbox->getOperand(0);
954 auto tileOp = retVal.getDefiningOp<TileOp>();
955
956 // Check if it is a shim Tile
957 if (!tileOp.isShimNOCTile())
958 continue;
959
960 // Check if the switchbox is empty
961 if (&switchbox.getBody()->front() == switchbox.getBody()->getTerminator())
962 continue;
963
964 Region &r = switchbox.getConnections();
965 Block &b = r.front();
966
967 // Find if the corresponding shimmux exsists or not
968 int shimExist = 0;
969 ShimMuxOp shimOp;
970 for (auto shimmux : device.getOps<ShimMuxOp>()) {
971 if (shimmux.getTile() == tileOp) {
972 shimExist = 1;
973 shimOp = shimmux;
974 break;
975 }
976 }
977
978 for (Operation &Op : b.getOperations()) {
979 if (auto pktrules = dyn_cast<PacketRulesOp>(Op)) {
980
981 // check if there is MM2S DMA in the switchbox of the 0th row
982 if (pktrules.getSourceBundle() == WireBundle::DMA) {
983
984 // If there is, then it should be put into the corresponding shimmux
985 // If shimmux not defined then create shimmux
986 if (!shimExist) {
987 builder.setInsertionPointAfter(tileOp);
988 shimOp = analyzer.getShimMux(builder, tileOp.colIndex());
989 shimExist = 1;
990 }
991
992 Region &r0 = shimOp.getConnections();
993 Block &b0 = r0.front();
994 builder.setInsertionPointToStart(&b0);
995
996 pktrules.setSourceBundle(WireBundle::South);
997 if (pktrules.getSourceChannel() == 0) {
998 pktrules.setSourceChannel(3);
999 ConnectOp::create(builder, builder.getUnknownLoc(), WireBundle::DMA,
1000 0, WireBundle::North, 3);
1001 }
1002 if (pktrules.getSourceChannel() == 1) {
1003 pktrules.setSourceChannel(7);
1004 ConnectOp::create(builder, builder.getUnknownLoc(), WireBundle::DMA,
1005 1, WireBundle::North, 7);
1006 }
1007 }
1008 }
1009
1010 if (auto mtset = dyn_cast<MasterSetOp>(Op)) {
1011
1012 // check if there is S2MM DMA in the switchbox of the 0th row
1013 if (mtset.getDestBundle() == WireBundle::DMA) {
1014
1015 // If there is, then it should be put into the corresponding shimmux
1016 // If shimmux not defined then create shimmux
1017 if (!shimExist) {
1018 builder.setInsertionPointAfter(tileOp);
1019 shimOp = analyzer.getShimMux(builder, tileOp.colIndex());
1020 shimExist = 1;
1021 }
1022
1023 Region &r0 = shimOp.getConnections();
1024 Block &b0 = r0.front();
1025 builder.setInsertionPointToStart(&b0);
1026
1027 mtset.setDestBundle(WireBundle::South);
1028 if (mtset.getDestChannel() == 0) {
1029 mtset.setDestChannel(2);
1030 ConnectOp::create(builder, builder.getUnknownLoc(),
1031 WireBundle::North, 2, WireBundle::DMA, 0);
1032 }
1033 if (mtset.getDestChannel() == 1) {
1034 mtset.setDestChannel(3);
1035 ConnectOp::create(builder, builder.getUnknownLoc(),
1036 WireBundle::North, 3, WireBundle::DMA, 1);
1037 }
1038 }
1039 }
1040 }
1041 }
1042
1043 RewritePatternSet patterns(&getContext());
1044
1045 if (failed(applyPartialConversion(device, target, std::move(patterns))))
1046 return failure();
1047
1048 return success();
1049}
1050
1052
1053 // create analysis pass with routing graph for entire device
1054 LLVM_DEBUG(llvm::dbgs() << "---Begin AIEPathfinderPass---\n");
1055
1056 DeviceOp d = getOperation();
1057 DynamicTileAnalysis &analyzer = getAnalysis<DynamicTileAnalysis>();
1058 if (failed(analyzer.runAnalysis(d))) {
1059 signalPassFailure();
1060 return;
1061 }
1062 OpBuilder builder = OpBuilder::atBlockTerminator(d.getBody());
1063
1064 if (clRouteCircuit && failed(runOnFlow(d, analyzer))) {
1065 signalPassFailure();
1066 return;
1067 }
1068 if (clRoutePacket && failed(runOnPacketFlow(d, builder, analyzer))) {
1069 signalPassFailure();
1070 return;
1071 }
1072
1073 // Populate wires between switchboxes and tiles.
1074 builder.setInsertionPoint(d.getBody()->getTerminator());
1075 for (int col = 0; col <= analyzer.getMaxCol(); col++) {
1076 for (int row = 0; row <= analyzer.getMaxRow(); row++) {
1077 TileOp tile;
1078 if (analyzer.coordToTile.count({col, row}))
1079 tile = analyzer.coordToTile[{col, row}];
1080 else
1081 continue;
1082 SwitchboxOp sw;
1083 if (analyzer.coordToSwitchbox.count({col, row}))
1084 sw = analyzer.coordToSwitchbox[{col, row}];
1085 else
1086 continue;
1087 if (col > 0) {
1088 // connections east-west between stream switches
1089 if (analyzer.coordToSwitchbox.count({col - 1, row})) {
1090 auto westsw = analyzer.coordToSwitchbox[{col - 1, row}];
1091 WireOp::create(builder, builder.getUnknownLoc(), westsw,
1092 WireBundle::East, sw, WireBundle::West);
1093 }
1094 }
1095 if (row > 0) {
1096 // connections between abstract 'core' of tile
1097 WireOp::create(builder, builder.getUnknownLoc(), tile, WireBundle::Core,
1098 sw, WireBundle::Core);
1099 // connections between abstract 'dma' of tile
1100 WireOp::create(builder, builder.getUnknownLoc(), tile, WireBundle::DMA,
1101 sw, WireBundle::DMA);
1102 // connections north-south inside array ( including connection to shim
1103 // row)
1104 if (analyzer.coordToSwitchbox.count({col, row - 1})) {
1105 auto southsw = analyzer.coordToSwitchbox[{col, row - 1}];
1106 WireOp::create(builder, builder.getUnknownLoc(), southsw,
1107 WireBundle::North, sw, WireBundle::South);
1108 }
1109 } else if (row == 0) {
1110 if (tile.isShimNOCTile()) {
1111 if (analyzer.coordToShimMux.count({col, 0})) {
1112 auto shimsw = analyzer.coordToShimMux[{col, 0}];
1113 WireOp::create(
1114 builder, builder.getUnknownLoc(), shimsw,
1115 WireBundle::North, // Changed to connect into the north
1116 sw, WireBundle::South);
1117 // PLIO is attached to shim mux
1118 if (analyzer.coordToPLIO.count(col)) {
1119 auto plio = analyzer.coordToPLIO[col];
1120 WireOp::create(builder, builder.getUnknownLoc(), plio,
1121 WireBundle::North, shimsw, WireBundle::South);
1122 }
1123
1124 // abstract 'DMA' connection on tile is attached to shim mux ( in
1125 // row 0 )
1126 WireOp::create(builder, builder.getUnknownLoc(), tile,
1127 WireBundle::DMA, shimsw, WireBundle::DMA);
1128 }
1129 } else if (tile.isShimPLTile()) {
1130 // PLIO is attached directly to switch
1131 if (analyzer.coordToPLIO.count(col)) {
1132 auto plio = analyzer.coordToPLIO[col];
1133 WireOp::create(builder, builder.getUnknownLoc(), plio,
1134 WireBundle::North, sw, WireBundle::South);
1135 }
1136 }
1137 }
1138 }
1139 }
1140}
1141
1142std::unique_ptr<OperationPass<DeviceOp>> createAIEPathfinderPass() {
1143 return std::make_unique<AIEPathfinderPass>();
1144}
1145
1146} // namespace xilinx::AIE
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::map< PathEndPoint, bool > processedFlows
mlir::LogicalResult runAnalysis(DeviceOp &device)
llvm::DenseMap< TileID, TileOp > coordToTile
std::map< PathEndPoint, SwitchSettings > flowSolutions
llvm::DenseMap< int, PLIOOp > coordToPLIO
std::shared_ptr< Value > value()
Definition cxxopts.hpp:1026
Include the generated interface declarations.
Connect { Port src Connect
Definition AIEDialect.h:158
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
std::map< TileID, SwitchSetting > SwitchSettings
TileID dstCoords
Port { WireBundle bundle Port
Definition AIEDialect.h:128
PathEndPoint src
TileID srcCoords
PathEndPoint { PathEndPoint()=default PathEndPoint
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEPathfinderPass()
AIEOpRemoval(MLIRContext *context, PatternBenefit benefit=1)
LogicalResult matchAndRewrite(MyOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
mlir::LogicalResult runOnPacketFlow(DeviceOp d, mlir::OpBuilder &builder, DynamicTileAnalysis &analyzer)
mlir::LogicalResult runOnFlow(DeviceOp d, DynamicTileAnalysis &analyzer)
bool findPathToDest(SwitchSettings settings, TileID currTile, WireBundle currDestBundle, int currDestChannel, TileID finalTile, WireBundle finalDestBundle, int finalDestChannel)