MLIR-AIE 1.4.5.dev
AIETargetXAIEV2.cpp
Go to the documentation of this file.
1//===- AIETargetXAIEV2.cpp --------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2021-2022 Xilinx, Inc.
4// Copyright (C) 2021-2023 Advanced Micro Devices, Inc.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
9
13
14#include "mlir/Dialect/Utils/StaticValueUtils.h"
15#include "mlir/IR/Attributes.h"
16#include "mlir/IR/IRMapping.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
19
20#include "llvm/ADT/StringExtras.h"
21#include "llvm/IR/Module.h"
22
23#include <limits>
24
25using namespace mlir;
26using namespace xilinx;
27using namespace xilinx::AIE;
28using namespace xilinx::AIEX;
29
30// This string is output at the top of the lowered C++ code.
31static const char *xaie_cpp_file_header = R"code(
32// This file was auto-generated by aiecc --aie-generate-xaie.
33
34#ifndef MLIR_AIE_QUIET
35#define __mlir_aie_verbose(x) x
36#else
37#define __mlir_aie_verbose(x)
38#endif
39
40// The following is a wrapper for the common "if(call() != 0) return 1" pattern.
41// Use this only in functions that return int. If the call this wrapper is used
42// on does not succeed, the expanded code will exit out of the function
43// containing this macro with an error code.
44#define __mlir_aie_try(x) do { \
45 AieRC ret = (x); \
46 if(ret != XAIE_OK) { \
47 return x; \
48 } \
49} while(0)
50
51static XAie_DmaDimDesc *__mlir_aie_alloc_dim_desc(size_t ndims) {
52 XAie_DmaDimDesc *ret = NULL;
53 ret = (XAie_DmaDimDesc *)calloc(sizeof(XAie_DmaDimDesc), ndims);
54 if(NULL == ret) {
55 __mlir_aie_verbose(fprintf(stderr, "Allocating DmaDimDesc failed.\n"));
56 }
57 return ret;
58}
59
60)code";
61
62static std::string tileLockStr(StringRef id, StringRef val) {
63 std::string str;
64 llvm::raw_string_ostream rss(str);
65 // rss << "XAie_Lock(" << id << "," << val << ")";
66 rss << "XAie_LockInit(" << id << "," << val << ")";
67 return str;
68}
69
70// Translate mlir-aie WireBundle enum to string of aie-rt StrmSwPortType enum
71static std::string wireBundleToPortType(WireBundle bundle) {
72 switch (bundle) {
73 case WireBundle::PLIO:
74 return "PL";
75 case WireBundle::TileControl:
76 return "CTRL";
77 default:
78 return stringifyWireBundle(bundle).upper();
79 }
80}
81
82// FIXME: this is a template pending a common DMA-like interface across the op
83// types.
84// blockMap: A map that gives a unique bd ID assignment for every block.
85template <typename OpType>
86static mlir::LogicalResult generateDMAConfig(OpType memOp, raw_ostream &output,
87 const AIETargetModel &targetModel,
88 DenseMap<Block *, int> blockMap) {
89 StringRef enable = "XAIE_ENABLE";
90 StringRef disable = "XAIE_DISABLE";
91 StringRef deviceInstRef = "ctx->XAieDevInst";
92
93 int col = memOp.colIndex();
94 int row = memOp.rowIndex();
95
96 // Get the region's entry block, then start traversing through the chain of
97 // blocks.
98 llvm::SetVector<Block *> blockVector =
99 getOrderedChainOfBlocks(&memOp.getBody());
100
101 llvm::SmallPtrSet<Block *, 8> oooBlocks =
102 collectOutOfOrderBlocks(blockVector);
103
104 for (auto *block : blockVector) {
105 bool foundBdPacket = false;
106 int packetType = 0;
107 int packetID = 0;
108 bool foundBd = false;
109 uint64_t lenA = 0;
110 int64_t offsetA = 0;
111 int BaseAddrA = 0;
112 int elementWidthInBytes = 0;
113 int ndims = 0;
114 int iterSize = 0, iterStride = 0, iterCurr = 0;
115 uint32_t axcache = 0;
116 std::optional<int> oooId;
117 ArrayRef<BDDimLayoutAttr> dims;
118 // Owning storage for the folded dims; must outlive `dims` (used far below).
119 SmallVector<BDDimLayoutAttr> dimsStorage;
120 // StringRef FifoMode = disable; // FIXME: when to enable FIFO mode?
121 // A legacy dma_bd_packet op is read first so that a packet attribute on
122 // the dma_bd overrides it, matching AIERT.
123 for (auto op : block->getOps<DMABDPACKETOp>()) {
124 foundBdPacket = true;
125 packetType = op.getPacketType();
126 packetID = op.getPacketID();
127 }
128 for (auto op : block->getOps<DMABDOp>()) {
129 foundBd = true;
130 if (auto packetInfo = op.getPacket()) {
131 foundBdPacket = true;
132 packetType = packetInfo->getPktType();
133 packetID = packetInfo->assignedId();
134 }
135 if (!targetModel.isShimNOCTile(col, row)) {
136 std::optional<int32_t> bufferAddr = op.getBufferOp().getAddress();
137 assert(bufferAddr && "buffer must have address assigned");
138 BaseAddrA = *bufferAddr;
139 int bufferCol = op.getBufferOp().getTileOp().colIndex();
140 int bufferRow = op.getBufferOp().getTileOp().rowIndex();
141
142 // Memtile DMAs can access neighboring tiles.
143 if (targetModel.isMemTile(col, row)) {
144 auto addrOffset = targetModel.getMemLocalBaseAddress(
145 col, row, bufferCol, bufferRow);
146 if (addrOffset)
147 BaseAddrA += addrOffset.value();
148 }
149 }
150
151 lenA = op.getLenInBytes();
152 offsetA = op.getOffsetInBytes();
153 elementWidthInBytes = op.getBufferElementTypeWidthInBytes();
154 if (auto iter = op.getIteration()) {
155 iterSize = iter->getSize();
156 iterStride = iter->getStride();
157 iterCurr = iter->getCurrent();
158 }
159 axcache = op.getAxcacheOrDefault();
160 oooId = op.getOutOfOrderId();
161 if (!op.getMixedSizes().empty()) {
162 // Runtime-valued sizes/strides are not supported on this static path.
163 for (mlir::OpFoldResult s : op.getMixedSizes())
164 if (!mlir::getConstantIntValue(s))
165 return op->emitOpError(
166 "runtime-valued BD size/stride is not supported on the static "
167 "XAIE lowering path; use compile-time constant sizes");
168 for (mlir::OpFoldResult s : op.getMixedStrides())
169 if (!mlir::getConstantIntValue(s))
170 return op->emitOpError(
171 "runtime-valued BD size/stride is not supported on the static "
172 "XAIE lowering path; use compile-time constant sizes");
173 std::optional<SmallVector<BDDimLayoutAttr>> folded =
174 op.getConstantDimensions();
175 if (!folded)
176 return op->emitOpError("internal error folding BD dimensions");
177 dimsStorage = std::move(*folded);
178 dims = dimsStorage;
179 ndims = dims.size();
180 }
181 }
182
183 if (0 != ndims)
185 return memOp.emitOpError("DMA contains at least one multi-dimensional "
186 "buffer descriptor. This is currently only "
187 "supported for AIE-ML and later devices.");
188 // Ensure BD length in bytes fits in the u32 `Len` argument of the libxaie
189 // DMA APIs (XAie_DmaSetAddrLen / XAie_DmaSetMultiDimAddr).
190 if (foundBd && lenA > std::numeric_limits<uint32_t>::max()) {
191 return memOp.emitOpError("buffer descriptor length in bytes (")
192 << lenA << ") does not fit in 32 bits";
193 }
194
195 int acqValue = 0, relValue = 0;
196 bool hasAcq = false, hasRel = false;
197 int acqLockID = 0, relLockID = 0;
198 for (auto op : block->getOps<UseLockOp>()) {
199 LockOp lock = cast<LockOp>(op.getLock().getDefiningOp());
200 int lockCol = lock.colIndex();
201 int lockRow = lock.rowIndex();
202 int lockID = lock.getLockIDValue();
203 // Memtile DMAs can access neighboring tiles.
204 if (targetModel.isMemTile(col, row)) {
205 auto lockOffset =
206 targetModel.getLockLocalBaseIndex(col, row, lockCol, lockRow);
207 if (lockOffset)
208 lockID += lockOffset.value();
209 }
210
211 if (op.acquire() || op.acquireGE()) {
212 hasAcq = true;
213 acqLockID = lockID;
214 auto value = op.getConstantValue();
215 if (failed(value))
216 return failure();
217 acqValue = *value; // NOLINT(bugprone-unchecked-optional-access)
218 if (op.acquireGE())
219 acqValue = -acqValue;
220 } else if (op.release()) {
221 hasRel = true;
222 relLockID = lockID;
223 auto value = op.getConstantValue();
224 if (failed(value))
225 return failure();
226 relValue = *value; // NOLINT(bugprone-unchecked-optional-access)
227 } else {
228 // unreachable for current targets
229 return op.emitOpError("unsupported lock action");
230 }
231 }
232
233 int bdNum = blockMap[block];
234 if (foundBd) {
235 // TODO For now, we are going to name each dma desc with loc and bd
236 // which we assume is unique. This is strictly not enforced but in
237 // practice, this is true
238 output << "XAie_DmaDesc " << tileDMAInstStr(col, row, bdNum) << ";\n";
239 output << "__mlir_aie_try(XAie_DmaDescInit(" << deviceInstRef << ", "
240 << tileDMAInstRefStr(col, row, bdNum) << ", "
241 << tileLocStr(col, row) << "));\n";
242 if (hasAcq || hasRel) {
243 output << "__mlir_aie_try(XAie_DmaSetLock("
244 << tileDMAInstRefStr(col, row, bdNum) << ", "
245 << "XAie_LockInit(" << acqLockID << "," << acqValue << "),"
246 << "XAie_LockInit(" << relLockID << "," << relValue << ")));\n";
247 if (!hasAcq)
248 output << tileDMAInstStr(col, row, bdNum)
249 << ".LockDesc.LockAcqEn = " << disable << ";\n";
250 if (!hasRel)
251 output << tileDMAInstStr(col, row, bdNum)
252 << ".LockDesc.LockRelEn = " << disable << ";\n";
253 }
254
255 if (0 == ndims) {
256 if (targetModel.isShimNOCTile(col, row)) {
257 output << "__mlir_aie_try(XAie_DmaSetAddrLen("
258 << tileDMAInstRefStr(col, row, bdNum) << ", /* addrA */ "
259 << "mlir_aie_external_get_addr_myBuffer_" << col << row << "_"
260 << bdNum << "(), "
261 << " /* len */ " << lenA << "));\n";
262 output << "__mlir_aie_try(XAie_DmaSetAxi("
263 << tileDMAInstRefStr(col, row, bdNum) << ", "
264 << "/* smid */ 0, "
265 << "/* burstlen */ 4, "
266 << "/* QoS */ 0, "
267 << "/* Cache */ " << axcache << ", "
268 << "/* Secure */ " << enable << "));\n";
269 } else {
270 if ((BaseAddrA + offsetA) % 4)
271 return memOp.emitError("bd address must be 4B (32b) aligned");
272 output << "__mlir_aie_try(XAie_DmaSetAddrLen("
273 << tileDMAInstRefStr(col, row, bdNum) << ", /* addrA */ "
274 << "0x" << llvm::utohexstr(BaseAddrA + offsetA) << ", "
275 << " /* len */ " << lenA << "));\n";
276 }
277 } else
278 generateXAieDmaSetMultiDimAddr(output, ndims, dims, col, row, bdNum,
279 BaseAddrA, offsetA, lenA,
280 elementWidthInBytes, "1");
281
282 if (iterSize > 1 && iterStride > 0) {
283 int64_t strideInBytes =
284 static_cast<int64_t>(iterStride) * elementWidthInBytes;
285 if (strideInBytes % 4)
286 return memOp.emitError("iteration_stride must result in a stride "
287 "aligned to 32-bit words");
288 int stepInWords = static_cast<int>(strideInBytes / 4);
289 output << "__mlir_aie_try(XAie_DmaSetBdIteration("
290 << tileDMAInstRefStr(col, row, bdNum) << ", " << stepInWords
291 << ", " << iterSize << ", " << iterCurr << "));\n";
292 }
293
294 if (block->getNumSuccessors() > 0) {
295 Block *nextBlock = block->getSuccessors()[0]; // should have only one
296 // successor block
297
298 int enableNextBd = 1;
299 if (!nextBlock->getOps<EndOp>().empty())
300 enableNextBd = 0;
301 if (oooBlocks.contains(block))
302 enableNextBd = 0;
303
304 int nextBdNum = blockMap[nextBlock];
305 output << "__mlir_aie_try(XAie_DmaSetNextBd("
306 << tileDMAInstRefStr(col, row, bdNum) << ", "
307 << " /* nextbd */ " << nextBdNum << ", "
308 << " /* enableNextBd */ " << enableNextBd << "));\n";
309 }
310
311 if (foundBdPacket) {
312 output << "__mlir_aie_try(XAie_DmaSetPkt("
313 << tileDMAInstRefStr(col, row, bdNum) << ", "
314 << packetStr(packetID, packetType) << "));\n";
315 }
316 if (oooId.has_value()) {
317 output << "__mlir_aie_try(XAie_DmaSetOutofOrderBdId("
318 << tileDMAInstRefStr(col, row, bdNum) << ", " << *oooId
319 << "));\n";
320 }
321 output << "__mlir_aie_try(XAie_DmaEnableBd("
322 << tileDMAInstRefStr(col, row, bdNum) << "));\n";
323 output << "__mlir_aie_try(XAie_DmaWriteBd(" << deviceInstRef << ", "
324 << tileDMAInstRefStr(col, row, bdNum) << ", "
325 << tileLocStr(col, row) << ", "
326 << " /* bd */ " << bdNum << "));\n";
327 }
328 }
329
330 for (auto *block : blockVector) {
331 for (auto op : block->getOps<DMAStartOp>()) {
332 int bdNum = blockMap[op.getDest()];
333 StringRef dmaDir = stringifyDMAChannelDir(op.getChannelDir());
334 int chNum = op.getChannelIndex();
335 const auto &target_model = xilinx::AIE::getTargetModel(op);
336 if (target_model.getTargetArch() == AIEArch::AIE1) {
337 output << "__mlir_aie_try(XAie_DmaChannelPushBdToQueue("
338 << deviceInstRef << ", " << tileLocStr(col, row) << ", "
339 << "/* ChNum */" << chNum
340 << ", "
341 // TODO: interim handling until the physical dialect changes.
342 << "/* dmaDir */ DMA_" << dmaDir << ", "
343 << "/* BdNum */" << bdNum << "));\n";
344 } else if (op.getOutOfOrder()) {
345 // in english repeat_count==0 means "do it once" and don't repeat but
346 // libxaie treats repeat_count=1 as do it once.
347 int repeatCount = op.getRepeatCount() + 1;
348 // StartBd is ignored and the in-order validity check is skipped.
349 std::string oooSuffix = std::to_string(col) + "_" +
350 std::to_string(row) + "_" +
351 std::to_string(chNum);
352 output << "XAie_DmaChannelDesc ooo_desc_" << oooSuffix << ";\n";
353 output << "__mlir_aie_try(XAie_DmaChannelDescInit(" << deviceInstRef
354 << ", &ooo_desc_" << oooSuffix << ", " << tileLocStr(col, row)
355 << "));\n";
356 output << "__mlir_aie_try(XAie_DmaChannelEnOutofOrder(&ooo_desc_"
357 << oooSuffix << ", " << enable << "));\n";
358 output << "__mlir_aie_try(XAie_DmaWriteChannel(" << deviceInstRef
359 << ", &ooo_desc_" << oooSuffix << ", " << tileLocStr(col, row)
360 << ", /* ChNum */" << chNum << ", /* dmaDir */ DMA_" << dmaDir
361 << "));\n";
362 output << "XAie_DmaDeclareQueueConfig(ooo_queue_" << oooSuffix
363 << ", /* StartBd */ 0, /* Repeat */ " << repeatCount
364 << ", /* EnToken */ " << disable << ", /* EnOutOfOrder */ "
365 << enable << ");\n";
366 output << "__mlir_aie_try(XAie_DmaChannelSetStartQueueGeneric("
367 << deviceInstRef << ", " << tileLocStr(col, row)
368 << ", /* ChNum */" << chNum << ", /* dmaDir */ DMA_" << dmaDir
369 << ", &ooo_queue_" << oooSuffix << "));\n";
370 } else {
371 // in english repeat_count==0 means "do it once" and don't repeat but
372 // libxaie treats repeat_count=1 as do it once.
373 int repeatCount = op.getRepeatCount() + 1;
374 output << "__mlir_aie_try(XAie_DmaChannelSetStartQueue("
375 << deviceInstRef << ", " << tileLocStr(col, row) << ", "
376 << "/* ChNum */" << chNum
377 << ", "
378 // TODO: interim handling until the physical dialect changes.
379 << "/* dmaDir */ DMA_" << dmaDir << ", "
380 << "/* BdNum */" << bdNum << ", "
381 << "/* Repeat */ " << repeatCount << ", "
382 << "/* EnToken */ "
383 << "XAIE_DISABLE"
384 << "));\n";
385 }
386 output << "__mlir_aie_try(XAie_DmaChannelEnable(" << deviceInstRef << ", "
387 << tileLocStr(col, row) << ", "
388 << "/* ChNum */ " << chNum
389 << ", "
390 // TODO: interim handling until the physical dialect changes.
391 << "/* dmaDir */ DMA_" << dmaDir << "));\n";
392 }
393 }
394 return success();
395}
396
397mlir::LogicalResult
398xilinx::AIE::AIETranslateToXAIEV2(ModuleOp module, raw_ostream &output,
399 llvm::StringRef deviceName) {
400 StringRef ctx_p = "aie_libxaie_ctx_t* ctx";
401 StringRef deviceInstRef = "ctx->XAieDevInst";
402
403 DenseMap<TileID, Operation *> tiles;
404 DenseMap<Operation *, SmallVector<BufferOp, 4>> buffers;
405
406 DeviceOp targetOp = AIE::DeviceOp::getForSymbolInModule(module, deviceName);
407 if (!targetOp)
408 return module.emitOpError("expected AIE.device operation at toplevel");
409 if (failed(verifyDMAChannelsResolved(targetOp)))
410 return failure();
411 const auto &targetModel = targetOp.getTargetModel();
412
413 collectTiles(targetOp, tiles);
414 collectBuffers(targetOp, buffers);
415
416 //---------------------------------------------------------------------------
417 // mlir_aie_init_libxaie
418 //---------------------------------------------------------------------------
419 output << xaie_cpp_file_header;
420 output << "aie_libxaie_ctx_t* mlir_aie_init_libxaie() {\n";
421 output << " aie_libxaie_ctx_t *ctx = new aie_libxaie_ctx_t;\n";
422 output << " if (!ctx)\n";
423 output << " return 0;\n";
424 output << " ctx->XAieConfig = new XAie_Config;\n";
425 output << " if (!ctx->XAieConfig)\n";
426 output << " return 0;\n";
427 output << " ctx->XAieDevInst = new XAie_DevInst;\n";
428 output << " if (!ctx->XAieDevInst)\n";
429 output << " return 0;\n";
430 auto arch = targetModel.getTargetArch();
431 std::string AIE1_device("XAIE_DEV_GEN_AIE");
432 std::string AIE2_device("XAIE_DEV_GEN_AIEML");
433 std::string AIE2p_device("XAIE_DEV_GEN_AIE2P");
434 std::string device;
435 switch (arch) {
436 case AIEArch::AIE1:
437 device = AIE1_device;
438 break;
439 case AIEArch::AIE2:
440 device = AIE2_device;
441 break;
442 case AIEArch::AIE2p:
443 device = AIE2p_device;
444 break;
445 case AIEArch::AIE2ps:
446 // AIE2PS configures the array through a CERT ELF instead of this libxaie
447 // source (see AIE2PSTargetModel). Listed here so that a new AIEArch still
448 // raises -Wswitch.
449 return targetOp.emitOpError(
450 "--aie-generate-xaie is not supported for the AIE2PS architecture");
451 }
452 output << " ctx->XAieConfig->AieGen = " << device << ";\n";
453 output << " ctx->XAieConfig->BaseAddr = 0x20000000000;\n";
454 output << " ctx->XAieConfig->ColShift = " << targetModel.getColumnShift()
455 << ";\n";
456 output << " ctx->XAieConfig->RowShift = " << targetModel.getRowShift()
457 << ";\n";
458 output << " ctx->XAieConfig->NumRows = " << targetModel.rows() << ";\n";
459 output << " ctx->XAieConfig->NumCols = " << targetModel.columns() << ";\n";
460 output << " ctx->XAieConfig->ShimRowNum = 0;\n";
461 output << " ctx->XAieConfig->MemTileRowStart = 1;\n";
462 output << " ctx->XAieConfig->MemTileNumRows = "
463 << targetModel.getNumMemTileRows() << ";\n";
464 output << " // ctx->XAieConfig->ReservedRowStart = "
465 "XAIE_RES_TILE_ROW_START;\n";
466 output
467 << " // ctx->XAieConfig->ReservedNumRows = XAIE_RES_TILE_NUM_ROWS;\n";
468 output << " ctx->XAieConfig->AieTileRowStart = "
469 << (1 + targetModel.getNumMemTileRows()) << ";\n";
470 output << " ctx->XAieConfig->AieTileNumRows = "
471 << (targetModel.rows() - 1 - targetModel.getNumMemTileRows()) << ";\n";
472 output << " memset(ctx->XAieDevInst, 0, sizeof(XAie_DevInst));\n";
473 output << " return ctx;\n";
474 output << "}\n";
475 output << "\n";
476
477 //---------------------------------------------------------------------------
478 // mlir_aie_configure_cores
479 //---------------------------------------------------------------------------
480 // Defined below. configure_cores calls it at its end, once every ELF is
481 // loaded, so a PT_LOAD cannot land on top of an initialized buffer.
482 output << "int mlir_aie_initialize_buffers(" << ctx_p << ");\n";
483 output << "int mlir_aie_configure_cores(" << ctx_p << ") {\n";
484 // Reset each core. Load the corresponding ELF file, if necessary.
485 for (auto tileOp : targetOp.getOps<TileOp>()) {
486 int col = tileOp.colIndex();
487 int row = tileOp.rowIndex();
488 if (tileOp.isShimTile() || tileOp.isMemTile()) {
489 // Resets no needed with V2 kernel driver
490 } else {
491 // Resets no needed with V2 kernel driver
492 output << "__mlir_aie_try(XAie_CoreReset(" << deviceInstRef << ", "
493 << tileLocStr(col, row) << "));\n";
494 output << "__mlir_aie_try(XAie_CoreDisable(" << deviceInstRef << ", "
495 << tileLocStr(col, row) << "));\n";
496 // Release locks
497 int numLocks = targetModel.getNumLocks(col, row);
498 output << "for (int l = 0; l < " << numLocks << "; ++l)\n"
499 << " __mlir_aie_try(XAie_LockRelease(" << deviceInstRef << ", "
500 << tileLocStr(col, row) << ", XAie_LockInit(l, 0x0), 0));\n";
501 if (auto coreOp = tileOp.getCoreOp()) {
502 if (coreOp.isEmpty() && coreOp.getElfFile() == std::nullopt) {
503 // This core has no MLIR code and references no ELF file -- it is
504 // completely empty, so don't generate any code for it.
505 continue;
506 }
507 std::string fileName;
508 if (auto fileAttr = coreOp.getElfFile()) {
509 fileName = fileAttr.value().str();
510 } else {
511 return coreOp.emitOpError()
512 << "Expected lowered ELF file to be given as attribute "
513 "`elf_file` for this core. Compile cores first.";
514 }
515 output << "{\n"
516 << "AieRC RC = XAie_LoadElf(" << deviceInstRef << ", "
517 << tileLocStr(col, row) << ", "
518 << "(const char*)\"" << fileName << "\",0);\n";
519 output << "if (RC != XAIE_OK)\n"
520 << " __mlir_aie_verbose(fprintf(stderr, \"Failed to load elf "
521 "for Core[%d,%d], ret is %d\\n\", "
522 << std::to_string(col) << ", " << std::to_string(row)
523 << ", RC));\n"
524 << "assert(RC == XAIE_OK);\n"
525 << "}\n";
526 }
527 }
528 }
529 // __mlir_aie_try assigns to an AieRC, and the generated entry points return
530 // int, which C++ does not convert implicitly.
531 output << "{\n";
532 output << " int rc = mlir_aie_initialize_buffers(ctx);\n";
533 output << " if (rc != XAIE_OK)\n";
534 output << " return rc;\n";
535 output << "}\n";
536 output << "return XAIE_OK;\n";
537 output << "} // mlir_aie_configure_cores\n\n";
538
539 //---------------------------------------------------------------------------
540 // mlir_aie_start_cores
541 //---------------------------------------------------------------------------
542 output << "int mlir_aie_start_cores(" << ctx_p << ") {\n";
543 // Start execution of all the cores.
544 for (auto tileOp : targetOp.getOps<TileOp>()) {
545 int col = tileOp.colIndex();
546 int row = tileOp.rowIndex();
547 if (!tileOp.isShimTile() && !tileOp.isMemTile()) {
548 output << "__mlir_aie_try(XAie_CoreUnreset(" << deviceInstRef << ", "
549 << tileLocStr(col, row) << "));\n";
550 output << "__mlir_aie_try(XAie_CoreEnable(" << deviceInstRef << ", "
551 << tileLocStr(col, row) << "));\n";
552 }
553 }
554 output << "return XAIE_OK;\n";
555 output << "} // mlir_aie_start_cores\n\n";
556
557 //---------------------------------------------------------------------------
558 // mlir_aie_configure_dmas
559 //---------------------------------------------------------------------------
560 output << "int mlir_aie_configure_dmas(" << ctx_p << ") {\n";
561
562 for (auto memOp : targetOp.getOps<MemOp>()) {
563 DenseMap<Block *, int> blockMap;
564
565 // Use bd_ids, if specified.
566 int bdNum = 0;
567 for (auto &block : memOp.getBody()) {
568 auto bdOps = block.getOps<DMABDOp>();
569 if (bdOps.empty())
570 continue;
571 std::optional<int32_t> id = (*bdOps.begin()).getBdId();
572 blockMap[&block] = id.has_value() ? *id : bdNum;
573 bdNum++;
574 }
575 auto result = generateDMAConfig(memOp, output, targetModel, blockMap);
576 if (result.failed())
577 return result;
578 }
579 for (auto memOp : targetOp.getOps<MemTileDMAOp>()) {
580 DenseMap<Block *, int> blockMap;
581 // Memtiles have restrictions on which channels can access which BDs
582 DenseMap<Block *, int> channelMap;
583
584 for (auto &block : memOp.getBody()) {
585 for (auto op : block.getOps<DMAStartOp>()) {
586 int chNum = op.getChannelIndex();
587 channelMap[&block] = chNum;
588 auto *dest = op.getDest();
589 while (dest) {
590 channelMap[dest] = chNum;
591 if (dest->getSuccessors().empty())
592 break;
593 dest = dest->getSuccessors()[0];
594 if (channelMap.count(dest))
595 break;
596 }
597 }
598 }
599
600 // Assign each block a BD number, if unassigned
601 int evenBdNum = 0;
602 int oddBdNum = 24;
603 for (auto &block : memOp.getBody()) {
604 auto bdOps = block.getOps<DMABDOp>();
605 if (bdOps.empty())
606 continue;
607 assert(channelMap.count(&block));
608 if (std::optional<int32_t> id = (*bdOps.begin()).getBdId())
609 blockMap[&block] = *id;
610 else if (channelMap[&block] & 1)
611 blockMap[&block] = oddBdNum++;
612 else
613 blockMap[&block] = evenBdNum++;
614 }
615 auto result = generateDMAConfig(memOp, output, targetModel, blockMap);
616 if (result.failed())
617 return result;
618 }
619
620 output << "return XAIE_OK;\n";
621 output << "} // mlir_aie_configure_dmas\n\n";
622
623 for (auto op : targetOp.getOps<ExternalBufferOp>()) {
624 if (op.hasName()) {
625 output << "static u64 _mlir_aie_external_" << op.name().getValue()
626 << ";\n";
627 output << "static bool _mlir_aie_external_set_" << op.name().getValue()
628 << " = false;\n";
629
630 output << "void mlir_aie_external_set_addr_" << op.name().getValue()
631 << "(" << ctx_p << ", u64 VA) {\n"
632 << " u64 device_address = mlir_aie_get_device_address(ctx, (void "
633 "*)VA);\n"
634 << " _mlir_aie_external_set_" << op.name().getValue()
635 << " = true;\n"
636 << " _mlir_aie_external_" << op.name().getValue()
637 << " = device_address;\n"
638 << "}\n";
639 }
640 }
641
642 // ShimDMA Config
643 // int index = 0;
644 for (auto op : targetOp.getOps<ShimDMAOp>()) {
645 int col = op.colIndex();
646 int row = op.rowIndex();
647
648 DenseMap<Block *, int> blockMap;
649 {
650 // Assign each block a BD number, if unassigned
651 int bdNum = 0;
652 for (auto &block : op.getBody()) {
653 auto bdOps = block.getOps<DMABDOp>();
654 if (bdOps.empty())
655 continue;
656 std::optional<int32_t> id = (*bdOps.begin()).getBdId();
657 int slot = id.has_value() ? *id : bdNum;
658 blockMap[&block] = slot;
659 uint64_t offset = 0;
660 for (auto op : block.getOps<DMABDOp>()) {
661 offset = op.getOffsetInBytes();
662 auto buffer = cast<ExternalBufferOp>(op.getBuffer().getDefiningOp());
663
664 output << "u64 mlir_aie_external_get_addr_myBuffer_" << col << row
665 << "_" << slot << "(void) {\n"
666 << " assert(_mlir_aie_external_set_"
667 << buffer.name().getValue() << ");\n"
668 << " return _mlir_aie_external_" << buffer.name().getValue()
669 << " + " << llvm::utohexstr(offset) << ";\n"
670 << "}\n";
671 }
672
673 bdNum++;
674 }
675 }
676
677 output << "int mlir_aie_configure_shimdma_" << col << row << "(" << ctx_p
678 << ") {\n";
679 auto result = generateDMAConfig(op, output, targetModel, blockMap);
680 if (result.failed())
681 return result;
682 output << "return XAIE_OK;\n";
683 output << "} // mlir_aie_configure_shimdma\n\n";
684 }
685
686 //---------------------------------------------------------------------------
687 // mlir_aie_initialize_locks
688 //---------------------------------------------------------------------------
689 output << "int mlir_aie_initialize_locks(" << ctx_p << ") {\n";
690 // Lock configuration
691 targetOp.walk<WalkOrder::PreOrder>([&](LockOp lock) {
692 TileOp tile = lock.getTileOp();
693 int col = tile.colIndex();
694 int row = tile.rowIndex();
695 int lockID = lock.getLockIDValue();
696 auto init = lock.getInit();
697 if (init)
698 output << "__mlir_aie_try(XAie_LockSetValue(" << deviceInstRef << ", "
699 << tileLocStr(col, row) << ", "
700 << "XAie_LockInit(" << lockID << ", " << *init << ")));\n";
701 });
702 output << "return XAIE_OK;\n";
703 output << "} // mlir_aie_initialize_locks\n";
704
705 //---------------------------------------------------------------------------
706 // mlir_aie_initialize_buffers
707 //---------------------------------------------------------------------------
708 // A buffer's initial_value is device state, written by whoever configures
709 // the device: the CDO writer on the NPU path, this function on the simulator
710 // path. This walks every tile, because a memtile buffer has no core and so no
711 // other route. mlir_aie_configure_cores calls this, so a host that already
712 // configures its cores needs no change.
713 output << "int mlir_aie_initialize_buffers(" << ctx_p << ") {\n";
714 auto initBuffersResult = targetOp.walk<WalkOrder::PreOrder>([&](BufferOp
715 buffer) {
716 auto initialValue = buffer.getInitialValue();
717 if (!initialValue)
718 return WalkResult::advance();
719 auto denseInit = llvm::dyn_cast<DenseElementsAttr>(*initialValue);
720 if (!denseInit)
721 return WalkResult::advance();
722 std::optional<std::vector<char>> bytes = denseAttrToBytes(denseInit);
723 if (!bytes)
724 return WalkResult(buffer.emitOpError(
725 "buffer op type not supported for initialization"));
726 if (!buffer.getAddress())
727 return WalkResult(buffer.emitOpError(
728 "buffer has no address; run --aie-assign-buffer-addresses first"));
729 TileOp tile = buffer.getTileOp();
730 // Emitted as a byte array, so the value lands as data and matches the block
731 // write the CDO path performs.
732 std::string bufName(buffer.name().getValue());
733 output << "{\n";
734 output << "static const unsigned char " << bufName << "_init[] = {";
735 for (size_t i = 0; i < bytes->size(); ++i)
736 output << (i ? "," : "") << (unsigned)(unsigned char)(*bytes)[i];
737 output << "};\n";
738 output << "__mlir_aie_try(XAie_DataMemBlockWrite(" << deviceInstRef << ", "
739 << tileLocStr(tile.colIndex(), tile.rowIndex()) << ", "
740 << "0x" << llvm::utohexstr(*buffer.getAddress()) << ", "
741 << "(void *)" << bufName << "_init, " << bytes->size() << "));\n";
742 output << "}\n";
743 return WalkResult::advance();
744 });
745 if (initBuffersResult.wasInterrupted())
746 return failure();
747 output << "return XAIE_OK;\n";
748 output << "} // mlir_aie_initialize_buffers\n";
749
750 //---------------------------------------------------------------------------
751 // mlir_aie_configure_switchboxes
752 //---------------------------------------------------------------------------
753 output << "int mlir_aie_configure_switchboxes(" << ctx_p << ") {\n";
754 output << " int x, y;\n";
755
756 // StreamSwitch (switchbox) configuration
757 for (auto switchboxOp : targetOp.getOps<SwitchboxOp>()) {
758 Region &r = switchboxOp.getConnections();
759 Block &b = r.front();
760 bool isEmpty = b.getOps<ConnectOp>().empty() &&
761 b.getOps<MasterSetOp>().empty() &&
762 b.getOps<PacketRulesOp>().empty();
763 bool isParam = false;
764
765 if (isa<TileOp>(switchboxOp.getTile().getDefiningOp())) {
766 int col = switchboxOp.colIndex();
767 int row = switchboxOp.rowIndex();
768 if (!isEmpty) {
769 output << "// Core Stream Switch column " << col << " row " << row
770 << "\n";
771 output << "x = " << col << ";\n";
772 output << "y = " << row << ";\n";
773 }
774 }
775
776 for (auto connectOp : b.getOps<ConnectOp>())
777 output << "__mlir_aie_try(XAie_StrmConnCctEnable(" << deviceInstRef
778 << ", " << tileLocStr("x", "y") << ", "
779 << wireBundleToPortType(connectOp.getSourceBundle()) << ", "
780 << connectOp.sourceIndex() << ", "
781 << wireBundleToPortType(connectOp.getDestBundle()) << ", "
782 << connectOp.destIndex() << "));\n";
783
784 for (auto connectOp : b.getOps<MasterSetOp>()) {
785 int mask = 0;
786 int arbiter = -1;
787 // MasterSetOp::verify guarantees every amsel names the same arbiter, so
788 // folding them into one arbiter plus msel mask is safe here.
789 for (auto val : connectOp.getAmsels()) {
790 AMSelOp amsel = cast<AMSelOp>(val.getDefiningOp());
791 arbiter = amsel.arbiterIndex();
792 int msel = amsel.getMselValue();
793 mask |= (1 << msel);
794 }
795 bool isdma = (connectOp.getDestBundle() == WireBundle::DMA);
796
797 output << "__mlir_aie_try(XAie_StrmPktSwMstrPortEnable(" << deviceInstRef
798 << ", " << tileLocStr("x", "y") << ", "
799 << wireBundleToPortType(connectOp.getDestBundle()) << ", "
800 << connectOp.destIndex() << ", "
801 << "/* drop_header */ "
802 << (isdma ? "XAIE_SS_PKT_DROP_HEADER"
803 : "XAIE_SS_PKT_DONOT_DROP_HEADER")
804 << ", "
805 << "/* arbiter */ " << arbiter << ", "
806 << "/* MSelEn */ "
807 << "0x" << llvm::utohexstr(mask) << "));\n";
808 }
809
810 for (auto connectOp : b.getOps<PacketRulesOp>()) {
811 int slot = 0;
812 Block &block = connectOp.getRules().front();
813 for (auto slotOp : block.getOps<PacketRuleOp>()) {
814 AMSelOp amselOp = cast<AMSelOp>(slotOp.getAmsel().getDefiningOp());
815 int arbiter = amselOp.arbiterIndex();
816 int msel = amselOp.getMselValue();
817 output << "__mlir_aie_try(XAie_StrmPktSwSlavePortEnable("
818 << deviceInstRef << ", " << tileLocStr("x", "y") << ", "
819 << wireBundleToPortType(connectOp.getSourceBundle()) << ", "
820 << connectOp.sourceIndex() << "));\n";
821
822 // TODO Need to better define packet id,type used here
823 output << "__mlir_aie_try(XAie_StrmPktSwSlaveSlotEnable("
824 << deviceInstRef << ", " << tileLocStr("x", "y") << ", "
825 << wireBundleToPortType(connectOp.getSourceBundle()) << ", "
826 << connectOp.sourceIndex() << ", "
827 << "/* slot */ " << slot << ", "
828 << "/* packet */ " << packetStr(slotOp.valueInt(), /*type*/ 0)
829 << ", "
830 << "/* mask */ "
831 << "0x" << llvm::utohexstr(slotOp.maskInt()) << ", "
832 << "/* msel */ " << msel << ", "
833 << "/* arbiter */ " << arbiter << "));\n";
834 slot++;
835 }
836 }
837
838 if (isParam) {
839 output << "}\n";
840 output << "}\n";
841 }
842 }
843 for (auto op : targetOp.getOps<ShimMuxOp>()) {
844 Region &r = op.getConnections();
845 Block &b = r.front();
846 bool isEmpty = b.getOps<ConnectOp>().empty();
847
848 if (isa<TileOp>(op.getTile().getDefiningOp())) {
849 int col = op.colIndex();
850 int row = op.rowIndex();
851 if (!isEmpty) {
852 output << "// ShimMux column " << col << " row " << row << "\n";
853 output << "// NOTE ShimMux always connects from the south as "
854 << "directions are defined relative to the tile stream "
855 << "switch\n";
856 output << "x = " << col << ";\n";
857 output << "y = " << row << ";\n";
858 }
859 }
860
861 for (auto connectOp : b.getOps<ConnectOp>()) {
862
863 if (connectOp.getSourceBundle() == WireBundle::DMA ||
864 connectOp.getDestBundle() == WireBundle::DMA) {
865 if (connectOp.getSourceBundle() == WireBundle::North)
866 // demux!
867 output << "__mlir_aie_try(XAie_EnableAieToShimDmaStrmPort("
868 << deviceInstRef << ", " << tileLocStr("x", "y") << ", "
869 << connectOp.sourceIndex() << "));\n";
870 else if (connectOp.getDestBundle() == WireBundle::North)
871 // mux
872 output << "__mlir_aie_try(XAie_EnableShimDmaToAieStrmPort("
873 << deviceInstRef << ", " << tileLocStr("x", "y") << ", "
874 << connectOp.destIndex() << "));\n";
875 }
876
877 else if (connectOp.getSourceBundle() == WireBundle::PLIO ||
878 connectOp.getDestBundle() == WireBundle::PLIO) {
879 if (connectOp.getSourceBundle() == WireBundle::North) {
880 // mux
881 output << "__mlir_aie_try(XAie_AieToPlIntfEnable(" << deviceInstRef
882 << ", " << tileLocStr("x", "y") << ", "
883 << connectOp.destIndex() << ", PLIF_WIDTH_64));\n";
884 } else if (connectOp.getDestBundle() == WireBundle::North) {
885 // mux
886 output << "__mlir_aie_try(XAie_PlToAieIntfEnable(" << deviceInstRef
887 << ", " << tileLocStr("x", "y") << ", "
888 << connectOp.destIndex() << ", PLIF_WIDTH_64));\n";
889 }
890 }
891 }
892 }
893 output << "return XAIE_OK;\n";
894 output << "} // mlir_aie_configure_switchboxes\n\n";
895
896 //---------------------------------------------------------------------------
897 // mlir_aie_configure_cascade
898 //---------------------------------------------------------------------------
899 output << "int mlir_aie_configure_cascade(" << ctx_p << ") {\n";
900 for (auto configOp : targetOp.getOps<ConfigureCascadeOp>()) {
901 TileOp tile = cast<TileOp>(configOp.getTile().getDefiningOp());
902 int col = tile.colIndex();
903 int row = tile.rowIndex();
904 output << "XAie_CoreConfigAccumulatorControl(" << deviceInstRef << ", "
905 << "XAie_TileLoc(" << col << ", " << row << "), "
906 << stringifyCascadeDir(configOp.getInputDir()).upper() << ", "
907 << stringifyCascadeDir(configOp.getOutputDir()).upper() << ");\n";
908 }
909 output << "return XAIE_OK;\n";
910 output << "} // mlir_aie_configure_cascade\n\n";
911
912 //---------------------------------------------------------------------------
913 // Output Buffer Accessors
914 //---------------------------------------------------------------------------
915 for (auto tile : tiles) {
916 Operation *tileOp = tile.second;
917 TileID coord = cast<TileOp>(tileOp).getTileID();
918 int col = coord.col;
919 int row = coord.row;
920 auto loc = tileLocStr(col, row);
921
922 auto bufferAccessor = [&](BufferOp buf) {
923 // int32_t mlir_aie_read_buffer_a13(int index) {
924 // void mlir_aie_write_buffer_a13(int index, int32_t value) {
925 std::string bufName(buf.name().getValue());
926 Type t = buf.getType();
927 Type et;
928 std::string typestr;
929 if (auto memrefType = llvm::dyn_cast<MemRefType>(t)) {
930 et = memrefType.getElementType();
931 if (et.isInteger(32))
932 typestr = "int32_t";
933 else if (et.isF32())
934 typestr = "float";
935 else {
936 output << "// buffer " << bufName << " with unsupported type " << t
937 << ";\n";
938 return; // Unsupported type
939 }
940
941 } else {
942 output << "// buffer " << bufName << " with unsupported type " << t
943 << ";\n";
944 return; // Unsupported type
945 }
946 assert(buf.getAddress().has_value() && "buffer must have address");
947 output << "const int " << bufName
948 << "_offset = " << buf.getAddress().value() << ";\n";
949 output << typestr << " mlir_aie_read_buffer_" << bufName << "(" << ctx_p
950 << ", int index) {\n";
951 output << "u32 value; auto rc = XAie_DataMemRdWord(" << deviceInstRef
952 << ", " << loc << ", " << bufName
953 << "_offset + (index*4), &value);\n";
954 if (et.isInteger(32))
955 output << " return value;\n";
956 else if (et.isF32()) {
957 output << " union caster { int32_t i; float f; };\n";
958 output << " caster c; c.i = value;\n";
959 output << " return c.f;\n";
960 }
961 output << "}\n";
962 output << "int mlir_aie_write_buffer_" << bufName << "(" << ctx_p
963 << ", int index, " << typestr << " value) {\n";
964 if (et.isInteger(32))
965 output << " int32_t int_value = value;\n";
966 else if (et.isF32()) {
967 output << " union caster { int32_t i; float f; };\n";
968 output << " caster c; c.f = value;\n";
969 output << " int32_t int_value = c.i;\n";
970 }
971 output << "AieRC rc = XAie_DataMemWrWord(" << deviceInstRef << ", "
972 << loc << ", " << bufName << "_offset + (index*4), int_value);\n";
973 output << "return rc;\n";
974 output << "}\n";
975 };
976
977 // if(tiles.count(tile.getValue()))
978 for (auto buf : buffers[tileOp])
979 bufferAccessor(buf);
980 }
981
982 auto lockAccessor = [&](LockOp lock) {
983 int col = lock.colIndex();
984 int row = lock.rowIndex();
985 if (!lock.hasName())
986 return;
987 std::string lockName(lock.name().getValue());
988 output << "int mlir_aie_acquire_" << lockName << "(" << ctx_p
989 << ", int value, int timeout) {\n";
990 output << " const int id = " << lock.getLockIDValue() << ";\n";
991 output << " return XAie_LockAcquire(" << deviceInstRef << ", "
992 << tileLocStr(col, row) << ", " << tileLockStr("id", "value")
993 << ", timeout);\n";
994 output << "}\n";
995 output << "int mlir_aie_release_" << lockName << "(" << ctx_p
996 << ", int value, int timeout) {\n";
997 output << " const int id = " << lock.getLockIDValue() << ";\n";
998 output << " return XAie_LockRelease(" << deviceInstRef << ", "
999 << tileLocStr(col, row) << ", " << tileLockStr("id", "value")
1000 << ", timeout);\n";
1001 output << "}\n";
1002 };
1003
1004 targetOp.walk<WalkOrder::PreOrder>([&](LockOp lock) { lockAccessor(lock); });
1005
1006 return success();
1007}
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 uint32_t getNumLocks(AIETileType tileType) const =0
Return the number of lock objects for a given tile type.
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.
virtual uint32_t getColumnShift() const =0
bool hasProperty(ModelProperty Prop) const
virtual int columns() const =0
Return the number of columns in the device.
virtual uint32_t getNumMemTileRows() const =0
virtual uint32_t getRowShift() const =0
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.
void collectTiles(DeviceOp &device, llvm::DenseMap< TileID, mlir::Operation * > &tiles)
void collectBuffers(DeviceOp &device, llvm::DenseMap< mlir::Operation *, llvm::SmallVector< BufferOp, 4 > > &buffers)
std::string tileDMAInstStr(llvm::StringRef col, llvm::StringRef row, llvm::StringRef bdNum)
void generateXAieDmaSetMultiDimAddr(llvm::raw_ostream &output, int ndims, llvm::ArrayRef< BDDimLayoutAttr > dims, int col, int row, int bdNum, int baseAddrA, int64_t offsetA, uint32_t lenA, int elementWidthInBytes, const char *errorRet)
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
const AIETargetModel & getTargetModel(mlir::Operation *op)
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.
std::string tileDMAInstRefStr(llvm::StringRef col, llvm::StringRef row, llvm::StringRef bdNum)
llvm::SetVector< mlir::Block * > getOrderedChainOfBlocks(mlir::Region *region)
mlir::LogicalResult AIETranslateToXAIEV2(mlir::ModuleOp module, llvm::raw_ostream &output, llvm::StringRef deviceName="")
std::string tileLocStr(llvm::StringRef col, llvm::StringRef row)
std::string packetStr(llvm::StringRef id, llvm::StringRef type)