diff --git a/lib/Dialect/AIE/Transforms/AIECreatePathfindFlows.cpp b/lib/Dialect/AIE/Transforms/AIECreatePathfindFlows.cpp index 8d8d38711f..823751ac2d 100644 --- a/lib/Dialect/AIE/Transforms/AIECreatePathfindFlows.cpp +++ b/lib/Dialect/AIE/Transforms/AIECreatePathfindFlows.cpp @@ -336,8 +336,8 @@ struct ConvertFlowsToInterconnect : public OpConversionPattern { analyzer.getSwitchbox(rewriter, curr->col, curr->row); int shim_ch = srcChannel; // TODO: must reserve N3, N7, S2, S3 for DMA connections - if (curr == srcSB && srcSB->row == 0 && - analyzer.getTile(rewriter, srcSB->col, 0).isShimNOCTile()) { + if (curr == srcSB && analyzer.getTile(rewriter, srcSB->col, srcSB->row) + .isShimNOCTile()) { // shim DMAs at start of flows if (srcBundle == WireBundle::DMA) { shim_ch = (srcChannel == 0 @@ -370,14 +370,18 @@ struct ConvertFlowsToInterconnect : public OpConversionPattern { WireBundle bundle = (*it).first; int channel = (*it).second; // handle special shim connectivity - if (curr == srcSB && srcSB->row == 0) { + if (curr == srcSB && + analyzer.getTile(rewriter, srcSB->col, srcSB->row) + .isShimNOCorPLTile()) { addConnection(rewriter, cast(swOp.getOperation()), flowOp, WireBundle::South, shim_ch, bundle, channel); - } else if (curr->row == 0 && + } else if (analyzer.getTile(rewriter, curr->col, curr->row) + .isShimNOCorPLTile() && (bundle == WireBundle::DMA || bundle == WireBundle::PLIO || bundle == WireBundle::NOC)) { shim_ch = channel; - if (analyzer.getTile(rewriter, curr->col, 0).isShimNOCTile()) { + if (analyzer.getTile(rewriter, curr->col, curr->row) + .isShimNOCTile()) { // shim DMAs at end of flows if (bundle == WireBundle::DMA) { shim_ch = (channel == 0 @@ -539,8 +543,155 @@ struct AIEPathfinderPass } } + // If the routing violates architecture-specific routing constraints, then + // attempt to partial reroute. + const auto &target_model = d.getTargetModel(); + std::vector problemConnects; + d.walk([&](ConnectOp connect) { + if (auto sw = connect->getParentOfType()) { + auto tile = sw.getTileOp(); + // Constraint: memtile stream switch constraints + if (tile.isMemTile() && + !target_model.isLegalMemtileConnection( + connect.getSourceBundle(), connect.getSourceChannel(), + connect.getDestBundle(), connect.getDestChannel())) { + problemConnects.push_back(connect); + } + } + }); + for (auto connect : problemConnects) { + auto sw_box = connect->getParentOfType(); + builder.setInsertionPoint(connect); + auto northSw = getSwitchbox(d, sw_box.colIndex(), sw_box.rowIndex() + 1); + auto southSw = getSwitchbox(d, sw_box.colIndex(), sw_box.rowIndex() - 1); + attemptFixupMemtileRouting(builder, sw_box, northSw, southSw, connect); + } + return; } + + bool attemptFixupMemtileRouting(OpBuilder builder, SwitchboxOp memtileSwOp, + SwitchboxOp northSwOp, SwitchboxOp southSwOp, + ConnectOp &problemConnect) { + unsigned problemNorthChannel; + if (problemConnect.getSourceBundle() == WireBundle::North) { + problemNorthChannel = problemConnect.getSourceChannel(); + } else if (problemConnect.getDestBundle() == WireBundle::North) { + problemNorthChannel = problemConnect.getDestChannel(); + } else + return false; // Problem is not about n-s routing + unsigned problemSouthChannel; + if (problemConnect.getSourceBundle() == WireBundle::South) { + problemSouthChannel = problemConnect.getSourceChannel(); + } else if (problemConnect.getDestBundle() == WireBundle::South) { + problemSouthChannel = problemConnect.getDestChannel(); + } else + return false; // Problem is not about n-s routing + + // Attempt to reroute northern neighbouring sw + if (reconnectConnectOps(builder, northSwOp, problemConnect, true, + WireBundle::South, problemNorthChannel, + problemSouthChannel)) + return true; + if (reconnectConnectOps(builder, northSwOp, problemConnect, false, + WireBundle::South, problemNorthChannel, + problemSouthChannel)) + return true; + // Otherwise, attempt to reroute southern neighbouring sw + if (reconnectConnectOps(builder, southSwOp, problemConnect, true, + WireBundle::North, problemSouthChannel, + problemNorthChannel)) + return true; + if (reconnectConnectOps(builder, southSwOp, problemConnect, false, + WireBundle::North, problemSouthChannel, + problemNorthChannel)) + return true; + return false; + } + + bool reconnectConnectOps(OpBuilder builder, SwitchboxOp sw, + ConnectOp problemConnect, bool isIncomingToSW, + WireBundle problemBundle, unsigned ProblemChan, + unsigned emptyChan) { + bool hasEmptyChannelSlot = true; + bool foundCandidateForFixup = false; + ConnectOp candidate; + if (isIncomingToSW) { + for (ConnectOp connect : sw.getOps()) { + if (connect.getDestBundle() == problemBundle && + connect.getDestChannel() == ProblemChan) { + candidate = connect; + foundCandidateForFixup = true; + } + if (connect.getDestBundle() == problemBundle && + connect.getDestChannel() == emptyChan) { + hasEmptyChannelSlot = false; + } + } + } else { + for (ConnectOp connect : sw.getOps()) { + if (connect.getSourceBundle() == problemBundle && + connect.getSourceChannel() == ProblemChan) { + candidate = connect; + foundCandidateForFixup = true; + } + if (connect.getSourceBundle() == problemBundle && + connect.getSourceChannel() == emptyChan) { + hasEmptyChannelSlot = false; + } + } + } + if (foundCandidateForFixup && hasEmptyChannelSlot) { + WireBundle problemBundleOpposite = (problemBundle == WireBundle::North) + ? (WireBundle::South) + : (WireBundle::North); + // Found empty channel slot, perform reroute + if (isIncomingToSW) { + replaceConnectOpWithNewDest(builder, candidate, problemBundle, + emptyChan); + replaceConnectOpWithNewSource(builder, problemConnect, + problemBundleOpposite, emptyChan); + } else { + replaceConnectOpWithNewSource(builder, candidate, problemBundle, + emptyChan); + replaceConnectOpWithNewDest(builder, problemConnect, + problemBundleOpposite, emptyChan); + } + return true; + } + return false; + } + + // Replace connect op + ConnectOp replaceConnectOpWithNewDest(OpBuilder builder, ConnectOp connect, + WireBundle newBundle, int newChannel) { + builder.setInsertionPoint(connect); + auto newOp = builder.create( + builder.getUnknownLoc(), connect.getSourceBundle(), + connect.getSourceChannel(), newBundle, newChannel); + connect.erase(); + return newOp; + } + ConnectOp replaceConnectOpWithNewSource(OpBuilder builder, ConnectOp connect, + WireBundle newBundle, + int newChannel) { + builder.setInsertionPoint(connect); + auto newOp = builder.create(builder.getUnknownLoc(), newBundle, + newChannel, connect.getDestBundle(), + connect.getDestChannel()); + connect.erase(); + return newOp; + } + + SwitchboxOp getSwitchbox(DeviceOp &d, int col, int row) { + SwitchboxOp output = nullptr; + d.walk([&](SwitchboxOp sw_box) { + if (sw_box.colIndex() == col && sw_box.rowIndex() == row) { + output = sw_box; + } + }); + return output; + } }; std::unique_ptr> diff --git a/test/create-flows/memtile_routing_constraints.mlir b/test/create-flows/memtile_routing_constraints.mlir new file mode 100644 index 0000000000..a42fd885f1 --- /dev/null +++ b/test/create-flows/memtile_routing_constraints.mlir @@ -0,0 +1,44 @@ +//===- memtile_routing_constraints.mlir ------------------------*- MLIR -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// (c) Copyright 2023, Advanced Micro Devices, Inc. +// +//===----------------------------------------------------------------------===// + +// RUN: aie-opt --aie-create-pathfinder-flows --aie-find-flows %s | FileCheck %s + +// CHECK: %[[T24:.*]] = AIE.tile(2, 4) +// CHECK: %[[T23:.*]] = AIE.tile(2, 3) +// CHECK: %[[T22:.*]] = AIE.tile(2, 2) +// CHECK: %[[T21:.*]] = AIE.tile(2, 1) +// CHECK: %[[T20:.*]] = AIE.tile(2, 0) +// CHECK: AIE.switchbox(%[[T21]]) { +// CHECK: AIE.connect +// CHECK: AIE.connect +// CHECK: } +// CHECK: AIE.switchbox(%[[T22]]) { +// CHECK: AIE.connect +// CHECK: AIE.connect +// CHECK: } +// CHECK: AIE.switchbox(%[[T20]]) { +// CHECK: AIE.connect +// CHECK: } +// CHECK: AIE.switchbox(%[[T23]]) { +// CHECK: AIE.connect +// CHECK: } + +module { + AIE.device(xcve2802) { + %t04 = AIE.tile(2, 4) + %t03 = AIE.tile(2, 3) + %t02 = AIE.tile(2, 2) + %t01 = AIE.tile(2, 1) + %t00 = AIE.tile(2, 0) + + AIE.flow(%t02, DMA : 0, %t01, DMA : 0) + AIE.flow(%t03, DMA : 0, %t00, DMA : 0) + } +}