MLIR-AIE
AIECtrlPacketToDma.cpp
Go to the documentation of this file.
1//===- AIECtrlPacketToDma.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 2024 Advanced Micro Devices Inc.
8//
9//===----------------------------------------------------------------------===//
10
15
16#include "mlir/IR/Attributes.h"
17#include "mlir/IR/IRMapping.h"
18#include "mlir/Pass/Pass.h"
19
20#include "llvm/ADT/TypeSwitch.h"
21
22namespace xilinx::AIEX {
23#define GEN_PASS_DEF_AIECTRLPACKETTODMA
24#define GEN_PASS_DEF_AIECTRLPACKETINFERTILES
25#include "aie/Dialect/AIEX/Transforms/AIEXPasses.h.inc"
26} // namespace xilinx::AIEX
27
28#define DEBUG_TYPE "aie-ctrl-packet-to-dma"
29
30using namespace mlir;
31using namespace xilinx;
32using namespace xilinx::AIE;
33using namespace xilinx::AIEX;
34
37 AIECtrlPacketInferTilesPass> {
38 void runOnOperation() override {
39 DeviceOp device = getOperation();
40 const auto &targetModel = device.getTargetModel();
41 OpBuilder devBuilder = OpBuilder::atBlockBegin(device.getBody());
42
43 auto sequenceOps = device.getOps<AIE::RuntimeSequenceOp>();
44 for (auto f : sequenceOps) {
45 auto ctrlPktOps = f.getOps<AIEX::NpuControlPacketOp>();
46 for (auto ctrlPktOp : ctrlPktOps) {
47 auto tOp = TileOp::getOrCreate(devBuilder, device,
48 (int)ctrlPktOp.getColumnFromAddr(),
49 (int)ctrlPktOp.getRowFromAddr());
50 // Assign controller id
51 auto tileIDMap = getTileToControllerIdMap(true, targetModel);
52 if (tOp->hasAttr("controller_id"))
53 continue;
54 auto pktInfoAttr = AIE::PacketInfoAttr::get(
55 tOp->getContext(), /*pkt_type*/ 0,
56 /*pkt_id*/ tileIDMap[{tOp.colIndex(), tOp.rowIndex()}]);
57 tOp->setAttr("controller_id", pktInfoAttr);
58 }
59 }
60 }
61};
62
64 : xilinx::AIEX::impl::AIECtrlPacketToDmaBase<AIECtrlPacketToDmaPass> {
65 void runOnOperation() override {
66 DeviceOp device = getOperation();
67 const auto &targetModel = device.getTargetModel();
68 auto ctx = device->getContext();
69 auto loc = device->getLoc();
70
71 if (targetModel.getTargetArch() == AIEArch::AIE1)
72 return; // Disable this pass for AIE1; AIE1 support NYI.
73
74 SmallVector<Operation *> erased;
75 auto sequenceOps = device.getOps<AIE::RuntimeSequenceOp>();
76 for (auto f : sequenceOps) {
77
78 auto controlPacketOps = f.getOps<AIEX::NpuControlPacketOp>();
79 if (controlPacketOps.empty())
80 continue;
81
82 OpBuilder builder(f);
83
84 IRMapping mapping;
85
86 auto newSeq =
87 AIE::RuntimeSequenceOp::create(builder, loc, f.getSymNameAttr());
88 newSeq.getBody().push_back(new Block);
89
90 // Copy the arguments from the old sequence to the new one.
91 for (auto arg : f.getBody().getArguments()) {
92 // Add the argument to the new sequence.
93 auto newArg = newSeq.getBody().addArgument(arg.getType(), arg.getLoc());
94 // Replace all uses of the old argument with the new one.
95 arg.replaceAllUsesWith(newArg);
96 // Add the mapping for the argument.
97 mapping.map(arg, newArg);
98 }
99
100 // Using dynamic shape for ctrl pkt stream.
101 auto ctrlPktMemrefType = MemRefType::get(
102 ShapedType::kDynamic, IntegerType::get(ctx, 32), nullptr, 0);
103 auto newBlockArg = newSeq.getBody().addArgument(ctrlPktMemrefType, loc);
104
105 builder.setInsertionPointToStart(&newSeq.getBody().front());
106
107 // Collect all npu.control_packet ops, grouped by location in 'batches'
108 struct BatchInfo {
109 TileID tileId;
110 int64_t startOffset;
111 int64_t totalSize;
112 std::string shimDmaAllocName;
113 int shimChan;
114 Operation *first;
115 };
116 std::vector<BatchInfo> batches;
117
118 int64_t ddrOffset = 0;
119 Block &entry = f.getBody().front();
120
121 // First pass: collect and batch control packet operations
122 bool new_batch = true;
123 for (Operation &o : entry) {
124 auto ctrlPktOp = dyn_cast<NpuControlPacketOp>(&o);
125
126 // A non-control_packet op ends the current batch
127 if (!ctrlPktOp) {
128 new_batch = true;
129 continue;
130 }
131 int col = ctrlPktOp.getColumnFromAddr();
132 int row = ctrlPktOp.getRowFromAddr();
133
134 // Calculate control packet size
135 int64_t ctrlPktSize = 0;
136 auto data = ctrlPktOp.getData();
137 if (data)
138 ctrlPktSize = data->size();
139 else if (ctrlPktOp.getLength())
140 ctrlPktSize = *ctrlPktOp.getLength();
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, builder.getUnknownLoc(), newBlockArg,
196 SmallVector<Value>{}, SmallVector<Value>{},
197 SmallVector<Value>{}, ArrayRef(staticOffsets),
198 ArrayRef(staticSizes), ArrayRef(staticStrides),
199 nullptr, metadata, 0, true, 0, 0, 0, 0, 0, 0);
200
201 auto shimRow = builder.getI32IntegerAttr(0);
202 auto shimCol = builder.getI32IntegerAttr(col);
203 auto dir = builder.getI32IntegerAttr(1); // MM2S
204 auto chan = builder.getI32IntegerAttr(batchIt->shimChan);
205 auto col_num = builder.getI32IntegerAttr(1);
206 auto row_num = builder.getI32IntegerAttr(1);
207 AIEX::NpuSyncOp::create(builder, loc, shimCol, shimRow, dir, chan,
208 col_num, row_num);
209 ++batchIt;
210 }
211
212 erased.push_back(f);
213 }
214
215 for (auto e : erased)
216 e->erase();
217 }
218};
219
220std::unique_ptr<OperationPass<DeviceOp>>
222 return std::make_unique<AIECtrlPacketInferTilesPass>();
223}
224std::unique_ptr<OperationPass<DeviceOp>> AIEX::createAIECtrlPacketToDmaPass() {
225 return std::make_unique<AIECtrlPacketToDmaPass>();
226}
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()
Include the generated interface declarations.
TileID { friend std::ostream &operator<<(std::ostream &os, const TileID &s) { os<< "TileID("<< s.col<< ", "<< s.row<< ")" TileID