11#include "mlir/IR/Attributes.h"
12#include "mlir/Pass/Pass.h"
13#include "llvm/ADT/DenseMap.h"
16#define GEN_PASS_DEF_AIEOBJECTFIFOSPLIT
17#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
29 ObjectFifoPoolOp pool;
30 SmallVector<int32_t> segments;
33std::optional<ObjectFifoLinkOp> getOptionalLinkOp(ObjectFifoCreateOp op) {
34 auto device = op->getParentOfType<DeviceOp>();
35 for (ObjectFifoLinkOp linkOp : device.getOps<ObjectFifoLinkOp>()) {
36 for (ObjectFifoCreateOp in : linkOp.getInputObjectFifos()) {
41 for (ObjectFifoCreateOp out : linkOp.getOutputObjectFifos()) {
52std::optional<ObjectFifoAllocateOp>
53getOptionalAllocateOp(ObjectFifoCreateOp op) {
55 op->getParentOfType<DeviceOp>().getOps<ObjectFifoAllocateOp>()) {
56 if (alloc.getObjFifoName() == op.name().getValue()) {
64bool delegateReachesBothEnds(ObjectFifoCreateOp op, TileOp delegate) {
65 auto consumerTileOp = cast<TileOp>(op.getConsumerTiles()[0].getDefiningOp());
66 auto reachesDelegate = [&](TileOp end) {
69 return shared == SharedMemory::First || shared == SharedMemory::Either;
71 return reachesDelegate(op.getProducerTileOp()) &&
72 reachesDelegate(consumerTileOp);
77std::optional<StringRef> dmaReason(ObjectFifoCreateOp createOp,
79 if (createOp.forcesDMA())
80 return "it asks for a dma transport";
81 if (createOp.getRepeatCount())
82 return "it repeats objects (`repeat_count`)";
83 if (createOp.usesStream())
84 return "an end is a Core stream port";
85 if (createOp.getConsumerElemType())
86 return "its consumer takes a different element type";
88 if (createOp.getConsumerTiles().size() == 1 &&
89 createOp.getDimensionsToStream().empty()) {
91 cast<TileOp>(createOp.getConsumerTiles()[0].getDefiningOp());
92 shared =
sharedMemory(createOp.getProducerTileOp(), consumerTileOp);
96 if (
auto alloc = getOptionalAllocateOp(createOp);
98 delegateReachesBothEnds(createOp, alloc->getDelegateTileOp()))
104 if (createOp.getConsumerTiles().size() > 1)
105 return "it has more than one consumer";
106 if (!createOp.getDimensionsToStream().empty())
107 return "its producer reshapes the data (`dimensionsToStream`)";
108 return "its ends share no memory module";
111 for (BDDimLayoutArrayAttr dims :
112 createOp.getDimensionsFromStreamPerConsumer()) {
114 return "a consumer reshapes the data (`dimensionsFromStream`)";
121 if (getOptionalLinkOp(createOp)) {
122 if (
auto alloc = getOptionalAllocateOp(createOp)) {
123 if (delegateReachesBothEnds(createOp, alloc->getDelegateTileOp()))
126 return "it is linked through a tile's DMAs";
135int objectCountOn(DeviceOp device, Value tile, ObjectFifoCreateOp objFifo) {
136 if (objFifo.size() == 0) {
141 for (
auto coreOp : device.getOps<CoreOp>()) {
142 if (coreOp.getTile() == tile) {
143 coreOp.walk([&](ObjectFifoAcquireOp acqOp) {
144 if (acqOp.getObjectFifo() == objFifo) {
145 maxAcquire = std::max(maxAcquire, acqOp.acqNumber());
151 if (maxAcquire == 0) {
152 return objFifo.size();
154 if (maxAcquire == 1 && objFifo.size() == 1) {
157 return maxAcquire + 1;
162int sharedObjectCount(ObjectFifoCreateOp objFifo) {
163 int depth = objFifo.size();
164 if (
auto depths = dyn_cast<ArrayAttr>(objFifo.getElemNumber())) {
165 for (
int index = 1; index < static_cast<int>(depths.size()); ++index) {
166 depth = std::max(depth, objFifo.size(index));
172bool hasCoreAccess(DeviceOp device, Value tile, ObjectFifoCreateOp objFifo,
173 ObjectFifoPort port) {
174 for (
auto coreOp : device.getOps<CoreOp>()) {
175 if (coreOp.getTile() != tile) {
179 coreOp.walk([&](Operation *op) {
180 if (
auto acq = dyn_cast<ObjectFifoAcquireOp>(op)) {
181 found |= acq.getObjectFifo() == objFifo && acq.getPort() == port;
182 }
else if (
auto rel = dyn_cast<ObjectFifoReleaseOp>(op)) {
183 found |= rel.getObjectFifo() == objFifo && rel.getPort() == port;
193struct AIEObjectFifoSplitPass
194 :
public xilinx::AIE::impl::AIEObjectFifoSplitBase<AIEObjectFifoSplitPass> {
197 OpBuilder builder{
static_cast<MLIRContext *
>(
nullptr)};
200 void createSegments(ObjectFifoPoolOp pool, Location loc,
201 ArrayRef<std::pair<int64_t, int64_t>> extents) {
202 OpBuilder::InsertionGuard guard(builder);
203 builder.createBlock(&pool.getSegments());
204 for (
auto [index, extent] : llvm::enumerate(extents)) {
205 ObjectFifoSegmentOp::create(
206 builder, loc, builder.getStringAttr(
"s" + std::to_string(index)),
207 builder.getI32IntegerAttr(extent.first),
208 builder.getI32IntegerAttr(extent.second),
210 FlatSymbolRefAttr());
215 ArrayAttr selectedSegmentNames(PoolRef ref) {
216 std::vector<ObjectFifoSegmentOp> all = ref.pool.getSegmentOps();
217 if (all.size() <= 1) {
220 SmallVector<Attribute> names;
221 for (int32_t index : ref.segments) {
222 names.push_back(FlatSymbolRefAttr::get(builder.getContext(),
223 all[index].getSymName()));
225 return builder.getArrayAttr(names);
228 ObjectFifoPoolOp createPool(Location loc, StringRef name, Value tile,
229 int depth, MemRefType elemType,
230 ObjectFifoCreateOp from,
231 ArrayRef<std::pair<int64_t, int64_t>> extents,
232 bool holdsInitialContents,
233 std::optional<int> repeatCount,
234 bool disableSynchronization =
false) {
235 auto pool = ObjectFifoPoolOp::create(
236 builder, loc, name, tile, depth, elemType, ArrayAttr(),
238 repeatCount ? builder.getI32IntegerAttr(*repeatCount) : IntegerAttr(),
239 disableSynchronization || from.getDisableSynchronization(),
240 builder.getStringAttr(from.name().getValue()),
241 holdsInitialContents ? from.getInitValuesAttr() : ArrayAttr(),
243 DenseI32ArrayAttr());
244 createSegments(pool, loc, extents);
248 ObjectFifoCoreEndpointOp createCoreEndpoint(Location loc, StringRef name,
249 Value tile, PoolRef ref,
250 ObjectFifoRole role) {
251 return ObjectFifoCoreEndpointOp::create(
252 builder, loc, name, tile, role, ref.pool.getSymName(),
253 selectedSegmentNames(ref),
254 ObjectFifoCorePushAttr());
257 ObjectFifoDmaEndpointOp createDmaEndpoint(Location loc, StringRef name,
258 Value tile, PoolRef ref,
260 ObjectFifoCreateOp from,
261 BDDimLayoutArrayAttr dims,
262 std::optional<int> channelIndex) {
266 std::optional<int32_t> iterCount;
267 if (std::optional<int32_t> fifoIterations = from.getIterCount()) {
268 iterCount = role == ObjectFifoRole::Drain
270 : *fifoIterations * from.getRepeatCount().value_or(1);
272 ArrayAttr segments = selectedSegmentNames(ref);
274 BDDimLayoutArrayArrayAttr dimensions;
275 if (dims && !dims.empty()) {
276 SmallVector<BDDimLayoutArrayAttr> perSegment(ref.segments.size(), dims);
278 BDDimLayoutArrayArrayAttr::get(builder.getContext(), perSegment);
281 BDPadLayoutArrayArrayAttr padding;
282 std::optional<ArrayRef<BDPadLayoutAttr>> padDims = from.getPadDimensions();
283 if (role == ObjectFifoRole::Drain && padDims && !padDims->empty()) {
284 SmallVector<BDPadLayoutArrayAttr> perSegment(ref.segments.size(),
285 from.getPadDimensionsAttr());
287 BDPadLayoutArrayArrayAttr::get(builder.getContext(), perSegment);
290 return ObjectFifoDmaEndpointOp::create(
291 builder, loc, builder.getStringAttr(name), tile,
292 ObjectFifoRoleAttr::get(builder.getContext(), role),
293 FlatSymbolRefAttr::get(builder.getContext(), ref.pool.getSymName()),
295 channelIndex ? builder.getI32IntegerAttr(*channelIndex) : IntegerAttr(),
297 from.getPadValue() ? builder.getI32IntegerAttr(from.getPadValue())
299 iterCount ? builder.getI32IntegerAttr(*iterCount) : IntegerAttr(),
301 builder.getStringAttr(from.name().getValue()), ArrayAttr(),
302 ArrayAttr(), ArrayAttr(),
303 from.getOutOfOrder() ? builder.getUnitAttr() : UnitAttr(),
305 role == ObjectFifoRole::Drain ? from.getReleaseStrategyAttr()
306 : ObjectFifoReleaseAttr());
311 RouteEndpointOp createRouteEndpoint(Location loc, StringRef name, Value tile,
313 std::optional<int> channelIndex,
314 ObjectFifoCreateOp from) {
315 auto endpoint = RouteEndpointOp::create(
316 builder, loc, builder.getStringAttr(name), tile,
317 WireBundleAttr::get(builder.getContext(), bundle),
318 channelIndex ? builder.getI32IntegerAttr(*channelIndex) : IntegerAttr(),
320 builder.getStringAttr(from.name().getValue()));
321 if (bundle == WireBundle::DMA &&
322 cast<TileOp>(tile.getDefiningOp()).isShimTile()) {
323 AIEDialect::ShareableWithAttrHelper sharing(builder.getContext());
324 if (
auto allowed = sharing.getAttr(from))
325 sharing.setAttr(endpoint, allowed);
332 std::optional<PoolRef> registeredPool(ObjectFifoCreateOp fifo, Value tile,
333 StringRef name, MemRefType elemType) {
334 SmallVector<Attribute> names;
335 for (
auto regOp : device.getOps<ObjectFifoRegisterExternalBuffersOp>()) {
336 if (regOp.getTile() != tile || regOp.getObjectFifo() != fifo) {
339 for (Value buffer : regOp.getExternalBuffers()) {
340 auto external = cast<ExternalBufferOp>(buffer.getDefiningOp());
341 names.push_back(SymbolRefAttr::get(external.getSymNameAttr()));
344 elemType = cast<MemRefType>(external.getType());
351 auto pool = ObjectFifoPoolOp::create(
352 builder, fifo.getLoc(), name, tile, (
int)names.size(), elemType,
353 builder.getArrayAttr(names),
354 ArrayAttr(), IntegerAttr(),
355 fifo.getDisableSynchronization(),
356 builder.getStringAttr(fifo.name().getValue()),
359 DenseI32ArrayAttr());
360 createSegments(pool, fifo.getLoc(), {{0, elemType.getNumElements()}});
361 return PoolRef{pool, {0}};
365 void retargetCoreAccesses(ObjectFifoCreateOp fifo, ObjectFifoPort port,
366 Value tile, StringRef endpointName) {
367 auto name = FlatSymbolRefAttr::get(builder.getContext(), endpointName);
371 for (
auto coreOp : device.getOps<CoreOp>()) {
372 if (coreOp.getTile() != tile) {
375 coreOp.walk([&](Operation *op) {
376 if (
auto acq = dyn_cast<ObjectFifoAcquireOp>(op)) {
377 if (acq.getObjectFifo() == fifo && acq.getPort() == port) {
378 acq.setObjFifoNameAttr(name);
379 acq.removePortAttr();
381 }
else if (
auto rel = dyn_cast<ObjectFifoReleaseOp>(op)) {
382 if (rel.getObjectFifo() == fifo && rel.getPort() == port) {
383 rel.setObjFifoNameAttr(name);
384 rel.removePortAttr();
391 void runOnOperation()
override;
394 LogicalResult verifyTilesArePlaced() {
395 for (
auto fifo : device.getOps<ObjectFifoCreateOp>()) {
396 auto placed = [](Value tile) {
397 return isa_and_nonnull<TileOp>(tile.getDefiningOp());
399 if (!placed(fifo.getProducerTile())) {
400 return fifo.emitOpError(
"producer tile is not a placed aie.tile; run "
401 "--aie-place-tiles before this pass");
403 for (Value consumer : fifo.getConsumerTiles()) {
404 if (!placed(consumer)) {
405 return fifo.emitOpError(
"consumer tile is not a placed aie.tile; run "
406 "--aie-place-tiles before this pass");
416 verifyOutOfOrderForSharedChannels(ArrayRef<ObjectFifoCreateOp> fifos) {
417 DenseMap<std::tuple<int, int, int>, SmallVector<ObjectFifoCreateOp>>
419 for (ObjectFifoCreateOp fifo : fifos) {
421 if (!dmaReason(fifo, shared))
423 for (
auto [index, consumer] : llvm::enumerate(fifo.getConsumerTiles())) {
424 ObjectFifoEndPortAttr port = fifo.getConsumerPort(index);
425 if (!port || port.isCore())
427 std::optional<int> pinnedChannel = port.channelIndex();
430 int channel = *pinnedChannel;
431 auto tile = consumer.getDefiningOp<TileOp>();
432 if (channel < 0 || !tile)
434 onChannel[{tile.getCol(), tile.getRow(), channel}].push_back(fifo);
437 for (
auto &[channel, sharing] : onChannel) {
438 if (sharing.size() < 2)
440 for (ObjectFifoCreateOp fifo : sharing) {
441 if (!fifo.getOutOfOrder())
442 return fifo.emitOpError(
443 "shares a receiving channel; explicitly set out_of_order = true "
444 "on its packet transport");
453 void selectPushQueueForSharedChannels(ArrayRef<ObjectFifoCreateOp> fifos) {
454 DenseMap<std::tuple<int, int, int>, SmallVector<ObjectFifoCreateOp>>
456 for (ObjectFifoCreateOp fifo : fifos) {
458 if (!dmaReason(fifo, shared))
460 ObjectFifoEndPortAttr port = fifo.getProdPortAttr();
461 std::optional<int> channel =
462 port && !port.isCore() ? port.channelIndex() : std::nullopt;
463 auto tile = fifo.getProducerTile().getDefiningOp<TileOp>();
465 if (!channel || !tile || tile.isShimTile())
467 onChannel[{tile.getCol(), tile.getRow(), *channel}].push_back(fifo);
469 for (
auto &[channel, sharing] : onChannel)
470 if (sharing.size() > 1)
471 for (ObjectFifoCreateOp fifo : sharing)
472 fifo.setReleaseStrategy(ObjectFifoRelease::PushQueue);
475 LogicalResult verifyPushQueueProducers(ArrayRef<ObjectFifoCreateOp> fifos) {
476 for (ObjectFifoCreateOp fifo : fifos) {
477 if (fifo.getReleaseStrategy() != ObjectFifoRelease::PushQueue)
480 if (!dmaReason(fifo, shared))
482 if (
auto values = fifo.getInitValues(); values && !values->empty())
483 return fifo.emitOpError(
484 "push_queue does not support initialized objects");
485 auto tile = fifo.getProducerTileOp();
486 if (!tile.isCoreTile() || fifo.streamsFromProducer() ||
487 device.getTargetModel().getDmaTaskQueueDepth() == 0)
488 return fifo.emitOpError(
489 "push_queue requires a sending DMA on a core tile "
490 "with core-issued task support");
491 bool hasRelease =
false;
492 for (CoreOp core : device.getOps<CoreOp>()) {
493 if (core.getTile() == fifo.getProducerTile())
494 core.walk([&](ObjectFifoReleaseOp release) {
495 hasRelease |= release.getObjectFifo() == fifo &&
496 release.getPort() == ObjectFifoPort::Produce;
500 return fifo.emitOpError(
"push_queue requires producer releases in a "
501 "local core to submit send tasks");
509 LogicalResult verifyAllocateDelegates() {
510 for (
auto fifo : device.getOps<ObjectFifoCreateOp>()) {
511 if (getOptionalLinkOp(fifo) && !linkPoolOwner.contains(fifo)) {
514 auto alloc = getOptionalAllocateOp(fifo);
515 if (alloc && !delegateReachesBothEnds(fifo, alloc->getDelegateTileOp())) {
516 return alloc->emitOpError(
"objectfifo has no shared memory access to "
517 "delegate tile's memory module");
525 LogicalResult verifyStreamPortAccesses() {
526 auto check = [&](Operation *op, ObjectFifoCreateOp fifo,
527 std::optional<ObjectFifoPort> port, StringRef verb) {
528 if (!fifo || !port || !fifo.usesStream()) {
531 bool onStreamPort = *port == ObjectFifoPort::Produce
532 ? fifo.streamsFromProducer()
533 : fifo.streamsToConsumer(0);
537 return LogicalResult(op->emitOpError(
"cannot ")
538 << verb <<
" objectfifo stream port");
540 for (
auto coreOp : device.getOps<CoreOp>()) {
541 auto result = coreOp.walk([&](Operation *op) {
542 LogicalResult ok = success();
543 if (
auto acq = dyn_cast<ObjectFifoAcquireOp>(op)) {
544 ok = check(op, acq.getObjectFifo(), acq.getPort(),
"acquire from");
545 }
else if (
auto rel = dyn_cast<ObjectFifoReleaseOp>(op)) {
546 ok = check(op, rel.getObjectFifo(), rel.getPort(),
"release from");
548 return failed(ok) ? WalkResult::interrupt() : WalkResult::advance();
550 if (result.wasInterrupted()) {
559 LogicalResult verifyLinkAccesses() {
560 for (
auto linkOp : device.getOps<ObjectFifoLinkOp>()) {
561 auto sharedTile = linkOp.getOptionalSharedTile();
565 auto reject = [&](ObjectFifoCreateOp fifo) {
566 return LogicalResult(
567 linkOp.emitOpError(
"core on the shared tile cannot access '")
568 << fifo.name().getValue() <<
"': a link moves objects by DMA");
570 for (ObjectFifoCreateOp in : linkOp.getInputObjectFifos()) {
571 if (hasCoreAccess(device, *sharedTile, in, ObjectFifoPort::Consume)) {
575 for (ObjectFifoCreateOp out : linkOp.getOutputObjectFifos()) {
576 if (hasCoreAccess(device, *sharedTile, out, ObjectFifoPort::Produce)) {
586 DenseMap<Operation *, PoolRef> linkedProducerEnd;
587 DenseMap<std::pair<Operation *, Value>, SmallVector<PoolRef>>
590 DenseSet<Operation *> linkPoolOwner;
592 DenseMap<Operation *, std::string> shimEndpointName;
595 DenseMap<Operation *, ObjectFifoLinkOp> timeLinkOf;
597 DenseMap<Operation *, ObjectFifoDmaEndpointOp> timeLinkEndpoint;
600 DenseMap<Operation *, SmallVector<Attribute>> mergeSources;
601 DenseMap<Operation *, DenseMap<Operation *, Attribute>> dispatchTurns;
605 ObjectFifoDmaEndpointOp timeLinkEndpointFor(ObjectFifoLinkOp link,
607 std::optional<int> channel);
608 void createTimeLinkRoutes();
609 LogicalResult createLinkPools();
614static Location linkLocation(ObjectFifoLinkOp link) {
615 SmallVector<Location> locs{link.getLoc()};
616 for (ObjectFifoCreateOp fifo : link.getInputObjectFifos())
617 locs.push_back(fifo.getLoc());
618 for (ObjectFifoCreateOp fifo : link.getOutputObjectFifos())
619 locs.push_back(fifo.getLoc());
620 return FusedLoc::get(link.getContext(), locs);
626LogicalResult AIEObjectFifoSplitPass::createLinkPools() {
627 for (
auto linkOp : device.getOps<ObjectFifoLinkOp>()) {
628 auto sharedTile = linkOp.getOptionalSharedTile();
633 std::vector<ObjectFifoCreateOp> ins = linkOp.getInputObjectFifos();
634 std::vector<ObjectFifoCreateOp> outs = linkOp.getOutputObjectFifos();
635 bool isJoin = linkOp.isJoin();
636 bool isDistribute = linkOp.isDistribute();
638 ObjectFifoCreateOp owner = linkOp.getPoolOwner();
639 if (linkOp.isMerge()) {
640 for (ObjectFifoCreateOp in : ins)
641 timeLinkOf[in] = linkOp;
643 if (linkOp.isDispatch()) {
644 for (ObjectFifoCreateOp out : outs)
645 timeLinkOf[out] = linkOp;
648 auto elemType = cast<MemRefType>(
649 cast<AIEObjectFifoType>(owner.getElemType()).getElementType());
650 builder.setInsertionPoint(owner);
652 SmallVector<std::pair<int64_t, int64_t>> extents = linkOp.getPoolSegments();
654 bool ownerIsOutput = llvm::is_contained(outs, owner);
659 bool window = linkOp.walksStream();
662 if (failed(linkOp.getStreamWindow(walk)))
664 name = (owner.name().getValue() +
"__cons__pool").str();
666 }
else if (ownerIsOutput) {
667 name = (owner.name().getValue() +
"__pool").str();
668 depth = owner.size();
670 auto consumers = owner.getConsumerTiles();
671 size_t earlier = linkedConsumerEnd.lookup({owner, *sharedTile}).size();
673 for (
size_t seen = 0; seen <= earlier;) {
674 assert(
static_cast<size_t>(index + 1) < consumers.size() &&
675 "fewer consumer tiles than already-linked pools for this tile");
676 if (consumers[++index] == *sharedTile)
680 (owner.name().getValue() +
681 (consumers.size() > 1 ?
"__cons" + std::to_string(index) +
"__pool"
684 depth = isa<ArrayAttr>(owner.getElemNumber())
685 ? owner.size(index + 1)
686 : objectCountOn(device, *sharedTile, owner);
691 auto asksToSkipLocks = [](ObjectFifoCreateOp fifo) {
692 return fifo.getDisableSynchronization();
694 bool disableSynchronization = llvm::any_of(ins, asksToSkipLocks) ||
695 llvm::any_of(outs, asksToSkipLocks);
697 auto pool = createPool(linkLocation(linkOp), name, *sharedTile, depth,
698 elemType, owner, extents,
700 linkOp.getRepeatCount(), disableSynchronization);
702 pool.setContiguous(
true);
706 pool.setZeroObjects(SmallVector<int32_t>{
707 static_cast<int32_t
>(walk.
top),
static_cast<int32_t
>(walk.
bottom)});
709 linkPoolOwner.insert(owner);
711 SmallVector<int32_t> allSegments;
712 for (int32_t index = 0; index < (int32_t)extents.size(); ++index) {
713 allSegments.push_back(index);
715 for (
auto [index, in] : llvm::enumerate(ins)) {
716 linkedConsumerEnd[{in, *sharedTile}].push_back(PoolRef{
717 pool, isJoin ? SmallVector<int32_t>{
static_cast<int32_t
>(index)}
720 for (
auto [index, out] : llvm::enumerate(outs)) {
721 linkedProducerEnd[out] = PoolRef{
722 pool, isDistribute ? SmallVector<int32_t>{
static_cast<int32_t
>(index)}
729ObjectFifoDmaEndpointOp
730AIEObjectFifoSplitPass::timeLinkEndpointFor(ObjectFifoLinkOp link, PoolRef ref,
731 std::optional<int> channel) {
732 ObjectFifoDmaEndpointOp &endpoint = timeLinkEndpoint[link];
734 ObjectFifoCreateOp owner = link.isMerge() ? link.getOutputObjectFifos()[0]
735 : link.getInputObjectFifos()[0];
736 endpoint = createDmaEndpoint(
738 (owner.name().getValue() +
739 (link.isMerge() ?
"__merge__dma" :
"__dispatch__dma"))
741 ref.pool.getTile(), ref,
742 link.isMerge() ? ObjectFifoRole::Fill : ObjectFifoRole::Drain, owner,
743 BDDimLayoutArrayAttr(), channel);
750void AIEObjectFifoSplitPass::createTimeLinkRoutes() {
751 for (
auto link : device.getOps<ObjectFifoLinkOp>()) {
752 ObjectFifoDmaEndpointOp endpoint = timeLinkEndpoint.lookup(link);
755 MLIRContext *ctx = builder.getContext();
756 if (link.isMerge()) {
757 PacketInfoAttr header = PacketInfoAttr::get(ctx, 0, std::nullopt);
758 for (ObjectFifoCreateOp in : link.getInputObjectFifos())
759 if (PacketInfoAttr pinned = in.packetHeader();
760 pinned && pinned.isAssigned())
762 builder.setInsertionPoint(link);
763 RouteOp::create(builder, link.getLoc(),
764 builder.getArrayAttr(mergeSources[link]),
765 builder.getArrayAttr(
766 {FlatSymbolRefAttr::get(ctx, endpoint.getSymName())}),
770 SmallVector<Attribute> turns;
771 for (ObjectFifoCreateOp out : link.getOutputObjectFifos())
772 turns.push_back(dispatchTurns[link].lookup(out));
773 endpoint.setDispatchAttr(builder.getArrayAttr(turns));
777void AIEObjectFifoSplitPass::runOnOperation() {
778 device = getOperation();
779 builder = OpBuilder(device.getContext());
780 builder.setInsertionPoint(device.getBody()->getTerminator());
782 SmallVector<ObjectFifoCreateOp> fifos(device.getOps<ObjectFifoCreateOp>());
784 if (failed(verifyTilesArePlaced()) || failed(verifyStreamPortAccesses()) ||
785 failed(verifyLinkAccesses()) ||
786 failed(verifyOutOfOrderForSharedChannels(fifos))) {
787 return signalPassFailure();
790 selectPushQueueForSharedChannels(fifos);
791 if (failed(verifyPushQueueProducers(fifos)))
792 return signalPassFailure();
794 if (failed(createLinkPools()) || failed(verifyAllocateDelegates())) {
795 return signalPassFailure();
798 for (ObjectFifoCreateOp fifo : fifos) {
799 builder.setInsertionPoint(fifo);
800 Location loc = fifo.getLoc();
801 StringRef fifoName = fifo.name().getValue();
802 auto fifoType = cast<AIEObjectFifoType>(fifo.getElemType());
803 auto elemType = cast<MemRefType>(fifoType.getElementType());
804 auto consElemType = cast<MemRefType>(
805 cast<AIEObjectFifoType>(fifo.getConsumerElemTypeOrDefault())
808 auto sharedModule = AIETargetModel::SharedMemory::None;
809 std::optional<StringRef> needsDMAs = dmaReason(fifo, sharedModule);
810 if (needsDMAs && fifo.forcesSharedMemory()) {
811 fifo.emitOpError(
"asks for a shared_mem transport, but ") << *needsDMAs;
812 return signalPassFailure();
814 bool shared = !needsDMAs;
818 if (
auto linked = linkedProducerEnd.find(fifo);
819 linked != linkedProducerEnd.end()) {
820 ref = linked->second;
821 }
else if (
auto linked =
822 linkedConsumerEnd.find({fifo, fifo.getConsumerTiles()[0]});
823 linked != linkedConsumerEnd.end()) {
824 ref = linked->second.front();
826 Value tile = sharedModule == AIETargetModel::SharedMemory::Second
827 ? fifo.getConsumerTiles()[0]
828 : fifo.getProducerTile();
831 if (
auto alloc = getOptionalAllocateOp(fifo)) {
832 if (!delegateReachesBothEnds(fifo, alloc->getDelegateTileOp())) {
833 alloc->emitOpError(
"objectfifo has no shared memory access to "
834 "delegate tile's memory module");
835 return signalPassFailure();
837 tile = alloc->getDelegateTile();
841 ref = PoolRef{createPool(loc, (fifoName +
"__pool").str(), tile,
842 sharedObjectCount(fifo), elemType, fifo,
843 {{0, elemType.getNumElements()}},
845 fifo.getRepeatCount()),
849 if (hasCoreAccess(device, fifo.getProducerTile(), fifo,
850 ObjectFifoPort::Produce)) {
851 auto name = (fifoName +
"__prod").str();
852 createCoreEndpoint(loc, name, fifo.getProducerTile(), ref,
853 ObjectFifoRole::Fill);
854 retargetCoreAccesses(fifo, ObjectFifoPort::Produce,
855 fifo.getProducerTile(), name);
857 if (hasCoreAccess(device, fifo.getConsumerTiles()[0], fifo,
858 ObjectFifoPort::Consume)) {
859 auto name = (fifoName +
"__cons").str();
860 createCoreEndpoint(loc, name, fifo.getConsumerTiles()[0], ref,
861 ObjectFifoRole::Drain);
862 retargetCoreAccesses(fifo, ObjectFifoPort::Consume,
863 fifo.getConsumerTiles()[0], name);
868 Value prodTile = fifo.getProducerTile();
871 ObjectFifoEndPortAttr prodPort = fifo.getProdPortAttr();
872 std::optional<int> prodChannel =
873 prodPort ? prodPort.channelIndex() : std::nullopt;
875 bool prodIsShim = cast<TileOp>(prodTile.getDefiningOp()).isShimTile();
876 std::optional<PoolRef> prodRef;
878 if (
auto linked = linkedProducerEnd.find(fifo);
879 linked != linkedProducerEnd.end()) {
880 prodRef = linked->second;
881 }
else if (prodIsShim) {
882 if (!fifo.getPlio()) {
883 prodRef = registeredPool(fifo, prodTile, (fifoName +
"__pool").str(),
886 }
else if (!fifo.streamsFromProducer()) {
887 int depth = fifo.getInitValues() ? fifo.size()
888 : objectCountOn(device, prodTile, fifo);
890 createPool(loc, (fifoName +
"__pool").str(), prodTile, depth,
891 elemType, fifo, {{0, elemType.getNumElements()}},
892 true, fifo.getRepeatCount());
893 prodRef = PoolRef{pool, {0}};
897 hasCoreAccess(device, prodTile, fifo, ObjectFifoPort::Produce)) {
898 auto name = (fifoName +
"__prod").str();
899 createCoreEndpoint(loc, name, prodTile, *prodRef, ObjectFifoRole::Fill);
900 retargetCoreAccesses(fifo, ObjectFifoPort::Produce, prodTile, name);
903 std::string prodDmaName = (fifoName +
"__prod__dma").str();
904 ObjectFifoLinkOp timeLink = timeLinkOf.lookup(fifo);
905 if (timeLink && timeLink.isDispatch()) {
907 timeLinkEndpointFor(timeLink, *prodRef, prodChannel).getSymName();
908 }
else if (prodRef) {
909 createDmaEndpoint(loc, prodDmaName, prodTile, *prodRef,
910 ObjectFifoRole::Drain, fifo,
911 fifo.getDimensionsToStreamAttr(), prodChannel);
913 createRouteEndpoint(loc, prodDmaName, prodTile,
914 fifo.streamsFromProducer() ? WireBundle::Core
915 : fifo.getPlio() ? WireBundle::PLIO
920 shimEndpointName[fifo] = prodDmaName;
923 SmallVector<Attribute> destinations;
924 ArrayRef<BDDimLayoutArrayAttr> consumerDims =
925 fifo.getDimensionsFromStreamPerConsumer();
926 int consumerIndex = 0;
927 DenseMap<Value, unsigned> linkedOn;
928 for (Value consumerTile : fifo.getConsumerTiles()) {
929 std::string suffix = fifo.getConsumerTiles().size() > 1
930 ? (
"__cons" + std::to_string(consumerIndex))
932 bool consIsShim = cast<TileOp>(consumerTile.getDefiningOp()).isShimTile();
934 std::optional<PoolRef> consRef;
935 if (
auto linked = linkedConsumerEnd.find({fifo, consumerTile});
936 linked != linkedConsumerEnd.end() &&
937 linkedOn[consumerTile] < linked->second.size()) {
938 consRef = linked->second[linkedOn[consumerTile]++];
939 }
else if (consIsShim) {
940 if (!fifo.getPlio()) {
941 consRef = registeredPool(fifo, consumerTile,
942 (fifoName + suffix +
"__pool").str(),
945 }
else if (!fifo.streamsToConsumer(consumerIndex)) {
946 int depth = isa<ArrayAttr>(fifo.getElemNumber())
947 ? fifo.size(consumerIndex + 1)
948 : objectCountOn(device, consumerTile, fifo);
949 auto pool = createPool(loc, (fifoName + suffix +
"__pool").str(),
950 consumerTile, depth, consElemType, fifo,
951 {{0, consElemType.getNumElements()}},
954 consRef = PoolRef{pool, {0}};
958 hasCoreAccess(device, consumerTile, fifo, ObjectFifoPort::Consume)) {
959 auto name = (fifoName + suffix).str();
960 createCoreEndpoint(loc, name, consumerTile, *consRef,
961 ObjectFifoRole::Drain);
962 retargetCoreAccesses(fifo, ObjectFifoPort::Consume, consumerTile, name);
965 ObjectFifoEndPortAttr consPort = fifo.getConsumerPort(consumerIndex);
966 std::optional<int> consChannel =
967 consPort ? consPort.channelIndex() : std::nullopt;
969 std::string consDmaName = (fifoName + suffix +
"__dma").str();
970 if (timeLink && timeLink.isMerge()) {
972 timeLinkEndpointFor(timeLink, *consRef, consChannel).getSymName();
973 }
else if (consRef) {
974 createDmaEndpoint(loc, consDmaName, consumerTile, *consRef,
975 ObjectFifoRole::Fill, fifo,
976 consumerDims.empty() ? BDDimLayoutArrayAttr()
977 : consumerDims[consumerIndex],
980 createRouteEndpoint(loc, consDmaName, consumerTile,
981 fifo.streamsToConsumer(consumerIndex)
983 : fifo.getPlio() ? WireBundle::PLIO
987 destinations.push_back(
988 FlatSymbolRefAttr::get(builder.getContext(), consDmaName));
990 shimEndpointName[fifo] = consDmaName;
995 FlatSymbolRefAttr source =
996 FlatSymbolRefAttr::get(builder.getContext(), prodDmaName);
997 if (timeLink && timeLink.isMerge()) {
998 mergeSources[timeLink].push_back(source);
1003 PacketInfoAttr header = fifo.packetHeader();
1004 if (timeLink && timeLink.isDispatch()) {
1005 dispatchTurns[timeLink][fifo] = destinations.front();
1007 header = PacketInfoAttr::get(builder.getContext(), 0, std::nullopt);
1009 RouteOp::create(builder, loc, builder.getArrayAttr({source}),
1010 builder.getArrayAttr(destinations), header);
1013 createTimeLinkRoutes();
1015 SmallVector<Operation *> toErase;
1016 for (ObjectFifoCreateOp fifo : fifos) {
1018 auto shim = shimEndpointName.find(fifo);
1019 if (shim != shimEndpointName.end()) {
1020 (void)SymbolTable::replaceAllSymbolUses(
1021 fifo, builder.getStringAttr(shim->second), device);
1023 toErase.push_back(fifo);
1025 for (
auto linkOp : device.getOps<ObjectFifoLinkOp>()) {
1026 toErase.push_back(linkOp);
1028 for (
auto allocOp : device.getOps<ObjectFifoAllocateOp>()) {
1029 toErase.push_back(allocOp);
1031 for (
auto regOp : device.getOps<ObjectFifoRegisterExternalBuffersOp>()) {
1032 toErase.push_back(regOp);
1034 for (Operation *op : toErase) {
1041std::unique_ptr<OperationPass<DeviceOp>>
1043 return std::make_unique<AIEObjectFifoSplitPass>();
SharedMemory
Which of two tiles' memory modules both of them can address.
@ None
Neither tile can address the other's memory module.
AIETargetModel::SharedMemory sharedMemory(TileOp a, TileOp b)
Which of a's and b's memory modules both tiles can address.
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoSplitPass()