MLIR-AIE
AIEGenerateColumnControlOverlay.cpp
Go to the documentation of this file.
1//===- AIEGenerateColumnControlOverlay.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 2024 Advanced Micro Devices Inc.
8//
9//===----------------------------------------------------------------------===//
10
14
15#include "mlir/IR/Attributes.h"
16#include "mlir/Pass/Pass.h"
17
18#include "llvm/ADT/SmallSet.h"
19
20namespace xilinx::AIE {
21#define GEN_PASS_DEF_AIEGENERATECOLUMNCONTROLOVERLAY
22#define GEN_PASS_DEF_AIEASSIGNTILECTRLIDS
23#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
24} // namespace xilinx::AIE
25
26#define DEBUG_TYPE "aie-generate-column-control-overlay"
27
28using namespace mlir;
29using namespace xilinx;
30using namespace xilinx::AIE;
31
32int getUnusedPacketIdFrom(DeviceOp device) {
33 int unusedPacketIdFrom = 0;
34 device.walk([&](AIE::PacketFlowOp pOp) {
35 unusedPacketIdFrom = std::max(unusedPacketIdFrom, pOp.IDInt());
36 });
37 device.walk([&](AIE::TileOp tOp) {
38 if (!tOp->hasAttr("controller_id"))
39 return;
40 auto controllerIdPkt =
41 tOp->getAttrOfType<AIE::PacketInfoAttr>("controller_id");
42 unusedPacketIdFrom =
43 std::max(unusedPacketIdFrom, (int)controllerIdPkt.getPktId());
44 });
45 return unusedPacketIdFrom + 1;
46}
47
48// AIE arch-specific tile id to controller id mapping. Users can use those
49// packet ids for design but run into risk of deadlocking control packet flows.
50DenseMap<AIE::TileID, int>
51getTileToControllerIdMap6RowsOrLess(bool clColumnWiseUniqueIDs,
52 const AIETargetModel &targetModel) {
53 // Below column controller id combinations were chosen to avoid packet flow
54 // deadlock due to single-level LUT bit-masking on packet header, which could
55 // fail to differentiate certain packet id combinations. Controller ids for
56 // shim tiles were chosen to be < 16, because when they act as actor id in TCT
57 // tokens, the actor id field only has 4 bits.
58 // FIXME
59 SmallVector<SmallVector<int>> validTileIds = {{15, 26, 27, 29, 30, 31},
60 {14, 21, 22, 23, 24, 25},
61 {13, 11, 17, 18, 19, 20},
62 {12, 28, 7, 8, 9, 10}};
63 DenseMap<AIE::TileID, int> tileIDMap;
64 for (int col = 0; col < targetModel.columns(); col++) {
65 for (int row = 0; row < targetModel.rows(); row++) {
66 if (clColumnWiseUniqueIDs)
67 tileIDMap[{col, row}] = validTileIds[0][row];
68 else
69 tileIDMap[{col, row}] = validTileIds[col][row];
70 }
71 }
72
73 return tileIDMap;
74}
75DenseMap<AIE::TileID, int>
76getTileToControllerIdMap(bool clColumnWiseUniqueIDs,
77 const AIETargetModel &targetModel) {
78 if (targetModel.rows() <= 6)
79 return getTileToControllerIdMap6RowsOrLess(clColumnWiseUniqueIDs,
80 targetModel);
81
82 // Controller id assignment strategy for devices with more than 6 rows.
83 if (!clColumnWiseUniqueIDs)
84 assert(false && "Device has more tiles than the total number of candidate "
85 "packet ids permitted by the device. Please switch to "
86 "clColumnWiseUniqueIDs mode for AIEAssignTileCtrlIDsPass.");
87 DenseMap<AIE::TileID, int> tileIDMap;
88
89 int unusedPacketIdFrom = 0;
90 for (int col = 0; col < targetModel.columns(); col++) {
91 if (clColumnWiseUniqueIDs)
92 unusedPacketIdFrom = 0;
93 for (int row = 0; row < targetModel.rows(); row++)
94 tileIDMap[{col, row}] = unusedPacketIdFrom++;
95 }
96
97 return tileIDMap;
98}
99
100// AIE arch-specific row id to shim dma mm2s channel mapping. All shim mm2s
101// channels were assumed to be available for control packet flow routing (i.e.
102// not reserved by any aie.flow circuit-switched routing).
103DenseMap<int, int> getRowToShimChanMap(const AIETargetModel &targetModel,
104 WireBundle bundle) {
105 DenseMap<int, int> rowMap;
106 SmallVector<int>
107 thresholdsToNextShimChannel; // a list of thresholds on the number of
108 // control ports that the ith shim channel
109 // could connect to, before advancing to
110 // the next shim channel in round robin
111 TileID shimTile = {0, 0};
112 while (!targetModel.isShimNOCTile(shimTile.col, shimTile.row)) {
113 shimTile.col++;
114 if (shimTile.col == targetModel.columns()) {
115 shimTile.col = 0;
116 shimTile.row++;
117 }
118 assert(!(shimTile.col == targetModel.columns() &&
119 shimTile.row == targetModel.rows()));
120 }
121
122 int numShimChans = targetModel.getNumSourceShimMuxConnections(
123 shimTile.col, shimTile.row, AIE::WireBundle::DMA);
124 for (int i = 1; i < numShimChans + 1; i++)
125 thresholdsToNextShimChannel.push_back(targetModel.rows() / numShimChans *
126 i);
127
128 if (bundle == WireBundle::DMA) { // Ctrl packets
129 int shimChanIdx = 0;
130 for (int r = 0; r < targetModel.rows(); r++) {
131 if (r >= thresholdsToNextShimChannel[shimChanIdx])
132 shimChanIdx++;
133 rowMap[r] = shimChanIdx;
134 }
135 } else if (bundle == WireBundle::South) { // TCT
136 for (int r = 0; r < targetModel.rows(); r++)
137 rowMap[r] = 0;
138 }
139
140 return rowMap;
141}
142
144 : xilinx::AIE::impl::AIEAssignTileCtrlIDsBase<AIEAssignTileCtrlIDsPass> {
145 void getDependentDialects(DialectRegistry &registry) const override {
146 registry.insert<AIEDialect>();
147 }
148 void runOnOperation() override {
149 DeviceOp device = getOperation();
150 const auto &targetModel = device.getTargetModel();
151
152 if (targetModel.getTargetArch() == AIEArch::AIE1)
153 return; // Disable this pass for AIE1; AIE1 support NYI.
154
155 // Collect all TileOps in columns occupied by the design.
156 llvm::MapVector<AIE::TileID, AIE::TileOp> tiles;
157 llvm::SmallSet<int, 1> occupiedCols;
158 for (auto tile : device.getOps<AIE::TileOp>()) {
159 int colIndex = tile.colIndex();
160 int rowIndex = tile.rowIndex();
161 tiles[{colIndex, rowIndex}] = tile;
162 occupiedCols.insert(colIndex);
163 }
164
165 auto tileIDMap =
166 getTileToControllerIdMap(clColumnWiseUniqueIDs, targetModel);
167 for (int col : occupiedCols) {
168 SmallVector<AIE::TileOp> tilesOnCol;
169 for (auto &[tId, tOp] : tiles) {
170 if (tId.col != col)
171 continue;
172 tilesOnCol.push_back(tOp);
173 }
174
175 for (auto tOp : tilesOnCol) {
176 if (tOp->hasAttr("controller_id"))
177 continue;
178 auto pktInfoAttr = AIE::PacketInfoAttr::get(
179 tOp->getContext(), /*pkt_type*/ 0,
180 /*pkt_id*/ tileIDMap[{tOp.colIndex(), tOp.rowIndex()}]);
181 tOp->setAttr("controller_id", pktInfoAttr);
182 }
183 }
184 }
185};
186
189 AIEGenerateColumnControlOverlayPass> {
190 void getDependentDialects(DialectRegistry &registry) const override {
191 registry.insert<AIEDialect>();
192 registry.insert<memref::MemRefDialect>();
193 }
194 void runOnOperation() override {
195 DeviceOp device = getOperation();
196 const auto &targetModel = device.getTargetModel();
197 OpBuilder builder = OpBuilder::atBlockTerminator(device.getBody());
198
199 if (targetModel.getTargetArch() == AIEArch::AIE1)
200 return; // Disable this pass for AIE1; AIE1 support NYI.
201
202 // Collect existing TileOps
203 llvm::MapVector<AIE::TileID, AIE::TileOp> tiles;
204 llvm::SmallSet<int, 1> occupiedCols;
205 for (auto tile : device.getOps<AIE::TileOp>()) {
206 int colIndex = tile.colIndex();
207 int rowIndex = tile.rowIndex();
208 tiles[{colIndex, rowIndex}] = tile;
209 occupiedCols.insert(colIndex);
210 }
211
212 auto tileIDMap = getTileToControllerIdMap(true, targetModel);
213 for (int col : occupiedCols) {
214 builder.setInsertionPointToStart(device.getBody());
215 AIE::TileOp shimTile = TileOp::getOrCreate(builder, device, col, 0);
216
217 if (clRouteShimCTRLToTCT == "all-tiles" ||
218 clRouteShimCTRLToTCT == "shim-only") {
219 // Get all tile ops on column col
220 SmallVector<AIE::TileOp> tilesOnCol;
221 for (auto &[tId, tOp] : tiles) {
222 if (tId.col != col)
223 continue;
224 if (clRouteShimCTRLToTCT == "shim-only" && !tOp.isShimNOCorPLTile())
225 continue;
226 tilesOnCol.push_back(tOp);
227 }
228
230 builder, device, shimTile, AIE::WireBundle::South, tilesOnCol,
231 AIE::WireBundle::TileControl, 0, tileIDMap, false);
232 }
233 if (clRouteShimDmaToTileCTRL) {
234 // Get all tile ops on column col
235 SmallVector<AIE::TileOp> tilesOnCol;
236 for (auto &[tId, tOp] : tiles) {
237 if (tId.col != col)
238 continue;
239 tilesOnCol.push_back(tOp);
240 }
241
243 builder, device, shimTile, AIE::WireBundle::DMA, tilesOnCol,
244 AIE::WireBundle::TileControl, 0, tileIDMap, true);
245 }
246 }
247 }
248
249 AIE::PacketFlowOp createPacketFlowOp(OpBuilder &builder, int &flowID,
250 Value source,
251 xilinx::AIE::WireBundle sourceBundle,
252 uint32_t sourceChannel, Value dest,
253 xilinx::AIE::WireBundle destBundle,
254 uint32_t destChannel,
255 mlir::BoolAttr keep_pkt_header = nullptr,
256 mlir::BoolAttr ctrl_pkt_flow = nullptr) {
257 OpBuilder::InsertionGuard guard(builder);
258
259 AIE::PacketFlowOp pktFlow =
260 AIE::PacketFlowOp::create(builder, builder.getUnknownLoc(), flowID++,
261 keep_pkt_header, ctrl_pkt_flow);
262 Region &r_pktFlow = pktFlow.getPorts();
263 Block *b_pktFlow = builder.createBlock(&r_pktFlow);
264 builder.setInsertionPointToStart(b_pktFlow);
265 AIE::PacketSourceOp::create(builder, builder.getUnknownLoc(), source,
266 sourceBundle, sourceChannel);
267 AIE::PacketDestOp::create(builder, builder.getUnknownLoc(), dest,
268 destBundle, destChannel);
269 AIE::EndOp::create(builder, builder.getUnknownLoc());
270 return pktFlow;
271 }
272
273 // Get a vector of shim channels not reserved by any circuit-switched aie.flow
274 // op
275 SmallVector<int> getAvailableShimChans(DeviceOp device, TileOp shimTile,
276 WireBundle shimWireBundle,
277 bool isShimMM2S) {
278 SmallVector<int> availableShimChans;
279 DenseMap<int, AIE::FlowOp> flowOpUsers;
280 const auto &targetModel = device.getTargetModel();
281
282 for (auto user : shimTile.getResult().getUsers()) {
283 auto fOp = dyn_cast<AIE::FlowOp>(user);
284 if (!fOp)
285 continue;
286 if (isShimMM2S && fOp.getSource() == shimTile &&
287 fOp.getSourceBundle() == shimWireBundle)
288 flowOpUsers[fOp.getSourceChannel()] = fOp;
289 else if (!isShimMM2S && fOp.getDest() == shimTile &&
290 fOp.getDestBundle() == shimWireBundle)
291 flowOpUsers[fOp.getDestChannel()] = fOp;
292 }
293 int numShimChans = 0;
294 if (isShimMM2S)
295 numShimChans = targetModel.getNumSourceShimMuxConnections(
296 shimTile.colIndex(), shimTile.rowIndex(), shimWireBundle);
297 else
298 numShimChans = targetModel.getNumDestShimMuxConnections(
299 shimTile.colIndex(), shimTile.rowIndex(), shimWireBundle);
300 for (int i = 0; i < numShimChans; i++) {
301 if (!flowOpUsers.count(i))
302 availableShimChans.push_back(i);
303 }
304
305 return availableShimChans;
306 }
307
308 // Create packet flows per col which moves control packets to and from shim
309 // dma
310 void generatePacketFlowsForControl(OpBuilder builder, DeviceOp device,
311 TileOp shimTile, WireBundle shimWireBundle,
312 SmallVector<AIE::TileOp> ctrlTiles,
313 WireBundle ctrlWireBundle,
314 int coreOrMemChanId,
315 DenseMap<TileID, int> tileIDMap,
316 bool isShimMM2S) {
317 int ctrlPktFlowID = 0;
318 auto rowToShimChanMap =
319 getRowToShimChanMap(device.getTargetModel(), shimWireBundle);
320 // Get all available shim channels, to verify that the one being used is
321 // available
322 auto availableShimChans =
323 getAvailableShimChans(device, shimTile, shimWireBundle, isShimMM2S);
324
325 builder.setInsertionPoint(device.getBody()->getTerminator());
326 for (auto tOp : ctrlTiles) {
327 if (tOp->hasAttr("controller_id"))
328 ctrlPktFlowID =
329 (int)tOp->getAttrOfType<AIE::PacketInfoAttr>("controller_id")
330 .getPktId();
331 else
332 ctrlPktFlowID = tileIDMap[{tOp.colIndex(), tOp.rowIndex()}];
333 // Check shim channel availability
334 if (!llvm::is_contained(availableShimChans,
335 rowToShimChanMap[tOp.rowIndex()])) {
336 device->emitOpError(
337 "failed to generate column control overlay from shim dma to tile "
338 "ctrl ports, because some shim mm2s dma channels were reserved "
339 "from routing control packets.");
340 return signalPassFailure();
341 }
342
343 auto keep_pkt_header = builder.getBoolAttr(true);
344 auto ctrl_pkt_flow = builder.getBoolAttr(true);
345 if (isShimMM2S)
346 (void)createPacketFlowOp(
347 builder, ctrlPktFlowID, shimTile, shimWireBundle,
348 rowToShimChanMap[tOp.rowIndex()], tOp, ctrlWireBundle,
349 coreOrMemChanId, keep_pkt_header, ctrl_pkt_flow);
350 else
351 (void)createPacketFlowOp(builder, ctrlPktFlowID, tOp, ctrlWireBundle,
352 coreOrMemChanId, shimTile, shimWireBundle,
353 rowToShimChanMap[tOp.rowIndex()],
354 keep_pkt_header, ctrl_pkt_flow);
355
356 // Generate shim dma alloc ops as handle for runtime sequence to pickup,
357 // when issuing control packets
358 if (shimWireBundle != WireBundle::DMA)
359 continue;
360
361 AIE::DMAChannelDir dir =
362 isShimMM2S ? AIE::DMAChannelDir::MM2S : AIE::DMAChannelDir::S2MM;
363 int chan = rowToShimChanMap[tOp.rowIndex()];
364 int col = shimTile.colIndex();
365 std::string dma_name = "ctrlpkt";
366 dma_name += "_col" + std::to_string(col); // col
367 dma_name += isShimMM2S ? "_mm2s" : "_s2mm"; // dir
368 dma_name += "_chan" + std::to_string(chan); // chan
369
370 // check to see if ShimDMAAllocationOp already exists
371 if (device.lookupSymbol(dma_name))
372 continue;
373
374 AIE::ShimDMAAllocationOp::create(
375 builder, builder.getUnknownLoc(), StringRef(dma_name),
376 shimTile.getResult(), dir, rowToShimChanMap[tOp.rowIndex()], false,
377 nullptr);
378 }
379 }
380
381 // Get packet-flow op with the same source or destination
382 AIE::PacketFlowOp getPktFlowWithSameSrcOrDst(DeviceOp device, TileOp srcTile,
383 WireBundle srcBundle,
384 int srcChan, TileOp destTile,
385 WireBundle destBundle,
386 int destChan) {
387 AIE::PacketFlowOp result = nullptr;
388 device.walk([&](AIE::PacketFlowOp fOp) {
389 for (auto srcOp : fOp.getOps<AIE::PacketSourceOp>()) {
390 if (srcOp.getTile() == srcTile && srcOp.getBundle() == srcBundle &&
391 srcOp.channelIndex() == srcChan) {
392 result = fOp;
393 return;
394 }
395 }
396 for (auto destOp : fOp.getOps<AIE::PacketDestOp>()) {
397 if (destOp.getTile() == destTile && destOp.getBundle() == destBundle &&
398 destOp.channelIndex() == destChan) {
399 result = fOp;
400 return;
401 }
402 }
403 });
404 return result;
405 }
406};
407
408std::unique_ptr<OperationPass<DeviceOp>> AIE::createAIEAssignTileCtrlIDsPass() {
409 return std::make_unique<AIEAssignTileCtrlIDsPass>();
410}
411
412std::unique_ptr<OperationPass<DeviceOp>>
414 return std::make_unique<AIEGenerateColumnControlOverlayPass>();
415}
416
417void populateAIEColumnControlOverlay(DeviceOp &device) {}
int getUnusedPacketIdFrom(DeviceOp device)
DenseMap< int, int > getRowToShimChanMap(const AIETargetModel &targetModel, WireBundle bundle)
DenseMap< AIE::TileID, int > getTileToControllerIdMap6RowsOrLess(bool clColumnWiseUniqueIDs, const AIETargetModel &targetModel)
void populateAIEColumnControlOverlay(DeviceOp &device)
DenseMap< AIE::TileID, int > getTileToControllerIdMap(bool clColumnWiseUniqueIDs, const AIETargetModel &targetModel)
virtual uint32_t getNumSourceShimMuxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a shimmux.
virtual int rows() const =0
Return the number of rows in the device.
bool isShimNOCTile(int col, int row) const
Return true if the given tile is a ShimNOC tile.
virtual int columns() const =0
Return the number of columns in the device.
Include the generated interface declarations.
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEAssignTileCtrlIDsPass()
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEGenerateColumnControlOverlayPass()
void getDependentDialects(DialectRegistry &registry) const override
SmallVector< int > getAvailableShimChans(DeviceOp device, TileOp shimTile, WireBundle shimWireBundle, bool isShimMM2S)
AIE::PacketFlowOp createPacketFlowOp(OpBuilder &builder, int &flowID, Value source, xilinx::AIE::WireBundle sourceBundle, uint32_t sourceChannel, Value dest, xilinx::AIE::WireBundle destBundle, uint32_t destChannel, mlir::BoolAttr keep_pkt_header=nullptr, mlir::BoolAttr ctrl_pkt_flow=nullptr)
void getDependentDialects(DialectRegistry &registry) const override
void generatePacketFlowsForControl(OpBuilder builder, DeviceOp device, TileOp shimTile, WireBundle shimWireBundle, SmallVector< AIE::TileOp > ctrlTiles, WireBundle ctrlWireBundle, int coreOrMemChanId, DenseMap< TileID, int > tileIDMap, bool isShimMM2S)
AIE::PacketFlowOp getPktFlowWithSameSrcOrDst(DeviceOp device, TileOp srcTile, WireBundle srcBundle, int srcChan, TileOp destTile, WireBundle destBundle, int destChan)