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