MLIR-AIE 1.4.6.dev
AIEExpandLoadPdi.cpp
Go to the documentation of this file.
1//===- AIEExpandLoadPdi.cpp -------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2025 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7//
8// This pass expands `npu.load_pdi` operations that reference a device. There
9// are two output modes, controlled by the `ctrl-pkt` option:
10//
11// 1. Default (ctrl-pkt=false): replaces each `load_pdi @device` with
12// a. an empty device PDI load (`load_pdi @empty_N`), which causes the
13// firmware to reset the device, and
14// b. an `aiex.npu.write_config @device`, whose register writes the NPU
15// translation emits (or, with inline-config, those writes as ops).
16// c. with teardown, an `aiex.npu.write_teardown @device` where it ends.
17// 2. With ctrl-pkt=true: replaces each `load_pdi @device` with
18// a. a `load_pdi @ctrl_pkt_overlay`, which configures the NPU to stream
19// further configuration as control packets, and
20// b. a sequence of `aiex.npu.control_packet` ops carrying the device's
21// configuration.
22//
23//===----------------------------------------------------------------------===//
24
30
31#include "mlir/Dialect/Arith/IR/Arith.h"
32#include "mlir/Dialect/MemRef/IR/MemRef.h"
33#include "mlir/Pass/Pass.h"
34
35#include "llvm/ADT/DenseMap.h"
36#include "llvm/ADT/MapVector.h"
37#include "llvm/ADT/StringRef.h"
38#include "llvm/Support/FormatVariadic.h"
39#include "llvm/Support/raw_ostream.h"
40
41#include <array>
42#include <map>
43#include <set>
44
45namespace xilinx::AIEX {
46#define GEN_PASS_DEF_AIEEXPANDLOADPDI
47#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
48} // namespace xilinx::AIEX
49
50#define DEBUG_TYPE "aie-expand-load-pdi"
51
52using namespace mlir;
53using namespace xilinx;
54using namespace xilinx::AIEX;
55using namespace xilinx::AIE;
56
57namespace {
58
59static AIE::DeviceOp controlOverlayOf(AIE::DeviceOp device) {
60 auto ref = device.getOverlayAttr();
61 return ref ? device->getParentOfType<ModuleOp>().lookupSymbol<AIE::DeviceOp>(
62 ref)
63 : AIE::DeviceOp{};
64}
65
66using OverlayCopies = llvm::DenseMap<AIE::DeviceOp, AIE::DeviceOp>;
67
68// Firmware caches the last PDI address, so consecutive resets alternate copies.
69static AIE::DeviceOp overlayPreload(AIE::DeviceOp overlay, unsigned parity,
70 OverlayCopies &copies) {
71 if (!parity)
72 return overlay;
73 auto [it, inserted] = copies.try_emplace(overlay);
74 if (!inserted)
75 return it->second;
76 auto copy = cast<AIE::DeviceOp>(overlay->clone());
77 copy.setSymName((overlay.getSymName() + "_copy").str());
78 SymbolTable(overlay->getParentOfType<ModuleOp>()).insert(copy);
79 return it->second = copy;
80}
81
82// A control-packet reload reaches a tile only if `overlay` routes control
83// packets to its TileControl port. `packets` are the reload's.
84static LogicalResult
85verifyOverlayReachesTiles(NpuLoadPdiOp loadPdiOp, AIE::DeviceOp overlay,
86 iterator_range<Block::iterator> packets) {
87 std::set<AIE::TileID> reached;
88 overlay.walk([&](AIE::SwitchboxOp sb) {
89 for (Operation &op : sb.getConnections().front()) {
90 std::optional<AIE::Port> dest;
91 if (auto master = dyn_cast<AIE::MasterSetOp>(op))
92 dest = master.destPort();
93 else if (auto connect = dyn_cast<AIE::ConnectOp>(op))
94 dest = connect.destPort();
95 if (dest == AIE::Port{AIE::WireBundle::TileControl, 0})
96 reached.insert({sb.colIndex(), sb.rowIndex()});
97 }
98 });
99 for (Operation &op : packets) {
100 auto packet = dyn_cast<NpuControlPacketOp>(op);
101 if (!packet)
102 continue;
103 AIE::TileID tile{static_cast<int>(packet.getColumnFromAddr()),
104 static_cast<int>(packet.getRowFromAddr())};
105 if (reached.count(tile))
106 continue;
107 return loadPdiOp.emitError()
108 << "a control-packet reload configures tile (" << tile.col << ", "
109 << tile.row << "), but @" << overlay.getSymName()
110 << " routes no control packets to it";
111 }
112 return success();
113}
114
115// The empty device whose PDI load makes the firmware reset the array. `parity`
116// alternates so that two consecutive loads never name the same PDI (see
117// transformLoadPdi).
118static AIE::DeviceOp getOrCreateEmptyDevice(ModuleOp moduleOp,
119 AIE::AIEDevice deviceType,
120 unsigned parity, Location loc) {
121 std::string emptyName = "empty_" + std::to_string(parity);
122 if (auto existing = moduleOp.lookupSymbol<AIE::DeviceOp>(emptyName)) {
123 // Shared reset PDIs retain every load and trailing sequence's provenance.
124 loc = FusedLoc::get(moduleOp.getContext(), {existing.getLoc(), loc});
125 existing->setLoc(loc);
126 existing.getRegion().front().getTerminator()->setLoc(loc);
127 return existing;
128 }
129
130 OpBuilder builder(moduleOp.getContext());
131 builder.setInsertionPointToStart(moduleOp.getBody());
132 auto emptyDevice = AIE::DeviceOp::create(
133 builder, loc, deviceType, builder.getStringAttr(emptyName), nullptr);
134 emptyDevice.getRegion().emplaceBlock();
135 builder.setInsertionPointToEnd(&emptyDevice.getRegion().front());
136 AIE::EndOp::create(builder, loc);
137 return emptyDevice;
138}
139
140// A control-packet reload: the load_pdi it replaces, the device it streams,
141// and the overlay load ahead of it.
142struct CtrlPktReload {
143 NpuLoadPdiOp load;
144 AIE::DeviceOp device;
145 NpuLoadPdiOp preload;
146 AIE::DeviceOp overlay;
147};
148
149// Set `builder` at the next load_pdi, or at the block's end if there is none.
150static void setInsertionPointToSegmentEnd(OpBuilder &builder,
151 NpuLoadPdiOp loadPdiOp) {
152 Operation *op = loadPdiOp->getNextNode();
153 while (op && !isa<NpuLoadPdiOp>(op) && !op->hasTrait<OpTrait::IsTerminator>())
154 op = op->getNextNode();
155 if (op)
156 builder.setInsertionPoint(op);
157 else
158 builder.setInsertionPointToEnd(loadPdiOp->getBlock());
159}
160
161// Helper to transform a single load_pdi operation. Without `keepPreload` the
162// reset or overlay load is left out. With `teardown` the configuration's
163// teardown goes where it ends. A control packet configuration under teardown
164// goes in here, its writes from `configData` and its teardown's from
165// `teardownData`, in program order, since masked writes fold onto the control
166// packets before them. Without teardown, control packets go in once every
167// load_pdi is known (`ctrlPktReloads`), so a sequence's loads share them.
168static LogicalResult transformLoadPdi(
169 NpuLoadPdiOp loadPdiOp, ModuleOp moduleOp, unsigned index,
170 AIEX::ExpandMode defaultMode, bool keepPreload, bool teardown,
171 AIEX::BlockwriteData *configData, AIEX::BlockwriteData *teardownData,
172 SmallVectorImpl<CtrlPktReload> &ctrlPktReloads, OverlayCopies &copies) {
173 OpBuilder builder(loadPdiOp);
174
175 // Only process load_pdi ops that reference a device
176 auto deviceRefAttr = loadPdiOp.getDeviceRefAttr();
177 if (!deviceRefAttr) {
178 return success();
179 }
180
181 // Per-op annotation takes precedence; fall back to pass default
182 AIEX::ExpandMode mode = loadPdiOp.getExpandMode().value_or(defaultMode);
183 if (mode == AIEX::ExpandMode::none)
184 return success();
185 bool ctrlPkt = (mode == AIEX::ExpandMode::ctrlpkt);
186
187 auto referencedDevice = moduleOp.lookupSymbol<AIE::DeviceOp>(deviceRefAttr);
188 if (!referencedDevice) {
189 loadPdiOp.emitError("Referenced symbol '")
190 << deviceRefAttr.getValue() << "' is not a device";
191 return failure();
192 }
193
194 AIE::DeviceOp overlay;
195 if (ctrlPkt) {
196 overlay = controlOverlayOf(referencedDevice);
197 if (!overlay)
198 return referencedDevice.emitOpError(
199 "requires an overlay symbol reference for control-packet "
200 "configuration");
201 }
202
203 // Consecutive preloads alternate between two PDIs with the same contents,
204 // since the firmware skips a load of the PDI it loaded last.
205 if (keepPreload) {
206 FlatSymbolRefAttr preloadRef;
207 if (ctrlPkt) {
208 preloadRef = FlatSymbolRefAttr::get(
209 overlayPreload(overlay, index % 2, copies).getSymNameAttr());
210 } else {
211 AIE::DeviceOp emptyDevice =
212 getOrCreateEmptyDevice(moduleOp, referencedDevice.getDevice(),
213 index % 2, loadPdiOp.getLoc());
214 preloadRef = FlatSymbolRefAttr::get(emptyDevice.getSymNameAttr());
215 }
216 auto expandNone =
217 AIEX::ExpandModeAttr::get(builder.getContext(), AIEX::ExpandMode::none);
218 if (ctrlPkt)
219 NpuLoadPdiOp::create(builder, loadPdiOp.getLoc(), preloadRef,
220 /*id=*/nullptr, /*size=*/nullptr,
221 /*address=*/nullptr, expandNone);
222 else
223 NpuLoadPdiOp::create(builder, loadPdiOp.getLoc(), preloadRef,
224 loadPdiOp.getIdAttr(), loadPdiOp.getSizeAttr(),
225 loadPdiOp.getAddressAttr(), expandNone);
226 }
227 Operation *beforeConfig = loadPdiOp->getPrevNode();
228
229 if (!ctrlPkt) {
230 NpuWriteConfigOp::create(builder, loadPdiOp.getLoc(), deviceRefAttr);
231 if (teardown) {
232 setInsertionPointToSegmentEnd(builder, loadPdiOp);
233 NpuWriteTeardownOp::create(builder, loadPdiOp.getLoc(), deviceRefAttr);
234 }
235 loadPdiOp.erase();
236 return success();
237 }
238 if (!teardown) {
239 ctrlPktReloads.push_back({loadPdiOp, referencedDevice,
240 cast<NpuLoadPdiOp>(beforeConfig), overlay});
241 return success();
242 }
243
246 builder, referencedDevice, *configData, /*clElfDir=*/"", outputType,
247 /*skipCtrlPktOverlay=*/true))) {
248 loadPdiOp.emitError("Failed to generate configuration operations");
249 return failure();
250 }
251 if (failed(verifyOverlayReachesTiles(
252 loadPdiOp, overlay,
253 {beforeConfig ? std::next(beforeConfig->getIterator())
254 : loadPdiOp->getBlock()->begin(),
255 loadPdiOp->getIterator()})))
256 return failure();
257
258 // Erase the load_pdi before inserting the teardown, so the teardown's masked
259 // writes fold onto the configuration's writes instead of stopping at it.
260 Location loc = loadPdiOp.getLoc();
261 setInsertionPointToSegmentEnd(builder, loadPdiOp);
262 loadPdiOp.erase();
264 builder, referencedDevice, *teardownData, outputType,
265 /*skipCtrlPktOverlay=*/true))) {
266 emitError(loc, "Failed to generate teardown operations");
267 return failure();
268 }
269 return success();
270}
271
272// The checks that need no expansion, so the pass fails before it changes the
273// IR.
274static LogicalResult
275verifyExpansion(ModuleOp module, ArrayRef<NpuLoadPdiOp> loadPdiOps,
276 function_ref<AIEX::ExpandMode(NpuLoadPdiOp)> expandMode,
277 bool teardown, bool initEntry) {
278 if (initEntry && !teardown)
279 return module.emitError(
280 "aie-expand-load-pdi: init-entry requires teardown");
281 if (initEntry)
282 for (auto device : module.getOps<AIE::DeviceOp>())
283 if (Operation *existing = device.lookupSymbol(initEntrySequenceName))
284 return existing->emitOpError()
285 << "is named '" << initEntrySequenceName
286 << "', which aie-expand-load-pdi reserves under init-entry";
287 if (!teardown)
288 return success();
289 for (NpuLoadPdiOp loadPdiOp : loadPdiOps) {
290 if (expandMode(loadPdiOp) == AIEX::ExpandMode::none)
291 continue;
292 // write_teardown's verifier checks this too, but inline-config leaves no
293 // write_teardown to verify.
294 auto dev = module.lookupSymbol<AIE::DeviceOp>(loadPdiOp.getDeviceRefAttr());
295 if (Operation *start = dev ? AIEX::findShimDmaStart(dev) : nullptr)
296 return start->emitOpError(
297 "starts a shim DMA channel, which teardown cannot stop");
298 }
299 return success();
300}
301
302struct AIEExpandLoadPdiPass
303 : public xilinx::AIEX::impl::AIEExpandLoadPdiBase<AIEExpandLoadPdiPass> {
304 using AIEExpandLoadPdiBase::AIEExpandLoadPdiBase;
305
306 void getDependentDialects(DialectRegistry &registry) const override {
307 registry.insert<arith::ArithDialect, memref::MemRefDialect, AIE::AIEDialect,
308 AIEX::AIEXDialect>();
309 }
310
311 void runOnOperation() override {
312 auto module = getOperation();
313
314 // Collect all load_pdi operations in program order;
315 // need to collect once, then transform all collected ops;
316 // since the transform inserts a new preload load_pdi, we can't transform
317 // as we walk or it'd infinitely recurse.
318 SmallVector<NpuLoadPdiOp> loadPdiOps;
319
320 module.walk(
321 [&](NpuLoadPdiOp loadPdiOp) { loadPdiOps.push_back(loadPdiOp); });
322
323 // Map the legacy bool option to the new ExpandMode enum.
324 AIEX::ExpandMode defaultMode =
325 clCtrlPkt ? AIEX::ExpandMode::ctrlpkt : AIEX::ExpandMode::write32;
326
327 auto expandMode = [&](NpuLoadPdiOp loadPdiOp) {
328 return loadPdiOp.getDeviceRefAttr()
329 ? loadPdiOp.getExpandMode().value_or(defaultMode)
330 : AIEX::ExpandMode::none;
331 };
332 // Per enclosing runtime sequence, the parity of the FIRST and LAST of its
333 // empty-device resets and of its overlay loads, plus the device type, and
334 // the kind of its last load, which leaves the array as the next dispatch
335 // finds it. `transformLoadPdi` picks the preload as `index % 2` over the
336 // module-wide op order, and that order includes ops this pass skips, so a
337 // sequence's first preload is not necessarily the even one and its parity
338 // cannot be recovered from a count. Captured BEFORE transforming, which
339 // erases the ops.
340 using ArrayState = std::pair<AIEX::ExpandMode, FlatSymbolRefAttr>;
341 struct PreloadParity {
342 unsigned firstParity = 0;
343 unsigned lastParity = 0;
344 bool seen = false;
345 AIE::AIEDevice device = {};
346 AIE::DeviceOp firstOverlay, lastOverlay;
347 };
348 struct SequenceLoads {
349 std::array<PreloadParity, 2> preloads;
350 ArrayState last = {AIEX::ExpandMode::none, {}};
351 };
352 llvm::MapVector<AIE::RuntimeSequenceOp, SequenceLoads> sequences;
353 for (auto [index, loadPdiOp] : llvm::enumerate(loadPdiOps)) {
354 auto seq = loadPdiOp->getParentOfType<AIE::RuntimeSequenceOp>();
355 if (!seq)
356 continue;
357 SequenceLoads &loads = sequences[seq];
358 AIEX::ExpandMode mode = expandMode(loadPdiOp);
359 loads.last = {mode, {}};
360 if (mode == AIEX::ExpandMode::none)
361 continue;
362 auto dev =
363 module.lookupSymbol<AIE::DeviceOp>(loadPdiOp.getDeviceRefAttr());
364 if (!dev)
365 continue;
366 auto overlay = mode == AIEX::ExpandMode::ctrlpkt ? controlOverlayOf(dev)
367 : AIE::DeviceOp{};
368 if (mode == AIEX::ExpandMode::ctrlpkt && !overlay) {
369 dev.emitOpError("requires an overlay symbol reference for "
370 "control-packet configuration");
371 return signalPassFailure();
372 }
373 loads.last.second = overlay ? dev.getOverlayAttr() : FlatSymbolRefAttr{};
374 auto &entry = loads.preloads[mode == AIEX::ExpandMode::ctrlpkt];
375 if (!entry.seen) {
376 entry.firstParity = index % 2;
377 entry.seen = true;
378 entry.firstOverlay = overlay;
379 }
380 entry.lastParity = index % 2;
381 entry.lastOverlay = overlay;
382 entry.device = dev.getDevice();
383 }
384
385 // Teardown leaves the array ready for the next configuration of the same
386 // kind, so only a sequence's first load, or one after a load of another
387 // kind, needs its preload. A load this pass leaves alone sets up a
388 // configuration no teardown undoes, so the load after it keeps its preload.
389 // The host may dispatch a device's sequences in any order, so with an init
390 // entry they start without a preload only if each ends torn down by the
391 // same kind, and `reconfig_init` holds that kind's preload.
392 llvm::MapVector<AIE::DeviceOp, ArrayState> initEntries;
393 if (clInitEntry) {
394 for (auto &[seq, loads] : sequences)
395 initEntries.try_emplace(seq->getParentOfType<AIE::DeviceOp>(),
396 loads.last);
397 for (auto &[seq, loads] : sequences) {
398 auto device = seq->getParentOfType<AIE::DeviceOp>();
399 auto *it = initEntries.find(device);
400 if (it != initEntries.end() &&
401 (loads.last.first == AIEX::ExpandMode::none ||
402 loads.last != it->second))
403 initEntries.erase(it);
404 }
405 }
406
407 if (failed(verifyExpansion(module, loadPdiOps, expandMode, clTeardown,
408 clInitEntry)))
409 return signalPassFailure();
410
411 OverlayCopies copies;
412 SmallVector<CtrlPktReload> ctrlPktReloads;
413 llvm::DenseMap<AIE::DeviceOp, AIEX::BlockwriteData> configData,
414 teardownData;
415 // The kind of each sequence's last load, which left the array as it is.
416 llvm::DenseMap<Operation *, std::optional<ArrayState>> arrayState;
417 for (auto [idx, loadPdiOp] : llvm::enumerate(loadPdiOps)) {
418 auto seq = loadPdiOp->getParentOfType<AIE::RuntimeSequenceOp>();
419 std::optional<ArrayState> start;
420 if (seq)
421 if (auto *it = initEntries.find(seq->getParentOfType<AIE::DeviceOp>());
422 it != initEntries.end())
423 start = it->second;
424 std::optional<ArrayState> &state =
425 arrayState.try_emplace(seq, start).first->second;
426 // We need to insert a reset/overlay `load_pdi` if the last configuration
427 // was configured using a different mode, or if we did not insert a
428 // teardown.
429 auto dev =
430 module.lookupSymbol<AIE::DeviceOp>(loadPdiOp.getDeviceRefAttr());
431 auto mode = expandMode(loadPdiOp);
432 ArrayState next = {mode, dev && mode == AIEX::ExpandMode::ctrlpkt
433 ? dev.getOverlayAttr()
434 : FlatSymbolRefAttr{}};
435 bool keepPreload = !clTeardown || state != next;
436 state = next;
437 AIEX::BlockwriteData *config = nullptr, *teardown = nullptr;
438 if (clTeardown && expandMode(loadPdiOp) == AIEX::ExpandMode::ctrlpkt) {
439 auto device = loadPdiOp->getParentOfType<AIE::DeviceOp>();
440 config =
441 &configData.try_emplace(device, device, "loadpdi_").first->second;
442 teardown = &teardownData.try_emplace(device, device, "teardown_")
443 .first->second;
444 }
445 if (failed(transformLoadPdi(loadPdiOp, module, idx, defaultMode,
446 keepPreload, clTeardown, config, teardown,
447 ctrlPktReloads, copies))) {
448 signalPassFailure();
449 return;
450 }
451 }
452
453 SmallVector<std::pair<Operation *, AIE::DeviceOp>> ctrlPktSites;
454 for (CtrlPktReload &reload : ctrlPktReloads)
455 ctrlPktSites.emplace_back(reload.load, reload.device);
456 if (failed(AIE::insertConfigOps(
458 signalPassFailure();
459 return;
460 }
461 for (CtrlPktReload &reload : ctrlPktReloads) {
462 if (failed(verifyOverlayReachesTiles(
463 reload.load, reload.overlay,
464 {std::next(reload.preload->getIterator()),
465 reload.load->getIterator()}))) {
466 signalPassFailure();
467 return;
468 }
469 reload.load.erase();
470 }
471
472 for (auto [device, kind] : initEntries) {
473 FlatSymbolRefAttr preloadRef =
474 kind.first == AIEX::ExpandMode::ctrlpkt
475 ? kind.second
476 : FlatSymbolRefAttr::get(
477 getOrCreateEmptyDevice(module, device.getDevice(), 0,
478 device.getLoc())
479 .getSymNameAttr());
480 OpBuilder builder(device.getContext());
481 builder.setInsertionPoint(
482 *device.getOps<AIE::RuntimeSequenceOp>().begin());
483 auto init = AIE::RuntimeSequenceOp::create(
484 builder, device.getLoc(),
485 builder.getStringAttr(initEntrySequenceName), BoolAttr{},
486 AIE::TraceBufferAttr{}, ArrayAttr{}, ArrayAttr{});
487 builder.setInsertionPointToStart(&init.getBody().emplaceBlock());
488 NpuLoadPdiOp::create(builder, device.getLoc(), preloadRef,
489 /*id=*/nullptr, /*size=*/nullptr,
490 /*address=*/nullptr,
491 /*expand_mode=*/
492 AIEX::ExpandModeAttr::get(builder.getContext(),
493 AIEX::ExpandMode::none));
494 }
495
496 if (clInlineConfig && failed(AIE::inlineWriteConfigs(module)))
497 return signalPassFailure();
498
499 // Under teardown a skipped preload follows only torn-down configurations
500 // (write_config's verifier checks this for resets), so nothing needs the
501 // append.
502 if (clTeardown)
503 return;
504
505 // The `index % 2` alternation above keeps two consecutive preloads on
506 // different PDIs WITHIN a sequence. But the host re-runs the whole sequence
507 // on every dispatch, so the alternation has to hold across that boundary
508 // too. When a sequence's last preload lands on the same PDI as its first,
509 // the firmware caches the address and no-ops the next dispatch's first
510 // load, and the configuration that follows lands on a device that was never
511 // reset. That is silent -- the first dispatch is correct and later ones
512 // return non-deterministic garbage.
513 //
514 // Append one more preload, of the parity opposite the last one, so the
515 // sequence ends somewhere its own start will not repeat. The empty PDI is a
516 // few hundred bytes, so the cost is negligible next to the configuration it
517 // guards.
518 for (auto &[seq, loads] : sequences) {
519 for (auto [ctrlPkt, info] : llvm::enumerate(loads.preloads)) {
520 if (!info.seen || info.firstParity != info.lastParity ||
521 info.firstOverlay != info.lastOverlay)
522 continue;
523 unsigned parity = 1 - info.lastParity;
524 FlatSymbolRefAttr preloadRef;
525 if (ctrlPkt) {
526 preloadRef = FlatSymbolRefAttr::get(
527 overlayPreload(info.lastOverlay, parity, copies)
528 .getSymNameAttr());
529 } else {
530 AIE::DeviceOp emptyDevice =
531 getOrCreateEmptyDevice(module, info.device, parity, seq.getLoc());
532 preloadRef = FlatSymbolRefAttr::get(emptyDevice.getSymNameAttr());
533 }
534 OpBuilder builder(seq.getContext());
535 Block &body = seq.getBody().front();
536 if (!body.empty() && body.back().hasTrait<OpTrait::IsTerminator>())
537 builder.setInsertionPoint(&body.back());
538 else
539 builder.setInsertionPointToEnd(&body);
540 NpuLoadPdiOp::create(builder, seq.getLoc(), preloadRef,
541 /*id=*/nullptr, /*size=*/nullptr,
542 /*address=*/nullptr,
543 /*expand_mode=*/
544 AIEX::ExpandModeAttr::get(seq.getContext(),
545 AIEX::ExpandMode::none));
546 }
547 }
548 }
549};
550
551} // namespace
552
553std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>>
555 return std::make_unique<AIEExpandLoadPdiPass>();
556}
constexpr llvm::StringLiteral initEntrySequenceName
Name of the runtime sequence aie-expand-load-pdi{init-entry} adds.
Definition AIEXPasses.h:100
mlir::Operation * findShimDmaStart(AIE::DeviceOp device)
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIEExpandLoadPdiPass()
mlir::LogicalResult insertConfigOps(llvm::ArrayRef< std::pair< mlir::Operation *, xilinx::AIE::DeviceOp > > sites, AIEToConfigurationOutputType outputType)
mlir::LogicalResult inlineWriteConfigs(mlir::ModuleOp module)
mlir::LogicalResult generateAndInsertConfigOps(mlir::OpBuilder &builder, xilinx::AIE::DeviceOp device, xilinx::AIEX::BlockwriteData &blockwriteData, llvm::StringRef clElfDir="", AIEToConfigurationOutputType outputType=AIEToConfigurationOutputType::Transaction, bool skipCtrlPktOverlay=false)
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
mlir::LogicalResult generateAndInsertTeardownOps(mlir::OpBuilder &builder, xilinx::AIE::DeviceOp device, xilinx::AIEX::BlockwriteData &blockwriteData, AIEToConfigurationOutputType outputType=AIEToConfigurationOutputType::Transaction, bool skipCtrlPktOverlay=false)
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