MLIR-AIE 1.4.6.dev
AIEObjectFifoLowerDMAs.cpp
Go to the documentation of this file.
1//===- AIEObjectFifoLowerDMAs.cpp -------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2026 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
10
11#include "mlir/IR/Attributes.h"
12#include "mlir/Pass/Pass.h"
13#include "llvm/Support/MathExtras.h"
14
15#include <map>
16
17using namespace mlir;
18using namespace xilinx;
19using namespace xilinx::AIE;
20
21namespace xilinx::AIE {
22#define GEN_PASS_DEF_AIEOBJECTFIFOLOWERDMAS
23#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
24} // namespace xilinx::AIE
25
26namespace {
27
28/// One buffer descriptor's worth of work: which object, which slice of it, and
29/// the locks guarding that slice.
30struct Descriptor {
31 Value buffer;
32 int64_t offset;
33 int64_t size;
34 BDDimLayoutArrayAttr dimensions;
35 BDPadLayoutArrayAttr padding;
36 FlatSymbolRefAttr acquireLock;
37 FlatSymbolRefAttr releaseLock;
38 /// Which of the pool's objects this moves; also the index of the object's
39 /// own binary lock, where the device uses those.
40 int64_t object;
41 /// Lock units taken and given back, where they are not one object's.
42 std::optional<int64_t> acquireValue = std::nullopt;
43 std::optional<int64_t> releaseValue = std::nullopt;
44 /// How the address steps on each run of a descriptor that walks a
45 /// contiguous pool's objects in turn.
46 BDIterationAttr iteration = nullptr;
47};
48
49Block *findEndOpBlock(Region &region) {
50 Block *endBlock = nullptr;
51 for (Block &block : region) {
52 if (!block.getOps<EndOp>().empty()) {
53 endBlock = &block;
54 }
55 }
56 return endBlock;
57}
58
59struct AIEObjectFifoLowerDMAsPass
60 : public xilinx::AIE::impl::AIEObjectFifoLowerDMAsBase<
61 AIEObjectFifoLowerDMAsPass> {
62
63 DeviceOp device;
64 int32_t nextSendTag = 0;
65 llvm::DenseSet<int32_t> usedSendTags;
66 OpBuilder builder{static_cast<MLIRContext *>(nullptr)};
67
68 /// The DMA program of `tile`, created empty if the tile has none yet. A
69 /// program shared by several endpoints is located at all of them.
70 DmaBody dmaProgramFor(TileLike tile, Location loc) {
71 Value wanted = tile->getResult(0);
72 for (auto program : device.getOps<DmaBody>()) {
73 if (program.getTile() == wanted) {
74 program->setLoc(
75 FusedLoc::get(loc.getContext(), {program->getLoc(), loc}));
76 return program;
77 }
78 }
79
80 OpBuilder::InsertionGuard g(builder);
81 builder.setInsertionPoint(device.getBody()->getTerminator());
82 DmaBody program;
83 if (tile.isShimTile()) {
84 program = ShimDMAOp::create(builder, loc, builder.getIndexType(), wanted);
85 } else if (tile.isMemTile()) {
86 program = MemTileDMAOp::create(builder, loc, wanted);
87 } else {
88 program = MemOp::create(builder, loc, wanted);
89 }
90
91 builder.setInsertionPointToStart(&program.getDmaBody().emplaceBlock());
92 EndOp::create(builder, loc);
93 return program;
94 }
95
96 /// Objects the endpoint moves.
97 SmallVector<Value> buffersOf(ObjectFifoPoolOp pool) {
98 SmallVector<Value> buffers;
99 for (BufferLike buffer : pool.getBufferOps()) {
100 buffers.push_back(buffer.getBuffer());
101 }
102 return buffers;
103 }
104
105 /// A contiguous pool's one buffer holds its objects side by side. Its
106 /// filling end steps through them with one descriptor; its draining end
107 /// walks the stream with one descriptor per output, each taking the objects
108 /// up to the newest it reads and giving back those no later one reads.
109 SmallVector<Descriptor>
110 contiguousDescriptors(ObjectFifoDmaEndpointOp endpoint, Value buffer,
111 bool drains) {
112 ObjectFifoPoolOp pool = endpoint.getPoolOp();
113 ObjectFifoSegmentOp segment = endpoint.getSelectedSegments().front();
114 FlatSymbolRefAttr take =
115 drains ? segment.getConsumeLockAttr() : segment.getProduceLockAttr();
116 FlatSymbolRefAttr give =
117 drains ? segment.getProduceLockAttr() : segment.getConsumeLockAttr();
118 MLIRContext *context = builder.getContext();
119 if (!endpoint.walksStream()) {
120 BDDimLayoutArrayAttr dims;
121 if (auto all = endpoint.getDimensions(); all && !all->empty())
122 dims = (*all)[0];
123 int64_t object = pool.getObjectSize();
124 int64_t first = 0;
125 if (auto zeros = pool.getZeroObjects())
126 first = (*zeros)[0] * object;
127 int64_t walkers = endpoint.getNumDescriptors();
128 SmallVector<Descriptor> descriptors;
129 for (int64_t j = 0; j < walkers; j++)
130 descriptors.push_back(
131 {buffer, first + j * object + segment.getOffset(),
132 segment.getSize(), dims, BDPadLayoutArrayAttr(), take, give, j,
133 std::nullopt, std::nullopt,
134 BDIterationAttr::get(context, pool.getDepth() / walkers,
135 walkers * object, 0)});
136 return descriptors;
137 }
138
139 StreamWindow window;
140 if (failed(endpoint.getStreamWindow(window)))
141 return {};
142 BDDimLayoutArrayAttr dims =
143 BDDimLayoutArrayAttr::get(context, window.inner);
144 SmallVector<Descriptor> descriptors;
145 for (StreamWindowStep step : streamWindowSteps(window))
146 descriptors.push_back({buffer, step.first * window.objectSize,
147 window.transfer(), dims, BDPadLayoutArrayAttr(),
148 take, give, 0, step.acquire, step.release,
149 nullptr});
150 return descriptors;
151 }
152
153 /// Buffer-major, segment-minor: the DMA walks the objects in turn, and within
154 /// each object the slices this endpoint is responsible for.
155 SmallVector<Descriptor> descriptorsFor(ObjectFifoDmaEndpointOp endpoint,
156 ArrayRef<Value> buffers, bool drains) {
157 if (endpoint.getPoolOp().getContiguous())
158 return contiguousDescriptors(endpoint, buffers.front(), drains);
159 std::vector<ObjectFifoSegmentOp> segments = endpoint.getSelectedSegments();
160 SmallVector<BDDimLayoutArrayAttr> dimensions;
161 if (auto allDimensions = endpoint.getDimensions()) {
162 llvm::append_range(dimensions, *allDimensions);
163 }
164 SmallVector<BDPadLayoutArrayAttr> padding;
165 if (auto allPadding = endpoint.getPadDimensions()) {
166 llvm::append_range(padding, *allPadding);
167 }
168
169 SmallVector<Descriptor> descriptors;
170 for (auto [index, buffer] : llvm::enumerate(buffers)) {
171 for (auto [position, segment] : llvm::enumerate(segments)) {
172 descriptors.push_back(
173 {buffer, segment.getOffset(), segment.getSize(),
174 position < dimensions.size() ? dimensions[position]
175 : BDDimLayoutArrayAttr(),
176 position < padding.size() ? padding[position]
177 : BDPadLayoutArrayAttr(),
178 drains ? segment.getConsumeLockAttr()
179 : segment.getProduceLockAttr(),
180 drains ? segment.getProduceLockAttr()
181 : segment.getConsumeLockAttr(),
182 static_cast<int64_t>(index)});
183 }
184 }
185 return descriptors;
186 }
187
188 void emitDescriptor(ObjectFifoDmaEndpointOp endpoint, ObjectFifoPoolOp pool,
189 Descriptor &descriptor, PacketInfoAttr packet,
190 Block *successor, bool binaryLocks, bool drains,
191 int64_t transmittedObject = -1,
192 ArrayRef<int32_t> sendTags = {}) {
193 Location loc = endpoint.getLoc();
194 int count = drains ? 1 : pool.getRepeatCount().value_or(1);
195
196 LockOp acquireLock, releaseLock;
197 int acquireValue = count, releaseValue = count;
198 auto action = LockAction::AcquireGreaterEqual;
199 if (binaryLocks) {
200 // A binary lock's value encodes which side owns the buffer.
201 SmallVector<LockOp> locks = pool.getLockOps();
202 if (descriptor.object < (int64_t)locks.size()) {
203 acquireLock = releaseLock = locks[descriptor.object];
204 acquireValue = drains ? 1 : 0;
205 releaseValue = drains ? 0 : 1;
206 action = LockAction::Acquire;
207 }
208 } else if (descriptor.acquireLock) {
209 acquireLock =
210 lookupNamedOp<LockOp>(device, descriptor.acquireLock.getAttr());
211 releaseLock =
212 lookupNamedOp<LockOp>(device, descriptor.releaseLock.getAttr());
213 }
214
215 if (descriptor.acquireValue)
216 acquireValue = *descriptor.acquireValue;
217 if (descriptor.releaseValue)
218 releaseValue = *descriptor.releaseValue;
219 if (acquireLock) {
220 UseLockOp::create(builder, loc, acquireLock, action, acquireValue);
221 }
222 if (packet) {
223 DMABDPACKETOp::create(builder, loc, packet.getPktType(),
224 packet.assignedId());
225 }
226
227 DMABDOp bd;
228 if (descriptor.dimensions && drains && descriptor.padding) {
229 bd = DMABDOp::create(builder, loc, descriptor.buffer, descriptor.offset,
230 paddedSize(descriptor.dimensions, descriptor.padding,
231 descriptor.size),
232 descriptor.dimensions, descriptor.padding);
233 } else if (descriptor.dimensions) {
234 bd = DMABDOp::create(builder, loc, descriptor.buffer, descriptor.offset,
235 descriptor.size, descriptor.dimensions);
236 } else {
237 bd = DMABDOp::create(builder, loc, descriptor.buffer, descriptor.offset,
238 descriptor.size);
239 }
240 if (endpoint.getOutOfOrder()) {
241 auto tags =
242 drains ? endpoint.getOutOfOrderTags() : pool.getOutOfOrderTags();
243 if (tags) {
244 assert(descriptor.object < static_cast<int64_t>(tags->size()) &&
245 "the allocator names every descriptor of an out-of-order pool");
246 int64_t slot = transmittedObject >= 0 && drains
247 ? transmittedObject % tags->size()
248 : descriptor.object;
249 int32_t tag = (*tags)[slot];
250 if (endpoint.getRouteDirection() == DMAChannelDir::MM2S)
251 bd.setOutOfOrderTag(tag);
252 else
253 bd.setBdTag(tag);
254 }
255 }
256 if (descriptor.iteration && descriptor.iteration.getSize() > 1)
257 bd.setIterationAttr(descriptor.iteration);
258 if (!sendTags.empty()) {
259 assert(descriptor.object < static_cast<int64_t>(sendTags.size()) &&
260 "a send tag names every buffer of a core-pushed pool");
261 bd.setBdTag(sendTags[descriptor.object]);
262 }
263
264 if (releaseLock) {
265 UseLockOp::create(builder, loc, releaseLock, LockAction::Release,
266 releaseValue);
267 }
268 NextBDOp::create(builder, loc, successor);
269 }
270
271 /// A `dma_start` of `channel` on the endpoint's tile, chained after the
272 /// tile's others; returns the first BD's block and the program's end block.
273 std::pair<Block *, Block *> startChain(ObjectFifoDmaEndpointOp endpoint,
274 int channel, int taskCount,
275 bool corePushes = false) {
276 Location loc = endpoint.getLoc();
277 DmaBody program = dmaProgramFor(endpoint.getTileLike(), loc);
278 Block *endBlock = findEndOpBlock(program.getDmaBody());
279 Block *lastDmaBlock = endBlock->getSinglePredecessor();
280 Block *dmaBlock = builder.createBlock(endBlock);
281 Block *bdBlock = builder.createBlock(endBlock);
282
283 builder.setInsertionPointToStart(dmaBlock);
284 DMAStartOp::create(builder, loc, endpoint.getRouteDirection(), channel,
285 taskCount, endpoint.getPadValue(),
286 endpoint.getOutOfOrder() &&
287 endpoint.getRouteDirection() == DMAChannelDir::S2MM,
288 corePushes, bdBlock, endBlock);
289 if (lastDmaBlock) {
290 lastDmaBlock->getTerminator()->setSuccessor(dmaBlock, 1);
291 }
292 return {bdBlock, endBlock};
293 }
294
295 /// Endpoints taking turns on one channel share one chain: the k-th BD moves
296 /// the next object of the endpoint `turns` names k-th, through its own pool.
297 LogicalResult lowerTurns(ObjectFifoDmaEndpointOp first, int channel) {
298 SmallVector<ObjectFifoDmaEndpointOp> ends = turnEndpoints(first);
299 SmallVector<SmallVector<Descriptor>> descriptors;
300 for (ObjectFifoDmaEndpointOp end : ends) {
301 descriptors.push_back(
302 descriptorsFor(end, buffersOf(end.getPoolOp()), end.drains()));
303 if (static_cast<int64_t>(descriptors.back().size()) !=
304 end.getNumDescriptors() ||
305 descriptors.back().empty())
306 return end.emitOpError("takes turns, but lowers to ")
307 << descriptors.back().size() << " descriptors per pass for "
308 << end.getNumDescriptors();
309 }
310 SmallVector<unsigned> turns;
311 for (auto name : first.getTurnsAttr().getAsRange<FlatSymbolRefAttr>())
312 turns.push_back(llvm::find_if(ends,
313 [&](ObjectFifoDmaEndpointOp end) {
314 return end.getSymNameAttr() ==
315 name.getAttr();
316 }) -
317 ends.begin());
318
319 auto [bdBlock, endBlock] = startChain(first, channel, /*taskCount=*/0);
320 bool binaryLocks = !device.getTargetModel().hasProperty(
322 SmallVector<size_t> next(ends.size(), 0);
323 int64_t total = first.getNumBDs();
324 Block *current = bdBlock;
325 for (int64_t k = 0; k < total; k++) {
326 unsigned i = turns[k % turns.size()];
327 Block *successor =
328 k + 1 < total ? builder.createBlock(endBlock) : bdBlock;
329 builder.setInsertionPointToStart(current);
330 emitDescriptor(ends[i], ends[i].getPoolOp(), descriptors[i][next[i]],
331 PacketInfoAttr(), successor, binaryLocks,
332 ends[i].drains());
333 next[i] = (next[i] + 1) % descriptors[i].size();
334 current = successor;
335 }
336 return success();
337 }
338
339 LogicalResult lowerEndpoint(ObjectFifoDmaEndpointOp endpoint, int channel) {
340 bool drains = endpoint.drains();
341 ObjectFifoPoolOp pool = endpoint.getPoolOp();
342 SmallVector<Value> buffers = buffersOf(pool);
343 if (buffers.empty()) {
344 return success();
345 }
346
347 SmallVector<Descriptor> descriptors =
348 descriptorsFor(endpoint, buffers, drains);
349 if (descriptors.empty()) {
350 return success();
351 }
352 // Allocation budgeted the channel's BDs by getNumBDs.
353 if (static_cast<int64_t>(descriptors.size()) !=
354 endpoint.getNumDescriptors())
355 return endpoint.emitOpError()
356 << "lowers to " << descriptors.size()
357 << " descriptors per pass, but its channel was allocated for "
358 << endpoint.getNumDescriptors();
359
360 Location loc = endpoint.getLoc();
361 // emitDescriptor scales the filling end's lock count instead.
362 int64_t repeat = endpoint.getRepeat();
363 std::optional<int32_t> iterCount = endpoint.getIterCount();
364
365 bool repeatInHardware = endpoint.repeatsInHardware();
366 // A dispatching chain runs until its turns line up with the pool's
367 // objects again, so one run covers that many passes.
368 int64_t passes = endpoint.getDispatchPasses();
369 int taskCount = iterCount ? *iterCount / passes - 1
370 : (repeatInHardware ? repeat - 1 : 0);
371 int64_t copies = repeatInHardware ? 1 : repeat;
372
373 // With push_queue the configuration pushes nothing: the core pushes
374 // the descriptor of each object it releases. Each descriptor then runs
375 // on its own, so a pool whose buffers do not map one-to-one onto them
376 // leaves the core no descriptor to name.
377 bool corePushes =
378 drains && endpoint.getReleaseStrategy() == ObjectFifoRelease::PushQueue;
379 SmallVector<int32_t> sendTags;
380 if (corePushes) {
381 bool hasRelease = false;
382 ObjectFifoCoreEndpointOp producer;
383 auto sentSegments = endpoint.getSelectedSegments();
384 for (auto coreEnd : device.getOps<ObjectFifoCoreEndpointOp>()) {
385 if (coreEnd.drains() || coreEnd.getPoolOp() != pool ||
386 coreEnd.getTile() != endpoint.getTile())
387 continue;
388 auto filledSegments = coreEnd.getSelectedSegments();
389 if (!llvm::any_of(sentSegments, [&](ObjectFifoSegmentOp segment) {
390 return llvm::is_contained(filledSegments, segment);
391 }))
392 continue;
393 if (producer)
394 return endpoint.emitOpError(
395 "push_queue requires a single core filler for its segment");
396 producer = coreEnd;
397 for (auto core : device.getOps<CoreOp>()) {
398 if (core.getTile() == coreEnd.getTile())
399 core.walk([&](ObjectFifoReleaseOp release) {
400 hasRelease |= release.getObjFifoName() == coreEnd.getSymName();
401 });
402 }
403 }
404 if (!hasRelease)
405 return endpoint.emitOpError(
406 "push_queue requires producer releases in a "
407 "local core to submit send tasks");
408 if (passes != 1)
409 return endpoint.emitOpError(
410 "push_queue requires one dispatch pass per object");
411 // Each release submits one task, with repetitions carried by that task.
412 taskCount = 0;
413 if (static_cast<int64_t>(descriptors.size()) != pool.getDepth())
414 return endpoint.emitOpError("sends ")
415 << descriptors.size() << " descriptors for a pool of "
416 << pool.getDepth()
417 << " objects, so a release names no one of them; push_queue "
418 << "needs one descriptor per object";
419 for (size_t slot = 0; slot < descriptors.size(); ++slot) {
420 while (usedSendTags.contains(nextSendTag))
421 ++nextSendTag;
422 sendTags.push_back(nextSendTag++);
423 }
424 producer.setCorePushAttr(
425 ObjectFifoCorePushAttr::get(pool.getContext(), channel, sendTags));
426 }
427
428 SmallVector<PacketInfoAttr> turns;
429 if (std::optional<ArrayAttr> headers = endpoint.getDispatchPackets()) {
430 llvm::append_range(turns, headers->getAsRange<PacketInfoAttr>());
431 } else if (endpoint.getDispatch()) {
432 return endpoint.emitOpError(
433 "has no dispatchPackets; run --aie-objectfifo-allocate");
434 }
435 // A contiguous pool's descriptor steps through every object, so it has no
436 // one object for a turn to follow.
437 if (!turns.empty() && pool.getContiguous())
438 return endpoint.emitOpError(
439 "dispatches from a contiguous pool, which has no per-object BDs");
440 int64_t depth = pool.getDepth();
441
442 auto [bdBlock, endBlock] =
443 startChain(endpoint, channel, taskCount, corePushes);
444 bool binaryLocks = !device.getTargetModel().hasProperty(
446 size_t total = descriptors.size() * copies * passes;
447 size_t emitted = 0;
448 Block *current = bdBlock;
449 for (int64_t pass = 0; pass < passes; pass++) {
450 for (Descriptor &descriptor : descriptors) {
451 PacketInfoAttr packet = endpoint.getPacketAttr();
452 if (!turns.empty())
453 packet = turns[(pass * depth + descriptor.object) % turns.size()];
454 for (int64_t copy = 0; copy < copies; copy++) {
455 Block *successor;
456 if (emitted + 1 < total) {
457 successor = builder.createBlock(endBlock);
458 } else if (iterCount) {
459 // A bounded chain exits after its final iteration.
460 successor = builder.createBlock(endBlock);
461 builder.setInsertionPointToStart(successor);
462 EndOp::create(builder, loc);
463 } else {
464 successor = bdBlock;
465 }
466 builder.setInsertionPointToStart(current);
467 emitDescriptor(endpoint, pool, descriptor, packet, successor,
468 binaryLocks, drains, emitted, sendTags);
469 current = successor;
470 emitted++;
471 }
472 }
473 }
474 return success();
475 }
476
477 LogicalResult
478 lowerOutOfOrderReceivers(ArrayRef<ObjectFifoDmaEndpointOp> endpoints,
479 int channel) {
480 auto first = endpoints.front();
481 SmallVector<SmallVector<Descriptor>> descriptors;
482 int64_t objects = 0;
483 bool unbounded = false;
484 size_t totalBDs = 0;
485 for (auto endpoint : endpoints) {
486 descriptors.push_back(
487 descriptorsFor(endpoint, buffersOf(endpoint.getPoolOp()), false));
488 if (descriptors.back().empty() ||
489 static_cast<int64_t>(descriptors.back().size()) !=
490 endpoint.getNumDescriptors())
491 return endpoint.emitOpError(
492 "incorrect number of receiving descriptors");
493 totalBDs += descriptors.back().size();
494 if (auto iterations = endpoint.getIterCount())
495 objects += descriptors.back().size() * *iterations;
496 else
497 unbounded = true;
498 }
499 int64_t perTask = device.getTargetModel().getMaxRepeatCount() + 1;
500 if (unbounded)
501 objects = perTask;
502 int64_t tasks = llvm::divideCeil(objects, perTask);
503 if (tasks > device.getTargetModel().getDmaTaskQueueDepth())
504 return first.emitOpError("shared receiving channel needs ")
505 << tasks << " initial tasks for " << objects
506 << " objects, exceeding its queue capacity; use an unbounded "
507 "receiver with core replenishment";
508
509 auto [head, endBlock] =
510 startChain(first, channel, std::min(objects, perTask) - 1);
511 for (objects -= perTask; objects > 0; objects -= perTask) {
512 auto [unusedHead, unusedEnd] =
513 startChain(first, channel, std::min(objects, perTask) - 1);
514 unusedHead->replaceAllUsesWith(head);
515 unusedHead->erase();
516 }
517 bool binaryLocks = !device.getTargetModel().hasProperty(
519 Block *current = head;
520 size_t emitted = 0;
521 for (auto [index, bds] : llvm::enumerate(descriptors)) {
522 ObjectFifoDmaEndpointOp endpoint = endpoints[index];
523 for (auto &descriptor : bds) {
524 Block *successor = builder.createBlock(endBlock);
525 if (++emitted == totalBDs) {
526 builder.setInsertionPointToStart(successor);
527 EndOp::create(builder, endpoint.getLoc());
528 }
529 builder.setInsertionPointToStart(current);
530 emitDescriptor(endpoint, endpoint.getPoolOp(), descriptor,
531 endpoint.getPacketAttr(), successor, binaryLocks, false);
532 current = successor;
533 }
534 }
535 return success();
536 }
537
538 void runOnOperation() override {
539 device = getOperation();
540 builder = OpBuilder(device.getContext());
541
542 // Reserve tags from both pool allocation and hand-written descriptors.
543 nextSendTag = 0;
544 usedSendTags.clear();
545 for (auto pool : device.getOps<ObjectFifoPoolOp>())
546 if (auto tags = pool.getOutOfOrderTags())
547 usedSendTags.insert(tags->begin(), tags->end());
548 for (auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>())
549 if (auto push = endpoint.getCorePushAttr())
550 usedSendTags.insert(push.getBdTags().begin(), push.getBdTags().end());
551 device.walk([&](DMABDOp bd) {
552 if (auto tag = bd.getBdTag())
553 usedSendTags.insert(*tag);
554 });
555
556 SmallVector<ObjectFifoDmaEndpointOp> endpoints(
557 device.getOps<ObjectFifoDmaEndpointOp>());
558 DenseMap<Operation *, ObjectFifoDmaEndpointOp> pushedDrainers;
559 for (ObjectFifoDmaEndpointOp endpoint : endpoints) {
560 if (!endpoint.drains() ||
561 endpoint.getReleaseStrategy() != ObjectFifoRelease::PushQueue)
562 continue;
563 auto [it, inserted] =
564 pushedDrainers.try_emplace(endpoint.getPoolOp(), endpoint);
565 if (!inserted) {
566 auto diag = endpoint.emitOpError(
567 "a pool supports only one core-pushed DMA drainer");
568 diag.attachNote(it->second.getLoc())
569 << "first core-pushed drainer is here";
570 return signalPassFailure();
571 }
572 }
573 // A tile's chains, and so the BD ids they get, follow its channels rather
574 // than the order the endpoints were written in.
575 DenseMap<Value, size_t> tileOrder;
576 for (ObjectFifoDmaEndpointOp endpoint : endpoints)
577 tileOrder.try_emplace(endpoint.getTile(), tileOrder.size());
578 llvm::stable_sort(
579 endpoints, [&](ObjectFifoDmaEndpointOp a, ObjectFifoDmaEndpointOp b) {
580 return std::make_tuple(tileOrder.lookup(a.getTile()),
581 a.getRouteDirection(),
582 a.getChannelIndex().value_or(-1)) <
583 std::make_tuple(tileOrder.lookup(b.getTile()),
584 b.getRouteDirection(),
585 b.getChannelIndex().value_or(-1));
586 });
587 using ChannelKey = std::tuple<int, int, int>;
588 std::map<ChannelKey, SmallVector<ObjectFifoDmaEndpointOp>> receivers;
589 for (auto endpoint : endpoints) {
590 if (endpoint.getOutOfOrder() && !endpoint.drains()) {
591 auto tile = endpoint.getTile().getDefiningOp<TileOp>();
592 receivers[{tile.getCol(), tile.getRow(),
593 endpoint.getChannelIndex().value_or(-1)}]
594 .push_back(endpoint);
595 }
596 }
597 for (ObjectFifoDmaEndpointOp endpoint : endpoints) {
598 std::optional<int> channel = endpoint.getChannelIndex();
599 if (!channel) {
600 endpoint.emitOpError("has no channel; run --aie-objectfifo-allocate");
601 return signalPassFailure();
602 }
603 if (endpoint.getOutOfOrder() && !endpoint.drains()) {
604 auto tile = endpoint.getTile().getDefiningOp<TileOp>();
605 auto &group = receivers.at({tile.getCol(), tile.getRow(), *channel});
606 if (group.front() == endpoint &&
607 failed(lowerOutOfOrderReceivers(group, *channel)))
608 return signalPassFailure();
609 continue;
610 }
611 SmallVector<ObjectFifoDmaEndpointOp> turns = turnEndpoints(endpoint);
612 if (turns.empty() ? failed(lowerEndpoint(endpoint, *channel))
613 : turns.front() == endpoint &&
614 failed(lowerTurns(endpoint, *channel)))
615 return signalPassFailure();
616 }
617 for (ObjectFifoDmaEndpointOp endpoint : endpoints)
618 endpoint.erase();
619
620 // Nothing to program at the far end of these; the routes they asked for
621 // have been drawn.
622 for (auto endpoint :
623 llvm::make_early_inc_range(device.getOps<RouteEndpointOp>())) {
624 endpoint.erase();
625 }
626 }
627};
628
629} // namespace
630
631std::unique_ptr<OperationPass<DeviceOp>>
633 return std::make_unique<AIEObjectFifoLowerDMAsPass>();
634}
llvm::SmallVector< ObjectFifoDmaEndpointOp > turnEndpoints(ObjectFifoDmaEndpointOp end)
llvm::SmallVector< StreamWindowStep > streamWindowSteps(const StreamWindow &w)
mlir::Operation * lookupNamedOp(mlir::Operation *from, mlir::StringAttr name)
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoLowerDMAsPass()
int64_t paddedSize(llvm::ArrayRef< BDDimLayoutAttr > dims, llvm::ArrayRef< BDPadLayoutAttr > pads, int64_t fallback)
llvm::SmallVector< BDDimLayoutAttr > inner
Definition AIEDialect.h:410