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