MLIR-AIE 1.4.6.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 bool configureChannel, bool pushTask) {
602 TxnLocBracket bracket(*this, op.getLoc());
603 XAie_DmaDirection direction =
604 channelDir == DMAChannelDir::S2MM ? DMA_S2MM : DMA_MM2S;
605 auto tileLoc = XAie_TileLoc(col, row);
606 if (configureChannel && padValue != 0 && direction == DMA_MM2S &&
607 targetModel.isMemTile(col, row)) {
608 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaSetPadValue, &aiert->devInst, tileLoc,
609 static_cast<uint8_t>(chNum), padValue);
610 }
611 // in english repeat_count==0 means "do it once" and don't repeat but
612 // libxaie treats repeat_count=1 as do it once.
613 repeatCount += 1;
614 if (outOfOrder) {
615 if (configureChannel) {
616 XAie_DmaChannelDesc dmaChannelDesc;
617 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelDescInit, &aiert->devInst,
618 &dmaChannelDesc, tileLoc);
619 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelEnOutofOrder, &dmaChannelDesc,
620 static_cast<uint8_t>(XAIE_ENABLE));
621 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaWriteChannel, &aiert->devInst,
622 &dmaChannelDesc, tileLoc,
623 static_cast<uint8_t>(chNum), direction);
624 }
625 // StartBd=0 (ignored) and skip the BD/channel validity check.
626 // Disable TCT because the bd lookup id aliases the token bits.
627 XAie_DmaDeclareQueueConfig(startQueueCfg, /*StartBd=*/0, repeatCount,
628 /*EnToken=*/static_cast<uint8_t>(XAIE_DISABLE),
629 static_cast<uint8_t>(XAIE_ENABLE));
630 if (pushTask)
631 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelSetStartQueueGeneric,
632 &aiert->devInst, tileLoc, chNum, direction,
633 &startQueueCfg);
634 } else if (pushTask) {
635 bool enTokenIssue = tileLoc.Row == 0 && direction == DMA_S2MM;
636 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelSetStartQueue, &aiert->devInst,
637 tileLoc, chNum, direction, bdId, repeatCount,
638 enTokenIssue);
639 }
640 if (configureChannel)
641 TRY_XAIE_API_EMIT_ERROR(op, XAie_DmaChannelEnable, &aiert->devInst, tileLoc,
642 chNum, direction);
643 return success();
644};
645
647 int col, int row,
648 bool outOfOrder) {
649 DMABDOp bd = *block.getOps<DMABDOp>().begin();
650 auto bdId = bd.getBdId();
651 assert(bdId.has_value() &&
652 "DMABDOp must have assigned bd_id; did you forget to run "
653 "aie-assign-bd-ids?");
654 TxnLocBracket bracket(*this, bd.getLoc());
655 XAie_DmaDesc dmaTileBd;
656 auto tileLoc = XAie_TileLoc(col, row);
657 TRY_XAIE_API_EMIT_ERROR(bd, XAie_DmaDescInit, &aiert->devInst, &dmaTileBd,
658 tileLoc);
659 if (!block.getOps<UseLockOp>().empty() &&
660 failed(configureLocksInBdBlock(targetModel, dmaTileBd, block, col, row,
661 outOfOrder)))
662 return failure();
663 // Out-of-order ignores Use_Next_BD; the IR chain exists only so every
664 // receive BD gets configured.
665 std::optional<int> nextBdId =
666 outOfOrder ? std::optional<int>() : bd.getNextBdId();
667 if (!block.getOps<DMABDOp>().empty() &&
668 failed(configureBdInBlock(
669 targetModel, &aiert->devInst, dmaTileBd, block, col, row,
670 *bdId, // NOLINT(bugprone-unchecked-optional-access)
671 nextBdId)))
672 return failure();
673 return success();
674}
675
676LogicalResult xilinx::AIE::AIERTControl::initLocks(DeviceOp &targetOp) {
677 for (auto tileOp : targetOp.getOps<TileOp>()) {
678 auto tileLoc = XAie_TileLoc(tileOp.colIndex(), tileOp.rowIndex());
679 if (!tileOp.isShimTile() && tileOp.getCoreOp()) {
680 TxnLocBracket bracket(*this, tileOp.getLoc());
681 TRY_XAIE_API_EMIT_ERROR(tileOp, XAie_CoreReset, &aiert->devInst, tileLoc);
682 TRY_XAIE_API_EMIT_ERROR(tileOp, XAie_CoreUnreset, &aiert->devInst,
683 tileLoc);
684 // Set locks to zero
685 for (uint8_t l = 0; l < NUM_LOCKS; l++) {
686 auto locInit = XAie_LockInit(l, 0);
687 TRY_XAIE_API_EMIT_ERROR(tileOp, XAie_LockSetValue, &aiert->devInst,
688 tileLoc, locInit);
689 }
690 }
691 }
692
693 // Set locks with explicit initializers
694 targetOp.walkOutsideRuntimeSequences<LockOp>([&](LockOp lockOp) {
695 if (lockOp.getLockID() && lockOp.getInit()) {
696 TxnLocBracket bracket(*this, lockOp.getLoc());
697 auto tileLoc = XAie_TileLoc(lockOp.getTileOp().colIndex(),
698 lockOp.getTileOp().rowIndex());
699 auto locInit = XAie_LockInit(*lockOp.getLockID(), *lockOp.getInit());
700 TRY_XAIE_API_FATAL_ERROR(XAie_LockSetValue, &aiert->devInst, tileLoc,
701 locInit);
702 } else
703 LLVM_DEBUG(llvm::dbgs()
704 << "lock op missing either id or init" << lockOp << "\n");
705 });
706 return success();
707}
708
709LogicalResult xilinx::AIE::AIERTControl::initBuffers(DeviceOp &targetOp) {
710 // Set buffers with explicit initializers
711 targetOp.walkOutsideRuntimeSequences<BufferOp>([&](BufferOp bufferOp) {
712 auto initialValue = bufferOp.getInitialValue();
713 if (!initialValue)
714 return;
715 TxnLocBracket bracket(*this, bufferOp.getLoc());
716 mlir::DenseElementsAttr denseInit =
717 dyn_cast<mlir::DenseElementsAttr>(initialValue.value());
718 if (!denseInit)
719 return;
720 auto tileLoc = XAie_TileLoc(bufferOp.getTileOp().colIndex(),
721 bufferOp.getTileOp().rowIndex());
722 std::optional<std::vector<char>> byteVec = denseAttrToBytes(denseInit);
723 if (!byteVec) {
724 llvm::outs() << "buffer op type not supported for initialization "
725 << bufferOp << "\n";
726 return;
727 }
728 TRY_XAIE_API_FATAL_ERROR(XAie_DataMemBlockWrite, &aiert->devInst, tileLoc,
729 bufferOp.getAddress().value(), byteVec->data(),
730 byteVec->size());
731 });
732 return success();
733}
734
735LogicalResult
737 bool skipCtrlPktOverlay) {
738 AIEDialect::IsCtrlPktOverlayAttrHelper overlay(targetOp.getContext());
739
740 // StreamSwitch (switchbox) configuration
741 for (auto switchboxOp : targetOp.getOps<SwitchboxOp>()) {
742 int32_t col = switchboxOp.colIndex();
743 int32_t row = switchboxOp.rowIndex();
744 XAie_LocType tileLoc = XAie_TileLoc(col, row);
745 assert(targetModel.hasProperty(AIETargetModel::IsNPU) &&
746 "Only NPU currently supported");
747
748 Block &b = switchboxOp.getConnections().front();
749 for (auto connectOp : b.getOps<ConnectOp>()) {
750 if (skipCtrlPktOverlay && overlay.isAttrPresent(connectOp))
751 continue;
752 TxnLocBracket bracket(*this, connectOp.getLoc());
754 switchboxOp, XAie_StrmConnCctEnable, &aiert->devInst, tileLoc,
755 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getSourceBundle()),
756 connectOp.sourceIndex(),
757 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getDestBundle()),
758 connectOp.destIndex());
759 }
760
761 for (auto masterSetOp : b.getOps<MasterSetOp>()) {
762 if (skipCtrlPktOverlay && overlay.isAttrPresent(masterSetOp))
763 continue;
764 TxnLocBracket bracket(*this, masterSetOp.getLoc());
765 int mask = 0;
766 int arbiter = -1;
767
768 // MasterSetOp::verify guarantees every amsel names the same arbiter, so
769 // folding them into one arbiter plus msel mask is safe here.
770 for (auto val : masterSetOp.getAmsels()) {
771 AMSelOp amsel = cast<AMSelOp>(val.getDefiningOp());
772 arbiter = amsel.arbiterIndex();
773 int msel = amsel.getMselValue();
774 mask |= (1 << msel);
775 }
776
777 auto dropHeader = masterSetOp.keepsPktHeader()
778 ? XAIE_SS_PKT_DONOT_DROP_HEADER
779 : XAIE_SS_PKT_DROP_HEADER;
781 masterSetOp, XAie_StrmPktSwMstrPortEnable, &aiert->devInst, tileLoc,
782 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(masterSetOp.getDestBundle()),
783 masterSetOp.destIndex(), dropHeader, arbiter, mask);
784 }
785
786 for (auto packetRulesOp : b.getOps<PacketRulesOp>()) {
787 if (skipCtrlPktOverlay && overlay.isAttrPresent(packetRulesOp))
788 continue;
789 TxnLocBracket bracket(*this, packetRulesOp.getLoc());
790 int slot = 0;
791 Block &block = packetRulesOp.getRules().front();
792 // The overlay's rules take the first slots of a port they share with the
793 // design's rules, and the overlay already enabled the port.
794 bool overlayPort =
795 skipCtrlPktOverlay &&
796 llvm::any_of(block.getOps<PacketRuleOp>(), [&](PacketRuleOp rule) {
797 return overlay.isAttrPresent(rule);
798 });
799 for (auto slotOp : block.getOps<PacketRuleOp>()) {
800 if (skipCtrlPktOverlay && overlay.isAttrPresent(slotOp)) {
801 slot++;
802 continue;
803 }
804 AMSelOp amselOp = cast<AMSelOp>(slotOp.getAmsel().getDefiningOp());
805 int arbiter = amselOp.arbiterIndex();
806 int msel = amselOp.getMselValue();
807 if (!overlayPort)
808 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlavePortEnable,
809 &aiert->devInst, tileLoc,
810 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
811 packetRulesOp.getSourceBundle()),
812 packetRulesOp.sourceIndex());
813 auto packetInit = XAie_PacketInit(slotOp.valueInt(), /*PktType*/ 0);
814 // TODO Need to better define packet id,type used here
815 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlaveSlotEnable,
816 &aiert->devInst, tileLoc,
817 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
818 packetRulesOp.getSourceBundle()),
819 packetRulesOp.sourceIndex(), slot, packetInit,
820 slotOp.maskInt(), msel, arbiter);
821 slot++;
822 }
823 }
824 }
825
826 for (auto muxOp : targetOp.getOps<ShimMuxOp>()) {
827 // NOTE ShimMux always connects from the south as directions are
828 // defined relative to the tile stream switch.
829 auto tileLoc =
830 XAie_TileLoc(muxOp.getTileOp().getCol(), muxOp.getTileOp().getRow());
831 Block &b = muxOp.getConnections().front();
832 for (auto connectOp : b.getOps<ConnectOp>()) {
833 if (skipCtrlPktOverlay && overlay.isAttrPresent(connectOp))
834 continue;
835 TxnLocBracket bracket(*this, connectOp.getLoc());
836 // demux!
837 if (connectOp.getSourceBundle() == WireBundle::North)
838 TRY_XAIE_API_EMIT_ERROR(muxOp, XAie_EnableAieToShimDmaStrmPort,
839 &aiert->devInst, tileLoc,
840 connectOp.sourceIndex());
841 // mux
842 if (connectOp.getDestBundle() == WireBundle::North)
843 TRY_XAIE_API_EMIT_ERROR(muxOp, XAie_EnableShimDmaToAieStrmPort,
844 &aiert->devInst, tileLoc,
845 connectOp.destIndex());
846 }
847 }
848
849 // Cascade configuration
850 if (isa<AIE2TargetModel>(targetModel)) {
851 for (auto configOp : targetOp.getOps<ConfigureCascadeOp>()) {
852 TxnLocBracket bracket(*this, configOp.getLoc());
853 TileOp tile = cast<TileOp>(configOp.getTile().getDefiningOp());
854 auto tileLoc = XAie_TileLoc(tile.getCol(), tile.getRow());
856 targetOp, XAie_CoreConfigAccumulatorControl, &aiert->devInst, tileLoc,
857 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
858 static_cast<WireBundle>(configOp.getInputDir())),
859 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(
860 static_cast<WireBundle>(configOp.getOutputDir())));
861 }
862 }
863
864 return success();
865}
866
867LogicalResult
869 bool skipCtrlPktOverlay) {
870 AIEDialect::IsCtrlPktOverlayAttrHelper overlay(targetOp.getContext());
871 auto skip = [&](Operation *op) {
872 return skipCtrlPktOverlay && overlay.isAttrPresent(op);
873 };
874 for (auto switchboxOp : targetOp.getOps<SwitchboxOp>()) {
875 XAie_LocType tileLoc =
876 XAie_TileLoc(switchboxOp.colIndex(), switchboxOp.rowIndex());
877 Block &b = switchboxOp.getConnections().front();
878 for (auto connectOp : b.getOps<ConnectOp>()) {
879 if (skip(connectOp))
880 continue;
881 TxnLocBracket bracket(*this, connectOp.getLoc());
883 switchboxOp, XAie_StrmConnCctDisable, &aiert->devInst, tileLoc,
884 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getSourceBundle()),
885 connectOp.sourceIndex(),
886 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(connectOp.getDestBundle()),
887 connectOp.destIndex());
888 }
889 for (auto masterSetOp : b.getOps<MasterSetOp>()) {
890 if (skip(masterSetOp))
891 continue;
892 TxnLocBracket bracket(*this, masterSetOp.getLoc());
894 masterSetOp, XAie_StrmPktSwMstrPortDisable, &aiert->devInst, tileLoc,
895 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(masterSetOp.getDestBundle()),
896 masterSetOp.destIndex());
897 }
898 for (auto packetRulesOp : b.getOps<PacketRulesOp>()) {
899 auto rules = packetRulesOp.getRules().front().getOps<PacketRuleOp>();
900 if (rules.empty() || skip(packetRulesOp))
901 continue;
902 TxnLocBracket bracket(*this, packetRulesOp.getLoc());
903 StrmSwPortType port =
904 WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE.at(packetRulesOp.getSourceBundle());
905 if (llvm::none_of(rules, skip))
906 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlavePortDisable,
907 &aiert->devInst, tileLoc, port,
908 packetRulesOp.sourceIndex());
909 // A slot stays enabled when its port is disabled.
910 for (auto [slot, rule] : llvm::enumerate(rules))
911 if (!skip(rule))
912 TRY_XAIE_API_EMIT_ERROR(packetRulesOp, XAie_StrmPktSwSlaveSlotDisable,
913 &aiert->devInst, tileLoc, port,
914 packetRulesOp.sourceIndex(), slot);
915 }
916 }
917 return success();
918}
919
920LogicalResult xilinx::AIE::AIERTControl::disableCores(DeviceOp &targetOp) {
921 for (auto coreOp : targetOp.getOps<CoreOp>()) {
922 TileID tile = coreOp.getTileID();
923 TxnLocBracket bracket(*this, coreOp.getLoc());
924 TRY_XAIE_API_EMIT_ERROR(coreOp, XAie_CoreDisable, &aiert->devInst,
925 XAie_TileLoc(tile.col, tile.row));
926 }
927 return success();
928}
929
930LogicalResult xilinx::AIE::AIERTControl::resetDmaChannels(DeviceOp &targetOp) {
931 auto memOps = llvm::to_vector_of<TileElement>(targetOp.getOps<MemOp>());
932 llvm::append_range(memOps, targetOp.getOps<MemTileDMAOp>());
933 for (TileElement memOp : memOps) {
934 TileID tile = memOp.getTileID();
935 XAie_LocType tileLoc = XAie_TileLoc(tile.col, tile.row);
936 llvm::SmallSet<std::pair<DMAChannelDir, int>, 4> reset;
937 auto resetChannel = [&](Operation *op, DMAChannelDir dir,
938 int channel) -> LogicalResult {
939 if (!reset.insert({dir, channel}).second)
940 return success();
941 TxnLocBracket bracket(*this, op->getLoc());
942 XAie_DmaDirection direction =
943 dir == DMAChannelDir::S2MM ? DMA_S2MM : DMA_MM2S;
944 TRY_XAIE_API_EMIT_ERROR((*op), XAie_DmaChannelReset, &aiert->devInst,
945 tileLoc, channel, direction,
946 XAie_DmaChReset::DMA_CHANNEL_RESET);
947 // Writing a fresh descriptor ends the reset and returns every field of
948 // the control register, such as out-of-order mode, to its reset value.
949 XAie_DmaChannelDesc defaults;
950 TRY_XAIE_API_EMIT_ERROR((*op), XAie_DmaChannelDescInit, &aiert->devInst,
951 &defaults, tileLoc);
952 TRY_XAIE_API_EMIT_ERROR((*op), XAie_DmaWriteChannel, &aiert->devInst,
953 &defaults, tileLoc, channel, direction);
954 return success();
955 };
956 Region &region = memOp.getOperation()->getRegion(0);
957 for (auto dmaOp : region.getOps<DMAOp>())
958 if (failed(resetChannel(dmaOp, dmaOp.getChannelDir(),
959 dmaOp.getChannelIndex())))
960 return failure();
961 for (Block *block : getOrderedChainOfBlocks(&region))
962 for (auto startOp : block->getOps<DMAStartOp>())
963 if (failed(resetChannel(startOp, startOp.getChannelDir(),
964 startOp.getChannelIndex())))
965 return failure();
966 }
967 return success();
968}
969
970LogicalResult
972 bool skipCtrlPktOverlay) {
973
974 if (failed(verifyDMAChannelsResolved(targetOp)))
975 return failure();
976
977 if (failed(initLocks(targetOp))) {
978 return failure();
979 }
980
981 if (failed(initBuffers(targetOp))) {
982 return failure();
983 }
984
985 // A DMA whose lock is already satisfied streams as soon as it is enabled, so
986 // its route must exist first.
987 if (failed(configureSwitches(targetOp, skipCtrlPktOverlay))) {
988 return failure();
989 }
990
991 auto memOps = llvm::to_vector_of<TileElement>(targetOp.getOps<MemOp>());
992 llvm::append_range(memOps, targetOp.getOps<MemTileDMAOp>());
993 llvm::append_range(memOps, targetOp.getOps<ShimDMAOp>());
994 for (TileElement memOp : memOps) {
995 int col = memOp.getTileID().col;
996 int row = memOp.getTileID().row;
997
998 // Get the region's entry block, then start traversing through the chain of
999 // blocks.
1000 llvm::SetVector<Block *> blockVector =
1001 getOrderedChainOfBlocks(&memOp.getOperation()->getRegion(0));
1002
1003 llvm::SmallPtrSet<Block *, 8> unchainedBlocks =
1004 collectUnchainedBlocks(blockVector);
1005
1006 // handle DMA ops separately
1007 auto dmaOps = llvm::to_vector_of<DMAOp>(
1008 memOp.getOperation()->getRegion(0).getOps<DMAOp>());
1009 if (!dmaOps.empty()) {
1010 for (auto dmaOp : dmaOps)
1011 for (auto &bdRegion : dmaOp.getBds()) {
1012 Block &block = bdRegion.getBlocks().front();
1013 if (failed(
1014 configureLocksAndBd(block, col, row, dmaOp.getOutOfOrder())))
1015 return failure();
1016 }
1017 } else {
1018 for (Block *block : blockVector) {
1019 if (block->getOps<DMABDOp>().empty())
1020 continue;
1021 if (failed(configureLocksAndBd(*block, col, row,
1022 unchainedBlocks.contains(block))))
1023 return failure();
1024 }
1025 }
1026
1027 if (!dmaOps.empty())
1028 for (auto dmaOp : dmaOps) {
1029 auto &block = dmaOp.getBds().front().getBlocks().front();
1030 DMABDOp bd = *block.getOps<DMABDOp>().begin();
1031 auto bdId = bd.getBdId();
1032 assert(bdId.has_value() &&
1033 "bd_id assigned by configureLocksAndBd above");
1034 if (failed(pushToBdQueueAndEnable(
1035 *dmaOp.getOperation(), col, row, dmaOp.getChannelIndex(),
1036 dmaOp.getChannelDir(), bdId.value(), dmaOp.getRepeatCount(),
1037 dmaOp.getPadValue(), dmaOp.getOutOfOrder())))
1038 return failure();
1039 }
1040 else
1041 for (Block *block : blockVector) {
1042 for (auto op : block->getOps<DMAStartOp>()) {
1043 DMABDOp bd = *op.getDest()->getOps<DMABDOp>().begin();
1044 int chNum = op.getChannelIndex();
1045 auto channelDir = op.getChannelDir();
1046 auto bdId = bd.getBdId();
1047 assert(bdId.has_value() &&
1048 "bd_id assigned by configureLocksAndBd above");
1049 if (failed(pushToBdQueueAndEnable(
1050 *bd.getOperation(), col, row, chNum, channelDir, bdId.value(),
1051 op.getRepeatCount(), op.getPadValue(), op.getOutOfOrder(),
1052 /*configureChannel=*/true,
1053 /*pushTask=*/!op.getCorePushes())))
1054 return failure();
1055 }
1056 }
1057 }
1058
1059 return success();
1060}
1061
1062LogicalResult xilinx::AIE::AIERTControl::addCoreEnable(DeviceOp &targetOp) {
1063 // Start execution of all the cores.
1064 for (auto tileOp : targetOp.getOps<TileOp>()) {
1065 auto tileLoc = XAie_TileLoc(tileOp.colIndex(), tileOp.rowIndex());
1066 if (!tileOp.isShimTile() && tileOp.getCoreOp()) {
1067 CoreOp coreOp = tileOp.getCoreOp();
1068 TxnLocBracket bracket(*this, coreOp.getLoc());
1069 // A core reaching its own tile's registers goes through the processor
1070 // bus, which starts disabled: an access made before this stalls the
1071 // core. Only the cores that ask for it pay for it.
1072 if (coreOp.getProcessorBus())
1073 TRY_XAIE_API_EMIT_ERROR(targetOp, XAie_CoreProcessorBusEnable,
1074 &aiert->devInst, tileLoc);
1075 TRY_XAIE_API_EMIT_ERROR(targetOp, XAie_CoreEnable, &aiert->devInst,
1076 tileLoc);
1077 }
1078 }
1079 return success();
1080}
1081
1082LogicalResult xilinx::AIE::AIERTControl::addAieElf(uint8_t col, uint8_t row,
1083 const StringRef elfPath,
1084 bool aieSim) {
1085 TRY_XAIE_API_LOGICAL_RESULT(XAie_CoreDisable, &aiert->devInst,
1086 XAie_TileLoc(col, row));
1087 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaChannelResetAll, &aiert->devInst,
1088 XAie_TileLoc(col, row),
1089 XAie_DmaChReset::DMA_CHANNEL_RESET);
1090
1091 // loadSym: Load symbols from .map file. This argument is not used when
1092 // __AIESIM__ is not defined.
1093 TRY_XAIE_API_LOGICAL_RESULT(XAie_LoadElf, &aiert->devInst,
1094 XAie_TileLoc(col, row), elfPath.str().c_str(),
1095 /*loadSym*/ aieSim);
1096
1097 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaChannelResetAll, &aiert->devInst,
1098 XAie_TileLoc(col, row),
1099 XAie_DmaChReset::DMA_CHANNEL_UNRESET);
1100
1101 return success();
1102}
1103
1105 TRY_XAIE_API_LOGICAL_RESULT(XAie_ResetPartition, &aiert->devInst);
1106 return success();
1107}
1108
1109LogicalResult xilinx::AIE::AIERTControl::resetDMA(int col, int row, bool on) {
1110 auto tileLoc = XAie_TileLoc(col, row);
1111 XAie_DmaDesc dmaTileBd;
1112 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaDescInit, &aiert->devInst, &dmaTileBd,
1113 tileLoc);
1114 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaDisableBd, &dmaTileBd);
1115 TRY_XAIE_API_LOGICAL_RESULT(XAie_DmaChannelResetAll, &aiert->devInst, tileLoc,
1116 on ? XAie_DmaChReset::DMA_CHANNEL_UNRESET
1117 : XAie_DmaChReset::DMA_CHANNEL_RESET);
1118 return success();
1119}
1120
1121LogicalResult xilinx::AIE::AIERTControl::resetCore(int col, int row) {
1122 auto tileLoc = XAie_TileLoc(col, row);
1123 TRY_XAIE_API_LOGICAL_RESULT(XAie_CoreReset, &aiert->devInst, tileLoc);
1124 return success();
1125}
1126
1127LogicalResult xilinx::AIE::AIERTControl::resetSwitch(int col, int row) {
1128 XAie_LocType tileLoc = XAie_TileLoc(col, row);
1129
1130 // Reset all combinations of input/output routing in the switchbox
1131 for (auto endpoint_a : WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE) {
1132 for (auto endpoint_b : WIRE_BUNDLE_TO_STRM_SW_PORT_TYPE) {
1133 unsigned n_a_connections = targetModel.getNumSourceSwitchboxConnections(
1134 col, row, endpoint_a.first);
1135 unsigned n_b_connections = targetModel.getNumDestSwitchboxConnections(
1136 col, row, endpoint_b.first);
1137 for (unsigned a_index = 0; a_index < n_a_connections; a_index++) {
1138 for (unsigned b_index = 0; b_index < n_b_connections; b_index++) {
1139 if (!targetModel.isLegalTileConnection(col, row, endpoint_a.first,
1140 a_index, endpoint_b.first,
1141 b_index)) {
1142 continue;
1143 }
1144 TRY_XAIE_API_FATAL_ERROR(XAie_StrmConnCctDisable, &aiert->devInst,
1145 tileLoc, endpoint_a.second, a_index,
1146 endpoint_b.second, b_index);
1147 }
1148 }
1149 }
1150 }
1151
1152 return success();
1153}
1154
1155LogicalResult xilinx::AIE::AIERTControl::resetCoreUnreset(int col, int row) {
1156 auto tileLoc = XAie_TileLoc(col, row);
1157 TRY_XAIE_API_LOGICAL_RESULT(XAie_CoreUnreset, &aiert->devInst, tileLoc);
1158 return success();
1159}
1160
1161LogicalResult xilinx::AIE::AIERTControl::resetLock(int col, int row,
1162 int lockId) {
1163 auto tileLoc = XAie_TileLoc(col, row);
1164 // Reset a single lock to value 0
1165 XAie_Lock lock;
1166 lock.LockId = lockId;
1167 lock.LockVal = 0;
1168 TRY_XAIE_API_LOGICAL_RESULT(XAie_LockSetValue, &aiert->devInst, tileLoc,
1169 lock);
1170 return success();
1171}
1172
1174 int col, int row, WireBundle sourceBundle, int sourceChannel,
1175 WireBundle destBundle, int destChannel) {
1176 auto tileLoc = XAie_TileLoc(col, row);
1177
1178 // Helper lambda to map WireBundle to StrmSwPortType
1179 auto mapBundle = [](WireBundle bundle) -> StrmSwPortType {
1180 switch (bundle) {
1181 case WireBundle::Core:
1182 return CORE;
1183 case WireBundle::DMA:
1184 return DMA;
1185 case WireBundle::FIFO:
1186 return FIFO;
1187 case WireBundle::South:
1188 return SOUTH;
1189 case WireBundle::West:
1190 return WEST;
1191 case WireBundle::North:
1192 return NORTH;
1193 case WireBundle::East:
1194 return EAST;
1195 case WireBundle::Trace:
1196 return TRACE;
1197 default:
1198 return SOUTH;
1199 }
1200 };
1201
1202 StrmSwPortType sourcePortType = mapBundle(sourceBundle);
1203 StrmSwPortType destPortType = mapBundle(destBundle);
1204
1205 // Disconnect the specific connection from source to destination
1206 TRY_XAIE_API_LOGICAL_RESULT(XAie_StrmConnCctDisable, &aiert->devInst, tileLoc,
1207 sourcePortType, sourceChannel, destPortType,
1208 destChannel);
1209
1210 return success();
1211}
1212
1213LogicalResult xilinx::AIE::AIERTControl::resetPerfCounters(int col, int row) {
1214 auto tileLoc = XAie_TileLoc(col, row);
1215 // Reset performance counters in all modules
1216 // Try core module counters (if applicable)
1217 for (int counterId = 0; counterId < 4; counterId++) {
1218 // Ignore errors as not all tiles have all counter types
1219 (void)XAie_PerfCounterReset(&aiert->devInst, tileLoc, XAIE_CORE_MOD,
1220 counterId);
1221 }
1222 // Try mem module counters
1223 for (int counterId = 0; counterId < 4; counterId++) {
1224 (void)XAie_PerfCounterReset(&aiert->devInst, tileLoc, XAIE_MEM_MOD,
1225 counterId);
1226 }
1227 // Try PL module counters (for shim tiles)
1228 for (int counterId = 0; counterId < 4; counterId++) {
1229 (void)XAie_PerfCounterReset(&aiert->devInst, tileLoc, XAIE_PL_MOD,
1230 counterId);
1231 }
1232 return success();
1233}
1234
1235LogicalResult xilinx::AIE::AIERTControl::addAieElfs(DeviceOp &targetOp,
1236 const StringRef elfPath,
1237 bool aieSim) {
1238 for (auto tileOp : targetOp.getOps<TileOp>())
1239 if (tileOp.isShimNOCorPLTile()) {
1240 // Resets no needed with V2 kernel driver
1241 } else {
1242 int col = tileOp.colIndex();
1243 int row = tileOp.rowIndex();
1244 if (auto coreOp = tileOp.getCoreOp()) {
1245 std::string fileName;
1246 if (auto fileAttr = coreOp.getElfFile()) {
1247 fileName = fileAttr->str();
1248 } else {
1249 // Skip cores without elf_file (e.g., lightweight reset
1250 // devices that only need DMA/lock reconfiguration).
1251 continue;
1252 }
1253 // Check if fileName is already an absolute path.
1254 // If so, use it directly. Otherwise, concatenate with elfPath.
1255 std::string fullPath;
1256 if (std::filesystem::path(fileName).is_absolute()) {
1257 fullPath = fileName;
1258 } else {
1259 auto ps = std::filesystem::path::preferred_separator;
1260 fullPath =
1261 (llvm::Twine(elfPath) + std::string(1, ps) + fileName).str();
1262 }
1263 if (failed(addAieElf(col, row, fullPath, aieSim)))
1264 return failure();
1265 }
1266 }
1267 return success();
1268}
1269
1270void xilinx::AIE::AIERTControl::dmaUpdateBdAddr(int col, int row, size_t addr,
1271 size_t bdId) {
1272 auto tileLoc = XAie_TileLoc(col, row);
1273 TRY_XAIE_API_FATAL_ERROR(XAie_DmaUpdateBdAddr, &aiert->devInst, tileLoc, addr,
1274 bdId);
1275}
1276
1278 aiert->txnCmdLocs.clear();
1279 aiert->txnOpLocs.clear();
1280 TRY_XAIE_API_FATAL_ERROR(XAie_StartTransaction, &aiert->devInst,
1281 XAIE_TRANSACTION_DISABLE_AUTO_FLUSH);
1282}
1283
1284// Project the per-XAie_TxnCmd locations in `cmdLocs` onto the operations
1285// aie-rt's serializer actually emits, mirroring _XAie_TxnExportSerialized's
1286// command-to-operation mapping. That mapping is not 1:1: the serializer
1287// buffers block writes and collapses a run of address-contiguous
1288// XAIE_IO_BLOCKWRITE commands into a single block-write operation, holds that
1289// pending run across intervening DDR-patch commands (so a patch is emitted
1290// *before* the block write it follows in CmdBuf), and drops
1291// XAIE_IO_LOAD_PM_END_INTERNAL entirely. Everything else emits one operation
1292// in command order.
1293static std::vector<mlir::Location>
1294projectCmdLocsOntoSerializedOps(const XAie_TxnInst &inst,
1295 const std::vector<mlir::Location> &cmdLocs) {
1296 std::vector<mlir::Location> opLocs;
1297 opLocs.reserve(inst.NumCmds);
1298
1299 mlir::Location unknown = mlir::UnknownLoc::get(cmdLocs.front().getContext());
1300 bool pendingBlockWrite = false;
1301 mlir::Location pendingBlockWriteLoc = unknown;
1302 uint64_t blockWriteEndOff = 0;
1303
1304 for (uint32_t i = 0; i < inst.NumCmds; ++i) {
1305 const XAie_TxnCmd &cmd = inst.CmdBuf[i];
1306 mlir::Location loc = i < cmdLocs.size() ? cmdLocs[i] : unknown;
1307
1308 if (cmd.Opcode == XAIE_IO_BLOCKWRITE) {
1309 // A command that extends the run in flight emits nothing of its own;
1310 // the run keeps the location of the command that started it.
1311 if (!pendingBlockWrite || cmd.RegOff != blockWriteEndOff) {
1312 if (pendingBlockWrite)
1313 opLocs.push_back(pendingBlockWriteLoc);
1314 pendingBlockWrite = true;
1315 pendingBlockWriteLoc = loc;
1316 }
1317 blockWriteEndOff = cmd.RegOff + cmd.Size * sizeof(uint32_t);
1318 continue;
1319 }
1320
1321 if (cmd.Opcode == XAIE_IO_LOAD_PM_END_INTERNAL)
1322 continue;
1323
1324 if (pendingBlockWrite && cmd.Opcode != XAIE_IO_CUSTOM_OP_DDR_PATCH) {
1325 opLocs.push_back(pendingBlockWriteLoc);
1326 pendingBlockWrite = false;
1327 }
1328 opLocs.push_back(loc);
1329 }
1330 if (pendingBlockWrite)
1331 opLocs.push_back(pendingBlockWriteLoc);
1332
1333 return opLocs;
1334}
1335
1337 // Export the transactions to a binary buffer
1338 uint8_t *txn_ptr = XAie_ExportSerializedTransaction(&aiert->devInst, 0, 0);
1339 XAie_TxnHeader *hdr = (XAie_TxnHeader *)txn_ptr;
1340 std::vector<uint8_t> txn_data(txn_ptr, txn_ptr + hdr->TxnSize);
1341 free(txn_ptr);
1342
1343 // Exporting leaves the recorded commands intact, so the loc projection can
1344 // still walk CmdBuf here. With nothing bracketed there is no location to
1345 // project and every emitted op falls back to the device location.
1346 XAie_TxnInst *inst = getActiveTxnInst(aiert->devInst);
1347 if (inst && !aiert->txnCmdLocs.empty())
1348 aiert->txnOpLocs =
1349 projectCmdLocsOntoSerializedOps(*inst, aiert->txnCmdLocs);
1350
1351 return txn_data;
1352}
1353
1354const std::vector<mlir::Location> &
1356 return aiert->txnOpLocs;
1357}
1358
1360 return getActiveTxnNumCmds(aiert->devInst);
1361}
1362
1364 uint32_t endCmds,
1365 mlir::Location loc) {
1366 if (endCmds <= startCmds)
1367 return;
1368 if (aiert->txnCmdLocs.size() < endCmds)
1369 aiert->txnCmdLocs.resize(endCmds, mlir::UnknownLoc::get(loc.getContext()));
1370 for (uint32_t i = startCmds; i < endCmds; ++i)
1371 aiert->txnCmdLocs[i] = loc;
1372}
#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
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::SmallPtrSet< mlir::Block *, 8 > collectUnchainedBlocks(const llvm::SetVector< mlir::Block * > &blockVector)
Collect BD blocks that require use_next_bd=0 during configuration.
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:736
mlir::LogicalResult resetPerfCounters(int col, int row)
Definition AIERT.cpp:1213
mlir::LogicalResult configureLocksAndBd(mlir::Block &block, int col, int row, bool outOfOrder=false)
Definition AIERT.cpp:646
mlir::LogicalResult resetCore(int col, int row)
Definition AIERT.cpp:1121
void dmaUpdateBdAddr(int col, int row, size_t addr, size_t bdId)
Definition AIERT.cpp:1270
mlir::LogicalResult resetPartition()
Definition AIERT.cpp:1104
mlir::LogicalResult resetCoreUnreset(int col, int row)
Definition AIERT.cpp:1155
mlir::LogicalResult setIOBackend(bool aieSim, bool xaieDebug)
Definition AIERT.cpp:307
uint32_t getCurrentTxnNumCmds() const
Definition AIERT.cpp:1359
std::vector< uint8_t > exportSerializedTransaction()
Definition AIERT.cpp:1336
AIERTControl(const xilinx::AIE::AIETargetModel &tm)
Definition AIERT.cpp:250
mlir::LogicalResult initLocks(DeviceOp &targetOp)
Definition AIERT.cpp:676
mlir::LogicalResult resetDMA(int col, int row, bool on)
Definition AIERT.cpp:1109
mlir::LogicalResult disableCores(DeviceOp &targetOp)
Definition AIERT.cpp:920
mlir::LogicalResult initBuffers(DeviceOp &targetOp)
Definition AIERT.cpp:709
mlir::LogicalResult resetSwitch(int col, int row)
Definition AIERT.cpp:1127
mlir::LogicalResult addCoreEnable(DeviceOp &targetOp)
Definition AIERT.cpp:1062
mlir::LogicalResult resetSwitchConnection(int col, int row, WireBundle sourceBundle, int sourceChannel, WireBundle destBundle, int destChannel)
Definition AIERT.cpp:1173
const std::vector< mlir::Location > & getTxnOpLocs() const
Definition AIERT.cpp:1355
mlir::LogicalResult addInitConfig(DeviceOp &targetOp, bool skipCtrlPktOverlay=false)
Definition AIERT.cpp:971
mlir::LogicalResult addAieElf(uint8_t col, uint8_t row, const mlir::StringRef elfPath, bool aieSim)
Definition AIERT.cpp:1082
void recordTxnLocRange(uint32_t startCmds, uint32_t endCmds, mlir::Location loc)
Definition AIERT.cpp:1363
mlir::LogicalResult resetDmaChannels(DeviceOp &targetOp)
Definition AIERT.cpp:930
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, bool configureChannel=true, bool pushTask=true)
Definition AIERT.cpp:598
mlir::LogicalResult addAieElfs(DeviceOp &targetOp, const mlir::StringRef elfPath, bool aieSim)
Definition AIERT.cpp:1235
mlir::LogicalResult resetLock(int col, int row, int lockId)
Definition AIERT.cpp:1161
mlir::LogicalResult teardownSwitches(DeviceOp &targetOp, bool skipCtrlPktOverlay=false)
Definition AIERT.cpp:868