MLIR-AIE 1.4.5.dev
AIECtrlPacketToDma.cpp
Go to the documentation of this file.
1//===- AIECtrlPacketToDma.cpp -----------------------------------*- C++ -*-===//
2//
3// Copyright (C) 2024 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
12
13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/IR/Attributes.h"
15#include "mlir/IR/IRMapping.h"
16#include "mlir/Pass/Pass.h"
17
18#include "llvm/ADT/TypeSwitch.h"
19
20namespace xilinx::AIEX {
21#define GEN_PASS_DEF_AIECTRLPACKETTODMA
22#define GEN_PASS_DEF_AIECTRLPACKETINFERTILES
23#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
24} // namespace xilinx::AIEX
25
26#define DEBUG_TYPE "aie-ctrl-packet-to-dma"
27
28using namespace mlir;
29using namespace xilinx;
30using namespace xilinx::AIE;
31using namespace xilinx::AIEX;
32
35 AIECtrlPacketInferTilesPass> {
36 void runOnOperation() override {
37 DeviceOp device = getOperation();
38 const auto &targetModel = device.getTargetModel();
39 OpBuilder devBuilder = OpBuilder::atBlockBegin(device.getBody());
40
41 auto sequenceOps = device.getOps<AIE::RuntimeSequenceOp>();
42 for (auto f : sequenceOps) {
43 auto ctrlPktOps = f.getOps<AIEX::NpuControlPacketOp>();
44 for (auto ctrlPktOp : ctrlPktOps) {
45 auto tOp = TileOp::getOrCreate(devBuilder, device,
46 (int)ctrlPktOp.getColumnFromAddr(),
47 (int)ctrlPktOp.getRowFromAddr());
48 // Assign controller id
49 auto tileIDMap = getTileToControllerIdMap(true, targetModel);
50 if (tOp->hasAttr("controller_id"))
51 continue;
52 auto pktInfoAttr = AIE::PacketInfoAttr::get(
53 tOp->getContext(), /*pkt_type*/ 0,
54 /*pkt_id*/ tileIDMap[{tOp.colIndex(), tOp.rowIndex()}]);
55 tOp->setAttr("controller_id", pktInfoAttr);
56 }
57 }
58 }
59};
60
62 : xilinx::AIEX::impl::AIECtrlPacketToDmaBase<AIECtrlPacketToDmaPass> {
63 void runOnOperation() override {
64 DeviceOp device = getOperation();
65 const auto &targetModel = device.getTargetModel();
66 auto *ctx = device->getContext();
67 auto loc = device->getLoc();
68
69 if (targetModel.getTargetArch() == AIEArch::AIE1)
70 return; // Disable this pass for AIE1; AIE1 support NYI.
71
72 SmallVector<Operation *> erased;
73 auto sequenceOps = device.getOps<AIE::RuntimeSequenceOp>();
74 for (auto f : sequenceOps) {
75
76 auto controlPacketOps = f.getOps<AIEX::NpuControlPacketOp>();
77 if (controlPacketOps.empty())
78 continue;
79
80 OpBuilder builder(f);
81
82 IRMapping mapping;
83
84 auto newSeq = AIE::RuntimeSequenceOp::create(
85 builder, loc, f.getSymNameAttr(), BoolAttr{}, f.getTraceBufferAttr(),
86 f.getTraceSlicesAttr(), f.getScratchpadParametersAttr());
87 newSeq.getBody().push_back(new Block);
88
89 // Copy the arguments from the old sequence to the new one.
90 for (auto arg : f.getBody().getArguments()) {
91 // Add the argument to the new sequence.
92 auto newArg = newSeq.getBody().addArgument(arg.getType(), arg.getLoc());
93 // Replace all uses of the old argument with the new one.
94 arg.replaceAllUsesWith(newArg);
95 // Add the mapping for the argument.
96 mapping.map(arg, newArg);
97 }
98
99 // Using dynamic shape for ctrl pkt stream.
100 auto ctrlPktMemrefType = MemRefType::get(
101 ShapedType::kDynamic, IntegerType::get(ctx, 32), nullptr, nullptr);
102 auto newBlockArg = newSeq.getBody().addArgument(ctrlPktMemrefType, loc);
103
104 builder.setInsertionPointToStart(&newSeq.getBody().front());
105
106 // Collect all npu.control_packet ops, grouped by location in 'batches'
107 struct BatchInfo {
108 TileID tileId;
109 int64_t startOffset;
110 int64_t totalSize;
111 std::string shimDmaAllocName;
112 int shimChan;
113 Operation *first;
114 };
115 std::vector<BatchInfo> batches;
116
117 int64_t ddrOffset = 0;
118 Block &entry = f.getBody().front();
119
120 // First pass: collect and batch control packet operations
121 bool new_batch = true;
122 for (Operation &o : entry) {
123 auto ctrlPktOp = dyn_cast<NpuControlPacketOp>(&o);
124
125 // A non-control_packet op ends the current batch
126 if (!ctrlPktOp) {
127 new_batch = true;
128 continue;
129 }
130 int col = ctrlPktOp.getColumnFromAddr();
131 int row = ctrlPktOp.getRowFromAddr();
132
133 // Calculate control packet size
134 int64_t ctrlPktSize = 0;
135 auto data = ctrlPktOp.getData();
136 auto length = ctrlPktOp.getLength();
137 if (data)
138 ctrlPktSize = data->size();
139 else if (length)
140 ctrlPktSize = *length;
141 ctrlPktSize++; // Ctrl info word
142 ctrlPktSize++; // Packet header
143
144 // Check if we can batch with the previous packet
145 if (targetModel.getTargetArch() == AIEArch::AIE2p && !new_batch &&
146 batches.back().tileId == TileID{col, row}) {
147 // Add to existing batch
148 batches.back().totalSize += ctrlPktSize;
149 } else {
150 // Start new batch
151 auto rowToShimChanMap =
152 getRowToShimChanMap(targetModel, WireBundle::DMA);
153 int shimChan = rowToShimChanMap[row];
154
155 std::string shimDmaAllocName = "ctrlpkt";
156 shimDmaAllocName += "_col" + std::to_string(col);
157 shimDmaAllocName += "_mm2s";
158 shimDmaAllocName += "_chan" + std::to_string(shimChan);
159
160 batches.push_back({TileID{col, row}, ddrOffset, ctrlPktSize,
161 shimDmaAllocName, shimChan, &o});
162 new_batch = false;
163 }
164 ddrOffset += ctrlPktSize;
165 }
166
167 // Second pass: emit batched operations in original order
168 auto batchIt = batches.begin();
169
170 for (Operation &o : entry) {
171 auto ctrlPktOp = dyn_cast<NpuControlPacketOp>(&o);
172 if (!ctrlPktOp) {
173 builder.clone(o, mapping);
174 continue;
175 }
176
177 // There are no more control packet batches to emit
178 if (batchIt == batches.end())
179 continue;
180
181 // Check if this is the first packet of a new batch, otherwise skip it.
182 if (batchIt->first != &o)
183 continue;
184
185 int col = ctrlPktOp.getColumnFromAddr();
186
187 // Emit the batched DMA operation for this (col, row) pair
188 const std::vector<int64_t> staticOffsets = {0, 0, 0,
189 batchIt->startOffset};
190 const std::vector<int64_t> staticSizes = {1, 1, 1, batchIt->totalSize};
191 const std::vector<int64_t> staticStrides = {0, 0, 0, 1};
192
193 SymbolRefAttr metadata =
194 SymbolRefAttr::get(builder.getContext(), batchIt->shimDmaAllocName);
195 NpuDmaMemcpyNdOp::create(builder, loc, newBlockArg, staticOffsets,
196 staticSizes, staticStrides, metadata,
197 /*id=*/0, /*issue_token=*/true);
198
199 Value shimRow = AIEX::createConstantI32(builder, loc, 0);
200 Value shimCol = AIEX::createConstantI32(builder, loc, col);
201 Value dir = AIEX::createConstantI32(builder, loc, 1); // MM2S
202 Value chan = AIEX::createConstantI32(builder, loc, batchIt->shimChan);
203 Value col_num = AIEX::createConstantI32(builder, loc, 1);
204 Value row_num = AIEX::createConstantI32(builder, loc, 1);
205 AIEX::NpuSyncOp::create(builder, loc, shimCol, shimRow, dir, chan,
206 col_num, row_num);
207 ++batchIt;
208 }
209
210 erased.push_back(f);
211 }
212
213 for (auto *e : erased)
214 e->erase();
215 }
216};
217
218std::unique_ptr<OperationPass<DeviceOp>>
220 return std::make_unique<AIECtrlPacketInferTilesPass>();
221}
222std::unique_ptr<OperationPass<DeviceOp>> AIEX::createAIECtrlPacketToDmaPass() {
223 return std::make_unique<AIECtrlPacketToDmaPass>();
224}
DenseMap< int, int > getRowToShimChanMap(const AIETargetModel &targetModel, WireBundle bundle)
DenseMap< TileID, int > getTileToControllerIdMap(bool clColumnWiseUniqueIDs, const AIETargetModel &targetModel)
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIECtrlPacketInferTilesPass()
std::unique_ptr< mlir::OperationPass< AIE::DeviceOp > > createAIECtrlPacketToDmaPass()
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