Skip to content

Commit

Permalink
Add a reroute process to enforce memtile routing constraints (#691)
Browse files Browse the repository at this point in the history
* Add a reroute process to enforce memtile routing constraints

* Fixup data format warnings

* Fixup data format warnings
  • Loading branch information
erwei-xilinx authored Oct 20, 2023
1 parent 7920cd0 commit fe868b1
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 5 deletions.
161 changes: 156 additions & 5 deletions lib/Dialect/AIE/Transforms/AIECreatePathfindFlows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ struct ConvertFlowsToInterconnect : public OpConversionPattern<AIE::FlowOp> {
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
Expand Down Expand Up @@ -370,14 +370,18 @@ struct ConvertFlowsToInterconnect : public OpConversionPattern<AIE::FlowOp> {
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<Interconnect>(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
Expand Down Expand Up @@ -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<ConnectOp> problemConnects;
d.walk([&](ConnectOp connect) {
if (auto sw = connect->getParentOfType<SwitchboxOp>()) {
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<SwitchboxOp>();
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<ConnectOp>()) {
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<ConnectOp>()) {
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<ConnectOp>(
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<ConnectOp>(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<OperationPass<DeviceOp>>
Expand Down
44 changes: 44 additions & 0 deletions test/create-flows/memtile_routing_constraints.mlir
Original file line number Diff line number Diff line change
@@ -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<North : 0, DMA : 0>
// CHECK: AIE.connect<North : 1, South : 1>
// CHECK: }
// CHECK: AIE.switchbox(%[[T22]]) {
// CHECK: AIE.connect<DMA : 0, South : 0>
// CHECK: AIE.connect<North : 1, South : 1>
// CHECK: }
// CHECK: AIE.switchbox(%[[T20]]) {
// CHECK: AIE.connect<North : 1, South : 2>
// CHECK: }
// CHECK: AIE.switchbox(%[[T23]]) {
// CHECK: AIE.connect<DMA : 0, South : 1>
// 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)
}
}

0 comments on commit fe868b1

Please sign in to comment.