12#include "mlir/Parser/Parser.h"
13#include "mlir/Pass/Pass.h"
15#include "llvm/ADT/StringExtras.h"
25#define GEN_PASS_DEF_AIECHECKAGAINSTIMAGE
26#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
31using Coords = std::pair<int, int>;
38 bool operator<(
const Channel &other)
const {
39 return std::tie(tile, dir, index) <
40 std::tie(other.tile, other.dir, other.index);
42 bool operator==(
const Channel &other)
const {
43 return !(*
this < other) && !(other < *
this);
49 std::optional<int64_t> address, bank;
54 std::optional<int64_t> id;
59 SmallVector<int64_t> bdIds;
67 std::map<std::string, Channel> shimEnds;
68 std::map<std::string, Buffer> buffers;
69 std::map<std::string, Lock> locks;
70 std::map<std::string, Chain> chains;
71 std::map<std::string, std::set<Channel>> tasks;
72 std::map<std::pair<Coords, std::string>, std::set<std::string>> switches;
75Coords coordsOf(Value tile) {
76 auto op = cast<TileOp>(tile.getDefiningOp());
77 return {op.colIndex(), op.rowIndex()};
80std::string nameOf(Operation *op) {
82 op->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName()))
90std::string identity(ArrayRef<Operation *> ops) {
91 std::set<std::string> buffers, locks;
92 for (Operation *op : ops) {
93 if (
auto bd = dyn_cast<DMABDOp>(op)) {
94 if (
auto buffer = bd.getBuffer().getDefiningOp())
95 if (std::string name = nameOf(buffer); !name.empty())
96 buffers.insert(name +
"@" +
97 std::to_string(bd.getConstantOffset().value_or(-1)));
98 }
else if (
auto use = dyn_cast<UseLockOp>(op)) {
99 if (
auto lock = use.getLock().getDefiningOp())
100 if (std::string name = nameOf(lock); !name.empty())
104 return llvm::join(buffers.empty() ? locks : buffers,
", ");
107Allocation allocationOf(DeviceOp device) {
108 Allocation allocation;
109 for (
auto alloc : device.getOps<ShimDMAAllocationOp>())
110 allocation.shimEnds[alloc.getSymName().str()] = {
111 coordsOf(alloc.getTile()), alloc.getChannelDir(),
112 static_cast<int>(alloc.getChannelIndex())};
113 for (
auto buffer : device.getOps<BufferOp>())
114 if (std::string name = nameOf(buffer); !name.empty())
115 allocation.buffers[name] = {coordsOf(buffer.getTile()),
116 buffer.getAddress(), buffer.getMemBank()};
117 for (
auto lock : device.getOps<LockOp>())
118 if (std::string name = nameOf(lock); !name.empty())
119 allocation.locks[name] = {coordsOf(lock.getTile()), lock.getLockID()};
121 for (
auto program : device.getOps<DmaBody>())
122 for (Block &block : program.getDmaBody())
123 for (
auto start : block.getOps<DMAStartOp>()) {
124 SmallVector<Operation *> ops;
125 SmallVector<int64_t> bdIds;
126 SmallPtrSet<Block *, 8> seen;
127 for (Block *at = start.getDest(); at && seen.insert(at).second;) {
128 if (at->getOps<DMABDOp>().empty())
130 for (Operation &op : *at) {
132 if (
auto bd = dyn_cast<DMABDOp>(op))
133 bdIds.push_back(bd.getBdId().value_or(-1));
135 auto next = dyn_cast<NextBDOp>(at->getTerminator());
136 at = next ? next.getDest() :
nullptr;
138 std::string name = identity(ops);
141 allocation.chains[name] = {{coordsOf(program.getTile()),
142 start.getChannelDir(),
143 start.getChannelIndex()},
147 device.walk([&](AIEX::DMAConfigureTaskOp task) {
148 SmallVector<Operation *> ops;
149 task.getBody().walk([&](Operation *op) { ops.push_back(op); });
150 if (std::string name = identity(ops); !name.empty())
151 allocation.tasks[name].insert({coordsOf(task.getTile()),
153 static_cast<int>(task.getChannel())});
156 auto addSwitch = [&](Operation *box, Value tile, StringRef kind) {
157 auto &connects = allocation.switches[{coordsOf(tile), kind.str()}];
158 box->walk([&](ConnectOp connect) {
159 connects.insert((stringifyWireBundle(connect.getSourceBundle()) +
":" +
160 Twine(connect.sourceIndex()) +
"->" +
161 stringifyWireBundle(connect.getDestBundle()) +
":" +
162 Twine(connect.destIndex()))
166 for (
auto box : device.getOps<SwitchboxOp>())
167 addSwitch(box, box.getTile(),
"switchbox");
168 for (
auto mux : device.getOps<ShimMuxOp>())
169 addSwitch(mux, mux.getTile(),
"shim mux");
173std::string str(Coords tile) {
174 return (
"(" + Twine(tile.first) +
", " + Twine(tile.second) +
")").str();
177std::string str(
const Channel &channel) {
178 return (str(channel.tile) +
" " + stringifyDMAChannelDir(channel.dir) +
" " +
179 Twine(channel.index))
183std::string str(
const std::set<Channel> &channels) {
184 SmallVector<std::string> parts;
185 for (
const Channel &channel : channels)
186 parts.push_back(str(channel));
187 return llvm::join(parts,
" and ");
190std::string str(std::optional<int64_t> value) {
191 return value ? std::to_string(*value) :
"none";
195std::string shortList(ArrayRef<std::string> names) {
196 if (names.size() <= 3)
197 return llvm::join(names,
", ");
198 return names[0] +
", " + names[1] +
" and " +
199 std::to_string(names.size() - 2) +
" more";
203std::string shortName(StringRef name) {
204 SmallVector<StringRef> parts;
205 name.split(parts,
", ");
206 return shortList(llvm::to_vector(
207 llvm::map_range(parts, [](StringRef part) {
return part.str(); })));
212SmallVector<std::string> differences(
const Allocation &image,
213 const Allocation &stream) {
214 SmallVector<std::string> lines;
215 auto both = [](
const auto &a,
const auto &b,
auto fn) {
216 std::set<std::string> keys;
217 for (
auto &[key, _] : a)
219 for (
auto &[key, _] : b)
221 for (
const std::string &key : keys) {
222 auto ia = a.find(key), ib = b.find(key);
223 fn(key, ia == a.end() ?
nullptr : &ia->second,
224 ib == b.end() ?
nullptr : &ib->second);
228 both(image.shimEnds, stream.shimEnds,
229 [&](
const std::string &name,
const Channel *i,
const Channel *s) {
230 StringRef fifo = StringRef(name).rsplit(
"_shim_alloc").first;
231 if (i && s && *i == *s)
233 lines.push_back((fifo +
"'s shim end: image " +
234 (i ? str(*i) :
"none") +
", this stream " +
235 (s ? str(*s) :
"none") +
236 "; pin it to keep it stable across shapes")
241 std::map<std::pair<Coords, Coords>, SmallVector<std::string>> moved;
242 for (
auto &[name, i] : image.buffers)
243 if (
auto it = stream.buffers.find(name);
244 it != stream.buffers.end() && it->second.tile != i.tile)
245 moved[{i.tile, it->second.tile}].push_back(name);
246 for (
auto &[tiles, names] : moved)
247 lines.push_back(
"the tile holding " + shortList(names) +
": image " +
248 str(tiles.first) +
", this stream " + str(tiles.second) +
249 "; pin it to keep it stable across shapes");
251 both(image.chains, stream.chains,
252 [&](
const std::string &name,
const Chain *i,
const Chain *s) {
253 if (i && s && i->channel == s->channel) {
254 auto ids = [](ArrayRef<int64_t> bdIds) {
256 llvm::map_range(bdIds,
257 [](int64_t id) { return std::to_string(id); }),
260 if (i->bdIds != s->bdIds)
261 lines.push_back(
"the BD ids of the chain moving " +
262 shortName(name) +
" at " + str(i->channel) +
263 ": image " + ids(i->bdIds) +
", this stream " +
267 lines.push_back(
"the DMA channel moving " + shortName(name) +
268 ": image " + (i ? str(i->channel) :
"none") +
269 ", this stream " + (s ? str(s->channel) :
"none") +
270 "; pin its endpoint to keep it stable across shapes");
273 both(image.tasks, stream.tasks,
274 [&](
const std::string &name,
const std::set<Channel> *i,
275 const std::set<Channel> *s) {
277 if (!i || !s || *i == *s)
280 "the runtime tasks moving " + shortName(name) +
": image " +
281 str(*i) +
", this stream " + str(*s) +
282 "; pin their endpoint to keep it stable across shapes");
285 both(image.buffers, stream.buffers,
286 [&](
const std::string &name,
const Buffer *i,
const Buffer *s) {
287 if (i && s && i->tile != s->tile)
289 if (i && s && i->address == s->address && i->bank == s->bank)
292 lines.push_back(
"buffer " + name +
": only " +
293 (i ?
"the image" :
"this stream") +
" has it, at " +
294 str(i ? i->tile : s->tile));
297 lines.push_back(
"buffer " + name +
" on " + str(i->tile) +
298 ": image address " + str(i->address) +
" (bank " +
299 str(i->bank) +
"), this stream " + str(s->address) +
300 " (bank " + str(s->bank) +
")");
303 both(image.locks, stream.locks,
304 [&](
const std::string &name,
const Lock *i,
const Lock *s) {
305 if (i && s && i->tile == s->tile && i->id == s->id)
307 if (i && s && i->tile != s->tile)
310 lines.push_back(
"lock " + name +
": only " +
311 (i ?
"the image" :
"this stream") +
" has it");
314 lines.push_back(
"lock " + name +
" on " + str(i->tile) +
315 ": image id " + str(i->id) +
", this stream id " +
320 std::set<std::pair<Coords, std::string>> boxes;
321 for (
auto &[key, _] : image.switches)
323 for (
auto &[key, _] : stream.switches)
325 for (
auto &key : boxes) {
326 std::set<std::string> none;
327 auto ia = image.switches.find(key), is = stream.switches.find(key);
328 const auto &i = ia == image.switches.end() ? none : ia->second;
329 const auto &s = is == stream.switches.end() ? none : is->second;
330 SmallVector<std::string> onlyImage, onlyStream;
331 std::set_difference(i.begin(), i.end(), s.begin(), s.end(),
332 std::back_inserter(onlyImage));
333 std::set_difference(s.begin(), s.end(), i.begin(), i.end(),
334 std::back_inserter(onlyStream));
335 if (onlyImage.empty() && onlyStream.empty())
339 key.second +
" " + str(key.first) +
": image connects " +
340 (onlyImage.empty() ?
"none" : llvm::join(onlyImage,
", ")) +
342 (onlyStream.empty() ?
"none" : llvm::join(onlyStream,
", ")));
345 lines.push_back(
"the routes follow the placement and channels above");
349struct AIECheckAgainstImagePass
350 : xilinx::AIE::impl::AIECheckAgainstImageBase<AIECheckAgainstImagePass> {
351 using AIECheckAgainstImageBase::AIECheckAgainstImageBase;
353 void runOnOperation()
override {
354 ModuleOp module = getOperation();
355 if (clImage.empty()) {
356 module.emitError("aie-check-against-image needs image=<module>");
357 return signalPassFailure();
359 OwningOpRef<ModuleOp> image =
360 parseSourceFile<ModuleOp>(clImage, ParserConfig(&getContext()));
362 module.emitError("could not read the image's module ") << clImage;
363 return signalPassFailure();
365 DenseMap<StringAttr, DeviceOp> imageDevices;
366 for (
auto device : image->getOps<DeviceOp>())
367 imageDevices[device.getSymNameAttr()] = device;
369 bool mismatch =
false;
370 for (
auto stream : module.getOps<DeviceOp>()) {
371 DeviceOp target = imageDevices.lookup(stream.getSymNameAttr());
373 stream.emitOpError(
"has no device of its name in the image's module ")
378 SmallVector<std::string> lines =
379 differences(allocationOf(target), allocationOf(stream));
382 auto diag = mlir::emitError(stream.getLoc())
383 <<
"this instruction stream would program resources its "
385 << clImage <<
") does not have; " << lines.size()
387 for (
const std::string &line : lines)
388 diag <<
"\n " << line;
398std::unique_ptr<OperationPass<ModuleOp>>
400 return std::make_unique<AIECheckAgainstImagePass>();
403std::unique_ptr<OperationPass<ModuleOp>>
405 const AIECheckAgainstImageOptions &options) {
406 return std::make_unique<AIECheckAgainstImagePass>(options);
std::unique_ptr< mlir::OperationPass< mlir::ModuleOp > > createAIECheckAgainstImagePass()