14#include "mlir/Dialect/Func/IR/FuncOps.h"
15#include "mlir/Dialect/MemRef/IR/MemRef.h"
16#include "mlir/IR/DialectImplementation.h"
17#include "mlir/IR/OpDefinition.h"
18#include "mlir/IR/SymbolTable.h"
19#include "mlir/Interfaces/FoldInterfaces.h"
20#include "mlir/Transforms/InliningUtils.h"
22#include "llvm/ADT/SmallSet.h"
23#include "llvm/ADT/TypeSwitch.h"
30#include "aie/Dialect/AIE/IR/AIEDialect.cpp.inc"
32#define GET_TYPEDEF_CLASSES
33#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
37struct AIEInlinerInterface : DialectInlinerInterface {
38 using DialectInlinerInterface::DialectInlinerInterface;
41 bool isLegalToInline(Region *dest, Region *
src,
bool wouldBeCloned,
42 IRMapping &valueMapping)
const final override {
48 bool isLegalToInline(Operation *op, Region *,
bool wouldBeCloned,
49 IRMapping &)
const final override {
55 void handleTerminator(Operation *op, Block *newDest)
const final override {}
59 void handleTerminator(Operation *op,
60 ValueRange valuesToRepl)
const final override {}
63struct AIEDialectFoldInterface : DialectFoldInterface {
64 using DialectFoldInterface::DialectFoldInterface;
69 bool shouldMaterializeInto(Region *region)
const final override {
71 return isa<CoreOp>(region->getParentOp());
77void AIEDialect::initialize() {
79#define GET_TYPEDEF_LIST
80#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
83#define GET_ATTRDEF_LIST
84#include "aie/Dialect/AIE/IR/AIEAttrs.cpp.inc"
88#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
90 addInterfaces<AIEInlinerInterface, AIEDialectFoldInterface>();
97static std::pair<uint32_t, uint32_t>
99 uint32_t burstLength) {
101 std::vector<std::pair<uint32_t, uint32_t>> bel =
106 if (burstLength == 0) {
107 return *std::max_element(
108 bel.begin(), bel.end(),
109 [](
auto pair1,
auto pair2) { return pair1.second < pair2.second; });
114 return *std::find_if(bel.begin(), bel.end(),
115 [=](
auto p) { return p.second == burstLength; });
119 uint32_t burstLength) {
121 return getShimBurstLength(tm, burstLength).second;
125 uint32_t burstLength) {
127 return getShimBurstLength(tm, burstLength).first;
132 std::array<unsigned, 3> maxRanks = op.getArrayAttrMaxRanks();
133 if (!(op.getMixedOffsets().size() == 1 && maxRanks[0] == 1) &&
134 op.getMixedOffsets().size() != op.getMixedSizes().size())
135 return op->emitError(
136 "expected mixed offsets rank to match mixed sizes rank (")
137 << op.getMixedOffsets().size() <<
" vs " << op.getMixedSizes().size()
138 <<
") so the rank of the result type is well-formed.";
139 if (failed(verifyListOfOperandsOrIntegers(
140 op,
"offset", maxRanks[0], op.getStaticOffsets(), op.getOffsets())))
142 if (failed(verifyListOfOperandsOrIntegers(
143 op,
"size", maxRanks[1], op.getStaticSizes(), op.getSizes())))
145 if (failed(verifyListOfOperandsOrIntegers(
146 op,
"stride", maxRanks[2], op.getStaticStrides(), op.getStrides())))
148 for (int64_t offset : op.getStaticOffsets())
149 if (offset < 0 && !ShapedType::isDynamic(offset))
150 return op->emitError(
"expected offsets to be non-negative, but got ")
152 for (int64_t size : op.getStaticSizes())
153 if (size < 0 && !ShapedType::isDynamic(size))
154 return op->emitError(
"expected sizes to be non-negative, but got ")
177 if (
auto t = dyn_cast<AIETarget>(op))
178 return t.getTargetModel();
179 if (
auto t = op->getParentOfType<AIETarget>())
180 return t.getTargetModel();
189 case AIEDevice::xcvc1902:
191 case AIEDevice::xcve2302:
193 case AIEDevice::xcve2802:
195 case AIEDevice::npu1:
197 case AIEDevice::npu1_1col:
199 case AIEDevice::npu1_2col:
201 case AIEDevice::npu1_3col:
203 case AIEDevice::npu2:
205 case AIEDevice::npu2_1col:
206 return NPU2model1col;
207 case AIEDevice::npu2_2col:
208 return NPU2model2col;
209 case AIEDevice::npu2_3col:
210 return NPU2model3col;
211 case AIEDevice::npu2_4col:
212 return NPU2model4col;
213 case AIEDevice::npu2_5col:
214 return NPU2model5col;
215 case AIEDevice::npu2_6col:
216 return NPU2model6col;
217 case AIEDevice::npu2_7col:
218 return NPU2model7col;
225static TileElement getParentTileElement(Operation *op) {
226 auto *parent = op->getParentOp();
227 while (!llvm::isa_and_nonnull<DeviceOp, ModuleOp>(parent)) {
228 if (
auto element = llvm::dyn_cast<TileElement>(parent))
230 parent = parent->getParentOp();
232 return llvm::dyn_cast<TileElement>(parent);
236static int64_t getDimsMaxIdx(ArrayRef<BDDimLayoutAttr> dims) {
238 for (BDDimLayoutAttr dim : dims) {
239 maxIdx += dim.getStride() * (dim.getSize() - 1);
246struct UsesAreAccessible {
247 static LogicalResult verifyTrait(Operation *op) {
248 auto thisElement = cast<TileElement>(op);
252 if (!isa<TileOp>(thisElement.getTile().getDefiningOp()))
255 auto thisID = thisElement.getTileID();
256 auto users = op->getResult(0).getUsers();
258 for (
auto *user : users) {
261 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>(user->getParentOp())) {
270 TileElement element = llvm::dyn_cast<TileElement>(user);
272 element = getParentTileElement(user);
276 return op->emitOpError(
"is accessed outside of a tile")
277 .attachNote(user->getLoc())
280 auto tileID = element.getTileID();
281 if (!targetModel.isLegalMemAffinity(tileID.col, tileID.row, thisID.col,
283 return (op->emitOpError(
"in Column ")
284 << thisID.col <<
" and Row " << thisID.row
285 <<
" is accessed from an unreachable tile in Column "
286 << tileID.col <<
" and Row " << tileID.row)
287 .attachNote(user->getLoc())
299template <
typename... TerminatorOpTypes>
302 for (
auto ®ion : op->getRegions()) {
303 for (
auto &block : region) {
304 if (!block.empty()) {
305 if (Operation *operation = &block.back();
306 !llvm::isa_and_nonnull<TerminatorOpTypes...>(operation))
307 return operation->emitOpError(
"is not an allowed terminator")
308 .attachNote(op->getLoc())
309 .append(
"in this context: ");
319template <
typename ConcreteType>
321 auto element = cast<ConcreteType>(op);
324 TileLike tile = element.getTileLike();
326 return op->emitOpError(
"tile must implement TileLike interface");
328 int bdMax = targetModel.getNumBDs(tile.getTileType());
331 for (
auto &block : element.getBody()) {
332 auto bdOps = llvm::to_vector_of<DMABDOp>(block.template getOps<DMABDOp>());
339 if (bdNum >= bdMax) {
340 return (op->emitOpError(
"has more than ") << bdMax <<
" blocks")
341 .attachNote(bdOps.front().getLoc())
342 .append(
"no space for this BD");
347 if (bdOps.size() != 1) {
348 return (op->emitOpError(
"BD block must have exactly one DMABDOp, found ")
350 .attachNote(block.front().getLoc())
351 .append(
"in this BD block");
356 llvm::to_vector_of<UseLockOp>(block.template getOps<UseLockOp>());
357 int acquireCount = 0;
358 int releaseCount = 0;
359 for (
auto useLock : useLockOps) {
360 if (useLock.acquire() || useLock.acquireGE())
362 else if (useLock.release())
366 if (acquireCount > 1) {
367 return (op->emitOpError(
368 "BD block must have at most one acquire UseLockOp, found ")
370 .attachNote(block.front().getLoc())
371 .append(
"in this BD block");
373 if (releaseCount > 1) {
374 return (op->emitOpError(
375 "BD block must have at most one release UseLockOp, found ")
377 .attachNote(block.front().getLoc())
378 .append(
"in this BD block");
386template <
typename ConcreteType>
388 DenseSet<DMAChannel> inputChannels;
389 DenseSet<DMAChannel> outputChannels;
390 auto element = cast<ConcreteType>(op);
391 Region &body = element.getBody();
393 return op->emitOpError(
"should have non-empty body");
394 for (
auto &bodyOp : body.getOps()) {
396 if (
auto dmaStart = dyn_cast<DMAStartOp>(bodyOp)) {
397 DMAChannel dmaChan = {dmaStart.getChannelDir(),
398 dmaStart.getChannelIndex()};
401 if (dmaChan.direction == DMAChannelDir::S2MM)
402 inputChannels.insert(dmaChan);
404 outputChannels.insert(dmaChan);
408 TileLike tile = element.getTileLike();
410 return op->emitOpError(
"tile must implement TileLike interface");
412 if (inputChannels.size() > tile.getNumSourceConnections(WireBundle::DMA))
413 return op->emitOpError(
414 "uses more input channels than available on this tile");
416 if (outputChannels.size() > tile.getNumDestConnections(WireBundle::DMA))
417 return op->emitOpError(
418 "uses more output channels than available on this tile");
426LogicalResult ObjectFifoCreateOp::verify() {
427 if (isa<ArrayAttr>(getElemNumber())) {
428 if (
size_t numDepths = dyn_cast<ArrayAttr>(getElemNumber()).size();
429 numDepths != getConsumerTiles().size() + 1)
430 return emitOpError(
"does not have enough depths specified for producer "
431 "and for each consumer.");
435 auto getTileLikeFromValue = [](Value v) -> TileLike {
436 return llvm::dyn_cast<TileLike>(v.getDefiningOp());
439 TileLike producerTile = getTileLikeFromValue(getProducerTile());
441 return emitError(
"producer tile must implement TileLike interface");
444 if (producerTile.isShimTile() && !getDimensionsToStream().empty()) {
446 "`dimensionsToStream` data layout transformations are not supported "
447 "on shim tile producers");
449 for (
auto consTileVal : getConsumerTiles()) {
450 TileLike consTile = getTileLikeFromValue(consTileVal);
452 return emitError(
"consumer tile must implement TileLike interface");
453 if (consTile.isShimTile() &&
454 !getDimensionsFromStream(consTileVal).
empty()) {
456 "`dimensionsFromStreamPerConsumer` data layout transformations are "
457 "not supported on shim tile consumers");
461 if (getRepeatCount().has_value()) {
462 if (producerTile.isShimTile())
463 return emitError(
"`repeat_count` unavailable for shim tiles");
466 if (getAieStreamPort().has_value()) {
467 if (!getAieStream().has_value())
468 return emitError(
"`aie_stream` must be defined");
471 if (getAieStream().has_value()) {
472 if (getConsumerTiles().size() > 1)
473 return emitError(
"`aie_stream` can only be used in 1-to-1 object FIFOs");
475 if (!getAieStreamPort().has_value())
476 return emitError(
"`aie_stream_port` must be defined");
478 if (getAieStream().
value() == 0 || getAieStream().
value() == 2) {
479 if (producerTile.isShimTile() || producerTile.isMemTile())
481 "`aie_stream` is not available for shim and mem tiles");
483 if (getRepeatCount().has_value())
484 return emitError(
"`repeat_count` unavailable on stream end");
486 if (getInitValues().has_value())
487 return emitError(
"`init_values` unavailable on stream end");
489 if (getIterCount().has_value())
490 return emitError(
"`iter_count` unavailable on stream end");
492 if (!getDimensionsToStream().
empty())
493 return emitError(
"`dimensionsToStream` data layout transformations are "
494 "unavailable on stream end");
497 if (getAieStream().
value() == 1 || getAieStream().
value() == 2) {
498 TileLike consTile = getTileLikeFromValue(getConsumerTiles()[0]);
499 if (consTile && (consTile.isShimTile() || consTile.isMemTile()))
501 "`aie_stream` is not available for shim and mem tiles");
504 if (!getDimensionsFromStreamPerConsumer()[0].
empty())
505 return emitError(
"`dimensionsFromStreamPerConsumer` data layout "
506 "transformations are unavailable on stream end");
509 if (getInitValues().has_value()) {
510 if (producerTile.isShimTile())
511 return emitError(
"`init_values` unavailable for shim tiles");
514 if (getInitValues().has_value()) {
515 if ((
int)getInitValues().
value().size() != size())
516 return emitError(
"`init_values` does not initialize all objects");
519 if (getIterCount().has_value()) {
520 int iterCount = getIterCount().value();
521 if (iterCount < 1 || iterCount > 256)
522 return emitError(
"`iter_count` must be between 1 and 256");
525 bool hasMemTile = producerTile.isMemTile();
527 for (
auto consTileVal : getConsumerTiles()) {
528 TileLike consTile = getTileLikeFromValue(consTileVal);
529 if (consTile && consTile.isMemTile()) {
536 return emitError(
"`iter_count` is currently only supported on MemTiles");
542TileOp ObjectFifoCreateOp::getProducerTileOp() {
543 return cast<TileOp>(getProducerTile().getDefiningOp());
547ObjectFifoCreateOp::getDimensionsFromStream(Value consumerTile) {
549 for (
auto cons : getConsumerTiles()) {
550 if (cons == consumerTile)
555 return getDimensionsFromStreamPerConsumer()[dimsIndex];
559 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
560 BDDimLayoutArrayAttr &dimensions) {
561 std::vector<BDDimLayoutAttr> emptyDims = {};
562 if (parser.parseOperand(operand))
564 if (succeeded(parser.parseOptionalKeyword(
"dimensionsToStream"))) {
565 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
571 BDDimLayoutArrayAttr::get(parser.getContext(), ArrayRef(emptyDims));
577 Operation *op, Value operand,
578 BDDimLayoutArrayAttr dimensions) {
580 if (!dimensions.empty()) {
581 printer <<
" dimensionsToStream ";
582 printer.printStrippedAttrOrType(dimensions);
587 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &tiles,
588 BDDimLayoutArrayArrayAttr &dimensions) {
591 std::vector<BDDimLayoutArrayAttr> tileDims = {};
593 auto parseOneOperand = [&]() -> ParseResult {
594 if (parser.parseOperand(tiles.emplace_back(),
true)) {
600 BDDimLayoutArrayAttr dimAttr =
601 BDDimLayoutArrayAttr::get(parser.getContext(), {});
603 if (succeeded(parser.parseOptionalKeyword(
"dimensionsFromStream"))) {
605 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
610 tileDims.emplace_back(dimAttr);
614 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::None,
615 parseOneOperand,
" in operand list"))
618 dimensions = BDDimLayoutArrayArrayAttr::get(parser.getContext(), tileDims);
623 OpAsmPrinter &printer, Operation *op, OperandRange tiles,
624 BDDimLayoutArrayArrayAttr dimsPerTileAttr) {
626 for (
auto tile : tiles) {
628 if (dimsPerTileAttr && tileIdx < dimsPerTileAttr.size() &&
629 dimsPerTileAttr[tileIdx] && !dimsPerTileAttr[tileIdx].empty()) {
630 printer <<
" dimensionsFromStream ";
631 printer.printStrippedAttrOrType(dimsPerTileAttr[tileIdx]);
633 if (tileIdx < tiles.size() - 1) {
640static void printObjectFifoInitValues(OpAsmPrinter &p, ObjectFifoCreateOp op,
641 Attribute numElem, TypeAttr type,
642 Attribute initValues) {
643 if (op.getInitValues()) {
645 int depth = llvm::dyn_cast<mlir::IntegerAttr>(numElem).getInt();
646 for (
int i = 0; i < depth; i++) {
647 p.printStrippedAttrOrType(llvm::dyn_cast<mlir::ArrayAttr>(initValues)[i]);
656static ParseResult parseObjectFifoInitValues(OpAsmParser &parser,
657 Attribute numElem, TypeAttr type,
658 Attribute &initValues) {
660 if (isa<ArrayAttr>(numElem)) {
661 depth = llvm::dyn_cast<mlir::IntegerAttr>(
662 llvm::dyn_cast<mlir::ArrayAttr>(numElem)[0])
665 depth = llvm::dyn_cast<mlir::IntegerAttr>(numElem).getInt();
667 auto objfifoType = llvm::cast<AIEObjectFifoType>(type.getValue());
668 auto memrefType = llvm::cast<MemRefType>(objfifoType.getElementType());
670 if (!memrefType.hasStaticShape())
671 return parser.emitError(parser.getNameLoc())
672 <<
"type should be static shaped memref, but got " << memrefType;
674 if (parser.parseOptionalEqual())
677 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
678 if (parser.parseAttribute(initValues, tensorType))
680 for (
int i = 0; i < depth; i++) {
681 auto initialValues = llvm::dyn_cast<mlir::ArrayAttr>(initValues);
682 if ((
int)initialValues.size() != depth)
683 return parser.emitError(parser.getNameLoc())
684 <<
"initial values should initialize all objects";
685 if (!llvm::isa<ElementsAttr>(initialValues[i]))
686 return parser.emitError(parser.getNameLoc())
687 <<
"initial value should be an elements attribute";
697LogicalResult ObjectFifoAllocateOp::verify() {
698 ObjectFifoCreateOp objFifo = getObjectFifo();
700 return emitError(
"cannot retrieve associated object FIFO");
701 if (objFifo.getConsumerTiles().size() != 1)
702 return emitError(
"can only be used in 1-to-1 object FIFOs");
703 if (objFifo.getVia_DMA())
704 return emitError(
"cannot allocate a shared memory module to objectfifo "
705 "with set `via_DMA` attribute");
706 if (objFifo.getRepeatCount().has_value())
707 return emitError(
"cannot allocate a shared memory module to objectfifo "
708 "with set `repeat_count` attribute");
709 if (!objFifo.getDimensionsToStream().empty())
710 return emitError(
"cannot allocate a shared memory module to objectfifo "
711 "with set dimensions attribute");
712 if (objFifo.getAieStream().has_value())
713 return emitError(
"cannot allocate a shared memory module to objectfifo "
714 "using stream port");
718TileOp ObjectFifoAllocateOp::getDelegateTileOp() {
719 return cast<TileOp>(getDelegateTile().getDefiningOp());
722ObjectFifoCreateOp ObjectFifoAllocateOp::getObjectFifo() {
723 Operation *parent = getOperation();
724 while ((parent = parent->getParentOp())) {
725 if (parent->hasTrait<OpTrait::SymbolTable>()) {
726 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
727 isa_and_nonnull<ObjectFifoCreateOp>(st))
728 return dyn_cast<ObjectFifoCreateOp>(st);
738LogicalResult ObjectFifoLinkOp::verify() {
739 if (isJoin() && isDistribute())
740 return emitError(
"ObjectFifoLinkOp does not support 'join' and "
741 "'distribute' at the same time");
743 auto sharedTile = getOptionalSharedTile();
745 return emitError(
"ObjectFifoLinkOp must have a link point, i.e., a "
746 "shared tile between objectFifos");
748 TileLike tile = llvm::dyn_cast<TileLike>(sharedTile.value().getDefiningOp());
750 return emitError(
"shared tile must implement TileLike interface");
751 if (!tile.isMemTile()) {
752 if (isJoin() || isDistribute())
753 return emitError(
"ObjectFifoLinkOp join and distribute are "
754 "unavailable on compute or shim tiles");
758 if (getFifoIns().size() != getSrcOffsets().size())
759 return emitOpError(
"number of provided src offsets must be equal "
760 "to the number of input objectFifos");
762 if (!getDstOffsets().
empty())
763 return emitOpError(
"dst offsets should be empty for join");
765 ObjectFifoCreateOp fifoOut = getOutputObjectFifos()[0];
766 if (!fifoOut.getDimensionsToStream().empty()) {
767 int64_t maxIdx = getDimsMaxIdx(fifoOut.getDimensionsToStream());
768 int64_t minInputBufferSize = -1;
769 for (
auto lenIn : getJoinTransferLengths()) {
770 if (lenIn <= minInputBufferSize || minInputBufferSize < 0)
771 minInputBufferSize = lenIn;
773 if (minInputBufferSize <= maxIdx) {
775 <<
"specified output stride(s) and size(s) result in out "
776 "of bounds access in join input, for index "
777 << std::to_string(maxIdx) <<
" in transfer of length "
778 << std::to_string(minInputBufferSize) <<
".";
782 }
else if (isDistribute()) {
783 if (getFifoOuts().size() != getDstOffsets().size())
784 return emitOpError(
"number of provided dst offsets must be equal "
785 "to the number of output objectFifos");
787 if (!getSrcOffsets().
empty())
788 return emitOpError(
"src offsets should be empty for distribute");
790 ObjectFifoCreateOp fifoIn = getInputObjectFifos()[0];
791 if (!fifoIn.getDimensionsFromStream(sharedTile.value()).empty()) {
793 getDimsMaxIdx(fifoIn.getDimensionsFromStream(sharedTile.value()));
794 int64_t minOutputBufferSize = -1;
795 for (
auto lenOut : getDistributeTransferLengths()) {
796 if (lenOut <= minOutputBufferSize || minOutputBufferSize < 0)
797 minOutputBufferSize = lenOut;
799 if (minOutputBufferSize <= maxIdx) {
801 <<
"specified input stride(s) and size(s) result in out "
802 "of bounds access in distribute output, for index "
803 << std::to_string(maxIdx) <<
" in transfer of length "
804 << std::to_string(minOutputBufferSize) <<
".";
808 std::vector<int> repeat_counts;
809 for (
auto fifoOut : getOutputObjectFifos()) {
810 if (fifoOut.getRepeatCount().has_value()) {
811 repeat_counts.push_back(fifoOut.getRepeatCount().value());
813 repeat_counts.push_back(0);
816 for (
auto repeat : repeat_counts)
817 if (repeat_counts[0] != repeat)
818 return emitError(
"repeat counts of output object FIFOs must be equal");
821 if (!getSrcOffsets().
empty() && !getDstOffsets().
empty())
822 return emitOpError(
"all offsets should be empty if there is no "
823 "join or distribute");
829std::optional<Value> ObjectFifoLinkOp::getOptionalSharedTile() {
831 auto fifoOut = getOutputObjectFifos()[0];
832 for (
auto fifoIn : getInputObjectFifos())
833 if (fifoOut.getProducerTile() != fifoIn.getConsumerTiles()[0])
835 return {fifoOut.getProducerTile()};
838 if (isDistribute()) {
839 auto fifoIn = getInputObjectFifos()[0];
840 for (
auto fifoOut : getOutputObjectFifos())
841 if (fifoIn.getConsumerTiles()[0] != fifoOut.getProducerTile())
843 return {fifoIn.getConsumerTiles()[0]};
846 auto fifoIn = getInputObjectFifos();
847 if (
auto fifoOut = getOutputObjectFifos();
848 !fifoIn.empty() && !fifoOut.empty())
849 for (
auto consumerIn : fifoIn[0].getConsumerTiles())
850 if (consumerIn == fifoOut[0].getProducerTile())
851 return {fifoOut[0].getProducerTile()};
855std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getInputObjectFifos() {
856 std::vector<ObjectFifoCreateOp> inputObjFifos;
857 Operation *parent = getOperation();
858 while ((parent = parent->getParentOp())) {
859 if (parent->hasTrait<OpTrait::SymbolTable>()) {
860 for (
auto sym : getFifoIns()) {
861 auto name = dyn_cast<FlatSymbolRefAttr>(sym);
862 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
863 isa_and_nonnull<ObjectFifoCreateOp>(st))
864 inputObjFifos.push_back(dyn_cast<ObjectFifoCreateOp>(st));
868 return inputObjFifos;
871std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getOutputObjectFifos() {
872 std::vector<ObjectFifoCreateOp> outputObjFifos;
873 Operation *parent = getOperation();
874 while ((parent = parent->getParentOp())) {
875 if (parent->hasTrait<OpTrait::SymbolTable>()) {
876 for (
auto sym : getFifoOuts()) {
877 auto name = dyn_cast<FlatSymbolRefAttr>(sym);
878 if (
auto *st = mlir::SymbolTable::lookupSymbolIn(parent, name);
879 isa_and_nonnull<ObjectFifoCreateOp>(st))
880 outputObjFifos.push_back(dyn_cast<ObjectFifoCreateOp>(st));
884 return outputObjFifos;
887std::vector<int> ObjectFifoLinkOp::getJoinTransferLengths() {
888 std::vector<int> lengths;
891 llvm::cast<AIEObjectFifoType>(getOutputObjectFifos()[0].getElemType());
892 auto elemTypeOut = llvm::cast<MemRefType>(fifoOut.getElementType());
893 int lenOut = elemTypeOut.getNumElements();
894 for (
size_t i = 0; i < getFifoIns().size(); i++) {
896 int offset = *getConstantIntValue(getSrcOffsets()[i]);
897 if (i == getFifoIns().size() - 1)
898 len = lenOut - *getConstantIntValue(getSrcOffsets()[i]);
900 len = *getConstantIntValue(getSrcOffsets()[i + 1]) - offset;
901 lengths.push_back(len);
907std::vector<int> ObjectFifoLinkOp::getDistributeTransferLengths() {
908 std::vector<int> lengths;
909 if (isDistribute()) {
911 llvm::cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType());
912 auto elemTypeIn = llvm::cast<MemRefType>(fifoIn.getElementType());
913 int lenIn = elemTypeIn.getNumElements();
914 for (
size_t i = 0; i < getFifoOuts().size(); i++) {
915 int offset = *getConstantIntValue(getDstOffsets()[i]);
917 if (i == getFifoOuts().size() - 1)
918 len = lenIn - *getConstantIntValue(getDstOffsets()[i]);
920 len = *getConstantIntValue(getDstOffsets()[i + 1]) - offset;
921 lengths.push_back(len);
927std::optional<int> ObjectFifoLinkOp::getRepeatCount() {
928 for (
auto fifoOut : getOutputObjectFifos())
929 if (fifoOut.getRepeatCount().has_value())
930 return {fifoOut.getRepeatCount().
value()};
938TileOp ObjectFifoRegisterExternalBuffersOp::getTileOp() {
939 return cast<TileOp>(getTile().getDefiningOp());
942ObjectFifoCreateOp ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
943 Operation *parent = getOperation();
944 while ((parent = parent->getParentOp())) {
945 if (parent->hasTrait<OpTrait::SymbolTable>()) {
946 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
947 isa_and_nonnull<ObjectFifoCreateOp>(st))
948 return dyn_cast<ObjectFifoCreateOp>(st);
958LogicalResult ObjectFifoAcquireOp::verify() {
959 auto parent = getOperation()->getParentOfType<CoreOp>();
960 if (parent ==
nullptr)
961 return emitOpError(
"must be called from inside a CoreOp");
963 auto coreTile = parent.getTile();
964 auto objFifo = getObjectFifo();
966 return emitError(
"cannot retrieve associated object FIFO");
967 if (getPort() == ObjectFifoPort::Produce) {
968 if (coreTile != objFifo.getProducerTile())
969 return parent.emitOpError(
970 "producer port of objectFifo accessed by core running "
971 "on non-producer tile");
972 }
else if (getPort() == ObjectFifoPort::Consume) {
974 for (
auto consumerTile : objFifo.getConsumerTiles()) {
975 if (coreTile == consumerTile) {
981 return parent.emitOpError(
982 "consumer port of objectFifo accessed by core running "
983 "on non-consumer tile");
987 llvm::cast<AIEObjectFifoType>(getObjectFifo().getElemType())
989 auto objFifoSubviewElem =
990 llvm::cast<AIEObjectFifoSubviewType>(getResult().getType())
992 if (objFifoElem != objFifoSubviewElem)
994 "ObjectFifo element and ObjectFifoSubview element must match.\n");
999ObjectFifoCreateOp ObjectFifoAcquireOp::getObjectFifo() {
1000 Operation *parent = getOperation();
1001 while ((parent = parent->getParentOp())) {
1002 if (parent->hasTrait<OpTrait::SymbolTable>()) {
1003 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
1004 isa_and_nonnull<ObjectFifoCreateOp>(st))
1005 return dyn_cast<ObjectFifoCreateOp>(st);
1015LogicalResult ObjectFifoReleaseOp::verify() {
1016 auto parent = getOperation()->getParentOfType<CoreOp>();
1017 if (parent ==
nullptr)
1018 return emitOpError(
"must be called from inside a CoreOp");
1020 auto coreTile = parent.getTile();
1021 auto objFifo = getObjectFifo();
1023 return emitError(
"cannot retrieve associated object FIFO");
1024 if (getPort() == ObjectFifoPort::Produce) {
1025 if (coreTile != objFifo.getProducerTile())
1026 return parent.emitOpError(
1027 "producer port of objectFifo accessed by core running "
1028 "on non-producer tile");
1029 }
else if (getPort() == ObjectFifoPort::Consume) {
1031 for (
auto consumerTile : objFifo.getConsumerTiles()) {
1032 if (coreTile == consumerTile) {
1038 return parent.emitOpError(
1039 "consumer port of objectFifo accessed by core running "
1040 "on non-consumer tile");
1046ObjectFifoCreateOp ObjectFifoReleaseOp::getObjectFifo() {
1047 Operation *parent = getOperation();
1048 while ((parent = parent->getParentOp())) {
1049 if (parent->hasTrait<OpTrait::SymbolTable>()) {
1050 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
1051 isa_and_nonnull<ObjectFifoCreateOp>(st))
1052 return dyn_cast<ObjectFifoCreateOp>(st);
1062LogicalResult ObjectFifoSubviewAccessOp::verify() {
1063 if (
auto parent = getOperation()->getParentOfType<CoreOp>();
1065 return emitOpError(
"must be called from inside a CoreOp");
1067 if (
auto acqOp = getSubview().getDefiningOp<ObjectFifoAcquireOp>();
1068 getIndex() >= acqOp.acqNumber())
1069 return emitOpError(
"accessed farther than number of acquired elements "
1070 "(index out of bounds).");
1079LogicalResult ObjectFifoRegisterProcessOp::verify() {
1080 if (getProcessLength() < 1)
1081 return emitOpError(
"process length must be >= 1");
1083 if (getAcquirePattern().size() != getReleasePattern().size()) {
1086 if (getAcquirePattern().size() != getProcessLength() &&
1087 getProcessLength() != getReleasePattern().size())
1089 "Acquire and Release patterns must be of equal length, or "
1090 "longest length of one must be equal to process "
1091 "length of the other");
1097ObjectFifoCreateOp ObjectFifoRegisterProcessOp::getObjectFifo() {
1098 Operation *parent = getOperation();
1099 while ((parent = parent->getParentOp())) {
1100 if (parent->hasTrait<OpTrait::SymbolTable>()) {
1101 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
1102 isa_and_nonnull<ObjectFifoCreateOp>(st))
1103 return dyn_cast<ObjectFifoCreateOp>(st);
1113LogicalResult CascadeFlowOp::verify() {
1114 TileLike
src = getSourceTileLike();
1115 TileLike
dst = getDestTileLike();
1118 return emitOpError(
"source and dest must be tile-like operations");
1120 if (
src.isShimTile() ||
dst.isShimTile())
1121 return emitOpError(
"shimTile row has no cascade stream interface");
1122 if (
src.isMemTile() ||
dst.isMemTile())
1123 return emitOpError(
"memTile row has no cascade stream interface");
1125 std::optional<int> srcCol =
src.tryGetCol();
1126 std::optional<int> srcRow =
src.tryGetRow();
1127 std::optional<int> dstCol =
dst.tryGetCol();
1128 std::optional<int> dstRow =
dst.tryGetRow();
1130 if (srcCol && srcRow && dstCol && dstRow) {
1132 if (!t.isSouth(*srcCol, *srcRow, *dstCol, *dstRow) &&
1133 !t.isWest(*srcCol, *srcRow, *dstCol, *dstRow) &&
1134 !t.isNorth(*srcCol, *srcRow, *dstCol, *dstRow) &&
1135 !t.isEast(*srcCol, *srcRow, *dstCol, *dstRow)) {
1136 return emitOpError(
"tiles must be adjacent");
1143TileLike CascadeFlowOp::getSourceTileLike() {
1144 return dyn_cast<TileLike>(getSourceTile().getDefiningOp());
1147TileLike CascadeFlowOp::getDestTileLike() {
1148 return dyn_cast<TileLike>(getDestTile().getDefiningOp());
1151TileOp CascadeFlowOp::getSourceTileOp() {
1152 if (
auto tileOp = dyn_cast_or_null<TileOp>(getSourceTile().getDefiningOp()))
1154 llvm::report_fatal_error(
"Calling getSourceTileOp requires TileOp.");
1157TileOp CascadeFlowOp::getDestTileOp() {
1158 if (
auto tileOp = dyn_cast_or_null<TileOp>(getDestTile().getDefiningOp()))
1160 llvm::report_fatal_error(
"Calling getDestTileOp requires TileOp.");
1167LogicalResult ConfigureCascadeOp::verify() {
1168 if (!isa<TileOp>(getTile().getDefiningOp()))
1169 return emitOpError(
"requires a placed tile (aie.tile), not a logical tile");
1172 TileOp tile = cast<TileOp>(getTile().getDefiningOp());
1173 CascadeDir inputDir = getInputDir();
1174 CascadeDir outputDir = getOutputDir();
1176 if (tile.isShimTile())
1177 return emitOpError(
"shimTile row has no cascade stream interface");
1178 if (tile.isMemTile())
1179 return emitOpError(
"memTile row has no cascade stream interface");
1181 if (isa<AIE2TargetModel>(t)) {
1182 if (inputDir == CascadeDir::South || inputDir == CascadeDir::East) {
1183 return emitOpError(
"input direction of cascade must be North or West on ")
1184 << stringifyAIEArch(t.getTargetArch());
1186 if (outputDir == CascadeDir::North || outputDir == CascadeDir::West) {
1188 "output direction of cascade must be South or East on ")
1189 << stringifyAIEArch(t.getTargetArch());
1192 return emitOpError(
"cascade not supported in ")
1193 << stringifyAIEArch(t.getTargetArch());
1202LogicalResult PutCascadeOp::verify() {
1204 Type type = getCascadeValue().getType();
1205 DataLayout dataLayout = DataLayout::closest(*
this);
1206 auto bits = dataLayout.getTypeSizeInBits(type);
1207 auto archbits = targetModel.getAccumulatorCascadeSize();
1208 if (bits != archbits)
1209 return emitOpError(
"type must match architecture cascade width (")
1210 << archbits <<
" bits in "
1211 << stringifyAIEArch(targetModel.getTargetArch()) <<
")";
1219LogicalResult GetCascadeOp::verify() {
1221 Type type = getCascadeValue().getType();
1222 DataLayout dataLayout = DataLayout::closest(*
this);
1223 auto bits = dataLayout.getTypeSizeInBits(type);
1224 if (isa<AIE1TargetModel>(targetModel)) {
1226 return emitOpError(
"must be a 384-bit type");
1227 }
else if (isa<AIE2TargetModel>(targetModel)) {
1229 return emitOpError(
"must be a 512-bit type");
1231 return emitOpError(
"cascade not supported in ")
1232 << stringifyAIEArch(targetModel.getTargetArch());
1244xilinx::AIE::DeviceOp DeviceOp::getForSymbolInModule(mlir::ModuleOp module,
1245 llvm::StringRef symbol) {
1247 if (!symbol.size()) {
1251 Operation *maybeDeviceOp = mlir::SymbolTable::lookupSymbolIn(module, symbol);
1252 if (!maybeDeviceOp) {
1255 deviceOp = llvm::dyn_cast<DeviceOp>(maybeDeviceOp);
1259xilinx::AIE::DeviceOp
1260DeviceOp::getForSymbolInModuleOrError(mlir::ModuleOp module,
1261 llvm::StringRef symbol) {
1262 DeviceOp deviceOp = getForSymbolInModule(module, symbol);
1264 if (!symbol.empty()) {
1265 module.emitError("No such device: ") << symbol;
1267 module.emitError("No 'main' device in module");
1277TileOp TileElement::getTileOp() {
1278 auto element = cast<TileElement>(this->getOperation());
1279 Operation *definingOp = element.getTile().getDefiningOp();
1280 if (
auto tileOp = dyn_cast_or_null<TileOp>(definingOp))
1282 llvm::report_fatal_error(
"Calling getTileOp requires TileOp.");
1289LogicalResult LogicalTileOp::verify() {
1291 int columns = targetModel.columns();
1292 int rows = targetModel.rows();
1295 if (
auto col = getCol()) {
1296 if (*
col >= columns)
1297 return emitOpError(
"column index (")
1299 <<
") must be less than the number of columns in the device ("
1302 if (
auto row = getRow()) {
1304 return emitOpError(
"row index (")
1305 << *
row <<
") must be less than the number of rows in the device ("
1310 AIETileType tileType = getTileType();
1311 bool tileTypeExists =
false;
1312 for (
int col = 0;
col < columns && !tileTypeExists;
col++) {
1313 for (
int row = 0;
row < rows && !tileTypeExists;
row++) {
1314 if (targetModel.getTileType(
col,
row) == tileType)
1315 tileTypeExists =
true;
1318 if (!tileTypeExists) {
1319 return emitOpError(
"tile type '")
1320 << stringifyAIETileType(tileType)
1321 <<
"' does not exist on the target device";
1326 if (
auto col = tryGetCol()) {
1327 if (
auto row = tryGetRow()) {
1328 if (targetModel.getTileType(*
col, *
row) != tileType) {
1329 return emitOpError(
"declared logical tile type does not match "
1330 "the tile type at coordinates (")
1331 << *
col <<
", " << *
row <<
")";
1336 if (isShimNOCorPLTile() && getAllocationScheme())
1337 return emitOpError(
"Shim tiles cannot have an allocation scheme");
1342TileID LogicalTileOp::getCanonicalTileID() {
1346 if (getCol().has_value() && getRow().has_value()) {
1347 return {getCol().value(), getRow().value()};
1351 AIETileType tileType = getTileType();
1352 for (
int col = 0;
col < targetModel.columns();
col++) {
1353 for (
int row = 0;
row < targetModel.rows();
row++) {
1354 if (targetModel.getTileType(
col,
row) == tileType) {
1359 llvm_unreachable(
"No tile of matching tile type found in AIE device");
1362size_t LogicalTileOp::getNumSourceConnections(WireBundle bundle) {
1364 TileID tile = getCanonicalTileID();
1366 if (bundle == WireBundle::Core || bundle == WireBundle::DMA) {
1368 if (isShimNOCorPLTile())
1369 return targetModel.getNumDestShimMuxConnections(tile.col, tile.row,
1371 return targetModel.getNumDestSwitchboxConnections(tile.col, tile.row,
1377size_t LogicalTileOp::getNumDestConnections(WireBundle bundle) {
1379 TileID tile = getCanonicalTileID();
1381 if (bundle == WireBundle::Core || bundle == WireBundle::DMA) {
1383 if (isShimNOCorPLTile())
1384 return targetModel.getNumDestShimMuxConnections(tile.col, tile.row,
1386 return targetModel.getNumSourceSwitchboxConnections(tile.col, tile.row,
1392std::optional<int> LogicalTileOp::tryGetCol() {
1393 if (
auto col = getCol())
1395 return std::nullopt;
1398std::optional<int> LogicalTileOp::tryGetRow() {
1399 if (
auto row = getRow())
1401 return std::nullopt;
1408ParseResult LogicalTileOp::parse(OpAsmParser &parser, OperationState &result) {
1409 AIETileType tileType;
1410 if (parser.parseLess())
1413 StringRef tileTypeStr;
1414 if (parser.parseKeyword(&tileTypeStr))
1417 auto tileTypeOpt = symbolizeAIETileType(tileTypeStr);
1419 return parser.emitError(parser.getCurrentLocation(),
1420 "unknown logical tile type: ")
1422 tileType = *tileTypeOpt;
1424 if (parser.parseGreater())
1427 if (parser.parseLParen())
1430 std::optional<int32_t>
col;
1431 if (succeeded(parser.parseOptionalQuestion())) {
1435 if (parser.parseInteger(colVal))
1440 if (parser.parseComma())
1443 std::optional<int32_t>
row;
1444 if (succeeded(parser.parseOptionalQuestion())) {
1448 if (parser.parseInteger(rowVal))
1453 if (parser.parseRParen())
1457 if (parser.parseOptionalAttrDict(result.attributes))
1461 result.getOrAddProperties<LogicalTileOp::Properties>().tile_type =
1462 AIETileTypeAttr::get(parser.getContext(), tileType);
1464 result.getOrAddProperties<LogicalTileOp::Properties>().
col =
1465 parser.getBuilder().getI32IntegerAttr(*
col);
1467 result.getOrAddProperties<LogicalTileOp::Properties>().
row =
1468 parser.getBuilder().getI32IntegerAttr(*
row);
1471 result.addTypes(parser.getBuilder().getIndexType());
1476void LogicalTileOp::print(OpAsmPrinter &printer) {
1477 printer <<
"<" << stringifyAIETileType(getTileType()) <<
">";
1480 if (
auto col = getCol())
1485 if (
auto row = getRow())
1491 SmallVector<StringRef, 3> elidedAttrs = {
"tile_type",
"col",
"row"};
1492 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
1499LogicalResult TileOp::verify() {
1501 int columns = targetModel.columns();
1502 int rows = targetModel.rows();
1503 if (colIndex() >= columns)
1504 return emitOpError(
"column index (")
1506 <<
") must be less than the number of columns in the device ("
1508 if (rowIndex() >= rows)
1509 return emitOpError(
"row index (")
1511 <<
") must be less than the number of rows in the device (" << rows
1514 auto users = getResult().getUsers();
1516 for (
auto *user : users) {
1517 if (llvm::isa<SwitchboxOp>(*user)) {
1519 return emitOpError(
"can only have one switchbox");
1524 if (isShimNOCorPLTile() && getAllocationScheme())
1525 return emitOpError(
"Shim tiles cannot have an allocation scheme");
1530size_t TileOp::getNumSourceConnections(WireBundle bundle) {
1532 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
1536 if (isShimNOCorPLTile())
1537 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
1539 return targetModel.getNumDestSwitchboxConnections(getCol(), getRow(),
1545size_t TileOp::getNumDestConnections(WireBundle bundle) {
1547 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
1551 if (isShimNOCorPLTile())
1552 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
1554 return targetModel.getNumSourceSwitchboxConnections(getCol(), getRow(),
1560std::optional<int> TileOp::tryGetCol() {
return getCol(); }
1561std::optional<int> TileOp::tryGetRow() {
return getRow(); }
1563AIETileType TileOp::getTileType() {
1565 return targetModel.getTileType(getCol(), getRow());
1569 MasterSetOp masterOp, PacketRulesOp slaveOp) {
1570 auto srcBundle = slaveOp.sourcePort().bundle;
1571 auto srcChan = slaveOp.sourcePort().channel;
1572 auto dstBundle = masterOp.destPort().bundle;
1573 auto dstChan = masterOp.destPort().channel;
1575 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
1579 ConnectOp connectOp) {
1580 auto srcBundle = connectOp.getSourceBundle();
1581 auto srcChan = connectOp.getSourceChannel();
1582 auto dstBundle = connectOp.getDestBundle();
1583 auto dstChan = connectOp.getDestChannel();
1585 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
1588TileOp TileOp::getOrCreate(mlir::OpBuilder builder, DeviceOp device,
int col,
1590 TileOp tile =
nullptr;
1592 for (
auto t : device.getOps<
AIE::TileOp>()) {
1593 if (t.getRow() ==
row && t.getCol() ==
col) {
1600 OpBuilder::InsertionGuard guard(builder);
1601 mlir::Block &device_start_block = *device.getBodyRegion().begin();
1602 builder.setInsertionPointToStart(&device_start_block);
1603 tile = TileOp::create(builder, builder.getUnknownLoc(),
1604 builder.getIndexType(),
col,
row);
1613LogicalResult ShimSwitchboxOp::verify() {
1614 Region &body = getConnections();
1615 DenseSet<Port> destset;
1617 return emitOpError(
"should have non-empty body");
1619 for (
auto &ops : body.front()) {
1620 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1621 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1622 if (destset.count(dest))
1623 return connectOp.emitOpError(
"targets same destination ")
1624 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1625 <<
" as another connect operation";
1626 destset.insert(dest);
1627 }
else if (isa<EndOp>(ops)) {
1630 return ops.emitOpError(
"cannot be contained in a Switchbox op");
1641LogicalResult ShimMuxOp::verify() {
1643 if (!isa<TileOp>(getTile().getDefiningOp()))
1644 return emitOpError(
"requires a placed tile (aie.tile), not a logical tile");
1646 Region &body = getConnections();
1647 DenseSet<Port> destset;
1649 return emitOpError(
"should have non-empty body");
1651 for (
auto &ops : body.front()) {
1652 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1653 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1654 if (destset.count(dest))
1655 return connectOp.emitOpError(
"targets same destination ")
1656 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1657 <<
" as another connect operation";
1658 destset.insert(dest);
1659 }
else if (isa<EndOp>(ops)) {
1662 return ops.emitOpError(
"cannot be contained in a Switchbox op");
1668size_t ShimMuxOp::getNumSourceConnections(WireBundle bundle) {
1669 auto tile = getTileOp();
1671 return targetModel.getNumSourceShimMuxConnections(tile.getCol(),
1672 tile.getRow(), bundle);
1675size_t ShimMuxOp::getNumDestConnections(WireBundle bundle) {
1676 auto tile = getTileOp();
1678 return targetModel.getNumDestShimMuxConnections(tile.getCol(), tile.getRow(),
1686LogicalResult ShimDMAOp::verify() {
1693TileOp ShimDMAOp::getTileOp() {
1694 return cast<TileElement>(this->getOperation()).getTileOp();
1697LogicalResult PacketRulesOp::verify() {
1698 if (Region &body = getRules(); body.empty())
1699 return emitOpError(
"should have non-empty body");
1703LogicalResult PacketFlowOp::verify() {
1704 Region &body = getPorts();
1706 return emitOpError(
"should have non-empty body");
1708 int numSources = 0, numDests = 0;
1709 for (
auto &ops : body.front()) {
1710 if (!isa<PacketSourceOp, PacketDestOp, EndOp>(ops))
1711 return ops.emitOpError(
"cannot be contained in a PacketFlow op");
1712 if (isa<PacketSourceOp>(ops))
1714 if (isa<PacketDestOp>(ops))
1718 if (numSources != 1)
1719 return emitOpError(
"must have exactly one aie.packet_source (got ")
1720 << numSources <<
")";
1722 return emitOpError(
"must have at least one aie.packet_dest");
1731LogicalResult CoreOp::verify() {
1732 if (getBody().
empty())
1733 return emitOpError(
"should have non-empty body");
1740 "When `elf_file` attribute is specified, core body must be empty "
1741 "(consist of exactly one `aie.end` op).");
1744 if (getLinkWith() && getLinkFiles())
1746 "cannot specify both 'link_with' (deprecated) and 'link_files' "
1747 "on the same core; run aie-assign-core-link-files to migrate");
1751bool CoreOp::isEmpty() {
1752 Region &body = getBody();
1754 return (body.hasOneBlock() && body.front().getOperations().size() == 1 &&
1755 llvm::isa<AIE::EndOp>(body.front().front()));
1758TileOp CoreOp::getTileOp() {
1759 return cast<TileElement>(this->getOperation()).getTileOp();
1766int64_t BufferOp::getAllocationSize() {
1767 auto type = llvm::cast<MemRefType>(getType());
1768 DataLayout dataLayout = DataLayout::closest(getOperation());
1769 return type.getNumElements() * dataLayout.getTypeSize(type.getElementType());
1772LogicalResult BufferOp::verify() {
1773 if (UsesAreAccessible::verifyTrait(*this).failed())
1781 if (
auto buf = dyn_cast<BufferOp>(bufOp)) {
1782 assert(buf.getAddress().has_value() &&
"buffer must have address assigned");
1783 return buf.getAddress().value();
1785 if (isa_and_nonnull<ExternalBufferOp>(bufOp))
1786 llvm::report_fatal_error(
1787 "External buffer addresses are assigned at runtime.");
1788 llvm::report_fatal_error(
"unknown buffer type");
1792 DenseMap<TileID, Operation *> &tiles) {
1793 for (
auto tile : device.getOps<TileOp>()) {
1794 tiles[tile.getTileID()] = tile;
1800 DenseMap<Operation *, SmallVector<BufferOp, 4>> &buffers) {
1801 for (BufferOp buffer : device.getOps<BufferOp>()) {
1802 Operation *tileOp = buffer.getTile().getDefiningOp();
1803 buffers[tileOp].push_back(buffer);
1807static void printBufferInitialValue(OpAsmPrinter &p, BufferOp op, Type type,
1808 Attribute initialValue) {
1809 if (op.getInitialValue()) {
1811 p.printAttributeWithoutType(initialValue);
1815static ParseResult parseBufferInitialValue(OpAsmParser &parser, Type &type,
1816 Attribute &initialValue) {
1817 auto memrefType = llvm::cast<MemRefType>(type);
1818 if (!memrefType.hasStaticShape())
1819 return parser.emitError(parser.getNameLoc())
1820 <<
"type should be static shaped memref, but got " << type;
1822 if (parser.parseOptionalEqual())
1825 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
1826 if (parser.parseAttribute(initialValue, tensorType))
1828 if (!llvm::isa<ElementsAttr>(initialValue))
1829 return parser.emitError(parser.getNameLoc())
1830 <<
"initial value should be an elements attribute";
1834TileOp BufferOp::getTileOp() {
1835 return cast<TileElement>(this->getOperation()).getTileOp();
1842LogicalResult MemOp::verify() {
1843 Region &body = getBody();
1848 for (
auto &bodyOp : body.getOps()) {
1849 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp))
1850 if (!allocOp->getAttr(
"id"))
1851 return allocOp.emitOpError()
1852 <<
"allocOp in MemOp region should have an id attribute";
1857TileOp MemOp::getTileOp() {
1858 return cast<TileElement>(this->getOperation()).getTileOp();
1865LogicalResult MemTileDMAOp::verify() {
1866 assert(getOperation()->getNumRegions() == 1 &&
1867 "MemTileDMAOp has zero region!");
1873 for (
auto &bodyOp : getBody().getOps()) {
1874 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp)) {
1875 if (!allocOp->getAttr(
"id"))
1876 return allocOp.emitOpError()
1877 <<
"allocOp in MemTileDMAOp region should have an id attribute";
1879 if (
auto startOp = dyn_cast<DMAStartOp>(bodyOp)) {
1880 if (startOp.getChannelIndex() > 3) {
1886 llvm::SmallSet<Block *, 16> reachable;
1887 SmallVector<Block *, 16> worklist;
1888 Block *firstBD = startOp.getSuccessor(0);
1889 reachable.insert(firstBD);
1890 worklist.push_back(firstBD);
1891 while (!worklist.empty()) {
1892 Block *block = worklist.pop_back_val();
1895 auto successors = block->getTerminator()->getSuccessors();
1896 for (
auto *i : successors) {
1897 if (!reachable.contains(i)) {
1898 reachable.insert(i);
1899 worklist.push_back(i);
1903 for (Block *b : reachable) {
1904 for (DMABDOp bd : b->getOps<DMABDOp>()) {
1905 if (
auto bufferOp = bd.getBufferOp();
1906 bufferOp.getTile() != getTile()) {
1907 InFlightDiagnostic err =
1909 <<
"is reachable from DMA channel "
1910 << startOp.getChannelIndex()
1911 <<
" and attempts to access a non-local buffer\n";
1912 err.attachNote(startOp->getLoc()) <<
"channel";
1913 err.attachNote(bufferOp->getLoc()) <<
"buffer";
1917 for (
auto useLock : b->getOps<UseLockOp>()) {
1918 if (
auto lockOp = useLock.getLockOp();
1919 lockOp.getTile() != getTile()) {
1920 InFlightDiagnostic err =
1921 useLock.emitOpError()
1922 <<
"is reachable from DMA channel "
1923 << startOp.getChannelIndex()
1924 <<
" and attempts to access a non-local lock\n";
1925 err.attachNote(startOp->getLoc()) <<
"channel";
1926 err.attachNote(lockOp->getLoc()) <<
"lock";
1938TileOp MemTileDMAOp::getTileOp() {
1939 return cast<TileElement>(this->getOperation()).getTileOp();
1946LogicalResult DMAOp::verify() {
1947 auto *parentOp = getOperation()->getParentOp();
1948 if (parentOp->getRegion(0).getBlocks().size() > 1)
1949 return emitOpError(
"DMAOp can only appear in single block region");
1950 if (!parentOp->getRegion(0).getOps<DMAStartOp>().empty())
1951 return emitOpError(
"DMAOp is not compatible with DMAStart ops");
1952 auto bdRegions = getBds();
1953 for (
auto &bdRegion : bdRegions) {
1954 if (!bdRegion.hasOneBlock())
1955 return emitOpError(
"DMAOp regions must have only one block");
1956 auto bds = llvm::to_vector_of<DMABDOp>(bdRegion.front().getOps<DMABDOp>());
1957 if (bds.size() != 1)
1958 return emitOpError(
"DMAOp regions/blocks must have exactly one DMABDOp");
1960 llvm::to_vector_of<UseLockOp>(bdRegion.front().getOps<UseLockOp>());
1961 if (useLocks.size() != 2)
1963 "DMAOp regions/blocks must have exactly two UseLock ops");
1972BufferOp DMABDOp::getBufferOp() {
1973 return cast<BufferOp>(getBuffer().getDefiningOp());
1981ParseResult DMABDOp::parse(OpAsmParser &parser, OperationState &result) {
1982 OpAsmParser::UnresolvedOperand bufferRawOperand{};
1983 ::llvm::ArrayRef<OpAsmParser::UnresolvedOperand> bufferOperands(
1984 &bufferRawOperand, 1);
1985 ::llvm::SMLoc bufferOperandsLoc;
1986 (void)bufferOperandsLoc;
1987 Type bufferRawType{};
1988 ::llvm::ArrayRef<Type> bufferTypes(&bufferRawType, 1);
1989 IntegerAttr offsetAttr;
1990 IntegerAttr lenAttr;
1991 ::xilinx::AIE::BDDimLayoutArrayAttr dimensionsAttr;
1992 ::xilinx::AIE::BDPadLayoutArrayAttr pad_dimensionsAttr;
1993 IntegerAttr pad_valueAttr;
1994 if (parser.parseLParen())
1997 bufferOperandsLoc = parser.getCurrentLocation();
1998 if (parser.parseOperand(bufferRawOperand))
2000 if (parser.parseColon())
2002 if (parser.parseCustomTypeWithFallback(bufferRawType))
2006 if (succeeded(parser.parseOptionalComma())) {
2007 if (parser.parseCustomAttributeWithFallback(
2008 offsetAttr, parser.getBuilder().getIntegerType(32))) {
2012 offsetAttr = parser.getBuilder().getIntegerAttr(
2013 parser.getBuilder().getIntegerType(32), 0);
2014 result.getOrAddProperties<DMABDOp::Properties>().offset = offsetAttr;
2018 if (succeeded(parser.parseOptionalComma())) {
2019 if (parser.parseCustomAttributeWithFallback(
2020 lenAttr, parser.getBuilder().getIntegerType(32))) {
2024 result.getOrAddProperties<DMABDOp::Properties>().len = lenAttr;
2028 if (succeeded(parser.parseOptionalComma())) {
2029 if (parser.parseCustomAttributeWithFallback(dimensionsAttr, Type{})) {
2033 result.getOrAddProperties<DMABDOp::Properties>().dimensions =
2038 if (succeeded(parser.parseOptionalComma())) {
2039 if (parser.parseCustomAttributeWithFallback(pad_dimensionsAttr, Type{})) {
2042 if (pad_dimensionsAttr)
2043 result.getOrAddProperties<DMABDOp::Properties>().pad_dimensions =
2048 if (succeeded(parser.parseOptionalComma())) {
2049 if (parser.parseKeyword(
"pad_value"))
2051 if (parser.parseEqual())
2054 if (parser.parseCustomAttributeWithFallback(
2055 pad_valueAttr, parser.getBuilder().getIntegerType(32))) {
2059 result.getOrAddProperties<DMABDOp::Properties>().pad_value =
2062 if (parser.parseRParen())
2065 auto loc = parser.getCurrentLocation();
2066 if (parser.parseOptionalAttrDict(result.attributes))
2068 if (failed(verifyInherentAttrs(result.name, result.attributes, [&]() {
2069 return parser.emitError(loc)
2070 <<
"'" << result.name.getStringRef() <<
"' op ";
2074 if (parser.resolveOperands(bufferOperands, bufferTypes, bufferOperandsLoc,
2081void DMABDOp::print(::mlir::OpAsmPrinter &printer) {
2083 printer << getBuffer();
2084 printer <<
' ' <<
":";
2087 auto type = getBuffer().getType();
2088 if (
auto validType = ::llvm::dyn_cast<::mlir::MemRefType>(type))
2089 printer.printStrippedAttrOrType(validType);
2095 ::mlir::OpBuilder((*this)->getContext())
2097 ::mlir::OpBuilder((*this)->getContext()).getIntegerType(32),
2101 printer.printAttributeWithoutType(getOffsetAttr());
2106 printer.printAttributeWithoutType(getLenAttr());
2108 if (getDimensionsAttr()) {
2111 printer.printStrippedAttrOrType(getDimensionsAttr());
2113 if (getPadDimensionsAttr()) {
2116 printer.printStrippedAttrOrType(getPadDimensionsAttr());
2118 if ((getPadValueAttr() &&
2119 getPadValueAttr() !=
2120 ::mlir::OpBuilder((*this)->getContext())
2122 ::mlir::OpBuilder((*this)->getContext()).getIntegerType(32),
2125 printer <<
' ' <<
"pad_value";
2126 printer <<
' ' <<
"=";
2128 printer.printAttributeWithoutType(getPadValueAttr());
2131 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
2132 elidedAttrs.push_back(
"offset");
2133 elidedAttrs.push_back(
"len");
2134 elidedAttrs.push_back(
"dimensions");
2135 elidedAttrs.push_back(
"pad_dimensions");
2136 elidedAttrs.push_back(
"pad_value");
2137 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
2140LogicalResult DMABDOp::verify() {
2144 Operation *p = (*this)->getParentOp();
2145 if (!llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(*p)) {
2150 bool isUnrankedMemRef = llvm::isa<UnrankedMemRefType>(getBuffer().getType());
2154 if (!isUnrankedMemRef) {
2155 if (!isa<BufferOp, ExternalBufferOp>(getBuffer().getDefiningOp()))
2157 "BDs only support BufferOp or ExternalBufferOp operands.");
2160 if (getLenInBytes() % 4)
2161 return emitOpError(
"transfer length must be multiple of 4 (i.e., represent "
2162 "4 byte aligned address)");
2164 TileElement parentTileElement = getParentTileElement(getOperation());
2165 TileLike parentTile = parentTileElement.getTileLike();
2167 return emitOpError(
"parent tile must implement TileLike interface");
2169 if (!isUnrankedMemRef && getOperation()->getParentOfType<MemOp>() &&
2170 getBufferOp().getTile() != parentTileElement.getTile())
2172 "Core tile DMAs can only access a buffer in the same tile.");
2176 uint32_t maxBds = targetModel.
getNumBDs(parentTile.getTileType());
2177 if (std::optional<int32_t> bdId = getBdId();
2178 bdId.has_value() &&
static_cast<uint32_t
>(*bdId) >= maxBds)
2179 return emitOpError(
"bdId attribute exceeds max: ") << maxBds - 1;
2180 if (std::optional<int32_t> nextBdId = getNextBdId();
2181 nextBdId.has_value() &&
static_cast<uint32_t
>(*nextBdId) >= maxBds)
2182 return emitOpError(
"nextBdId attribute exceeds max: ") << maxBds - 1;
2183 if (
auto dims = getDimensions(); dims.has_value()) {
2184 size_t maxNDims = 3;
2185 if (getOperation()->getParentOfType<MemTileDMAOp>())
2187 if (dims->size() > maxNDims)
2188 return emitOpError() <<
"Cannot give more than "
2189 << std::to_string(maxNDims)
2190 <<
" dimensions for step sizes and wraps in this "
2192 << std::to_string(dims->size()) <<
" dimensions).";
2194 auto buffer = llvm::dyn_cast<MemRefType>(getBuffer().getType());
2196 return emitOpError() <<
"dimensions attribute cannot be used with "
2197 "unranked memref buffer type.";
2198 int64_t maxIdx = getDimsMaxIdx(*dims);
2199 if (buffer.getNumElements() <= maxIdx)
2200 return emitOpError() <<
"Specified stride(s) and size(s) result in out "
2201 "of bounds access in buffer, for index "
2202 << std::to_string(maxIdx) <<
" in memref of length "
2203 << std::to_string(buffer.getNumElements()) <<
".";
2205 for (BDDimLayoutAttr dim : *dims) {
2206 if (0 == dim.getStride())
2207 return emitOpError()
2208 <<
"Invalid step size; must be a positive integer.";
2209 if (dim.getStride() > buffer.getNumElements())
2210 return emitOpError() <<
"Step size " << std::to_string(dim.getStride())
2211 <<
" exceeds memref size "
2212 << std::to_string(buffer.getNumElements());
2213 if (dim.getSize() >= (1UL << 9) + 1)
2214 return emitOpError() <<
"Size may not exceed 1023.";
2215 if (dim.getStride() >= (1UL << 19))
2216 return emitOpError() <<
"Stride may not exceed " << (1 << 20);
2222 if (getBufferElementTypeWidthInBytes() < 4 && dims->back().getStride() != 1)
2224 "For <32b width datatypes, inner-most dim stride must be 1");
2226 if (getBufferElementTypeWidthInBytes() > 4 && dims->back().getStride() != 1)
2228 "For >32b width datatypes, inner-most dim stride must be 1");
2230 if (
auto paddims = getPadDimensions(); paddims.has_value()) {
2231 auto dims = getDimensions();
2232 if (!dims.has_value())
2233 return emitOpError() <<
"Padding requires n-d data layouts expressed as"
2234 <<
" wrap(s) and stride(s).";
2235 if (!parentTile.isMemTile())
2236 return emitOpError() <<
"Padding is only supported by memtile dma bds.";
2237 if (dims->size() != paddims->size())
2238 return emitOpError() <<
"Mismatch number of dimensions between padding(s)"
2239 <<
" and wrap(s) and stride(s).";
2241 for (
unsigned i = 0; i < paddims->size(); i++) {
2242 auto dim = (*dims)[i];
2243 auto paddim = (*paddims)[i];
2244 actuallen *= paddim.getConstPadBefore() + paddim.getConstPadAfter() +
2246 if (actuallen > getLen())
2247 return emitOpError() <<
"Data exceeds len after padding.";
2249 if ((paddims->back().getConstPadBefore() *
2250 getBufferElementTypeWidthInBytes()) %
2252 return emitOpError() <<
"Inner-most padding-before count must result in"
2253 <<
" padding in 32-bit words.";
2254 if ((paddims->back().getConstPadAfter() *
2255 getBufferElementTypeWidthInBytes()) %
2257 return emitOpError() <<
"Inner-most padding-after count must result in"
2258 <<
" padding in 32-bit words.";
2260 if (!isUnrankedMemRef &&
2261 (parentTile.isMemTile() || parentTile.isCoreTile())) {
2262 if (
auto baseAddr = getBufferOp().getAddress(); baseAddr.has_value()) {
2263 int offsetInBytes = *baseAddr + getOffsetInBytes();
2264 if (offsetInBytes % 4)
2265 return emitOpError(
"bd address must be 4 byte (32b) aligned; got "
2267 << offsetInBytes <<
" (bytes)";
2270 if (
auto packetInfo = getPacket()) {
2271 if (packetInfo->getPktType() > 7)
2272 return emitOpError(
"Packet type field can only hold 3 bits.");
2273 if (packetInfo->getPktId() > 31)
2274 return emitOpError(
"Packet ID field can only hold 5 bits.");
2277 if (!getLen() && !getBuffer().getType().hasStaticShape())
2278 return emitOpError() <<
"buffer with dynamic shape requires static length.";
2280 if (getBurstLength() != 0 && !parentTile.isShimNOCTile())
2281 return emitOpError(
"Burst length is only supported in Shim NOC tiles that "
2282 "are connected to the memory-mapped NOC.");
2291static LogicalResult FoldDMAStartOp(DMAStartOp op, PatternRewriter &rewriter) {
2293 llvm::SetVector<Block *> reachable;
2294 SmallVector<Block *, 16> worklist;
2295 Block *firstBD = op.getSuccessor(0);
2296 reachable.insert(firstBD);
2297 worklist.push_back(firstBD);
2298 while (!worklist.empty()) {
2299 Block *block = worklist.pop_back_val();
2302 auto successors = block->getTerminator()->getSuccessors();
2303 for (
auto *i : successors) {
2304 if (!reachable.contains(i)) {
2305 reachable.insert(i);
2306 worklist.push_back(i);
2313 if (isa<EndOp>((reachable.back())->getTerminator()))
2317 auto areEquivalentBDs = [](Block *b1, Block *b2) {
2318 auto b1OpRange = b1->without_terminator();
2319 auto b2OpRange = b2->without_terminator();
2320 if (llvm::range_size(b1OpRange) != llvm::range_size(b2OpRange))
2322 if (!llvm::all_of(llvm::zip_equal(b1OpRange, b2OpRange),
2323 [](std::tuple<Operation &, Operation &> pair) {
2324 return OperationEquivalence::isEquivalentTo(
2325 &std::get<0>(pair), &std::get<1>(pair),
2326 OperationEquivalence::IgnoreLocations);
2333 SmallVector<Block *> uniquePattern;
2334 auto patternIt = reachable.begin();
2335 while (patternIt != reachable.end() &&
2336 llvm::none_of(uniquePattern, [patternIt, areEquivalentBDs](Block *b1) {
2337 return areEquivalentBDs(*patternIt, b1);
2339 uniquePattern.push_back(*patternIt);
2344 while (patternIt != reachable.end()) {
2346 if (!areEquivalentBDs(*patternIt, uniquePattern[idx]))
2349 idx = (++idx) % uniquePattern.size();
2353 auto lastBDTerm = dyn_cast<NextBDOp>(reachable.back()->getTerminator());
2354 auto lastUniqueBDTerm =
2355 dyn_cast<NextBDOp>(uniquePattern.back()->getTerminator());
2356 lastUniqueBDTerm.setSuccessor(lastBDTerm.getSuccessor());
2361void DMAStartOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2362 MLIRContext *context) {
2363 patterns.add(FoldDMAStartOp);
2370LogicalResult SwitchboxOp::verify() {
2372 if (!isa<TileOp>(getTile().getDefiningOp()))
2373 return emitOpError(
"requires a placed tile (aie.tile), not a logical tile");
2375 Region &body = getConnections();
2376 DenseSet<Port> sourceset;
2377 DenseSet<Port> destset;
2378 auto tile = getTileOp();
2381 return emitOpError(
"should have non-empty body");
2382 for (
auto &ops : body.front()) {
2384 auto checkBound = [&ops](StringRef dir, WireBundle bundle,
int index,
2385 int bound) -> LogicalResult {
2386 if (index >= bound) {
2388 return ops.emitOpError(
"index ")
2389 << index <<
" for " << dir <<
" bundle "
2390 << stringifyWireBundle(bundle) <<
" must be less than "
2392 return ops.emitOpError()
2393 << dir <<
" bundle " << stringifyWireBundle(bundle)
2394 <<
" not supported; index: " << index <<
", bound: " << bound;
2399 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
2400 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
2401 sourceset.insert(source);
2403 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
2404 if (destset.count(dest)) {
2405 return connectOp.emitOpError()
2406 <<
"; connecting " <<
to_string(source) <<
" to "
2408 <<
to_string(this->getTileOp().getTileID())
2409 <<
" targets same dst as another connect op; existing "
2411 << llvm::join(llvm::map_range(
2412 destset, [](
auto &p) {
return to_string(p); }),
2415 destset.insert(dest);
2417 if (connectOp.sourceIndex() < 0)
2418 return connectOp.emitOpError(
"source index cannot be less than zero");
2420 if (checkBound(
"source", connectOp.getSourceBundle(),
2421 connectOp.sourceIndex(),
2422 getNumSourceConnections(connectOp.getSourceBundle()))
2426 if (connectOp.destIndex() < 0)
2427 return connectOp.emitOpError(
"dest index cannot be less than zero");
2429 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
2430 getNumDestConnections(connectOp.getDestBundle()))
2436 return connectOp.emitOpError(
"illegal stream switch connection");
2438 }
else if (
auto connectOp = dyn_cast<MasterSetOp>(ops)) {
2439 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
2440 if (destset.count(dest))
2441 return connectOp.emitOpError(
"targets same destination ")
2442 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
2443 <<
" as another connect or masterset operation";
2444 destset.insert(dest);
2446 if (connectOp.destIndex() < 0)
2447 return connectOp.emitOpError(
"dest index cannot be less than zero");
2449 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
2450 getNumDestConnections(connectOp.getDestBundle()))
2455 for (
auto val : connectOp.getAmsels()) {
2456 auto amsel = dyn_cast<AMSelOp>(val.getDefiningOp());
2457 if (arbiter != -1 && arbiter != amsel.arbiterIndex())
2458 return connectOp.emitOpError(
2459 "a master port can only be tied to one arbiter");
2460 arbiter = amsel.arbiterIndex();
2462 }
else if (
auto connectOp = dyn_cast<PacketRulesOp>(ops)) {
2463 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
2464 if (sourceset.count(source))
2465 return connectOp.emitOpError(
"packet switched source ")
2466 << stringifyWireBundle(source.bundle) << source.channel
2467 <<
" cannot match another connect or masterset operation";
2468 sourceset.insert(source);
2470 }
else if (
auto amselOp = dyn_cast<AMSelOp>(ops)) {
2471 std::vector<MasterSetOp> mstrs;
2472 std::vector<PacketRulesOp> slvs;
2473 for (
auto *user : amselOp.getResult().getUsers()) {
2474 if (
auto s = dyn_cast<PacketRuleOp>(user)) {
2475 auto pktRules = dyn_cast<PacketRulesOp>(s->getParentOp());
2476 slvs.push_back(pktRules);
2477 }
else if (
auto m = dyn_cast<MasterSetOp>(user))
2480 for (
auto m : mstrs) {
2481 for (
auto s : slvs) {
2484 return amselOp->emitOpError(
"illegal stream switch connection");
2488 }
else if (isa<EndOp>(ops)) {
2491 return ops.emitOpError(
"cannot be contained in a Switchbox op");
2498template <
typename... ParentOpTypes>
2501 Operation *operation = op->getParentOp();
2503 if (llvm::isa_and_nonnull<ParentOpTypes...>(operation))
2505 operation = operation->getParentOp();
2511TileOp LockOp::getTileOp() {
2512 return cast<TileElement>(this->getOperation()).getTileOp();
2515LogicalResult LockOp::verify() {
2516 if (
auto result = UsesAreAccessible::verifyTrait(*
this); result.failed())
2519 if (getLockID().has_value()) {
2520 TileLike tileLike = getTileLike();
2522 return emitOpError(
"tile operand must implement TileLike interface");
2524 auto tileType = tileLike.getTileType();
2525 if (
int numLocks = targetModel.
getNumLocks(tileType);
2526 getLockID().value() >= numLocks)
2527 return emitOpError(
"lock assigned invalid id (maximum is ")
2528 << numLocks - 1 <<
")";
2536 auto *block = op->getBlock();
2538 for (
auto op : block->getOps<UseLockOp>()) {
2539 if (
auto lock = dyn_cast<LockOp>(op.getLock().getDefiningOp());
2540 lock.getLockID().has_value()) {
2541 if (lockID != -1 && lockID != lock.getLockIDValue())
2543 lockID = lock.getLockIDValue();
2552 auto *block = op->getBlock();
2553 int acqValue = -1, relValue = -1;
2554 for (
auto op : block->getOps<UseLockOp>()) {
2555 if (op.acquire() || op.acquireGE()) {
2556 if (acqValue != -1 && acqValue != op.getLockValue()) {
2559 acqValue = op.getLockValue();
2560 }
else if (op.release()) {
2561 if (relValue != -1 && relValue != op.getLockValue()) {
2564 relValue = op.getLockValue();
2573 if (
auto memOp = op->getParentOfType<MemOp>()) {
2574 auto useLock = dyn_cast<UseLockOp>(op);
2575 if (
auto lock = useLock.getLockOp(); lock.getTile() != memOp.getTile())
2582LogicalResult UseLockOp::verify() {
2584 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>((*this)->getParentOp()))
2585 return (*this)->emitOpError(
"must be used in a core or memory operation.");
2588 if (targetModel.
getTargetArch() == AIEArch::AIE1 && acquireGE())
2589 return (*this)->emitOpError(
2590 "AcquireGreaterEqual is not supported in AIE1.");
2596 if (!(*this)->getBlock())
2597 return (*this)->emitOpError(
"is not in a block.");
2601 return (*this)->emitOpError(
2602 "used in a DMA block that have multiple locks.");
2605 return (*this)->emitOpError(
"acquires/releases the lock in a DMA block "
2606 "from/to multiple states.");
2610 return (*this)->emitOpError(
"can only access a lock in the same tile");
2621 Operation *operation = (*this)->getParentOp();
2623 if (operation->getName().getStringRef() ==
"aiex.dma_configure_task")
2625 operation = operation->getParentOp();
2628 return (*this)->emitOpError()
2629 <<
"expects some parent op to be one of "
2630 <<
"AIE::device, AIE::core, func::func, AIE::mem, AIE::shimDMA, or "
2631 "AIEX::dma_configure_task";
2634#include "aie/Dialect/AIE/IR/AIEEnums.cpp.inc"
2635#include "aie/Dialect/AIE/IR/AIEInterfaces.cpp.inc"
2642static ParseResult parseTraceEventValue(AsmParser &parser, Attribute &value) {
2647static void printTraceEventValue(AsmPrinter &printer, Attribute value) {
2652static ParseResult parseTraceRegValue(OpAsmParser &parser, Attribute &value) {
2656 OptionalParseResult parseResult = parser.parseOptionalInteger(intValue);
2657 if (parseResult.has_value() && succeeded(parseResult.value())) {
2658 MLIRContext *ctx = parser.getContext();
2659 value = IntegerAttr::get(IntegerType::get(ctx, 32), intValue);
2666static void printTraceRegValue(OpAsmPrinter &printer, Operation *op,
2669 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(value)) {
2670 printer << intAttr.getInt();
2676#define GET_OP_CLASSES
2677#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
2679TileOp SwitchboxOp::getTileOp() {
2680 return cast<TileElement>(this->getOperation()).getTileOp();
2683size_t SwitchboxOp::getNumSourceConnections(WireBundle bundle) {
2684 auto tile = getTileOp();
2687 tile.getRow(), bundle);
2690size_t SwitchboxOp::getNumDestConnections(WireBundle bundle) {
2691 auto tile = getTileOp();
2694 tile.getRow(), bundle);
2697TileOp ShimMuxOp::getTileOp() {
2698 return cast<TileElement>(this->getOperation()).getTileOp();
2703 case WireBundle::North:
2704 return WireBundle::South;
2705 case WireBundle::South:
2706 return WireBundle::North;
2707 case WireBundle::East:
2708 return WireBundle::West;
2709 case WireBundle::West:
2710 return WireBundle::East;
2720ParseResult BDChainOp::parse(OpAsmParser &parser, OperationState &result) {
2721 SmallVector<OpAsmParser::Argument> entryArgs;
2724 StringAttr symNameAttr;
2725 if (parser.parseSymbolName(symNameAttr, SymbolTable::getSymbolAttrName(),
2726 result.attributes)) {
2731 ParseResult argParseResult = parser.parseCommaSeparatedList(
2732 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
2733 OpAsmParser::Argument argument;
2734 if (parser.parseArgument(argument,
true,
true)) {
2737 entryArgs.push_back(argument);
2740 if (argParseResult) {
2741 return argParseResult;
2745 auto *body = result.addRegion();
2746 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
2747 if (bodyParseResult) {
2748 return bodyParseResult;
2754void BDChainOp::print(OpAsmPrinter &printer) {
2757 ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
2760 printer.printSymbolName(taskName);
2762 Region &body = getRegion();
2763 auto argsIter = body.getArguments();
2765 for (
auto it = argsIter.begin(); it != argsIter.end(); ++it) {
2766 if (it != argsIter.begin()) {
2769 printer.printRegionArgument(*it);
2774 printer.printRegion(body,
false,
true);
2781LogicalResult ShimDMAAllocationOp::verify() {
2782 TileLike tileLike = llvm::dyn_cast<TileLike>(getTile().getDefiningOp());
2784 return emitOpError(
"tile operand must implement TileLike interface");
2787 if (!tileLike.isShimNOCorPLTile()) {
2789 auto col = tileLike.tryGetCol();
2790 auto row = tileLike.tryGetRow();
2792 return emitOpError(
"tile must be a shim tile, but got tile(")
2793 << *
col <<
", " << *
row <<
")";
2795 return emitOpError(
"tile must be a shim tile");
2801TileOp ShimDMAAllocationOp::getTileOp() {
2802 return cast<TileOp>(getTile().getDefiningOp());
2805ShimDMAAllocationOp ShimDMAAllocationOp::getForSymbol(DeviceOp device,
2806 llvm::StringRef symbol) {
2807 Operation *maybeOp = device.lookupSymbol(symbol);
2809 if (ShimDMAAllocationOp op = dyn_cast<ShimDMAAllocationOp>(maybeOp)) {
2820ParseResult RuntimeSequenceOp::parse(OpAsmParser &parser,
2821 OperationState &result) {
2824 StringAttr nameAttr;
2825 (void)parser.parseOptionalSymbolName(
2826 nameAttr, mlir::SymbolTable::getSymbolAttrName(), result.attributes);
2828 SmallVector<OpAsmParser::Argument> entryArgs;
2831 ParseResult argParseResult = parser.parseCommaSeparatedList(
2832 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
2833 OpAsmParser::Argument argument;
2834 if (parser.parseArgument(argument,
true,
true)) {
2837 entryArgs.push_back(argument);
2840 if (argParseResult) {
2841 return argParseResult;
2845 auto *body = result.addRegion();
2846 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
2847 if (bodyParseResult) {
2848 return bodyParseResult;
2854void RuntimeSequenceOp::print(OpAsmPrinter &printer) {
2855 Region &body = getRegion();
2857 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
2858 mlir::SymbolTable::getSymbolAttrName());
2860 nameAttr != ::mlir::OpBuilder((*this)->getContext())
2861 .getStringAttr(getDefaultRuntimeSequenceName())) {
2863 printer.printSymbolName(nameAttr);
2867 for (
unsigned i = 0, n = body.getNumArguments(); i < n; i++) {
2871 printer.printRegionArgument(body.getArgument(i));
2876 printer.printRegion(body,
false,
true);
2879LogicalResult RuntimeSequenceOp::verify() {
2880 DeviceOp device = (*this)->getParentOfType<DeviceOp>();
2883 (*this)->emitOpError() <<
"must be inside AIE device operation.";
2890RuntimeSequenceOp::getForSymbolInDevice(DeviceOp deviceOp,
2891 llvm::StringRef symbol) {
2892 RuntimeSequenceOp runtimeSequenceOp;
2893 if (!symbol.size()) {
2894 runtimeSequenceOp = *deviceOp.getOps<RuntimeSequenceOp>().begin();
2896 Operation *maybeRuntimeSequenceOp =
2897 mlir::SymbolTable::lookupSymbolIn(deviceOp, symbol);
2898 if (!maybeRuntimeSequenceOp) {
2902 llvm::dyn_cast<RuntimeSequenceOp>(maybeRuntimeSequenceOp);
2904 return runtimeSequenceOp;
2908RuntimeSequenceOp::getForSymbolInDeviceOrError(DeviceOp deviceOp,
2909 llvm::StringRef symbol) {
2910 RuntimeSequenceOp runtimeSequenceOp = getForSymbolInDevice(deviceOp, symbol);
2911 if (!runtimeSequenceOp) {
2912 if (!symbol.empty()) {
2913 deviceOp.emitError(
"No such runtime sequence: ") << symbol;
2915 deviceOp.emitError(
"No runtime sequence in device");
2918 return runtimeSequenceOp;
2921LogicalResult RuntimeSequenceOp::verifyBeforeMaterialization() {
2925 auto result = (*this)->walk([&](Operation *op) {
2926 for (NamedAttribute namedAttr : op->getAttrs()) {
2927 Attribute attr = namedAttr.getValue();
2928 auto walkResult = attr.walk([&](SymbolRefAttr symbolRef) {
2929 Operation *symbolDefOp =
2930 SymbolTable::lookupNearestSymbolFrom(*
this, symbolRef);
2932 if (!llvm::isa<ShimDMAAllocationOp>(symbolDefOp) &&
2933 !llvm::isa<DeviceOp>(symbolDefOp) &&
2934 !llvm::isa<RuntimeSequenceOp>(symbolDefOp) &&
2935 !llvm::isa<BufferOp>(symbolDefOp) &&
2936 !llvm::isa<memref::GlobalOp>(symbolDefOp)) {
2938 <<
"references symbol '"
2939 << symbolRef.getRootReference().getValue()
2940 <<
"' which must be either a ShimDMAAllocationOp, DeviceOp, "
2941 "RuntimeSequenceOp, BufferOp or GlobalOp, but got: "
2942 << symbolDefOp->getName().getStringRef();
2943 return WalkResult::interrupt();
2945 if (BufferOp bufferOp = llvm::dyn_cast<BufferOp>(symbolDefOp)) {
2946 if (!bufferOp.getAddress()) {
2948 <<
"Unallocated buffer; fixed addresses are required before "
2949 "runtime sequence materialization.";
2950 return WalkResult::interrupt();
2954 return WalkResult::advance();
2956 if (walkResult.wasInterrupted()) {
2957 return WalkResult::interrupt();
2960 return WalkResult::advance();
2963 if (result.wasInterrupted()) {
2971#define GET_ATTRDEF_CLASSES
2972#include "aie/Dialect/AIE/IR/AIEAttrs.cpp.inc"
bool isLegalTileConnection(TileOp tile, const AIETargetModel &targetModel, MasterSetOp masterOp, PacketRulesOp slaveOp)
virtual AIEArch getTargetArch() const =0
Return the target architecture.
virtual uint32_t getNumLocks(AIETileType tileType) const =0
Return the number of lock objects for a given tile type.
virtual std::vector< std::pair< uint32_t, uint32_t > > getShimBurstEncodingsAndLengths() const =0
virtual bool isLegalTileConnection(int col, int row, WireBundle srcBundle, int srcChan, WireBundle dstBundle, int dstChan) const =0
virtual uint32_t getNumBDs(AIETileType tileType) const =0
Return the number of buffer descriptors for a given tile type.
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 getNumSourceSwitchboxConnections(int col, int row, WireBundle bundle) const =0
Return the number of sources of connections inside a switchbox.
bool empty(const std::string &s)
std::shared_ptr< Value > value()
Include the generated interface declarations.
void printObjectFifoConsumerTiles(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange tiles, BDDimLayoutArrayArrayAttr dimensions)
uint32_t getShimBurstLengthBytes(const AIE::AIETargetModel &tm, uint32_t burstLength)
void collectTiles(DeviceOp &device, llvm::DenseMap< TileID, mlir::Operation * > &tiles)
void collectBuffers(DeviceOp &device, llvm::DenseMap< mlir::Operation *, llvm::SmallVector< BufferOp, 4 > > &buffers)
mlir::ParseResult parseTraceEvent(mlir::AsmParser &parser, mlir::Attribute &result)
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID
friend std::string to_string(const TileID &s)
DMAChannel { DMAChannelDir direction DMAChannel
uint32_t getShimBurstLengthEncoding(const AIE::AIETargetModel &tm, uint32_t burstLength)
int32_t getBufferBaseAddress(mlir::Operation *bufOp)
Port { WireBundle bundle Port
const AIETargetModel & getTargetModel(mlir::Operation *op)
mlir::ParseResult parseObjectFifoProducerTile(mlir::OpAsmParser &parser, mlir::OpAsmParser::UnresolvedOperand &operand, BDDimLayoutArrayAttr &dimensions)
void printTraceEventEnum(mlir::AsmPrinter &printer, mlir::Attribute attr)
mlir::LogicalResult myVerifyOffsetSizeAndStrideOp(mlir::OffsetSizeAndStrideOpInterface op)
void printObjectFifoProducerTile(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::Value tile, BDDimLayoutArrayAttr dimensions)
mlir::ParseResult parseObjectFifoConsumerTiles(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &tiles, BDDimLayoutArrayArrayAttr &dimensions)
WireBundle getConnectingBundle(WireBundle dir)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static LogicalResult verifyTrait(Operation *op)
static mlir::LogicalResult verifyTrait(mlir::Operation *op)
static mlir::LogicalResult verifyTrait(mlir::Operation *op)