16#include "mlir/Dialect/Arith/IR/Arith.h"
17#include "mlir/Dialect/MemRef/IR/MemRef.h"
18#include "mlir/IR/IRMapping.h"
20#include "llvm/ADT/APInt.h"
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/Support/Debug.h"
25#include "xaiengine/xaiegbl.h"
28#include "xaiengine/xaie_txn.h"
38#define GEN_PASS_DEF_CONVERTAIETOCONTROLPACKETS
39#define GEN_PASS_DEF_CONVERTAIETOTRANSACTION
40#include "aie/Conversion/Passes.h.inc"
43#define DEBUG_TYPE "aie-convert-to-config"
54struct TransactionBinaryOperation {
55 struct XAie_TxnCmd cmd = {};
66 struct LoadPdiPayload {
72 struct AddressPatchPayload {
80 std::optional<SyncPayload> sync;
81 std::optional<LoadPdiPayload> loadPdi;
82 std::optional<AddressPatchPayload> addressPatch;
83 std::optional<mlir::Location> sourceLoc;
85 TransactionBinaryOperation() =
default;
87 TransactionBinaryOperation(XAie_TxnOpcode opc, uint32_t mask, uint64_t addr,
88 uint32_t value,
const uint8_t *data,
94 cmd.DataPtr =
reinterpret_cast<uint64_t
>(data);
99constexpr size_t kTxnHeaderBytes = 16;
101struct TxnPreemptHeader {
107struct TxnLoadPdiHeader {
118static std::optional<int>
119parseTransactionBinary(
const std::vector<uint8_t> &data,
120 std::vector<TransactionBinaryOperation> &ops) {
122 if (data.size() < kTxnHeaderBytes) {
123 llvm::errs() <<
"Transaction binary is too small for header\n";
127 uint32_t major = data[0];
128 uint32_t minor = data[1];
129 uint32_t num_cols = data[4];
131 uint32_t num_ops, txn_size;
132 std::memcpy(&num_ops, &data[8], 4);
133 std::memcpy(&txn_size, &data[12], 4);
135 LLVM_DEBUG(llvm::dbgs() <<
"Major: " << major <<
"\n");
136 LLVM_DEBUG(llvm::dbgs() <<
"Minor: " << minor <<
"\n");
137 LLVM_DEBUG(llvm::dbgs() <<
"DevGen: " << data[2] <<
"\n");
138 LLVM_DEBUG(llvm::dbgs() <<
"NumRows: " << data[3] <<
"\n");
139 LLVM_DEBUG(llvm::dbgs() <<
"NumCols: " << num_cols <<
"\n");
140 LLVM_DEBUG(llvm::dbgs() <<
"NumMemTileRows: " << data[5] <<
"\n");
141 LLVM_DEBUG(llvm::dbgs() <<
"NumOps: " << num_ops <<
"\n");
142 LLVM_DEBUG(llvm::dbgs() <<
"TxnSize: " << txn_size <<
" bytes\n");
144 size_t i = kTxnHeaderBytes;
146 auto requireBytes = [&](
size_t offset,
size_t length) ->
bool {
147 if (offset + length > data.size()) {
148 llvm::errs() <<
"Transaction binary truncated while parsing opcode\n";
154 auto read32 = [&](
size_t offset) -> uint32_t {
156 std::memcpy(&value, data.data() + offset,
sizeof(uint32_t));
161 auto convertOpcode = [](uint8_t opc) -> std::optional<uint8_t> {
163 case static_cast<uint8_t
>(XAie_TxnOpcode::XAIE_IO_WRITE):
164 case static_cast<uint8_t>(XAie_TxnOpcode::XAIE_IO_BLOCKWRITE):
165 case static_cast<uint8_t>(XAie_TxnOpcode::XAIE_IO_MASKWRITE):
168 case static_cast<uint8_t>(XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT):
169 case static_cast<uint8_t>(XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH):
172 llvm::errs() <<
"Unhandled opcode: " << std::to_string(opc) <<
"\n";
180 if (major == 0 && minor == 1) {
181 while (i < data.size()) {
182 auto maybeOpcode = convertOpcode(data[i]);
185 XAie_TxnOpcode opcode =
static_cast<XAie_TxnOpcode
>(*maybeOpcode);
186 LLVM_DEBUG(llvm::dbgs() <<
"opcode: " + std::to_string(opcode) <<
"\n");
188 TransactionBinaryOperation op;
189 op.cmd.Opcode = opcode;
192 case XAie_TxnOpcode::XAIE_IO_WRITE: {
193 LLVM_DEBUG(llvm::dbgs() <<
"opcode: WRITE (0x00)\n");
194 if (!requireBytes(i, 24))
196 uint32_t addrLo = read32(i + 8);
197 uint32_t addrHi = read32(i + 12);
198 uint32_t value = read32(i + 16);
199 uint32_t opSize = read32(i + 20);
200 if (!requireBytes(i, opSize))
202 uint64_t addr = (
static_cast<uint64_t
>(addrHi) << 32) | addrLo;
203 op.cmd.RegOff = addr;
204 op.cmd.Value = value;
209 case XAie_TxnOpcode::XAIE_IO_BLOCKWRITE: {
210 LLVM_DEBUG(llvm::dbgs() <<
"opcode: BLOCKWRITE (0x01)\n");
211 if (!requireBytes(i, 16))
213 uint32_t addr = read32(i + 8);
214 uint32_t opSize = read32(i + 12);
215 if (opSize < 16 || !requireBytes(i, opSize))
217 const uint8_t *payload = data.data() + i + 16;
218 uint32_t payloadBytes = opSize - 16;
219 op.cmd.RegOff = addr;
220 op.cmd.DataPtr =
reinterpret_cast<uint64_t
>(payload);
221 op.cmd.Size = payloadBytes;
225 case XAie_TxnOpcode::XAIE_IO_MASKWRITE: {
226 LLVM_DEBUG(llvm::dbgs() <<
"opcode: MASKWRITE (0x03)\n");
227 if (!requireBytes(i, 28))
229 uint32_t addrLo = read32(i + 8);
230 uint32_t addrHi = read32(i + 12);
231 uint32_t value = read32(i + 16);
232 uint32_t mask = read32(i + 20);
233 uint32_t opSize = read32(i + 24);
234 if (!requireBytes(i, opSize))
236 uint64_t addr = (
static_cast<uint64_t
>(addrHi) << 32) | addrLo;
237 op.cmd.RegOff = addr;
238 op.cmd.Value = value;
240 op.cmd.Size = opSize;
244 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT: {
245 uint32_t opSize = read32(i + 4);
246 if (opSize < 16 || !requireBytes(i, opSize))
248 uint32_t descriptor = read32(i + 8);
249 uint32_t config = read32(i + 12);
250 TransactionBinaryOperation::SyncPayload payload{
251 static_cast<int32_t
>((descriptor >> 16) & 0xff),
252 static_cast<int32_t
>((descriptor >> 8) & 0xff),
253 static_cast<int32_t
>(descriptor & 0xff),
254 static_cast<int32_t
>((config >> 24) & 0xff),
255 static_cast<int32_t
>((config >> 16) & 0xff),
256 static_cast<int32_t
>((config >> 8) & 0xff)};
258 op.cmd.Size = opSize;
266 LLVM_DEBUG(llvm::dbgs() <<
"opcode: LOAD_PDI (0x08)\n");
267 constexpr size_t opSize =
sizeof(TxnLoadPdiHeader);
268 if (!requireBytes(i, opSize))
270 TxnLoadPdiHeader header;
271 std::memcpy(&header, data.data() + i, opSize);
272 TransactionBinaryOperation::LoadPdiPayload payload{
273 header.id, header.size, header.address};
274 op.loadPdi = payload;
275 op.cmd.Size = opSize;
279 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH: {
280 uint32_t opSize = read32(i + 4);
281 if (opSize < 44 || !requireBytes(i, opSize))
283 uint32_t action = read32(i + 20);
284 uint32_t addr = read32(i + 24);
285 int32_t argIdx =
static_cast<int32_t
>(read32(i + 32));
286 uint64_t argPlus =
static_cast<uint64_t
>(read32(i + 40)) |
287 (
static_cast<uint64_t
>(read32(i + 44)) << 32);
288 TransactionBinaryOperation::AddressPatchPayload payload{
289 action, addr, argIdx, argPlus};
290 op.addressPatch = payload;
291 op.cmd.Size = opSize;
296 LLVM_DEBUG(llvm::dbgs() <<
"opcode: PREEMPT (0x06)\n");
297 constexpr size_t opSize =
sizeof(TxnPreemptHeader);
298 if (!requireBytes(i, opSize))
301 reinterpret_cast<const TxnPreemptHeader *
>(data.data() + i);
302 op.cmd.Value = header->level;
303 op.cmd.Size = opSize;
308 llvm::errs() <<
"Unhandled opcode: " << std::to_string(opcode)
309 <<
" for v0.1 transaction\n";
315 }
else if (major == 1 && minor == 0) {
316 while (i < data.size()) {
317 auto maybeOpcode = convertOpcode(data[i]);
320 XAie_TxnOpcode opcode =
static_cast<XAie_TxnOpcode
>(*maybeOpcode);
321 LLVM_DEBUG(llvm::dbgs() <<
"opcode: " + std::to_string(opcode) <<
"\n");
323 TransactionBinaryOperation op;
324 op.cmd.Opcode = opcode;
327 case XAie_TxnOpcode::XAIE_IO_WRITE: {
328 LLVM_DEBUG(llvm::dbgs() <<
"opcode: WRITE (0x00)\n");
329 if (!requireBytes(i, 12))
331 uint32_t addr = read32(i + 4);
332 uint32_t value = read32(i + 8);
333 op.cmd.RegOff = addr;
334 op.cmd.Value = value;
339 case XAie_TxnOpcode::XAIE_IO_BLOCKWRITE: {
340 LLVM_DEBUG(llvm::dbgs() <<
"opcode: BLOCKWRITE (0x01)\n");
341 if (!requireBytes(i, 12))
343 uint32_t addr = read32(i + 4);
344 uint32_t opSize = read32(i + 8);
345 if (opSize < 12 || !requireBytes(i, opSize))
347 const uint8_t *payload = data.data() + i + 12;
348 uint32_t payloadBytes = opSize - 12;
349 op.cmd.RegOff = addr;
350 op.cmd.DataPtr =
reinterpret_cast<uint64_t
>(payload);
351 op.cmd.Size = payloadBytes;
355 case XAie_TxnOpcode::XAIE_IO_MASKWRITE: {
356 LLVM_DEBUG(llvm::dbgs() <<
"opcode: MASKWRITE (0x03)\n");
357 if (!requireBytes(i, 16))
359 uint32_t addr = read32(i + 4);
360 uint32_t value = read32(i + 8);
361 uint32_t mask = read32(i + 12);
362 op.cmd.RegOff = addr;
363 op.cmd.Value = value;
369 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT: {
370 uint32_t opSize = read32(i + 4);
371 if (opSize < 16 || !requireBytes(i, opSize))
373 uint32_t descriptor = read32(i + 8);
374 uint32_t config = read32(i + 12);
375 TransactionBinaryOperation::SyncPayload payload{
376 static_cast<int32_t
>((descriptor >> 16) & 0xff),
377 static_cast<int32_t
>((descriptor >> 8) & 0xff),
378 static_cast<int32_t
>(descriptor & 0xff),
379 static_cast<int32_t
>((config >> 24) & 0xff),
380 static_cast<int32_t
>((config >> 16) & 0xff),
381 static_cast<int32_t
>((config >> 8) & 0xff)};
383 op.cmd.Size = opSize;
391 LLVM_DEBUG(llvm::dbgs() <<
"opcode: LOAD_PDI (0x08)\n");
392 constexpr size_t opSize =
sizeof(TxnLoadPdiHeader);
393 if (!requireBytes(i, opSize))
395 TxnLoadPdiHeader header;
396 std::memcpy(&header, data.data() + i, opSize);
397 TransactionBinaryOperation::LoadPdiPayload payload{
398 header.id, header.size, header.address};
399 op.loadPdi = payload;
400 op.cmd.Size = opSize;
404 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH: {
405 uint32_t opSize = read32(i + 4);
406 if (opSize < 44 || !requireBytes(i, opSize))
408 uint32_t action = read32(i + 20);
409 uint32_t addr = read32(i + 24);
410 int32_t argIdx =
static_cast<int32_t
>(read32(i + 32));
411 uint64_t argPlus =
static_cast<uint64_t
>(read32(i + 40)) |
412 (
static_cast<uint64_t
>(read32(i + 44)) << 32);
413 TransactionBinaryOperation::AddressPatchPayload payload{
414 action, addr, argIdx, argPlus};
415 op.addressPatch = payload;
416 op.cmd.Size = opSize;
421 LLVM_DEBUG(llvm::dbgs() <<
"opcode: PREEMPT (0x06)\n");
422 constexpr size_t opSize =
sizeof(TxnPreemptHeader);
423 if (!requireBytes(i, opSize))
426 reinterpret_cast<const TxnPreemptHeader *
>(data.data() + i);
427 op.cmd.Value = header->level;
428 op.cmd.Size = opSize;
433 llvm::errs() <<
"Unhandled opcode: " << std::to_string(opcode)
434 <<
" for v1.0 transaction\n";
441 llvm::errs() <<
"Unsupported TXN binary version: " << major <<
"." << minor
449static LogicalResult generateTransactions(
AIERTControl &ctl,
450 const StringRef workDirPath,
451 DeviceOp &targetOp,
bool aieSim,
452 bool enableElfs,
bool enableInit,
454 bool skipCtrlPktOverlay =
false) {
455 if (enableElfs && !targetOp.getOps<CoreOp>().empty() &&
456 failed(ctl.
addAieElfs(targetOp, workDirPath, aieSim)))
458 if (enableInit && failed(ctl.
addInitConfig(targetOp, skipCtrlPktOverlay)))
460 if (enableCores && !targetOp.getOps<CoreOp>().empty() &&
471emitTransactionOps(OpBuilder &builder, Location fallbackLoc,
472 std::vector<TransactionBinaryOperation> &operations,
473 std::vector<memref::GlobalOp> &global_data) {
482 llvm::DenseMap<uint32_t, Value> constantCache;
483 auto constant = [&](Location loc, uint32_t value) {
484 Value &cached = constantCache[value];
491 for (
auto [op, payload] : llvm::zip(operations, global_data)) {
492 Location loc = op.sourceLoc.value_or(fallbackLoc);
494 if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_WRITE) {
495 Value address = constant(loc, op.cmd.RegOff);
496 Value value = constant(loc, op.cmd.Value);
497 AIEX::NpuWrite32Op::create(builder, loc, address, value,
nullptr,
nullptr,
499 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_BLOCKWRITE) {
500 auto memref = memref::GetGlobalOp::create(builder, loc, payload.getType(),
502 AIEX::NpuBlockWriteOp::create(
503 builder, loc, builder.getUI32IntegerAttr(op.cmd.RegOff),
504 memref.getResult(),
nullptr,
nullptr,
nullptr);
505 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_MASKWRITE) {
506 Value address = constant(loc, op.cmd.RegOff);
507 Value value = constant(loc, op.cmd.Value);
508 Value mask = constant(loc, op.cmd.Mask);
509 AIEX::NpuMaskWrite32Op::create(builder, loc, address, value, mask,
510 nullptr,
nullptr,
nullptr);
511 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT) {
513 llvm::errs() <<
"Missing sync payload while emitting transaction\n";
516 const TransactionBinaryOperation::SyncPayload &sync = *op.sync;
517 Value column = constant(loc, sync.column);
518 Value row = constant(loc, sync.row);
519 Value direction = constant(loc, sync.direction);
520 Value channel = constant(loc, sync.channel);
521 Value columnCount = constant(loc, sync.columnCount);
522 Value rowCount = constant(loc, sync.rowCount);
523 AIEX::NpuSyncOp::create(builder, loc, column, row, direction, channel,
524 columnCount, rowCount);
525 }
else if (op.cmd.Opcode == 0x8 ) {
527 llvm::errs() <<
"Missing load_pdi payload while emitting transaction\n";
530 const TransactionBinaryOperation::LoadPdiPayload &payloadInfo =
533 builder.getI32IntegerAttr(
static_cast<int32_t
>(payloadInfo.id));
534 IntegerAttr sizeAttr =
535 builder.getI32IntegerAttr(
static_cast<int32_t
>(payloadInfo.size));
538 IntegerType::get(builder.getContext(), 64, IntegerType::Unsigned);
539 IntegerAttr addressAttr =
540 IntegerAttr::get(ui64Ty, llvm::APInt(64, payloadInfo.address));
542 AIEX::NpuLoadPdiOp::create(builder, loc,
nullptr, idAttr,
543 sizeAttr, addressAttr,
545 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH) {
546 if (!op.addressPatch) {
548 <<
"Missing address_patch payload while emitting transaction\n";
551 const TransactionBinaryOperation::AddressPatchPayload &patch =
553 AIEX::NpuAddressPatchOp::create(
554 builder, loc, patch.addr,
555 mlir::Value(),
static_cast<int32_t
>(patch.argIdx),
557 }
else if (op.cmd.Opcode == 0x6 ) {
559 IntegerType::get(builder.getContext(), 8, IntegerType::Unsigned);
560 auto levelAttr = IntegerAttr::get(ui8Ty, llvm::APInt(8, op.cmd.Value));
561 AIEX::NpuPreemptOp::create(builder, loc, levelAttr);
563 llvm::errs() <<
"Unhandled txn opcode: " << op.cmd.Opcode <<
"\n";
575emitControlPacketOps(OpBuilder &builder, Location fallbackLoc,
576 std::vector<TransactionBinaryOperation> &operations,
577 std::vector<memref::GlobalOp> &global_data) {
579 auto *ctx = builder.getContext();
582 for (
auto [op, payload] : llvm::zip(operations, global_data)) {
583 Location loc = op.sourceLoc.value_or(fallbackLoc);
585 if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_WRITE) {
586 AIEX::NpuControlPacketOp::create(
587 builder, loc, builder.getUI32IntegerAttr(op.cmd.RegOff),
nullptr,
588 builder.getI32IntegerAttr(0),
589 builder.getI32IntegerAttr(0),
590 DenseI32ArrayAttr::get(ctx, ArrayRef<int32_t>(op.cmd.Value)));
591 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_BLOCKWRITE) {
592 auto initialValue = payload.getInitialValue();
595 auto blockWriteData = dyn_cast<DenseIntElementsAttr>(*initialValue);
596 if (!blockWriteData) {
598 "Global symbol initial value is not a dense int array");
601 auto blockWriteDataValues = blockWriteData.getValues<int32_t>();
603 int currAddr = op.cmd.RegOff;
604 for (
size_t i = 0; i < blockWriteDataValues.size(); i += 4) {
605 auto last = std::min(blockWriteDataValues.size(), i + 4);
606 SmallVector<int32_t> splitData =
607 SmallVector<int32_t>(blockWriteDataValues.begin() + i,
608 blockWriteDataValues.begin() + last);
609 AIEX::NpuControlPacketOp::create(
610 builder, loc, builder.getUI32IntegerAttr(currAddr),
nullptr,
611 builder.getI32IntegerAttr(0),
612 builder.getI32IntegerAttr(0),
613 DenseI32ArrayAttr::get(ctx, ArrayRef<int32_t>(splitData)));
614 currAddr += splitData.size() *
sizeof(int32_t);
617 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_MASKWRITE) {
618 AIEX::NpuControlPacketOp::create(
619 builder, loc, builder.getUI32IntegerAttr(op.cmd.RegOff),
nullptr,
620 builder.getI32IntegerAttr(0),
621 builder.getI32IntegerAttr(0),
622 DenseI32ArrayAttr::get(ctx, ArrayRef<int32_t>(op.cmd.Value)));
624 llvm::errs() <<
"Unhandled txn opcode: " << op.cmd.Opcode <<
"\n";
633static LogicalResult orConsecutiveWritesOnSameAddr(Block *body) {
634 SmallVector<AIEX::NpuControlPacketOp> ctrlPktOps;
636 [&](AIEX::NpuControlPacketOp cpOp) { ctrlPktOps.push_back(cpOp); });
637 if (ctrlPktOps.empty())
640 SmallVector<Operation *> erased;
641 int addrBuffer = ctrlPktOps[0].getAddress();
642 AIEX::NpuControlPacketOp ctrlPktBuffer = ctrlPktOps[0];
643 for (
size_t i = 1; i < ctrlPktOps.size(); i++) {
644 int currentAddrBuffer = ctrlPktOps[i].getAddress();
645 if (addrBuffer != currentAddrBuffer) {
646 addrBuffer = currentAddrBuffer;
647 ctrlPktBuffer = ctrlPktOps[i];
650 auto bufferedDataAttr = ctrlPktBuffer.getData();
651 auto currentDataAttr = ctrlPktOps[i].getData();
652 if (!bufferedDataAttr || !currentDataAttr) {
653 ctrlPktOps[i].emitError(
654 "cannot OR consecutive control packets on the same address: "
655 "control packet has no data payload");
658 auto bufferedData = *bufferedDataAttr;
659 auto currentData = *currentDataAttr;
660 SmallVector<int> newData;
661 for (
unsigned j = 0; j < std::max(bufferedData.size(), currentData.size());
663 if (j < std::min(bufferedData.size(), currentData.size())) {
664 newData.push_back(bufferedData[j] | currentData[j]);
667 newData.push_back(j < bufferedData.size() ? bufferedData[j]
670 ctrlPktBuffer.getProperties().data = DenseI32ArrayAttr::get(
671 ctrlPktBuffer->getContext(), ArrayRef<int>{newData});
672 erased.push_back(ctrlPktOps[i]);
675 for (
auto *e : erased)
684convertTransactionOpsToMLIR(OpBuilder builder,
686 std::vector<TransactionBinaryOperation> &operations,
691 std::vector<memref::GlobalOp> global_data;
692 DeviceOp device = blockwriteData.
getDevice();
693 Location loc = device.getLoc();
695 OpBuilder::InsertionGuard guard(builder);
696 builder.setInsertionPointToStart(device.getBody());
697 for (
auto &op : operations) {
698 if (op.cmd.Opcode != XAIE_IO_BLOCKWRITE) {
699 global_data.emplace_back(
nullptr);
702 const uint32_t *d =
reinterpret_cast<const uint32_t *
>(op.cmd.DataPtr);
704 builder, loc, ArrayRef<uint32_t>(d, op.cmd.Size / 4)));
710 if (failed(emitTransactionOps(builder, loc, operations, global_data)))
713 if (failed(emitControlPacketOps(builder, loc, operations, global_data)))
716 if (failed(orConsecutiveWritesOnSameAddr(builder.getBlock())))
719 llvm_unreachable(
"bad output type");
729std::optional<mlir::ModuleOp>
731 std::vector<uint8_t> &binary) {
734 std::vector<TransactionBinaryOperation> operations;
735 auto c = parseTransactionBinary(binary, operations);
737 llvm::errs() <<
"Failed to parse binary\n";
745 ctx->getOrLoadDialect<mlir::arith::ArithDialect>();
747 auto loc = mlir::UnknownLoc::get(ctx);
750 auto module = ModuleOp::create(loc);
751 OpBuilder builder(module.getBodyRegion());
752 builder.setInsertionPointToStart(module.getBody());
755 std::vector<AIEDevice> devices{AIEDevice::npu1_1col, AIEDevice::npu1_2col,
756 AIEDevice::npu1_3col, AIEDevice::npu1};
757 auto device = DeviceOp::create(builder, loc, devices[columns - 1],
758 DeviceOp::getDefaultDeviceName());
759 device.getRegion().emplaceBlock();
760 DeviceOp::ensureTerminator(device.getBodyRegion(), builder, loc);
761 builder.setInsertionPointToStart(device.getBody());
765 if (failed(convertTransactionOpsToMLIR(
775insertRecordedTransaction(
AIERTControl &ctl, OpBuilder &builder,
782 std::vector<TransactionBinaryOperation> operations;
783 if (!parseTransactionBinary(txn_data, operations)) {
784 llvm::errs() <<
"Failed to parse binary\n";
793 const std::vector<mlir::Location> &opLocs = ctl.
getTxnOpLocs();
799 assert((opLocs.empty() || opLocs.size() == operations.size()) &&
800 "txn loc/op count mismatch: AIERTControl's command-to-operation "
801 "projection disagrees with the transaction parser; aie-rt's "
802 "serializer has drifted from projectCmdLocsOntoSerializedOps");
803 for (
size_t i = 0, e = std::min(opLocs.size(), operations.size()); i < e; ++i)
804 if (!isa<UnknownLoc>(opLocs[i]))
805 operations[i].sourceLoc = opLocs[i];
807 return convertTransactionOpsToMLIR(builder, outputType, operations,
812 OpBuilder &builder, xilinx::AIE::DeviceOp device,
822 bool xaieDebug =
false;
831 bool generateElfs =
true;
832 if (failed(generateTransactions(ctl, clElfDir, device, aieSim, generateElfs,
833 true,
true, skipCtrlPktOverlay)))
836 return insertRecordedTransaction(ctl, builder, outputType, blockwriteData);
840 OpBuilder &builder, xilinx::AIE::DeviceOp device,
855 return insertRecordedTransaction(ctl, builder, outputType, blockwriteData);
863 std::array<llvm::DenseMap<DeviceOp, AIEX::BlockwriteData>, 2> blockwriteData;
864 std::array<llvm::DenseMap<std::pair<Operation *, Operation *>,
865 std::unique_ptr<Block>>,
868 struct ConstantPool {
869 Operation *last =
nullptr;
870 llvm::DenseMap<Attribute, Value> values;
872 llvm::DenseMap<Operation *, ConstantPool> pools;
873 for (
auto [at, configured] : sites) {
874 auto device = at->getParentOfType<DeviceOp>();
876 return at->emitError(
"expected to be nested under an aie.device op");
877 bool teardown = isa<AIEX::NpuWriteTeardownOp>(at);
878 std::unique_ptr<Block> &config = configs[teardown][{configured, device}];
881 blockwriteData[teardown]
882 .try_emplace(device, device, teardown ?
"teardown_" :
"loadpdi_")
884 auto generated = std::make_unique<Block>();
885 OpBuilder configBuilder(at->getContext());
886 configBuilder.setInsertionPointToEnd(generated.get());
888 configBuilder, configured, payloads, outputType))
890 configBuilder, configured, payloads,
893 return at->emitError(teardown ?
"Failed to generate teardown operations"
894 :
"Failed to generate configuration "
896 config = std::move(generated);
898 auto seq = at->getParentOfType<RuntimeSequenceOp>();
899 ConstantPool *pool = seq ? &pools[seq] :
nullptr;
900 OpBuilder builder(at);
902 for (Operation &op : *config) {
904 if (
auto constant = dyn_cast<arith::ConstantOp>(op))
905 key = constant.getValue();
906 else if (
auto global = dyn_cast<memref::GetGlobalOp>(op))
907 key = global.getNameAttr();
909 builder.clone(op, mapping);
912 Value &pooled = pool->values[key];
914 OpBuilder::InsertionGuard guard(builder);
916 builder.setInsertionPointAfter(pool->last);
918 builder.setInsertionPointToStart(&seq.getBody().front());
919 pool->last = builder.clone(op);
920 pooled = pool->last->getResult(0);
922 mapping.map(op.getResult(0), pooled);
929 SmallVector<std::pair<Operation *, DeviceOp>> sites;
930 WalkResult walk =
module.walk([&](Operation *write) {
931 FlatSymbolRefAttr ref;
932 if (
auto config = dyn_cast<AIEX::NpuWriteConfigOp>(write))
933 ref = config.getDeviceRefAttr();
934 else if (
auto teardown = dyn_cast<AIEX::NpuWriteTeardownOp>(write))
935 ref = teardown.getDeviceRefAttr();
937 return WalkResult::advance();
938 auto configured =
module.lookupSymbol<DeviceOp>(ref);
940 write->emitOpError() << ref <<
" is not a device";
941 return WalkResult::interrupt();
943 sites.emplace_back(write, configured);
944 return WalkResult::advance();
946 if (walk.wasInterrupted() ||
949 for (
auto [write, configured] : sites)
955convertAIEToConfiguration(AIE::DeviceOp device, StringRef clElfDir,
958 OpBuilder builder(device.getBodyRegion());
963 auto loc = device.getLoc();
964 SmallVector<AIEX::ConfigureOp> configureOps;
965 device.walk([&](AIEX::ConfigureOp op) { configureOps.push_back(op); });
967 if (configureOps.empty()) {
970 std::string seq_name =
"configure";
971 while (device.lookupSymbol(seq_name))
972 seq_name =
"configure" + std::to_string(
id++);
973 StringAttr seq_sym_name = builder.getStringAttr(seq_name);
974 auto seq = AIE::RuntimeSequenceOp::create(
975 builder, loc, seq_sym_name, BoolAttr{}, AIE::TraceBufferAttr{},
976 ArrayAttr{}, ArrayAttr{});
977 seq.getBody().push_back(
new Block);
978 builder.setInsertionPointToStart(&seq.getBody().front());
980 builder.setInsertionPoint(configureOps.front());
986 clElfDir, outputType)))
991 if (!configureOps.empty()) {
993 builder.getBlock()->getOperations().splice(
994 builder.getInsertionPoint(),
995 configureOps.front().getBody().front().getOperations());
996 configureOps.front().erase();
1004template <
typename BaseClass, AIE::AIEToConfigurationOutputType MyOutputType>
1005struct ConvertAIEToConfigurationPass : BaseClass {
1006 std::string &ref_clElfDir;
1007 std::string &ref_clDeviceName;
1008 ConvertAIEToConfigurationPass(std::string &clElfDir,
1009 std::string &clDeviceName)
1010 : ref_clElfDir(clElfDir), ref_clDeviceName(clDeviceName) {}
1012 void getDependentDialects(DialectRegistry ®istry)
const override {
1013 registry.insert<memref::MemRefDialect, AIEX::AIEXDialect,
1014 arith::ArithDialect>();
1017 void runOnOperation()
override {
1018 AIE::DeviceOp deviceOp = BaseClass::getOperation();
1019 if (!ref_clDeviceName.empty() &&
1020 deviceOp.getSymName() != ref_clDeviceName) {
1024 convertAIEToConfiguration(deviceOp, ref_clElfDir, MyOutputType))) {
1025 return BaseClass::signalPassFailure();
1030struct ConvertAIEToTransactionPass
1031 : ConvertAIEToConfigurationPass<
1032 xilinx::impl::ConvertAIEToTransactionBase<
1033 ConvertAIEToTransactionPass>,
1034 AIE::AIEToConfigurationOutputType::Transaction> {
1035 ConvertAIEToTransactionPass()
1036 : ConvertAIEToConfigurationPass<
1037 xilinx::impl::ConvertAIEToTransactionBase<
1038 ConvertAIEToTransactionPass>,
1043struct ConvertAIEToControlPacketsPass
1044 : ConvertAIEToConfigurationPass<
1045 xilinx::impl::ConvertAIEToControlPacketsBase<
1046 ConvertAIEToControlPacketsPass>,
1047 AIE::AIEToConfigurationOutputType::ControlPacket> {
1048 ConvertAIEToControlPacketsPass()
1049 : ConvertAIEToConfigurationPass<
1050 xilinx::impl::ConvertAIEToControlPacketsBase<
1051 ConvertAIEToControlPacketsPass>,
1058std::unique_ptr<mlir::OperationPass<xilinx::AIE::DeviceOp>>
1060 return std::make_unique<ConvertAIEToTransactionPass>();
1063std::unique_ptr<mlir::OperationPass<xilinx::AIE::DeviceOp>>
1065 return std::make_unique<ConvertAIEToControlPacketsPass>();
memref::GlobalOp getOrCreate(OpBuilder &builder, mlir::Location loc, ArrayRef< uint32_t > words)
AIE::DeviceOp getDevice() const
bool hasProperty(ModelProperty Prop) const
mlir::Value createConstantArgPlus(mlir::OpBuilder &builder, mlir::Location loc, uint64_t value)
mlir::Value createConstantI32(mlir::OpBuilder &builder, mlir::Location loc, uint32_t value)
mlir::LogicalResult insertConfigOps(llvm::ArrayRef< std::pair< mlir::Operation *, xilinx::AIE::DeviceOp > > sites, AIEToConfigurationOutputType outputType)
mlir::LogicalResult inlineWriteConfigs(mlir::ModuleOp module)
std::unique_ptr< mlir::OperationPass< xilinx::AIE::DeviceOp > > createConvertAIEToTransactionPass()
mlir::LogicalResult generateAndInsertConfigOps(mlir::OpBuilder &builder, xilinx::AIE::DeviceOp device, xilinx::AIEX::BlockwriteData &blockwriteData, llvm::StringRef clElfDir="", AIEToConfigurationOutputType outputType=AIEToConfigurationOutputType::Transaction, bool skipCtrlPktOverlay=false)
AIEToConfigurationOutputType
std::optional< mlir::ModuleOp > convertTransactionBinaryToMLIR(mlir::MLIRContext *ctx, std::vector< uint8_t > &binary)
std::unique_ptr< mlir::OperationPass< xilinx::AIE::DeviceOp > > createConvertAIEToControlPacketsPass()
mlir::LogicalResult generateAndInsertTeardownOps(mlir::OpBuilder &builder, xilinx::AIE::DeviceOp device, xilinx::AIEX::BlockwriteData &blockwriteData, AIEToConfigurationOutputType outputType=AIEToConfigurationOutputType::Transaction)
mlir::LogicalResult setIOBackend(bool aieSim, bool xaieDebug)
std::vector< uint8_t > exportSerializedTransaction()
mlir::LogicalResult disableCores(DeviceOp &targetOp)
mlir::LogicalResult addCoreEnable(DeviceOp &targetOp)
const std::vector< mlir::Location > & getTxnOpLocs() const
mlir::LogicalResult addInitConfig(DeviceOp &targetOp, bool skipCtrlPktOverlay=false)
mlir::LogicalResult resetDmaChannels(DeviceOp &targetOp)
mlir::LogicalResult addAieElfs(DeviceOp &targetOp, const mlir::StringRef elfPath, bool aieSim)
mlir::LogicalResult teardownSwitches(DeviceOp &targetOp)