13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/Dialect/Func/IR/FuncOps.h"
15#include "mlir/IR/DialectImplementation.h"
16#include "mlir/IR/Matchers.h"
17#include "mlir/IR/Operation.h"
18#include "mlir/IR/RegionGraphTraits.h"
19#include "mlir/IR/SymbolTable.h"
20#include "mlir/IR/TypeUtilities.h"
21#include "mlir/Interfaces/DataLayoutInterfaces.h"
22#include "mlir/Interfaces/FoldInterfaces.h"
23#include "mlir/Transforms/InliningUtils.h"
24#include "llvm/ADT/SCCIterator.h"
25#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/DenseMap.h"
28#include "llvm/ADT/TypeSwitch.h"
29#include "llvm/Support/CheckedArithmetic.h"
30#include "llvm/Support/MathExtras.h"
31#include "llvm/Support/TypeSize.h"
40#include "aie/Dialect/AIEX/IR/AIEXDialect.cpp.inc"
42#include "aie/Dialect/AIEX/IR/AIEXEnums.cpp.inc"
44#include "aie/Dialect/AIEX/IR/AIEXInterfaces.cpp.inc"
46#define GET_TYPEDEF_CLASSES
47#include "aie/Dialect/AIEX/IR/AIEXTypes.cpp.inc"
49#define GET_ATTRDEF_CLASSES
50#include "aie/Dialect/AIEX/IR/AIEXAttrDefs.cpp.inc"
55void AIEXDialect::initialize() {
58#include "aie/Dialect/AIEX/IR/AIEX.cpp.inc"
61#define GET_TYPEDEF_LIST
62#include "aie/Dialect/AIEX/IR/AIEXTypes.cpp.inc"
65#define GET_ATTRDEF_LIST
66#include "aie/Dialect/AIEX/IR/AIEXAttrDefs.cpp.inc"
76#include "aie/Dialect/AIEX/IR/AIEX.cpp.inc"
113 mlir::BaseMemRefType referencedBufType,
114 llvm::SmallVector<int64_t, 4> inputSizes,
115 llvm::SmallVector<int64_t, 4> inputStrides,
116 llvm::SmallVector<int64_t, 4> &sizes,
117 llvm::SmallVector<int64_t, 4> &strides) {
118 assert(inputSizes.size() == inputStrides.size());
119 assert(sizes.size() == 4);
120 assert(strides.size() == 4);
122 DataLayout dataLayout = DataLayout::closest(op);
124 dataLayout.getTypeSizeInBits(referencedBufType.getElementType());
128 llvm::fill(sizes, 0);
129 llvm::fill(strides, 0);
131 if (inputSizes[0] == 0) {
138 int64_t inS[4] = {inputSizes[0], inputSizes[1], inputSizes[2], inputSizes[3]};
139 int64_t inT[4] = {inputStrides[0], inputStrides[1], inputStrides[2],
141 int64_t outS[4], outT[4];
144 for (
int i = 0; i < 4; i++) {
146 strides[i] = outT[i];
151 mlir::Operation *forOp,
152 llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
153 mlir::BaseMemRefType referencedBufType,
int tileCol,
int tileRow,
154 llvm::SmallVector<int64_t, 4> inputSizes,
155 llvm::SmallVector<int64_t, 4> inputStrides,
156 llvm::SmallVector<int64_t, 4> hardwareSizes,
157 llvm::SmallVector<int64_t, 4> hardwareStrides,
158 bool skipTransformationChecks) {
160 auto addressGranularity = targetModel.getAddressGenGranularity();
161 DataLayout dataLayout = DataLayout::closest(forOp);
163 dataLayout.getTypeSizeInBits(referencedBufType.getElementType());
166 if (!targetModel.isCoreTile(tileCol, tileRow) &&
167 !targetModel.isMemTile(tileCol, tileRow) &&
168 !targetModel.isShimNOCTile(tileCol, tileRow))
169 return emitError() <<
"Unsupported tile type at (" << tileCol <<
", "
170 << tileRow <<
") Must be ShimNOC, Mem or Core.";
172 uint32_t wrap_bits = targetModel.getDmaBdWrapBits(tileCol, tileRow);
173 uint32_t step_bits = targetModel.getDmaBdStepBits(tileCol, tileRow);
175 for (
int i = 0; i < 4; i++) {
176 if (inputSizes[i] <= 0) {
177 return emitError() <<
"Size " << i <<
" must be a positive integer.";
182 std::stringstream msg;
183 msg <<
"Transfer sizes must be multiples of " << (addressGranularity / 8)
184 <<
" bytes. " << inputSizes[0] <<
" elements at " << (elemWidth / 8)
185 <<
" bytes each equal " << (inputSizes[0] * elemWidth / 8)
186 <<
" bytes, which is not divisible by " << (addressGranularity / 8)
188 return emitError() << msg.str();
191 for (
int i = 0; i < 3; i++) {
192 if (inputSizes[i] > 1 && inputStrides[i] < 1) {
196 return emitError() <<
"Stride " << i <<
" must be a positive integer.";
201 if (inputSizes[3] > 1 && inputStrides[3] < 0) {
202 return emitError() <<
"Stride 3 must be a non-negative integer.";
205 for (
int i = 0; i < 4; i++) {
208 if (i == 0 && inputStrides[i] == 1)
211 addressGranularity)) {
212 std::stringstream msg;
213 msg <<
"Stride " << i <<
" is " << inputStrides[i] <<
" elements * "
214 << (elemWidth / 8) <<
" bytes = " << (inputStrides[i] * elemWidth / 8)
215 <<
" bytes, which is not divisible by " << (addressGranularity / 8)
217 return emitError() << msg.str();
222 if (elemWidth != addressGranularity && inputSizes[0] > 1 &&
223 inputStrides[0] != 1)
224 return emitError() <<
"Stride 0 is " << inputStrides[0]
225 <<
" elements, but must be 1 for " << (elemWidth / 8)
226 <<
"-byte elements: the DMA moves whole "
227 << (addressGranularity / 8) <<
"-byte words.";
229 auto outOfRange = [&](
const char *what,
int dim, int64_t written,
230 int64_t encoded, int64_t lo, int64_t hi) {
231 return emitError() << what <<
" " << dim <<
" is " << written
232 <<
" (encoded as " << encoded <<
"), which exceeds the ["
233 << lo <<
":" << hi <<
"] range.";
236 int64_t maxWrap = (1 << wrap_bits) - 1;
237 int64_t maxStep = 1 << step_bits;
239 for (
int dim : {0, 1}) {
240 if (!skipTransformationChecks && hardwareSizes[dim] > maxWrap)
241 return outOfRange(
"Size", dim, inputSizes[dim], hardwareSizes[dim], 0,
246 bool pureRepeat = inputStrides[3] == 0;
248 pureRepeat ? targetModel.getMaxRepeatCount() + 1
249 : 1LL << targetModel.getDmaBdIterBits(tileCol, tileRow);
250 if (inputSizes[3] > maxCount)
251 return emitError() << (pureRepeat ?
"repeat count " :
"iteration count ")
252 << inputSizes[3] <<
" exceeds the [1:" << maxCount
254 for (
int dim : {0, 1, 2}) {
255 if (hardwareStrides[dim] > maxStep - 1)
256 return outOfRange(
"Stride", dim, inputStrides[dim], hardwareStrides[dim],
261 if (hardwareStrides[3] > maxStep - 1 && hardwareSizes[3] > 0)
262 return outOfRange(
"Stride", 3, inputStrides[3], hardwareStrides[3], 1,
275LogicalResult AIEX::CertWrite32DOp::verify() {
276 if (getAddressIsReg() && getAddress() > 23)
277 return emitOpError(
"has address_is_reg, so address ")
278 << getAddress() <<
" must be a register index in [0, 23]";
279 if (getValueIsReg() && getValue() > 23)
280 return emitOpError(
"has value_is_reg, so value ")
281 << getValue() <<
" must be a register index in [0, 23]";
285LogicalResult AIEX::UseTokenOp::verify() {
286 auto *parentOp = (*this)->getParentOp();
287 if (isa<func::FuncOp>(parentOp) || isa<AIE::CoreOp>(parentOp) ||
288 isa<AIE::MemOp>(parentOp) || isa<AIE::ShimDMAOp>(parentOp))
297LogicalResult AIEX::MulticastOp::verify() {
298 Region &body = getPorts();
299 assert(getOperation()->getNumRegions());
300 assert(!body.empty());
301 for (
auto &ops : body.front())
302 if (!isa<MultiDestOp, AIE::EndOp>(ops))
303 return ops.emitOpError(
"cannot be contained in a Multicast op");
312LogicalResult AIEX::BroadcastPacketOp::verify() {
313 Region &body = getPorts();
314 assert(getOperation()->getNumRegions());
315 assert(!body.empty());
316 for (
auto &ops : body.front())
317 if (!isa<BPIDOp, AIE::EndOp>(ops))
318 return ops.emitOpError(
"cannot be contained in a BroadcastPacket op");
329uint32_t AIEX::NpuDmaMemcpyNdOp::getAxcacheOrDefault() {
333int64_t AIEX::NpuDmaMemcpyNdOp::getOffsetInBytes() {
334 llvm::SmallVector<int64_t, 4> offsets =
335 llvm::map_to_vector(llvm::reverse(getMixedOffsets()), [](OpFoldResult s) {
336 return getConstantIntValue(s).value();
338 auto strides = llvm::to_vector<4>(llvm::reverse(getMixedStrides()));
340 size_t R = offsets.size();
341 size_t el_bit_width = getElementTypeBitwidth();
342 assert(el_bit_width % 8 == 0 &&
343 "Expected Memref element bitwidth to be multiple of 8.");
344 size_t S = el_bit_width / 8;
349 for (
size_t i = 0; i < R; i++) {
352 auto strideConst = getConstantIntValue(strides[i]);
353 assert(strideConst &&
354 "verifier requires a stride paired with a non-zero offset to be "
356 offset += offsets[i] * (*strideConst) * S;
365 llvm::ArrayRef<int64_t> strides) {
366 return sizes[1] == 1 && sizes[2] == 1 && strides[0] == 1 && strides[1] == 0 &&
377 llvm::ArrayRef<int64_t> strides) {
380 if (sizes[1] > 1 && strides[1] != sizes[0])
382 if (sizes[2] > 1 && strides[2] != sizes[0] * sizes[1])
388 int row, llvm::ArrayRef<int64_t> sizes,
389 llvm::ArrayRef<int64_t> strides) {
399bool AIEX::NpuDmaMemcpyNdOp::isLinearTransferWithoutTransformation() {
400 llvm::SmallVector<int64_t, 4> inputSizes =
401 llvm::map_to_vector(llvm::reverse(getMixedSizes()), [](OpFoldResult s) {
402 return getConstantIntValue(s).value();
404 llvm::SmallVector<int64_t, 4> inputStrides =
405 llvm::map_to_vector(llvm::reverse(getMixedStrides()), [](OpFoldResult s) {
406 return getConstantIntValue(s).value();
434struct LinearizeContiguousTransfer
435 :
public mlir::OpRewritePattern<AIEX::NpuDmaMemcpyNdOp> {
436 using OpRewritePattern::OpRewritePattern;
439 matchAndRewrite(AIEX::NpuDmaMemcpyNdOp op,
440 mlir::PatternRewriter &rewriter)
const override {
442 if (!llvm::all_of(op.getMixedSizes(), [](mlir::OpFoldResult s) {
443 return mlir::getConstantIntValue(s).has_value();
445 return mlir::failure();
446 if (!llvm::all_of(op.getMixedStrides(), [](mlir::OpFoldResult s) {
447 return mlir::getConstantIntValue(s).has_value();
449 return mlir::failure();
450 if (!llvm::all_of(op.getMixedOffsets(), [](mlir::OpFoldResult s) {
451 return mlir::getConstantIntValue(s).has_value();
453 return mlir::failure();
456 if (op.isLinearTransferWithoutTransformation())
457 return mlir::failure();
461 llvm::SmallVector<int64_t, 4> sizes = llvm::map_to_vector(
462 llvm::reverse(op.getMixedSizes()), [](mlir::OpFoldResult s) {
463 return mlir::getConstantIntValue(s).value();
465 llvm::SmallVector<int64_t, 4> strides = llvm::map_to_vector(
466 llvm::reverse(op.getMixedStrides()), [](mlir::OpFoldResult s) {
467 return mlir::getConstantIntValue(s).value();
469 llvm::SmallVector<int64_t, 4> offsets = llvm::map_to_vector(
470 llvm::reverse(op.getMixedOffsets()), [](mlir::OpFoldResult s) {
471 return mlir::getConstantIntValue(s).value();
476 return mlir::failure();
480 int64_t N = sizes[0] * sizes[1] * sizes[2];
481 llvm::SmallVector<int64_t, 4> newSizesOuter = {sizes[3], 1, 1, N};
482 llvm::SmallVector<int64_t, 4> newStridesOuter = {strides[3], 0, 0, 1};
489 int64_t linearOffset = offsets[0] * strides[0] + offsets[1] * strides[1] +
490 offsets[2] * strides[2];
491 llvm::SmallVector<int64_t, 4> newOffsetsOuter = {offsets[3], 0, 0,
494 rewriter.replaceOpWithNewOp<AIEX::NpuDmaMemcpyNdOp>(
495 op, op, newOffsetsOuter, newSizesOuter, newStridesOuter);
496 return mlir::success();
501void AIEX::NpuDmaMemcpyNdOp::getCanonicalizationPatterns(
502 mlir::RewritePatternSet &results, mlir::MLIRContext *context) {
503 results.add<LinearizeContiguousTransfer>(context);
509static std::optional<std::string>
511 uint32_t requestedBurstLength) {
512 if (requestedBurstLength != 0) {
514 auto pair = llvm::find_if(bel, [=](
const std::pair<uint32_t, uint32_t> &p) {
515 return p.second == requestedBurstLength;
518 if (pair == bel.end()) {
519 std::string errorMessage =
520 "Requested burst length is not supported by the target. "
521 "Supported burst lengths:";
524 std::accumulate(bel.begin(), bel.end(), errorMessage,
525 [](
const std::string &a,
auto b) {
526 return a +
" " + std::to_string(b.second);
540LogicalResult AIEX::NpuDmaMemcpyNdOp::verifyDynamicSizesStrides(
542 AIE::ShimDMAAllocationOp allocOp) {
546 "runtime sizes/strides require a shim_dma_allocation to resolve the "
547 "tile; none found.");
548 AIE::TileOp tile = allocOp.getTileOp();
552 if (!targetModel.
isShimNOCTile(tile.getCol(), tile.getRow()))
554 "runtime sizes/strides are only supported for shim NOC tile DMAs.");
557 if (getD0ZeroBefore() || getD1ZeroBefore() || getD2ZeroBefore() ||
558 getD0ZeroAfter() || getD1ZeroAfter() || getD2ZeroAfter())
560 "zero padding is not supported with runtime sizes/strides.");
567 auto sizes = getMixedSizes();
568 auto strides = getMixedStrides();
586 DataLayout dataLayout = DataLayout::closest(getOperation());
587 uint64_t elemWidth = dataLayout.getTypeSizeInBits(buffer.getElementType());
589 llvm::SmallVector<mlir::OpFoldResult, 4> sizesRev(llvm::reverse(sizes));
590 auto checkSize = [&](mlir::OpFoldResult ofr, int64_t hwVal, int64_t hi,
591 llvm::StringRef what) -> LogicalResult {
592 if (!getConstantIntValue(ofr))
594 if (hwVal < 0 || hwVal > hi)
595 return emitOpError(what) <<
" hardware value " << hwVal
596 <<
" exceeds hardware range [0:" << hi <<
"].";
599 auto hwSize = [&](mlir::OpFoldResult ofr) {
600 return getConstantIntValue(ofr).value_or(0);
602 int64_t d0Hw = (int64_t)(hwSize(sizesRev[0]) * elemWidth / gran);
603 int64_t d1Hw = hwSize(sizesRev[1]);
604 int64_t iterRaw = hwSize(sizesRev[3]);
605 int64_t iterHw = iterRaw > 1 ? iterRaw - 1 : 0;
606 std::optional<int64_t> outerStride = getConstantIntValue(strides.front());
607 bool pureRepeat = outerStride && *outerStride == 0;
608 AIE::AIETileType tileType =
609 targetModel.
getTileType(tile.getCol(), tile.getRow());
613 if (failed(checkSize(sizesRev[0], d0Hw, wrapMax,
"d0 size")) ||
614 failed(checkSize(sizesRev[1], d1Hw, wrapMax,
"d1 size")) ||
615 failed(checkSize(sizesRev[3], iterHw, maxCount - 1,
616 pureRepeat ?
"repeat count" :
"iteration size")))
622 llvm::SmallVector<mlir::OpFoldResult, 4> stridesRev(llvm::reverse(strides));
629 auto errorMessage = checkBurstLength(targetModel, getBurstLength());
630 if (errorMessage.has_value())
631 return emitOpError(errorMessage.value());
636LogicalResult AIEX::NpuDmaMemcpyNdOp::verify() {
637 BaseMemRefType buffer = getMemref().getType();
641 if (getOffsetParameterAttr() || getOffsetStateTableIdxAttr()) {
642 uint64_t elemBitWidth = buffer.getElementTypeBitWidth();
643 if (elemBitWidth == 0 || (elemBitWidth % 8) != 0)
644 return emitOpError(
"offset_parameter requires a whole-byte element type");
647 if (getElementTypeBitwidth() > addressGranularity) {
648 return emitOpError(
"Maximum element bit width allowed is ")
649 << addressGranularity <<
"bits. ";
651 if (buffer.hasStaticShape() &&
652 (buffer.getNumElements() * getElementTypeBitwidth()) <
653 addressGranularity) {
654 return emitOpError(
"Minimum data transfer size required is ")
655 << addressGranularity <<
"bits. ";
657 bool allStridesConstant = llvm::all_of(getMixedStrides(), [](OpFoldResult s) {
658 return getConstantIntValue(s).has_value();
660 bool allSizesConstant = llvm::all_of(getMixedSizes(), [](OpFoldResult s) {
661 return getConstantIntValue(s).has_value();
663 bool allOffsetsConstant = llvm::all_of(getMixedOffsets(), [](OpFoldResult s) {
664 return getConstantIntValue(s).has_value();
667 bool hasLengthParameter =
668 getLengthParameterAttr() || getLengthStateTableIdxAttr();
671 if (hasLengthParameter &&
672 (!allStridesConstant || !allSizesConstant || !allOffsetsConstant))
674 "length_parameter requires constant offsets, sizes and strides");
677 if (!allStridesConstant || !allSizesConstant || !allOffsetsConstant)
680 llvm::SmallVector<int64_t, 4> inputSizes =
681 llvm::map_to_vector(llvm::reverse(getMixedSizes()), [](OpFoldResult s) {
682 return getConstantIntValue(s).value();
684 llvm::SmallVector<int64_t, 4> inputStrides =
685 llvm::map_to_vector(llvm::reverse(getMixedStrides()), [](OpFoldResult s) {
686 return getConstantIntValue(s).value();
688 int64_t offset = getOffsetInBytes();
690 auto errorMessage = checkBurstLength(targetModel, getBurstLength());
691 if (errorMessage.has_value()) {
692 return emitOpError(errorMessage.value());
695 if (hasLengthParameter) {
697 *
this, getLengthUnit(), buffer,
698 inputSizes[0] * inputSizes[1] * inputSizes[2],
700 llvm::ArrayRef<int64_t>(inputSizes).take_front(3))))
708 if (offset % 4 != 0) {
709 return emitOpError(
"Offset must be 4-byte-aligned.");
713 if (
auto packetInfo = getPacket()) {
716 if (packetInfo->getPktType() > 7)
717 return emitOpError(
"Packet type field can only hold 3 bits.");
718 if (packetInfo->assignedId() > 31)
719 return emitOpError(
"Packet ID field can only hold 5 bits.");
728AIEX::NpuDmaMemcpyNdOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
729 BaseMemRefType buffer = getMemref().getType();
731 bool allStridesConstant = llvm::all_of(getMixedStrides(), [](OpFoldResult s) {
732 return getConstantIntValue(s).has_value();
734 bool allSizesConstant = llvm::all_of(getMixedSizes(), [](OpFoldResult s) {
735 return getConstantIntValue(s).has_value();
737 bool allOffsetsConstant = llvm::all_of(getMixedOffsets(), [](OpFoldResult s) {
738 return getConstantIntValue(s).has_value();
741 !allStridesConstant || !allSizesConstant || !allOffsetsConstant;
743 bool hasLengthParameter =
744 getLengthParameterAttr() || getLengthStateTableIdxAttr();
745 bool isAIE1 = targetModel.
getTargetArch() == AIE::AIEArch::AIE1;
746 AIE::DeviceOp dev = getOperation()->getParentOfType<AIE::DeviceOp>();
747 if (!dev && isAIE1 && !isDynamic && !hasLengthParameter)
749 auto allocOp = symbolTable.lookupSymbolIn<AIE::ShimDMAAllocationOp>(
750 dev, getMetadata().getRootReference());
752 return verifyDynamicSizesStrides(targetModel, buffer, allocOp);
758 auto tile = allocOp.getTileOp();
761 if (isAIE1 && hasLengthParameter)
762 return emitOpError(
"length_parameter is only supported on AIE2 and "
766 int col = tile.getCol();
767 int row = tile.getRow();
768 if (hasLengthParameter &&
772 llvm::SmallVector<int64_t, 4> inputSizes =
773 llvm::map_to_vector(llvm::reverse(getMixedSizes()), [](OpFoldResult s) {
774 return getConstantIntValue(s).value();
776 llvm::SmallVector<int64_t, 4> inputStrides =
777 llvm::map_to_vector(llvm::reverse(getMixedStrides()), [](OpFoldResult s) {
778 return getConstantIntValue(s).value();
780 llvm::SmallVector<int64_t, 4> hardwareSizes(4);
781 llvm::SmallVector<int64_t, 4> hardwareStrides(4);
783 inputStrides, hardwareSizes, hardwareStrides);
795 targetModel, col, row, inputSizes, inputStrides);
802 llvm::SmallVector<int64_t, 4> inputOffsets =
803 llvm::map_to_vector(llvm::reverse(getMixedOffsets()), [](OpFoldResult s) {
804 return getConstantIntValue(s).value();
810 if (!isAIE1 && !hasLengthParameter &&
812 row, inputOffsets, inputSizes,
816 *
this, [&] {
return emitOpError(); }, buffer, col, row,
817 inputSizes, inputStrides, hardwareSizes, hardwareStrides,
818 skipTransformationChecks))) {
829LogicalResult AIEX::NpuDmaWaitOp::verify() {
830 AIE::DeviceOp dev = (*this)->getParentOfType<AIE::DeviceOp>();
833 if (dev && !dev.lookupSymbol(getSymbol()))
834 return emitOpError(
"couldn't find symbol in parent device");
842LogicalResult AIEX::NpuPushQueueOp::verify() {
844 auto numBds = targetModel.
getNumBDs(getColumn(), getRow());
849 bdId && *bdId >= numBds)
850 return emitOpError(
"BD ID exceeds the maximum ID.");
852 if (std::optional<uint64_t> repeatCount =
854 repeatCount && *repeatCount > maxRepeat)
855 return emitOpError(
"Repeat count exceeds the [0:")
856 << maxRepeat <<
"] range.";
857 if (getRepeatStateTableIdxAttr()) {
859 if (arch != AIE::AIEArch::AIE2 && arch != AIE::AIEArch::AIE2p)
860 return emitOpError(
"repeat_parameter is only supported on AIE2 and "
863 !targetModel.
isMemTile(getColumn(), getRow()))
864 return emitOpError(
"repeat_parameter is only supported on shim NOC "
865 "and mem tiles, got tile (")
866 << getColumn() <<
", " << getRow() <<
")";
868 return emitOpError(
"takes its repeat count from the scratchpad, so its "
869 "repeat_count operand must be 0");
872 return emitOpError(
"takes its repeat count from the scratchpad, so its "
873 "bd_id must be a constant");
875 return emitOpError(
"takes its repeat count from the scratchpad, whose "
876 "update clears the low two bits of the queue word, "
877 "so its bd_id must be a multiple of 4, got ")
887LogicalResult AIEX::NpuWriteBdOp::verify() {
889 auto numBds = targetModel.
getNumBDs(getColumn(), getRow());
891 (getD0Size() >= 1) && (getD1Size() == 1) && (getIterationSize() == 0);
892 if (getBdId() >= numBds)
893 return emitOpError(
"BD ID exceeds the maximum ID.");
894 if (getPacketId() > 31)
895 return emitOpError(
"Packet ID exceeds the maximum supported by 5 bits.");
896 if (getPacketType() > 7)
897 return emitOpError(
"Packet Type exceeds the maximum supported by 3 bits.");
898 int64_t oooId = getOutOfOrderId();
901 return emitOpError(
"out_of_order_id must be in [0, ")
903 if (oooId != 0 && getEnablePacket() == 0)
904 return emitOpError(
"out_of_order_id requires a packet-enabled BD");
909 AIE::AIETileType tileType = targetModel.
getTileType(getColumn(), getRow());
913 uint32_t maxWrap = wrapBits > 0 ? (1u << wrapBits) - 1 : 0;
914 uint32_t maxStep = stepBits > 0 ? (1u << stepBits) - 1 : 0;
915 uint32_t maxIter = iterBits > 0 ? (1u << iterBits) - 1 : 0;
917 if (!isLinearTransfer && getD0Size() > maxWrap)
918 return emitOpError() <<
"D0 Size exceeds the [0:" << maxWrap <<
"] range.";
919 if (getD0Stride() > maxStep)
920 return emitOpError() <<
"D0 Stride exceeds the [0:" << maxStep
922 if (getD1Size() > maxWrap)
923 return emitOpError() <<
"D1 Size exceeds the [0:" << maxWrap <<
"] range.";
924 if (getD1Stride() > maxStep)
925 return emitOpError() <<
"D1 Stride exceeds the [0:" << maxStep
927 if (getD2Stride() > maxStep)
928 return emitOpError() <<
"D2 Stride exceeds the [0:" << maxStep
930 if (getIterationSize() > maxIter)
931 return emitOpError() <<
"Iteration Size exceeds the [0:" << maxIter
933 if (
static_cast<uint32_t
>(getIterationStride()) > maxStep)
934 return emitOpError() <<
"Iteration Stride exceeds the [0:" << maxStep
939 if (getBufferLength() > maxLen)
940 return emitOpError() <<
"Buffer length exceeds the [0:" << maxLen
942 if (targetModel.
isShimNOCTile(getColumn(), getRow()) && getD2Size() != 0)
943 return emitOpError(
"ShimTile only supports 3 dimensions of sizes.");
945 (getD0ZeroBefore() != 0 || getD0ZeroAfter() != 0 ||
946 getD1ZeroBefore() != 0 || getD1ZeroAfter() != 0 ||
947 getD2ZeroBefore() != 0 || getD2ZeroAfter() != 0))
948 return emitOpError(
"ShimTile doesn't support zero padding.");
966 if (getD0ZeroBefore() > 0x3F || getD0ZeroAfter() > 0x3F)
967 return emitOpError(
"D0 pad_before/pad_after exceeds the [0:63] range.");
968 if (getD1ZeroBefore() > 0x1F || getD1ZeroAfter() > 0x1F)
969 return emitOpError(
"D1 pad_before/pad_after exceeds the [0:31] range.");
970 if (getD2ZeroBefore() > 0xF || getD2ZeroAfter() > 0xF)
971 return emitOpError(
"D2 pad_before/pad_after exceeds the [0:15] range.");
974 getBurstLength() != 0)
975 return emitOpError(
"Only ShimTiles support burst length.");
976 auto errorMessage = checkBurstLength(targetModel, getBurstLength());
977 if (errorMessage.has_value()) {
978 return emitOpError(errorMessage.value());
980 if (!targetModel.
isShimNOCTile(getColumn(), getRow()) && getAxcache())
981 return emitOpError(
"Only ShimTiles support AxCACHE configuration.");
986uint32_t AIEX::NpuWriteBdOp::getAxcacheOrDefault() {
992 return static_cast<uint32_t
>(*cst);
1000 if (
auto constant = v.getDefiningOp<arith::ConstantOp>())
1001 if (
auto attr = dyn_cast<IntegerAttr>(constant.getValue()))
1002 return attr.getValue().getZExtValue();
1004 if (!mlir::matchPattern(v, mlir::m_ConstantInt(&cst)))
1005 return std::nullopt;
1006 return cst.getZExtValue();
1010 mlir::Location loc, uint32_t value) {
1011 return arith::ConstantOp::create(
1012 builder, loc, builder.getI32IntegerAttr(
static_cast<int32_t
>(value)));
1016 mlir::Location loc, uint64_t value) {
1017 if (value <= std::numeric_limits<uint32_t>::max())
1019 return arith::ConstantOp::create(
1021 IntegerAttr::get(builder.getIntegerType(64),
1022 static_cast<int64_t
>(value)));
1032static bool hasQueryableStorageLayout(Type type) {
1033 if (type.isIntOrFloat() || isa<IndexType, DataLayoutTypeInterface>(type))
1035 if (
auto vector = dyn_cast<VectorType>(type))
1036 return hasQueryableStorageLayout(
vector.getElementType());
1037 if (
auto complex = dyn_cast<ComplexType>(type))
1038 return hasQueryableStorageLayout(complex.getElementType());
1044static std::optional<uint64_t>
1045getStaticMemRefStorageBytes(MemRefType type,
const DataLayout &layout) {
1046 if (!type || !type.hasStaticShape() ||
1047 !hasQueryableStorageLayout(type.getElementType()))
1048 return std::nullopt;
1049 if (
auto vector = dyn_cast<VectorType>(type.getElementType())) {
1053 return std::nullopt;
1054 int64_t elements = 1;
1055 for (int64_t dim :
vector.getShape()) {
1056 auto product = llvm::checkedMul(elements, dim);
1058 return std::nullopt;
1059 elements = *product;
1061 uint64_t inner =
vector.getShape().back();
1062 auto padded = llvm::checkedMulUnsigned(
1063 static_cast<uint64_t
>(elements) / inner, llvm::PowerOf2Ceil(inner));
1064 llvm::TypeSize bits = layout.getTypeSizeInBits(
vector.getElementType());
1065 if (!padded || bits.isScalable() ||
1066 !llvm::checkedMulUnsigned(*padded, bits.getFixedValue()))
1067 return std::nullopt;
1069 llvm::TypeSize elementBytes = layout.getTypeSize(type.getElementType());
1070 if (elementBytes.isScalable())
1071 return std::nullopt;
1072 if (llvm::is_contained(type.getShape(), int64_t{0}))
1074 uint64_t bytes = elementBytes.getFixedValue();
1075 for (int64_t dim : type.getShape()) {
1076 auto product = llvm::checkedMulUnsigned(bytes,
static_cast<uint64_t
>(dim));
1078 return std::nullopt;
1084template <
typename OpTy>
1085static LogicalResult verifyBufferWriteScope(OpTy op) {
1086 if (op.getBuffer() && !op->template getParentOfType<AIE::DeviceOp>())
1087 return op.emitOpError(
"must be inside a device when buffer is present");
1091template <
typename OpTy>
1093verifyBufferWrite(OpTy op, std::optional<uint32_t> wordOffset,
1095 auto bufferAttr = op.getBufferAttr();
1099 return op.emitOpError(
"address must be constant when buffer is present");
1100 auto device = op->template getParentOfType<AIE::DeviceOp>();
1102 return op.emitOpError(
"must be inside a device when buffer is present");
1105 auto buffer = names.
lookupIn<AIE::BufferOp>(device, bufferAttr.getAttr());
1107 return op.emitOpError()
1108 <<
"buffer '" << bufferAttr.getValue() <<
"' not found in device";
1109 auto bufferBytes = getStaticMemRefStorageBytes(buffer.getType(),
1110 DataLayout::closest(buffer));
1112 return op.emitOpError(
1113 "referenced buffer must have a static, fixed-size storage extent "
1114 "representable in 64 bits");
1116 uint64_t offsetBytes = uint64_t{*wordOffset} *
sizeof(uint32_t);
1117 auto end = llvm::checkedAddUnsigned(offsetBytes, writeBytes);
1119 return op.emitOpError(
"buffer-relative write range overflows 64-bit bytes");
1120 if (*end > *bufferBytes)
1121 return op.emitOpError() <<
"write range [" << offsetBytes <<
", " << *end
1122 <<
") exceeds buffer @" << bufferAttr.getValue()
1123 <<
" size " << *bufferBytes <<
" bytes";
1125 auto tile = buffer.getTileLike();
1126 const auto &targetModel = device.getTargetModel();
1129 uint64_t start = offsetBytes;
1130 if (
auto base = buffer.getAddress()) {
1132 return op.emitOpError(
"referenced buffer address must be nonnegative");
1133 start +=
static_cast<uint64_t
>(*base);
1134 if (start %
sizeof(uint32_t) != 0)
1135 return op.emitOpError() <<
"resolved buffer write address " << start
1136 <<
" is not 4-byte aligned";
1140 auto memoryEnd = llvm::checkedAddUnsigned(start, writeBytes);
1142 return op.emitOpError(
"resolved buffer write range overflows 64-bit bytes");
1143 if (*memoryEnd > memoryBytes)
1144 return op.emitOpError()
1145 <<
"write range [" << start <<
", " << *memoryEnd
1146 <<
") exceeds tile data memory size " << memoryBytes <<
" bytes";
1154LogicalResult AIEX::NpuWrite32Op::verify() {
1155 return verifyBufferWriteScope(*
this);
1163 sizeof(uint32_t), names);
1166template <
typename T>
1167static std::optional<uint32_t>
1168getAbsoluteAddress(T *op, uint32_t addressOffset,
1170 AIE::DeviceOp device =
1171 op->getOperation()->template getParentOfType<AIE::DeviceOp>();
1173 op->emitError(
"Must be inside a device.");
1174 return std::nullopt;
1178 uint32_t address = 0;
1182 if (
auto bufferSym = op->getBuffer()) {
1183 AIE::BufferOp buffer =
1184 names ? names->
lookup<AIE::BufferOp>(op->getBufferAttr().getAttr())
1187 op->emitError() <<
"buffer '" << *bufferSym <<
"' not found in device";
1188 return std::nullopt;
1191 auto bufferAddress = buffer.getAddress();
1192 if (!bufferAddress) {
1193 mlir::InFlightDiagnostic err =
1194 op->emitError(
"referenced buffer must have address assigned");
1195 err.attachNote(buffer.getLoc()) <<
"This buffer must have an address.";
1196 return std::nullopt;
1199 uint32_t col = buffer.getTileOp().getCol();
1200 uint32_t row = buffer.getTileOp().getRow();
1201 address =
static_cast<uint32_t
>(*bufferAddress) +
1202 addressOffset *
sizeof(uint32_t);
1204 ((row & 0xff) << tm.
getRowShift()) | (address & 0xfffff);
1206 address = addressOffset;
1207 std::optional<uint32_t> col = op->getColumn();
1208 std::optional<uint32_t> row = op->getRow();
1213 ((*row & 0xff) << tm.
getRowShift()) | (address & 0xfffff);
1220std::optional<uint32_t>
1224 return std::nullopt;
1225 return ::getAbsoluteAddress(
this, *addressOffset, names);
1232LogicalResult AIEX::NpuAddressPatchOp::verify() {
1236 if (getAddrVal() && !getAddrVal().getType().isInteger(32))
1237 return emitOpError(
"addr_val must be an i32 value");
1238 if (getArgIdx().has_value() ==
static_cast<bool>(getBuffer()))
1239 return emitOpError(
"must name the host buffer either by the 'arg_idx' "
1240 "attribute or by the 'buffer' operand, not both and not "
1249std::optional<uint32_t> AIEX::NpuUpdateFromScratchpadOp::getAbsoluteAddress(
1251 return ::getAbsoluteAddress(
this, getAddress(), names);
1254LogicalResult AIEX::NpuUpdateFromScratchpadOp::verify() {
1256 constexpr uint32_t kMaxStateTableEntries = 32;
1257 if (getStateTableIdx() >= kMaxStateTableEntries)
1258 return emitOpError(
"state_table_idx ")
1259 <<
static_cast<uint32_t
>(getStateTableIdx())
1260 <<
" exceeds maximum StateTable index ("
1261 << (kMaxStateTableEntries - 1) <<
").";
1271LogicalResult AIEX::NpuCreateScratchpadOp::verify() {
1273 if (getUsageType() != 0) {
1274 return emitOpError(
"usage_type must be 0 (got ")
1275 <<
static_cast<uint32_t
>(getUsageType())
1276 <<
"); other layouts are not supported.";
1281 constexpr uint32_t kMaxScratchpadSizeBytes = 128;
1282 if (getSize() == 0) {
1283 return emitOpError(
"size must be greater than 0.");
1285 if (getSize() % 4 != 0) {
1286 return emitOpError(
"size (")
1287 << getSize() <<
") must be a multiple of 4 bytes.";
1289 if (getSize() > kMaxScratchpadSizeBytes) {
1290 return emitOpError(
"size (")
1291 << getSize() <<
" bytes) exceeds maximum scratchpad size of "
1292 << kMaxScratchpadSizeBytes <<
" bytes.";
1297 uint32_t numEntries = getSize() / 4;
1298 if (Block *block = (*this)->getBlock()) {
1299 for (
auto updateOp : block->getOps<AIEX::NpuUpdateFromScratchpadOp>()) {
1300 if (updateOp.getStateTableIdx() < numEntries)
1302 return updateOp.emitOpError(
"state_table_idx ")
1303 <<
static_cast<uint32_t
>(updateOp.getStateTableIdx())
1304 <<
" is out of bounds for scratchpad of size " << getSize()
1305 <<
" bytes (" << numEntries <<
" entries) created by "
1306 << (*this)->getName() <<
".";
1314 auto runtimeSeq = getOperation()->getParentOfType<AIE::RuntimeSequenceOp>();
1319 NpuCreateScratchpadOp firstSeen;
1320 runtimeSeq.walk([&](NpuCreateScratchpadOp op) {
1322 return WalkResult::interrupt();
1324 if (firstSeen != *
this) {
1325 InFlightDiagnostic diag =
1326 emitOpError(
"only one 'aiex.npu.create_scratchpad' is allowed per "
1327 "runtime sequence");
1328 diag.attachNote(firstSeen.getLoc())
1329 <<
"previous 'aiex.npu.create_scratchpad' here";
1340LogicalResult AIEX::NpuMaskWrite32Op::verify() {
1341 return verifyBufferWriteScope(*
this);
1349 sizeof(uint32_t), names);
1352std::optional<uint32_t>
1356 return std::nullopt;
1357 return ::getAbsoluteAddress(
this, *addressOffset, names);
1364LogicalResult AIEX::NpuMaskPollOp::verify() {
1365 return verifyBufferWriteScope(*
this);
1373 sizeof(uint32_t), names);
1376std::optional<uint32_t>
1380 return std::nullopt;
1381 return ::getAbsoluteAddress(
this, *addressOffset, names);
1388LogicalResult AIEX::NpuBlockWriteOp::verify() {
1389 return verifyBufferWriteScope(*
this);
1396 auto type = dyn_cast<MemRefType>(getData().getType());
1397 DataLayout layout = DataLayout::closest(*
this);
1398 auto bytes = getStaticMemRefStorageBytes(type, layout);
1400 return emitOpError(
"payload must have a static, fixed-size storage extent "
1401 "representable in 64 bits when buffer is present");
1402 llvm::TypeSize elementBits = layout.getTypeSizeInBits(type.getElementType());
1403 if (elementBits.isScalable() || elementBits.getFixedValue() != 32)
1405 "payload must have 32-bit elements when buffer is present");
1406 return verifyBufferWrite(*
this, getAddress(), *bytes, names);
1409std::optional<uint32_t>
1411 return ::getAbsoluteAddress(
this, getAddress(), names);
1414DenseIntElementsAttr AIEX::NpuBlockWriteOp::getDataWords() {
1415 Value
memref = this->getData();
1416 DataLayout dataLayout = DataLayout::closest(*
this);
1417 int64_t width = dataLayout.getTypeSizeInBits(
1418 cast<MemRefType>(
memref.getType()).getElementType());
1420 emitWarning(
"Only 32-bit data type is supported for now");
1424 memref::GetGlobalOp getGlobal =
memref.getDefiningOp<memref::GetGlobalOp>();
1426 emitError(
"Only MemRefs from memref.get_global are supported");
1430 auto global = dyn_cast_if_present<memref::GlobalOp>(
1431 (*this)->getParentOfType<AIE::DeviceOp>().lookupSymbol(
1432 getGlobal.getName()));
1434 emitError(
"Global symbol not found");
1438 auto initVal = global.getInitialValue();
1440 emitError(
"Global symbol has no initial value");
1444 auto data = dyn_cast<DenseIntElementsAttr>(*initVal);
1446 emitError(
"Global symbol initial value is not a dense int array");
1457AIE::DMABDOp AIEX::DMAConfigureTaskOp::getFirstBd() {
1458 Region &body = getBody();
1462 auto bd_ops = body.front().getOps<AIE::DMABDOp>();
1463 if (bd_ops.empty() && body.front().getNumSuccessors() == 1) {
1467 Block &chain_entry = *body.front().getSuccessor(0);
1468 bd_ops = chain_entry.getOps<AIE::DMABDOp>();
1470 if (bd_ops.empty()) {
1473 return *bd_ops.begin();
1477AIEX::DMAConfigureTaskOp::canonicalize(AIEX::DMAConfigureTaskOp op,
1478 PatternRewriter &rewriter) {
1480 Region &body = op.getBody();
1481 bool did_rewrite =
false;
1482 for (
auto &block : body) {
1483 if (block.empty()) {
1486 auto ops_it = block.without_terminator();
1487 if (std::distance(ops_it.begin(), ops_it.end()) == 0) {
1488 rewriter.eraseOp(block.getTerminator());
1527 LogicalResult result = success();
1528 body.walk([&](AIE::DMABDOp bd) {
1531 if (failed(bd.verifyMixedSizesAndStrides())) {
1540 size_t numDims = bd.getMixedSizes().size();
1541 if (bd.getIteration()) {
1542 if (numDims + 1 > maxNDims) {
1543 bd.emitOpError() <<
"Cannot give more than "
1544 << std::to_string(maxNDims - 1)
1545 <<
" dimensions for step sizes and wraps alongside "
1546 "the iteration attribute on this tile (got "
1547 << std::to_string(numDims) <<
" dimensions).";
1552 auto isConstant = [](OpFoldResult v) {
1553 return getConstantIntValue(v).has_value();
1555 if (numDims > maxNDims && llvm::all_of(bd.getMixedSizes(), isConstant) &&
1556 llvm::all_of(bd.getMixedStrides(), isConstant))
1558 if (numDims > maxNDims) {
1559 bd.emitOpError() <<
"Cannot give more than " << std::to_string(maxNDims)
1560 <<
" dimensions for step sizes and wraps on this tile "
1562 << std::to_string(numDims) <<
" dimensions).";
1569LogicalResult AIEX::DMAConfigureTaskOp::verify() {
1574 std::optional<int> col = getTileLike().tryGetCol();
1575 std::optional<int> row = getTileLike().tryGetRow();
1577 failed(verifyTaskBDDimensions(targetModel, *col, *row, getBody())))
1579 Region &body = getBody();
1586 LogicalResult result = success();
1587 bool taskHasPacket = getPacket().has_value();
1588 llvm::SmallVector<AIE::DMABDOp> bds;
1589 for (
auto &block : body) {
1590 if (block.empty()) {
1593 if (block.hasNoPredecessors() && !block.isEntryBlock()) {
1594 auto error = block.getTerminator()->emitError(
1595 "Block ending in this terminator does not form a chain with "
1603 block.walk([&](AIE::DMABDOp bd) {
1604 if (bd.getBurstLength() != 0 &&
1605 !targetModel.
isShimNOCTile(getTileID().col, getTileID().row)) {
1606 bd.emitOpError(
"Burst length is only supported in Shim NOC tiles that "
1607 "are connected to the memory-mapped NOC.");
1613 bd, bd.getPacket().value_or(AIE::PacketInfoAttr()))) ||
1615 failed(bd.verifyTransferInBounds()))
1620 if (getOutOfOrder()) {
1622 if (getIssueToken()) {
1624 emitOpError(
"out_of_order channel cannot issue a completion token");
1625 err.attachNote() <<
"set issue_token = false on this out-of-order task";
1629 getOperation(), getDirection(), getOutOfOrder(),
1630 llvm::ArrayRef<AIE::DMABDOp>(bds),
1637LogicalResult AIEX::DMAStartTaskOp::verify() {
1638 if (getRepeatCountAttr() && getRepeatCountVal())
1640 "takes repeat_count or a runtime repeat count, not both");
1641 if (IntegerAttr rc = getRepeatCountAttr(); rc && rc.getInt() < 0)
1642 return emitOpError(
"repeat_count must be non-negative, got ")
1644 if (hasRepeatParameter() && (getRepeatCountAttr() || getRepeatCountVal()))
1645 return emitOpError(
"takes a repeat_parameter or a repeat count, not both");
1646 if (getRepeatParameterAttr() && getRepeatStateTableIdxAttr())
1647 return emitOpError(
"takes repeat_parameter or repeat_state_table_idx, "
1655static std::optional<int64_t> staticBdLenBytes(AIE::DMABDOp bd) {
1656 if (bd.getLengthParameterAttr() || bd.getLengthStateTableIdxAttr())
1657 return std::nullopt;
1659 if (std::optional<int32_t> len = bd.getConstantLen()) {
1661 elems =
static_cast<uint32_t
>(*len);
1665 if (bd.hasLen() || !bd.getSizes().empty() || !bd.getStrides().empty())
1666 return std::nullopt;
1667 auto buffer = dyn_cast<MemRefType>(bd.getBuffer().getType());
1668 if (!buffer || !buffer.hasStaticShape())
1669 return std::nullopt;
1670 elems = buffer.getNumElements();
1672 return llvm::checkedMul(
1673 elems,
static_cast<int64_t
>(bd.getBufferElementTypeWidthInBytes()));
1678static std::optional<int64_t>
1679staticBytesStreamed(AIEX::DMAConfigureTaskForOp task) {
1681 for (Operation *user : task.getResult().getUsers()) {
1682 auto start = dyn_cast<AIEX::DMAStartTaskOp>(user);
1684 if (isa<AIEX::DMAAwaitTaskOp, AIEX::DMAFreeTaskOp>(user))
1688 return std::nullopt;
1692 if (start->getBlock() != task->getBlock() || start.getRepeatCountAttr() ||
1693 start.getRepeatCountVal() || start.hasRepeatParameter())
1694 return std::nullopt;
1699 std::optional<int64_t> repeat =
1700 getConstantIntValue(task.getRepeatCountValue());
1701 if (!repeat || *repeat < 0)
1702 return std::nullopt;
1704 Region &body = task.getBody();
1706 return std::nullopt;
1710 std::optional<int64_t> moved = 0;
1711 for (
auto scc = llvm::scc_begin(&body.front()); !scc.isAtEnd(); ++scc) {
1713 return std::nullopt;
1714 for (AIE::DMABDOp bd : scc->front()->getOps<AIE::DMABDOp>()) {
1715 std::optional<int64_t> len = staticBdLenBytes(bd);
1716 if (!len || bd.getIteration() || bd.getNextBdId())
1717 return std::nullopt;
1718 moved = llvm::checkedAdd(*moved, *len);
1720 return std::nullopt;
1723 std::optional<int64_t> runs = llvm::checkedAdd(*repeat, int64_t{1});
1725 runs = llvm::checkedMul(*runs, starts);
1726 return runs ? llvm::checkedMul(*moved, *runs) : std::nullopt;
1733 SmallVector<int64_t> segmentBytes;
1735 int64_t objectBytes;
1738 static std::optional<BdChain>
get(ArrayRef<int64_t> segmentBytes,
1740 if (segmentBytes.size() == 1)
1742 std::optional<int64_t> objectBytes = 0;
1743 for (int64_t bytes : segmentBytes) {
1744 std::optional<int64_t> run = llvm::checkedMul(bytes, copies);
1745 if (bytes <= 0 || copies <= 0 || !run)
1746 return std::nullopt;
1747 objectBytes = llvm::checkedAdd(*objectBytes, *run);
1749 return std::nullopt;
1751 if (segmentBytes.empty())
1752 return std::nullopt;
1753 return BdChain{SmallVector<int64_t>(segmentBytes), copies, *objectBytes};
1757 bool endsOnBd(int64_t bytes)
const {
1758 int64_t rest = bytes % objectBytes;
1760 for (int64_t segment : segmentBytes) {
1761 if (rest < start + segment * copies)
1762 return (rest - start) % segment == 0;
1763 start += segment * copies;
1768 bool operator==(
const BdChain &other)
const {
1769 return segmentBytes == other.segmentBytes && copies == other.copies;
1781verifyTaskCoversWholeObjects(AIEX::DMAConfigureTaskForOp task,
1782 AIE::ObjectFifoCreateOp fifo) {
1783 if (fifo.getPadDimensions() || fifo.usesStream())
1785 std::optional<int64_t> moved = staticBytesStreamed(task);
1786 if (!moved || *moved == 0)
1788 DataLayout layout = DataLayout::closest(task);
1790 auto objectBytesOf = [&](Type fifoType) -> std::optional<int64_t> {
1791 MemRefType elem = cast<AIE::AIEObjectFifoType>(fifoType).getElementType();
1792 return llvm::checkedMul(
1793 elem.getNumElements(),
1794 static_cast<int64_t
>(layout.getTypeSize(elem.getElementType())));
1804 SmallVector<Value> linkTiles;
1805 SmallVector<BdChain> chains;
1806 if (AIE::DeviceOp device = fifo->getParentOfType<AIE::DeviceOp>())
1807 for (AIE::ObjectFifoLinkOp link : device.getOps<AIE::ObjectFifoLinkOp>()) {
1808 std::vector<AIE::ObjectFifoCreateOp> ins = link.getInputObjectFifos();
1809 std::vector<AIE::ObjectFifoCreateOp> outs = link.getOutputObjectFifos();
1810 if (!llvm::is_contained(ins, fifo) && !llvm::is_contained(outs, fifo))
1812 std::optional<Value> sharedTile = link.getOptionalSharedTile();
1813 if (ins.empty() || outs.empty() || !sharedTile)
1815 linkTiles.push_back(*sharedTile);
1816 AIE::ObjectFifoCreateOp owner = link.getPoolOwner();
1818 cast<AIE::AIEObjectFifoType>(owner.getElemType()).getElementType();
1820 static_cast<int64_t
>(layout.getTypeSize(pool.getElementType()));
1821 SmallVector<std::pair<int64_t, int64_t>> segments =
1822 link.getPoolSegments();
1823 if (fifo != owner && (link.isJoin() || link.isDistribute())) {
1824 std::vector<AIE::ObjectFifoCreateOp> &many = link.isJoin() ? ins : outs;
1825 segments = {segments[llvm::find(many, fifo) - many.begin()]};
1827 SmallVector<int64_t> segmentBytes;
1828 for (
auto [offset, size] : segments) {
1829 std::optional<int64_t> bytes = llvm::checkedMul(size, width);
1832 segmentBytes.push_back(*bytes);
1834 int64_t copies = llvm::is_contained(outs, fifo)
1835 ? link.getRepeatCount().value_or(1)
1837 std::optional<BdChain> chain;
1838 if (segmentBytes.size() == segments.size())
1839 chain = BdChain::get(segmentBytes, copies);
1840 if (chain && !llvm::is_contained(chains, *chain))
1841 chains.push_back(*chain);
1848 auto addChain = [&](Value tile, Type fifoType) {
1849 if (llvm::is_contained(linkTiles, tile))
1851 std::optional<BdChain> chain;
1852 if (std::optional<int64_t> bytes = objectBytesOf(fifoType))
1853 chain = BdChain::get({*bytes}, 1);
1854 if (chain && !llvm::is_contained(chains, *chain))
1855 chains.push_back(*chain);
1858 dyn_cast_or_null<AIE::TileLike>(fifo.getProducerTile().getDefiningOp());
1861 if (!producer.isShimTile())
1862 addChain(fifo.getProducerTile(), fifo.getElemType());
1863 for (Value consumer : fifo.getConsumerTiles()) {
1864 auto tile = dyn_cast_or_null<AIE::TileLike>(consumer.getDefiningOp());
1865 if (!tile || !tile.isShimTile())
1866 addChain(consumer, fifo.getConsumerElemTypeOrDefault());
1872 auto sequence = task->getParentOfType<AIE::RuntimeSequenceOp>();
1873 if (!sequence || task->getBlock() != &sequence.getBody().front())
1878 if (llvm::any_of(task->getBlock()->getOps<AIEX::DmaChannelResetForOp>(),
1879 [&](AIEX::DmaChannelResetForOp reset) {
1880 return reset.getObjfifoAttr() == task.getAlloc();
1886 for (Operation *prev = task->getPrevNode(); prev; prev = prev->getPrevNode())
1887 if (
auto other = dyn_cast<AIEX::DMAConfigureTaskForOp>(prev);
1888 other && other.getAlloc() == task.getAlloc() &&
1889 staticBytesStreamed(other) != 0)
1891 std::optional<int64_t> total = 0;
1892 int64_t sharing = 0;
1893 WalkResult walk = sequence.walk([&](Operation *op) {
1896 if (
auto memcpy = dyn_cast<AIEX::NpuDmaMemcpyNdOp>(op))
1897 return memcpy.getMetadata() == task.getAlloc() ? WalkResult::interrupt()
1898 : WalkResult::advance();
1899 if (
auto chain = dyn_cast<AIEX::DMAStartBdChainForOp>(op))
1900 return chain.getAllocAttr() == task.getAlloc() ? WalkResult::interrupt()
1901 : WalkResult::advance();
1902 auto other = dyn_cast<AIEX::DMAConfigureTaskForOp>(op);
1903 if (!other || other.getAlloc() != task.getAlloc())
1904 return WalkResult::advance();
1905 std::optional<int64_t> bytes = staticBytesStreamed(other);
1908 return WalkResult::advance();
1909 if (!bytes || other->getBlock() != task->getBlock())
1910 return WalkResult::interrupt();
1911 total = llvm::checkedAdd(*total, *bytes);
1913 return WalkResult::interrupt();
1915 return WalkResult::advance();
1917 if (walk.wasInterrupted())
1919 const BdChain *partial = llvm::find_if(
1920 chains, [&](
const BdChain &chain) {
return !chain.endsOnBd(*total); });
1921 if (partial == chains.end())
1924 InFlightDiagnostic error =
1925 task.emitOpError() <<
"moves " << *moved <<
" bytes through objectFIFO @"
1926 << fifo.getSymName() <<
", which";
1928 error <<
", with this runtime sequence's other transfers through it ("
1929 << *total <<
" bytes in all),";
1930 error <<
" is not a whole number of its " << partial->objectBytes
1932 if (partial->segmentBytes.size() == 1)
1933 return error <<
"; the far end's buffer descriptor is sized by the "
1934 "object, so a partial one never completes";
1935 std::string segments;
1936 llvm::raw_string_ostream os(segments);
1937 llvm::interleaveComma(partial->segmentBytes, os);
1938 error <<
" and doesn't end on a segment boundary (segments of " << segments
1940 if (partial->copies > 1)
1941 error <<
", each sent " << partial->copies <<
" times in a row";
1942 return error <<
"); the far end's buffer descriptors are sized by the "
1943 "segments, so a partial one never completes";
1949LogicalResult AIEX::DMAConfigureTaskForOp::verifySymbolUses(
1950 SymbolTableCollection &symbolTable) {
1951 AIE::DeviceOp dev = getOperation()->getParentOfType<AIE::DeviceOp>();
1955 symbolTable.lookupSymbolIn(dev, getAlloc().getRootReference());
1957 return emitOpError() << getAlloc() <<
" does not name a symbol";
1963 LogicalResult named =
1964 TypeSwitch<Operation *, LogicalResult>(target)
1965 .Case([&](AIE::ShimDMAAllocationOp alloc) {
1966 tileValue = alloc.getTile();
1969 .Case([&](AIE::RouteEndpointOp endpoint) -> LogicalResult {
1970 tileValue = endpoint.getTile();
1971 if (endpoint.getBundle() == AIE::WireBundle::DMA)
1973 return emitOpError() << getAlloc() <<
" names a "
1974 << stringifyWireBundle(endpoint.getBundle())
1975 <<
" port, not a DMA channel";
1977 .Case([&](AIE::ObjectFifoCreateOp fifo) {
1978 return verifyTaskCoversWholeObjects(*
this, fifo);
1980 .Default([&](Operation *) -> LogicalResult {
1981 return emitOpError() << getAlloc()
1982 <<
" must name an aie.shim_dma_allocation, an "
1983 "aie.route_endpoint or an aie.objectfifo";
1989 auto tile = tileValue ? tileValue.getDefiningOp<AIE::TileOp>() : nullptr;
1993 return verifyTaskBDDimensions(targetModel, tile.getCol(), tile.getRow(),
2001AIE::BDChainOp AIEX::DMAStartBdChainOp::getBDChainOp() {
2002 AIE::DeviceOp device = (*this)->getParentOfType<AIE::DeviceOp>();
2003 AIE::BDChainOp chain = device.lookupSymbol<AIE::BDChainOp>(getSymbol());
2007LogicalResult AIEX::DMAStartBdChainOp::verify() {
2008 AIE::BDChainOp chain = getBDChainOp();
2010 return emitOpError(
"symbol does not reference valid BD chain");
2013 auto actualArgTypes = getArgs().getTypes();
2014 auto expectedArgTypes = chain.getRegion().getArgumentTypes();
2015 if (actualArgTypes.size() != expectedArgTypes.size()) {
2016 return emitOpError(
"Number of arguments mismatches.");
2018 for (
unsigned i = 0, n = expectedArgTypes.size(); i < n; i++) {
2019 if (actualArgTypes[i] != expectedArgTypes[i]) {
2020 return emitOpError(
"Argument ") << (i + 1) <<
" types mismatch: "
2021 <<
"expected " << expectedArgTypes[i]
2022 <<
" but got " << actualArgTypes[i];
2032uint32_t AIEX::NpuControlPacketOp::getRowFromAddr() {
2034 uint32_t addr = getAddress();
2035 uint32_t rowInt = (addr >> targetModel.
getRowShift()) & 0x1f;
2039uint32_t AIEX::NpuControlPacketOp::getColumnFromAddr() {
2041 uint32_t addr = getAddress();
2050LogicalResult AIEX::SetLockOp::verify() {
2054 return emitOpError(
"SetLockOp is not supported on AIE1.");
2056 if (std::optional<int64_t> value = getConstantIntValue(getValue())) {
2058 return emitOpError(
"Lock value must be non-negative");
2060 return emitOpError(
"Lock value exceeds the maximum value of " +
2064 auto lockOp = getLockOp();
2065 auto lockIDOpt = getLockOp().getLockID();
2074 std::optional<int> col = lockOp.getTileLike().tryGetCol();
2075 std::optional<int> row = lockOp.getTileLike().tryGetRow();
2079 uint32_t lockID = lockOp.getLockIDValue();
2081 if (lockID >= targetModel.
getNumLocks(*col, *row)) {
2082 return emitOpError(
"Lock ID out of range for given tile. Max ID: " +
2083 std::to_string(targetModel.
getNumLocks(*col, *row) - 1));
2087 return emitOpError(
"Invalid lock ID and tile combination when trying to "
2088 "retrieve the local lock address.");
2098static LogicalResult verifyBdPoolPartition(Operation *op,
int col,
int row,
2099 uint32_t begin, uint32_t end) {
2101 if (begin >= end || end > numBds)
2102 return op->emitOpError()
2103 <<
"partition [" << begin <<
", " << end
2104 <<
") is not a nonempty range of the " << numBds
2105 <<
" buffer descriptors on tile (" << col <<
", " << row <<
")";
2109LogicalResult AIEX::DMABdPoolPopOp::verify() {
2110 return verifyBdPoolPartition(*
this, getColumn(), getRow(), getBdBegin(),
2114LogicalResult AIEX::DMABdPoolPushOp::verify() {
2115 return verifyBdPoolPartition(*
this, getColumn(), getRow(), getBdBegin(),
2123LogicalResult AIEX::DmaChannelResetOp::verify() {
2126 auto tile = dyn_cast_or_null<AIE::TileOp>(getTile().getDefiningOp());
2128 return emitOpError() <<
"tile operand must be produced by an aie.tile op";
2129 int col = tile.getCol();
2130 int row = tile.getRow();
2137 return emitOpError() <<
"tile (" << col <<
", " << row
2138 <<
") has no DMA channel reset (only core and mem "
2139 "tiles do; shim NOC DMA has no reset bit)";
2144 uint32_t numChannels = getDirection() == AIE::DMAChannelDir::S2MM
2146 col, row, AIE::WireBundle::DMA)
2148 col, row, AIE::WireBundle::DMA);
2149 if (getChannel() >= numChannels)
2150 return emitOpError() <<
"channel " << getChannel()
2151 <<
" out of range for this tile and direction (tile "
2153 << numChannels <<
" DMA channel(s) in this direction)";
2158LogicalResult AIEX::DmaChannelResetForOp::verify() {
2164 AIE::DeviceOp dev = getOperation()->getParentOfType<AIE::DeviceOp>();
2172 return emitOpError() <<
"is not supported on AIE1 devices (the resident "
2173 "re-arm relies on the aie2p start-queue push)";
2174 Operation *target = dev.lookupSymbol(getObjfifo());
2177 if (!isa<AIE::ObjectFifoCreateOp, AIE::RouteEndpoint,
2178 AIE::ObjectFifoRearmBindingOp>(target))
2179 return emitOpError() <<
"'" << getObjfifo()
2180 <<
"' must reference an aie.objectfifo (or its "
2181 "aie.objectfifo_rearm_binding)";
2189LogicalResult AIEX::CoreResetOp::verify() {
2195 return emitOpError(
"aiex.core_reset is not supported on AIE1.");
2197 auto tile = dyn_cast_or_null<AIE::TileOp>(getTile().getDefiningOp());
2199 return emitOpError() <<
"tile operand must be produced by an aie.tile op";
2200 int col = tile.getCol();
2201 int row = tile.getRow();
2210 return emitOpError() <<
"tile (" << col <<
", " << row
2211 <<
") has no core to reset (only core tiles have a "
2212 "CORE_CONTROL register)";
2220uint64_t AIEX::BlockFloatType::getTotalSizeInBits()
const {
2221 return getBlockSize() * getMantissaBits() + getExponentBits() +
2222 getSubtileShiftBits();
2225llvm::TypeSize AIEX::BlockFloatType::getTypeSizeInBits(
2226 const mlir::DataLayout &dataLayout,
2227 mlir::DataLayoutEntryListRef params)
const {
2228 return llvm::TypeSize::getFixed(getTotalSizeInBits());
2231uint64_t AIEX::BlockFloatType::getABIAlignment(
2232 const mlir::DataLayout &dataLayout,
2233 mlir::DataLayoutEntryListRef params)
const {
2239std::optional<AIEX::BlockFloatType::BlockFormat>
2240AIEX::BlockFloatType::getBlockFormat(StringRef blockType) {
2241 static const llvm::StringMap<AIEX::BlockFloatType::BlockFormat>
2243 {
"v8bfp16ebs8", {8, 8, 8, 0}},
2244 {
"v16bfp16ebs16", {16, 8, 8, 0}},
2247 auto it = blockFormatsMap.find(blockType);
2248 if (it != blockFormatsMap.end()) {
2252 return std::nullopt;
2256AIEX::BlockFloatType::verify(function_ref<InFlightDiagnostic()> emitError,
2257 StringRef block_type) {
2258 if (!getBlockFormat(block_type))
2259 return emitError() <<
"Invalid block type: " << block_type
2260 <<
". Known types are: v8bfp16ebs8, v16bfp16ebs16.";
2269AIE::DeviceOp AIEX::ConfigureOp::getReferencedDeviceOp() {
2270 ModuleOp moduleOp = this->getOperation()->getParentOfType<ModuleOp>();
2272 emitError(
"aiex.configure must be inside of a module");
2275 Operation *maybeReferencedDevice =
2276 SymbolTable::lookupSymbolIn(moduleOp.getOperation(), getSymbolAttr());
2277 if (!maybeReferencedDevice) {
2278 emitError(
"No such device: '") << getSymbolAttr() <<
"'";
2281 AIE::DeviceOp referencedDevice =
2282 llvm::dyn_cast<AIE::DeviceOp>(maybeReferencedDevice);
2283 if (!referencedDevice) {
2284 emitError(
"Not a device: '") << getSymbolAttr() <<
"'";
2287 return referencedDevice;
2294LogicalResult AIEX::ReadScratchpadParameterOp::verify() {
2295 auto device = (*this)->getParentOfType<AIE::DeviceOp>();
2297 return emitOpError(
"must be inside an aie.device");
2299 if (!(*this)->getParentOfType<AIE::CoreOp>()) {
2300 return emitOpError(
"must be inside an aie.core");
2302 auto moduleOp = (*this)->getParentOfType<ModuleOp>();
2304 !moduleOp.lookupSymbol<AIEX::ScratchpadParameterOp>(getParameter())) {
2305 return emitOpError(
"references unknown parameter '")
2307 <<
"' (aiex.scratchpad_parameter ops are declared at module scope)";
2309 if (getResult().getType().isF32()) {
2311 "f32 parameters are not supported: the scratchpad encoding zeroes "
2312 "the top 2 bits, which clobbers the sign bit and top exponent bit "
2313 "of an f32. Use bf16 or an integer type up to i32 instead.");
2318LogicalResult AIEX::WaitScratchpadParametersOp::verify() {
2319 if (!(*this)->getParentOfType<AIE::CoreOp>()) {
2320 return emitOpError(
"must be inside an aie.core");
2325LogicalResult AIEX::ConfigureOp::verify() {
2326 AIE::DeviceOp parentDev = getOperation()->getParentOfType<AIE::DeviceOp>();
2327 AIE::DeviceOp referencedDev = getReferencedDeviceOp();
2328 if (!referencedDev) {
2331 if (parentDev.getDevice() != referencedDev.getDevice()) {
2332 emitError(
"Device types do not match: '")
2333 << AIE::stringifyAIEDevice(parentDev.getDevice()) <<
"' vs. '"
2334 << AIE::stringifyAIEDevice(referencedDev.getDevice()) <<
"'";
2344AIE::DeviceOp AIEX::RunOp::getCalleeDeviceOp() {
2345 AIEX::ConfigureOp configureOp =
2346 getOperation()->getParentOfType<AIEX::ConfigureOp>();
2350 AIE::DeviceOp referencedDevice = configureOp.getReferencedDeviceOp();
2351 return referencedDevice;
2354AIE::RuntimeSequenceOp
2356 AIEX::ConfigureOp configureOp =
2357 getOperation()->getParentOfType<AIEX::ConfigureOp>();
2361 AIE::DeviceOp referencedDevice = configureOp.getReferencedDeviceOp();
2362 if (!referencedDevice) {
2366 StringAttr name = getRuntimeSequenceSymbolAttr().getAttr();
2370 return tables->
lookupIn<AIE::RuntimeSequenceOp>(referencedDevice, name);
2377LogicalResult AIEX::NpuLoadPdiOp::canonicalize(AIEX::NpuLoadPdiOp op,
2378 PatternRewriter &rewriter) {
2380 Operation *nextOp = op->getNextNode();
2385 auto nextLoadPdi = dyn_cast<AIEX::NpuLoadPdiOp>(nextOp);
2390 if (op.getDeviceRefAttr() == nextLoadPdi.getDeviceRefAttr() &&
2391 op.getId() == nextLoadPdi.getId() &&
2392 op.getSize() == nextLoadPdi.getSize() &&
2393 op.getAddress() == nextLoadPdi.getAddress()) {
2395 rewriter.eraseOp(op);
2406static bool writesConfig(Operation *op) {
2407 return isa<AIEX::NpuLoadPdiOp, AIEX::NpuWriteConfigOp,
2408 AIEX::NpuWriteTeardownOp>(op);
2415static Operation *findConfigOpBefore(Operation *op,
2416 function_ref<
bool(Operation *)> visit) {
2417 for (Operation *prev = op->getPrevNode(); prev; prev = prev->getPrevNode())
2418 if (writesConfig(prev) && visit(prev))
2420 for (Operation &next : llvm::reverse(*op->getBlock())) {
2423 if (writesConfig(&next) && visit(&next))
2426 return visit(op) ? op :
nullptr;
2431static Operation *precedingReset(Operation *op) {
2432 return findConfigOpBefore(op, [&](Operation *prev) {
2433 return prev != op &&
2434 isa<AIEX::NpuLoadPdiOp, AIEX::NpuWriteTeardownOp>(prev);
2438static bool loadsSamePdi(AIEX::NpuLoadPdiOp a, AIEX::NpuLoadPdiOp b) {
2439 return a.getDeviceRefAttr() == b.getDeviceRefAttr() &&
2440 a.getId() == b.getId() && a.getSize() == b.getSize() &&
2441 a.getAddress() == b.getAddress();
2444LogicalResult AIEX::NpuWriteConfigOp::verify() {
2445 Operation *found = precedingReset(*
this);
2447 return emitOpError(
"must follow the load_pdi that resets the array");
2448 auto reset = dyn_cast<AIEX::NpuLoadPdiOp>(found);
2453 bool tornDown =
true;
2454 Operation *later = reset;
2456 cast<AIEX::NpuLoadPdiOp>(findConfigOpBefore(reset, [&](Operation *op) {
2457 if (isa<AIEX::NpuWriteConfigOp>(op) &&
2458 !isa<AIEX::NpuWriteTeardownOp>(later))
2461 return isa<AIEX::NpuLoadPdiOp>(op);
2463 if (!loadsSamePdi(before, reset) || tornDown)
2465 InFlightDiagnostic diag = emitOpError(
2466 "follows a load_pdi the firmware skips, as it loads the PDI loaded "
2467 "just before it, so the array is not reset");
2468 diag.attachNote(before.getLoc()) <<
"the PDI is loaded here";
2473AIEX::NpuLoadPdiOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2474 FlatSymbolRefAttr ref = getDeviceRefAttr();
2477 auto device = (*this)->getParentOfType<AIE::DeviceOp>();
2478 Operation *scope = device ? device->getParentOp()
2479 : SymbolTable::getNearestSymbolTable(*
this);
2480 if (!isa_and_present<AIE::DeviceOp>(symbolTable.lookupSymbolIn(scope, ref)))
2481 return emitOpError() << ref <<
" is not a device";
2486AIEX::NpuWriteConfigOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2487 Operation *scope = (*this)->getParentOfType<AIE::DeviceOp>()->getParentOp();
2488 if (!isa_and_present<AIE::DeviceOp>(
2489 symbolTable.lookupSymbolIn(scope, getDeviceRefAttr())))
2490 return emitOpError() << getDeviceRefAttr() <<
" is not a device";
2491 auto reset = dyn_cast_or_null<AIEX::NpuLoadPdiOp>(precedingReset(*
this));
2492 FlatSymbolRefAttr resetRef = reset ? reset.getDeviceRefAttr() :
nullptr;
2493 auto empty = resetRef ? dyn_cast_or_null<AIE::DeviceOp>(
2494 symbolTable.lookupSymbolIn(scope, resetRef))
2500 !llvm::all_of(empty.getBody()->without_terminator(), [](Operation &op) {
2501 return isa<AIE::RuntimeSequenceOp>(op) &&
2502 llvm::all_of(op.getRegion(0),
2503 [](Block &block) { return block.empty(); });
2505 return emitOpError(
"must follow a load_pdi of an empty device, which "
2506 "resets the array");
2514LogicalResult AIEX::NpuWriteTeardownOp::verify() {
2515 Operation *prev = (*this)->getPrevNode();
2516 while (prev && !writesConfig(prev))
2517 prev = prev->getPrevNode();
2518 auto config = dyn_cast_or_null<AIEX::NpuWriteConfigOp>(prev);
2519 if (!config || config.getDeviceRefAttr() != getDeviceRefAttr())
2520 return emitOpError(
"must follow the write_config of ")
2521 << getDeviceRefAttr() <<
", with no load_pdi between";
2526AIEX::NpuWriteTeardownOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2527 Operation *scope = (*this)->getParentOfType<AIE::DeviceOp>()->getParentOp();
2528 auto device = dyn_cast_or_null<AIE::DeviceOp>(
2529 symbolTable.lookupSymbolIn(scope, getDeviceRefAttr()));
2531 return emitOpError() << getDeviceRefAttr() <<
" is not a device";
2535 InFlightDiagnostic diag =
2536 emitOpError() << getDeviceRefAttr()
2537 <<
" starts a shim DMA channel, which teardown cannot stop";
2538 diag.attachNote(shimStart->getLoc()) <<
"started here";
2543 Operation *found =
nullptr;
2544 device.walk([&](Operation *op) {
2545 if (!isa<AIE::DMAStartOp, AIE::DMAOp>(op) ||
2546 !isa<AIE::ShimDMAOp>(op->getParentOp()))
2547 return WalkResult::advance();
2549 return WalkResult::interrupt();
bool isCoreTile(int col, int row) const
Return true if the given tile is a Core tile.
virtual uint64_t getDmaBdMaxLen(AIETileType tileType) const =0
Return the maximum transfer length, measured in address-generation granules (see getAddressGenGranula...
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.
virtual uint32_t getDmaBdWrapBits(AIETileType tileType) const =0
Return the bit width of the wrap (size) field in a DMA buffer descriptor for the given tile type.
virtual uint32_t getLocalMemorySize() const =0
Return the size (in bytes) of the local data memory of a core.
virtual uint32_t getNumLocks(AIETileType tileType) const =0
Return the number of lock objects for a given tile type.
virtual uint32_t getDmaBdIterBits(AIETileType tileType) const =0
Return the bit width of the iteration (repeat) wrap/stride fields in a DMA buffer descriptor for the ...
virtual uint32_t getBDMaxDims(AIETileType tileType) const =0
Return the maximum number of addressing dimensions (wraps/strides) a single buffer descriptor support...
virtual std::vector< std::pair< uint32_t, uint32_t > > getShimBurstEncodingsAndLengths() const =0
virtual uint32_t getDmaBdStepBits(AIETileType tileType) const =0
Return the bit width of the step (stride) field in a DMA buffer descriptor for the given tile type.
virtual std::optional< uint32_t > getLocalLockAddress(uint32_t lockId, TileID tile) const =0
virtual AIETileType getTileType(int col, int row) const =0
Return the tile type for the given tile coordinates.
virtual uint32_t getMaxRepeatCount() const =0
Return the largest DMA task repeat count (unsupported = 0).
bool isShimNOCTile(int col, int row) const
Return true if the given tile is a ShimNOC tile.
virtual uint32_t getNumBDs(AIETileType tileType) const =0
Return the number of buffer descriptors for a given tile type.
virtual uint32_t getMaxLockValue() const =0
Return the maximum value that can be stored in a lock register.
virtual uint32_t getMaxOutOfOrderId() const =0
Return the largest out-of-order BD id (unsupported = 0).
virtual uint32_t getColumnShift() const =0
virtual uint32_t getNumDestSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of destinations of connections inside a switchbox.
virtual uint32_t getRowShift() const =0
virtual uint32_t getAddressGenGranularity() const =0
Return the data bus width of the device.
virtual uint32_t getMemTileSize() const =0
Return the size (in bytes) of a MemTile.
virtual uint32_t getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
OpTy lookupIn(mlir::Operation *symbolTableOp, mlir::StringAttr name)
mlir::Operation * lookup(mlir::StringAttr name) const
const V & get(const std::map< K, V > &pa, K k)
mlir::Operation * findShimDmaStart(AIE::DeviceOp device)
bool isContiguousTransfer(llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
void encodeHardwareStridesWraps(Policy &p, uint64_t elemWidth, uint32_t addressGranularity, typename Policy::V inputSizes[4], typename Policy::V inputStrides[4], typename Policy::V sizes[4], typename Policy::V strides[4])
mlir::LogicalResult verifyStridesWraps(mlir::Operation *forOp, llvm::function_ref< mlir::InFlightDiagnostic()> emitError, mlir::BaseMemRefType referencedBufType, int tileCol, int tileRow, llvm::SmallVector< int64_t, 4 > inputSizes, llvm::SmallVector< int64_t, 4 > inputStrides, llvm::SmallVector< int64_t, 4 > hardwareSizes, llvm::SmallVector< int64_t, 4 > hardwareStrides, bool skipTransformationChecks=false)
std::optional< uint64_t > getConstantInt64Operand(mlir::Value v)
bool isDecomposableNdDmaPattern(mlir::Operation *forOp, mlir::BaseMemRefType referencedBufType, const xilinx::AIE::AIETargetModel &targetModel, int tileCol, int tileRow, llvm::ArrayRef< int64_t > offsetsInnermostFirst, llvm::ArrayRef< int64_t > sizesInnermostFirst, llvm::ArrayRef< int64_t > stridesInnermostFirst)
True when the pattern is fully constant, non-contiguous, currently illegal, and decomposeNdDmaPattern...
std::optional< uint32_t > getConstantIntOperand(mlir::Value v)
mlir::Value createConstantArgPlus(mlir::OpBuilder &builder, mlir::Location loc, uint64_t value)
void getHardwareStridesWraps(const AIE::AIETargetModel &targetModel, mlir::Operation *op, mlir::BaseMemRefType referencedBufType, llvm::SmallVector< int64_t, 4 > inputSizes, llvm::SmallVector< int64_t, 4 > inputStrides, llvm::SmallVector< int64_t, 4 > &sizes, llvm::SmallVector< int64_t, 4 > &strides)
bool isLinearModeTransfer(const AIE::AIETargetModel &targetModel, int col, int row, llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
bool isConstMultipleOfGranule(int64_t value, uint64_t elemWidth, uint32_t addressGranularity)
bool isLinearTransfer(llvm::ArrayRef< int64_t > sizes, llvm::ArrayRef< int64_t > strides)
mlir::LogicalResult verifyConstBdRealizability(mlir::Operation *op, llvm::ArrayRef< mlir::OpFoldResult > sizes, llvm::ArrayRef< mlir::OpFoldResult > strides, uint64_t elemWidth, uint32_t gran)
mlir::Value createConstantI32(mlir::OpBuilder &builder, mlir::Location loc, uint32_t value)
void printTypedDynamicIndexList(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange values, llvm::ArrayRef< int64_t > integers, mlir::TypeRange types)
mlir::LogicalResult verifyLengthParameterTile(mlir::Operation *op, const AIETargetModel &targetModel, int col, int row)
mlir::LogicalResult verifyAssignedPacket(mlir::Operation *op, PacketInfoAttr packet, llvm::StringRef what="packet")
mlir::ParseResult parseTypedDynamicIndexList(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &values, mlir::DenseI64ArrayAttr &integers, llvm::SmallVectorImpl< mlir::Type > &types)
mlir::Operation * lookupNamedOpIn(mlir::Operation *symbolTableOp, mlir::StringAttr name)
mlir::LogicalResult verifyDMABDOutOfOrderId(DMABDOp bd, bool packetEnabledByContext=false)
mlir::LogicalResult verifyOutOfOrderChannel(mlir::Operation *op, DMAChannelDir dir, bool outOfOrder, llvm::ArrayRef< DMABDOp > bds, bool packetEnabledByContext=false)
mlir::LogicalResult verifyLengthParameter(mlir::Operation *op, std::optional< int64_t > lengthUnit, mlir::BaseMemRefType buffer, std::optional< int64_t > lenElems, bool contiguous, llvm::ArrayRef< int64_t > innerSizes)
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 verifyDMABDIteration(DMABDOp bd, AIETileType tileType)