MLIR-AIE 1.4.5.dev
AIEInlineTraceConfig.cpp
Go to the documentation of this file.
1//===- AIEInlineTraceConfig.cpp ---------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2025 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7// Pass to inline trace.start_config and generate npu.write32
8//===----------------------------------------------------------------------===//
9
13
14#include "mlir/Dialect/Arith/IR/Arith.h"
15#include "mlir/IR/Attributes.h"
16#include "mlir/Pass/Pass.h"
17
18namespace xilinx::AIEX {
19#define GEN_PASS_DEF_AIEXINLINETRACECONFIG
20#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
21} // namespace xilinx::AIEX
22
23using namespace mlir;
24using namespace xilinx;
25using namespace xilinx::AIE;
26using namespace xilinx::AIEX;
27
28namespace {
29
30struct AIEInlineTraceConfigPass
31 : xilinx::AIEX::impl::AIEXInlineTraceConfigBase<AIEInlineTraceConfigPass> {
32 void runOnOperation() override {
33 AIE::DeviceOp device = getOperation();
34 const auto &targetModel = device.getTargetModel();
35
36 // Collect all trace.start_config operations
37 SmallVector<TraceStartConfigOp> startConfigs;
38 device.walk([&](TraceStartConfigOp startConfig) {
39 startConfigs.push_back(startConfig);
40 });
41
42 for (auto startConfig : startConfigs) {
43 OpBuilder builder(startConfig);
44
45 // Lookup the trace config symbol
46 auto configSymbolName = startConfig.getTraceConfig();
47 auto configOp =
48 dyn_cast_or_null<TraceConfigOp>(SymbolTable::lookupNearestSymbolFrom(
49 device, builder.getStringAttr(configSymbolName)));
50
51 if (!configOp) {
52 startConfig.emitError("trace config symbol '")
53 << configSymbolName << "' not found";
54 return signalPassFailure();
55 }
56
57 // Get tile and extract col/row
58 auto tile = configOp.getTile();
59 auto tileOp = dyn_cast<TileOp>(tile.getDefiningOp());
60 if (!tileOp) {
61 startConfig.emitError("tile operand must be a TileOp");
62 return signalPassFailure();
63 }
64
65 int col = tileOp.getCol();
66 int row = tileOp.getRow();
67 TileID tileID = {col, row};
68
69 // Determine if we're accessing memory module from packet type
70 bool isMem = false;
71 if (auto packetType = configOp.getPacketType()) {
72 isMem = (*packetType == TracePacketType::Mem);
73 }
74
75 // Process all trace.reg operations in the config
76 for (auto &op : configOp.getBody().getOps()) {
77 auto regOp = dyn_cast<TraceRegOp>(op);
78 if (!regOp)
79 continue;
80
81 // After packing, field should not be present
82 if (regOp.getField()) {
83 regOp.emitError("aie.trace.reg still has field attribute - run "
84 "-aie-trace-pack-reg-writes pass first");
85 return signalPassFailure();
86 }
87
88 // Look up register to get offset
89 auto regName = regOp.getRegName().str();
90 const RegisterInfo *regInfo =
91 targetModel.lookupRegister(regName, tileID, isMem);
92 if (!regInfo) {
93 regOp.emitError("Register '") << regName << "' not found for tile ("
94 << col << ", " << row << ")";
95 return signalPassFailure();
96 }
97
98 // Extract value (mask is discarded)
99 uint32_t value = 0;
100 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(regOp.getValue())) {
101 value = intAttr.getInt();
102 } else {
103 regOp.emitError("value must be an integer after packing");
104 return signalPassFailure();
105 }
106
107 // Generate aiex.npu.write32 operation with col/row
108 AIEX::NpuWrite32Op::create(
109 builder, regOp.getLoc(),
110 AIEX::createConstantI32(builder, regOp.getLoc(), regInfo->offset),
111 AIEX::createConstantI32(builder, regOp.getLoc(), value),
112 nullptr, // buffer
113 builder.getI32IntegerAttr(col), // column
114 builder.getI32IntegerAttr(row) // row
115 );
116 }
117
118 // Remove the start_config invocation
119 startConfig.erase();
120 }
121 }
122};
123
124} // namespace
125
126std::unique_ptr<OperationPass<AIE::DeviceOp>>
128 return std::make_unique<AIEInlineTraceConfigPass>();
129}
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIEXInlineTraceConfigPass()
mlir::Value createConstantI32(mlir::OpBuilder &builder, mlir::Location loc, uint32_t value)
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