13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/Dialect/MemRef/IR/MemRef.h"
15#include "mlir/Dialect/SCF/IR/SCF.h"
16#include "mlir/IR/Builders.h"
17#include "mlir/Pass/Pass.h"
20#define GEN_PASS_DEF_AIEOBJECTFIFOINSERTCOREQUEUEPUSHES
21#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
30struct AIEObjectFifoInsertCoreQueuePushesPass
31 :
public xilinx::AIE::impl::AIEObjectFifoInsertCoreQueuePushesBase<
32 AIEObjectFifoInsertCoreQueuePushesPass> {
33 using Base = xilinx::AIE::impl::AIEObjectFifoInsertCoreQueuePushesBase<
34 AIEObjectFifoInsertCoreQueuePushesPass>;
38 Value makeCounter(CoreOp core, int32_t initial) {
39 OpBuilder builder(core.getContext());
40 Block &entry = core.getBody().front();
41 builder.setInsertionPointToStart(&entry);
42 Location loc = core.getLoc();
43 Type i32 = builder.getI32Type();
45 memref::AllocaOp::create(builder, loc, MemRefType::get({}, i32));
46 Value start = arith::ConstantOp::create(builder, loc, i32,
47 builder.getI32IntegerAttr(initial));
48 memref::StoreOp::create(builder, loc, start, slot, ValueRange{});
55 void emitCountAndPush(OpBuilder &builder, Location loc, Value counter,
56 int32_t released, int32_t perPush, int32_t repeat,
57 int channel,
bool assumeQueueSpace) {
58 Type i32 = builder.getI32Type();
59 Value left = memref::LoadOp::create(builder, loc, counter, ValueRange{});
60 Value step = arith::ConstantOp::create(builder, loc, i32,
61 builder.getI32IntegerAttr(released));
62 Value zero = arith::ConstantOp::create(builder, loc, i32,
63 builder.getI32IntegerAttr(0));
64 Value run = arith::ConstantOp::create(builder, loc, i32,
65 builder.getI32IntegerAttr(perPush));
66 Value rest = arith::SubIOp::create(builder, loc, left, step);
67 Value spent = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::sle,
70 scf::IfOp::create(builder, loc, spent,
false);
72 OpBuilder::InsertionGuard guard(builder);
73 builder.setInsertionPointToStart(push.thenBlock());
76 if (checkQueueSpace && !assumeQueueSpace)
77 DmaPollForQueueSpaceOp::create(builder, loc, DMAChannelDir::S2MM,
80 DmaPushQueueOp::create(builder, loc, DMAChannelDir::S2MM, channel,
81 builder.getI32IntegerAttr(0),
nullptr,
86 Value renewed = arith::AddIOp::create(builder, loc, rest, run);
87 Value next = arith::SelectOp::create(builder, loc, spent, renewed, rest);
88 memref::StoreOp::create(builder, loc, next, counter, ValueRange{});
91 void runOnOperation()
override {
92 DeviceOp device = getOperation();
93 const AIETargetModel &targetModel = device.getTargetModel();
95 using ChannelKey = std::tuple<int, int, int>;
96 auto channelKey = [](ObjectFifoDmaEndpointOp endpoint) -> ChannelKey {
97 auto tile = endpoint.getTile().getDefiningOp<TileOp>();
98 return {tile.getCol(), tile.getRow(),
99 endpoint.getChannelIndex().value_or(-1)};
101 DenseSet<ChannelKey> unboundedChannels;
102 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>())
103 if (endpoint.getOutOfOrder() && !endpoint.drains() &&
104 !endpoint.getIterCount())
105 unboundedChannels.insert(channelKey(endpoint));
109 DenseMap<StringRef, ObjectFifoDmaEndpointOp> receiverOfPool;
110 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>())
111 if (endpoint.getOutOfOrder() && !endpoint.drains() &&
112 unboundedChannels.contains(channelKey(endpoint)))
113 receiverOfPool[endpoint.getPool()] = endpoint;
114 if (receiverOfPool.empty())
116 DenseSet<StringRef> replenishedPools;
119 int32_t perPush = repeat + 1;
121 for (CoreOp core : device.getOps<CoreOp>()) {
124 DenseMap<int, SmallVector<std::pair<ObjectFifoReleaseOp, int32_t>>>
126 WalkResult walked = core.walk([&](ObjectFifoReleaseOp release) {
128 SymbolTable::lookupNearestSymbolFrom<ObjectFifoCoreEndpointOp>(
129 device, release.getObjFifoNameAttr());
130 if (!endpoint || !endpoint.drains())
131 return WalkResult::advance();
132 auto it = receiverOfPool.find(endpoint.getPool());
133 if (it == receiverOfPool.end())
134 return WalkResult::advance();
135 std::optional<int32_t> channel = it->second.getChannelIndex();
137 it->second.emitOpError(
"receives out of order without a channel; "
138 "run --aie-objectfifo-allocate first");
139 return WalkResult::interrupt();
141 auto receiverTile = it->second.getTile().getDefiningOp<TileOp>();
142 auto coreTile = core.getTileOp();
143 if (receiverTile.getCol() != coreTile.getCol() ||
144 receiverTile.getRow() != coreTile.getRow())
145 return WalkResult::advance();
146 replenishedPools.insert(endpoint.getPool());
147 releasesOfChannel[*channel].emplace_back(release, release.relNumber());
148 return WalkResult::advance();
150 if (walked.wasInterrupted())
151 return signalPassFailure();
152 if (releasesOfChannel.empty())
155 for (
auto &[channel, releases] : releasesOfChannel) {
159 Value counter = makeCounter(core, 1);
160 for (
auto [release, size] : releases) {
161 OpBuilder builder(release.getContext());
162 builder.setInsertionPointAfter(release);
163 emitCountAndPush(builder, release.getLoc(), counter, size, perPush,
164 repeat, channel, release.getAssumeQueueSpace());
168 for (
auto &[pool, receiver] : receiverOfPool) {
169 if (!replenishedPools.contains(pool)) {
170 receiver.emitOpError(
171 "unbounded out-of-order receiving channel requires a local core "
172 "consumer release to replenish it; specify iter_count on every "
173 "FIFO sharing this channel");
174 return signalPassFailure();
182std::unique_ptr<OperationPass<DeviceOp>>
184 return std::make_unique<AIEObjectFifoInsertCoreQueuePushesPass>();
187std::unique_ptr<OperationPass<DeviceOp>>
189 const AIEObjectFifoInsertCoreQueuePushesOptions &options) {
190 return std::make_unique<AIEObjectFifoInsertCoreQueuePushesPass>(options);
virtual uint32_t getMaxRepeatCount() const =0
Return the largest DMA task repeat count (unsupported = 0).
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoInsertCoreQueuePushesPass()