MLIR-AIE
AIEAssignBufferDescriptorIDs.cpp
Go to the documentation of this file.
1//===- AIEAssignBufferDescriptorIDs.cpp -------------------------*- C++ -*-===//
2//
3// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7// (c) Copyright 2024 Advanced Micro Devices Inc.
8//
9//===----------------------------------------------------------------------===//
10
14
15#include "mlir/Pass/Pass.h"
16
17#define DEBUG_TYPE "aie-assign-bd-ids"
18
19using namespace mlir;
20using namespace xilinx;
21using namespace xilinx::AIE;
22
24 const AIETargetModel &targetModel)
25 : col(col), row(row), targetModel(targetModel) {}
26
27std::optional<uint32_t> BdIdGenerator::nextBdId(int channelIndex) {
28 uint32_t bd_id = 0;
29 uint32_t max_bd_id = targetModel.getNumBDs(col, row) - 1;
30 // Find the next free BD ID. This is not an efficient algorithm, but doesn't
31 for (; (bdIdAlreadyAssigned(bd_id) ||
32 !targetModel.isBdChannelAccessible(col, row, bd_id, channelIndex)) &&
33 bd_id <= max_bd_id;
34 bd_id++)
35 ;
36 if (bd_id > max_bd_id) {
37 return std::nullopt;
38 }
39 assignBdId(bd_id);
40 return std::optional<uint32_t>(bd_id);
41}
42
43void BdIdGenerator::assignBdId(uint32_t bdId) {
44 assert(!alreadyAssigned.count(bdId) && "bdId has already been assigned");
45 alreadyAssigned.insert(bdId);
46}
47
49 return alreadyAssigned.count(bdId);
50}
51
52void BdIdGenerator::freeBdId(uint32_t bdId) { alreadyAssigned.erase(bdId); }
53
55 : AIEAssignBufferDescriptorIDsBase<AIEAssignBufferDescriptorIDsPass> {
56 void runOnOperation() override {
57 DeviceOp targetOp = getOperation();
58 const AIETargetModel &targetModel = targetOp.getTargetModel();
59
60 auto memOps = llvm::to_vector_of<TileElement>(targetOp.getOps<MemOp>());
61 llvm::append_range(memOps, targetOp.getOps<MemTileDMAOp>());
62 llvm::append_range(memOps, targetOp.getOps<ShimDMAOp>());
63 for (TileElement memOp : memOps) {
64 int col = memOp.getTileID().col;
65 int row = memOp.getTileID().row;
66
67 BdIdGenerator gen(col, row, targetModel);
68 memOp->walk<WalkOrder::PreOrder>([&](DMABDOp bd) {
69 if (bd.getBdId().has_value())
70 gen.assignBdId(bd.getBdId().value());
71 });
72
73 auto dmaOps = memOp.getOperation()->getRegion(0).getOps<DMAOp>();
74 if (!dmaOps.empty()) {
75 for (auto dmaOp : dmaOps) {
76 auto bdRegions = dmaOp.getBds();
77 for (auto &bdRegion : bdRegions) {
78 auto &block = bdRegion.getBlocks().front();
79 DMABDOp bd = *block.getOps<DMABDOp>().begin();
80 if (bd.getBdId().has_value()) {
81 assert(
82 gen.bdIdAlreadyAssigned(bd.getBdId().value()) &&
83 "bdId assigned by user but not found during previous walk");
84 } else {
85 std::optional<int32_t> next_id =
86 gen.nextBdId(dmaOp.getChannelIndex());
87 if (!next_id) {
88 bd.emitOpError()
89 << "Allocator exhausted available BD IDs (maximum "
90 << targetModel.getNumBDs(col, row) << " available).";
91 return signalPassFailure();
92 }
93 bd.setBdId(*next_id);
94 }
95 }
96 }
97 } else {
98 DenseMap<Block *, int> blockChannelMap;
99 // Associate with each block the channel index specified by the
100 // dma_start
101 for (Block &block : memOp.getOperation()->getRegion(0))
102 for (auto op : block.getOps<DMAStartOp>()) {
103 int chNum = op.getChannelIndex();
104 blockChannelMap[&block] = chNum;
105 Block *dest = op.getDest();
106 while (dest) {
107 blockChannelMap[dest] = chNum;
108 if (dest->hasNoSuccessors())
109 break;
110 dest = dest->getSuccessors()[0];
111 if (blockChannelMap.contains(dest))
112 dest = nullptr;
113 }
114 }
115
116 for (Block &block : memOp.getOperation()->getRegion(0)) {
117 if (block.getOps<DMABDOp>().empty())
118 continue;
119 assert(blockChannelMap.count(&block));
120 DMABDOp bd = (*block.getOps<DMABDOp>().begin());
121 if (bd.getBdId().has_value()) {
122 assert(gen.bdIdAlreadyAssigned(bd.getBdId().value()) &&
123 "bdId assigned by user but not found during previous walk");
124 } else {
125 std::optional<int32_t> next_id =
126 gen.nextBdId(blockChannelMap[&block]);
127 if (!next_id) {
128 bd.emitOpError()
129 << "Allocator exhausted available BD IDs (maximum "
130 << targetModel.getNumBDs(col, row) << " available).";
131 return signalPassFailure();
132 }
133 bd.setBdId(*next_id);
134 }
135 }
136 }
137 }
138 for (TileElement memOp : memOps) {
139 auto dmaOps = memOp.getOperation()->getRegion(0).getOps<DMAOp>();
140 if (!dmaOps.empty()) {
141 for (auto dmaOp : dmaOps) {
142 auto bdRegions = dmaOp.getBds();
143 for (auto *bdRegionIt = bdRegions.begin();
144 bdRegionIt != bdRegions.end();) {
145 auto &block = bdRegionIt->getBlocks().front();
146 DMABDOp bd = *block.getOps<DMABDOp>().begin();
147 std::optional<int> nextBdId;
148 if (++bdRegionIt != bdRegions.end())
149 nextBdId =
150 (*bdRegionIt->getBlocks().front().getOps<DMABDOp>().begin())
151 .getBdId();
152 else if (dmaOp.getLoop())
153 nextBdId = (*bdRegions.front()
154 .getBlocks()
155 .front()
156 .getOps<DMABDOp>()
157 .begin())
158 .getBdId();
159 bd.setNextBdId(nextBdId);
160 }
161 }
162 } else {
163 DenseMap<Block *, int> blockBdIdMap;
164 for (Block &block : memOp.getOperation()->getRegion(0)) {
165 if (block.getOps<DMABDOp>().empty())
166 continue;
167 DMABDOp bd = *block.getOps<DMABDOp>().begin();
168 assert(bd.getBdId().has_value() &&
169 "DMABDOp should have bd_id assigned by now");
170 blockBdIdMap[&block] = bd.getBdId().value();
171 }
172
173 for (Block &block : memOp.getOperation()->getRegion(0)) {
174 if (block.getOps<DMABDOp>().empty())
175 continue;
176 DMABDOp bd = *block.getOps<DMABDOp>().begin();
177 std::optional<int> nextBdId;
178 if (block.getNumSuccessors()) {
179 assert(llvm::range_size(block.getSuccessors()) == 1 &&
180 "should have only one successor block");
181 Block *nextBlock = block.getSuccessor(0);
182 if (!blockBdIdMap.contains(nextBlock))
183 assert(nextBlock->getOperations().size() == 1 &&
184 isa<EndOp>(nextBlock->getOperations().front()) &&
185 "bb that's not in blockMap can only have aie.end");
186 else
187 nextBdId = blockBdIdMap[nextBlock];
188 bd.setNextBdId(nextBdId);
189 }
190 }
191 }
192 }
193 }
194};
195
196std::unique_ptr<OperationPass<DeviceOp>>
198 return std::make_unique<AIEAssignBufferDescriptorIDsPass>();
199}
virtual uint32_t getNumBDs(int col, int row) const =0
Return the number of buffer descriptors supported by the DMA in the given tile.
virtual bool 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.
Include the generated interface declarations.
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEAssignBufferDescriptorIDsPass()
bool bdIdAlreadyAssigned(uint32_t bdId)
const AIETargetModel & targetModel
void assignBdId(uint32_t bdId)
void freeBdId(uint32_t bdId)
std::optional< uint32_t > nextBdId(int channelIndex)
std::set< uint32_t > alreadyAssigned
BdIdGenerator(int col, int row, const AIETargetModel &targetModel)