18#include "mlir/IR/Attributes.h"
19#include "mlir/Interfaces/DataLayoutInterfaces.h"
20#include "mlir/Pass/Pass.h"
21#include "llvm/ADT/MapVector.h"
22#include "llvm/ADT/StringMap.h"
23#include "llvm/Support/Debug.h"
27#define DEBUG_TYPE "aie-objectfifo-allocate"
34#define GEN_PASS_DEF_AIEOBJECTFIFOALLOCATE
35#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
40struct AIEObjectFifoAllocatePass
41 :
public xilinx::AIE::impl::AIEObjectFifoAllocateBase<
42 AIEObjectFifoAllocatePass> {
46 OpBuilder builder{
static_cast<MLIRContext *
>(
nullptr)};
48 DenseMap<Value, Operation *> lastPlaced;
50 DenseSet<Operation *> filledPools;
52 llvm::StringMap<SmallVector<Operation *>> rearmUsers;
55 SmallVector<Operation *> loweredFlows;
58 DenseMap<StringAttr, SmallVector<RouteOp>> sourcedFlows;
59 llvm::MapVector<ObjectFifoDmaEndpointOp, llvm::StringMap<PacketInfoAttr>>
63 llvm::MapVector<Operation *, Operation *> channelSharers;
64 DenseMap<Value, SmallVector<int64_t>> plannedMemory;
65 DenseMap<Operation *, SmallVector<Value>> bufferPlacements;
66 DenseMap<Operation *, int> channelAssignments;
67 DenseMap<Operation *, Value> localPools;
68 DenseMap<Operation *, SmallVector<Value>> poolUsers;
69 DenseMap<Operation *, SmallVector<Operation *>> lockUsers;
70 DenseMap<Operation *, Value> lockPlacements;
74 DenseMap<Operation *, SmallVector<DMAStartOp>> endpointStarts;
75 DenseMap<Operation *, SmallVector<AIEX::DMAConfigureTaskForOp>> endpointTasks;
80 DenseMap<std::tuple<int, int, int>, int64_t> fixedBDs;
83 DenseMap<std::tuple<int, int, int>, int64_t> taskBDs;
86 DenseMap<std::tuple<int, int, int>, int64_t> channelDemand;
89 DenseMap<Operation *, unsigned> structuralRank;
90 RouteEndpoint channelFailure;
93 ObjectFifoPoolOp bufferFailure;
94 size_t unplacedAtFailure = 0;
97 SmallVector<TileOp> spillTiles;
99 Operation *lockAccessFailure =
nullptr;
101 bool sameTile(Value a, Value b) {
104 auto first = cast<TileLike>(a.getDefiningOp());
105 auto second = cast<TileLike>(b.getDefiningOp());
106 auto ac = first.tryGetCol(), ar = first.tryGetRow();
107 auto bc = second.tryGetCol(), br = second.tryGetRow();
108 return ac && ar && bc && br && ac == bc && ar == br;
111 int64_t memoryUsed(Value tile, int64_t extraSize = 0,
int extraCount = 0) {
112 SmallVector<BufferAllocation> layout;
114 device.getTargetModel().getMemTileLoadStoreBusWidth() / 8;
118 for (Operation &op : *device.getBody()) {
119 if (
auto placed = dyn_cast<TileLike>(op);
120 placed && sameTile(tile, placed->getResult(0))) {
121 Value value = placed->getResult(0);
122 for (int64_t size : plannedMemory[value])
123 layout.push_back({size, alignment, std::nullopt});
125 for (
int i = 0; i < extraCount; ++i)
126 layout.push_back({extraSize, alignment, std::nullopt});
127 }
else if (
auto buffer = dyn_cast<BufferOp>(op);
128 buffer && sameTile(tile, buffer.getTile())) {
129 layout.push_back({buffer.getAllocationSize(),
130 buffer.getAligned() ? alignment : 1,
131 buffer.getAddress()});
137 LogicalResult collectFixedMemory() {
138 plannedMemory.clear();
139 for (
auto tile : device.getOps<TileLike>()) {
140 if (!tile.isMemTile())
142 int64_t bytes = memoryUsed(tile->getResult(0));
143 int64_t capacity = device.getTargetModel().getMemTileSize();
144 if (bytes > capacity)
145 return tile->emitOpError(
"existing buffers require ")
146 << bytes <<
" bytes, exceeding MemTile capacity of " << capacity
152 bool canAccess(Value user, Value memory,
bool localOnly =
false) {
155 auto userTile = dyn_cast<TileLike>(user.getDefiningOp());
156 auto memoryTile = dyn_cast<TileLike>(memory.getDefiningOp());
157 if (!userTile || !memoryTile)
159 const auto &target = device.getTargetModel();
160 auto positions = [&](TileLike tile) {
161 SmallVector<TileID> compatible;
162 auto col = tile.tryGetCol(), row = tile.tryGetRow();
163 for (
int c = 0; c < target.columns(); ++c) {
164 if (col && c != *col)
166 for (
int r = 0; r < target.rows(); ++r) {
167 if ((!row || r == *row) &&
168 target.getTileType(c, r) == tile.getTileType())
169 compatible.push_back({c, r});
176 auto userPositions = positions(userTile);
177 auto memoryPositions = positions(memoryTile);
178 for (
TileID u : userPositions)
179 for (
TileID m : memoryPositions) {
180 if (localOnly && u != m)
182 auto shared = target.getSharedMemory(u, m);
183 if (shared == AIETargetModel::SharedMemory::Second ||
184 shared == AIETargetModel::SharedMemory::Either)
190 bool canPlace(ObjectFifoPoolOp pool, Value tile, int64_t sizeBytes,
192 if (cast<TileLike>(tile.getDefiningOp()).isMemTile() &&
193 memoryUsed(tile, sizeBytes, count) >
194 device.getTargetModel().getMemTileSize())
196 return llvm::all_of(poolUsers[pool],
197 [&](Value user) {
return canAccess(user, tile); });
200 Value lockPlacement(ObjectFifoPoolOp pool, Operation *group) {
201 if (Value placed = lockPlacements.lookup(group))
203 Value local = localPools.lookup(pool);
204 return local ? local : pool.getTile();
208 static int bufferCount(ObjectFifoPoolOp pool) {
209 return pool.getContiguous() ? 1 : pool.getDepth();
211 static int64_t bufferBytes(ObjectFifoPoolOp pool) {
212 return pool.getObjectSizeInBytes() * pool.getObjectsPerBuffer();
218 LogicalResult planBuffers(ArrayRef<ObjectFifoPoolOp> pools) {
219 plannedMemory.clear();
220 bufferPlacements.clear();
221 bufferFailure =
nullptr;
222 for (
auto pool : pools) {
223 if (!localPools.contains(pool))
225 Value tile = localPools.lookup(pool);
226 int count = pool.getBuffers() ? 0 : bufferCount(pool);
227 int64_t size = bufferBytes(pool);
228 if (!canAccess(pool.getTile(), tile) ||
229 !canPlace(pool, tile, size, count)) {
230 bufferFailure = pool;
233 plannedMemory[tile].append(count, size);
235 SmallVector<ObjectFifoPoolOp> generated;
236 for (
auto pool : pools) {
237 if (pool.getBuffers()) {
238 for (
auto buffer : pool.getBufferOps()) {
239 Value tile = buffer.getBufferTile();
240 if (tile && ((localPools.contains(pool) &&
241 !sameTile(tile, localPools.lookup(pool))) ||
242 !llvm::all_of(poolUsers[pool], [&](Value user) {
243 return canAccess(user, tile);
245 bufferFailure = pool;
248 bufferPlacements[pool].push_back(tile);
252 if (pool.getTileLike().isShimTile())
254 if (localPools.contains(pool))
255 bufferPlacements[pool].append(bufferCount(pool),
256 localPools.lookup(pool));
258 generated.append(bufferCount(pool), pool);
260 int retries = kSpillRetries;
261 return success(placeBuffers(generated, retries));
269 static constexpr int kSpillRetries = 256;
271 bool placeBuffers(ArrayRef<ObjectFifoPoolOp> buffers,
int &retries) {
274 ObjectFifoPoolOp pool = buffers.front();
275 int64_t size = bufferBytes(pool);
276 Value homeTile = pool.getTileLike()->getResult(0);
277 bool fitsHome = canPlace(pool, homeTile, size);
278 SmallVector<Value> candidates;
280 candidates.push_back(homeTile);
282 candidates = spillTargets(pool, size);
283 if (candidates.empty() &&
284 (!bufferFailure || buffers.size() < unplacedAtFailure)) {
285 bufferFailure = pool;
286 unplacedAtFailure = buffers.size();
288 auto tryTile = [&](Value tile) {
289 plannedMemory[tile].push_back(size);
290 bufferPlacements[pool].push_back(tile);
291 if (placeBuffers(buffers.drop_front(), retries))
293 plannedMemory[tile].pop_back();
294 bufferPlacements[pool].pop_back();
297 for (
auto [index, tile] : llvm::enumerate(candidates)) {
298 if (index > 0 && retries-- <= 0)
307 for (Value tile : spillTargets(pool, size)) {
325 SmallVector<Value> spillTargets(ObjectFifoPoolOp pool, int64_t sizeBytes) {
326 TileLike home = pool.getTileLike();
327 auto &target = device.getTargetModel();
328 if (!home.isMemTile())
331 auto homeOp = dyn_cast<TileOp>(home.getOperation());
336 SmallVector<TileOp> neighbors;
337 for (
int col : {homeOp.getCol() - 1, homeOp.getCol() + 1}) {
338 if (col < 0 || col >= target.columns()) {
341 int row = homeOp.getRow();
342 bool declared = llvm::any_of(device.getOps<TileOp>(), [&](TileOp t) {
343 return t.getCol() == col && t.getRow() == row;
346 TileOp::getOrCreate(builder, device, col, row, pool.getLoc());
348 spillTiles.push_back(neighbor);
351 if (shared == SharedMemory::Second || shared == SharedMemory::Either) {
352 neighbors.push_back(neighbor);
355 llvm::stable_sort(neighbors, [&](TileOp a, TileOp b) {
356 return memoryUsed(a.getResult()) < memoryUsed(b.getResult());
358 SmallVector<Value> fitting;
359 for (TileOp neighbor : neighbors) {
360 if (canPlace(pool, neighbor.getResult(), sizeBytes)) {
361 fitting.push_back(neighbor.getResult());
369 void setInsertionPointOn(Value tile) {
370 Operation *after = lastPlaced.lookup(tile);
372 after = tile.getDefiningOp();
375 builder.setInsertionPointAfter(after);
377 builder.setInsertionPointToStart(device.getBody());
381 void allocateBuffers(ObjectFifoPoolOp pool) {
382 if (pool.getBuffers()) {
385 TileLike home = pool.getTileLike();
386 if (home.isShimTile()) {
390 ArrayRef<Attribute> initValues;
391 if (
auto initAttr = pool.getInitValues()) {
392 initValues = initAttr->getValue();
394 int filled = initValues.size();
395 StringRef base = pool.getBaseName();
397 SmallVector<Attribute> names;
398 MemRefType type = pool.getElemType();
400 if (pool.getContiguous()) {
402 MemRefType::get({pool.getObjectsPerBuffer() * type.getNumElements()},
403 type.getElementType());
404 if (pool.getZeroObjects())
405 zeros = DenseElementsAttr::get(
406 RankedTensorType::get(type.getShape(), type.getElementType()),
407 builder.getZeroAttr(type.getElementType()));
410 for (
int i = 0; i < bufferCount(pool); i++) {
412 i < filled ? cast<ElementsAttr>(initValues[i]) : zeros;
413 std::string name = (base +
"_buff_" + std::to_string(i)).str();
414 Value placement = bufferPlacements[pool][i];
415 setInsertionPointOn(placement);
416 lastPlaced[placement] = BufferOp::create(
417 builder, pool.getLoc(), type, placement, builder.getStringAttr(name),
420 names.push_back(FlatSymbolRefAttr::get(builder.getContext(), name));
422 pool.setBuffersAttr(builder.getArrayAttr(names));
425 LockOp createLock(ObjectFifoPoolOp pool, Value tile, StringRef name,
427 setInsertionPointOn(tile);
428 auto lock = LockOp::create(builder, pool.getLoc(), tile, value);
429 lastPlaced[tile] = lock;
430 lock->setAttr(SymbolTable::getSymbolAttrName(),
431 builder.getStringAttr(name));
435 bool needsLocks(ObjectFifoPoolOp pool) {
436 if (pool.getDisableSynchronization()) {
440 int depth = pool.getDepth();
441 auto initValues = pool.getInitValues();
442 int filled = initValues ? initValues->size() : 0;
448 std::optional<StringRef> fifo = pool.getFifoName();
449 return filled != depth || filled <= 0 || filledPools.contains(pool) ||
450 (fifo && rearmUsers.contains(*fifo));
453 LogicalResult planLocks(ArrayRef<ObjectFifoPoolOp> pools) {
455 lockAccessFailure =
nullptr;
456 lockPlacements.clear();
457 DenseMap<std::pair<int, int>, int64_t> used;
458 auto reserve = [&](Value tile, int64_t count) {
459 auto like = cast<TileLike>(tile.getDefiningOp());
460 auto col = like.tryGetCol(), row = like.tryGetRow();
463 if (used[{*col, *row}] + count >
464 device.getTargetModel().getNumLocks(*col, *row)) {
468 used[{*col, *row}] += count;
471 for (
auto lock : device.getOps<LockOp>())
472 if (failed(reserve(lock.getTile(), 1)))
475 SmallVector<TileLike> tiles(device.getOps<TileLike>());
476 llvm::stable_sort(tiles, [&](TileLike a, TileLike b) {
477 auto aRank = structuralRank.find(a), bRank = structuralRank.find(b);
478 if ((aRank == structuralRank.end()) != (bRank == structuralRank.end()))
479 return bRank == structuralRank.end();
480 return aRank != structuralRank.end() && aRank->second < bRank->second;
482 auto place = [&](ObjectFifoPoolOp pool, Operation *group,
int count) {
483 SmallVector<Value> candidates{lockPlacement(pool, group)};
484 if (!localPools.contains(pool)) {
485 llvm::append_range(candidates, bufferPlacements[pool]);
486 for (Operation *user : lockUsers[group])
487 candidates.push_back(user->getOperand(0));
488 for (
auto tile : tiles)
489 candidates.push_back(tile->getResult(0));
491 bool reachable =
false;
492 for (Value tile : candidates) {
493 if (!tile || !llvm::all_of(lockUsers[group], [&](Operation *user) {
494 return canAccessLocks(user, tile);
498 if (succeeded(reserve(tile, count))) {
499 lockPlacements[group] = tile;
504 if (!reachable && !lockUsers[group].empty()) {
506 lockAccessFailure = lockUsers[group].front();
510 for (
auto pool : pools) {
511 if (!needsLocks(pool))
513 if (device.getTargetModel().getTargetArch() == AIEArch::AIE1) {
514 if (!pool.getLocks() && failed(place(pool, pool, pool.getDepth())))
517 for (
auto segment : pool.getSegmentOps())
518 if (!(segment.getProduceLock() && segment.getConsumeLock()) &&
519 failed(place(pool, segment, 2)))
523 auto check = [&](
auto endpoint) {
524 for (Value tile : endpointLockTiles(endpoint)) {
525 if (!canAccessLocks(endpoint, tile)) {
526 lockAccessFailure = endpoint;
532 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>())
533 if (failed(check(endpoint)))
535 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>())
536 if (failed(check(endpoint)))
544 void allocateLocks(ObjectFifoPoolOp pool) {
545 if (!needsLocks(pool))
547 StringRef base = pool.getBaseName();
548 int depth = pool.getDepth();
549 auto initValues = pool.getInitValues();
550 int filled = initValues ? initValues->size() : 0;
551 int repeat = pool.getRepeatCount().value_or(1);
553 if (device.getTargetModel().getTargetArch() == AIEArch::AIE1) {
554 if (pool.getLocks()) {
557 SmallVector<Attribute> names;
558 for (
int i = 0; i < depth; i++) {
559 std::string name = (base +
"_lock_" + std::to_string(i)).str();
560 createLock(pool, lockPlacement(pool, pool), name, i < filled ? 1 : 0);
561 names.push_back(FlatSymbolRefAttr::get(builder.getContext(), name));
563 pool.setLocksAttr(builder.getArrayAttr(names));
567 for (
auto [index, segment] : llvm::enumerate(pool.getSegmentOps())) {
568 if (segment.getProduceLock() && segment.getConsumeLock()) {
571 std::string produce =
572 (base +
"_prod_lock_" + std::to_string(index)).str();
573 std::string consume =
574 (base +
"_cons_lock_" + std::to_string(index)).str();
575 Value tile = lockPlacement(pool, segment);
576 createLock(pool, tile, produce, (depth - filled) * repeat);
577 createLock(pool, tile, consume, filled * repeat);
578 segment.setProduceLockAttr(
579 FlatSymbolRefAttr::get(builder.getContext(), produce));
580 segment.setConsumeLockAttr(
581 FlatSymbolRefAttr::get(builder.getContext(), consume));
585 bool canAccessLocks(Operation *endpoint, Value tile) {
586 Value user = endpoint->getOperand(0);
588 if (isa<ObjectFifoDmaEndpointOp>(endpoint) &&
589 !cast<TileLike>(user.getDefiningOp()).isMemTile())
590 return canAccess(user, tile,
true);
591 return canAccess(user, tile);
594 template <
typename Endpo
intOp>
595 void collectLockUsers(EndpointOp endpoint) {
596 auto pool = endpoint.getPoolOp();
597 if (device.getTargetModel().getTargetArch() == AIEArch::AIE1) {
598 lockUsers[pool].push_back(endpoint);
601 for (
auto segment : endpoint.getSelectedSegments())
602 lockUsers[segment].push_back(endpoint);
607 template <
typename Endpo
intOp>
608 SmallVector<Value> endpointLockTiles(EndpointOp endpoint) {
609 SmallVector<Value> tiles;
610 auto pool = endpoint.getPoolOp();
611 if (pool.getDepth() == 0)
613 if (device.getTargetModel().getTargetArch() == AIEArch::AIE1) {
614 if (needsLocks(pool) && !pool.getLocks())
615 tiles.push_back(lockPlacement(pool, pool));
617 for (
auto lock : pool.getLockOps())
618 tiles.push_back(lock.getTile());
621 for (
auto segment : endpoint.getSelectedSegments()) {
622 if (needsLocks(pool) &&
623 !(segment.getProduceLock() && segment.getConsumeLock())) {
624 tiles.push_back(lockPlacement(pool, segment));
628 {segment.getProduceLockAttr(), segment.getConsumeLockAttr()})
631 tiles.push_back(lock.getTile());
639 bool reachesAdjacentTile(RouteEndpoint endpoint) {
640 if (
auto route = dyn_cast<RouteEndpointOp>(endpoint.getOperation()))
641 return programReachesAdjacentTile(route);
642 auto dma = dyn_cast<ObjectFifoDmaEndpointOp>(endpoint.getOperation());
643 if (!dma || !dma.getTileLike().isMemTile()) {
646 ObjectFifoPoolOp pool = dma.getPoolOp();
647 auto remote = [&](Value tile) {
648 return tile && !sameTile(tile, endpoint.getTile());
650 return pool && (llvm::any_of(bufferPlacements[pool], remote) ||
651 llvm::any_of(endpointLockTiles(dma), remote));
654 TileLike tileOf(RouteEndpoint endpoint) {
655 return dyn_cast<TileLike>(endpoint.getTile().getDefiningOp());
660 static bool isProgramOwned(RouteEndpoint endpoint) {
661 auto route = dyn_cast<RouteEndpointOp>(endpoint.getOperation());
662 return route && route.getBundle() == WireBundle::DMA &&
663 !route.getTileLike().isShimTile();
667 static SmallVector<Block *> chainOf(DMAStartOp start) {
668 SmallVector<Block *> chain;
669 SmallPtrSet<Block *, 8> seen;
670 SmallVector<Block *> work{start.getDest()};
671 while (!work.empty()) {
672 Block *block = work.pop_back_val();
673 if (!seen.insert(block).second || block->getOps<DMABDOp>().empty())
675 chain.push_back(block);
676 if (
auto next = dyn_cast<NextBDOp>(block->getTerminator()))
677 work.push_back(next.getDest());
682 static int64_t countBDs(DMAStartOp start) {
684 for (Block *block : chainOf(start))
685 bds += llvm::range_size(block->getOps<DMABDOp>());
689 static int64_t countBDs(Region &taskBody) {
691 taskBody.walk([&](DMABDOp) { bds++; });
696 int64_t endpointBDs(Operation *endpoint) {
698 for (DMAStartOp start : endpointStarts.lookup(endpoint))
699 bds += countBDs(start);
704 int64_t endpointTaskBDs(Operation *endpoint) {
706 for (
auto task : endpointTasks.lookup(endpoint))
707 largest = std::max(largest, countBDs(task.getBody()));
713 bool programReachesAdjacentTile(RouteEndpointOp endpoint) {
714 if (!endpoint.getTileLike().isMemTile())
716 auto remote = [&](Operation *op) {
718 if (
auto bd = dyn_cast<DMABDOp>(op)) {
720 dyn_cast_or_null<BufferOp>(bd.getBuffer().getDefiningOp()))
721 tile = buffer.getTile();
722 }
else if (
auto use = dyn_cast<UseLockOp>(op)) {
723 if (
auto lock = dyn_cast_or_null<LockOp>(use.getLock().getDefiningOp()))
724 tile = lock.getTile();
726 return tile && !sameTile(tile, endpoint.getTile());
728 for (DMAStartOp start : endpointStarts.lookup(endpoint))
729 for (Block *block : chainOf(start))
730 if (llvm::any_of(block->getOperations(),
731 [&](Operation &op) { return remote(&op); }))
733 for (
auto task : endpointTasks.lookup(endpoint)) {
734 bool reaches =
false;
735 task.getBody().walk([&](Operation *op) { reaches |= remote(op); });
744 void collectChannelPrograms() {
745 endpointStarts.clear();
746 endpointTasks.clear();
749 auto key = [](Value tile,
750 int channel) -> std::optional<std::tuple<int, int, int>> {
751 auto placed = cast<TileLike>(tile.getDefiningOp());
752 auto col = placed.tryGetCol(), row = placed.tryGetRow();
755 return std::make_tuple(*col, *row, channel);
757 for (
auto program : device.getOps<DmaBody>())
758 for (Block &block : program.getDmaBody())
759 for (
auto start : block.getOps<DMAStartOp>()) {
760 if (
auto endpoint = start.getEndpointOp()) {
761 endpointStarts[endpoint].push_back(start);
763 key(program.getTile(), start.getChannelIndex())) {
764 fixedBDs[*at] += countBDs(start);
767 device.walk([&](Operation *op) {
768 if (
auto task = dyn_cast<AIEX::DMAConfigureTaskForOp>(op)) {
769 if (
auto endpoint = dyn_cast_or_null<RouteEndpointOp>(
770 SymbolTable::lookupNearestSymbolFrom(
771 device, task.getAlloc().getRootReference())))
772 endpointTasks[endpoint].push_back(task);
773 }
else if (
auto task = dyn_cast<AIEX::DMAConfigureTaskOp>(op)) {
774 if (
auto at = key(task.getTile(), task.getChannel())) {
775 int64_t &largest = taskBDs[*at];
776 largest = std::max(largest, countBDs(task.getBody()));
786 void rankStructurally() {
787 StructuralGraph graph;
789 DenseMap<std::pair<int, int>, Operation *> placed;
790 DenseMap<Operation *, Operation *> tileNode;
791 for (
auto tile : device.getOps<TileLike>()) {
792 auto col = tile.tryGetCol(), row = tile.tryGetRow();
794 graph.
add(tileNode[tile] = tile);
797 auto [it, first] = placed.try_emplace({*col, *row}, tile);
798 tileNode[tile] = it->second;
800 graph.
nodes.push_back(tile);
801 llvm::raw_string_ostream(graph.
features[tile])
802 <<
"tile " << *col <<
' ' << *row <<
' '
803 << stringifyAIETileType(tile.getTileType());
806 auto link = [&](Operation *from, Operation *to, StringRef label) {
807 if (Operation *tile = tileNode.lookup(to))
809 graph.
link(from, to, label);
811 for (
auto pool : device.getOps<ObjectFifoPoolOp>()) {
812 llvm::raw_string_ostream os(graph.
add(pool));
813 for (
auto segment : pool.getSegmentOps())
814 os <<
" [" << segment.getOffset() <<
", " << segment.getSize() <<
"]";
815 link(pool, pool.getTile().getDefiningOp(),
"tile");
817 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>()) {
818 llvm::raw_string_ostream os(graph.
add(endpoint));
819 for (
auto segment : endpoint.getSelectedSegments())
820 os <<
" @" << segment.getOffset();
821 link(endpoint, endpoint.getTile().getDefiningOp(),
"tile");
822 link(endpoint, endpoint.getPoolOp(),
"pool");
824 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
825 llvm::raw_string_ostream os(graph.
add(endpoint));
827 dyn_cast<ObjectFifoDmaEndpointOp>(endpoint.getOperation())) {
828 for (
auto segment : dma.getSelectedSegments())
829 os <<
" @" << segment.getOffset();
830 link(endpoint, dma.getPoolOp(),
"pool");
832 link(endpoint, endpoint.getTile().getDefiningOp(),
"tile");
834 for (
auto route : device.getOps<RouteOp>()) {
836 for (StringRef source : route.getSourceNames())
837 link(route, lookupEndpoint(source),
"source");
838 for (StringRef dest : route.getDestinationNames())
839 link(route, lookupEndpoint(dest),
"dest");
841 DenseMap<Operation *, unsigned> rank = graph.
rank();
842 for (
auto [tile, node] : tileNode)
843 rank[tile] = rank[node];
844 structuralRank = std::move(rank);
850 int64_t bdRoom(TileLike tile,
int col,
int row,
int channel,
851 const DenseMap<std::tuple<int, int, int>, int64_t> &demand) {
852 const AIETargetModel &target = device.getTargetModel();
853 uint32_t numBDs = target.
getNumBDs(col, row);
854 int numChannels = std::max(tile.getNumSourceConnections(WireBundle::DMA),
855 tile.getNumDestConnections(WireBundle::DMA));
856 auto sharesBDs = [&](
int other) {
857 for (uint32_t bd = 0; bd < numBDs; ++bd)
864 for (
int other = 0; other < numChannels; ++other)
865 if (sharesBDs(other))
866 if (
auto it = demand.find({col, row, other}); it != demand.end())
875 int chooseChannel(DMAChannelAnalysis &channels, RouteEndpoint endpoint,
876 const DenseMap<std::tuple<int, int, int>, int64_t> &demand,
878 TileLike tile = tileOf(endpoint);
879 DMAChannelDir dir = endpoint.getRouteDirection();
880 std::optional<int> col = tile.tryGetCol(), row = tile.tryGetRow();
883 endpoint.getOperation());
885 int64_t bestRoom = 0;
887 for (
int channel = 0; channel < limit; ++channel) {
890 int64_t room = bdRoom(tile, *col, *row, channel, demand);
891 if (best < 0 || room > bestRoom) {
899 endpoint.getOperation());
902 void noteChannelOwner(InFlightDiagnostic &diag, Operation *owner,
903 std::optional<int> channel = std::nullopt) {
904 auto ¬e = diag.attachNote(owner->getLoc());
905 if (
auto endpoint = dyn_cast<RouteEndpoint>(owner)) {
906 note <<
"DMA endpoint @" << cast<SymbolOpInterface>(owner).getName();
907 if (
auto fifo = owner->getAttrOfType<StringAttr>(
"fifoName"))
908 note <<
" for ObjectFifo @" << fifo.getValue();
909 if (reachesAdjacentTile(endpoint))
910 note <<
" requires adjacent MemTile access";
912 note <<
"; occupies channel " << *channel;
913 }
else if (channel) {
914 note <<
"pre-existing " << owner->getName() <<
" reserves DMA channel "
921 LogicalResult checkStreamPorts() {
922 DMAChannelAnalysis channels(device);
923 DenseSet<Operation *> packetEndpoints;
924 for (
auto flow : device.getOps<RouteOp>()) {
925 if (!flow.getPacket())
927 for (StringRef source : flow.getSourceNames())
928 packetEndpoints.insert(lookupEndpoint(source).getOperation());
929 for (StringRef dest : flow.getDestinationNames())
930 packetEndpoints.insert(lookupEndpoint(dest).getOperation());
933 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
934 if (endpoint.getRouteBundle() != WireBundle::Core)
936 std::optional<int> channel = endpoint.getRouteChannel();
938 return endpoint->emitOpError(
"a stream port names its own channel");
940 tileOf(endpoint), {endpoint.getRouteDirection(), *channel},
941 endpoint.getOperation(),
942 packetEndpoints.contains(endpoint.getOperation())));
944 return failure(clash);
947 LogicalResult assignChannels(DMAChannelAnalysis &channels,
948 bool diagnose =
true) {
949 channelAssignments.clear();
950 channelFailure =
nullptr;
951 SmallVector<RouteEndpoint> pending;
954 DenseMap<std::tuple<int, int, int>, int64_t> demand = fixedBDs;
955 auto noteDemand = [&](RouteEndpoint endpoint,
int channel) {
956 TileLike tile = tileOf(endpoint);
957 auto col = tile.tryGetCol(), row = tile.tryGetRow();
961 if (
auto dma = dyn_cast<ObjectFifoDmaEndpointOp>(endpoint.getOperation()))
962 bds = dma.getNumBDs();
963 else if (isProgramOwned(endpoint))
964 bds = endpointBDs(endpoint.getOperation());
965 demand[{*col, *row, channel}] += bds;
967 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
968 DMAChannelDir dir = endpoint.getRouteDirection();
969 std::optional<int> channel = endpoint.getRouteChannel();
972 if (endpoint.getRouteBundle() == WireBundle::Core ||
973 channelSharers.count(endpoint.getOperation()))
977 if (reachesAdjacentTile(endpoint) &&
980 channelFailure = endpoint;
983 return endpoint->emitOpError(
"pinned ")
984 << stringifyDMAChannelDir(dir) <<
" DMA channel " << *channel
985 <<
" cannot access adjacent MemTile buffers or locks";
988 endpoint.getOperation()) < 0) {
991 auto diag = endpoint->emitOpError(
"pinned ");
992 diag << stringifyDMAChannelDir(dir) <<
" DMA channel " << *channel
993 <<
" is out of range or already in use on this tile";
994 if (Operation *owner =
996 noteChannelOwner(diag, owner, channel);
999 noteDemand(endpoint, *channel);
1002 pending.push_back(endpoint);
1010 DenseMap<Operation *, int64_t> programBDs;
1011 for (
auto endpoint : pending)
1012 if (isProgramOwned(endpoint))
1013 programBDs[endpoint.getOperation()] =
1014 endpointBDs(endpoint.getOperation());
1015 llvm::stable_sort(pending, [&](RouteEndpoint a, RouteEndpoint b) {
1016 bool aAdjacent = reachesAdjacentTile(a);
1017 bool bAdjacent = reachesAdjacentTile(b);
1018 if (aAdjacent != bAdjacent)
1020 bool aProgram = isProgramOwned(a), bProgram = isProgramOwned(b);
1021 if (aProgram != bProgram)
1023 int64_t aBDs = programBDs.lookup(a.getOperation());
1024 int64_t bBDs = programBDs.lookup(b.getOperation());
1027 return structuralRank.lookup(a.getOperation()) <
1028 structuralRank.lookup(b.getOperation());
1031 for (
auto endpoint : pending) {
1032 DMAChannelDir dir = endpoint.getRouteDirection();
1033 TileLike tile = tileOf(endpoint);
1034 bool adjacent = reachesAdjacentTile(endpoint);
1038 auto dma = dyn_cast<ObjectFifoDmaEndpointOp>(endpoint.getOperation());
1039 std::optional<int> col = tile.tryGetCol(), row = tile.tryGetRow();
1040 if (dma && col && row) {
1042 for (
int free = 0; free < limit; ++free) {
1045 roomy = bdRoom(tile, *col, *row, free, demand) >= dma.getNumBDs();
1049 int channel = isProgramOwned(endpoint) || !roomy
1050 ? chooseChannel(channels, endpoint, demand, adjacent)
1052 endpoint.getOperation());
1054 channelFailure = endpoint;
1060 tile.emitOpError(dir == DMAChannelDir::MM2S
1061 ?
"number of output DMA channel exceeded!"
1062 :
"number of input DMA channel exceeded!");
1063 diag <<
" requires at least " << capacity + 1 <<
" "
1064 << stringifyDMAChannelDir(dir) <<
" channels, but capacity is "
1067 diag <<
" for adjacent MemTile access";
1069 for (
int i = 0; i < capacity; ++i)
1071 noteChannelOwner(diag, owner, i);
1072 noteChannelOwner(diag, endpoint.getOperation());
1075 channelAssignments[endpoint.getOperation()] = channel;
1076 noteDemand(endpoint, channel);
1078 for (
auto [sharer, owner] : channelSharers)
1079 channelAssignments[sharer] = channelAssignments.lookup(owner);
1082 for (
auto &[at, bds] : taskBDs)
1084 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
1085 TileLike tile = tileOf(endpoint);
1086 std::optional<int> col = tile.tryGetCol(), row = tile.tryGetRow();
1087 std::optional<int> channel = endpoint.getRouteChannel();
1088 if (
auto it = channelAssignments.find(endpoint.getOperation());
1089 it != channelAssignments.end())
1090 channel = it->second;
1091 if (isProgramOwned(endpoint) && channel && col && row)
1092 demand[{*col, *row, *channel}] +=
1093 endpointTaskBDs(endpoint.getOperation());
1095 channelDemand = std::move(demand);
1103 bool planShimSharing(TileLike exhausted, DMAChannelDir dir) {
1104 ShimTransferSpans spans(
1106 [&](StringAttr symbol) -> std::optional<ShimTransferSpans::End> {
1107 auto endpoint = SymbolTable::lookupNearestSymbolFrom<RouteEndpointOp>(
1109 if (!endpoint || !endpoint.getFifoName())
1110 return std::nullopt;
1111 return ShimTransferSpans::End{endpoint.getFifoNameAttr(),
1112 endpoint.getRouteDirection() ==
1113 DMAChannelDir::MM2S};
1115 SmallVector<Operation *> candidates;
1116 SmallVector<StringAttr> fifos;
1117 for (
auto endpoint : device.getOps<RouteEndpointOp>()) {
1118 if (endpoint.getBundle() != WireBundle::DMA ||
1119 endpoint.getChannelIndex() || !endpoint.getFifoName() ||
1120 endpoint.getTile() != exhausted->getResult(0) ||
1121 endpoint.getRouteDirection() != dir)
1123 candidates.push_back(endpoint);
1124 fifos.push_back(endpoint.getFifoNameAttr());
1126 for (
auto &members : spans.groups(fifos))
1127 for (
unsigned sharer : llvm::drop_begin(members))
1129 .insert({candidates[sharer], candidates[members.front()]})
1143 bool canWalkWhole(ObjectFifoPoolOp pool,
1144 ArrayRef<ObjectFifoDmaEndpointOp> regrouped = {}) {
1145 const AIETargetModel &target = device.getTargetModel();
1147 pool ? dyn_cast<TileLike>(pool.getTile().getDefiningOp()) : TileLike();
1148 return tile && tile.isMemTile() && !pool.getContiguous() &&
1149 pool.getSegmentOps().size() == 1 && !pool.getRepeatCount() &&
1150 !pool.getBuffers() && pool.getDepth() > 1 &&
1152 llvm::all_of(device.getOps<ObjectFifoDmaEndpointOp>(),
1153 [&](ObjectFifoDmaEndpointOp end) {
1154 return end.getPoolOp() != pool ||
1155 (!end.getDispatch() &&
1157 llvm::is_contained(regrouped, end)));
1161 void walkDeepPoolsWhole() {
1162 const AIETargetModel &target = device.getTargetModel();
1163 llvm::MapVector<Value, SmallVector<ObjectFifoDmaEndpointOp>> byTile;
1164 for (
auto dma : device.getOps<ObjectFifoDmaEndpointOp>())
1165 if (dma.getTileLike() && dma.getTileLike().isMemTile())
1166 byTile[dma.getTile()].push_back(dma);
1167 for (SmallVector<ObjectFifoDmaEndpointOp> &ends :
1168 llvm::make_second_range(byTile)) {
1174 TileLike tileLike = ends.front().getTileLike();
1175 std::optional<int> col = tileLike.tryGetCol(), row = tileLike.tryGetRow();
1176 int64_t numBDs = target.
getNumBDs(AIETileType::MemTile);
1180 int64_t bds = 0, largest = 0;
1181 for (ObjectFifoDmaEndpointOp end : ends) {
1182 bds += end.getNumBDs();
1183 largest = std::max(largest, end.getNumBDs());
1185 return halved ? bds + largest <= numBDs + 1 : bds <= numBDs;
1187 SmallVector<ObjectFifoPoolOp> pools;
1188 for (ObjectFifoDmaEndpointOp end : ends) {
1189 ObjectFifoPoolOp pool = end.getPoolOp();
1190 if (!llvm::is_contained(pools, pool) && canWalkWhole(pool))
1191 pools.push_back(pool);
1193 llvm::stable_sort(pools, [&](ObjectFifoPoolOp a, ObjectFifoPoolOp b) {
1194 if (a.getDepth() != b.getDepth())
1195 return a.getDepth() > b.getDepth();
1196 return structuralRank.lookup(a) < structuralRank.lookup(b);
1198 for (ObjectFifoPoolOp pool : pools) {
1201 pool.setContiguous(
true);
1208 ObjectFifoDmaEndpointOp circuitPeer(ObjectFifoDmaEndpointOp endpoint) {
1209 ObjectFifoDmaEndpointOp peer;
1210 StringRef name = endpoint.getSymName();
1211 for (RouteOp route : device.getOps<RouteOp>()) {
1212 SmallVector<StringRef> sources = llvm::to_vector(route.getSourceNames());
1213 SmallVector<StringRef> dests =
1214 llvm::to_vector(route.getDestinationNames());
1215 bool source = llvm::is_contained(sources, name);
1216 if (!source && !llvm::is_contained(dests, name))
1218 if (peer || route.getPacket() || sources.size() != 1 || dests.size() != 1)
1220 RouteEndpoint other = lookupEndpoint(source ? dests[0] : sources[0]);
1221 peer = other ? dyn_cast<ObjectFifoDmaEndpointOp>(other.getOperation())
1222 : ObjectFifoDmaEndpointOp();
1231 ObjectFifoCoreEndpointOp coreEndOf(ObjectFifoDmaEndpointOp endpoint) {
1232 ObjectFifoCoreEndpointOp found;
1233 for (
auto core : device.getOps<ObjectFifoCoreEndpointOp>()) {
1234 if (core.getPool() != endpoint.getPool())
1236 if (found || core.drains() == endpoint.drains())
1243 static bool canTakeTurns(ObjectFifoDmaEndpointOp endpoint) {
1244 return endpoint && !endpoint.getChannelIndex() && !endpoint.getDispatch() &&
1245 !endpoint.getPacketAttr() && !endpoint.getIterCount() &&
1246 endpoint.getRepeat() == 1 && !endpoint.walksStream();
1252 static bool contiguousWalkersFit(ArrayRef<ObjectFifoDmaEndpointOp> ends,
1253 ArrayRef<unsigned> turns) {
1254 SmallVector<std::pair<int64_t, int64_t>> counts;
1255 for (
auto [i, constEnd] : llvm::enumerate(ends)) {
1256 ObjectFifoDmaEndpointOp end = constEnd;
1257 counts.push_back({llvm::count(turns, i), end.getPoolOp().getContiguous()
1259 : end.getNumDescriptors()});
1262 for (
auto [i, constEnd] : llvm::enumerate(ends)) {
1263 ObjectFifoDmaEndpointOp end = constEnd;
1264 if (end.getPoolOp().getContiguous() &&
1265 (end.walksStream() ||
1266 end.getPoolOp().getDepth() % (counts[i].first * passes)))
1273 void takeTurns(MutableArrayRef<ObjectFifoDmaEndpointOp> ends,
1274 ArrayRef<unsigned> order) {
1275 SmallVector<Attribute> names;
1276 for (
unsigned i : order)
1277 names.push_back(FlatSymbolRefAttr::get(ends[i].getSymNameAttr()));
1278 ArrayAttr turns = builder.getArrayAttr(names);
1279 Operation *owner = ends[order.front()];
1280 channelSharers.erase(owner);
1281 for (ObjectFifoDmaEndpointOp end : ends) {
1282 end.setTurnsAttr(turns);
1284 channelSharers[end] = owner;
1292 bool planTurnSharing(TileLike exhausted, DMAChannelDir dir) {
1297 DenseMap<Operation *, ObjectFifoCoreEndpointOp> coreEndCache;
1298 DenseMap<Operation *, ObjectFifoDmaEndpointOp> circuitPeerCache;
1299 for (
auto dma : device.getOps<ObjectFifoDmaEndpointOp>()) {
1300 coreEndCache[dma] = coreEndOf(dma);
1301 circuitPeerCache[dma] = circuitPeer(dma);
1303 auto cachedCoreEnd = [&](ObjectFifoDmaEndpointOp dma) {
1304 return coreEndCache.lookup(dma);
1306 auto cachedCircuitPeer = [&](ObjectFifoDmaEndpointOp dma) {
1307 return circuitPeerCache.lookup(dma);
1309 SmallVector<ObjectFifoDmaEndpointOp> candidates;
1310 for (
auto dma : device.getOps<ObjectFifoDmaEndpointOp>())
1311 if (sameTile(dma.getTile(), exhausted->getResult(0)) &&
1312 dma.getRouteDirection() == dir && cachedCoreEnd(dma) &&
1313 canTakeTurns(dma.getTurns() ?
turnEndpoints(dma).front() : dma) &&
1314 canTakeTurns(cachedCircuitPeer(dma)))
1315 candidates.push_back(dma);
1316 llvm::stable_sort(candidates, [&](Operation *a, Operation *b) {
1317 return structuralRank.lookup(a) < structuralRank.lookup(b);
1320 llvm::dbgs() <<
"turns on " << exhausted->getLoc() <<
": "
1321 << candidates.size() <<
" candidate ends\n";
1322 for (
auto dma : device.getOps<ObjectFifoDmaEndpointOp>())
1323 if (sameTile(dma.getTile(), exhausted->getResult(0)) &&
1324 dma.getRouteDirection() == dir)
1325 llvm::dbgs() <<
" " << dma.getSymName() <<
": core end "
1326 << bool(cachedCoreEnd(dma)) <<
", takes turns "
1327 << canTakeTurns(dma) <<
", peer takes turns "
1328 << canTakeTurns(cachedCircuitPeer(dma)) <<
"\n";
1330 auto groupOf = [](ObjectFifoDmaEndpointOp end) {
1331 SmallVector<ObjectFifoDmaEndpointOp> group =
turnEndpoints(end);
1332 return group.empty() ? SmallVector<ObjectFifoDmaEndpointOp>{end} : group;
1334 for (
auto [i, a] : llvm::enumerate(candidates)) {
1335 for (ObjectFifoDmaEndpointOp b : llvm::drop_begin(candidates, i + 1)) {
1336 SmallVector<ObjectFifoDmaEndpointOp> group = groupOf(a);
1337 if (llvm::is_contained(group, b))
1339 llvm::append_range(group, groupOf(b));
1340 SmallVector<ObjectFifoCoreEndpointOp> cores;
1341 SmallVector<ObjectFifoDmaEndpointOp> peers;
1342 SmallVector<int64_t> depths;
1343 for (ObjectFifoDmaEndpointOp end : group) {
1344 cores.push_back(cachedCoreEnd(end));
1345 peers.push_back(cachedCircuitPeer(end));
1346 depths.push_back(end.getPoolOp().getDepth());
1348 if (llvm::any_of(peers, [&](ObjectFifoDmaEndpointOp peer) {
1349 return !sameTile(peer.getTile(), peers.front().getTile()) ||
1351 return !llvm::is_contained(peers, other);
1354 LLVM_DEBUG(llvm::dbgs()
1355 <<
" " << a.getSymName() <<
" + " << b.getSymName()
1356 <<
": senders on different tiles\n");
1361 if (succeeded(turns.
read(cores, reason)) && !turns.
keepsUp(depths))
1362 reason =
"would stall";
1365 bool overflows =
false;
1366 if (reason.empty()) {
1367 SmallVector<std::pair<int64_t, int64_t>> counts;
1368 for (
auto [i, constPeer] : llvm::enumerate(peers)) {
1369 ObjectFifoDmaEndpointOp peer = constPeer;
1370 counts.push_back({llvm::count(turns.
turns(), i),
1371 peer.getPoolOp().getContiguous()
1373 : peer.getNumDescriptors()});
1375 TileLike tile = peers.front().getTileLike();
1376 std::optional<int> col = tile.tryGetCol(), row = tile.tryGetRow();
1380 device.getTargetModel().getNumBDsForChannel(*col, *row, 0);
1386 SmallVector<ObjectFifoPoolOp> walked;
1387 if (reason.empty() &&
1388 (overflows || !contiguousWalkersFit(peers, turns.
turns())))
1389 for (ObjectFifoDmaEndpointOp peer : peers) {
1390 ObjectFifoPoolOp pool = peer.getPoolOp();
1391 SmallVector<ObjectFifoDmaEndpointOp> others;
1392 for (
auto end : device.getOps<ObjectFifoDmaEndpointOp>())
1393 if (end.getPoolOp() == pool && end.getTurns() &&
1394 !llvm::is_contained(peers, end))
1395 others.push_back(end);
1396 SmallVector<ObjectFifoDmaEndpointOp> ignored(peers);
1397 llvm::append_range(ignored, others);
1398 if (!canWalkWhole(pool, ignored)) {
1399 LLVM_DEBUG(llvm::dbgs() <<
" " << pool.getSymName()
1400 <<
" cannot be walked whole\n");
1403 pool.setContiguous(
true);
1405 llvm::all_of(others, [&](ObjectFifoDmaEndpointOp end) {
1406 SmallVector<ObjectFifoDmaEndpointOp> ends =
1408 SmallVector<unsigned> order;
1410 end.getTurnsAttr().getAsRange<FlatSymbolRefAttr>())
1413 [&](ObjectFifoDmaEndpointOp e) {
1414 return e.getSymNameAttr() ==
1418 return contiguousWalkersFit(ends, order);
1422 <<
" " << pool.getSymName() <<
" walked whole"
1423 << (linedUp ?
"" :
", but its other turns would not line up")
1426 walked.push_back(pool);
1428 pool.setContiguous(
false);
1430 if (reason.empty() && (!contiguousWalkersFit(group, turns.
turns()) ||
1431 !contiguousWalkersFit(peers, turns.
turns()))) {
1432 reason =
"a contiguous pool's objects do not divide among its turns";
1433 for (ObjectFifoPoolOp pool : walked)
1434 pool.setContiguous(
false);
1436 if (!reason.empty()) {
1437 LLVM_DEBUG(llvm::dbgs() <<
" " << a.getSymName() <<
" + "
1438 << b.getSymName() <<
": " << reason <<
"\n");
1441 takeTurns(group, turns.
turns());
1442 takeTurns(peers, turns.
turns());
1453 LogicalResult headShimSharingRoutes() {
1454 DenseMap<Operation *, Operation *> channelOf;
1455 for (
auto [sharer, owner] : channelSharers) {
1456 if (!tileOf(cast<RouteEndpoint>(owner)).isShimTile())
1458 channelOf[sharer] = owner;
1459 channelOf[owner] = owner;
1461 auto channelsOf = [&](RouteOp route) {
1462 SmallVector<Operation *> channels;
1463 auto note = [&](StringRef name) {
1464 if (
auto endpoint = lookupEndpoint(name);
1465 endpoint && channelOf.count(endpoint.getOperation()))
1466 channels.push_back(channelOf.lookup(endpoint.getOperation()));
1468 llvm::for_each(route.getSourceNames(), note);
1469 llvm::for_each(route.getDestinationNames(), note);
1472 DenseMap<Operation *, DenseSet<int>> idsOn;
1473 for (
auto route : device.getOps<RouteOp>())
1474 if (PacketInfoAttr packet = route.getPacketAttr();
1475 packet && packet.isAssigned())
1476 for (Operation *channel : channelsOf(route))
1477 idsOn[channel].insert(packet.assignedId());
1478 PacketIdSpace ids(device);
1479 SmallVector<int> reused;
1480 for (
auto route : device.getOps<RouteOp>()) {
1481 SmallVector<Operation *> channels = channelsOf(route);
1482 if (route.getPacket() || channels.empty())
1484 auto freeOnAll = [&](
int id) {
1485 return llvm::none_of(channels, [&](Operation *channel) {
1486 return idsOn[channel].contains(
id);
1489 std::optional<int> id;
1490 if (
auto *it = llvm::find_if(reused, freeOnAll); it != reused.end())
1493 reused.push_back(*
id);
1495 return route.emitOpError(
"shares a shim channel, but no packet id is "
1496 "left to tell its transfers apart");
1497 for (Operation *channel : channels)
1498 idsOn[channel].insert(*
id);
1499 route.setPacketAttr(PacketInfoAttr::get(builder.getContext(), 0,
1500 static_cast<uint16_t
>(*
id)));
1508 void warnOvercommittedBDs() {
1509 const AIETargetModel &target = device.getTargetModel();
1510 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
1511 auto it = channelAssignments.find(endpoint.getOperation());
1512 TileLike tile = tileOf(endpoint);
1513 std::optional<int> col = tile.tryGetCol(), row = tile.tryGetRow();
1514 if (it == channelAssignments.end() || !isProgramOwned(endpoint) || !col ||
1517 int channel = it->second;
1518 int64_t room = bdRoom(tile, *col, *row, channel, channelDemand);
1522 endpoint->emitWarning(
"channel ")
1523 << channel <<
" can use " << numBDs
1524 <<
" BDs, but it and the channels sharing them need " << numBDs - room
1525 <<
" at once; BD id assignment fails unless tasks free their BDs "
1526 "before others start";
1537 LogicalResult planAllocation(ArrayRef<ObjectFifoPoolOp> pools,
1538 std::set<std::vector<unsigned>> &tried) {
1539 std::vector<unsigned> key;
1540 for (
auto [index, pool] : llvm::enumerate(pools)) {
1541 if (
auto tile = localPools.lookup(pool)) {
1542 auto placed = cast<TileLike>(tile.getDefiningOp());
1543 auto col = placed.tryGetCol(), row = placed.tryGetRow();
1544 assert(col && row &&
"locality repair requires resolved coordinates");
1545 key.push_back(index);
1546 key.push_back(*col);
1547 key.push_back(*row);
1550 if (tried.size() >= 64 || !tried.insert(key).second ||
1551 failed(planBuffers(pools)) || failed(planLocks(pools)))
1553 DMAChannelAnalysis channels(device);
1554 if (succeeded(assignChannels(channels,
false)))
1556 if (!channelFailure || !tileOf(channelFailure).isMemTile() ||
1557 !tileOf(channelFailure).tryGetCol() ||
1558 !tileOf(channelFailure).tryGetRow())
1561 Value localTile = channelFailure.getTile();
1562 SmallVector<ObjectFifoPoolOp> candidates;
1563 for (
auto dma : device.getOps<ObjectFifoDmaEndpointOp>()) {
1564 auto endpoint = cast<RouteEndpoint>(dma.getOperation());
1565 auto pool = dma.getPoolOp();
1566 if (!pool || localPools.contains(pool) ||
1567 !sameTile(endpoint.getTile(), channelFailure.getTile()) ||
1568 endpoint.getRouteDirection() != channelFailure.getRouteDirection() ||
1569 (channelFailure.getRouteChannel() && endpoint != channelFailure) ||
1570 (endpoint.getRouteChannel() && endpoint != channelFailure) ||
1571 !reachesAdjacentTile(endpoint) ||
1572 llvm::is_contained(candidates, pool))
1574 candidates.push_back(pool);
1576 llvm::stable_sort(candidates, [&](ObjectFifoPoolOp a, ObjectFifoPoolOp b) {
1578 a.getBuffers() ? 0 : a.getObjectSizeInBytes() * a.getDepth();
1580 b.getBuffers() ? 0 : b.getObjectSizeInBytes() * b.getDepth();
1581 if (aBytes != bBytes)
1582 return aBytes < bBytes;
1583 return structuralRank.lookup(a) < structuralRank.lookup(b);
1585 for (
auto pool : candidates) {
1586 localPools[pool] = localTile;
1587 if (succeeded(planAllocation(pools, tried)))
1589 localPools.erase(pool);
1598 LogicalResult lowerPacketFlow(RouteOp flow, ArrayRef<RouteEndpoint> sources,
1599 PacketInfoAttr header) {
1600 int packetID = header.assignedId();
1601 for (
auto [name, endpoint] : llvm::zip_equal(
1602 flow.getSources().getAsRange<FlatSymbolRefAttr>(), sources)) {
1603 RouteEndpoint source = endpoint;
1605 if (
auto dma = dyn_cast<ObjectFifoDmaEndpointOp>(*source);
1606 dma && dma.getDispatch()) {
1607 dispatchHeaders[dma][*flow.getDestinationNames().begin()] = header;
1610 if (sourcedFlows.at(name.getAttr()).size() == 1)
1611 source.setRoutePacket(header);
1614 builder.setInsertionPoint(flow);
1615 auto packetFlow = PacketFlowOp::create(
1616 builder, flow.getLoc(),
1617 builder.getIntegerAttr(builder.getI8Type(), packetID),
1618 flow.getKeepPktHeader() ? builder.getBoolAttr(
true) :
nullptr,
nullptr);
1619 OpBuilder::InsertionGuard g(builder);
1620 Block &ports = packetFlow.getRegion().emplaceBlock();
1621 builder.setInsertionPointToStart(&ports);
1622 EndOp::create(builder, flow.getLoc());
1624 builder.setInsertionPointToStart(&ports);
1625 for (RouteEndpoint source : sources) {
1626 PacketSourceOp::create(builder, flow.getLoc(), source.getTile(),
1627 source.getRouteBundle(), channelOf(source));
1629 for (StringRef destName : flow.getDestinationNames()) {
1630 auto dest = lookupEndpoint(destName);
1631 PacketDestOp::create(builder, flow.getLoc(), dest.getTile(),
1632 dest.getRouteBundle(), channelOf(dest));
1638 int channelOf(RouteEndpoint endpoint) {
1639 std::optional<int> channel = endpoint.getRouteChannel();
1640 assert(channel &&
"channels are assigned before flows are lowered");
1644 RouteEndpoint lookupEndpoint(StringRef name) {
1645 return dyn_cast_or_null<RouteEndpoint>(SymbolTable::lookupNearestSymbolFrom(
1646 device, StringAttr::get(device.getContext(), name)));
1649 LogicalResult lowerFlows() {
1650 DenseSet<std::tuple<Value, int, Value, int>> circuits;
1651 for (RouteOp flow : device.getOps<RouteOp>())
1652 for (
auto name : flow.getSources().getAsRange<FlatSymbolRefAttr>())
1653 sourcedFlows[name.getAttr()].push_back(flow);
1654 for (
auto flow : device.getOps<RouteOp>()) {
1655 SmallVector<RouteEndpoint> sources;
1656 for (StringRef name : flow.getSourceNames()) {
1657 sources.push_back(lookupEndpoint(name));
1659 loweredFlows.push_back(flow);
1663 if (std::optional<PacketInfoAttr> packet = flow.getPacket()) {
1664 if (!packet->isAssigned()) {
1665 return flow.emitOpError(
"has a packet header with no pkt_id; run "
1666 "--aie-assign-packet-ids before this pass");
1668 if (failed(lowerPacketFlow(flow, sources, *packet))) {
1675 RouteEndpoint source = sources.front();
1677 int sourceChannel = channelOf(source);
1678 builder.setInsertionPoint(flow);
1679 for (StringRef destName : flow.getDestinationNames()) {
1680 auto dest = lookupEndpoint(destName);
1683 .insert({source.getTile(), sourceChannel, dest.getTile(),
1687 FlowOp::create(builder, flow.getLoc(), source.getTile(),
1688 source.getRouteBundle(), sourceChannel, dest.getTile(),
1689 dest.getRouteBundle(), channelOf(dest));
1692 for (
auto &[endpoint, byDest] : dispatchHeaders) {
1693 SmallVector<Attribute> headers;
1694 for (StringRef dest :
1695 endpoint.getDispatchAttr().getAsValueRange<FlatSymbolRefAttr>())
1696 headers.push_back(byDest.lookup(dest));
1697 endpoint.setDispatchPacketsAttr(builder.getArrayAttr(headers));
1702 void collectRearmUsers() {
1703 device.walk([&](Operation *op) {
1704 if (op->getName().getStringRef() !=
"aiex.dma_channel_reset_for") {
1707 auto sym = op->getAttrOfType<FlatSymbolRefAttr>(
"objfifo");
1712 StringRef name = sym.getValue();
1713 if (
auto endpoint = lookupEndpoint(sym.getValue())) {
1714 if (
auto fifoName = endpoint.getFifoName()) {
1718 rearmUsers[name].push_back(op);
1725 LogicalResult bindRearmTargets() {
1726 if (rearmUsers.empty()) {
1730 builder.setInsertionPoint(device.getBody()->getTerminator());
1731 for (
auto &[fifoName, users] : rearmUsers) {
1732 SmallVector<Value> channelTiles, lockValues;
1733 SmallVector<int32_t> channelDirs, channelIndices, lockInits;
1735 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
1736 std::optional<int> channel = endpoint.getRouteChannel();
1737 if (endpoint.getFifoName() != fifoName ||
1738 tileOf(endpoint).isShimTile() || !channel) {
1741 channelTiles.push_back(endpoint.getTile());
1742 channelDirs.push_back(
1743 static_cast<int32_t
>(endpoint.getRouteDirection()));
1744 channelIndices.push_back(*channel);
1746 for (
auto pool : device.getOps<ObjectFifoPoolOp>()) {
1747 if (pool.getFifoName() != fifoName || pool.getTileLike().isShimTile()) {
1750 for (LockOp lock : pool.getLockOps()) {
1751 lockValues.push_back(lock.getResult());
1752 lockInits.push_back(lock.getInit().value_or(0));
1756 if (channelTiles.empty() && lockValues.empty()) {
1757 for (Operation *user : users) {
1758 user->emitOpError() <<
"objectFIFO '" << fifoName
1759 <<
"' has no resident core/mem DMA channels or "
1765 std::string name = (fifoName +
"_rearm").str();
1768 name = (fifoName +
"_rearm_" + std::to_string(suffix)).str();
1772 ObjectFifoRearmBindingOp::create(
1773 builder, device.getLoc(), builder.getStringAttr(name),
1774 ValueRange(channelTiles), ValueRange(lockValues),
1775 builder.getDenseI32ArrayAttr(channelDirs),
1776 builder.getDenseI32ArrayAttr(channelIndices),
1777 builder.getDenseI32ArrayAttr(lockInits),
1778 DenseI32ArrayAttr(),
1779 DenseI32ArrayAttr());
1780 auto target = FlatSymbolRefAttr::get(builder.getContext(), name);
1781 for (Operation *user : users) {
1782 user->setAttr(
"objfifo", target);
1789 LogicalResult resolveStart(DMAStartOp start, RouteEndpointOp endpoint,
1790 ArrayRef<RouteOp> flows, ArrayRef<int> ids,
1792 DMAChannelDir dir = endpoint.getRouteDirection();
1793 if (start.getChannelDir() != dir)
1794 return start.emitOpError(
"starts ")
1795 << stringifyDMAChannelDir(start.getChannelDir()) <<
" on "
1796 << start.getEndpoint() <<
", but its route makes it "
1797 << stringifyDMAChannelDir(dir);
1800 bool packet = !flows.empty() && RouteOp(flows.front()).getPacket();
1802 SmallVector<DMABDOp> bds;
1803 for (Block *block : chainOf(start))
1804 llvm::append_range(bds, block->getOps<DMABDOp>());
1805 if (failed(checkHeaders(endpoint, ids, bds)))
1808 start.setChannelAttr(builder.getI32IntegerAttr(channel));
1814 LogicalResult checkHeaders(RouteEndpointOp endpoint, ArrayRef<int> ids,
1815 ArrayRef<DMABDOp> bds) {
1816 for (DMABDOp bd : bds) {
1817 std::optional<int> id;
1818 if (
auto packet = bd.getPacket())
1819 id = packet->getPktId();
1820 else if (
auto ops = bd->getBlock()->getOps<DMABDPACKETOp>(); !ops.empty())
1821 id = (*ops.begin()).getPacketId();
1822 if (
id && llvm::is_contained(ids, *
id))
1824 return bd.emitOpError(
"sends on @")
1825 << endpoint.getSymName()
1826 <<
" without the packet header of one of its flows, ids " << ids;
1834 LogicalResult checkSharedSourceUsers(RouteEndpointOp endpoint,
1835 ArrayRef<int> ids) {
1836 auto uses = SymbolTable::getSymbolUses(endpoint, device);
1839 for (
const SymbolTable::SymbolUse &use : *uses) {
1840 Operation *user = use.getUser();
1841 if (
auto task = dyn_cast<AIEX::DMAConfigureTaskForOp>(user)) {
1842 SmallVector<DMABDOp> bds;
1843 task.getBody().walk([&](DMABDOp bd) { bds.push_back(bd); });
1844 if (failed(checkHeaders(endpoint, ids, bds)))
1848 if (
auto start = dyn_cast<AIEX::DMAStartBdChainForOp>(user)) {
1849 auto chain = device.lookupSymbol<BDChainOp>(start.getSymbol());
1851 return start.emitOpError(
"symbol does not reference valid BD chain");
1852 SmallVector<DMABDOp> bds;
1853 chain.getBody().walk([&](DMABDOp bd) { bds.push_back(bd); });
1854 if (failed(checkHeaders(endpoint, ids, bds)))
1858 auto memcpy = dyn_cast<AIEX::NpuDmaMemcpyNdOp>(user);
1861 std::optional<PacketInfoAttr> packet = memcpy.getPacket();
1862 if (packet && llvm::is_contained(ids, packet->getPktId()))
1864 return memcpy.emitOpError(
"sends on @")
1865 << endpoint.getSymName()
1866 <<
", a source several packet routes share, which has no one "
1867 "header to stamp; it needs a packet header of its own, one of "
1876 void resolveTask(AIEX::DMAConfigureTaskForOp task, RouteEndpointOp endpoint,
1878 builder.setInsertionPoint(task);
1879 auto configured = AIEX::DMAConfigureTaskOp::create(
1880 builder, task.getLoc(), builder.getIndexType(), endpoint.getTile(),
1881 DMAChannelDirAttr::get(builder.getContext(),
1882 endpoint.getRouteDirection()),
1883 builder.getI32IntegerAttr(channel),
1884 builder.getBoolAttr(task.getIssueToken()),
1885 builder.getI32IntegerAttr(task.getRepeatCount()),
1886 task.getRepeatCountVal(), endpoint.getPacketAttr());
1887 task.getResult().replaceAllUsesWith(configured.getResult());
1888 configured.getBody().takeBody(task.getBody());
1895 LogicalResult resolveEndpointUses() {
1896 for (
auto endpoint : device.getOps<RouteEndpointOp>()) {
1897 auto starts = endpointStarts.lookup(endpoint);
1898 auto tasks = endpointTasks.lookup(endpoint);
1899 auto sourced = sourcedFlows.find(endpoint.getSymNameAttr());
1900 ArrayRef<RouteOp> flows;
1901 if (sourced != sourcedFlows.end())
1902 flows = sourced->second;
1903 SmallVector<int> ids;
1904 for (RouteOp flow : flows)
1905 if (std::optional<PacketInfoAttr> packet = flow.getPacket();
1906 packet && packet->isAssigned())
1907 ids.push_back(packet->assignedId());
1908 bool shared = flows.size() > 1;
1909 if (shared && failed(checkSharedSourceUsers(endpoint, ids)))
1911 if (starts.empty() && tasks.empty())
1913 std::optional<int> channel = endpoint.getRouteChannel();
1914 assert(channel &&
"assignChannels gives every routed endpoint one");
1915 for (DMAStartOp start : starts)
1916 if (failed(resolveStart(start, endpoint, flows, ids, *channel)))
1918 if (endpoint.getTileLike().isShimTile() && endpoint.getFifoName())
1920 for (
auto task : tasks)
1921 resolveTask(task, endpoint, *channel);
1928 void emitShimAllocations() {
1929 builder.setInsertionPoint(device.getBody()->getTerminator());
1934 llvm::StringMap<unsigned> shimEndsSeen;
1935 for (
auto endpoint : device.getOps<RouteEndpoint>()) {
1936 std::optional<StringRef> fifoName = endpoint.getFifoName();
1937 std::optional<int> channel = endpoint.getRouteChannel();
1938 if (!tileOf(endpoint).isShimTile() || !fifoName || !channel) {
1941 std::string name = (*fifoName +
"_shim_alloc").str();
1942 unsigned ordinal = shimEndsSeen[*fifoName]++;
1946 create = !SymbolTable::lookupNearestSymbolFrom<ShimDMAAllocationOp>(
1947 device, builder.getStringAttr(name));
1949 for (
unsigned suffix = ordinal - 1; device.lookupSymbol(name);
1951 name = (*fifoName +
"_shim_alloc_" + std::to_string(suffix)).str();
1955 ShimDMAAllocationOp::create(
1956 builder, endpoint.getLoc(), builder.getStringAttr(name),
1958 DMAChannelDirAttr::get(builder.getContext(),
1959 endpoint.getRouteDirection()),
1960 builder.getI64IntegerAttr(*channel),
1961 builder.getBoolAttr(endpoint.getRouteBundle() == WireBundle::PLIO),
1962 endpoint->getAttrOfType<PacketInfoAttr>(
"packet"));
1965 (void)SymbolTable::replaceAllSymbolUses(
1966 endpoint, builder.getStringAttr(name), device);
1974 SmallVector<ObjectFifoPoolOp>
1975 demandOrderedPools(ArrayRef<ObjectFifoPoolOp> pools) {
1976 SmallVector<ObjectFifoPoolOp> ordered(pools);
1977 SmallVector<size_t> memTileSlots;
1978 SmallVector<Value> memTileHomes;
1979 DenseMap<Value, SmallVector<ObjectFifoPoolOp>> memTilePools;
1980 DenseMap<Operation *, std::pair<int, int>> poolChannelDemand;
1981 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>()) {
1982 ObjectFifoPoolOp pool = endpoint.getPoolOp();
1983 if (!sameTile(endpoint.getTile(), pool.getTile()))
1985 auto &demand = poolChannelDemand[pool];
1986 if (endpoint.getRouteDirection() == DMAChannelDir::S2MM)
1991 for (
auto [index, pool] : llvm::enumerate(ordered)) {
1992 if (pool.getTileLike().isMemTile()) {
1993 memTileSlots.push_back(index);
1994 Value home = pool.getTile();
1995 for (Value known : memTileHomes)
1996 if (sameTile(home, known)) {
2000 if (!memTilePools.count(home))
2001 memTileHomes.push_back(home);
2002 memTilePools[home].push_back(pool);
2005 DenseMap<Value, size_t> nextPool;
2006 auto demand = [&](ObjectFifoPoolOp pool) {
2007 auto [input, output] = poolChannelDemand.lookup(pool.getOperation());
2008 return std::max(input, output);
2010 for (Value home : memTileHomes) {
2011 auto &tilePools = memTilePools[home];
2012 llvm::stable_sort(tilePools, [&](ObjectFifoPoolOp a, ObjectFifoPoolOp b) {
2013 if (demand(a) != demand(b))
2014 return demand(a) > demand(b);
2015 return a.getObjectSizeInBytes() > b.getObjectSizeInBytes();
2018 for (
size_t slot : memTileSlots) {
2020 ObjectFifoPoolOp bestPool;
2021 for (Value home : memTileHomes) {
2022 auto &tilePools = memTilePools[home];
2023 size_t index = nextPool[home];
2024 if (index >= tilePools.size())
2026 ObjectFifoPoolOp pool = tilePools[index];
2028 pool.getObjectSizeInBytes() > bestPool.getObjectSizeInBytes() ||
2029 (pool.getObjectSizeInBytes() == bestPool.getObjectSizeInBytes() &&
2030 demand(pool) > demand(bestPool))) {
2035 ordered[slot] = bestPool;
2036 ++nextPool[bestHome];
2041 void runOnOperation()
override {
2042 device = getOperation();
2043 builder = OpBuilder(device.getContext());
2047 filledPools.clear();
2049 loweredFlows.clear();
2050 sourcedFlows.clear();
2054 lockPlacements.clear();
2057 if (failed(collectFixedMemory()) || failed(checkStreamPorts()))
2058 return signalPassFailure();
2059 collectChannelPrograms();
2061 walkDeepPoolsWhole();
2065 SmallVector<ObjectFifoPoolOp> pools(device.getOps<ObjectFifoPoolOp>());
2066 llvm::stable_sort(pools, [&](ObjectFifoPoolOp a, ObjectFifoPoolOp b) {
2067 return structuralRank.lookup(a) < structuralRank.lookup(b);
2069 SmallVector<size_t> memTileSlots;
2070 SmallVector<ObjectFifoPoolOp> memTilePools;
2071 for (
auto [index, pool] : llvm::enumerate(pools)) {
2072 if (pool.getTileLike().isMemTile()) {
2073 memTileSlots.push_back(index);
2074 memTilePools.push_back(pool);
2077 llvm::stable_sort(memTilePools, [](ObjectFifoPoolOp a, ObjectFifoPoolOp b) {
2078 return a.getObjectSizeInBytes() > b.getObjectSizeInBytes();
2080 for (
auto [slot, pool] : llvm::zip(memTileSlots, memTilePools)) {
2084 collectRearmUsers();
2085 for (
auto endpoint : device.getOps<ObjectFifoCoreEndpointOp>()) {
2086 poolUsers[endpoint.getPoolOp()].push_back(endpoint.getTile());
2087 collectLockUsers(endpoint);
2088 if (!endpoint.drains()) {
2089 filledPools.insert(endpoint.getPoolOp());
2092 for (
auto endpoint : device.getOps<ObjectFifoDmaEndpointOp>()) {
2093 poolUsers[endpoint.getPoolOp()].push_back(endpoint.getTile());
2094 collectLockUsers(endpoint);
2095 if (!endpoint.drains()) {
2096 filledPools.insert(endpoint.getPoolOp());
2099 for (
auto &[group, users] : lockUsers)
2100 llvm::stable_sort(users, [&](Operation *a, Operation *b) {
2101 return structuralRank.lookup(a) < structuralRank.lookup(b);
2104 std::set<std::vector<unsigned>> tried;
2105 LogicalResult allocated = planAllocation(pools, tried);
2106 if (failed(allocated)) {
2107 auto demandOrdered = demandOrderedPools(pools);
2108 if (!llvm::equal(pools, demandOrdered)) {
2112 allocated = planAllocation(demandOrdered, tried);
2118 while (failed(allocated) && channelFailure &&
2119 (tileOf(channelFailure).isShimTile()
2120 ? planShimSharing(tileOf(channelFailure),
2121 channelFailure.getRouteDirection())
2122 : planTurnSharing(tileOf(channelFailure),
2123 channelFailure.getRouteDirection()))) {
2126 allocated = planAllocation(pools, tried);
2128 if (succeeded(allocated) && !channelSharers.empty() &&
2129 failed(headShimSharingRoutes()))
2130 return signalPassFailure();
2131 if (failed(allocated)) {
2133 if (failed(planBuffers(pools))) {
2134 bufferFailure.emitOpError(
2135 "could not place buffers in accessible memory with available "
2137 }
else if (failed(planLocks(pools))) {
2138 if (lockAccessFailure)
2139 lockAccessFailure->emitOpError(
"cannot access pool locks");
2141 lockFailure.getDefiningOp()->emitOpError(
2142 "could not place locks within tile lock capacity");
2144 DMAChannelAnalysis channels(device);
2145 (void)assignChannels(channels);
2147 "could not find a spill-aware allocation with size-first and "
2148 "demand-first packing and bounded local-pool retries; tile "
2149 "placement and buffer packing remain greedy");
2151 return signalPassFailure();
2153 for (ObjectFifoPoolOp pool : pools) {
2154 allocateBuffers(pool);
2155 allocateLocks(pool);
2157 for (TileOp tile : spillTiles)
2158 if (tile->use_empty())
2160 for (
auto endpoint : device.getOps<RouteEndpoint>())
2161 if (
auto it = channelAssignments.find(endpoint.getOperation());
2162 it != channelAssignments.end())
2163 endpoint.setRouteChannel(it->second);
2164 warnOvercommittedBDs();
2166 if (failed(bindRearmTargets())) {
2167 return signalPassFailure();
2169 if (failed(lowerFlows()) || failed(resolveEndpointUses())) {
2170 return signalPassFailure();
2172 emitShimAllocations();
2173 for (Operation *flow : loweredFlows) {
2181std::unique_ptr<OperationPass<DeviceOp>>
2183 return std::make_unique<AIEObjectFifoAllocatePass>();
virtual bool isBdChannelAccessible(int col, int row, uint32_t bd_id, int channel) const =0
Return true iff buffer descriptor bd_id on tile (col, row) can be submitted on channel channel.
virtual uint32_t getNumBDs(AIETileType tileType) const =0
Return the number of buffer descriptors for a given tile type.
virtual uint32_t getMaxLockValue() const =0
Return the maximum value that can be stored in a lock register.
uint32_t getNumBDsForChannel(int col, int row, int channel) const
Return the number of buffer descriptors accessible on channel channel for the tile at (col,...
SharedMemory
Which of two tiles' memory modules both of them can address.
mlir::LogicalResult read(llvm::ArrayRef< ObjectFifoCoreEndpointOp > ends, std::string &reason)
Reads the order off the program of the core working through ends, all draining (the channel fills the...
bool keepsUp(llvm::ArrayRef< int64_t > depths)
Whether a channel moving objects in turns() order forever, through pools of depths,...
llvm::ArrayRef< unsigned > turns() const
Indices into ends, one per object the channel moves in a period of the core's steady state.
int getDMAChannelIndex(TileLike tile, DMAChannelDir dir, bool requiresAdjacentTileAccessChannels, mlir::Operation *owner=nullptr)
Next free channel of tile in dir, or -1 when the tile has none left.
mlir::Operation * getDMAChannelOwner(TileLike tile, DMAChannelDir dir, int channel)
Operation reserving this channel, or else the flow routing it; null if neither.
bool isChannelFree(TileLike tile, DMAChannelDir dir, int channel)
Whether first-free assignment could hand out channel: nothing reserves it and no explicit flow routes...
int reservePinnedChannel(TileLike tile, DMAChannelDir dir, int channel, mlir::Operation *owner=nullptr)
Claim channel for (tile, dir) so first-free assignment cannot take it.
static int getDMAChannelLimit(TileLike tile, DMAChannelDir dir, bool requiresAdjacentTileAccessChannels)
Exclusive upper bound on channel indices eligible for this transfer.
mlir::LogicalResult checkAIEStreamIndex(TileLike tile, DMAChannel chan, mlir::Operation *user, bool packet=false)
Claim a raw stream port for user, reporting on it when the port is taken; packet users may share it w...
std::optional< int > takeLowestFrom(int from=0)
Takes and returns the lowest free id at or above from, or nothing when the field is exhausted.
llvm::SmallVector< ObjectFifoDmaEndpointOp > turnEndpoints(ObjectFifoDmaEndpointOp end)
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEObjectFifoAllocatePass()
mlir::Operation * lookupNamedOpIn(mlir::Operation *symbolTableOp, mlir::StringAttr name)
struct TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")"; return os; } friend std::string to_string(const TileID &s) { std::ostringstream ss; ss<< s; return ss.str(); } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const TileID &s) { os<< to_string(s); return os; } inline bool operator<(const TileID &rhs) const { return std::tie(col, row)< std::tie(rhs.col, rhs.row); } bool operator==(const TileID &rhs) const { return std::tie(col, row)==std::tie(rhs.col, rhs.row); } bool operator!=(const TileID &rhs) const { return !(*this==rhs);} int col, row;} TileID
int64_t turnPasses(llvm::ArrayRef< std::pair< int64_t, int64_t > > ends)
mlir::Operation * lookupNamedOp(mlir::Operation *from, mlir::StringAttr name)
int64_t assignSequentialBufferAddresses(llvm::MutableArrayRef< BufferAllocation > buffers, int64_t start=0)
The basic address allocator and resource planner share this layout: retain address pins,...
AIETargetModel::SharedMemory sharedMemory(TileOp a, TileOp b)
Which of a's and b's memory modules both tiles can address.
void link(mlir::Operation *from, mlir::Operation *to, llvm::StringRef label)
Link from to to under label, and to back to from.
llvm::SmallVector< mlir::Operation * > nodes
std::string & add(mlir::Operation *op)
Add op as a node whose features are its name and attributes, symbol names and references left out.
llvm::DenseMap< mlir::Operation *, unsigned > rank() const
Rank the nodes by their features and then, a round at a time until no class splits,...
llvm::DenseMap< mlir::Operation *, std::string > features
What each node is, before anything it links to is considered.