MLIR-AIE 1.4.6.dev
AIEGenerateColumnControlOverlay.cpp
Go to the documentation of this file.
1//===- AIEGenerateColumnControlOverlay.cpp ----------------------*- C++ -*-===//
2//
3// Copyright (C) 2024 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
12
13#include "mlir/IR/Attributes.h"
14#include "mlir/Pass/PassManager.h"
15
16#include "llvm/ADT/SmallSet.h"
17
18#include <map>
19#include <optional>
20#include <set>
21#include <tuple>
22
23namespace xilinx::AIE {
24#define GEN_PASS_DEF_AIEGENERATECOLUMNCONTROLOVERLAY
25#define GEN_PASS_DEF_AIEASSIGNTILECTRLIDS
26#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
27} // namespace xilinx::AIE
28
29#define DEBUG_TYPE "aie-generate-column-control-overlay"
30
31using namespace mlir;
32using namespace xilinx;
33using namespace xilinx::AIE;
34
35static std::string getCtrlPktAllocName(int col, int chan) {
36 return "ctrlpkt_col" + std::to_string(col) + "_mm2s_chan" +
37 std::to_string(chan);
38}
39
41 : xilinx::AIE::impl::AIEAssignTileCtrlIDsBase<AIEAssignTileCtrlIDsPass> {
42 void runOnOperation() override {
43 DeviceOp device = getOperation();
44 const auto &targetModel = device.getTargetModel();
45
46 if (targetModel.getTargetArch() == AIEArch::AIE1)
47 return; // Disable this pass for AIE1; AIE1 support NYI.
48
49 auto tileIDMap =
50 targetModel.getTileToControllerIdMap(clColumnWiseUniqueIDs);
51 for (auto tile : device.getOps<TileOp>()) {
52 if (!tile->hasAttr("controller_id"))
53 tile->setAttr("controller_id",
54 PacketInfoAttr::get(tile.getContext(), 0,
55 tileIDMap[tile.getTileID()]));
56 }
57 }
58};
59
62 AIEGenerateColumnControlOverlayPass> {
63 using Base::Base;
64 DenseMap<std::pair<int, int>, unsigned> packetDemand;
65 DenseMap<int, llvm::SmallSet<int, 2>> fixedCircuits;
66
67 void runOnOperation() override {
68 llvm::MapVector<AIEDevice, SmallVector<DeviceOp>> groups;
69 AIEDialect::IsCtrlPktOverlayAttrHelper marker(&getContext());
70 for (DeviceOp device : getOperation().getOps<DeviceOp>())
71 if (!marker.isAttrPresent(device) && !device.getOverlayAttr() &&
72 device.getTargetModel().getTargetArch() != AIEArch::AIE1 &&
73 (configuration || !collectTokenIssuingTiles({device}).empty()))
74 groups[device.getDevice()].push_back(device);
75 for (auto &[target, users] : groups)
76 if (failed(generate(users)))
77 return signalPassFailure();
78 }
79
80 LogicalResult generate(ArrayRef<DeviceOp> users) {
81 ModuleOp module = getOperation();
82 AIEDialect::IsCtrlPktOverlayAttrHelper marker(&getContext());
83 DeviceOp first = users.front();
84 auto target = first.getDevice();
85 auto tokenTiles = collectTokenIssuingTiles(users);
86 std::set<int> columns;
87 if (configuration)
88 for (DeviceOp device : users)
89 for (TileOp tile : device.getOps<TileOp>())
90 columns.insert(tile.colIndex());
91 for (TileID tile : tokenTiles)
92 columns.insert(tile.col);
93 if (configuration) {
94 for (DeviceOp device : users)
95 device.walk([&](AIEX::NpuControlPacketOp packet) {
96 columns.insert(packet.getColumnFromAddr());
97 });
98 columns.insert(coverColumns.begin(), coverColumns.end());
100 }
101 SymbolTable symbols(module);
102 OpBuilder builder(module.getContext());
103 builder.setInsertionPointToEnd(module.getBody());
104 DeviceOp overlay =
105 DeviceOp::create(builder, module.getLoc(), target,
106 builder.getStringAttr("ctrl_pkt_overlay"), nullptr);
107 symbols.insert(overlay);
108 overlay.getRegion().emplaceBlock();
109 builder.setInsertionPointToEnd(overlay.getBody());
110 EndOp::create(builder, overlay.getLoc());
111 marker.setAttr(overlay, builder.getUnitAttr());
112 builder.setInsertionPointToStart(overlay.getBody());
113 for (DeviceOp device : users)
114 for (TileOp tile : device.getOps<TileOp>()) {
115 TileOp dest = TileOp::getOrCreate(builder, overlay, tile.colIndex(),
116 tile.rowIndex());
117 if (auto id = tile->getAttr("controller_id")) {
118 if (auto other = dest->getAttr("controller_id");
119 other && other != id) {
120 tile.emitOpError(
121 "has a different controller ID in another configuration");
122 return failure();
123 }
124 dest->setAttr("controller_id", id);
125 }
126 }
127 const auto &model = overlay.getTargetModel();
128 auto ids = model.getTileToControllerIdMap(true);
129 auto flow = [&](TileOp source, Port src, TileOp dest, Port dst,
130 TileOp controller) {
131 builder.setInsertionPoint(overlay.getBody()->getTerminator());
132 int id = ids[controller.getTileID()];
133 if (auto attr =
134 controller->getAttrOfType<PacketInfoAttr>("controller_id"))
135 id = attr.assignedId();
136 auto packet = PacketFlowOp::create(builder, controller.getLoc(), id,
137 builder.getBoolAttr(true),
138 builder.getBoolAttr(true));
139 OpBuilder::InsertionGuard guard(builder);
140 builder.createBlock(&packet.getPorts());
141 PacketSourceOp::create(builder, packet.getLoc(), source, src.bundle,
142 src.channel);
143 PacketDestOp::create(builder, packet.getLoc(), dest, dst.bundle,
144 dst.channel);
145 EndOp::create(builder, packet.getLoc());
146 };
147 for (int col : columns) {
148 if (col < 0 || col >= model.columns()) {
149 overlay.emitOpError("control coverage column is outside the target");
150 return failure();
151 }
152 builder.setInsertionPointToStart(overlay.getBody());
153 TileOp shim = TileOp::getOrCreate(builder, overlay, col, 0);
154 flow(shim, {WireBundle::TileControl, 0}, shim, {WireBundle::South, 0},
155 shim);
156 for (TileID tile : tokenTiles)
157 if (tile.col == col && tile.row != 0) {
158 builder.setInsertionPointToStart(overlay.getBody());
159 TileOp source = TileOp::getOrCreate(builder, overlay, col, tile.row);
160 flow(source, {WireBundle::TileControl, 0}, shim,
161 {WireBundle::South, 0}, source);
162 }
163 if (!configuration)
164 continue;
165 int channel = -1;
166 unsigned best = std::numeric_limits<unsigned>::max();
167 int count =
168 adaptive
169 ? model.getNumSourceShimMuxConnections(col, 0, WireBundle::DMA)
170 : 1;
171 for (int ch = 0; ch < count; ++ch)
172 if (!fixedCircuits.lookup(col).contains(ch) &&
173 packetDemand.lookup({col, ch}) < best) {
174 channel = ch;
175 best = packetDemand.lookup({col, ch});
176 }
177 if (channel < 0) {
178 overlay.emitOpError("failed to generate control overlay: no available "
179 "shim MM2S channel in column ")
180 << col;
181 return failure();
182 }
183 builder.setInsertionPoint(overlay.getBody()->getTerminator());
184 auto alloc = ShimDMAAllocationOp::create(
185 builder, shim.getLoc(), getCtrlPktAllocName(col, channel), shim,
186 DMAChannelDir::MM2S, channel, false, nullptr);
187 marker.setAttr(alloc, builder.getUnitAttr());
188 for (int row = 0; row < model.rows(); ++row) {
189 builder.setInsertionPointToStart(overlay.getBody());
190 TileOp dest = TileOp::getOrCreate(builder, overlay, col, row);
191 flow(shim, {WireBundle::DMA, channel}, dest,
192 {WireBundle::TileControl, 0}, dest);
193 }
194 }
195 for (DeviceOp device : users) {
196 device.setOverlayAttr(FlatSymbolRefAttr::get(overlay.getSymNameAttr()));
197 if (configuration)
198 AIEDialect::ReserveControllerPacketIdsAttrHelper(&getContext())
199 .setAttr(device, builder.getUnitAttr());
200 }
201 if (!configuration)
202 return success();
203 std::vector<std::pair<TileID, Port>> avoided;
204 if (adaptive && failed(applicationPorts(module, avoided)))
205 return failure();
206 OpPassManager routing(DeviceOp::getOperationName());
207 routing.addPass(createAIEAssignTileCtrlIDsPass());
208 routing.addPass(createAIEPathfinderPass({}, avoided));
209 if (failed(runPipeline(routing, overlay)))
210 return failure();
211 // Reprogramming another master set of an active arbiter can stall control
212 // packets.
213 for (auto box : overlay.getOps<SwitchboxOp>()) {
214 auto amsels = llvm::to_vector(box.getConnections().getOps<AMSelOp>());
215 std::set<std::pair<int, int>> slots;
216 for (auto amsel : amsels)
217 slots.emplace(amsel.arbiterIndex(), amsel.getMselValue());
218 builder.setInsertionPoint(box.getConnections().front().getTerminator());
219 for (auto amsel : amsels)
220 for (unsigned msel = 0; msel < model.getNumMselsPerArbiter(); ++msel)
221 if (slots.emplace(amsel.arbiterIndex(), msel).second)
222 AMSelOp::create(builder, amsel.getLoc(), amsel.arbiterIndex(),
223 msel);
224 }
225 overlay.walk([&](Operation *op) {
226 op->removeAttr("aie.priority_route");
227 if (!isa<DeviceOp, EndOp, PacketRulesOp>(op))
228 marker.setAttr(op, builder.getUnitAttr());
229 });
230 return success();
231 }
232 void collectShimMM2SUses(ArrayRef<DeviceOp> devices) {
233 packetDemand.clear();
234 fixedCircuits.clear();
235 auto shimCol = [](Value tile) -> std::optional<int> {
236 auto tOp = dyn_cast_or_null<TileOp>(tile.getDefiningOp());
237 if (!tOp || !tOp.isShimNOCorPLTile())
238 return std::nullopt;
239 return tOp.colIndex();
240 };
241 for (DeviceOp device : devices) {
242 for (auto endpoint : device.getOps<RouteEndpointOp>()) {
243 auto col = shimCol(endpoint.getTile());
244 auto channel = endpoint.getChannelIndex();
245 if (!col || endpoint.getBundle() != WireBundle::DMA ||
246 endpoint.getRouteDirection() != DMAChannelDir::MM2S || !channel)
247 continue;
248 bool packet =
249 llvm::any_of(device.getOps<RouteOp>(), [&](RouteOp route) {
250 return route.namesAsSource(endpoint.getSymName()) &&
251 route.getPacket();
252 });
253 if (packet)
254 ++packetDemand[{*col, *channel}];
255 else
256 fixedCircuits[*col].insert(*channel);
257 }
258 for (auto alloc : device.getOps<AIE::ShimDMAAllocationOp>()) {
259 if (alloc.getChannelDir() != AIE::DMAChannelDir::MM2S)
260 continue;
261 int col = alloc.getTileOp().colIndex();
262 int chan = static_cast<int>(alloc.getChannelIndex());
263 fixedCircuits[col].insert(chan);
264 }
265 for (auto flow : device.getOps<AIE::FlowOp>())
266 if (flow.getSourceBundle() == WireBundle::DMA)
267 if (auto col = shimCol(flow.getSource())) {
268 fixedCircuits[*col].insert(flow.getSourceChannel());
269 }
270 for (auto flow : device.getOps<AIE::PacketFlowOp>()) {
271 for (auto src : flow.getOps<AIE::PacketSourceOp>())
272 if (src.getBundle() == WireBundle::DMA)
273 if (auto col = shimCol(src.getTile())) {
274 ++packetDemand[{*col, src.channelIndex()}];
275 }
276 }
277 for (auto mux : device.getOps<AIE::ShimMuxOp>())
278 for (auto connect :
279 mux.getConnections().front().getOps<AIE::ConnectOp>())
280 if (connect.getSourceBundle() == WireBundle::DMA) {
281 int col = mux.getTileOp().colIndex();
282 fixedCircuits[col].insert(connect.sourceIndex());
283 }
284 }
285 }
286
287 static llvm::SmallSet<AIE::TileID, 4>
288 collectTokenIssuingTiles(ArrayRef<DeviceOp> devices) {
289 llvm::SmallSet<AIE::TileID, 4> tiles;
290 auto addTile = [&](Value tileValue) {
291 if (!tileValue)
292 return;
293 if (auto tile = dyn_cast_or_null<TileOp>(tileValue.getDefiningOp()))
294 tiles.insert({tile.colIndex(), tile.rowIndex()});
295 };
296 for (DeviceOp device : devices) {
297 for (TileOp tile : device.getOps<TileOp>())
298 if (tile.isShimNOCorPLTile())
299 tiles.insert(tile.getTileID());
300 device.walk([&](Operation *op) {
301 if (auto task = dyn_cast<AIEX::DMAConfigureTaskOp>(op)) {
302 if (task.getIssueToken())
303 addTile(task.getTile());
304 } else if (auto task = dyn_cast<AIEX::DMAConfigureTaskForOp>(op)) {
305 if (!task.getIssueToken())
306 return;
307 Operation *endpoint =
308 SymbolTable::lookupSymbolIn(device, task.getAlloc());
309 if (auto route = dyn_cast_or_null<RouteEndpointOp>(endpoint))
310 addTile(route.getTile());
311 else if (auto alloc = dyn_cast_or_null<ShimDMAAllocationOp>(endpoint))
312 addTile(alloc.getTile());
313 } else if (auto push = dyn_cast<AIEX::NpuPushQueueOp>(op)) {
314 if (push.getIssueToken())
315 tiles.insert({static_cast<int>(push.getColumn()),
316 static_cast<int>(push.getRow())});
317 }
318 });
319 }
320 return tiles;
321 }
322
323 LogicalResult
324 applicationPorts(ModuleOp module,
325 std::vector<std::pair<TileID, Port>> &avoided) {
326 OwningOpRef<ModuleOp> trial = cast<ModuleOp>(module->clone());
327 for (DeviceOp device :
328 llvm::make_early_inc_range(trial->getOps<DeviceOp>())) {
329 if (device->hasAttr("aie.is_ctrl_pkt_overlay"))
330 device.erase();
331 else
332 device.removeOverlayAttr();
333 }
334 PassManager pm(&getContext());
335 auto &dpm = pm.nest<DeviceOp>();
336 dpm.addPass(createAIEAssignPacketIdsPass());
337 dpm.addPass(createAIEObjectFifoAllocatePass());
339 if (failed(pm.run(*trial))) {
340 module.emitError("failed to analyze application resources for adaptive "
341 "control overlay");
342 return failure();
343 }
344 std::set<std::pair<TileID, Port>> ports;
345 for (DeviceOp device : trial->getOps<DeviceOp>()) {
346 for (SwitchboxOp box : device.getOps<SwitchboxOp>()) {
347 for (ConnectOp connect : box.getOps<ConnectOp>())
348 ports.insert({box.getTileOp().getTileID(), connect.destPort()});
349 for (MasterSetOp master : box.getOps<MasterSetOp>())
350 ports.insert({box.getTileOp().getTileID(), master.destPort()});
351 }
352 DynamicTileAnalysis analysis;
353 llvm::Error error = analysis.runAnalysis(device);
354 if (error) {
355 device.emitOpError("cannot plan adaptive control overlay: ")
356 << llvm::toString(std::move(error));
357 return failure();
358 }
359 auto collect = [&](const SwitchSettings &settings) {
360 for (const auto &[tile, setting] : settings)
361 for (Port output : setting.dsts)
362 ports.insert({tile, output});
363 };
364 for (const auto &[_, settings] : analysis.routing.settings)
365 collect(settings);
366 for (const auto &[_, settings] : analysis.routing.idSettings)
367 collect(settings);
368 }
369 avoided.assign(ports.begin(), ports.end());
370 return success();
371 }
372};
373
374std::unique_ptr<OperationPass<DeviceOp>> AIE::createAIEAssignTileCtrlIDsPass() {
375 return std::make_unique<AIEAssignTileCtrlIDsPass>();
376}
377
378std::unique_ptr<OperationPass<mlir::ModuleOp>>
380 return std::make_unique<AIEGenerateColumnControlOverlayPass>();
381}
382
383std::unique_ptr<OperationPass<mlir::ModuleOp>>
385 const AIEGenerateColumnControlOverlayOptions &options) {
386 return std::make_unique<AIEGenerateColumnControlOverlayPass>(options);
387}
llvm::Error runAnalysis(DeviceOp &device)
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEAssignTileCtrlIDsPass()
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoAllocatePass()
struct TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")"; return os; } friend std::string to_string(const TileID &s) { std::ostringstream ss; ss<< s; return ss.str(); } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const TileID &s) { os<< to_string(s); return os; } inline bool operator<(const TileID &rhs) const { return std::tie(col, row)< std::tie(rhs.col, rhs.row); } bool operator==(const TileID &rhs) const { return std::tie(col, row)==std::tie(rhs.col, rhs.row); } bool operator!=(const TileID &rhs) const { return !(*this==rhs);} int col, row;} TileID
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIEGenerateColumnControlOverlayPass()
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEAssignPacketIdsPass()
struct Port { WireBundle bundle; int channel; bool operator==(const Port &rhs) const { return std::tie(bundle, channel)==std::tie(rhs.bundle, rhs.channel); } bool operator!=(const Port &rhs) const { return !(*this==rhs);} bool operator<(const Port &rhs) const { return std::tie(bundle, channel)< std::tie(rhs.bundle, rhs.channel); } friend std::ostream &operator<<(std::ostream &os, const Port &port) { os<< "("; os<< stringifyWireBundle(port.bundle).str(); os<< ": "<< std::to_string(port.channel)<< ")"; return os; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Port &port) { os<< to_string(port); return os; } } Port
Definition AIEDialect.h:265
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoLowerDMAsPass()
std::map< TileID, SwitchSetting > SwitchSettings
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEPathfinderPass()
DenseMap< std::pair< int, int >, unsigned > packetDemand
void collectShimMM2SUses(ArrayRef< DeviceOp > devices)
LogicalResult applicationPorts(ModuleOp module, std::vector< std::pair< TileID, Port > > &avoided)
LogicalResult generate(ArrayRef< DeviceOp > users)
static llvm::SmallSet< AIE::TileID, 4 > collectTokenIssuingTiles(ArrayRef< DeviceOp > devices)
DenseMap< int, llvm::SmallSet< int, 2 > > fixedCircuits
std::map< PathEndPoint, SwitchSettings > settings
std::map< std::pair< PathEndPoint, int >, SwitchSettings > idSettings