MLIR-AIE 1.4.5.dev
AIETargets.cpp
Go to the documentation of this file.
1//===- AIETargets.cpp -------------------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2021-2022 Xilinx, Inc.
4// Copyright (C) 2022-2026 Advanced Micro Devices, Inc.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
15
16#include "mlir/Dialect/Affine/IR/AffineOps.h"
17#include "mlir/Dialect/Arith/IR/Arith.h"
18#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
19#include "mlir/Dialect/DLTI/DLTI.h"
20#include "mlir/Dialect/EmitC/IR/EmitC.h"
21#include "mlir/Dialect/Func/IR/FuncOps.h"
22#include "mlir/Dialect/Index/IR/IndexDialect.h"
23#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
24#include "mlir/Dialect/Math/IR/Math.h"
25#include "mlir/Dialect/Ptr/IR/PtrOps.h"
26#include "mlir/Dialect/SCF/IR/SCF.h"
27#include "mlir/Dialect/UB/IR/UBOps.h"
28#include "mlir/Dialect/Vector/IR/VectorOps.h"
29#include "mlir/IR/Attributes.h"
30#include "mlir/Target/LLVMIR/Export.h"
31#include "mlir/Target/LLVMIR/Import.h"
32#include "mlir/Tools/mlir-translate/Translation.h"
33
34#include "llvm/Support/CommandLine.h"
35#include "llvm/Support/Debug.h"
36#include "llvm/Support/FileSystem.h"
37#include "llvm/Support/JSON.h"
38#include "llvm/Support/raw_ostream.h"
39
40#include <fstream>
41#include <iostream>
42#include <set>
43
44#define DEBUG_TYPE "aie-targets"
45
46using namespace mlir;
47using namespace mlir::vector;
48using namespace xilinx;
49using namespace xilinx::AIE;
50
51static llvm::json::Value attrToJSON(Attribute &attr) {
52 if (auto a = llvm::dyn_cast<StringAttr>(attr))
53 return {a.getValue().str()};
54
55 if (auto arrayAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
56 llvm::json::Array arrayJSON;
57 for (auto a : arrayAttr)
58 arrayJSON.push_back(attrToJSON(a));
59 return llvm::json::Value(std::move(arrayJSON));
60 }
61
62 if (auto dictAttr = llvm::dyn_cast<DictionaryAttr>(attr)) {
63 llvm::json::Object dictJSON;
64 for (auto a : dictAttr) {
65 auto ident = a.getName();
66 auto attr = a.getValue();
67 dictJSON[ident.str()] = attrToJSON(attr);
68 }
69 return llvm::json::Value(std::move(dictJSON));
70 }
71
72 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr))
73 return llvm::json::Value(intAttr.getInt());
74
75 return llvm::json::Value(std::string(""));
76}
77
78namespace xilinx::AIE {
79
80static void registerDialects(DialectRegistry &registry) {
81 registry.insert<xilinx::AIE::AIEDialect>();
82 registry.insert<xilinx::AIEX::AIEXDialect>();
83 registry.insert<xilinx::aievec::AIEVecDialect>();
84 registry.insert<func::FuncDialect>();
85 registry.insert<scf::SCFDialect>();
86 registry.insert<cf::ControlFlowDialect>();
87 registry.insert<DLTIDialect>();
88 registry.insert<arith::ArithDialect>();
89 registry.insert<affine::AffineDialect>();
90 registry.insert<ub::UBDialect>();
91 registry.insert<math::MathDialect>();
92 registry.insert<memref::MemRefDialect>();
93 registry.insert<VectorDialect>();
94 registry.insert<LLVM::LLVMDialect>();
95 registry.insert<emitc::EmitCDialect>();
96 registry.insert<index::IndexDialect>();
97 registry.insert<ptr::PtrDialect>();
98}
99
100// Output the buffer map for the given buffer operations, with the given offset.
101// The offset is different depending on where the buffers are accessed from.
102static void writeBufferMap(raw_ostream &output, BufferOp buf, int offset) {
103 std::string bufName(buf.name().getValue());
104 int bufferBaseAddr = getBufferBaseAddress(buf);
105 int numBytes = buf.getAllocationSize();
106 output << "_symbol " << bufName << " "
107 << "0x" << llvm::utohexstr(offset + bufferBaseAddr) << " " << numBytes
108 << '\n';
109}
110
111LogicalResult AIETranslateToTargetArch(ModuleOp module, raw_ostream &output,
112 llvm::StringRef deviceName) {
113 DeviceOp targetOp = AIE::DeviceOp::getForSymbolInModule(module, deviceName);
114 AIEArch arch = AIEArch::AIE1;
115 if (targetOp) {
116 arch = targetOp.getTargetModel().getTargetArch();
117 }
118 if (arch == AIEArch::AIE1)
119 output << "AIE\n";
120 else
121 output << stringifyEnum(arch) << "\n";
122 return success();
123}
124
126 static llvm::cl::opt<int> tileCol(
127 "tilecol", llvm::cl::desc("column coordinate of core to translate"),
128 llvm::cl::init(0));
129 static llvm::cl::opt<int> tileRow(
130 "tilerow", llvm::cl::desc("row coordinate of core to translate"),
131 llvm::cl::init(0));
132
133 static llvm::cl::opt<std::string> workDirPath(
134 "work-dir-path", llvm::cl::Optional,
135 llvm::cl::desc("Absolute path to working directory"));
136
137 static llvm::cl::opt<bool> bigEndian("big-endian", llvm::cl::init(false),
138 llvm::cl::desc("Endianness"));
139
140 static llvm::cl::opt<bool> cdoUnified(
141 "cdo-unified", llvm::cl::init(false),
142 llvm::cl::desc("Emit unified CDO bin (or separate bins)"));
143 static llvm::cl::opt<bool> cdoDebug("cdo-debug", llvm::cl::init(false),
144 llvm::cl::desc("Emit cdo debug info"));
145 static llvm::cl::opt<bool> cdoAieSim(
146 "cdo-aiesim", llvm::cl::init(false),
147 llvm::cl::desc("AIESIM target cdo generation"));
148 static llvm::cl::opt<bool> cdoXaieDebug(
149 "cdo-xaie-debug", llvm::cl::init(false),
150 llvm::cl::desc("Emit libxaie debug info"));
151 static llvm::cl::opt<size_t> cdoEnableCores(
152 "cdo-enable-cores", llvm::cl::init(true),
153 llvm::cl::desc("Enable cores in CDO"));
154
155 static llvm::cl::opt<bool> outputBinary(
156 "aie-output-binary", llvm::cl::init(true),
157 llvm::cl::desc(
158 "Select binary (true) or text (false) output for supported "
159 "translations. e.g. aie-npu-to-binary, aie-ctrlpkt-to-bin"));
160 static llvm::cl::opt<bool> npuFoldDDRAddrOffset(
161 "aie-npu-fold-ddr-addr-offset", llvm::cl::init(true),
162 llvm::cl::desc(
163 "For aie-npu-to-binary and aie-npu-to-cpp: fold the AIE "
164 "DDR-aperture offset into the arg_plus of DDR address patches for "
165 "host arguments beyond the firmware-translated set. Required for "
166 "the xclbin + instruction-buffer runtime; must be false for the "
167 "full-ELF (xrt.ext.kernel) runtime and for HRX, which translate "
168 "every host buffer address themselves."));
169 static llvm::cl::opt<bool> npuEmitDispatchShim(
170 "aie-npu-emit-dispatch-shim", llvm::cl::init(false),
171 llvm::cl::desc(
172 "For aie-npu-to-cpp: also emit the extern \"C\" dispatch_abi() and "
173 "dispatch_generate() entry points the JIT dispatch bridge loads via "
174 "ctypes. Requires exactly one aie.runtime_sequence."));
175 static llvm::cl::opt<std::string> deviceName(
176 "aie-device-name", llvm::cl::init(""),
177 llvm::cl::desc("Specify which device to translate"));
178 static llvm::cl::opt<std::string> sequenceName(
179 "aie-sequence-name", llvm::cl::init(""),
180 llvm::cl::desc(
181 "Specify the name of the aiex.runtime_sequence to translate"));
182 static llvm::cl::opt<std::string> npuEmitLocmap(
183 "aie-npu-emit-locmap", llvm::cl::init(""),
184 llvm::cl::value_desc("filename"),
185 llvm::cl::desc(
186 "For aie-npu-to-binary, also write a JSON sidecar mapping each "
187 "transaction word's byte offset to its source MLIR Location (and "
188 "regdb register name where applicable) to the given file. The binary "
189 "is still emitted to the main output."));
190
191 TranslateFromMLIRRegistration registrationMMap(
192 "aie-generate-mmap", "Generate AIE memory map",
193 [](ModuleOp module, raw_ostream &output) {
194 DenseMap<TileID, Operation *> tiles;
195 DenseMap<Operation *, SmallVector<BufferOp, 4>> buffers;
196
197 DeviceOp targetOp =
198 AIE::DeviceOp::getForSymbolInModule(module, deviceName);
199 if (!targetOp) {
200 module.emitOpError("expected AIE.device operation at toplevel");
201 }
202
203 collectTiles(targetOp, tiles);
204 // sort the tiles for deterministic output
205 using tileType = std::pair<TileID, Operation *>;
206 struct tileCmp {
207 bool operator()(const tileType &lhs, const tileType &rhs) const {
208 return lhs.first < rhs.first;
209 }
210 };
211 std::set<tileType, tileCmp> sortedTiles;
212 for (auto tile : tiles)
213 sortedTiles.insert(tileType{tile.first, tile.second});
214
215 collectBuffers(targetOp, buffers);
216
217 for (auto tile : sortedTiles) {
218 Operation *srcTileOp = tile.second;
219 TileID srcCoord = cast<TileOp>(srcTileOp).getTileID();
220 int srcCol = srcCoord.col;
221 int srcRow = srcCoord.row;
222
223 output << "// Tile(" << srcCol << ", " << srcRow << ")\n";
224 output << "// Memory map: name base_address num_bytes\n";
225
226 auto doBuffer = [&](std::optional<TileID> tile, int offset) {
227 if (tiles.count(*tile))
228 for (auto buf : buffers[tiles[*tile]])
229 writeBufferMap(output, buf, offset);
230 };
231
232 const auto &targetModel = xilinx::AIE::getTargetModel(srcTileOp);
233
234 if (auto tile = targetModel.getMemSouth(srcCoord))
235 doBuffer(tile, targetModel.getMemSouthBaseAddress());
236 if (auto tile = targetModel.getMemWest(srcCoord))
237 doBuffer(tile, targetModel.getMemWestBaseAddress());
238 if (auto tile = targetModel.getMemNorth(srcCoord))
239 doBuffer(tile, targetModel.getMemNorthBaseAddress());
240 if (auto tile = targetModel.getMemEast(srcCoord))
241 doBuffer(tile, targetModel.getMemEastBaseAddress());
242 }
243 return success();
244 },
245 registerDialects);
246
247 TranslateFromMLIRRegistration registrationShimDMAToJSON(
248 "aie-generate-json", "Transform AIE shim DMA allocation info into JSON",
249 [](ModuleOp module, raw_ostream &output) {
250 DeviceOp d =
251 AIE::DeviceOp::getForSymbolInModuleOrError(module, deviceName);
252 if (!d) {
253 return failure();
254 }
255 llvm::json::Object moduleJSON;
256 for (auto shimDMAMeta : d.getOps<ShimDMAAllocationOp>()) {
257 llvm::json::Object shimJSON;
258 auto channelDir = shimDMAMeta.getChannelDirAttr();
259 shimJSON["channelDir"] = attrToJSON(channelDir);
260 auto channelIndex = shimDMAMeta.getChannelIndexAttr();
261 shimJSON["channelIndex"] = attrToJSON(channelIndex);
262 AIE::TileOp tile = shimDMAMeta.getTileOp();
263 if (!tile) {
264 shimDMAMeta.emitError(
265 "shim DMA allocation must reference a valid TileOp");
266 return failure();
267 }
268 auto col = tile.getColAttr();
269 shimJSON["col"] = attrToJSON(col);
270 moduleJSON[shimDMAMeta.getSymName()] =
271 llvm::json::Value(std::move(shimJSON));
272 }
273 llvm::json::Value topv(std::move(moduleJSON));
274 std::string ret;
275 llvm::raw_string_ostream ss(ret);
276 ss << llvm::formatv("{0:2}", topv) << "\n";
277 output << ss.str();
278 return success();
279 },
280 registerDialects);
281
282 TranslateFromMLIRRegistration registrationLDScript(
283 "aie-generate-ldscript", "Generate AIE loader script",
284 [](ModuleOp module, raw_ostream &output) {
285 return AIETranslateToLdScript(module, output, tileCol, tileRow,
286 deviceName);
287 },
288 registerDialects);
289
290 TranslateFromMLIRRegistration registrationBCF(
291 "aie-generate-bcf", "Generate AIE bcf",
292 [](ModuleOp module, raw_ostream &output) {
293 return AIETranslateToBCF(module, output, tileCol, tileRow, deviceName);
294 },
295 registerDialects);
296
297 TranslateFromMLIRRegistration registrationTargetArch(
298 "aie-generate-target-arch", "Get the target architecture",
299 [](ModuleOp module, raw_ostream &output) {
300 return AIETranslateToTargetArch(module, output, deviceName);
301 },
302 registerDialects);
303
304 TranslateFromMLIRRegistration registrationCoreList(
305 "aie-generate-corelist", "Generate python list of cores",
306 [](ModuleOp module, raw_ostream &output) {
307 DeviceOp targetOp =
308 AIE::DeviceOp::getForSymbolInModule(module, deviceName);
309 if (!targetOp) {
310 module.emitOpError("expected AIE.device operation at toplevel");
311 return failure();
312 }
313
314 output << "[";
315 for (auto tileOp : targetOp.getOps<TileOp>()) {
316 int col = tileOp.colIndex();
317 int row = tileOp.rowIndex();
318 if (auto coreOp = tileOp.getCoreOp()) {
319 std::string elfFile = "None";
320 if (auto fileAttr = coreOp.getElfFile())
321 elfFile = "\"" + fileAttr.value().str() + "\"";
322 output << '(' << col << ',' << row << ',' << elfFile << "),";
323 }
324 }
325 output << "]\n";
326 return success();
327 },
328 registerDialects);
329
330 TranslateFromMLIRRegistration registrationXAIE(
331 "aie-generate-xaie", "Generate libxaie configuration",
332 [](ModuleOp module, raw_ostream &output) {
333 return AIETranslateToXAIEV2(module, output, deviceName);
334 },
335 registerDialects);
336 TranslateFromMLIRRegistration registrationXJSON(
337 "aie-flows-to-json", "Translate AIE flows to JSON",
338 [](ModuleOp module, raw_ostream &output) {
339 return AIEFlowsToJSON(module, output, deviceName);
340 },
341 registerDialects);
342 TranslateFromMLIRRegistration registrationXPE(
343 "aie-mlir-to-xpe", "Translate AIE design to XPE file for simulation",
344 [](ModuleOp module, raw_ostream &output) {
345 return AIETranslateGraphXPE(module, output, deviceName);
346 },
347 registerDialects);
348 TranslateFromMLIRRegistration registrationSCSimConfig(
349 "aie-mlir-to-scsim-config",
350 "Translate AIE design to SCSimConfig file for simulation",
351 [](ModuleOp module, raw_ostream &output) {
352 return AIETranslateSCSimConfig(module, output, deviceName);
353 },
354 registerDialects);
355 TranslateFromMLIRRegistration registrationShimSolution(
356 "aie-mlir-to-shim-solution",
357 "Translate AIE design to ShimSolution file for simulation",
358 [](ModuleOp module, raw_ostream &output) {
359 return AIETranslateShimSolution(module, output, deviceName);
360 },
361 registerDialects);
362 TranslateFromMLIRRegistration registrationCDODirect(
363 "aie-generate-cdo", "Generate libxaie for CDO directly",
364 [](ModuleOp module, raw_ostream &) {
365 SmallString<128> workDirPath_;
366 if (workDirPath.getNumOccurrences() == 0) {
367 if (llvm::sys::fs::current_path(workDirPath_))
368 llvm::report_fatal_error(
369 "couldn't get cwd to use as work-dir-path");
370 } else
371 workDirPath_ = workDirPath.getValue();
372 LLVM_DEBUG(llvm::dbgs() << "work-dir-path: " << workDirPath_ << "\n");
373 return AIETranslateToCDODirect(module, workDirPath_.c_str(), deviceName,
374 bigEndian, cdoUnified, cdoDebug,
375 cdoAieSim, cdoXaieDebug, cdoEnableCores);
376 },
377 registerDialects);
378 TranslateFromMLIRRegistration registrationNPU(
379 "aie-npu-to-binary", "Translate npu instructions to binary",
380 [](ModuleOp module, raw_ostream &output) {
381 std::vector<uint32_t> instructions;
382 std::vector<TxnLocEntry> locmap;
383 bool emitLocmap = !npuEmitLocmap.empty();
385 module, instructions, deviceName, sequenceName,
386 emitLocmap ? &locmap : nullptr, npuFoldDDRAddrOffset);
387 if (failed(r))
388 return r;
389 // The binary (or hex text) is always emitted to the main output.
390 if (outputBinary) {
391 output.write(reinterpret_cast<const char *>(instructions.data()),
392 instructions.size() * sizeof(uint32_t));
393 } else {
394 for (auto w : instructions)
395 output << llvm::format("%08X\n", w);
396 }
397 // With -aie-npu-emit-locmap=<file>, additionally write the JSON
398 // location sidecar (mapping each transaction word's byte offset to its
399 // source MLIR Location) to that file.
400 if (emitLocmap) {
401 std::error_code ec;
402 llvm::raw_fd_ostream locFile(npuEmitLocmap, ec,
403 llvm::sys::fs::OF_Text);
404 if (ec) {
405 llvm::errs() << "Error opening locmap file '" << npuEmitLocmap
406 << "': " << ec.message() << "\n";
407 return failure();
408 }
409 emitNpuLocmapJSON(locFile, deviceName, /*binaryName=*/"", locmap);
410 }
411 return success();
412 },
413 registerDialects);
414 TranslateFromMLIRRegistration registrationNpuToCpp(
415 "aie-npu-to-cpp",
416 "Translate npu instructions to a C++ TXN-builder function",
417 [](ModuleOp module, raw_ostream &output) {
418 return AIETranslateNpuToCpp(module, output, npuFoldDDRAddrOffset,
419 npuEmitDispatchShim);
420 },
421 registerDialects);
422 TranslateFromMLIRRegistration registrationCtrlPkt(
423 "aie-ctrlpkt-to-bin", "Translate aiex.control_packet ops to binary",
424 [](ModuleOp module, raw_ostream &output) {
425 std::vector<uint32_t> instructions;
426 auto r = AIETranslateControlPacketsToUI32Vec(module, instructions,
427 deviceName, sequenceName);
428 if (failed(r))
429 return r;
430 if (outputBinary) {
431 output.write(reinterpret_cast<const char *>(instructions.data()),
432 instructions.size() * sizeof(uint32_t));
433 } else {
434 for (auto w : instructions)
435 output << llvm::format("%08X\n", w);
436 }
437 return success();
438 },
439 registerDialects);
440
441 // Translate cert dialect operations to assembly
442 TranslateFromMLIRRegistration registrationCertToAsm(
443 "aie-cert-to-asm", "Translate cert operations to assembly",
444 [](ModuleOp module, raw_ostream &output) {
445 return AIETranslateToUcDma(module, output);
446 },
447 registerDialects);
448}
449} // namespace xilinx::AIE
void emitNpuLocmapJSON(llvm::raw_ostream &output, llvm::StringRef deviceName, llvm::StringRef binaryName, const std::vector< TxnLocEntry > &locmap)
mlir::LogicalResult AIETranslateToUcDma(mlir::ModuleOp module, llvm::raw_ostream &output)
mlir::LogicalResult AIETranslateSCSimConfig(mlir::ModuleOp module, llvm::raw_ostream &output, llvm::StringRef deviceName="")
mlir::LogicalResult AIETranslateToCDODirect(mlir::ModuleOp m, llvm::StringRef workDirPath, llvm::StringRef deviceName, bool bigEndian=false, bool emitUnified=false, bool cdoDebug=false, bool aieSim=false, bool xaieDebug=false, bool enableCores=true)
mlir::LogicalResult AIETranslateControlPacketsToUI32Vec(mlir::ModuleOp, std::vector< uint32_t > &, llvm::StringRef deviceName="", llvm::StringRef sequenceName="", std::vector< TxnLocEntry > *locmap=nullptr)
void collectTiles(DeviceOp &device, llvm::DenseMap< TileID, mlir::Operation * > &tiles)
mlir::LogicalResult AIETranslateToTargetArch(mlir::ModuleOp module, llvm::raw_ostream &output, llvm::StringRef deviceName)
mlir::LogicalResult AIETranslateNpuToBinary(mlir::ModuleOp, std::vector< uint32_t > &, llvm::StringRef deviceName="", llvm::StringRef sequenceName="", std::vector< TxnLocEntry > *locmap=nullptr, bool foldDDRAddrOffset=true, NpuConfigCache *configs=nullptr)
void collectBuffers(DeviceOp &device, llvm::DenseMap< mlir::Operation *, llvm::SmallVector< BufferOp, 4 > > &buffers)
void registerAIETranslations()
mlir::LogicalResult AIEFlowsToJSON(mlir::ModuleOp module, llvm::raw_ostream &output, llvm::StringRef deviceName="")
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
int32_t getBufferBaseAddress(mlir::Operation *bufOp)
mlir::LogicalResult AIETranslateToBCF(mlir::ModuleOp module, llvm::raw_ostream &output, int tileCol, int tileRow, llvm::StringRef deviceName="")
mlir::LogicalResult AIETranslateNpuToCpp(mlir::ModuleOp module, llvm::raw_ostream &output, bool foldDDRAddrOffset=true, bool emitDispatchShim=false)
Emit a C++ builder that reconstructs the TXN stream at runtime.
const AIETargetModel & getTargetModel(mlir::Operation *op)
mlir::LogicalResult AIETranslateShimSolution(mlir::ModuleOp module, llvm::raw_ostream &, llvm::StringRef deviceName="")
mlir::LogicalResult AIETranslateGraphXPE(mlir::ModuleOp module, llvm::raw_ostream &, llvm::StringRef)
mlir::LogicalResult AIETranslateToXAIEV2(mlir::ModuleOp module, llvm::raw_ostream &output, llvm::StringRef deviceName="")
mlir::LogicalResult AIETranslateToLdScript(mlir::ModuleOp module, llvm::raw_ostream &output, int tileCol, int tileRow, llvm::StringRef deviceName="", bool probe=false, llvm::function_ref< std::string(llvm::StringRef)> resolveLinkFile=nullptr)
AIEArch
Definition Passes.h:19