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