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/Interfaces/FoldInterfaces.h"
19#include "mlir/Transforms/InliningUtils.h"
21#include "llvm/ADT/SmallSet.h"
22#include "llvm/ADT/TypeSwitch.h"
29#include "aie/Dialect/AIE/IR/AIEDialect.cpp.inc"
31#define GET_TYPEDEF_CLASSES
32#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
36struct AIEInlinerInterface : DialectInlinerInterface {
37 using DialectInlinerInterface::DialectInlinerInterface;
40 bool isLegalToInline(Region *dest, Region *
src,
bool wouldBeCloned,
41 IRMapping &valueMapping)
const final override {
47 bool isLegalToInline(Operation *op, Region *,
bool wouldBeCloned,
48 IRMapping &)
const final override {
54 void handleTerminator(Operation *op, Block *newDest)
const final override {}
58 void handleTerminator(Operation *op,
59 ValueRange valuesToRepl)
const final override {}
62struct AIEDialectFoldInterface : DialectFoldInterface {
63 using DialectFoldInterface::DialectFoldInterface;
68 bool shouldMaterializeInto(Region *region)
const final override {
70 return isa<CoreOp>(region->getParentOp());
76void AIEDialect::initialize() {
78#define GET_TYPEDEF_LIST
79#include "aie/Dialect/AIE/IR/AIETypes.cpp.inc"
82#define GET_ATTRDEF_LIST
83#include "aie/Dialect/AIE/IR/AIEAttrs.cpp.inc"
87#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
89 addInterfaces<AIEInlinerInterface, AIEDialectFoldInterface>();
99std::optional<xilinx::AIE::ShimDMAAllocationOp>
101 auto key = std::make_pair(dev, sym_name);
102 auto it = allocGetter.find(key);
103 if (it != allocGetter.end()) {
108 auto allocOp = cachelessGet(dev, sym_name);
111 if (allocOp.has_value()) {
112 allocGetter[key] = allocOp;
122std::optional<xilinx::AIE::ShimDMAAllocationOp>
123xilinx::AIE::ShimDMAllocationGetter::cachelessGet(DeviceOp dev,
124 mlir::StringRef sym_name) {
125 auto *sym = dev.lookupSymbol(sym_name);
128 auto uses = SymbolTable::getSymbolUses(sym, dev);
129 for (
auto use : *uses)
130 if (auto infoOp = dyn_cast<
AIE::ShimDMAAllocationOp>(use.getUser()))
139static std::pair<uint32_t, uint32_t>
141 uint32_t burstLength) {
143 std::vector<std::pair<uint32_t, uint32_t>> bel =
148 if (burstLength == 0) {
149 return *std::max_element(
150 bel.begin(), bel.end(),
151 [](
auto pair1,
auto pair2) { return pair1.second < pair2.second; });
156 return *std::find_if(bel.begin(), bel.end(),
157 [=](
auto p) { return p.second == burstLength; });
161 uint32_t burstLength) {
163 return getShimBurstLength(tm, burstLength).second;
167 uint32_t burstLength) {
169 return getShimBurstLength(tm, burstLength).first;
174 std::array<unsigned, 3> maxRanks = op.getArrayAttrMaxRanks();
175 if (!(op.getMixedOffsets().size() == 1 && maxRanks[0] == 1) &&
176 op.getMixedOffsets().size() != op.getMixedSizes().size())
177 return op->emitError(
178 "expected mixed offsets rank to match mixed sizes rank (")
179 << op.getMixedOffsets().size() <<
" vs " << op.getMixedSizes().size()
180 <<
") so the rank of the result type is well-formed.";
181 if (failed(verifyListOfOperandsOrIntegers(
182 op,
"offset", maxRanks[0], op.getStaticOffsets(), op.getOffsets())))
184 if (failed(verifyListOfOperandsOrIntegers(
185 op,
"size", maxRanks[1], op.getStaticSizes(), op.getSizes())))
187 if (failed(verifyListOfOperandsOrIntegers(
188 op,
"stride", maxRanks[2], op.getStaticStrides(), op.getStrides())))
190 for (int64_t offset : op.getStaticOffsets())
191 if (offset < 0 && !ShapedType::isDynamic(offset))
192 return op->emitError(
"expected offsets to be non-negative, but got ")
194 for (int64_t size : op.getStaticSizes())
195 if (size < 0 && !ShapedType::isDynamic(size))
196 return op->emitError(
"expected sizes to be non-negative, but got ")
219 if (
auto t = dyn_cast<AIETarget>(op))
220 return t.getTargetModel();
221 if (
auto t = op->getParentOfType<AIETarget>())
222 return t.getTargetModel();
231 case AIEDevice::xcvc1902:
233 case AIEDevice::xcve2302:
235 case AIEDevice::xcve2802:
237 case AIEDevice::npu1:
239 case AIEDevice::npu1_1col:
241 case AIEDevice::npu1_2col:
243 case AIEDevice::npu1_3col:
245 case AIEDevice::npu2:
247 case AIEDevice::npu2_1col:
248 return NPU2model1col;
249 case AIEDevice::npu2_2col:
250 return NPU2model2col;
251 case AIEDevice::npu2_3col:
252 return NPU2model3col;
253 case AIEDevice::npu2_4col:
254 return NPU2model4col;
255 case AIEDevice::npu2_5col:
256 return NPU2model5col;
257 case AIEDevice::npu2_6col:
258 return NPU2model6col;
259 case AIEDevice::npu2_7col:
260 return NPU2model7col;
267static TileElement getParentTileElement(Operation *op) {
268 auto *parent = op->getParentOp();
269 while (!llvm::isa_and_nonnull<DeviceOp, ModuleOp>(parent)) {
270 if (
auto element = llvm::dyn_cast<TileElement>(parent))
272 parent = parent->getParentOp();
274 return llvm::dyn_cast<TileElement>(parent);
279struct UsesAreAccessible {
280 static LogicalResult verifyTrait(Operation *op) {
281 auto thisElement = cast<TileElement>(op);
282 auto thisID = thisElement.getTileID();
283 auto users = op->getResult(0).getUsers();
285 for (
auto *user : users) {
288 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>(user->getParentOp())) {
297 TileElement element = llvm::dyn_cast<TileElement>(user);
299 element = getParentTileElement(user);
303 return op->emitOpError(
"is accessed outside of a tile")
304 .attachNote(user->getLoc())
307 auto tileID = element.getTileID();
308 if (!targetModel.isLegalMemAffinity(tileID.col, tileID.row, thisID.col,
310 return (op->emitOpError(
"in Column ")
311 << thisID.col <<
" and Row " << thisID.row
312 <<
" is accessed from an unreachable tile in Column "
313 << tileID.col <<
" and Row " << tileID.row)
314 .attachNote(user->getLoc())
326template <
typename... TerminatorOpTypes>
329 for (
auto ®ion : op->getRegions()) {
330 for (
auto &block : region) {
331 if (!block.empty()) {
332 if (Operation *operation = &block.back();
333 !llvm::isa_and_nonnull<TerminatorOpTypes...>(operation))
334 return operation->emitOpError(
"is not an allowed terminator")
335 .attachNote(op->getLoc())
336 .append(
"in this context: ");
346template <
typename ConcreteType>
348 auto element = cast<ConcreteType>(op);
351 targetModel.getNumBDs(element.getTileID().col, element.getTileID().row);
354 for (
auto &block : element.getBody()) {
355 if (!block.template getOps<DMABDOp>().empty()) {
356 if (bdNum >= bdMax) {
357 auto bd = *block.template getOps<DMABDOp>().begin();
358 return (op->emitOpError(
"has more than ") << bdMax <<
" blocks")
359 .attachNote(bd.getLoc())
360 .append(
"no space for this bd: ");
370template <
typename ConcreteType>
372 DenseSet<DMAChannel> inputChannels;
373 DenseSet<DMAChannel> outputChannels;
374 auto element = cast<ConcreteType>(op);
375 Region &body = element.getBody();
377 return op->emitOpError(
"should have non-empty body");
378 for (
auto &bodyOp : body.getOps()) {
380 if (
auto dmaStart = dyn_cast<DMAStartOp>(bodyOp)) {
381 DMAChannel dmaChan = {dmaStart.getChannelDir(),
382 dmaStart.getChannelIndex()};
385 if (dmaChan.direction == DMAChannelDir::S2MM)
386 inputChannels.insert(dmaChan);
388 outputChannels.insert(dmaChan);
392 if (inputChannels.size() >
393 element.getTileOp().getNumSourceConnections(WireBundle::DMA))
394 return op->emitOpError(
395 "uses more input channels than available on this tile");
397 if (outputChannels.size() >
398 element.getTileOp().getNumDestConnections(WireBundle::DMA))
399 return op->emitOpError(
400 "uses more output channels than available on this tile");
408LogicalResult ObjectFifoCreateOp::verify() {
409 if (isa<ArrayAttr>(getElemNumber())) {
410 if (
size_t numDepths = dyn_cast<ArrayAttr>(getElemNumber()).size();
411 numDepths != getConsumerTiles().size() + 1)
412 return emitOpError(
"does not have enough depths specified for producer "
413 "and for each consumer.");
416 if (getProducerTileOp().isShimTile() && !getDimensionsToStream().empty()) {
418 "`dimensionsToStream` data layout transformations are not supported "
419 "on shim tile producers");
422 if (getViaSharedMem().has_value()) {
423 if (getConsumerTiles().size() > 1)
425 "`via_shared_mem` can only be used in 1-to-1 object FIFOs");
427 return emitError(
"`via_shared_mem` and `via_DMA` cannot occur together");
430 if (getRepeatCount().has_value()) {
431 if (getProducerTileOp().isShimTile())
432 return emitError(
"`repeat_count` unavailable for shim tiles");
435 if (getInitValues().has_value()) {
436 if (getProducerTileOp().isShimTile())
437 return emitError(
"`init_values` unavailable for shim tiles");
440 if (getInitValues().has_value()) {
441 if ((
int)getInitValues().
value().size() != size())
442 return emitError(
"`init_values` does not initialize all objects");
448TileOp ObjectFifoCreateOp::getProducerTileOp() {
449 return cast<TileOp>(getProducerTile().getDefiningOp());
453 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
454 BDDimLayoutArrayAttr &dimensions) {
455 std::vector<BDDimLayoutAttr> emptyDims = {};
456 if (parser.parseOperand(operand))
458 if (succeeded(parser.parseOptionalKeyword(
"dimensionsToStream"))) {
459 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
465 BDDimLayoutArrayAttr::get(parser.getContext(), ArrayRef(emptyDims));
471 Operation *op, Value operand,
472 BDDimLayoutArrayAttr dimensions) {
474 if (!dimensions.empty()) {
475 printer <<
" dimensionsToStream ";
476 printer.printStrippedAttrOrType(dimensions);
481 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &tiles,
482 BDDimLayoutArrayArrayAttr &dimensions) {
485 std::vector<BDDimLayoutArrayAttr> tileDims = {};
487 auto parseOneOperand = [&]() -> ParseResult {
488 if (parser.parseOperand(tiles.emplace_back(),
true)) {
494 BDDimLayoutArrayAttr dimAttr =
495 BDDimLayoutArrayAttr::get(parser.getContext(), {});
497 if (succeeded(parser.parseOptionalKeyword(
"dimensionsFromStream"))) {
499 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
504 tileDims.emplace_back(dimAttr);
508 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::None,
509 parseOneOperand,
" in operand list"))
512 dimensions = BDDimLayoutArrayArrayAttr::get(parser.getContext(), tileDims);
517 OpAsmPrinter &printer, Operation *op, OperandRange tiles,
518 BDDimLayoutArrayArrayAttr dimsPerTileAttr) {
520 for (
auto tile : tiles) {
522 if (dimsPerTileAttr && tileIdx < dimsPerTileAttr.size() &&
523 dimsPerTileAttr[tileIdx] && !dimsPerTileAttr[tileIdx].empty()) {
524 printer <<
" dimensionsFromStream ";
525 printer.printStrippedAttrOrType(dimsPerTileAttr[tileIdx]);
527 if (tileIdx < tiles.size() - 1) {
534static void printObjectFifoInitValues(OpAsmPrinter &p, ObjectFifoCreateOp op,
535 Attribute numElem, TypeAttr type,
536 Attribute initValues) {
537 if (op.getInitValues()) {
539 int depth = llvm::dyn_cast<mlir::IntegerAttr>(numElem).getInt();
540 for (
int i = 0; i < depth; i++) {
541 p.printStrippedAttrOrType(llvm::dyn_cast<mlir::ArrayAttr>(initValues)[i]);
550static ParseResult parseObjectFifoInitValues(OpAsmParser &parser,
551 Attribute numElem, TypeAttr type,
552 Attribute &initValues) {
554 if (isa<ArrayAttr>(numElem)) {
555 depth = llvm::dyn_cast<mlir::IntegerAttr>(
556 llvm::dyn_cast<mlir::ArrayAttr>(numElem)[0])
559 depth = llvm::dyn_cast<mlir::IntegerAttr>(numElem).getInt();
561 auto objfifoType = llvm::cast<AIEObjectFifoType>(type.getValue());
562 auto memrefType = llvm::cast<MemRefType>(objfifoType.getElementType());
564 if (!memrefType.hasStaticShape())
565 return parser.emitError(parser.getNameLoc())
566 <<
"type should be static shaped memref, but got " << memrefType;
568 if (parser.parseOptionalEqual())
571 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
572 if (parser.parseAttribute(initValues, tensorType))
574 for (
int i = 0; i < depth; i++) {
575 auto initialValues = llvm::dyn_cast<mlir::ArrayAttr>(initValues);
576 if ((
int)initialValues.size() != depth)
577 return parser.emitError(parser.getNameLoc())
578 <<
"initial values should initialize all objects";
579 if (!llvm::isa<ElementsAttr>(initialValues[i]))
580 return parser.emitError(parser.getNameLoc())
581 <<
"initial value should be an elements attribute";
591LogicalResult ObjectFifoLinkOp::verify() {
592 if (isJoin() && isDistribute())
593 return emitError(
"ObjectFifoLinkOp does not support 'join' and "
594 "'distribute' at the same time");
596 auto sharedTile = getOptionalSharedTile();
598 return emitError(
"ObjectFifoLinkOp must have a link point, i.e., a "
599 "shared tile between objectFifos");
601 TileOp tile = cast<TileOp>(sharedTile.value().getDefiningOp());
602 if (!tile.isMemTile()) {
603 if (isJoin() || isDistribute())
604 return emitError(
"ObjectFifoLinkOp join and distribute are "
605 "unavailable on compute or shim tiles");
609 if (getFifoIns().size() != getSrcOffsets().size())
610 return emitOpError(
"number of provided src offsets must be equal "
611 "to the number of input objectFifos");
613 if (!getDstOffsets().
empty())
614 return emitOpError(
"dst offsets should be empty for join");
616 }
else if (isDistribute()) {
617 if (getFifoOuts().size() != getDstOffsets().size())
618 return emitOpError(
"number of provided dst offsets must be equal "
619 "to the number of output objectFifos");
621 if (!getSrcOffsets().
empty())
622 return emitOpError(
"src offsets should be empty for distribute");
624 ObjectFifoCreateOp fifoIn = getInputObjectFifos()[0];
625 if (!fifoIn.getDimensionsToStream().empty()) {
626 return emitOpError(
"currently does not support objectFifos with "
627 "dimensionsToStream.");
629 for (
auto dims : fifoIn.getDimensionsFromStreamPerConsumer()) {
631 return emitOpError(
"currently does not support objectFifos with "
632 "dimensionsFromStreamPerConsumer.");
635 for (
auto fifoOut : getOutputObjectFifos()) {
636 for (
auto dims : fifoOut.getDimensionsFromStreamPerConsumer()) {
638 return emitOpError(
"currently does not support objectFifos with "
639 "dimensionsFromStreamPerConsumer.");
643 std::vector<int> repeat_counts;
644 for (
auto fifoOut : getOutputObjectFifos()) {
645 if (fifoOut.getRepeatCount().has_value())
646 repeat_counts.push_back(fifoOut.getRepeatCount().value());
648 repeat_counts.push_back(0);
650 for (
auto repeat : repeat_counts)
651 if (repeat_counts[0] != repeat)
652 return emitError(
"repeat counts of output object FIFOs must be equal");
655 if (!getSrcOffsets().
empty() && !getDstOffsets().
empty())
656 return emitOpError(
"all offsets should be empty if there is no "
657 "join or distribute");
663std::optional<Value> ObjectFifoLinkOp::getOptionalSharedTile() {
665 auto fifoOut = getOutputObjectFifos()[0];
666 for (
auto fifoIn : getInputObjectFifos())
667 if (fifoOut.getProducerTile() != fifoIn.getConsumerTiles()[0])
669 return {fifoOut.getProducerTile()};
672 if (isDistribute()) {
673 auto fifoIn = getInputObjectFifos()[0];
674 for (
auto fifoOut : getOutputObjectFifos())
675 if (fifoIn.getConsumerTiles()[0] != fifoOut.getProducerTile())
677 return {fifoIn.getConsumerTiles()[0]};
680 auto fifoIn = getInputObjectFifos();
681 if (
auto fifoOut = getOutputObjectFifos();
682 !fifoIn.empty() && !fifoOut.empty())
683 for (
auto consumerIn : fifoIn[0].getConsumerTiles())
684 if (consumerIn == fifoOut[0].getProducerTile())
685 return {fifoOut[0].getProducerTile()};
689std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getInputObjectFifos() {
690 std::vector<ObjectFifoCreateOp> inputObjFifos;
691 Operation *parent = getOperation();
692 while ((parent = parent->getParentOp())) {
693 if (parent->hasTrait<OpTrait::SymbolTable>()) {
694 for (
auto sym : getFifoIns()) {
695 auto name = dyn_cast<FlatSymbolRefAttr>(sym);
696 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
697 isa_and_nonnull<ObjectFifoCreateOp>(st))
698 inputObjFifos.push_back(dyn_cast<ObjectFifoCreateOp>(st));
702 return inputObjFifos;
705std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getOutputObjectFifos() {
706 std::vector<ObjectFifoCreateOp> outputObjFifos;
707 Operation *parent = getOperation();
708 while ((parent = parent->getParentOp())) {
709 if (parent->hasTrait<OpTrait::SymbolTable>()) {
710 for (
auto sym : getFifoOuts()) {
711 auto name = dyn_cast<FlatSymbolRefAttr>(sym);
712 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
713 isa_and_nonnull<ObjectFifoCreateOp>(st))
714 outputObjFifos.push_back(dyn_cast<ObjectFifoCreateOp>(st));
718 return outputObjFifos;
721std::vector<int> ObjectFifoLinkOp::getJoinTransferLengths() {
722 std::vector<int> lengths;
725 llvm::cast<AIEObjectFifoType>(getOutputObjectFifos()[0].getElemType());
726 auto elemTypeOut = llvm::cast<MemRefType>(fifoOut.getElementType());
727 int lenOut = elemTypeOut.getNumElements();
728 for (
size_t i = 0; i < getFifoIns().size(); i++) {
730 int offset = *getConstantIntValue(getSrcOffsets()[i]);
731 if (i == getFifoIns().size() - 1)
732 len = lenOut - *getConstantIntValue(getSrcOffsets()[i]);
734 len = *getConstantIntValue(getSrcOffsets()[i + 1]) - offset;
735 lengths.push_back(len);
741std::vector<int> ObjectFifoLinkOp::getDistributeTransferLengths() {
742 std::vector<int> lengths;
743 if (isDistribute()) {
745 llvm::cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType());
746 auto elemTypeIn = llvm::cast<MemRefType>(fifoIn.getElementType());
747 int lenIn = elemTypeIn.getNumElements();
748 for (
size_t i = 0; i < getFifoOuts().size(); i++) {
749 int offset = *getConstantIntValue(getDstOffsets()[i]);
751 if (i == getFifoOuts().size() - 1)
752 len = lenIn - *getConstantIntValue(getDstOffsets()[i]);
754 len = *getConstantIntValue(getDstOffsets()[i + 1]) - offset;
755 lengths.push_back(len);
761std::optional<int> ObjectFifoLinkOp::getRepeatCount() {
762 for (
auto fifoOut : getOutputObjectFifos())
763 if (fifoOut.getRepeatCount().has_value())
764 return {fifoOut.getRepeatCount().
value()};
772LogicalResult ObjectFifoRegisterExternalBuffersOp::verify() {
773 if (!getTileOp().isShimTile())
774 return emitOpError(
"tile is not a shim tile");
779TileOp ObjectFifoRegisterExternalBuffersOp::getTileOp() {
780 return cast<TileOp>(getTile().getDefiningOp());
783ObjectFifoCreateOp ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
784 Operation *parent = getOperation();
785 while ((parent = parent->getParentOp())) {
786 if (parent->hasTrait<OpTrait::SymbolTable>()) {
787 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
788 isa_and_nonnull<ObjectFifoCreateOp>(st))
789 return dyn_cast<ObjectFifoCreateOp>(st);
799LogicalResult ObjectFifoAcquireOp::verify() {
800 auto parent = getOperation()->getParentOfType<CoreOp>();
801 if (parent ==
nullptr)
802 return emitOpError(
"must be called from inside a CoreOp");
804 auto coreTile = parent.getTile();
805 auto objFifo = getObjectFifo();
806 if (getPort() == ObjectFifoPort::Produce) {
807 if (coreTile != objFifo.getProducerTile())
808 return parent.emitOpError(
809 "producer port of objectFifo accessed by core running "
810 "on non-producer tile");
811 }
else if (getPort() == ObjectFifoPort::Consume) {
813 for (
auto consumerTile : objFifo.getConsumerTiles()) {
814 if (coreTile == consumerTile) {
820 return parent.emitOpError(
821 "consumer port of objectFifo accessed by core running "
822 "on non-consumer tile");
826 llvm::cast<AIEObjectFifoType>(getObjectFifo().getElemType())
828 auto objFifoSubviewElem =
829 llvm::cast<AIEObjectFifoSubviewType>(getResult().getType())
831 if (objFifoElem != objFifoSubviewElem)
833 "ObjectFifo element and ObjectFifoSubview element must match.\n");
838ObjectFifoCreateOp ObjectFifoAcquireOp::getObjectFifo() {
839 Operation *parent = getOperation();
840 while ((parent = parent->getParentOp())) {
841 if (parent->hasTrait<OpTrait::SymbolTable>()) {
842 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
843 isa_and_nonnull<ObjectFifoCreateOp>(st))
844 return dyn_cast<ObjectFifoCreateOp>(st);
854LogicalResult ObjectFifoReleaseOp::verify() {
855 auto parent = getOperation()->getParentOfType<CoreOp>();
856 if (parent ==
nullptr)
857 return emitOpError(
"must be called from inside a CoreOp");
859 auto coreTile = parent.getTile();
860 auto objFifo = getObjectFifo();
861 if (getPort() == ObjectFifoPort::Produce) {
862 if (coreTile != objFifo.getProducerTile())
863 return parent.emitOpError(
864 "producer port of objectFifo accessed by core running "
865 "on non-producer tile");
866 }
else if (getPort() == ObjectFifoPort::Consume) {
868 for (
auto consumerTile : objFifo.getConsumerTiles()) {
869 if (coreTile == consumerTile) {
875 return parent.emitOpError(
876 "consumer port of objectFifo accessed by core running "
877 "on non-consumer tile");
883ObjectFifoCreateOp ObjectFifoReleaseOp::getObjectFifo() {
884 Operation *parent = getOperation();
885 while ((parent = parent->getParentOp())) {
886 if (parent->hasTrait<OpTrait::SymbolTable>()) {
887 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
888 isa_and_nonnull<ObjectFifoCreateOp>(st))
889 return dyn_cast<ObjectFifoCreateOp>(st);
899LogicalResult ObjectFifoSubviewAccessOp::verify() {
900 if (
auto parent = getOperation()->getParentOfType<CoreOp>();
902 return emitOpError(
"must be called from inside a CoreOp");
904 if (
auto acqOp = getSubview().getDefiningOp<ObjectFifoAcquireOp>();
905 getIndex() >= acqOp.acqNumber())
906 return emitOpError(
"accessed farther than number of acquired elements "
907 "(index out of bounds).");
916LogicalResult ObjectFifoRegisterProcessOp::verify() {
917 if (getProcessLength() < 1)
918 return emitOpError(
"process length must be >= 1");
920 if (getAcquirePattern().size() != getReleasePattern().size()) {
923 if (getAcquirePattern().size() != getProcessLength() &&
924 getProcessLength() != getReleasePattern().size())
926 "Acquire and Release patterns must be of equal length, or "
927 "longest length of one must be equal to process "
928 "length of the other");
934ObjectFifoCreateOp ObjectFifoRegisterProcessOp::getObjectFifo() {
935 Operation *parent = getOperation();
936 while ((parent = parent->getParentOp())) {
937 if (parent->hasTrait<OpTrait::SymbolTable>()) {
938 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
939 isa_and_nonnull<ObjectFifoCreateOp>(st))
940 return dyn_cast<ObjectFifoCreateOp>(st);
950LogicalResult CascadeFlowOp::verify() {
951 TileOp
src = getSourceTileOp();
952 TileOp
dst = getDestTileOp();
955 if (
src.isShimTile() ||
dst.isShimTile())
956 return emitOpError(
"shimTile row has no cascade stream interface");
957 if (t.isMemTile(
src.colIndex(),
src.rowIndex()) ||
958 t.isMemTile(
dst.colIndex(),
dst.rowIndex()))
959 return emitOpError(
"memTile row has no cascade stream interface");
961 if (!t.isSouth(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow()) &&
962 !t.isWest(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow()) &&
963 !t.isNorth(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow()) &&
964 !t.isEast(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow())) {
965 return emitOpError(
"tiles must be adjacent");
970TileOp CascadeFlowOp::getSourceTileOp() {
971 return cast<TileOp>(getSourceTile().getDefiningOp());
974TileOp CascadeFlowOp::getDestTileOp() {
975 return cast<TileOp>(getDestTile().getDefiningOp());
982LogicalResult ConfigureCascadeOp::verify() {
984 TileOp tile = cast<TileOp>(getTile().getDefiningOp());
985 CascadeDir inputDir = getInputDir();
986 CascadeDir outputDir = getOutputDir();
988 if (tile.isShimTile())
989 return emitOpError(
"shimTile row has no cascade stream interface");
990 if (t.isMemTile(tile.colIndex(), tile.rowIndex()))
991 return emitOpError(
"memTile row has no cascade stream interface");
993 if (isa<AIE2TargetModel>(t)) {
994 if (inputDir == CascadeDir::South || inputDir == CascadeDir::East) {
995 return emitOpError(
"input direction of cascade must be North or West on ")
996 << stringifyAIEArch(t.getTargetArch());
998 if (outputDir == CascadeDir::North || outputDir == CascadeDir::West) {
1000 "output direction of cascade must be South or East on ")
1001 << stringifyAIEArch(t.getTargetArch());
1004 return emitOpError(
"cascade not supported in ")
1005 << stringifyAIEArch(t.getTargetArch());
1014LogicalResult PutCascadeOp::verify() {
1016 Type type = getCascadeValue().getType();
1017 DataLayout dataLayout = DataLayout::closest(*
this);
1018 auto bits = dataLayout.getTypeSizeInBits(type);
1019 auto archbits = targetModel.getAccumulatorCascadeSize();
1020 if (bits != archbits)
1021 return emitOpError(
"type must match architecture cascade width (")
1022 << archbits <<
" bits in "
1023 << stringifyAIEArch(targetModel.getTargetArch()) <<
")";
1031LogicalResult GetCascadeOp::verify() {
1033 Type type = getCascadeValue().getType();
1034 DataLayout dataLayout = DataLayout::closest(*
this);
1035 auto bits = dataLayout.getTypeSizeInBits(type);
1036 if (isa<AIE1TargetModel>(targetModel)) {
1038 return emitOpError(
"must be a 384-bit type");
1039 }
else if (isa<AIE2TargetModel>(targetModel)) {
1041 return emitOpError(
"must be a 512-bit type");
1043 return emitOpError(
"cascade not supported in ")
1044 << stringifyAIEArch(targetModel.getTargetArch());
1060LogicalResult TileOp::verify() {
1062 int columns = targetModel.columns();
1063 int rows = targetModel.rows();
1064 if (colIndex() >= columns)
1065 return emitOpError(
"column index (")
1067 <<
") must be less than the number of columns in the device ("
1069 if (rowIndex() >= rows)
1070 return emitOpError(
"row index (")
1072 <<
") must be less than the number of rows in the device (" << rows
1075 auto users = getResult().getUsers();
1077 for (
auto *user : users) {
1078 if (llvm::isa<SwitchboxOp>(*user)) {
1080 return emitOpError(
"can only have one switchbox");
1085 if (isShimTile() && getAllocationScheme())
1086 return emitOpError(
"Shim tiles cannot have an allocation scheme");
1091size_t TileOp::getNumSourceConnections(WireBundle bundle) {
1093 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
1097 if (targetModel.isShimNOCTile(getCol(), getRow()) ||
1098 targetModel.isShimPLTile(getCol(), getRow()))
1099 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
1101 return targetModel.getNumDestSwitchboxConnections(getCol(), getRow(),
1107size_t TileOp::getNumDestConnections(WireBundle bundle) {
1109 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
1113 if (targetModel.isShimNOCTile(getCol(), getRow()) ||
1114 targetModel.isShimPLTile(getCol(), getRow()))
1115 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
1117 return targetModel.getNumSourceSwitchboxConnections(getCol(), getRow(),
1123bool TileOp::isMemTile() {
1125 return targetModel.isMemTile(getCol(), getRow());
1128bool TileOp::isShimNOCTile() {
1130 return targetModel.isShimNOCTile(getCol(), getRow());
1133bool TileOp::isShimPLTile() {
1135 return targetModel.isShimPLTile(getCol(), getRow());
1138bool TileOp::isShimNOCorPLTile() {
1140 return targetModel.isShimNOCorPLTile(getCol(), getRow());
1144 MasterSetOp masterOp, PacketRulesOp slaveOp) {
1145 auto srcBundle = slaveOp.sourcePort().bundle;
1146 auto srcChan = slaveOp.sourcePort().channel;
1147 auto dstBundle = masterOp.destPort().bundle;
1148 auto dstChan = masterOp.destPort().channel;
1150 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
1154 ConnectOp connectOp) {
1155 auto srcBundle = connectOp.getSourceBundle();
1156 auto srcChan = connectOp.getSourceChannel();
1157 auto dstBundle = connectOp.getDestBundle();
1158 auto dstChan = connectOp.getDestChannel();
1160 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
1163TileOp TileOp::getOrCreate(mlir::OpBuilder builder, DeviceOp device,
int col,
1165 TileOp tile =
nullptr;
1167 for (
auto t : device.getOps<
AIE::TileOp>()) {
1168 if (t.getRow() ==
row && t.getCol() ==
col) {
1175 OpBuilder::InsertionGuard guard(builder);
1176 mlir::Block &device_start_block = *device.getBodyRegion().begin();
1177 builder.setInsertionPointToStart(&device_start_block);
1178 tile = builder.create<TileOp>(builder.getUnknownLoc(),
1179 builder.getIndexType(),
col,
row);
1188LogicalResult ShimSwitchboxOp::verify() {
1189 Region &body = getConnections();
1190 DenseSet<Port> destset;
1192 return emitOpError(
"should have non-empty body");
1194 for (
auto &ops : body.front()) {
1195 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1196 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1197 if (destset.count(dest))
1198 return connectOp.emitOpError(
"targets same destination ")
1199 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1200 <<
" as another connect operation";
1201 destset.insert(dest);
1202 }
else if (isa<EndOp>(ops)) {
1205 return ops.emitOpError(
"cannot be contained in a Switchbox op");
1216LogicalResult ShimMuxOp::verify() {
1217 Region &body = getConnections();
1218 DenseSet<Port> destset;
1220 return emitOpError(
"should have non-empty body");
1222 for (
auto &ops : body.front()) {
1223 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1224 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1225 if (destset.count(dest))
1226 return connectOp.emitOpError(
"targets same destination ")
1227 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1228 <<
" as another connect operation";
1229 destset.insert(dest);
1230 }
else if (isa<EndOp>(ops)) {
1233 return ops.emitOpError(
"cannot be contained in a Switchbox op");
1239size_t ShimMuxOp::getNumSourceConnections(WireBundle bundle) {
1240 auto tile = getTileOp();
1242 return targetModel.getNumSourceShimMuxConnections(tile.getCol(),
1243 tile.getRow(), bundle);
1246size_t ShimMuxOp::getNumDestConnections(WireBundle bundle) {
1247 auto tile = getTileOp();
1249 return targetModel.getNumDestShimMuxConnections(tile.getCol(), tile.getRow(),
1253TileOp ShimMuxOp::getTileOp() {
1254 return cast<TileOp>(getTile().getDefiningOp());
1257int ShimMuxOp::colIndex() {
return getTileOp().colIndex(); }
1259int ShimMuxOp::rowIndex() {
return getTileOp().rowIndex(); }
1265LogicalResult ShimDMAOp::verify() {
1266 if (!getTileOp().isShimNOCTile())
1267 return emitOpError(
"must be in a ShimTile with a NOC connection");
1275TileOp ShimDMAOp::getTileOp() {
1276 return cast<TileOp>(getTile().getDefiningOp());
1279int ShimDMAOp::colIndex() {
return getTileOp().colIndex(); }
1281int ShimDMAOp::rowIndex() {
return getTileOp().rowIndex(); }
1283LogicalResult PacketRulesOp::verify() {
1284 if (Region &body = getRules(); body.empty())
1285 return emitOpError(
"should have non-empty body");
1289LogicalResult PacketFlowOp::verify() {
1290 Region &body = getPorts();
1292 return emitOpError(
"should have non-empty body");
1294 for (
auto &ops : body.front()) {
1295 if (!isa<PacketSourceOp, PacketDestOp, EndOp>(ops))
1296 return ops.emitOpError(
"cannot be contained in a PacketFlow op");
1306LogicalResult CoreOp::verify() {
1307 if (getBody().
empty())
1308 return emitOpError(
"should have non-empty body");
1309 if (getTileOp().isShimTile())
1310 return emitOpError(
"CoreOp cannot be created on shim tile, i.e. row == 0");
1311 if (getTileOp().isMemTile())
1312 return emitOpError(
"CoreOp cannot be created on mem tile");
1316int CoreOp::colIndex() {
return getTileOp().colIndex(); }
1318int CoreOp::rowIndex() {
return getTileOp().rowIndex(); }
1320TileOp CoreOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
1326int64_t BufferOp::getAllocationSize() {
1327 auto type = llvm::cast<MemRefType>(getType());
1328 DataLayout dataLayout = DataLayout::closest(getOperation());
1329 return type.getNumElements() * dataLayout.getTypeSize(type.getElementType());
1332TileOp BufferOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
1334LogicalResult BufferOp::verify() {
1335 if (UsesAreAccessible::verifyTrait(*this).failed())
1343 if (
auto buf = dyn_cast<BufferOp>(bufOp)) {
1344 assert(buf.getAddress().has_value() &&
"buffer must have address assigned");
1345 return buf.getAddress().value();
1347 if (isa_and_nonnull<ExternalBufferOp>(bufOp))
1348 llvm::report_fatal_error(
1349 "External buffer addresses are assigned at runtime.");
1350 llvm::report_fatal_error(
"unknown buffer type");
1353void xilinx::AIE::collectTiles(DeviceOp &device,
1354 DenseMap<TileID, Operation *> &tiles) {
1355 for (
auto tile : device.getOps<TileOp>()) {
1356 int colIndex = tile.colIndex();
1357 int rowIndex = tile.rowIndex();
1358 tiles[{colIndex, rowIndex}] = tile;
1362void xilinx::AIE::collectBuffers(
1364 DenseMap<Operation *, SmallVector<BufferOp, 4>> &buffers) {
1365 for (BufferOp buffer : device.getOps<BufferOp>()) {
1366 Operation *tileOp = buffer.getTile().getDefiningOp();
1367 buffers[tileOp].push_back(buffer);
1371static void printBufferInitialValue(OpAsmPrinter &p, BufferOp op, Type type,
1372 Attribute initialValue) {
1373 if (op.getInitialValue()) {
1375 p.printAttributeWithoutType(initialValue);
1379static ParseResult parseBufferInitialValue(OpAsmParser &parser, Type &type,
1380 Attribute &initialValue) {
1381 auto memrefType = llvm::cast<MemRefType>(type);
1382 if (!memrefType.hasStaticShape())
1383 return parser.emitError(parser.getNameLoc())
1384 <<
"type should be static shaped memref, but got " << type;
1386 if (parser.parseOptionalEqual())
1389 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
1390 if (parser.parseAttribute(initialValue, tensorType))
1392 if (!llvm::isa<ElementsAttr>(initialValue))
1393 return parser.emitError(parser.getNameLoc())
1394 <<
"initial value should be an elements attribute";
1402LogicalResult MemOp::verify() {
1403 Region &body = getBody();
1408 for (
auto &bodyOp : body.getOps()) {
1409 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp))
1410 if (!allocOp->getAttr(
"id"))
1411 return allocOp.emitOpError()
1412 <<
"allocOp in MemOp region should have an id attribute";
1417TileOp MemOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
1419int MemOp::colIndex() {
return getTileOp().colIndex(); }
1421int MemOp::rowIndex() {
return getTileOp().rowIndex(); }
1427LogicalResult MemTileDMAOp::verify() {
1428 assert(getOperation()->getNumRegions() == 1 &&
1429 "MemTileDMAOp has zero region!");
1435 for (
auto &bodyOp : getBody().getOps()) {
1436 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp)) {
1437 if (!allocOp->getAttr(
"id"))
1438 return allocOp.emitOpError()
1439 <<
"allocOp in MemTileDMAOp region should have an id attribute";
1441 if (
auto startOp = dyn_cast<DMAStartOp>(bodyOp)) {
1442 if (startOp.getChannelIndex() > 3) {
1448 llvm::SmallSet<Block *, 16> reachable;
1449 SmallVector<Block *, 16> worklist;
1450 Block *firstBD = startOp.getSuccessor(0);
1451 reachable.insert(firstBD);
1452 worklist.push_back(firstBD);
1453 while (!worklist.empty()) {
1454 Block *block = worklist.pop_back_val();
1457 auto successors = block->getTerminator()->getSuccessors();
1458 for (
auto *i : successors) {
1459 if (!reachable.contains(i)) {
1460 reachable.insert(i);
1461 worklist.push_back(i);
1465 for (Block *b : reachable) {
1466 for (DMABDOp bd : b->getOps<DMABDOp>()) {
1467 if (
auto bufferOp = bd.getBufferOp();
1468 bufferOp.getTileOp().colIndex() != colIndex() ||
1469 bufferOp.getTileOp().rowIndex() != rowIndex()) {
1470 InFlightDiagnostic err =
1472 <<
"is reachable from DMA channel "
1473 << startOp.getChannelIndex()
1474 <<
" and attempts to access a non-local buffer\n";
1475 err.attachNote(startOp->getLoc()) <<
"channel";
1476 err.attachNote(bufferOp->getLoc()) <<
"buffer";
1480 for (
auto useLock : b->getOps<UseLockOp>()) {
1481 if (
auto lockOp = useLock.getLockOp();
1482 lockOp.getTileOp().colIndex() != colIndex() ||
1483 lockOp.getTileOp().rowIndex() != rowIndex()) {
1484 InFlightDiagnostic err =
1485 useLock.emitOpError()
1486 <<
"is reachable from DMA channel "
1487 << startOp.getChannelIndex()
1488 <<
" and attempts to access a non-local lock\n";
1489 err.attachNote(startOp->getLoc()) <<
"channel";
1490 err.attachNote(lockOp->getLoc()) <<
"lock";
1506LogicalResult DMAOp::verify() {
1507 auto *parentOp = getOperation()->getParentOp();
1508 if (parentOp->getRegion(0).getBlocks().size() > 1)
1509 return emitOpError(
"DMAOp can only appear in single block region");
1510 if (!parentOp->getRegion(0).getOps<DMAStartOp>().empty())
1511 return emitOpError(
"DMAOp is not compatible with DMAStart ops");
1512 auto bdRegions = getBds();
1513 for (
auto &bdRegion : bdRegions) {
1514 if (!bdRegion.hasOneBlock())
1515 return emitOpError(
"DMAOp regions must have only one block");
1516 auto bds = llvm::to_vector_of<DMABDOp>(bdRegion.front().getOps<DMABDOp>());
1517 if (bds.size() != 1)
1518 return emitOpError(
"DMAOp regions/blocks must have exactly one DMABDOp");
1520 llvm::to_vector_of<UseLockOp>(bdRegion.front().getOps<UseLockOp>());
1521 if (useLocks.size() != 2)
1523 "DMAOp regions/blocks must have exactly two UseLock ops");
1532BufferOp DMABDOp::getBufferOp() {
1533 return cast<BufferOp>(getBuffer().getDefiningOp());
1541ParseResult DMABDOp::parse(OpAsmParser &parser, OperationState &result) {
1542 OpAsmParser::UnresolvedOperand bufferRawOperand{};
1543 ::llvm::ArrayRef<OpAsmParser::UnresolvedOperand> bufferOperands(
1544 &bufferRawOperand, 1);
1545 ::llvm::SMLoc bufferOperandsLoc;
1546 (void)bufferOperandsLoc;
1547 Type bufferRawType{};
1548 ::llvm::ArrayRef<Type> bufferTypes(&bufferRawType, 1);
1549 IntegerAttr offsetAttr;
1550 IntegerAttr lenAttr;
1551 ::xilinx::AIE::BDDimLayoutArrayAttr dimensionsAttr;
1552 ::xilinx::AIE::BDPadLayoutArrayAttr pad_dimensionsAttr;
1553 IntegerAttr pad_valueAttr;
1554 if (parser.parseLParen())
1557 bufferOperandsLoc = parser.getCurrentLocation();
1558 if (parser.parseOperand(bufferRawOperand))
1560 if (parser.parseColon())
1562 if (parser.parseCustomTypeWithFallback(bufferRawType))
1566 if (succeeded(parser.parseOptionalComma())) {
1567 if (parser.parseCustomAttributeWithFallback(
1568 offsetAttr, parser.getBuilder().getIntegerType(32))) {
1572 offsetAttr = parser.getBuilder().getIntegerAttr(
1573 parser.getBuilder().getIntegerType(32), 0);
1574 result.getOrAddProperties<DMABDOp::Properties>().offset = offsetAttr;
1578 if (succeeded(parser.parseOptionalComma())) {
1579 if (parser.parseCustomAttributeWithFallback(
1580 lenAttr, parser.getBuilder().getIntegerType(32))) {
1584 result.getOrAddProperties<DMABDOp::Properties>().len = lenAttr;
1588 if (succeeded(parser.parseOptionalComma())) {
1589 if (parser.parseCustomAttributeWithFallback(dimensionsAttr, Type{})) {
1593 result.getOrAddProperties<DMABDOp::Properties>().dimensions =
1598 if (succeeded(parser.parseOptionalComma())) {
1599 if (parser.parseCustomAttributeWithFallback(pad_dimensionsAttr, Type{})) {
1602 if (pad_dimensionsAttr)
1603 result.getOrAddProperties<DMABDOp::Properties>().pad_dimensions =
1608 if (succeeded(parser.parseOptionalComma())) {
1609 if (parser.parseKeyword(
"pad_value"))
1611 if (parser.parseEqual())
1614 if (parser.parseCustomAttributeWithFallback(
1615 pad_valueAttr, parser.getBuilder().getIntegerType(32))) {
1619 result.getOrAddProperties<DMABDOp::Properties>().pad_value =
1622 if (parser.parseRParen())
1625 auto loc = parser.getCurrentLocation();
1626 if (parser.parseOptionalAttrDict(result.attributes))
1628 if (failed(verifyInherentAttrs(result.name, result.attributes, [&]() {
1629 return parser.emitError(loc)
1630 <<
"'" << result.name.getStringRef() <<
"' op ";
1634 if (parser.resolveOperands(bufferOperands, bufferTypes, bufferOperandsLoc,
1641void DMABDOp::print(::mlir::OpAsmPrinter &printer) {
1643 printer << getBuffer();
1644 printer <<
' ' <<
":";
1647 auto type = getBuffer().getType();
1648 if (
auto validType = ::llvm::dyn_cast<::mlir::MemRefType>(type))
1649 printer.printStrippedAttrOrType(validType);
1655 ::mlir::OpBuilder((*this)->getContext())
1657 ::mlir::OpBuilder((*this)->getContext()).getIntegerType(32),
1661 printer.printAttributeWithoutType(getOffsetAttr());
1666 printer.printAttributeWithoutType(getLenAttr());
1668 if (getDimensionsAttr()) {
1671 printer.printStrippedAttrOrType(getDimensionsAttr());
1673 if (getPadDimensionsAttr()) {
1676 printer.printStrippedAttrOrType(getPadDimensionsAttr());
1678 if ((getPadValueAttr() &&
1679 getPadValueAttr() !=
1680 ::mlir::OpBuilder((*this)->getContext())
1682 ::mlir::OpBuilder((*this)->getContext()).getIntegerType(32),
1685 printer <<
' ' <<
"pad_value";
1686 printer <<
' ' <<
"=";
1688 printer.printAttributeWithoutType(getPadValueAttr());
1691 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
1692 elidedAttrs.push_back(
"offset");
1693 elidedAttrs.push_back(
"len");
1694 elidedAttrs.push_back(
"dimensions");
1695 elidedAttrs.push_back(
"pad_dimensions");
1696 elidedAttrs.push_back(
"pad_value");
1697 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
1700LogicalResult DMABDOp::verify() {
1704 Operation *p = (*this)->getParentOp();
1705 if (!llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(*p)) {
1709 if (!isa<BufferOp, ExternalBufferOp>(getBuffer().getDefiningOp()))
1711 "BDs only support BufferOp or ExternalBufferOp operands.");
1713 if (getLenInBytes() % 4)
1714 return emitOpError(
"transfer length must be multiple of 4 (i.e., represent "
1715 "4 byte aligned address)");
1717 TileID parentTileId = getParentTileElement(getOperation()).getTileID();
1719 if (getOperation()->getParentOfType<MemOp>() &&
1720 (getBufferOp().getTileOp().colIndex() != parentTileId.col ||
1721 getBufferOp().getTileOp().rowIndex() != parentTileId.row))
1723 "Core tile DMAs can only access a buffer in the same tile.");
1727 uint32_t maxBds = targetModel.
getNumBDs(parentTileId.col, parentTileId.row);
1728 if (std::optional<int32_t> bdId = getBdId();
1729 bdId.has_value() &&
static_cast<uint32_t
>(*bdId) >= maxBds)
1730 return emitOpError(
"bdId attribute exceeds max: ") << maxBds - 1;
1731 if (std::optional<int32_t> nextBdId = getNextBdId();
1732 nextBdId.has_value() &&
static_cast<uint32_t
>(*nextBdId) >= maxBds)
1733 return emitOpError(
"nextBdId attribute exceeds max: ") << maxBds - 1;
1734 if (
auto dims = getDimensions(); dims.has_value()) {
1735 size_t maxNDims = 3;
1736 if (getOperation()->getParentOfType<MemTileDMAOp>())
1738 if (dims->size() > maxNDims)
1739 return emitOpError() <<
"Cannot give more than "
1740 << std::to_string(maxNDims)
1741 <<
" dimensions for step sizes and wraps in this "
1743 << std::to_string(dims->size()) <<
" dimensions).";
1745 MemRefType buffer = getBuffer().getType();
1747 for (BDDimLayoutAttr dim : *dims) {
1748 maxIdx += dim.getStride() * (dim.getSize() - 1);
1749 if (0 == dim.getStride())
1750 return emitOpError()
1751 <<
"Invalid step size; must be a positive integer.";
1752 if (dim.getStride() > buffer.getNumElements())
1753 return emitOpError() <<
"Step size " << std::to_string(dim.getStride())
1754 <<
" exceeds memref size "
1755 << std::to_string(buffer.getNumElements());
1756 if (dim.getSize() >= (1UL << 9) + 1)
1757 return emitOpError() <<
"Size may not exceed 1023.";
1758 if (dim.getStride() >= (1UL << 19))
1759 return emitOpError() <<
"Stride may not exceed " << (1 << 20);
1762 if (buffer.getNumElements() <= maxIdx)
1763 return emitOpError() <<
"Specified stride(s) and size(s) result in out "
1764 "of bounds access in buffer, for index "
1765 << std::to_string(maxIdx) <<
" in memref of length "
1766 << std::to_string(buffer.getNumElements()) <<
".";
1771 if (getBufferElementTypeWidthInBytes() < 4 && dims->back().getStride() != 1)
1773 "For <32b width datatypes, inner-most dim stride must be 1");
1775 if (getBufferElementTypeWidthInBytes() > 4 && dims->back().getStride() != 1)
1777 "For >32b width datatypes, inner-most dim stride must be 1");
1779 if (
auto paddims = getPadDimensions(); paddims.has_value()) {
1780 auto dims = getDimensions();
1781 if (!dims.has_value())
1782 return emitOpError() <<
"Padding requires n-d data layouts expressed as"
1783 <<
" wrap(s) and stride(s).";
1784 if (!targetModel.
isMemTile(parentTileId.col, parentTileId.row))
1785 return emitOpError() <<
"Padding is only supported by memtile dma bds.";
1786 if (dims->size() != paddims->size())
1787 return emitOpError() <<
"Mismatch number of dimensions between padding(s)"
1788 <<
" and wrap(s) and stride(s).";
1790 for (
unsigned i = 0; i < paddims->size(); i++) {
1791 auto dim = (*dims)[i];
1792 auto paddim = (*paddims)[i];
1793 actuallen *= paddim.getConstPadBefore() + paddim.getConstPadAfter() +
1795 if (actuallen > getLen())
1796 return emitOpError() <<
"Data exceeds len after padding.";
1798 if ((paddims->back().getConstPadBefore() *
1799 getBufferElementTypeWidthInBytes()) %
1801 return emitOpError() <<
"Inner-most padding-before count must result in"
1802 <<
" padding in 32-bit words.";
1803 if ((paddims->back().getConstPadAfter() *
1804 getBufferElementTypeWidthInBytes()) %
1806 return emitOpError() <<
"Inner-most padding-after count must result in"
1807 <<
" padding in 32-bit words.";
1809 if (targetModel.
isMemTile(parentTileId.col, parentTileId.row) ||
1810 targetModel.
isCoreTile(parentTileId.col, parentTileId.row)) {
1811 if (
auto baseAddr = getBufferOp().getAddress(); baseAddr.has_value()) {
1812 int offsetInBytes = *baseAddr + getOffsetInBytes();
1813 if (offsetInBytes % 4)
1814 return emitOpError(
"bd address must be 4 byte (32b) aligned; got "
1816 << offsetInBytes <<
" (bytes)";
1819 if (
auto packetInfo = getPacket()) {
1820 if (packetInfo->getPktType() > 7)
1821 return emitOpError(
"Packet type field can only hold 3 bits.");
1822 if (packetInfo->getPktId() > 31)
1823 return emitOpError(
"Packet ID field can only hold 5 bits.");
1826 if (!getLen() && !getBuffer().getType().hasStaticShape())
1827 return emitOpError() <<
"buffer with dynamic shape requires static length.";
1829 if (getBurstLength() != 0 &&
1830 !targetModel.
isShimNOCTile(parentTileId.col, parentTileId.row))
1831 return emitOpError(
"Burst length is only supported in Shim NOC tiles that "
1832 "are connected to the memory-mapped NOC.");
1837TileOp MemTileDMAOp::getTileOp() {
1838 return cast<TileOp>(getTile().getDefiningOp());
1841int MemTileDMAOp::colIndex() {
return getTileOp().colIndex(); }
1843int MemTileDMAOp::rowIndex() {
return getTileOp().rowIndex(); }
1849static LogicalResult FoldDMAStartOp(DMAStartOp op, PatternRewriter &rewriter) {
1851 llvm::SetVector<Block *> reachable;
1852 SmallVector<Block *, 16> worklist;
1853 Block *firstBD = op.getSuccessor(0);
1854 reachable.insert(firstBD);
1855 worklist.push_back(firstBD);
1856 while (!worklist.empty()) {
1857 Block *block = worklist.pop_back_val();
1860 auto successors = block->getTerminator()->getSuccessors();
1861 for (
auto *i : successors) {
1862 if (!reachable.contains(i)) {
1863 reachable.insert(i);
1864 worklist.push_back(i);
1871 if (isa<EndOp>((reachable.back())->getTerminator()))
1875 auto areEquivalentBDs = [](Block *b1, Block *b2) {
1876 auto b1OpRange = b1->without_terminator();
1877 auto b2OpRange = b2->without_terminator();
1878 if (llvm::range_size(b1OpRange) != llvm::range_size(b2OpRange))
1880 if (!llvm::all_of(llvm::zip_equal(b1OpRange, b2OpRange),
1881 [](std::tuple<Operation &, Operation &> pair) {
1882 return OperationEquivalence::isEquivalentTo(
1883 &std::get<0>(pair), &std::get<1>(pair),
1884 OperationEquivalence::IgnoreLocations);
1891 SmallVector<Block *> uniquePattern;
1892 auto patternIt = reachable.begin();
1893 while (patternIt != reachable.end() &&
1894 llvm::none_of(uniquePattern, [patternIt, areEquivalentBDs](Block *b1) {
1895 return areEquivalentBDs(*patternIt, b1);
1897 uniquePattern.push_back(*patternIt);
1902 while (patternIt != reachable.end()) {
1904 if (!areEquivalentBDs(*patternIt, uniquePattern[idx]))
1907 idx = (++idx) % uniquePattern.size();
1911 auto lastBDTerm = dyn_cast<NextBDOp>(reachable.back()->getTerminator());
1912 auto lastUniqueBDTerm =
1913 dyn_cast<NextBDOp>(uniquePattern.back()->getTerminator());
1914 lastUniqueBDTerm.setSuccessor(lastBDTerm.getSuccessor());
1919void DMAStartOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1920 MLIRContext *context) {
1921 patterns.add(FoldDMAStartOp);
1928LogicalResult SwitchboxOp::verify() {
1929 Region &body = getConnections();
1930 DenseSet<Port> sourceset;
1931 DenseSet<Port> destset;
1932 auto tile = getTileOp();
1935 return emitOpError(
"should have non-empty body");
1936 for (
auto &ops : body.front()) {
1938 auto checkBound = [&ops](StringRef dir, WireBundle bundle,
int index,
1939 int bound) -> LogicalResult {
1940 if (index >= bound) {
1942 return ops.emitOpError(
"index ")
1943 << index <<
" for " << dir <<
" bundle "
1944 << stringifyWireBundle(bundle) <<
" must be less than "
1946 return ops.emitOpError()
1947 << dir <<
" bundle " << stringifyWireBundle(bundle)
1948 <<
" not supported; index: " << index <<
", bound: " << bound;
1953 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1954 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
1955 sourceset.insert(source);
1957 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1958 if (destset.count(dest)) {
1959 return connectOp.emitOpError()
1960 <<
"; connecting " <<
to_string(source) <<
" to "
1962 <<
to_string(this->getTileOp().getTileID())
1963 <<
" targets same dst as another connect op; existing "
1965 << llvm::join(llvm::map_range(
1966 destset, [](
auto &p) {
return to_string(p); }),
1969 destset.insert(dest);
1971 if (connectOp.sourceIndex() < 0)
1972 return connectOp.emitOpError(
"source index cannot be less than zero");
1974 if (checkBound(
"source", connectOp.getSourceBundle(),
1975 connectOp.sourceIndex(),
1976 getNumSourceConnections(connectOp.getSourceBundle()))
1980 if (connectOp.destIndex() < 0)
1981 return connectOp.emitOpError(
"dest index cannot be less than zero");
1983 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
1984 getNumDestConnections(connectOp.getDestBundle()))
1990 return connectOp.emitOpError(
"illegal stream switch connection");
1992 }
else if (
auto connectOp = dyn_cast<MasterSetOp>(ops)) {
1993 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1994 if (destset.count(dest))
1995 return connectOp.emitOpError(
"targets same destination ")
1996 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1997 <<
" as another connect or masterset operation";
1998 destset.insert(dest);
2000 if (connectOp.destIndex() < 0)
2001 return connectOp.emitOpError(
"dest index cannot be less than zero");
2003 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
2004 getNumDestConnections(connectOp.getDestBundle()))
2009 for (
auto val : connectOp.getAmsels()) {
2010 auto amsel = dyn_cast<AMSelOp>(val.getDefiningOp());
2011 if (arbiter != -1 && arbiter != amsel.arbiterIndex())
2012 return connectOp.emitOpError(
2013 "a master port can only be tied to one arbiter");
2014 arbiter = amsel.arbiterIndex();
2016 }
else if (
auto connectOp = dyn_cast<PacketRulesOp>(ops)) {
2017 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
2018 if (sourceset.count(source))
2019 return connectOp.emitOpError(
"packet switched source ")
2020 << stringifyWireBundle(source.bundle) << source.channel
2021 <<
" cannot match another connect or masterset operation";
2022 sourceset.insert(source);
2024 }
else if (
auto amselOp = dyn_cast<AMSelOp>(ops)) {
2025 std::vector<MasterSetOp> mstrs;
2026 std::vector<PacketRulesOp> slvs;
2027 for (
auto *user : amselOp.getResult().getUsers()) {
2028 if (
auto s = dyn_cast<PacketRuleOp>(user)) {
2029 auto pktRules = dyn_cast<PacketRulesOp>(s->getParentOp());
2030 slvs.push_back(pktRules);
2031 }
else if (
auto m = dyn_cast<MasterSetOp>(user))
2034 for (
auto m : mstrs) {
2035 for (
auto s : slvs) {
2038 return amselOp->emitOpError(
"illegal stream switch connection");
2042 }
else if (isa<EndOp>(ops)) {
2045 return ops.emitOpError(
"cannot be contained in a Switchbox op");
2052TileOp SwitchboxOp::getTileOp() {
2053 return cast<TileOp>(getTile().getDefiningOp());
2056int SwitchboxOp::colIndex() {
return getTileOp().colIndex(); }
2058int SwitchboxOp::rowIndex() {
return getTileOp().rowIndex(); }
2060template <
typename... ParentOpTypes>
2063 Operation *operation = op->getParentOp();
2065 if (llvm::isa_and_nonnull<ParentOpTypes...>(operation))
2067 operation = operation->getParentOp();
2073TileOp LockOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
2075int LockOp::colIndex() {
return getTileOp().colIndex(); }
2077int LockOp::rowIndex() {
return getTileOp().rowIndex(); }
2079LogicalResult LockOp::verify() {
2080 if (
auto result = UsesAreAccessible::verifyTrait(*
this); result.failed())
2083 if (getLockID().has_value()) {
2085 auto tileOp = getTileOp();
2087 targetModel.
getNumLocks(tileOp.getCol(), tileOp.getRow());
2088 getLockID().value() >= numLocks)
2089 return emitOpError(
"lock assigned invalid id (maximum is ")
2090 << numLocks - 1 <<
")";
2098 auto *block = op->getBlock();
2100 for (
auto op : block->getOps<UseLockOp>()) {
2101 if (
auto lock = dyn_cast<LockOp>(op.getLock().getDefiningOp());
2102 lock.getLockID().has_value()) {
2103 if (lockID != -1 && lockID != lock.getLockIDValue())
2105 lockID = lock.getLockIDValue();
2114 auto *block = op->getBlock();
2115 int acqValue = -1, relValue = -1;
2116 for (
auto op : block->getOps<UseLockOp>()) {
2117 if (op.acquire() || op.acquireGE()) {
2118 if (acqValue != -1 && acqValue != op.getLockValue()) {
2121 acqValue = op.getLockValue();
2122 }
else if (op.release()) {
2123 if (relValue != -1 && relValue != op.getLockValue()) {
2126 relValue = op.getLockValue();
2135 if (
auto memOp = op->getParentOfType<MemOp>()) {
2136 auto useLock = dyn_cast<UseLockOp>(op);
2137 if (
auto lock = useLock.getLockOp();
2138 lock.getTileOp().colIndex() != memOp.colIndex() ||
2139 lock.getTileOp().rowIndex() != memOp.rowIndex())
2146LogicalResult UseLockOp::verify() {
2148 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>((*this)->getParentOp()))
2149 return (*this)->emitOpError(
"must be used in a core or memory operation.");
2152 if (targetModel.
getTargetArch() == AIEArch::AIE1 && acquireGE())
2153 return (*this)->emitOpError(
2154 "AcquireGreaterEqual is not supported in AIE1.");
2160 if (!(*this)->getBlock())
2161 return (*this)->emitOpError(
"is not in a block.");
2165 return (*this)->emitOpError(
2166 "used in a DMA block that have multiple locks.");
2169 return (*this)->emitOpError(
"acquires/releases the lock in a DMA block "
2170 "from/to multiple states.");
2174 return (*this)->emitOpError(
"can only access a lock in the same tile");
2182 return (*this)->emitOpError()
2183 <<
"expects some parent op to be one of "
2184 <<
"AIE::device, AIE::core, func::func, AIE::mem, or AIE::shimDMA";
2187#include "aie/Dialect/AIE/IR/AIEEnums.cpp.inc"
2188#include "aie/Dialect/AIE/IR/AIEInterfaces.cpp.inc"
2190#define GET_OP_CLASSES
2191#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
2193size_t SwitchboxOp::getNumSourceConnections(WireBundle bundle) {
2194 auto tile = getTileOp();
2197 tile.getRow(), bundle);
2200size_t SwitchboxOp::getNumDestConnections(WireBundle bundle) {
2201 auto tile = getTileOp();
2204 tile.getRow(), bundle);
2209 case WireBundle::North:
2210 return WireBundle::South;
2211 case WireBundle::South:
2212 return WireBundle::North;
2213 case WireBundle::East:
2214 return WireBundle::West;
2215 case WireBundle::West:
2216 return WireBundle::East;
2226ParseResult BDChainOp::parse(OpAsmParser &parser, OperationState &result) {
2227 SmallVector<OpAsmParser::Argument> entryArgs;
2230 StringAttr symNameAttr;
2231 if (parser.parseSymbolName(symNameAttr, SymbolTable::getSymbolAttrName(),
2232 result.attributes)) {
2237 ParseResult argParseResult = parser.parseCommaSeparatedList(
2238 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
2239 OpAsmParser::Argument argument;
2240 if (parser.parseArgument(argument,
true,
true)) {
2243 entryArgs.push_back(argument);
2246 if (argParseResult) {
2247 return argParseResult;
2251 auto *body = result.addRegion();
2252 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
2253 if (bodyParseResult) {
2254 return bodyParseResult;
2260void BDChainOp::print(OpAsmPrinter &printer) {
2263 ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
2266 printer.printSymbolName(taskName);
2268 Region &body = getRegion();
2269 auto argsIter = body.getArguments();
2271 for (
auto it = argsIter.begin(); it != argsIter.end(); ++it) {
2272 if (it != argsIter.begin()) {
2275 printer.printRegionArgument(*it);
2280 printer.printRegion(body,
false,
true);
2287ShimDMAAllocationOp ShimDMAAllocationOp::getForSymbol(DeviceOp device,
2288 llvm::StringRef symbol) {
2289 auto alloc_ops = device.getOps<ShimDMAAllocationOp>();
2290 for (
auto it = alloc_ops.begin(); it != alloc_ops.end(); ++it) {
2291 AIE::ShimDMAAllocationOp a = *it;
2292 if (a.getSymName() == symbol) {
2300#define GET_ATTRDEF_CLASSES
2301#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 getNumBDs(int col, int row) const =0
Return the number of buffer descriptors supported by the DMA in the given tile.
virtual bool isCoreTile(int col, int row) const =0
Return true if the given tile is a 'Core' tile.
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 bool isMemTile(int col, int row) const =0
Return true if the given tile is an AIE2 'Memory' tile.
virtual uint32_t getNumLocks(int col, int row) const =0
Return the number of lock objects.
virtual bool isShimNOCTile(int col, int row) const =0
Return true if the given tile is a Shim NOC tile.
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)
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)
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)
std::optional< AIE::ShimDMAAllocationOp > get(DeviceOp dev, mlir::StringRef sym_name)