MLIR-AIE 1.4.5.dev
AIERT.cpp
Go to the documentation of this file.
1//===- AIERT.cpp ------------------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2024 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
8#include "aie/Targets/AIERT.h"
10
11#include "mlir/Dialect/Utils/StaticValueUtils.h"
12#include "mlir/Support/LogicalResult.h"
13#include "llvm/ADT/SmallPtrSet.h"
14#include "llvm/ADT/SmallSet.h"
15
16extern "C" {
17#include "xaiengine/xaie_core.h"
18#include "xaiengine/xaie_dma.h"
19#include "xaiengine/xaie_elfloader.h"
20#include "xaiengine/xaie_interrupt.h"
21#include "xaiengine/xaie_locks.h"
22#include "xaiengine/xaie_mem.h"
23#include "xaiengine/xaie_perfcnt.h"
24#include "xaiengine/xaie_plif.h"
25#include "xaiengine/xaie_reset.h"
26#include "xaiengine/xaie_ss.h"
27#include "xaiengine/xaie_txn.h"
28#include "xaiengine/xaiegbl.h"
29#include "xaiengine/xaiegbl_defs.h"
30}
31
32#include <filesystem>
33
34#define AIERC_STR(x) x, #x
35static const std::map<AieRC, std::string> AIERCTOSTR = {
36 {AIERC_STR(XAIE_OK)},
37 {AIERC_STR(XAIE_ERR)},
38 {AIERC_STR(XAIE_INVALID_DEVICE)},
39 {AIERC_STR(XAIE_INVALID_RANGE)},
40 {AIERC_STR(XAIE_INVALID_ARGS)},
41 {AIERC_STR(XAIE_INVALID_TILE)},
42 {AIERC_STR(XAIE_ERR_STREAM_PORT)},
43 {AIERC_STR(XAIE_INVALID_DMA_TILE)},
44 {AIERC_STR(XAIE_INVALID_BD_NUM)},
45 {AIERC_STR(XAIE_ERR_OUTOFBOUND)},
46 {AIERC_STR(XAIE_INVALID_DATA_MEM_ADDR)},
47 {AIERC_STR(XAIE_INVALID_ELF)},
48 {AIERC_STR(XAIE_CORE_STATUS_TIMEOUT)},
49 {AIERC_STR(XAIE_INVALID_CHANNEL_NUM)},
50 {AIERC_STR(XAIE_INVALID_LOCK)},
51 {AIERC_STR(XAIE_INVALID_DMA_DIRECTION)},
52 {AIERC_STR(XAIE_INVALID_PLIF_WIDTH)},
53 {AIERC_STR(XAIE_INVALID_LOCK_ID)},
54 {AIERC_STR(XAIE_INVALID_LOCK_VALUE)},
55 {AIERC_STR(XAIE_LOCK_RESULT_FAILED)},
56 {AIERC_STR(XAIE_INVALID_DMA_DESC)},
57 {AIERC_STR(XAIE_INVALID_ADDRESS)},
58 {AIERC_STR(XAIE_FEATURE_NOT_SUPPORTED)},
59 {AIERC_STR(XAIE_INVALID_BURST_LENGTH)},
60 {AIERC_STR(XAIE_INVALID_BACKEND)},
61 {AIERC_STR(XAIE_INSUFFICIENT_BUFFER_SIZE)},
62 {AIERC_STR(XAIE_ERR_MAX)}};
63#undef AIERC_STR
64
65static const std::map<xilinx::AIE::WireBundle, StrmSwPortType>
66 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE = {
67 {xilinx::AIE::WireBundle::Core, StrmSwPortType::CORE},
68 {xilinx::AIE::WireBundle::DMA, StrmSwPortType::DMA},
69 {xilinx::AIE::WireBundle::TileControl, StrmSwPortType::CTRL},
70 {xilinx::AIE::WireBundle::FIFO, StrmSwPortType::FIFO},
71 {xilinx::AIE::WireBundle::South, StrmSwPortType::SOUTH},
72 {xilinx::AIE::WireBundle::West, StrmSwPortType::WEST},
73 {xilinx::AIE::WireBundle::North, StrmSwPortType::NORTH},
74 {xilinx::AIE::WireBundle::East, StrmSwPortType::EAST},
75 // missing PLIO from WireBundle
76 // missing NOC from WireBundle
77 {xilinx::AIE::WireBundle::Trace, StrmSwPortType::TRACE},
78};
79
80#ifndef NDEBUG
81
82// https://stackoverflow.com/a/32230306
83template <typename H1>
84static llvm::raw_ostream &showAIEXRTArgs(llvm::raw_ostream &out,
85 const char *label, H1 &&value) {
86 return out << label << "=" << std::forward<H1>(value);
87}
88
89template <typename H1, typename... T>
90static llvm::raw_ostream &showAIEXRTArgs(llvm::raw_ostream &out,
91 const char *label, H1 &&value,
92 T &&...rest) {
93 const char *pcomma = strchr(label, ',');
94 return showAIEXRTArgs(out.write(label, pcomma - label)
95 << "=" << std::forward<H1>(value) << ',',
96 pcomma + 1, std::forward<T>(rest)...);
97}
98
99static llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
100 const XAie_LocType &loc);
101
102static llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
103 const XAie_Lock &lock);
104
105static llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
106 const XAie_Packet &packet);
107
108#define SHOW_AIERT_ARGS(os, ...) showAIEXRTArgs(os, #__VA_ARGS__, __VA_ARGS__)
109
110// So that we can use the pattern if(auto r = TRY_XAIE_API...) { // r is nonzero
111// }
112static_assert(XAIE_OK == 0);
113
114#define TRY_XAIE_API_FATAL_ERROR(API, ...) \
115 do { \
116 LLVM_DEBUG(llvm::dbgs() << "trying XAIE API: " << #API << " with args: "); \
117 LLVM_DEBUG(SHOW_AIERT_ARGS(llvm::dbgs(), __VA_ARGS__)); \
118 LLVM_DEBUG(llvm::dbgs() << "\n"); \
119 if (auto r = API(__VA_ARGS__)) \
120 llvm::report_fatal_error(llvm::Twine(#API " failed with ") + \
121 AIERCTOSTR.at(r)); \
122 } while (0)
123
124#define TRY_XAIE_API_EMIT_ERROR(OP, API, ...) \
125 do { \
126 LLVM_DEBUG(llvm::dbgs() << "trying XAIE API: " << #API << " with args: "); \
127 LLVM_DEBUG(SHOW_AIERT_ARGS(llvm::dbgs(), __VA_ARGS__)); \
128 LLVM_DEBUG(llvm::dbgs() << "\n"); \
129 if (auto r = API(__VA_ARGS__)) \
130 return OP.emitOpError() << #API " failed with " << AIERCTOSTR.at(r); \
131 } while (0)
132
133#define TRY_XAIE_API_LOGICAL_RESULT(API, ...) \
134 do { \
135 LLVM_DEBUG(llvm::dbgs() << "trying XAIE API: " << #API << " with args: "); \
136 LLVM_DEBUG(SHOW_AIERT_ARGS(llvm::dbgs(), __VA_ARGS__)); \
137 LLVM_DEBUG(llvm::dbgs() << "\n"); \
138 if (auto r = API(__VA_ARGS__)) { \
139 llvm::errs() << #API " failed with " << AIERCTOSTR.at(r); \
140 return failure(); \
141 } \
142 } while (0)
143
144#else
145
146#define TRY_XAIE_API_FATAL_ERROR(API, ...) \
147 do { \
148 if (auto r = API(__VA_ARGS__)) \
149 llvm::report_fatal_error(llvm::Twine(#API " failed with ") + \
150 AIERCTOSTR.at(r)); \
151 } while (0)
152
153#define TRY_XAIE_API_EMIT_ERROR(OP, API, ...) \
154 do { \
155 if (auto r = API(__VA_ARGS__)) \
156 return OP.emitOpError() << #API " failed with " << AIERCTOSTR.at(r); \
157 } while (0)
158
159#define TRY_XAIE_API_LOGICAL_RESULT(API, ...) \
160 do { \
161 if (auto r = API(__VA_ARGS__)) { \
162 llvm::errs() << #API " failed with " << AIERCTOSTR.at(r); \
163 return failure(); \
164 } \
165 } while (0)
166
167#endif
168
169using namespace mlir;
170using namespace xilinx;
171
172#define DEBUG_TYPE "aie-aiert"
173
174llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const XAie_LocType &loc) {
175 os << "XAie_LocType(col: " << std::to_string(loc.Col)
176 << ", row: " << std::to_string(loc.Row) << ")";
177 return os;
178}
179
180llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const XAie_Lock &lock) {
181 os << "XAie_Lock(id: " << std::to_string(lock.LockId)
182 << ", val: " << std::to_string(lock.LockVal) << ")";
183 return os;
184}
185
186llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
187 const XAie_Packet &packet) {
188 os << "XAie_Packet(id: " << std::to_string(packet.PktId)
189 << ", type: " << std::to_string(packet.PktType) << ")";
190 return os;
191}
192
193#define XAIE_BASE_ADDR 0x40000000
194#define XAIE_SHIM_ROW 0
195#define XAIE_MEM_TILE_ROW_START 1
196#define XAIE_PARTITION_BASE_ADDR 0x0
197
198#define NPI_ADDR 0x0
199#define NUM_LOCKS 16
200#define EVEN_BD_NUM_START 0
201#define ODD_BD_NUM_START 24
202
204 XAie_Config configPtr;
205 XAie_DevInst devInst;
206
207 // One Location per recorded XAie_TxnCmd, indexed by position in
208 // XAie_TxnInst::CmdBuf. Populated by TxnLocBracket scopes.
209 std::vector<mlir::Location> txnCmdLocs;
210
211 // One Location per operation in the exported transaction binary. Derived
212 // from txnCmdLocs by exportSerializedTransaction().
213 std::vector<mlir::Location> txnOpLocs;
214};
215
216// Walk DevInst->TxnList to find the active XAie_TxnInst (single-threaded
217// compilation: at most one). Returns null if no transaction is being
218// recorded. The XAie_TxnInst struct and TxnList layout are part of aie-rt's
219// public xaiegbl.h, so this only depends on the public header.
220static XAie_TxnInst *getActiveTxnInst(XAie_DevInst &devInst) {
221 XAie_List *node = devInst.TxnList.Next;
222 if (!node)
223 return nullptr;
224 return reinterpret_cast<XAie_TxnInst *>(reinterpret_cast<char *>(node) -
225 offsetof(XAie_TxnInst, Node));
226}
227
228static uint32_t getActiveTxnNumCmds(XAie_DevInst &devInst) {
229 XAie_TxnInst *inst = getActiveTxnInst(devInst);
230 return inst ? inst->NumCmds : 0;
231}
232
234 : ctl(c), loc(l), startCmds(c.getCurrentTxnNumCmds()) {}
235
237 uint32_t endCmds = ctl.getCurrentTxnNumCmds();
238 if (endCmds <= startCmds)
239 return;
240 // Only record meaningful locations. Falling through (no entry) lets the
241 // downstream emitter use its fallback (device.getLoc()), which preserves
242 // the pre-existing behavior for source ops that don't carry a real loc.
243 if (mlir::isa<mlir::UnknownLoc>(loc))
244 return;
245 ctl.recordTxnLocRange(startCmds, endCmds, loc);
246}
247
248xilinx::AIE::AIERTControl::~AIERTControl() { XAie_Finish(&aiert->devInst); }
249
251 : targetModel(tm), aiert(std::make_unique<AIERtImpl>()) {
252 // The first column in the NPU lacks a shim tile. AIE-RT exposes some of
253 // the internals about how this is modeled in a somewhat awkward way.
254 size_t partitionStartCol =
256 size_t partitionNumCols = tm.columns();
257 size_t deviceRows = tm.rows();
258 size_t deviceCols = tm.columns() + partitionStartCol;
259
260 // Don't put this in the target model, because it's XAIE specific.
261 unsigned char devGen = XAIE_DEV_GEN_AIE;
262 switch (tm.getTargetArch()) {
263 case AIEArch::AIE1: // probably unreachable.
264 devGen = XAIE_DEV_GEN_AIE;
265 break;
266 case AIEArch::AIE2:
267 // FIXME: What if we don't have an IPU? aie-rt
268 // models non-IPU devices differently.
269 devGen = XAIE_DEV_GEN_AIE2IPU;
270 break;
271 case AIEArch::AIE2p:
272 devGen = XAIE_DEV_GEN_AIE2P_STRIX_B0;
273 break;
274 case AIEArch::AIE2ps:
275 // AIE2PS configures the array through a CERT ELF instead of CDO or an
276 // xclbin (see AIE2PSTargetModel), so it has no aie-rt device generation.
277 // Listed here so that a new AIEArch still raises -Wswitch.
278 llvm::report_fatal_error("aie-rt configuration is not supported for the "
279 "AIE2PS architecture");
280 }
281 aiert->configPtr = XAie_Config{
282 /*AieGen*/ devGen,
283 /*BaseAddr*/ XAIE_BASE_ADDR,
284 /*ColShift*/ static_cast<uint8_t>(tm.getColumnShift()),
285 /*RowShift*/ static_cast<uint8_t>(tm.getRowShift()),
286 /*NumRows*/ static_cast<uint8_t>(deviceRows),
287 /*NumCols*/ static_cast<uint8_t>(deviceCols),
288 /*ShimRowNum*/ XAIE_SHIM_ROW,
289 /*MemTileRowStart*/ XAIE_MEM_TILE_ROW_START,
290 /*MemTileNumRows*/ static_cast<uint8_t>(tm.getNumMemTileRows()),
291 /*AieTileRowStart*/
292 static_cast<uint8_t>(XAIE_MEM_TILE_ROW_START + tm.getNumMemTileRows()),
293 /*AieTileNumRows*/
294 static_cast<uint8_t>(tm.rows() - tm.getNumMemTileRows() - 1),
295 /*PartProp*/ {},
296 /*Backend*/ XAIE_IO_BACKEND_CDO};
297 XAie_InstDeclare(_devInst, &aiert->configPtr);
298 aiert->devInst = _devInst;
299 TRY_XAIE_API_FATAL_ERROR(XAie_SetupPartitionConfig, &aiert->devInst,
300 XAIE_PARTITION_BASE_ADDR, partitionStartCol,
301 partitionNumCols);
302 TRY_XAIE_API_FATAL_ERROR(XAie_CfgInitialize, &aiert->devInst,
303 &aiert->configPtr);
304 TRY_XAIE_API_FATAL_ERROR(XAie_UpdateNpiAddr, &aiert->devInst, NPI_ADDR);
305}
306
308 bool xaieDebug) {
309 // Quoting: The instance of a device must be always declared using this
310 // macro. In the future, the same macro will be expanded to
311 // allocate more memory from the user application for resource
312 // management.
313 if (aieSim) {
314 TRY_XAIE_API_FATAL_ERROR(XAie_SetIOBackend, &aiert->devInst,
315 XAIE_IO_BACKEND_SIM);
316 } else if (xaieDebug)
317 TRY_XAIE_API_FATAL_ERROR(XAie_SetIOBackend, &aiert->devInst,
318 XAIE_IO_BACKEND_DEBUG);
319 else
320 TRY_XAIE_API_FATAL_ERROR(XAie_SetIOBackend, &aiert->devInst,
321 XAIE_IO_BACKEND_CDO);
322 return success();
323}
324
325static LogicalResult
326configureLocksInBdBlock(const AIE::AIETargetModel &targetModel,
327 XAie_DmaDesc &dmaTileBd, Block &block, int col, int row,
328 bool outOfOrder) {
329 LLVM_DEBUG(llvm::dbgs() << "\nstart configuring bds\n");
330 AIE::UseLockOp acquire, release;
331 if (failed(AIE::verifyBdLockPair(block, outOfOrder, acquire, release)))
332 return failure();
333 std::optional<int> acqValue, relValue, acqLockId, relLockId;
334 bool acqEn = false;
335
336 // switch (lock->getAc)
337 AIE::LockOp lock;
338 for (auto op : block.getOps<AIE::UseLockOp>()) {
339 // Only dyn_cast if you are going to check if it was of the type
340 // expected; if you aren't checking use cast instead as it will at
341 // least assert in debug mode with an easier to understand error than
342 // dereferencing.
343 lock = cast<AIE::LockOp>(op.getLock().getDefiningOp());
344 switch (op.getAction()) {
345 case AIE::LockAction::Acquire:
346 case AIE::LockAction::AcquireGreaterEqual: {
347 acqEn = op.getAcqEn();
348 acqLockId = lock.getLockIDValue();
349 auto value = op.getConstantValue();
350 if (failed(value))
351 return failure();
352 int32_t v = *value; // NOLINT(bugprone-unchecked-optional-access)
353 if (op.acquireGE())
354 v = -v;
355 acqValue = v;
356 break;
357 }
358 case AIE::LockAction::Release: {
359 relLockId = lock.getLockIDValue();
360 auto value = op.getConstantValue();
361 if (failed(value))
362 return failure();
363 relValue = value;
364 break;
365 }
366 }
367 }
368
369 if (targetModel.isMemTile(col, row)) {
370 auto lockOffset = targetModel.getLockLocalBaseIndex(
371 col, row, lock.colIndex(), lock.rowIndex());
372 if (lockOffset && acqLockId)
373 acqLockId.value() += lockOffset.value();
374 if (lockOffset && relLockId)
375 relLockId.value() += lockOffset.value();
376 }
377
378 // no RelEn in the arch spec even though the API requires you to set it?
379 bool relEn = false;
380 if (outOfOrder && !acqLockId) {
381 // Release-only out-of-order BD: point the disabled acquire lock at the
382 // release lock so XAie_LockInit gets a valid id.
383 acqLockId = relLockId;
384 acqValue = 0;
385 }
386 assert(acqLockId && acqValue && relLockId && relValue &&
387 "lock values must be resolved before init");
388 XAie_Lock acqLock = XAie_LockInit(acqLockId.value(), acqValue.value());
389 XAie_Lock relLock = XAie_LockInit(relLockId.value(), relValue.value());
390 TRY_XAIE_API_EMIT_ERROR((*block.getOps<AIE::UseLockOp>().begin()),
391 dmaTileBd.DmaMod->SetLock, &dmaTileBd, acqLock,
392 relLock, acqEn, relEn);
393 return success();
394}
395
396static LogicalResult configureBdInBlock(const AIE::AIETargetModel &targetModel,
397 XAie_DevInst *devInst,
398 XAie_DmaDesc &dmaTileBd, Block &block,
399 int col, int row, int bdId,
400 std::optional<int> nextBdId) {
401 std::optional<int> packetType;
402 std::optional<int> packetID;
403
404 // Below should go
405 auto maybePacketOps = block.getOps<AIE::DMABDPACKETOp>();
406 if (!maybePacketOps.empty()) {
407 assert(llvm::range_size(maybePacketOps) == 1 &&
408 "expected only one dma_bd_packet");
409 auto packetOp = *maybePacketOps.begin();
410 packetType = packetOp.getPacketType();
411 packetID = packetOp.getPacketID();
412 }
413
414 auto bdOp = *block.getOps<AIE::DMABDOp>().begin();
415
416 if (targetModel.isShimNOCTile(col, row)) {
417 // write them out like this so they show up with names in debug prints
418 uint8_t smid = 0;
419 uint32_t burstLen =
420 getShimBurstLengthBytes(targetModel, bdOp.getBurstLength());
421 uint8_t qOs = 0;
422 uint8_t cache = static_cast<uint8_t>(bdOp.getAxcacheOrDefault());
423 uint8_t secure = 0;
424 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetAxi, &dmaTileBd, smid,
425 burstLen / 16, qOs, cache, secure);
426 }
427
428 // get address from BufferOp (core,mem) or ExternalBufferOp (shim)
429 uint64_t baseAddr = 0;
430 if (targetModel.isShimNOCTile(col, row)) {
431 auto bufferOp =
432 cast<AIE::ExternalBufferOp>(bdOp.getBuffer().getDefiningOp());
433 // external buffers aren't required to have an address here because the
434 // address might get patched later or the default of zero might be a valid
435 // address.
436 if (auto addr = bufferOp.getAddress())
437 baseAddr = *addr; // NOLINT(bugprone-unchecked-optional-access)
438 } else {
439 auto bufferOp = cast<AIE::BufferOp>(bdOp.getBuffer().getDefiningOp());
440 auto addr = bufferOp.getAddress();
441 if (!addr)
442 return bufferOp.emitError("buffer must have address assigned");
443 baseAddr = *addr; // NOLINT(bugprone-unchecked-optional-access)
444 }
445
446 if (targetModel.isMemTile(col, row)) {
447 // check if buffer is allocated on the same memtile, the west, or the east
448 // one
449 auto bufferOp = cast<AIE::BufferOp>(bdOp.getBuffer().getDefiningOp());
450 auto bufferRow = bufferOp.getTileOp().getRow();
451 auto bufferCol = bufferOp.getTileOp().getCol();
452 auto addrOffset =
453 targetModel.getMemLocalBaseAddress(col, row, bufferCol, bufferRow);
454 if (addrOffset)
455 baseAddr += addrOffset.value();
456 }
457
458 // Runtime-valued sizes/strides are not supported on this static lowering
459 // path.
460 for (mlir::OpFoldResult s : bdOp.getMixedSizes())
461 if (!mlir::getConstantIntValue(s))
462 return bdOp->emitOpError(
463 "runtime-valued BD size/stride is not supported on the static XAIE "
464 "lowering path; use compile-time constant sizes");
465 for (mlir::OpFoldResult s : bdOp.getMixedStrides())
466 if (!mlir::getConstantIntValue(s))
467 return bdOp->emitOpError(
468 "runtime-valued BD size/stride is not supported on the static XAIE "
469 "lowering path; use compile-time constant sizes");
470 // The owning storage must outlive the ArrayRef used below.
471 std::optional<llvm::SmallVector<AIE::BDDimLayoutAttr>> dimsStorage =
472 bdOp.getConstantDimensions();
473 if (!dimsStorage)
474 return bdOp->emitOpError("internal error folding BD dimensions");
475 std::optional<llvm::ArrayRef<AIE::BDDimLayoutAttr>> dims;
476 if (!dimsStorage->empty())
477 dims = llvm::ArrayRef<AIE::BDDimLayoutAttr>(*dimsStorage);
478 uint64_t lenInBytes = bdOp.getLenInBytes();
479 uint64_t basePlusOffsetInBytes = baseAddr + bdOp.getOffsetInBytes();
480 if (!dims) {
481 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetAddrLen, &dmaTileBd,
482 basePlusOffsetInBytes, lenInBytes);
483 } else {
484 llvm::SmallVector<XAie_DmaDimDesc, 4> dimDescs(dims->size());
485 XAie_DmaTensor dmaTileBdTensor = {};
486 dmaTileBdTensor.NumDim = dimDescs.size();
487 dmaTileBdTensor.Dim = dimDescs.data();
488 // libxaie requires stride in multiples of 32b
489 double elementWidthIn32bWords =
490 static_cast<double>(bdOp.getBufferElementTypeWidthInBytes()) / 4.0;
491 for (size_t i = 0; i < dims->size(); i++) {
492 // Pass down dimensions in reverse order; in the MLIR, this allows
493 // us to specify step sizes/wraps in the same order as we would
494 // access a multi-dim C array, with the highest dimension first.
495 int j = dims->size() - i - 1;
496 uint16_t size;
497 uint32_t stride;
498 if (j > 0) {
499 stride = static_cast<uint32_t>(dims.value()[i].getStride() *
500 elementWidthIn32bWords);
501 size = dims.value()[i].getSize();
502 } else {
503 stride = dims.value()[i].getStride();
504 size = static_cast<uint16_t>(dims.value()[i].getSize() *
505 elementWidthIn32bWords);
506 }
507 stride = stride > 0 ? stride : 1;
508 // Assume AIE-ML architecture (ie use AieMlDimDesc instead of AieDimDesc);
509 // asserted in AIETranslateToCDODirect).
510 dmaTileBdTensor.Dim[j].AieMlDimDesc = {stride, size};
511 }
512 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetMultiDimAddr, &dmaTileBd,
513 &dmaTileBdTensor, basePlusOffsetInBytes,
514 lenInBytes);
515 }
516
517 // BD iteration.
518 if (auto iter = bdOp.getIteration(); iter && iter->getSize() > 1) {
519 if (iter->getStride() > 0) {
520 double elementWidthIn32bWords =
521 static_cast<double>(bdOp.getBufferElementTypeWidthInBytes()) / 4.0;
522 uint32_t stepInWords =
523 static_cast<uint32_t>(iter->getStride() * elementWidthIn32bWords);
524 uint8_t wrap = static_cast<uint8_t>(iter->getSize());
525 uint8_t iterCurr = static_cast<uint8_t>(iter->getCurrent());
526 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetBdIteration, &dmaTileBd,
527 stepInWords, wrap, iterCurr);
528 }
529 }
530
531 // ND zero padding.
532 std::optional<llvm::ArrayRef<AIE::BDPadLayoutAttr>> padDims =
533 bdOp.getPadDimensions();
534
535 if (padDims) {
536 llvm::SmallVector<XAie_PadDesc, 4> padDescs(padDims->size());
537 XAie_DmaPadTensor dmaPadTensor = {};
538 dmaPadTensor.NumDim = padDescs.size();
539 dmaPadTensor.PadDesc = padDescs.data();
540 // libxaie requires stride in multiples of 32b
541 double elementWidthIn32bWords =
542 static_cast<double>(bdOp.getBufferElementTypeWidthInBytes()) / 4.0;
543 for (size_t i = 0; i < padDims->size(); i++) {
544 // Pass down dimensions in reverse order.
545 int j = padDims->size() - i - 1;
546 uint8_t before;
547 uint8_t after;
548 if (j > 0) {
549 before = static_cast<uint8_t>(padDims.value()[i].getConstPadBefore());
550 after = static_cast<uint8_t>(padDims.value()[i].getConstPadAfter());
551 } else {
552 before = static_cast<uint8_t>(padDims.value()[i].getConstPadBefore() *
553 elementWidthIn32bWords);
554 after = static_cast<uint8_t>(padDims.value()[i].getConstPadAfter() *
555 elementWidthIn32bWords);
556 }
557 dmaPadTensor.PadDesc[j] = {before, after};
558 }
559 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetPadding, &dmaTileBd,
560 &dmaPadTensor);
561 }
562 if (nextBdId) {
563 auto enableNextBd = 1;
564 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetNextBd, &dmaTileBd,
565 nextBdId.value(), enableNextBd);
566 }
567
568 if (auto packetInfo = bdOp.getPacket()) {
569 packetType = packetInfo->getPktType();
570 packetID = packetInfo->assignedId();
571 }
572
573 if (packetID) {
574 if (!packetType)
575 return bdOp.emitError("must have packetType with packetID");
576 // getConstantLen() is nullopt for a runtime len operand, so this guard only
577 // catches a compile-time-zero length. A runtime len that happens to be 0 is
578 // not diagnosed here (runtime len is not yet reachable on this static
579 // path).
580 if (bdOp.getConstantLen() == 0)
581 return bdOp.emitOpError(
582 "For MM2S channels, if Buffer_Length=0 then Enable_Packet must be "
583 "set to 0, otherwise behavior is undefined (3.7.8 arch spec)");
584 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetPkt, &dmaTileBd,
585 XAie_PacketInit(*packetID, *packetType));
586 }
587 if (std::optional<int32_t> oooId = bdOp.getOutOfOrderId())
588 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaSetOutofOrderBdId, &dmaTileBd,
589 static_cast<uint8_t>(*oooId));
590 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaEnableBd, &dmaTileBd);
591 auto tileLoc = XAie_TileLoc(col, row);
592 TRY_XAIE_API_EMIT_ERROR(bdOp, XAie_DmaWriteBd, devInst, &dmaTileBd, tileLoc,
593 bdId);
594 LLVM_DEBUG(llvm::dbgs() << "\nend configuring bds\n");
595 return success();
596};
597
599 Operation &op, int col, int row, int chNum, const DMAChannelDir &channelDir,
600 int bdId, int repeatCount, uint32_t padValue, bool outOfOrder) {
601 TxnLocBracket bracket(*this, op.getLoc());
602 XAie_DmaDirection direction =
603 channelDir == DMAChannelDir::S2MM ? DMA_S2MM : DMA_MM2S;
604 auto tileLoc = XAie_TileLoc(col, row);
605 if (padValue != 0 && direction == DMA_MM2S &&
606 targetModel.isMemTile(col, row)) {
607 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaSetPadValue, &aiert->devInst, tileLoc,
608 static_cast<uint8_t>(chNum), padValue);
609 }
610 // in english repeat_count==0 means "do it once" and don't repeat but
611 // libxaie treats repeat_count=1 as do it once.
612 repeatCount += 1;
613 if (outOfOrder) {
614 XAie_DmaChannelDesc dmaChannelDesc;
615 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelDescInit, &aiert->devInst,
616 &dmaChannelDesc, tileLoc);
617 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelEnOutofOrder, &dmaChannelDesc,
618 static_cast<uint8_t>(XAIE_ENABLE));
619 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaWriteChannel, &aiert->devInst,
620 &dmaChannelDesc, tileLoc,
621 static_cast<uint8_t>(chNum), direction);
622 // StartBd=0 (ignored) and skip the BD/channel validity check.
623 // Disable TCT because the bd lookup id aliases the token bits.
624 XAie_DmaDeclareQueueConfig(startQueueCfg, /*StartBd=*/0, repeatCount,
625 /*EnToken=*/static_cast<uint8_t>(XAIE_DISABLE),
626 static_cast<uint8_t>(XAIE_ENABLE));
627 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelSetStartQueueGeneric,
628 &aiert->devInst, tileLoc, chNum, direction,
629 &startQueueCfg);
630 } else {
631 bool enTokenIssue = tileLoc.Row == 0 && direction == DMA_S2MM;
632 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelSetStartQueue, &aiert->devInst,
633 tileLoc, chNum, direction, bdId, repeatCount,
634 enTokenIssue);
635 }
636 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelEnable, &aiert->devInst, tileLoc,
637 chNum, direction);
638 return success();
639};
640
642 int col, int row,
643 bool outOfOrder) {
644 DMABDOp bd = *block.getOps<DMABDOp>().begin();
645 auto bdId = bd.getBdId();
646 assert(bdId.has_value() &&
647 "DMABDOp must have assigned bd_id; did you forget to run "
648 "aie-assign-bd-ids?");
649 TxnLocBracket bracket(*this, bd.getLoc());
650 XAie_DmaDesc dmaTileBd;
651 auto tileLoc = XAie_TileLoc(col, row);
652 TRY_XAIE_API_EMIT_ERROR(bd, XAie_DmaDescInit, &aiert->devInst, &dmaTileBd,
653 tileLoc);
654 if (!block.getOps<UseLockOp>().empty() &&
655 failed(configureLocksInBdBlock(targetModel, dmaTileBd, block, col, row,
656 outOfOrder)))
657 return failure();
658 // Out-of-order ignores Use_Next_BD; the IR chain exists only so every
659 // receive BD gets configured.
660 std::optional<int> nextBdId =
661 outOfOrder ? std::optional<int>() : bd.getNextBdId();
662 if (!block.getOps<DMABDOp>().empty() &&
663 failed(configureBdInBlock(
664 targetModel, &aiert->devInst, dmaTileBd, block, col, row,
665 *bdId, // NOLINT(bugprone-unchecked-optional-access)
666 nextBdId)))
667 return failure();
668 return success();
669}
670
671LogicalResult xilinx::AIE::AIERTControl::initLocks(DeviceOp &targetOp) {
672 for (auto tileOp : targetOp.getOps<TileOp>()) {
673 auto tileLoc = XAie_TileLoc(tileOp.colIndex(), tileOp.rowIndex());
674 if (!tileOp.isShimTile() && tileOp.getCoreOp()) {
675 TxnLocBracket bracket(*this, tileOp.getLoc());
676 TRY_XAIE_API_EMIT_ERROR(tileOp, XAie_CoreReset, &aiert->devInst, tileLoc);
677 TRY_XAIE_API_EMIT_ERROR(tileOp, XAie_CoreUnreset, &aiert->devInst,
678 tileLoc);
679 // Set locks to zero
680 for (uint8_t l = 0; l < NUM_LOCKS; l++) {
681 auto locInit = XAie_LockInit(l, 0);
682 TRY_XAIE_API_EMIT_ERROR(tileOp, XAie_LockSetValue, &aiert->devInst,
683 tileLoc, locInit);
684 }
685 }
686 }
687
688 // Set locks with explicit initializers
689 targetOp.walkOutsideRuntimeSequences<LockOp>([&](LockOp lockOp) {
690 if (lockOp.getLockID() && lockOp.getInit()) {
691 TxnLocBracket bracket(*this, lockOp.getLoc());
692 auto tileLoc = XAie_TileLoc(lockOp.getTileOp().colIndex(),
693 lockOp.getTileOp().rowIndex());
694 auto locInit = XAie_LockInit(*lockOp.getLockID(), *lockOp.getInit());
695 TRY_XAIE_API_FATAL_ERROR(XAie_LockSetValue, &aiert->devInst, tileLoc,
696 locInit);
697 } else
698 LLVM_DEBUG(llvm::dbgs()
699 << "lock op missing either id or init" << lockOp << "\n");
700 });
701 return success();
702}
703
704LogicalResult xilinx::AIE::AIERTControl::initBuffers(DeviceOp &targetOp) {
705 // Set buffers with explicit initializers
706 targetOp.walkOutsideRuntimeSequences<BufferOp>([&](BufferOp bufferOp) {
707 auto initialValue = bufferOp.getInitialValue();
708 if (!initialValue)
709 return;
710 TxnLocBracket bracket(*this, bufferOp.getLoc());
711 mlir::DenseElementsAttr denseInit =
712 dyn_cast<mlir::DenseElementsAttr>(initialValue.value());
713 if (!denseInit)
714 return;
715 auto tileLoc = XAie_TileLoc(bufferOp.getTileOp().colIndex(),
716 bufferOp.getTileOp().rowIndex());
717 std::optional<std::vector<char>> byteVec = denseAttrToBytes(denseInit);
718 if (!byteVec) {
719 llvm::outs() << "buffer op type not supported for initialization "
720 << bufferOp << "\n";
721 return;
722 }
723 TRY_XAIE_API_FATAL_ERROR(XAie_DataMemBlockWrite, &aiert->devInst, tileLoc,
724 bufferOp.getAddress().value(), byteVec->data(),
725 byteVec->size());
726 });
727 return success();
728}
729
730LogicalResult
732 bool skipCtrlPktOverlay) {
733 AIEDialect::IsCtrlPktOverlayAttrHelper overlay(targetOp.getContext());
734
735 // StreamSwitch (switchbox) configuration
736 for (auto switchboxOp : targetOp.getOps<SwitchboxOp>()) {
737 int32_t col = switchboxOp.colIndex();
738 int32_t row = switchboxOp.rowIndex();
739 XAie_LocType tileLoc = XAie_TileLoc(col, row);
740 assert(targetModel.hasProperty(AIETargetModel::IsNPU) &&
741 "Only NPU currently supported");
742
743 Block &b = switchboxOp.getConnections().front();
744 for (auto connectOp : b.getOps<ConnectOp>()) {
745 if (skipCtrlPktOverlay && overlay.isAttrPresent(connectOp))
746 continue;
747 TxnLocBracket bracket(*this, connectOp.getLoc());
749 switchboxOp, XAie_StrmConnCctEnable, &aiert->devInst, tileLoc,
750 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getSourceBundle()),
751 connectOp.sourceIndex(),
752 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getDestBundle()),
753 connectOp.destIndex());
754 }
755
756 for (auto masterSetOp : b.getOps<MasterSetOp>()) {
757 if (skipCtrlPktOverlay && overlay.isAttrPresent(masterSetOp))
758 continue;
759 TxnLocBracket bracket(*this, masterSetOp.getLoc());
760 int mask = 0;
761 int arbiter = -1;
762
763 // MasterSetOp::verify guarantees every amsel names the same arbiter, so
764 // folding them into one arbiter plus msel mask is safe here.
765 for (auto val : masterSetOp.getAmsels()) {
766 AMSelOp amsel = cast<AMSelOp>(val.getDefiningOp());
767 arbiter = amsel.arbiterIndex();
768 int msel = amsel.getMselValue();
769 mask |= (1 << msel);
770 }
771
772 auto dropHeader = masterSetOp.keepsPktHeader()
773 ? XAIE_SS_PKT_DONOT_DROP_HEADER
774 : XAIE_SS_PKT_DROP_HEADER;
776 masterSetOp, XAie_StrmPktSwMstrPortEnable, &aiert->devInst, tileLoc,
777 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(masterSetOp.getDestBundle()),
778 masterSetOp.destIndex(), dropHeader, arbiter, mask);
779 }
780
781 for (auto packetRulesOp : b.getOps<PacketRulesOp>()) {
782 if (skipCtrlPktOverlay && overlay.isAttrPresent(packetRulesOp))
783 continue;
784 TxnLocBracket bracket(*this, packetRulesOp.getLoc());
785 int slot = 0;
786 Block &block = packetRulesOp.getRules().front();
787 // The overlay's rules take the first slots of a port they share with the
788 // design's rules, and the overlay already enabled the port.
789 bool overlayPort =
790 skipCtrlPktOverlay &&
791 llvm::any_of(block.getOps<PacketRuleOp>(), [&](PacketRuleOp rule) {
792 return overlay.isAttrPresent(rule);
793 });
794 for (auto slotOp : block.getOps<PacketRuleOp>()) {
795 if (skipCtrlPktOverlay && overlay.isAttrPresent(slotOp)) {
796 slot++;
797 continue;
798 }
799 AMSelOp amselOp = cast<AMSelOp>(slotOp.getAmsel().getDefiningOp());
800 int arbiter = amselOp.arbiterIndex();
801 int msel = amselOp.getMselValue();
802 if (!overlayPort)
803 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlavePortEnable,
804 &aiert->devInst, tileLoc,
805 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
806 packetRulesOp.getSourceBundle()),
807 packetRulesOp.sourceIndex());
808 auto packetInit = XAie_PacketInit(slotOp.valueInt(), /*PktType*/ 0);
809 // TODO Need to better define packet id,type used here
810 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlaveSlotEnable,
811 &aiert->devInst, tileLoc,
812 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
813 packetRulesOp.getSourceBundle()),
814 packetRulesOp.sourceIndex(), slot, packetInit,
815 slotOp.maskInt(), msel, arbiter);
816 slot++;
817 }
818 }
819 }
820
821 for (auto muxOp : targetOp.getOps<ShimMuxOp>()) {
822 // NOTE ShimMux always connects from the south as directions are
823 // defined relative to the tile stream switch.
824 auto tileLoc =
825 XAie_TileLoc(muxOp.getTileOp().getCol(), muxOp.getTileOp().getRow());
826 Block &b = muxOp.getConnections().front();
827 for (auto connectOp : b.getOps<ConnectOp>()) {
828 TxnLocBracket bracket(*this, connectOp.getLoc());
829 // demux!
830 if (connectOp.getSourceBundle() == WireBundle::North)
831 TRY_XAIE_API_EMIT_ERROR(muxOp, XAie_EnableAieToShimDmaStrmPort,
832 &aiert->devInst, tileLoc,
833 connectOp.sourceIndex());
834 // mux
835 if (connectOp.getDestBundle() == WireBundle::North)
836 TRY_XAIE_API_EMIT_ERROR(muxOp, XAie_EnableShimDmaToAieStrmPort,
837 &aiert->devInst, tileLoc,
838 connectOp.destIndex());
839 }
840 }
841
842 // Cascade configuration
843 if (isa<AIE2TargetModel>(targetModel)) {
844 for (auto configOp : targetOp.getOps<ConfigureCascadeOp>()) {
845 TxnLocBracket bracket(*this, configOp.getLoc());
846 TileOp tile = cast<TileOp>(configOp.getTile().getDefiningOp());
847 auto tileLoc = XAie_TileLoc(tile.getCol(), tile.getRow());
849 targetOp, XAie_CoreConfigAccumulatorControl, &aiert->devInst, tileLoc,
850 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
851 static_cast<WireBundle>(configOp.getInputDir())),
852 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
853 static_cast<WireBundle>(configOp.getOutputDir())));
854 }
855 }
856
857 return success();
858}
859
860LogicalResult xilinx::AIE::AIERTControl::teardownSwitches(DeviceOp &targetOp) {
861 for (auto switchboxOp : targetOp.getOps<SwitchboxOp>()) {
862 XAie_LocType tileLoc =
863 XAie_TileLoc(switchboxOp.colIndex(), switchboxOp.rowIndex());
864 Block &b = switchboxOp.getConnections().front();
865 for (auto connectOp : b.getOps<ConnectOp>()) {
866 TxnLocBracket bracket(*this, connectOp.getLoc());
868 switchboxOp, XAie_StrmConnCctDisable, &aiert->devInst, tileLoc,
869 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getSourceBundle()),
870 connectOp.sourceIndex(),
871 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getDestBundle()),
872 connectOp.destIndex());
873 }
874 for (auto masterSetOp : b.getOps<MasterSetOp>()) {
875 TxnLocBracket bracket(*this, masterSetOp.getLoc());
877 masterSetOp, XAie_StrmPktSwMstrPortDisable, &aiert->devInst, tileLoc,
878 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(masterSetOp.getDestBundle()),
879 masterSetOp.destIndex());
880 }
881 for (auto packetRulesOp : b.getOps<PacketRulesOp>()) {
882 auto rules = packetRulesOp.getRules().front().getOps<PacketRuleOp>();
883 if (rules.empty())
884 continue;
885 TxnLocBracket bracket(*this, packetRulesOp.getLoc());
886 StrmSwPortType port =
887 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(packetRulesOp.getSourceBundle());
888 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlavePortDisable,
889 &aiert->devInst, tileLoc, port,
890 packetRulesOp.sourceIndex());
891 // A slot stays enabled when its port is disabled.
892 for (auto [slot, rule] : llvm::enumerate(rules))
893 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlaveSlotDisable,
894 &aiert->devInst, tileLoc, port,
895 packetRulesOp.sourceIndex(), slot);
896 }
897 }
898 return success();
899}
900
901LogicalResult xilinx::AIE::AIERTControl::disableCores(DeviceOp &targetOp) {
902 for (auto coreOp : targetOp.getOps<CoreOp>()) {
903 TileID tile = coreOp.getTileID();
904 TxnLocBracket bracket(*this, coreOp.getLoc());
905 TRY_XAIE_API_EMIT_ERROR(coreOp, XAie_CoreDisable, &aiert->devInst,
906 XAie_TileLoc(tile.col, tile.row));
907 }
908 return success();
909}
910
911LogicalResult xilinx::AIE::AIERTControl::resetDmaChannels(DeviceOp &targetOp) {
912 auto memOps = llvm::to_vector_of<TileElement>(targetOp.getOps<MemOp>());
913 llvm::append_range(memOps, targetOp.getOps<MemTileDMAOp>());
914 for (TileElement memOp : memOps) {
915 TileID tile = memOp.getTileID();
916 XAie_LocType tileLoc = XAie_TileLoc(tile.col, tile.row);
917 llvm::SmallSet<std::pair<DMAChannelDir, int>, 4> reset;
918 auto resetChannel = [&](Operation *op, DMAChannelDir dir,
919 int channel) -> LogicalResult {
920 if (!reset.insert({dir, channel}).second)
921 return success();
922 TxnLocBracket bracket(*this, op->getLoc());
923 XAie_DmaDirection direction =
924 dir == DMAChannelDir::S2MM ? DMA_S2MM : DMA_MM2S;
925 TRY_XAIE_API_EMIT_ERROR((*op), XAie_DmaChannelReset, &aiert->devInst,
926 tileLoc, channel, direction,
927 XAie_DmaChReset::DMA_CHANNEL_RESET);
928 // Writing a fresh descriptor ends the reset and returns every field of
929 // the control register, such as out-of-order mode, to its reset value.
930 XAie_DmaChannelDesc defaults;
931 TRY_XAIE_API_EMIT_ERROR((*op), XAie_DmaChannelDescInit, &aiert->devInst,
932 &defaults, tileLoc);
933 TRY_XAIE_API_EMIT_ERROR((*op), XAie_DmaWriteChannel, &aiert->devInst,
934 &defaults, tileLoc, channel, direction);
935 return success();
936 };
937 Region &region = memOp.getOperation()->getRegion(0);
938 for (auto dmaOp : region.getOps<DMAOp>())
939 if (failed(resetChannel(dmaOp, dmaOp.getChannelDir(),
940 dmaOp.getChannelIndex())))
941 return failure();
942 for (Block *block : getOrderedChainOfBlocks(&region))
943 for (auto startOp : block->getOps<DMAStartOp>())
944 if (failed(resetChannel(startOp, startOp.getChannelDir(),
945 startOp.getChannelIndex())))
946 return failure();
947 }
948 return success();
949}
950
951LogicalResult
953 bool skipCtrlPktOverlay) {
954
955 if (failed(verifyDMAChannelsResolved(targetOp)))
956 return failure();
957
958 if (failed(initLocks(targetOp))) {
959 return failure();
960 }
961
962 if (failed(initBuffers(targetOp))) {
963 return failure();
964 }
965
966 // A DMA whose lock is already satisfied streams as soon as it is enabled, so
967 // its route must exist first.
968 if (failed(configureSwitches(targetOp, skipCtrlPktOverlay))) {
969 return failure();
970 }
971
972 auto memOps = llvm::to_vector_of<TileElement>(targetOp.getOps<MemOp>());
973 llvm::append_range(memOps, targetOp.getOps<MemTileDMAOp>());
974 llvm::append_range(memOps, targetOp.getOps<ShimDMAOp>());
975 for (TileElement memOp : memOps) {
976 int col = memOp.getTileID().col;
977 int row = memOp.getTileID().row;
978
979 // Get the region's entry block, then start traversing through the chain of
980 // blocks.
981 llvm::SetVector<Block *> blockVector =
982 getOrderedChainOfBlocks(&memOp.getOperation()->getRegion(0));
983
984 llvm::SmallPtrSet<Block *, 8> oooBlocks =
985 collectOutOfOrderBlocks(blockVector);
986
987 // handle DMA ops separately
988 auto dmaOps = llvm::to_vector_of<DMAOp>(
989 memOp.getOperation()->getRegion(0).getOps<DMAOp>());
990 if (!dmaOps.empty()) {
991 for (auto dmaOp : dmaOps)
992 for (auto &bdRegion : dmaOp.getBds()) {
993 Block &block = bdRegion.getBlocks().front();
994 if (failed(
995 configureLocksAndBd(block, col, row, dmaOp.getOutOfOrder())))
996 return failure();
997 }
998 } else {
999 for (Block *block : blockVector) {
1000 if (block->getOps<DMABDOp>().empty())
1001 continue;
1002 if (failed(configureLocksAndBd(*block, col, row,
1003 oooBlocks.contains(block))))
1004 return failure();
1005 }
1006 }
1007
1008 if (!dmaOps.empty())
1009 for (auto dmaOp : dmaOps) {
1010 auto &block = dmaOp.getBds().front().getBlocks().front();
1011 DMABDOp bd = *block.getOps<DMABDOp>().begin();
1012 auto bdId = bd.getBdId();
1013 assert(bdId.has_value() &&
1014 "bd_id assigned by configureLocksAndBd above");
1015 if (failed(pushToBdQueueAndEnable(
1016 *dmaOp.getOperation(), col, row, dmaOp.getChannelIndex(),
1017 dmaOp.getChannelDir(), bdId.value(), dmaOp.getRepeatCount(),
1018 dmaOp.getPadValue(), dmaOp.getOutOfOrder())))
1019 return failure();
1020 }
1021 else
1022 for (Block *block : blockVector) {
1023 for (auto op : block->getOps<DMAStartOp>()) {
1024 DMABDOp bd = *op.getDest()->getOps<DMABDOp>().begin();
1025 int chNum = op.getChannelIndex();
1026 auto channelDir = op.getChannelDir();
1027 auto bdId = bd.getBdId();
1028 assert(bdId.has_value() &&
1029 "bd_id assigned by configureLocksAndBd above");
1030 if (failed(pushToBdQueueAndEnable(
1031 *bd.getOperation(), col, row, chNum, channelDir, bdId.value(),
1032 op.getRepeatCount(), op.getPadValue(), op.getOutOfOrder())))
1033 return failure();
1034 }
1035 }
1036 }
1037
1038 return success();
1039}
1040
1041LogicalResult xilinx::AIE::AIERTControl::addCoreEnable(DeviceOp &targetOp) {
1042 // Start execution of all the cores.
1043 for (auto tileOp : targetOp.getOps<TileOp>()) {
1044 auto tileLoc = XAie_TileLoc(tileOp.colIndex(), tileOp.rowIndex());
1045 if (!tileOp.isShimTile() && tileOp.getCoreOp()) {
1046 TxnLocBracket bracket(*this, tileOp.getCoreOp().getLoc());
1047 TRY_XAIE_API_EMIT_ERROR(targetOp, XAie_CoreEnable, &aiert->devInst,
1048 tileLoc);
1049 }
1050 }
1051 return success();
1052}
1053
1054LogicalResult xilinx::AIE::AIERTControl::addAieElf(uint8_t col, uint8_t row,
1055 const StringRef elfPath,
1056 bool aieSim) {
1057 TRY_XAIE_API_LOGICAL_RESULT(XAie_CoreDisable, &aiert->devInst,
1058 XAie_TileLoc(col, row));
1059 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaChannelResetAll, &aiert->devInst,
1060 XAie_TileLoc(col, row),
1061 XAie_DmaChReset::DMA_CHANNEL_RESET);
1062
1063 // loadSym: Load symbols from .map file. This argument is not used when
1064 // __AIESIM__ is not defined.
1065 TRY_XAIE_API_LOGICAL_RESULT(XAie_LoadElf, &aiert->devInst,
1066 XAie_TileLoc(col, row), elfPath.str().c_str(),
1067 /*loadSym*/ aieSim);
1068
1069 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaChannelResetAll, &aiert->devInst,
1070 XAie_TileLoc(col, row),
1071 XAie_DmaChReset::DMA_CHANNEL_UNRESET);
1072
1073 return success();
1074}
1075
1077 TRY_XAIE_API_LOGICAL_RESULT(XAie_ResetPartition, &aiert->devInst);
1078 return success();
1079}
1080
1081LogicalResult xilinx::AIE::AIERTControl::resetDMA(int col, int row, bool on) {
1082 auto tileLoc = XAie_TileLoc(col, row);
1083 XAie_DmaDesc dmaTileBd;
1084 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaDescInit, &aiert->devInst, &dmaTileBd,
1085 tileLoc);
1086 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaDisableBd, &dmaTileBd);
1087 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaChannelResetAll, &aiert->devInst, tileLoc,
1088 on ? XAie_DmaChReset::DMA_CHANNEL_UNRESET
1089 : XAie_DmaChReset::DMA_CHANNEL_RESET);
1090 return success();
1091}
1092
1093LogicalResult xilinx::AIE::AIERTControl::resetCore(int col, int row) {
1094 auto tileLoc = XAie_TileLoc(col, row);
1095 TRY_XAIE_API_LOGICAL_RESULT(XAie_CoreReset, &aiert->devInst, tileLoc);
1096 return success();
1097}
1098
1099LogicalResult xilinx::AIE::AIERTControl::resetSwitch(int col, int row) {
1100 XAie_LocType tileLoc = XAie_TileLoc(col, row);
1101
1102 // Reset all combinations of input/output routing in the switchbox
1103 for (auto endpoint_a : WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE) {
1104 for (auto endpoint_b : WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE) {
1105 unsigned n_a_connections = targetModel.getNumSourceSwitchboxConnections(
1106 col, row, endpoint_a.first);
1107 unsigned n_b_connections = targetModel.getNumDestSwitchboxConnections(
1108 col, row, endpoint_b.first);
1109 for (unsigned a_index = 0; a_index < n_a_connections; a_index++) {
1110 for (unsigned b_index = 0; b_index < n_b_connections; b_index++) {
1111 if (!targetModel.isLegalTileConnection(col, row, endpoint_a.first,
1112 a_index, endpoint_b.first,
1113 b_index)) {
1114 continue;
1115 }
1116 TRY_XAIE_API_FATAL_ERROR(XAie_StrmConnCctDisable, &aiert->devInst,
1117 tileLoc, endpoint_a.second, a_index,
1118 endpoint_b.second, b_index);
1119 }
1120 }
1121 }
1122 }
1123
1124 return success();
1125}
1126
1127LogicalResult xilinx::AIE::AIERTControl::resetCoreUnreset(int col, int row) {
1128 auto tileLoc = XAie_TileLoc(col, row);
1129 TRY_XAIE_API_LOGICAL_RESULT(XAie_CoreUnreset, &aiert->devInst, tileLoc);
1130 return success();
1131}
1132
1133LogicalResult xilinx::AIE::AIERTControl::resetLock(int col, int row,
1134 int lockId) {
1135 auto tileLoc = XAie_TileLoc(col, row);
1136 // Reset a single lock to value 0
1137 XAie_Lock lock;
1138 lock.LockId = lockId;
1139 lock.LockVal = 0;
1140 TRY_XAIE_API_LOGICAL_RESULT(XAie_LockSetValue, &aiert->devInst, tileLoc,
1141 lock);
1142 return success();
1143}
1144
1146 int col, int row, WireBundle sourceBundle, int sourceChannel,
1147 WireBundle destBundle, int destChannel) {
1148 auto tileLoc = XAie_TileLoc(col, row);
1149
1150 // Helper lambda to map WireBundle to StrmSwPortType
1151 auto mapBundle = [](WireBundle bundle) -> StrmSwPortType {
1152 switch (bundle) {
1153 case WireBundle::Core:
1154 return CORE;
1155 case WireBundle::DMA:
1156 return DMA;
1157 case WireBundle::FIFO:
1158 return FIFO;
1159 case WireBundle::South:
1160 return SOUTH;
1161 case WireBundle::West:
1162 return WEST;
1163 case WireBundle::North:
1164 return NORTH;
1165 case WireBundle::East:
1166 return EAST;
1167 case WireBundle::Trace:
1168 return TRACE;
1169 default:
1170 return SOUTH;
1171 }
1172 };
1173
1174 StrmSwPortType sourcePortType = mapBundle(sourceBundle);
1175 StrmSwPortType destPortType = mapBundle(destBundle);
1176
1177 // Disconnect the specific connection from source to destination
1178 TRY_XAIE_API_LOGICAL_RESULT(XAie_StrmConnCctDisable, &aiert->devInst, tileLoc,
1179 sourcePortType, sourceChannel, destPortType,
1180 destChannel);
1181
1182 return success();
1183}
1184
1185LogicalResult xilinx::AIE::AIERTControl::resetPerfCounters(int col, int row) {
1186 auto tileLoc = XAie_TileLoc(col, row);
1187 // Reset performance counters in all modules
1188 // Try core module counters (if applicable)
1189 for (int counterId = 0; counterId < 4; counterId++) {
1190 // Ignore errors as not all tiles have all counter types
1191 (void)XAie_PerfCounterReset(&aiert->devInst, tileLoc, XAIE_CORE_MOD,
1192 counterId);
1193 }
1194 // Try mem module counters
1195 for (int counterId = 0; counterId < 4; counterId++) {
1196 (void)XAie_PerfCounterReset(&aiert->devInst, tileLoc, XAIE_MEM_MOD,
1197 counterId);
1198 }
1199 // Try PL module counters (for shim tiles)
1200 for (int counterId = 0; counterId < 4; counterId++) {
1201 (void)XAie_PerfCounterReset(&aiert->devInst, tileLoc, XAIE_PL_MOD,
1202 counterId);
1203 }
1204 return success();
1205}
1206
1207LogicalResult xilinx::AIE::AIERTControl::addAieElfs(DeviceOp &targetOp,
1208 const StringRef elfPath,
1209 bool aieSim) {
1210 for (auto tileOp : targetOp.getOps<TileOp>())
1211 if (tileOp.isShimNOCorPLTile()) {
1212 // Resets no needed with V2 kernel driver
1213 } else {
1214 int col = tileOp.colIndex();
1215 int row = tileOp.rowIndex();
1216 if (auto coreOp = tileOp.getCoreOp()) {
1217 std::string fileName;
1218 if (auto fileAttr = coreOp.getElfFile()) {
1219 fileName = fileAttr->str();
1220 } else {
1221 // Skip cores without elf_file (e.g., lightweight reset
1222 // devices that only need DMA/lock reconfiguration).
1223 continue;
1224 }
1225 // Check if fileName is already an absolute path.
1226 // If so, use it directly. Otherwise, concatenate with elfPath.
1227 std::string fullPath;
1228 if (std::filesystem::path(fileName).is_absolute()) {
1229 fullPath = fileName;
1230 } else {
1231 auto ps = std::filesystem::path::preferred_separator;
1232 fullPath =
1233 (llvm::Twine(elfPath) + std::string(1, ps) + fileName).str();
1234 }
1235 if (failed(addAieElf(col, row, fullPath, aieSim)))
1236 return failure();
1237 }
1238 }
1239 return success();
1240}
1241
1242void xilinx::AIE::AIERTControl::dmaUpdateBdAddr(int col, int row, size_t addr,
1243 size_t bdId) {
1244 auto tileLoc = XAie_TileLoc(col, row);
1245 TRY_XAIE_API_FATAL_ERROR(XAie_DmaUpdateBdAddr, &aiert->devInst, tileLoc, addr,
1246 bdId);
1247}
1248
1250 aiert->txnCmdLocs.clear();
1251 aiert->txnOpLocs.clear();
1252 TRY_XAIE_API_FATAL_ERROR(XAie_StartTransaction, &aiert->devInst,
1253 XAIE_TRANSACTION_DISABLE_AUTO_FLUSH);
1254}
1255
1256// Project the per-XAie_TxnCmd locations in `cmdLocs` onto the operations
1257// aie-rt's serializer actually emits, mirroring _XAie_TxnExportSerialized's
1258// command-to-operation mapping. That mapping is not 1:1: the serializer
1259// buffers block writes and collapses a run of address-contiguous
1260// XAIE_IO_BLOCKWRITE commands into a single block-write operation, holds that
1261// pending run across intervening DDR-patch commands (so a patch is emitted
1262// *before* the block write it follows in CmdBuf), and drops
1263// XAIE_IO_LOAD_PM_END_INTERNAL entirely. Everything else emits one operation
1264// in command order.
1265static std::vector<mlir::Location>
1266projectCmdLocsOntoSerializedOps(const XAie_TxnInst &inst,
1267 const std::vector<mlir::Location> &cmdLocs) {
1268 std::vector<mlir::Location> opLocs;
1269 opLocs.reserve(inst.NumCmds);
1270
1271 mlir::Location unknown = mlir::UnknownLoc::get(cmdLocs.front().getContext());
1272 bool pendingBlockWrite = false;
1273 mlir::Location pendingBlockWriteLoc = unknown;
1274 uint64_t blockWriteEndOff = 0;
1275
1276 for (uint32_t i = 0; i < inst.NumCmds; ++i) {
1277 const XAie_TxnCmd &cmd = inst.CmdBuf[i];
1278 mlir::Location loc = i < cmdLocs.size() ? cmdLocs[i] : unknown;
1279
1280 if (cmd.Opcode == XAIE_IO_BLOCKWRITE) {
1281 // A command that extends the run in flight emits nothing of its own;
1282 // the run keeps the location of the command that started it.
1283 if (!pendingBlockWrite || cmd.RegOff != blockWriteEndOff) {
1284 if (pendingBlockWrite)
1285 opLocs.push_back(pendingBlockWriteLoc);
1286 pendingBlockWrite = true;
1287 pendingBlockWriteLoc = loc;
1288 }
1289 blockWriteEndOff = cmd.RegOff + cmd.Size * sizeof(uint32_t);
1290 continue;
1291 }
1292
1293 if (cmd.Opcode == XAIE_IO_LOAD_PM_END_INTERNAL)
1294 continue;
1295
1296 if (pendingBlockWrite && cmd.Opcode != XAIE_IO_CUSTOM_OP_DDR_PATCH) {
1297 opLocs.push_back(pendingBlockWriteLoc);
1298 pendingBlockWrite = false;
1299 }
1300 opLocs.push_back(loc);
1301 }
1302 if (pendingBlockWrite)
1303 opLocs.push_back(pendingBlockWriteLoc);
1304
1305 return opLocs;
1306}
1307
1309 // Export the transactions to a binary buffer
1310 uint8_t *txn_ptr = XAie_ExportSerializedTransaction(&aiert->devInst, 0, 0);
1311 XAie_TxnHeader *hdr = (XAie_TxnHeader *)txn_ptr;
1312 std::vector<uint8_t> txn_data(txn_ptr, txn_ptr + hdr->TxnSize);
1313 free(txn_ptr);
1314
1315 // Exporting leaves the recorded commands intact, so the loc projection can
1316 // still walk CmdBuf here. With nothing bracketed there is no location to
1317 // project and every emitted op falls back to the device location.
1318 XAie_TxnInst *inst = getActiveTxnInst(aiert->devInst);
1319 if (inst && !aiert->txnCmdLocs.empty())
1320 aiert->txnOpLocs =
1321 projectCmdLocsOntoSerializedOps(*inst, aiert->txnCmdLocs);
1322
1323 return txn_data;
1324}
1325
1326const std::vector<mlir::Location> &
1328 return aiert->txnOpLocs;
1329}
1330
1332 return getActiveTxnNumCmds(aiert->devInst);
1333}
1334
1336 uint32_t endCmds,
1337 mlir::Location loc) {
1338 if (endCmds <= startCmds)
1339 return;
1340 if (aiert->txnCmdLocs.size() < endCmds)
1341 aiert->txnCmdLocs.resize(endCmds, mlir::UnknownLoc::get(loc.getContext()));
1342 for (uint32_t i = startCmds; i < endCmds; ++i)
1343 aiert->txnCmdLocs[i] = loc;
1344}
#define XAIE_PARTITION_BASE_ADDR
Definition AIERT.cpp:196
#define TRY_XAIE_API_LOGICAL_RESULT(API,...)
Definition AIERT.cpp:133
#define NPI_ADDR
Definition AIERT.cpp:198
#define NUM_LOCKS
Definition AIERT.cpp:199
#define TRY_XAIE_API_FATAL_ERROR(API,...)
Definition AIERT.cpp:114
#define TRY_XAIE_API_EMIT_ERROR(OP, API,...)
Definition AIERT.cpp:124
#define AIERC_STR(x)
Definition AIERT.cpp:34
std::optional< uint32_t > getMemLocalBaseAddress(int localCol, int localRow, int memCol, int memRow) const
Return the memory base address (or offset) in the local tile when accessing a neighbor's memory or an...
virtual AIEArch getTargetArch() const =0
Return the target architecture.
bool isMemTile(int col, int row) const
Return true if the given tile is a Mem tile.
std::optional< uint32_t > getLockLocalBaseIndex(int localCol, int localRow, int lockCol, int lockRow) const
Return the lock base index (or offset) in the local tile when accessing a neighbor's lock or an empty...
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.
bool hasProperty(ModelProperty Prop) const
virtual int columns() const =0
Return the number of columns in the device.
TxnLocBracket(AIERTControl &ctl, mlir::Location loc)
Definition AIERT.cpp:233
llvm::SmallPtrSet< mlir::Block *, 8 > collectOutOfOrderBlocks(const llvm::SetVector< mlir::Block * > &blockVector)
Collect every BD block reached from an out-of-order aie.dma_start channel.
uint32_t getShimBurstLengthBytes(const AIE::AIETargetModel &tm, uint32_t burstLength)
mlir::LogicalResult verifyBdLockPair(mlir::Block &block, bool outOfOrder, UseLockOp &acquire, UseLockOp &release)
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 verifyDMAChannelsResolved(DeviceOp device)
std::optional< std::vector< char > > denseAttrToBytes(mlir::DenseElementsAttr denseInit)
A buffer's initial_value as the bytes to write to its tile memory, in element order.
llvm::SetVector< mlir::Block * > getOrderedChainOfBlocks(mlir::Region *region)
std::vector< mlir::Location > txnCmdLocs
Definition AIERT.cpp:209
std::vector< mlir::Location > txnOpLocs
Definition AIERT.cpp:213
mlir::LogicalResult configureSwitches(DeviceOp &targetOp, bool skipCtrlPktOverlay=false)
Definition AIERT.cpp:731
mlir::LogicalResult resetPerfCounters(int col, int row)
Definition AIERT.cpp:1185
mlir::LogicalResult configureLocksAndBd(mlir::Block &block, int col, int row, bool outOfOrder=false)
Definition AIERT.cpp:641
mlir::LogicalResult resetCore(int col, int row)
Definition AIERT.cpp:1093
void dmaUpdateBdAddr(int col, int row, size_t addr, size_t bdId)
Definition AIERT.cpp:1242
mlir::LogicalResult resetPartition()
Definition AIERT.cpp:1076
mlir::LogicalResult resetCoreUnreset(int col, int row)
Definition AIERT.cpp:1127
mlir::LogicalResult setIOBackend(bool aieSim, bool xaieDebug)
Definition AIERT.cpp:307
uint32_t getCurrentTxnNumCmds() const
Definition AIERT.cpp:1331
std::vector< uint8_t > exportSerializedTransaction()
Definition AIERT.cpp:1308
AIERTControl(const xilinx::AIE::AIETargetModel &tm)
Definition AIERT.cpp:250
mlir::LogicalResult initLocks(DeviceOp &targetOp)
Definition AIERT.cpp:671
mlir::LogicalResult resetDMA(int col, int row, bool on)
Definition AIERT.cpp:1081
mlir::LogicalResult disableCores(DeviceOp &targetOp)
Definition AIERT.cpp:901
mlir::LogicalResult initBuffers(DeviceOp &targetOp)
Definition AIERT.cpp:704
mlir::LogicalResult resetSwitch(int col, int row)
Definition AIERT.cpp:1099
mlir::LogicalResult addCoreEnable(DeviceOp &targetOp)
Definition AIERT.cpp:1041
mlir::LogicalResult resetSwitchConnection(int col, int row, WireBundle sourceBundle, int sourceChannel, WireBundle destBundle, int destChannel)
Definition AIERT.cpp:1145
const std::vector< mlir::Location > & getTxnOpLocs() const
Definition AIERT.cpp:1327
mlir::LogicalResult addInitConfig(DeviceOp &targetOp, bool skipCtrlPktOverlay=false)
Definition AIERT.cpp:952
mlir::LogicalResult pushToBdQueueAndEnable(mlir::Operation &op, int col, int row, int chNum, const DMAChannelDir &channelDir, int bdId, int repeatCount, uint32_t padValue=0, bool outOfOrder=false)
Definition AIERT.cpp:598
mlir::LogicalResult addAieElf(uint8_t col, uint8_t row, const mlir::StringRef elfPath, bool aieSim)
Definition AIERT.cpp:1054
void recordTxnLocRange(uint32_t startCmds, uint32_t endCmds, mlir::Location loc)
Definition AIERT.cpp:1335
mlir::LogicalResult resetDmaChannels(DeviceOp &targetOp)
Definition AIERT.cpp:911
mlir::LogicalResult addAieElfs(DeviceOp &targetOp, const mlir::StringRef elfPath, bool aieSim)
Definition AIERT.cpp:1207
mlir::LogicalResult teardownSwitches(DeviceOp &targetOp)
Definition AIERT.cpp:860
mlir::LogicalResult resetLock(int col, int row, int lockId)
Definition AIERT.cpp:1133