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 ")
220 if (
auto t = dyn_cast<AIETarget>(op))
221 return t.getTargetModel();
222 if (
auto t = op->getParentOfType<AIETarget>())
223 return t.getTargetModel();
232 case AIEDevice::xcvc1902:
234 case AIEDevice::xcve2302:
236 case AIEDevice::xcve2802:
238 case AIEDevice::npu1:
240 case AIEDevice::npu1_1col:
242 case AIEDevice::npu1_2col:
244 case AIEDevice::npu1_3col:
246 case AIEDevice::npu1_4col:
248 case AIEDevice::npu2:
250 case AIEDevice::npu2_1col:
251 return NPU2model1col;
252 case AIEDevice::npu2_2col:
253 return NPU2model2col;
254 case AIEDevice::npu2_3col:
255 return NPU2model3col;
256 case AIEDevice::npu2_4col:
257 return NPU2model4col;
258 case AIEDevice::npu2_5col:
259 return NPU2model5col;
260 case AIEDevice::npu2_6col:
261 return NPU2model6col;
262 case AIEDevice::npu2_7col:
263 return NPU2model7col;
270static TileElement getParentTileElement(Operation *op) {
271 auto *parent = op->getParentOp();
272 while (!llvm::isa_and_nonnull<DeviceOp, ModuleOp>(parent)) {
273 if (
auto element = llvm::dyn_cast<TileElement>(parent))
275 parent = parent->getParentOp();
277 return llvm::dyn_cast<TileElement>(parent);
282struct UsesAreAccessible {
283 static LogicalResult verifyTrait(Operation *op) {
284 auto thisElement = cast<TileElement>(op);
285 auto thisID = thisElement.getTileID();
286 auto users = op->getResult(0).getUsers();
288 for (
auto *user : users) {
291 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>(user->getParentOp())) {
300 TileElement element = llvm::dyn_cast<TileElement>(user);
302 element = getParentTileElement(user);
306 return op->emitOpError(
"is accessed outside of a tile")
307 .attachNote(user->getLoc())
310 auto tileID = element.getTileID();
311 if (!targetModel.isLegalMemAffinity(tileID.col, tileID.row, thisID.col,
313 return (op->emitOpError(
"in Column ")
314 << thisID.col <<
" and Row " << thisID.row
315 <<
" is accessed from an unreachable tile in Column "
316 << tileID.col <<
" and Row " << tileID.row)
317 .attachNote(user->getLoc())
329template <
typename... TerminatorOpTypes>
332 for (
auto ®ion : op->getRegions()) {
333 for (
auto &block : region) {
334 if (!block.empty()) {
335 if (Operation *operation = &block.back();
336 !llvm::isa_and_nonnull<TerminatorOpTypes...>(operation))
337 return operation->emitOpError(
"is not an allowed terminator")
338 .attachNote(op->getLoc())
339 .append(
"in this context: ");
349template <
typename ConcreteType>
351 auto element = cast<ConcreteType>(op);
354 targetModel.getNumBDs(element.getTileID().col, element.getTileID().row);
357 for (
auto &block : element.getBody()) {
358 if (!block.template getOps<DMABDOp>().empty()) {
359 if (bdNum >= bdMax) {
360 auto bd = *block.template getOps<DMABDOp>().begin();
361 return (op->emitOpError(
"has more than ") << bdMax <<
" blocks")
362 .attachNote(bd.getLoc())
363 .append(
"no space for this bd: ");
373template <
typename ConcreteType>
375 DenseSet<DMAChannel> inputChannels;
376 DenseSet<DMAChannel> outputChannels;
377 auto element = cast<ConcreteType>(op);
378 Region &body = element.getBody();
380 return op->emitOpError(
"should have non-empty body");
381 for (
auto &bodyOp : body.getOps()) {
383 if (
auto dmaStart = dyn_cast<DMAStartOp>(bodyOp)) {
384 DMAChannel dmaChan = {dmaStart.getChannelDir(),
385 dmaStart.getChannelIndex()};
388 if (dmaChan.direction == DMAChannelDir::S2MM)
389 inputChannels.insert(dmaChan);
391 outputChannels.insert(dmaChan);
395 if (inputChannels.size() >
396 element.getTileOp().getNumSourceConnections(WireBundle::DMA))
397 return op->emitOpError(
398 "uses more input channels than available on this tile");
400 if (outputChannels.size() >
401 element.getTileOp().getNumDestConnections(WireBundle::DMA))
402 return op->emitOpError(
403 "uses more output channels than available on this tile");
411LogicalResult ObjectFifoCreateOp::verify() {
412 if (isa<ArrayAttr>(getElemNumber())) {
413 if (
size_t numDepths = dyn_cast<ArrayAttr>(getElemNumber()).size();
414 numDepths != getConsumerTiles().size() + 1)
415 return emitOpError(
"does not have enough depths specified for producer "
416 "and for each consumer.");
419 if (getProducerTileOp().isShimTile() && !getDimensionsToStream().empty()) {
421 "`dimensionsToStream` data layout transformations are not supported "
422 "on shim tile producers");
425 if (getViaSharedMem().has_value()) {
426 if (getConsumerTiles().size() > 1)
428 "`via_shared_mem` can only be used in 1-to-1 object FIFOs");
430 return emitError(
"`via_shared_mem` and `via_DMA` cannot occur together");
433 if (getRepeatCount().has_value()) {
434 if (getProducerTileOp().isShimTile())
435 return emitError(
"`repeat_count` unavailable for shim tiles");
438 if (getInitValues().has_value()) {
439 if (getProducerTileOp().isShimTile())
440 return emitError(
"`init_values` unavailable for shim tiles");
443 if (getInitValues().has_value()) {
444 if ((
int)getInitValues().value().size() != size())
445 return emitError(
"`init_values` does not initialize all objects");
451TileOp ObjectFifoCreateOp::getProducerTileOp() {
452 return cast<TileOp>(getProducerTile().getDefiningOp());
456 OpAsmParser &parser, OpAsmParser::UnresolvedOperand &operand,
457 BDDimLayoutArrayAttr &dimensions) {
458 std::vector<BDDimLayoutAttr> emptyDims = {};
459 if (parser.parseOperand(operand))
461 if (succeeded(parser.parseOptionalKeyword(
"dimensionsToStream"))) {
462 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
468 BDDimLayoutArrayAttr::get(parser.getContext(), ArrayRef(emptyDims));
474 Operation *op, Value operand,
475 BDDimLayoutArrayAttr dimensions) {
477 if (!dimensions.empty()) {
478 printer <<
" dimensionsToStream ";
479 printer.printStrippedAttrOrType(dimensions);
484 OpAsmParser &parser, SmallVectorImpl<OpAsmParser::UnresolvedOperand> &tiles,
485 BDDimLayoutArrayArrayAttr &dimensions) {
488 std::vector<BDDimLayoutArrayAttr> tileDims = {};
490 auto parseOneOperand = [&]() -> ParseResult {
491 if (parser.parseOperand(tiles.emplace_back(),
true)) {
497 BDDimLayoutArrayAttr dimAttr =
498 BDDimLayoutArrayAttr::get(parser.getContext(), {});
500 if (succeeded(parser.parseOptionalKeyword(
"dimensionsFromStream"))) {
502 if (parser.parseCustomAttributeWithFallback<BDDimLayoutArrayAttr>(
507 tileDims.emplace_back(dimAttr);
511 if (parser.parseCommaSeparatedList(AsmParser::Delimiter::None,
512 parseOneOperand,
" in operand list"))
515 dimensions = BDDimLayoutArrayArrayAttr::get(parser.getContext(), tileDims);
520 OpAsmPrinter &printer, Operation *op, OperandRange tiles,
521 BDDimLayoutArrayArrayAttr dimsPerTileAttr) {
523 for (
auto tile : tiles) {
525 if (dimsPerTileAttr && tileIdx < dimsPerTileAttr.size() &&
526 dimsPerTileAttr[tileIdx] && !dimsPerTileAttr[tileIdx].empty()) {
527 printer <<
" dimensionsFromStream ";
528 printer.printStrippedAttrOrType(dimsPerTileAttr[tileIdx]);
530 if (tileIdx < tiles.size() - 1) {
537static void printObjectFifoInitValues(OpAsmPrinter &p, ObjectFifoCreateOp op,
538 Attribute numElem, TypeAttr type,
539 Attribute initValues) {
540 if (op.getInitValues()) {
542 int depth = llvm::dyn_cast<mlir::IntegerAttr>(numElem).getInt();
543 for (
int i = 0; i < depth; i++) {
544 p.printStrippedAttrOrType(llvm::dyn_cast<mlir::ArrayAttr>(initValues)[i]);
553static ParseResult parseObjectFifoInitValues(OpAsmParser &parser,
554 Attribute numElem, TypeAttr type,
555 Attribute &initValues) {
557 if (isa<ArrayAttr>(numElem)) {
558 depth = llvm::dyn_cast<mlir::IntegerAttr>(
559 llvm::dyn_cast<mlir::ArrayAttr>(numElem)[0])
562 depth = llvm::dyn_cast<mlir::IntegerAttr>(numElem).getInt();
564 auto objfifoType = llvm::cast<AIEObjectFifoType>(type.getValue());
565 auto memrefType = llvm::cast<MemRefType>(objfifoType.getElementType());
567 if (!memrefType.hasStaticShape())
568 return parser.emitError(parser.getNameLoc())
569 <<
"type should be static shaped memref, but got " << memrefType;
571 if (parser.parseOptionalEqual())
574 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
575 if (parser.parseAttribute(initValues, tensorType))
577 for (
int i = 0; i < depth; i++) {
578 auto initialValues = llvm::dyn_cast<mlir::ArrayAttr>(initValues);
579 if ((
int)initialValues.size() != depth)
580 return parser.emitError(parser.getNameLoc())
581 <<
"initial values should initialize all objects";
582 if (!llvm::isa<ElementsAttr>(initialValues[i]))
583 return parser.emitError(parser.getNameLoc())
584 <<
"initial value should be an elements attribute";
594LogicalResult ObjectFifoLinkOp::verify() {
595 if (isJoin() && isDistribute())
596 return emitError(
"ObjectFifoLinkOp does not support 'join' and "
597 "'distribute' at the same time");
599 auto sharedTile = getOptionalSharedTile();
601 return emitError(
"ObjectFifoLinkOp must have a link point, i.e., a "
602 "shared tile between objectFifos");
604 TileOp tile = cast<TileOp>(sharedTile.value().getDefiningOp());
605 if (!tile.isMemTile()) {
606 if (isJoin() || isDistribute())
607 return emitError(
"ObjectFifoLinkOp join and distribute are "
608 "unavailable on compute or shim tiles");
612 if (getFifoIns().size() != getSrcOffsets().size())
613 return emitOpError(
"number of provided src offsets must be equal "
614 "to the number of input objectFifos");
616 if (!getDstOffsets().empty())
617 return emitOpError(
"dst offsets should be empty for join");
619 }
else if (isDistribute()) {
620 if (getFifoOuts().size() != getDstOffsets().size())
621 return emitOpError(
"number of provided dst offsets must be equal "
622 "to the number of output objectFifos");
624 if (!getSrcOffsets().empty())
625 return emitOpError(
"src offsets should be empty for distribute");
627 ObjectFifoCreateOp fifoIn = getInputObjectFifos()[0];
628 if (!fifoIn.getDimensionsToStream().empty()) {
629 return emitOpError(
"currently does not support objectFifos with "
630 "dimensionsToStream.");
632 for (
auto dims : fifoIn.getDimensionsFromStreamPerConsumer()) {
634 return emitOpError(
"currently does not support objectFifos with "
635 "dimensionsFromStreamPerConsumer.");
638 for (
auto fifoOut : getOutputObjectFifos()) {
639 for (
auto dims : fifoOut.getDimensionsFromStreamPerConsumer()) {
641 return emitOpError(
"currently does not support objectFifos with "
642 "dimensionsFromStreamPerConsumer.");
646 std::vector<int> repeat_counts;
647 for (
auto fifoOut : getOutputObjectFifos()) {
648 if (fifoOut.getRepeatCount().has_value())
649 repeat_counts.push_back(fifoOut.getRepeatCount().value());
651 repeat_counts.push_back(0);
653 for (
auto repeat : repeat_counts)
654 if (repeat_counts[0] != repeat)
655 return emitError(
"repeat counts of output object FIFOs must be equal");
658 if (!getSrcOffsets().empty() && !getDstOffsets().empty())
659 return emitOpError(
"all offsets should be empty if there is no "
660 "join or distribute");
666std::optional<Value> ObjectFifoLinkOp::getOptionalSharedTile() {
668 auto fifoOut = getOutputObjectFifos()[0];
669 for (
auto fifoIn : getInputObjectFifos())
670 if (fifoOut.getProducerTile() != fifoIn.getConsumerTiles()[0])
672 return {fifoOut.getProducerTile()};
675 if (isDistribute()) {
676 auto fifoIn = getInputObjectFifos()[0];
677 for (
auto fifoOut : getOutputObjectFifos())
678 if (fifoIn.getConsumerTiles()[0] != fifoOut.getProducerTile())
680 return {fifoIn.getConsumerTiles()[0]};
683 auto fifoIn = getInputObjectFifos();
684 if (
auto fifoOut = getOutputObjectFifos();
685 !fifoIn.empty() && !fifoOut.empty())
686 for (
auto consumerIn : fifoIn[0].getConsumerTiles())
687 if (consumerIn == fifoOut[0].getProducerTile())
688 return {fifoOut[0].getProducerTile()};
692std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getInputObjectFifos() {
693 std::vector<ObjectFifoCreateOp> inputObjFifos;
694 Operation *parent = getOperation();
695 while ((parent = parent->getParentOp())) {
696 if (parent->hasTrait<OpTrait::SymbolTable>()) {
697 for (
auto sym : getFifoIns()) {
698 auto name = dyn_cast<FlatSymbolRefAttr>(sym);
699 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
700 isa_and_nonnull<ObjectFifoCreateOp>(st))
701 inputObjFifos.push_back(dyn_cast<ObjectFifoCreateOp>(st));
705 return inputObjFifos;
708std::vector<ObjectFifoCreateOp> ObjectFifoLinkOp::getOutputObjectFifos() {
709 std::vector<ObjectFifoCreateOp> outputObjFifos;
710 Operation *parent = getOperation();
711 while ((parent = parent->getParentOp())) {
712 if (parent->hasTrait<OpTrait::SymbolTable>()) {
713 for (
auto sym : getFifoOuts()) {
714 auto name = dyn_cast<FlatSymbolRefAttr>(sym);
715 if (
auto *st = SymbolTable::lookupSymbolIn(parent, name);
716 isa_and_nonnull<ObjectFifoCreateOp>(st))
717 outputObjFifos.push_back(dyn_cast<ObjectFifoCreateOp>(st));
721 return outputObjFifos;
724std::vector<int> ObjectFifoLinkOp::getJoinTransferLengths() {
725 std::vector<int> lengths;
728 llvm::cast<AIEObjectFifoType>(getOutputObjectFifos()[0].getElemType());
729 auto elemTypeOut = llvm::cast<MemRefType>(fifoOut.getElementType());
730 int lenOut = elemTypeOut.getNumElements();
731 for (
size_t i = 0; i < getFifoIns().size(); i++) {
733 int offset = *getConstantIntValue(getSrcOffsets()[i]);
734 if (i == getFifoIns().size() - 1)
735 len = lenOut - *getConstantIntValue(getSrcOffsets()[i]);
737 len = *getConstantIntValue(getSrcOffsets()[i + 1]) - offset;
738 lengths.push_back(len);
744std::vector<int> ObjectFifoLinkOp::getDistributeTransferLengths() {
745 std::vector<int> lengths;
746 if (isDistribute()) {
748 llvm::cast<AIEObjectFifoType>(getInputObjectFifos()[0].getElemType());
749 auto elemTypeIn = llvm::cast<MemRefType>(fifoIn.getElementType());
750 int lenIn = elemTypeIn.getNumElements();
751 for (
size_t i = 0; i < getFifoOuts().size(); i++) {
752 int offset = *getConstantIntValue(getDstOffsets()[i]);
754 if (i == getFifoOuts().size() - 1)
755 len = lenIn - *getConstantIntValue(getDstOffsets()[i]);
757 len = *getConstantIntValue(getDstOffsets()[i + 1]) - offset;
758 lengths.push_back(len);
764std::optional<int> ObjectFifoLinkOp::getRepeatCount() {
765 for (
auto fifoOut : getOutputObjectFifos())
766 if (fifoOut.getRepeatCount().has_value())
767 return {fifoOut.getRepeatCount().value()};
775LogicalResult ObjectFifoRegisterExternalBuffersOp::verify() {
776 if (!getTileOp().isShimTile())
777 return emitOpError(
"tile is not a shim tile");
782TileOp ObjectFifoRegisterExternalBuffersOp::getTileOp() {
783 return cast<TileOp>(getTile().getDefiningOp());
786ObjectFifoCreateOp ObjectFifoRegisterExternalBuffersOp::getObjectFifo() {
787 Operation *parent = getOperation();
788 while ((parent = parent->getParentOp())) {
789 if (parent->hasTrait<OpTrait::SymbolTable>()) {
790 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
791 isa_and_nonnull<ObjectFifoCreateOp>(st))
792 return dyn_cast<ObjectFifoCreateOp>(st);
802LogicalResult ObjectFifoAcquireOp::verify() {
803 auto parent = getOperation()->getParentOfType<CoreOp>();
804 if (parent ==
nullptr)
805 return emitOpError(
"must be called from inside a CoreOp");
807 auto coreTile = parent.getTile();
808 auto objFifo = getObjectFifo();
809 if (getPort() == ObjectFifoPort::Produce) {
810 if (coreTile != objFifo.getProducerTile())
811 return parent.emitOpError(
812 "producer port of objectFifo accessed by core running "
813 "on non-producer tile");
814 }
else if (getPort() == ObjectFifoPort::Consume) {
816 for (
auto consumerTile : objFifo.getConsumerTiles()) {
817 if (coreTile == consumerTile) {
823 return parent.emitOpError(
824 "consumer port of objectFifo accessed by core running "
825 "on non-consumer tile");
829 llvm::cast<AIEObjectFifoType>(getObjectFifo().getElemType())
831 auto objFifoSubviewElem =
832 llvm::cast<AIEObjectFifoSubviewType>(getResult().getType())
834 if (objFifoElem != objFifoSubviewElem)
836 "ObjectFifo element and ObjectFifoSubview element must match.\n");
841ObjectFifoCreateOp ObjectFifoAcquireOp::getObjectFifo() {
842 Operation *parent = getOperation();
843 while ((parent = parent->getParentOp())) {
844 if (parent->hasTrait<OpTrait::SymbolTable>()) {
845 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
846 isa_and_nonnull<ObjectFifoCreateOp>(st))
847 return dyn_cast<ObjectFifoCreateOp>(st);
857LogicalResult ObjectFifoReleaseOp::verify() {
858 auto parent = getOperation()->getParentOfType<CoreOp>();
859 if (parent ==
nullptr)
860 return emitOpError(
"must be called from inside a CoreOp");
862 auto coreTile = parent.getTile();
863 auto objFifo = getObjectFifo();
864 if (getPort() == ObjectFifoPort::Produce) {
865 if (coreTile != objFifo.getProducerTile())
866 return parent.emitOpError(
867 "producer port of objectFifo accessed by core running "
868 "on non-producer tile");
869 }
else if (getPort() == ObjectFifoPort::Consume) {
871 for (
auto consumerTile : objFifo.getConsumerTiles()) {
872 if (coreTile == consumerTile) {
878 return parent.emitOpError(
879 "consumer port of objectFifo accessed by core running "
880 "on non-consumer tile");
886ObjectFifoCreateOp ObjectFifoReleaseOp::getObjectFifo() {
887 Operation *parent = getOperation();
888 while ((parent = parent->getParentOp())) {
889 if (parent->hasTrait<OpTrait::SymbolTable>()) {
890 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
891 isa_and_nonnull<ObjectFifoCreateOp>(st))
892 return dyn_cast<ObjectFifoCreateOp>(st);
902LogicalResult ObjectFifoSubviewAccessOp::verify() {
903 if (
auto parent = getOperation()->getParentOfType<CoreOp>();
905 return emitOpError(
"must be called from inside a CoreOp");
907 if (
auto acqOp = getSubview().getDefiningOp<ObjectFifoAcquireOp>();
908 getIndex() >= acqOp.acqNumber())
909 return emitOpError(
"accessed farther than number of acquired elements "
910 "(index out of bounds).");
919LogicalResult ObjectFifoRegisterProcessOp::verify() {
920 if (getProcessLength() < 1)
921 return emitOpError(
"process length must be >= 1");
923 if (getAcquirePattern().size() != getReleasePattern().size()) {
926 if (getAcquirePattern().size() != getProcessLength() &&
927 getProcessLength() != getReleasePattern().size())
929 "Acquire and Release patterns must be of equal length, or "
930 "longest length of one must be equal to process "
931 "length of the other");
937ObjectFifoCreateOp ObjectFifoRegisterProcessOp::getObjectFifo() {
938 Operation *parent = getOperation();
939 while ((parent = parent->getParentOp())) {
940 if (parent->hasTrait<OpTrait::SymbolTable>()) {
941 if (
auto *st = SymbolTable::lookupSymbolIn(parent, getObjFifoName());
942 isa_and_nonnull<ObjectFifoCreateOp>(st))
943 return dyn_cast<ObjectFifoCreateOp>(st);
953LogicalResult CascadeFlowOp::verify() {
954 TileOp
src = getSourceTileOp();
955 TileOp
dst = getDestTileOp();
958 if (
src.isShimTile() ||
dst.isShimTile())
959 return emitOpError(
"shimTile row has no cascade stream interface");
960 if (t.isMemTile(
src.colIndex(),
src.rowIndex()) ||
961 t.isMemTile(
dst.colIndex(),
dst.rowIndex()))
962 return emitOpError(
"memTile row has no cascade stream interface");
964 if (!t.isSouth(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow()) &&
965 !t.isWest(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow()) &&
966 !t.isNorth(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow()) &&
967 !t.isEast(
src.getCol(),
src.getRow(),
dst.getCol(),
dst.getRow())) {
968 return emitOpError(
"tiles must be adjacent");
973TileOp CascadeFlowOp::getSourceTileOp() {
974 return cast<TileOp>(getSourceTile().getDefiningOp());
977TileOp CascadeFlowOp::getDestTileOp() {
978 return cast<TileOp>(getDestTile().getDefiningOp());
985LogicalResult ConfigureCascadeOp::verify() {
987 TileOp tile = cast<TileOp>(getTile().getDefiningOp());
988 CascadeDir inputDir = getInputDir();
989 CascadeDir outputDir = getOutputDir();
991 if (tile.isShimTile())
992 return emitOpError(
"shimTile row has no cascade stream interface");
993 if (t.isMemTile(tile.colIndex(), tile.rowIndex()))
994 return emitOpError(
"memTile row has no cascade stream interface");
996 if (isa<AIE2TargetModel>(t)) {
997 if (inputDir == CascadeDir::South || inputDir == CascadeDir::East) {
998 return emitOpError(
"input direction of cascade must be North or West on ")
999 << stringifyAIEArch(t.getTargetArch());
1001 if (outputDir == CascadeDir::North || outputDir == CascadeDir::West) {
1003 "output direction of cascade must be South or East on ")
1004 << stringifyAIEArch(t.getTargetArch());
1007 return emitOpError(
"cascade not supported in ")
1008 << stringifyAIEArch(t.getTargetArch());
1017LogicalResult PutCascadeOp::verify() {
1019 Type type = getCascadeValue().getType();
1020 DataLayout dataLayout = DataLayout::closest(*
this);
1021 auto bits = dataLayout.getTypeSizeInBits(type);
1022 auto archbits = targetModel.getAccumulatorCascadeSize();
1023 if (bits != archbits)
1024 return emitOpError(
"type must match architecture cascade width (")
1025 << archbits <<
" bits in "
1026 << stringifyAIEArch(targetModel.getTargetArch()) <<
")";
1034LogicalResult GetCascadeOp::verify() {
1036 Type type = getCascadeValue().getType();
1037 DataLayout dataLayout = DataLayout::closest(*
this);
1038 auto bits = dataLayout.getTypeSizeInBits(type);
1039 if (isa<AIE1TargetModel>(targetModel)) {
1041 return emitOpError(
"must be a 384-bit type");
1042 }
else if (isa<AIE2TargetModel>(targetModel)) {
1044 return emitOpError(
"must be a 512-bit type");
1046 return emitOpError(
"cascade not supported in ")
1047 << stringifyAIEArch(targetModel.getTargetArch());
1063LogicalResult TileOp::verify() {
1065 int columns = targetModel.columns();
1066 int rows = targetModel.rows();
1067 if (colIndex() >= columns)
1068 return emitOpError(
"column index (")
1070 <<
") must be less than the number of columns in the device ("
1072 if (rowIndex() >= rows)
1073 return emitOpError(
"row index (")
1075 <<
") must be less than the number of rows in the device (" << rows
1078 auto users = getResult().getUsers();
1080 for (
auto *user : users) {
1081 if (llvm::isa<SwitchboxOp>(*user)) {
1083 return emitOpError(
"can only have one switchbox");
1088 if (isShimTile() && getAllocationScheme())
1089 return emitOpError(
"Shim tiles cannot have an allocation scheme");
1094size_t TileOp::getNumSourceConnections(WireBundle bundle) {
1096 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
1100 if (targetModel.isShimNOCTile(getCol(), getRow()) ||
1101 targetModel.isShimPLTile(getCol(), getRow()))
1102 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
1104 return targetModel.getNumDestSwitchboxConnections(getCol(), getRow(),
1110size_t TileOp::getNumDestConnections(WireBundle bundle) {
1112 if (bundle == WireBundle::Core || bundle == WireBundle::DMA)
1116 if (targetModel.isShimNOCTile(getCol(), getRow()) ||
1117 targetModel.isShimPLTile(getCol(), getRow()))
1118 return targetModel.getNumDestShimMuxConnections(getCol(), getRow(),
1120 return targetModel.getNumSourceSwitchboxConnections(getCol(), getRow(),
1126bool TileOp::isMemTile() {
1128 return targetModel.isMemTile(getCol(), getRow());
1131bool TileOp::isShimNOCTile() {
1133 return targetModel.isShimNOCTile(getCol(), getRow());
1136bool TileOp::isShimPLTile() {
1138 return targetModel.isShimPLTile(getCol(), getRow());
1141bool TileOp::isShimNOCorPLTile() {
1143 return targetModel.isShimNOCorPLTile(getCol(), getRow());
1147 MasterSetOp masterOp, PacketRulesOp slaveOp) {
1148 auto srcBundle = slaveOp.sourcePort().bundle;
1149 auto srcChan = slaveOp.sourcePort().channel;
1150 auto dstBundle = masterOp.destPort().bundle;
1151 auto dstChan = masterOp.destPort().channel;
1153 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
1157 ConnectOp connectOp) {
1158 auto srcBundle = connectOp.getSourceBundle();
1159 auto srcChan = connectOp.getSourceChannel();
1160 auto dstBundle = connectOp.getDestBundle();
1161 auto dstChan = connectOp.getDestChannel();
1163 tile.colIndex(), tile.rowIndex(), srcBundle, srcChan, dstBundle, dstChan);
1166TileOp TileOp::getOrCreate(mlir::OpBuilder builder, DeviceOp device,
int col,
1168 TileOp tile =
nullptr;
1170 for (
auto t : device.getOps<
AIE::TileOp>()) {
1171 if (t.getRow() ==
row && t.getCol() ==
col) {
1178 OpBuilder::InsertionGuard guard(builder);
1179 mlir::Block &device_start_block = *device.getBodyRegion().begin();
1180 builder.setInsertionPointToStart(&device_start_block);
1181 tile = builder.create<TileOp>(builder.getUnknownLoc(),
1182 builder.getIndexType(),
col,
row);
1191LogicalResult ShimSwitchboxOp::verify() {
1192 Region &body = getConnections();
1193 DenseSet<Port> destset;
1195 return emitOpError(
"should have non-empty body");
1197 for (
auto &ops : body.front()) {
1198 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1199 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1200 if (destset.count(dest))
1201 return connectOp.emitOpError(
"targets same destination ")
1202 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1203 <<
" as another connect operation";
1204 destset.insert(dest);
1205 }
else if (isa<EndOp>(ops)) {
1208 return ops.emitOpError(
"cannot be contained in a Switchbox op");
1219LogicalResult ShimMuxOp::verify() {
1220 Region &body = getConnections();
1221 DenseSet<Port> destset;
1223 return emitOpError(
"should have non-empty body");
1225 for (
auto &ops : body.front()) {
1226 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1227 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1228 if (destset.count(dest))
1229 return connectOp.emitOpError(
"targets same destination ")
1230 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1231 <<
" as another connect operation";
1232 destset.insert(dest);
1233 }
else if (isa<EndOp>(ops)) {
1236 return ops.emitOpError(
"cannot be contained in a Switchbox op");
1242size_t ShimMuxOp::getNumSourceConnections(WireBundle bundle) {
1243 auto tile = getTileOp();
1245 return targetModel.getNumSourceShimMuxConnections(tile.getCol(),
1246 tile.getRow(), bundle);
1249size_t ShimMuxOp::getNumDestConnections(WireBundle bundle) {
1250 auto tile = getTileOp();
1252 return targetModel.getNumDestShimMuxConnections(tile.getCol(), tile.getRow(),
1256TileOp ShimMuxOp::getTileOp() {
1257 return cast<TileOp>(getTile().getDefiningOp());
1260int ShimMuxOp::colIndex() {
return getTileOp().colIndex(); }
1262int ShimMuxOp::rowIndex() {
return getTileOp().rowIndex(); }
1268LogicalResult ShimDMAOp::verify() {
1269 if (!getTileOp().isShimNOCTile())
1270 return emitOpError(
"must be in a ShimTile with a NOC connection");
1278TileOp ShimDMAOp::getTileOp() {
1279 return cast<TileOp>(getTile().getDefiningOp());
1282int ShimDMAOp::colIndex() {
return getTileOp().colIndex(); }
1284int ShimDMAOp::rowIndex() {
return getTileOp().rowIndex(); }
1286LogicalResult PacketRulesOp::verify() {
1287 if (Region &body = getRules(); body.empty())
1288 return emitOpError(
"should have non-empty body");
1292LogicalResult PacketFlowOp::verify() {
1293 Region &body = getPorts();
1295 return emitOpError(
"should have non-empty body");
1297 for (
auto &ops : body.front()) {
1298 if (!isa<PacketSourceOp, PacketDestOp, EndOp>(ops))
1299 return ops.emitOpError(
"cannot be contained in a PacketFlow op");
1309LogicalResult CoreOp::verify() {
1310 if (getBody().empty())
1311 return emitOpError(
"should have non-empty body");
1312 if (getTileOp().isShimTile())
1313 return emitOpError(
"CoreOp cannot be created on shim tile, i.e. row == 0");
1314 if (getTileOp().isMemTile())
1315 return emitOpError(
"CoreOp cannot be created on mem tile");
1319int CoreOp::colIndex() {
return getTileOp().colIndex(); }
1321int CoreOp::rowIndex() {
return getTileOp().rowIndex(); }
1323TileOp CoreOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
1329int64_t BufferOp::getAllocationSize() {
1330 auto type = llvm::cast<MemRefType>(getType());
1331 return type.getNumElements() * type.getElementTypeBitWidth() / 8;
1334TileOp BufferOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
1336LogicalResult BufferOp::verify() {
1337 if (UsesAreAccessible::verifyTrait(*this).failed())
1345 if (
auto buf = dyn_cast<BufferOp>(bufOp)) {
1346 assert(buf.getAddress().has_value() &&
"buffer must have address assigned");
1347 return buf.getAddress().value();
1349 if (isa_and_nonnull<ExternalBufferOp>(bufOp))
1350 llvm::report_fatal_error(
1351 "External buffer addresses are assigned at runtime.");
1352 llvm::report_fatal_error(
"unknown buffer type");
1355void xilinx::AIE::collectTiles(DeviceOp &device,
1356 DenseMap<TileID, Operation *> &tiles) {
1357 for (
auto tile : device.getOps<TileOp>()) {
1358 int colIndex = tile.colIndex();
1359 int rowIndex = tile.rowIndex();
1360 tiles[{colIndex, rowIndex}] = tile;
1364void xilinx::AIE::collectBuffers(
1366 DenseMap<Operation *, SmallVector<BufferOp, 4>> &buffers) {
1367 for (BufferOp buffer : device.getOps<BufferOp>()) {
1368 Operation *tileOp = buffer.getTile().getDefiningOp();
1369 buffers[tileOp].push_back(buffer);
1373static void printBufferInitialValue(OpAsmPrinter &p, BufferOp op, Type type,
1374 Attribute initialValue) {
1375 if (op.getInitialValue()) {
1377 p.printAttributeWithoutType(initialValue);
1381static ParseResult parseBufferInitialValue(OpAsmParser &parser, Type &type,
1382 Attribute &initialValue) {
1383 auto memrefType = llvm::cast<MemRefType>(type);
1384 if (!memrefType.hasStaticShape())
1385 return parser.emitError(parser.getNameLoc())
1386 <<
"type should be static shaped memref, but got " << type;
1388 if (parser.parseOptionalEqual())
1391 Type tensorType = mlir::memref::getTensorTypeFromMemRefType(memrefType);
1392 if (parser.parseAttribute(initialValue, tensorType))
1394 if (!llvm::isa<ElementsAttr>(initialValue))
1395 return parser.emitError(parser.getNameLoc())
1396 <<
"initial value should be an elements attribute";
1404LogicalResult MemOp::verify() {
1405 Region &body = getBody();
1410 for (
auto &bodyOp : body.getOps()) {
1411 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp))
1412 if (!allocOp->getAttr(
"id"))
1413 return allocOp.emitOpError()
1414 <<
"allocOp in MemOp region should have an id attribute";
1419TileOp MemOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
1421int MemOp::colIndex() {
return getTileOp().colIndex(); }
1423int MemOp::rowIndex() {
return getTileOp().rowIndex(); }
1429LogicalResult MemTileDMAOp::verify() {
1430 assert(getOperation()->getNumRegions() == 1 &&
1431 "MemTileDMAOp has zero region!");
1437 for (
auto &bodyOp : getBody().getOps()) {
1438 if (
auto allocOp = dyn_cast<memref::AllocOp>(bodyOp)) {
1439 if (!allocOp->getAttr(
"id"))
1440 return allocOp.emitOpError()
1441 <<
"allocOp in MemTileDMAOp region should have an id attribute";
1443 if (
auto startOp = dyn_cast<DMAStartOp>(bodyOp)) {
1444 if (startOp.getChannelIndex() > 3) {
1450 llvm::SmallSet<Block *, 16> reachable;
1451 SmallVector<Block *, 16> worklist;
1452 Block *firstBD = startOp.getSuccessor(0);
1453 reachable.insert(firstBD);
1454 worklist.push_back(firstBD);
1455 while (!worklist.empty()) {
1456 Block *block = worklist.pop_back_val();
1459 auto successors = block->getTerminator()->getSuccessors();
1460 for (
auto *i : successors) {
1461 if (!reachable.contains(i)) {
1462 reachable.insert(i);
1463 worklist.push_back(i);
1467 for (Block *b : reachable) {
1468 for (DMABDOp bd : b->getOps<DMABDOp>()) {
1469 if (
auto bufferOp = bd.getBufferOp();
1470 bufferOp.getTileOp().colIndex() != colIndex() ||
1471 bufferOp.getTileOp().rowIndex() != rowIndex()) {
1472 InFlightDiagnostic err =
1474 <<
"is reachable from DMA channel "
1475 << startOp.getChannelIndex()
1476 <<
" and attempts to access a non-local buffer\n";
1477 err.attachNote(startOp->getLoc()) <<
"channel";
1478 err.attachNote(bufferOp->getLoc()) <<
"buffer";
1482 for (
auto useLock : b->getOps<UseLockOp>()) {
1483 if (
auto lockOp = useLock.getLockOp();
1484 lockOp.getTileOp().colIndex() != colIndex() ||
1485 lockOp.getTileOp().rowIndex() != rowIndex()) {
1486 InFlightDiagnostic err =
1487 useLock.emitOpError()
1488 <<
"is reachable from DMA channel "
1489 << startOp.getChannelIndex()
1490 <<
" and attempts to access a non-local lock\n";
1491 err.attachNote(startOp->getLoc()) <<
"channel";
1492 err.attachNote(lockOp->getLoc()) <<
"lock";
1508LogicalResult DMAOp::verify() {
1509 auto *parentOp = getOperation()->getParentOp();
1510 if (parentOp->getRegion(0).getBlocks().size() > 1)
1511 return emitOpError(
"DMAOp can only appear in single block region");
1512 if (!parentOp->getRegion(0).getOps<DMAStartOp>().empty())
1513 return emitOpError(
"DMAOp is not compatible with DMAStart ops");
1514 auto bdRegions = getBds();
1515 for (
auto &bdRegion : bdRegions) {
1516 if (!bdRegion.hasOneBlock())
1517 return emitOpError(
"DMAOp regions must have only one block");
1518 auto bds = llvm::to_vector_of<DMABDOp>(bdRegion.front().getOps<DMABDOp>());
1519 if (bds.size() != 1)
1520 return emitOpError(
"DMAOp regions/blocks must have exactly one DMABDOp");
1522 llvm::to_vector_of<UseLockOp>(bdRegion.front().getOps<UseLockOp>());
1523 if (useLocks.size() != 2)
1525 "DMAOp regions/blocks must have exactly two UseLock ops");
1534BufferOp DMABDOp::getBufferOp() {
1535 return cast<BufferOp>(getBuffer().getDefiningOp());
1543ParseResult DMABDOp::parse(OpAsmParser &parser, OperationState &result) {
1544 OpAsmParser::UnresolvedOperand bufferRawOperand{};
1545 ::llvm::ArrayRef<OpAsmParser::UnresolvedOperand> bufferOperands(
1546 &bufferRawOperand, 1);
1547 ::llvm::SMLoc bufferOperandsLoc;
1548 (void)bufferOperandsLoc;
1549 Type bufferRawType{};
1550 ::llvm::ArrayRef<Type> bufferTypes(&bufferRawType, 1);
1551 IntegerAttr offsetAttr;
1552 IntegerAttr lenAttr;
1553 ::xilinx::AIE::BDDimLayoutArrayAttr dimensionsAttr;
1554 ::xilinx::AIE::BDPadLayoutArrayAttr pad_dimensionsAttr;
1555 IntegerAttr pad_valueAttr;
1556 if (parser.parseLParen())
1559 bufferOperandsLoc = parser.getCurrentLocation();
1560 if (parser.parseOperand(bufferRawOperand))
1562 if (parser.parseColon())
1564 if (parser.parseCustomTypeWithFallback(bufferRawType))
1568 if (succeeded(parser.parseOptionalComma())) {
1569 if (parser.parseCustomAttributeWithFallback(
1570 offsetAttr, parser.getBuilder().getIntegerType(32))) {
1574 offsetAttr = parser.getBuilder().getIntegerAttr(
1575 parser.getBuilder().getIntegerType(32), 0);
1576 result.getOrAddProperties<DMABDOp::Properties>().offset = offsetAttr;
1580 if (succeeded(parser.parseOptionalComma())) {
1581 if (parser.parseCustomAttributeWithFallback(
1582 lenAttr, parser.getBuilder().getIntegerType(32))) {
1586 result.getOrAddProperties<DMABDOp::Properties>().len = lenAttr;
1590 if (succeeded(parser.parseOptionalComma())) {
1591 if (parser.parseCustomAttributeWithFallback(dimensionsAttr, Type{})) {
1595 result.getOrAddProperties<DMABDOp::Properties>().dimensions =
1600 if (succeeded(parser.parseOptionalComma())) {
1601 if (parser.parseCustomAttributeWithFallback(pad_dimensionsAttr, Type{})) {
1604 if (pad_dimensionsAttr)
1605 result.getOrAddProperties<DMABDOp::Properties>().pad_dimensions =
1610 if (succeeded(parser.parseOptionalComma())) {
1611 if (parser.parseKeyword(
"pad_value"))
1613 if (parser.parseEqual())
1616 if (parser.parseCustomAttributeWithFallback(
1617 pad_valueAttr, parser.getBuilder().getIntegerType(32))) {
1621 result.getOrAddProperties<DMABDOp::Properties>().pad_value =
1624 if (parser.parseRParen())
1627 auto loc = parser.getCurrentLocation();
1628 if (parser.parseOptionalAttrDict(result.attributes))
1630 if (failed(verifyInherentAttrs(result.name, result.attributes, [&]() {
1631 return parser.emitError(loc)
1632 <<
"'" << result.name.getStringRef() <<
"' op ";
1636 if (parser.resolveOperands(bufferOperands, bufferTypes, bufferOperandsLoc,
1643void DMABDOp::print(::mlir::OpAsmPrinter &printer) {
1645 printer << getBuffer();
1646 printer <<
' ' <<
":";
1649 auto type = getBuffer().getType();
1650 if (
auto validType = ::llvm::dyn_cast<::mlir::MemRefType>(type))
1651 printer.printStrippedAttrOrType(validType);
1657 ::mlir::OpBuilder((*this)->getContext())
1659 ::mlir::OpBuilder((*this)->getContext()).getIntegerType(32),
1663 printer.printAttributeWithoutType(getOffsetAttr());
1668 printer.printAttributeWithoutType(getLenAttr());
1670 if (getDimensionsAttr()) {
1673 printer.printStrippedAttrOrType(getDimensionsAttr());
1675 if (getPadDimensionsAttr()) {
1678 printer.printStrippedAttrOrType(getPadDimensionsAttr());
1680 if ((getPadValueAttr() &&
1681 getPadValueAttr() !=
1682 ::mlir::OpBuilder((*this)->getContext())
1684 ::mlir::OpBuilder((*this)->getContext()).getIntegerType(32),
1687 printer <<
' ' <<
"pad_value";
1688 printer <<
' ' <<
"=";
1690 printer.printAttributeWithoutType(getPadValueAttr());
1693 ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;
1694 elidedAttrs.push_back(
"offset");
1695 elidedAttrs.push_back(
"len");
1696 elidedAttrs.push_back(
"dimensions");
1697 elidedAttrs.push_back(
"pad_dimensions");
1698 elidedAttrs.push_back(
"pad_value");
1699 printer.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
1702LogicalResult DMABDOp::verify() {
1706 Operation *p = (*this)->getParentOp();
1707 if (!llvm::isa<MemOp, MemTileDMAOp, ShimDMAOp, DMAOp>(*p)) {
1711 if (!isa<BufferOp, ExternalBufferOp>(getBuffer().getDefiningOp()))
1713 "BDs only support BufferOp or ExternalBufferOp operands.");
1715 if (getLenInBytes() % 4)
1716 return emitOpError(
"transfer length must be multiple of 4 (i.e., represent "
1717 "4 byte aligned address)");
1719 TileID parentTileId = getParentTileElement(getOperation()).getTileID();
1721 if (getOperation()->getParentOfType<MemOp>() &&
1722 (getBufferOp().getTileOp().colIndex() != parentTileId.col ||
1723 getBufferOp().getTileOp().rowIndex() != parentTileId.row))
1725 "Core tile DMAs can only access a buffer in the same tile.");
1729 uint32_t maxBds = targetModel.
getNumBDs(parentTileId.col, parentTileId.row);
1730 if (std::optional<int32_t> bdId = getBdId();
1731 bdId.has_value() &&
static_cast<uint32_t
>(*bdId) >= maxBds)
1732 return emitOpError(
"bdId attribute exceeds max: ") << maxBds - 1;
1733 if (std::optional<int32_t> nextBdId = getNextBdId();
1734 nextBdId.has_value() &&
static_cast<uint32_t
>(*nextBdId) >= maxBds)
1735 return emitOpError(
"nextBdId attribute exceeds max: ") << maxBds - 1;
1736 if (
auto dims = getDimensions(); dims.has_value()) {
1737 size_t maxNDims = 3;
1738 if (getOperation()->getParentOfType<MemTileDMAOp>())
1740 if (dims->size() > maxNDims)
1741 return emitOpError() <<
"Cannot give more than "
1742 << std::to_string(maxNDims)
1743 <<
" dimensions for step sizes and wraps in this "
1745 << std::to_string(dims->size()) <<
" dimensions).";
1747 MemRefType buffer = getBuffer().getType();
1749 for (BDDimLayoutAttr dim : *dims) {
1750 maxIdx += dim.getStride() * (dim.getSize() - 1);
1751 if (0 == dim.getStride())
1752 return emitOpError()
1753 <<
"Invalid step size; must be a positive integer.";
1754 if (dim.getStride() > buffer.getNumElements())
1755 return emitOpError() <<
"Step size " << std::to_string(dim.getStride())
1756 <<
" exceeds memref size "
1757 << std::to_string(buffer.getNumElements());
1758 if (dim.getSize() >= (1UL << 9) + 1)
1759 return emitOpError() <<
"Size may not exceed 1023.";
1760 if (dim.getStride() >= (1UL << 19))
1761 return emitOpError() <<
"Stride may not exceed " << (1 << 20);
1764 if (buffer.getNumElements() <= maxIdx)
1765 return emitOpError() <<
"Specified stride(s) and size(s) result in out "
1766 "of bounds access in buffer, for index "
1767 << std::to_string(maxIdx) <<
" in memref of length "
1768 << std::to_string(buffer.getNumElements()) <<
".";
1773 if (getBufferElementTypeWidthInBytes() < 4 && dims->back().getStride() != 1)
1775 "For <32b width datatypes, inner-most dim stride must be 1");
1777 if (
auto paddims = getPadDimensions(); paddims.has_value()) {
1778 auto dims = getDimensions();
1779 if (!dims.has_value())
1780 return emitOpError() <<
"Padding requires n-d data layouts expressed as"
1781 <<
" wrap(s) and stride(s).";
1782 if (!targetModel.
isMemTile(parentTileId.col, parentTileId.row))
1783 return emitOpError() <<
"Padding is only supported by memtile dma bds.";
1784 if (dims->size() != paddims->size())
1785 return emitOpError() <<
"Mismatch number of dimensions between padding(s)"
1786 <<
" and wrap(s) and stride(s).";
1788 for (
unsigned i = 0; i < paddims->size(); i++) {
1789 auto dim = (*dims)[i];
1790 auto paddim = (*paddims)[i];
1791 actuallen *= paddim.getConstPadBefore() + paddim.getConstPadAfter() +
1793 if (actuallen > getLen())
1794 return emitOpError() <<
"Data exceeds len after padding.";
1796 if ((paddims->back().getConstPadBefore() *
1797 getBufferElementTypeWidthInBytes()) %
1799 return emitOpError() <<
"Inner-most padding-before count must result in"
1800 <<
" padding in 32-bit words.";
1801 if ((paddims->back().getConstPadAfter() *
1802 getBufferElementTypeWidthInBytes()) %
1804 return emitOpError() <<
"Inner-most padding-after count must result in"
1805 <<
" padding in 32-bit words.";
1807 if (targetModel.
isMemTile(parentTileId.col, parentTileId.row) ||
1808 targetModel.
isCoreTile(parentTileId.col, parentTileId.row)) {
1809 if (
auto baseAddr = getBufferOp().getAddress(); baseAddr.has_value()) {
1810 int offsetInBytes = *baseAddr + getOffsetInBytes();
1811 if (offsetInBytes % 4)
1812 return emitOpError(
"bd address must be 4 byte (32b) aligned; got "
1814 << offsetInBytes <<
" (bytes)";
1817 if (
auto packetInfo = getPacket()) {
1818 if (packetInfo->getPktType() > 7)
1819 return emitOpError(
"Packet type field can only hold 3 bits.");
1820 if (packetInfo->getPktId() > 31)
1821 return emitOpError(
"Packet ID field can only hold 5 bits.");
1824 if (!getLen() && !getBuffer().getType().hasStaticShape())
1825 return emitOpError() <<
"buffer with dynamic shape requires static length.";
1827 if (getBurstLength() != 0 &&
1828 !targetModel.
isShimNOCTile(parentTileId.col, parentTileId.row))
1829 return emitOpError(
"Burst length is only supported in Shim NOC tiles that "
1830 "are connected to the memory-mapped NOC.");
1835TileOp MemTileDMAOp::getTileOp() {
1836 return cast<TileOp>(getTile().getDefiningOp());
1839int MemTileDMAOp::colIndex() {
return getTileOp().colIndex(); }
1841int MemTileDMAOp::rowIndex() {
return getTileOp().rowIndex(); }
1847static LogicalResult FoldDMAStartOp(DMAStartOp op, PatternRewriter &rewriter) {
1849 llvm::SetVector<Block *> reachable;
1850 SmallVector<Block *, 16> worklist;
1851 Block *firstBD = op.getSuccessor(0);
1852 reachable.insert(firstBD);
1853 worklist.push_back(firstBD);
1854 while (!worklist.empty()) {
1855 Block *block = worklist.pop_back_val();
1858 auto successors = block->getTerminator()->getSuccessors();
1859 for (
auto *i : successors) {
1860 if (!reachable.contains(i)) {
1861 reachable.insert(i);
1862 worklist.push_back(i);
1869 if (isa<EndOp>((reachable.back())->getTerminator()))
1873 auto areEquivalentBDs = [](Block *b1, Block *b2) {
1874 auto b1OpRange = b1->without_terminator();
1875 auto b2OpRange = b2->without_terminator();
1876 if (llvm::range_size(b1OpRange) != llvm::range_size(b2OpRange))
1878 if (!llvm::all_of(llvm::zip_equal(b1OpRange, b2OpRange),
1879 [](std::tuple<Operation &, Operation &> pair) {
1880 return OperationEquivalence::isEquivalentTo(
1881 &std::get<0>(pair), &std::get<1>(pair),
1882 OperationEquivalence::IgnoreLocations);
1889 SmallVector<Block *> uniquePattern;
1890 auto patternIt = reachable.begin();
1891 while (patternIt != reachable.end() &&
1892 llvm::none_of(uniquePattern, [patternIt, areEquivalentBDs](Block *b1) {
1893 return areEquivalentBDs(*patternIt, b1);
1895 uniquePattern.push_back(*patternIt);
1900 while (patternIt != reachable.end()) {
1902 if (!areEquivalentBDs(*patternIt, uniquePattern[idx]))
1905 idx = (++idx) % uniquePattern.size();
1909 auto lastBDTerm = dyn_cast<NextBDOp>(reachable.back()->getTerminator());
1910 auto lastUniqueBDTerm =
1911 dyn_cast<NextBDOp>(uniquePattern.back()->getTerminator());
1912 lastUniqueBDTerm.setSuccessor(lastBDTerm.getSuccessor());
1917void DMAStartOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
1918 MLIRContext *context) {
1919 patterns.add(FoldDMAStartOp);
1926LogicalResult SwitchboxOp::verify() {
1927 Region &body = getConnections();
1928 DenseSet<Port> sourceset;
1929 DenseSet<Port> destset;
1930 auto tile = getTileOp();
1933 return emitOpError(
"should have non-empty body");
1934 for (
auto &ops : body.front()) {
1936 auto checkBound = [&ops](StringRef dir, WireBundle bundle,
int index,
1937 int bound) -> LogicalResult {
1938 if (index >= bound) {
1940 return ops.emitOpError(
"index ")
1941 << index <<
" for " << dir <<
" bundle "
1942 << stringifyWireBundle(bundle) <<
" must be less than "
1944 return ops.emitOpError()
1945 << dir <<
" bundle " << stringifyWireBundle(bundle)
1946 <<
" not supported; index: " << index <<
", bound: " << bound;
1951 if (
auto connectOp = dyn_cast<ConnectOp>(ops)) {
1952 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
1953 sourceset.insert(source);
1955 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1956 if (destset.count(dest)) {
1957 return connectOp.emitOpError()
1958 <<
"; connecting " <<
to_string(source) <<
" to "
1960 <<
to_string(this->getTileOp().getTileID())
1961 <<
" targets same dst as another connect op; existing "
1963 << llvm::join(llvm::map_range(
1964 destset, [](
auto &p) {
return to_string(p); }),
1967 destset.insert(dest);
1969 if (connectOp.sourceIndex() < 0)
1970 return connectOp.emitOpError(
"source index cannot be less than zero");
1972 if (checkBound(
"source", connectOp.getSourceBundle(),
1973 connectOp.sourceIndex(),
1974 getNumSourceConnections(connectOp.getSourceBundle()))
1978 if (connectOp.destIndex() < 0)
1979 return connectOp.emitOpError(
"dest index cannot be less than zero");
1981 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
1982 getNumDestConnections(connectOp.getDestBundle()))
1988 return connectOp.emitOpError(
"illegal stream switch connection");
1990 }
else if (
auto connectOp = dyn_cast<MasterSetOp>(ops)) {
1991 Port dest = {connectOp.getDestBundle(), connectOp.destIndex()};
1992 if (destset.count(dest))
1993 return connectOp.emitOpError(
"targets same destination ")
1994 << stringifyWireBundle(dest.bundle) <<
": " << dest.channel
1995 <<
" as another connect or masterset operation";
1996 destset.insert(dest);
1998 if (connectOp.destIndex() < 0)
1999 return connectOp.emitOpError(
"dest index cannot be less than zero");
2001 if (checkBound(
"dest", connectOp.getDestBundle(), connectOp.destIndex(),
2002 getNumDestConnections(connectOp.getDestBundle()))
2007 for (
auto val : connectOp.getAmsels()) {
2008 auto amsel = dyn_cast<AMSelOp>(val.getDefiningOp());
2009 if (arbiter != -1 && arbiter != amsel.arbiterIndex())
2010 return connectOp.emitOpError(
2011 "a master port can only be tied to one arbiter");
2012 arbiter = amsel.arbiterIndex();
2014 }
else if (
auto connectOp = dyn_cast<PacketRulesOp>(ops)) {
2015 Port source = {connectOp.getSourceBundle(), connectOp.sourceIndex()};
2016 if (sourceset.count(source))
2017 return connectOp.emitOpError(
"packet switched source ")
2018 << stringifyWireBundle(source.bundle) << source.channel
2019 <<
" cannot match another connect or masterset operation";
2020 sourceset.insert(source);
2022 }
else if (
auto amselOp = dyn_cast<AMSelOp>(ops)) {
2023 std::vector<MasterSetOp> mstrs;
2024 std::vector<PacketRulesOp> slvs;
2025 for (
auto *user : amselOp.getResult().getUsers()) {
2026 if (
auto s = dyn_cast<PacketRuleOp>(user)) {
2027 auto pktRules = dyn_cast<PacketRulesOp>(s->getParentOp());
2028 slvs.push_back(pktRules);
2029 }
else if (
auto m = dyn_cast<MasterSetOp>(user))
2032 for (
auto m : mstrs) {
2033 for (
auto s : slvs) {
2036 return amselOp->emitOpError(
"illegal stream switch connection");
2040 }
else if (isa<EndOp>(ops)) {
2043 return ops.emitOpError(
"cannot be contained in a Switchbox op");
2050TileOp SwitchboxOp::getTileOp() {
2051 return cast<TileOp>(getTile().getDefiningOp());
2054int SwitchboxOp::colIndex() {
return getTileOp().colIndex(); }
2056int SwitchboxOp::rowIndex() {
return getTileOp().rowIndex(); }
2058template <
typename... ParentOpTypes>
2061 Operation *operation = op->getParentOp();
2063 if (llvm::isa_and_nonnull<ParentOpTypes...>(operation))
2065 operation = operation->getParentOp();
2071TileOp LockOp::getTileOp() {
return cast<TileOp>(getTile().getDefiningOp()); }
2073int LockOp::colIndex() {
return getTileOp().colIndex(); }
2075int LockOp::rowIndex() {
return getTileOp().rowIndex(); }
2077LogicalResult LockOp::verify() {
2078 if (
auto result = UsesAreAccessible::verifyTrait(*
this); result.failed())
2081 if (getLockID().has_value()) {
2083 auto tileOp = getTileOp();
2085 targetModel.
getNumLocks(tileOp.getCol(), tileOp.getRow());
2086 getLockID().value() >= numLocks)
2087 return emitOpError(
"lock assigned invalid id (maximum is ")
2088 << numLocks - 1 <<
")";
2096 auto *block = op->getBlock();
2098 for (
auto op : block->getOps<UseLockOp>()) {
2099 if (
auto lock = dyn_cast<LockOp>(op.getLock().getDefiningOp());
2100 lock.getLockID().has_value()) {
2101 if (lockID != -1 && lockID != lock.getLockIDValue())
2103 lockID = lock.getLockIDValue();
2112 auto *block = op->getBlock();
2113 int acqValue = -1, relValue = -1;
2114 for (
auto op : block->getOps<UseLockOp>()) {
2115 if (op.acquire() || op.acquireGE()) {
2116 if (acqValue != -1 && acqValue != op.getLockValue()) {
2119 acqValue = op.getLockValue();
2120 }
else if (op.release()) {
2121 if (relValue != -1 && relValue != op.getLockValue()) {
2124 relValue = op.getLockValue();
2133 if (
auto memOp = op->getParentOfType<MemOp>()) {
2134 auto useLock = dyn_cast<UseLockOp>(op);
2135 if (
auto lock = useLock.getLockOp();
2136 lock.getTileOp().colIndex() != memOp.colIndex() ||
2137 lock.getTileOp().rowIndex() != memOp.rowIndex())
2144LogicalResult UseLockOp::verify() {
2146 if (llvm::isa_and_nonnull<DeviceOp, ModuleOp>((*this)->getParentOp()))
2147 return (*this)->emitOpError(
"must be used in a core or memory operation.");
2150 if (targetModel.
getTargetArch() == AIEArch::AIE1 && acquireGE())
2151 return (*this)->emitOpError(
2152 "AcquireGreaterEqual is not supported in AIE1.");
2158 if (!(*this)->getBlock())
2159 return (*this)->emitOpError(
"is not in a block.");
2163 return (*this)->emitOpError(
2164 "used in a DMA block that have multiple locks.");
2167 return (*this)->emitOpError(
"acquires/releases the lock in a DMA block "
2168 "from/to multiple states.");
2172 return (*this)->emitOpError(
"can only access a lock in the same tile");
2180 return (*this)->emitOpError()
2181 <<
"expects some parent op to be one of "
2182 <<
"AIE::device, AIE::core, func::func, AIE::mem, or AIE::shimDMA";
2185#include "aie/Dialect/AIE/IR/AIEEnums.cpp.inc"
2186#include "aie/Dialect/AIE/IR/AIEInterfaces.cpp.inc"
2188#define GET_OP_CLASSES
2189#include "aie/Dialect/AIE/IR/AIEOps.cpp.inc"
2191size_t SwitchboxOp::getNumSourceConnections(WireBundle bundle) {
2192 auto tile = getTileOp();
2195 tile.getRow(), bundle);
2198size_t SwitchboxOp::getNumDestConnections(WireBundle bundle) {
2199 auto tile = getTileOp();
2202 tile.getRow(), bundle);
2207 case WireBundle::North:
2208 return WireBundle::South;
2209 case WireBundle::South:
2210 return WireBundle::North;
2211 case WireBundle::East:
2212 return WireBundle::West;
2213 case WireBundle::West:
2214 return WireBundle::East;
2224ParseResult BDChainOp::parse(OpAsmParser &parser, OperationState &result) {
2225 SmallVector<OpAsmParser::Argument> entryArgs;
2228 StringAttr symNameAttr;
2229 if (parser.parseSymbolName(symNameAttr, SymbolTable::getSymbolAttrName(),
2230 result.attributes)) {
2235 ParseResult argParseResult = parser.parseCommaSeparatedList(
2236 OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
2237 OpAsmParser::Argument argument;
2238 if (parser.parseArgument(argument,
true,
true)) {
2241 entryArgs.push_back(argument);
2244 if (argParseResult) {
2245 return argParseResult;
2249 auto *body = result.addRegion();
2250 ParseResult bodyParseResult = parser.parseRegion(*body, entryArgs,
false);
2251 if (bodyParseResult) {
2252 return bodyParseResult;
2258void BDChainOp::print(OpAsmPrinter &printer) {
2261 ->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName())
2264 printer.printSymbolName(taskName);
2266 Region &body = getRegion();
2267 auto argsIter = body.getArguments();
2269 for (
auto it = argsIter.begin(); it != argsIter.end(); ++it) {
2270 if (it != argsIter.begin()) {
2273 printer.printRegionArgument(*it);
2278 printer.printRegion(body,
false,
true);
2285ShimDMAAllocationOp ShimDMAAllocationOp::getForSymbol(DeviceOp device,
2286 llvm::StringRef symbol) {
2287 auto alloc_ops = device.getOps<ShimDMAAllocationOp>();
2288 for (
auto it = alloc_ops.begin(); it != alloc_ops.end(); ++it) {
2289 AIE::ShimDMAAllocationOp a = *it;
2290 if (a.getSymName() == symbol) {
2298#define GET_ATTRDEF_CLASSES
2299#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.
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)