17#include "mlir/Dialect/Arith/IR/Arith.h"
18#include "mlir/Dialect/MemRef/IR/MemRef.h"
19#include "mlir/IR/IRMapping.h"
21#include "llvm/ADT/APInt.h"
22#include "llvm/ADT/DenseMap.h"
23#include "llvm/Support/Debug.h"
26#include "xaiengine/xaiegbl.h"
29#include "xaiengine/xaie_txn.h"
39#define GEN_PASS_DEF_CONVERTAIETOCONTROLPACKETS
40#define GEN_PASS_DEF_CONVERTAIETOTRANSACTION
41#include "aie/Conversion/Passes.h.inc"
44#define DEBUG_TYPE "aie-convert-to-config"
52constexpr size_t kTxnHeaderBytes = 16;
54struct TxnPreemptHeader {
60struct TxnLoadPdiHeader {
72 const std::vector<uint8_t> &data,
73 std::vector<TransactionBinaryOperation> &ops) {
75 if (data.size() < kTxnHeaderBytes) {
76 llvm::errs() <<
"Transaction binary is too small for header\n";
80 uint32_t major = data[0];
81 uint32_t minor = data[1];
82 uint32_t num_cols = data[4];
84 uint32_t num_ops, txn_size;
85 std::memcpy(&num_ops, &data[8], 4);
86 std::memcpy(&txn_size, &data[12], 4);
88 LLVM_DEBUG(llvm::dbgs() <<
"Major: " << major <<
"\n");
89 LLVM_DEBUG(llvm::dbgs() <<
"Minor: " << minor <<
"\n");
90 LLVM_DEBUG(llvm::dbgs() <<
"DevGen: " << data[2] <<
"\n");
91 LLVM_DEBUG(llvm::dbgs() <<
"NumRows: " << data[3] <<
"\n");
92 LLVM_DEBUG(llvm::dbgs() <<
"NumCols: " << num_cols <<
"\n");
93 LLVM_DEBUG(llvm::dbgs() <<
"NumMemTileRows: " << data[5] <<
"\n");
94 LLVM_DEBUG(llvm::dbgs() <<
"NumOps: " << num_ops <<
"\n");
95 LLVM_DEBUG(llvm::dbgs() <<
"TxnSize: " << txn_size <<
" bytes\n");
97 size_t i = kTxnHeaderBytes;
99 auto requireBytes = [&](
size_t offset,
size_t length) ->
bool {
100 if (offset + length > data.size()) {
101 llvm::errs() <<
"Transaction binary truncated while parsing opcode\n";
107 auto read32 = [&](
size_t offset) -> uint32_t {
109 std::memcpy(&value, data.data() + offset,
sizeof(uint32_t));
114 auto convertOpcode = [](uint8_t opc) -> std::optional<uint8_t> {
116 case static_cast<uint8_t
>(XAie_TxnOpcode::XAIE_IO_WRITE):
117 case static_cast<uint8_t
>(XAie_TxnOpcode::XAIE_IO_BLOCKWRITE):
118 case static_cast<uint8_t
>(XAie_TxnOpcode::XAIE_IO_MASKWRITE):
121 case static_cast<uint8_t
>(XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT):
122 case static_cast<uint8_t
>(XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH):
125 llvm::errs() <<
"Unhandled opcode: " << std::to_string(opc) <<
"\n";
133 if (major == 0 && minor == 1) {
134 while (i < data.size()) {
135 auto maybeOpcode = convertOpcode(data[i]);
138 XAie_TxnOpcode opcode =
static_cast<XAie_TxnOpcode
>(*maybeOpcode);
139 LLVM_DEBUG(llvm::dbgs() <<
"opcode: " + std::to_string(opcode) <<
"\n");
142 op.
cmd.Opcode = opcode;
145 case XAie_TxnOpcode::XAIE_IO_WRITE: {
146 LLVM_DEBUG(llvm::dbgs() <<
"opcode: WRITE (0x00)\n");
147 if (!requireBytes(i, 24))
149 uint32_t addrLo = read32(i + 8);
150 uint32_t addrHi = read32(i + 12);
151 uint32_t value = read32(i + 16);
152 uint32_t opSize = read32(i + 20);
153 if (!requireBytes(i, opSize))
155 uint64_t addr = (
static_cast<uint64_t
>(addrHi) << 32) | addrLo;
156 op.
cmd.RegOff = addr;
157 op.
cmd.Value = value;
162 case XAie_TxnOpcode::XAIE_IO_BLOCKWRITE: {
163 LLVM_DEBUG(llvm::dbgs() <<
"opcode: BLOCKWRITE (0x01)\n");
164 if (!requireBytes(i, 16))
166 uint32_t addr = read32(i + 8);
167 uint32_t opSize = read32(i + 12);
168 if (opSize < 16 || !requireBytes(i, opSize))
170 const uint8_t *payload = data.data() + i + 16;
171 uint32_t payloadBytes = opSize - 16;
172 op.
cmd.RegOff = addr;
173 op.
cmd.DataPtr =
reinterpret_cast<uint64_t
>(payload);
174 op.
cmd.Size = payloadBytes;
178 case XAie_TxnOpcode::XAIE_IO_MASKWRITE: {
179 LLVM_DEBUG(llvm::dbgs() <<
"opcode: MASKWRITE (0x03)\n");
180 if (!requireBytes(i, 28))
182 uint32_t addrLo = read32(i + 8);
183 uint32_t addrHi = read32(i + 12);
184 uint32_t value = read32(i + 16);
185 uint32_t mask = read32(i + 20);
186 uint32_t opSize = read32(i + 24);
187 if (!requireBytes(i, opSize))
189 uint64_t addr = (
static_cast<uint64_t
>(addrHi) << 32) | addrLo;
190 op.
cmd.RegOff = addr;
191 op.
cmd.Value = value;
193 op.
cmd.Size = opSize;
197 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT: {
198 uint32_t opSize = read32(i + 4);
199 if (opSize < 16 || !requireBytes(i, opSize))
201 uint32_t descriptor = read32(i + 8);
202 uint32_t config = read32(i + 12);
204 static_cast<int32_t
>((descriptor >> 16) & 0xff),
205 static_cast<int32_t
>((descriptor >> 8) & 0xff),
206 static_cast<int32_t
>(descriptor & 0xff),
207 static_cast<int32_t
>((config >> 24) & 0xff),
208 static_cast<int32_t
>((config >> 16) & 0xff),
209 static_cast<int32_t
>((config >> 8) & 0xff)};
211 op.
cmd.Size = opSize;
219 LLVM_DEBUG(llvm::dbgs() <<
"opcode: LOAD_PDI (0x08)\n");
220 constexpr size_t opSize =
sizeof(TxnLoadPdiHeader);
221 if (!requireBytes(i, opSize))
223 TxnLoadPdiHeader header;
224 std::memcpy(&header, data.data() + i, opSize);
226 header.id, header.size, header.address};
228 op.
cmd.Size = opSize;
232 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH: {
233 uint32_t opSize = read32(i + 4);
234 if (opSize < 44 || !requireBytes(i, opSize))
236 uint32_t action = read32(i + 20);
237 uint32_t addr = read32(i + 24);
238 int32_t argIdx =
static_cast<int32_t
>(read32(i + 32));
239 uint64_t argPlus =
static_cast<uint64_t
>(read32(i + 40)) |
240 (
static_cast<uint64_t
>(read32(i + 44)) << 32);
242 action, addr, argIdx, argPlus};
244 op.
cmd.Size = opSize;
249 LLVM_DEBUG(llvm::dbgs() <<
"opcode: PREEMPT (0x06)\n");
250 constexpr size_t opSize =
sizeof(TxnPreemptHeader);
251 if (!requireBytes(i, opSize))
254 reinterpret_cast<const TxnPreemptHeader *
>(data.data() + i);
255 op.
cmd.Value = header->level;
256 op.
cmd.Size = opSize;
261 llvm::errs() <<
"Unhandled opcode: " << std::to_string(opcode)
262 <<
" for v0.1 transaction\n";
268 }
else if (major == 1 && minor == 0) {
269 while (i < data.size()) {
270 auto maybeOpcode = convertOpcode(data[i]);
273 XAie_TxnOpcode opcode =
static_cast<XAie_TxnOpcode
>(*maybeOpcode);
274 LLVM_DEBUG(llvm::dbgs() <<
"opcode: " + std::to_string(opcode) <<
"\n");
277 op.
cmd.Opcode = opcode;
280 case XAie_TxnOpcode::XAIE_IO_WRITE: {
281 LLVM_DEBUG(llvm::dbgs() <<
"opcode: WRITE (0x00)\n");
282 if (!requireBytes(i, 12))
284 uint32_t addr = read32(i + 4);
285 uint32_t value = read32(i + 8);
286 op.
cmd.RegOff = addr;
287 op.
cmd.Value = value;
292 case XAie_TxnOpcode::XAIE_IO_BLOCKWRITE: {
293 LLVM_DEBUG(llvm::dbgs() <<
"opcode: BLOCKWRITE (0x01)\n");
294 if (!requireBytes(i, 12))
296 uint32_t addr = read32(i + 4);
297 uint32_t opSize = read32(i + 8);
298 if (opSize < 12 || !requireBytes(i, opSize))
300 const uint8_t *payload = data.data() + i + 12;
301 uint32_t payloadBytes = opSize - 12;
302 op.
cmd.RegOff = addr;
303 op.
cmd.DataPtr =
reinterpret_cast<uint64_t
>(payload);
304 op.
cmd.Size = payloadBytes;
308 case XAie_TxnOpcode::XAIE_IO_MASKWRITE: {
309 LLVM_DEBUG(llvm::dbgs() <<
"opcode: MASKWRITE (0x03)\n");
310 if (!requireBytes(i, 16))
312 uint32_t addr = read32(i + 4);
313 uint32_t value = read32(i + 8);
314 uint32_t mask = read32(i + 12);
315 op.
cmd.RegOff = addr;
316 op.
cmd.Value = value;
322 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT: {
323 uint32_t opSize = read32(i + 4);
324 if (opSize < 16 || !requireBytes(i, opSize))
326 uint32_t descriptor = read32(i + 8);
327 uint32_t config = read32(i + 12);
329 static_cast<int32_t
>((descriptor >> 16) & 0xff),
330 static_cast<int32_t
>((descriptor >> 8) & 0xff),
331 static_cast<int32_t
>(descriptor & 0xff),
332 static_cast<int32_t
>((config >> 24) & 0xff),
333 static_cast<int32_t
>((config >> 16) & 0xff),
334 static_cast<int32_t
>((config >> 8) & 0xff)};
336 op.
cmd.Size = opSize;
344 LLVM_DEBUG(llvm::dbgs() <<
"opcode: LOAD_PDI (0x08)\n");
345 constexpr size_t opSize =
sizeof(TxnLoadPdiHeader);
346 if (!requireBytes(i, opSize))
348 TxnLoadPdiHeader header;
349 std::memcpy(&header, data.data() + i, opSize);
351 header.id, header.size, header.address};
353 op.
cmd.Size = opSize;
357 case XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH: {
358 uint32_t opSize = read32(i + 4);
359 if (opSize < 44 || !requireBytes(i, opSize))
361 uint32_t action = read32(i + 20);
362 uint32_t addr = read32(i + 24);
363 int32_t argIdx =
static_cast<int32_t
>(read32(i + 32));
364 uint64_t argPlus =
static_cast<uint64_t
>(read32(i + 40)) |
365 (
static_cast<uint64_t
>(read32(i + 44)) << 32);
367 action, addr, argIdx, argPlus};
369 op.
cmd.Size = opSize;
374 LLVM_DEBUG(llvm::dbgs() <<
"opcode: PREEMPT (0x06)\n");
375 constexpr size_t opSize =
sizeof(TxnPreemptHeader);
376 if (!requireBytes(i, opSize))
379 reinterpret_cast<const TxnPreemptHeader *
>(data.data() + i);
380 op.
cmd.Value = header->level;
381 op.
cmd.Size = opSize;
386 llvm::errs() <<
"Unhandled opcode: " << std::to_string(opcode)
387 <<
" for v1.0 transaction\n";
394 llvm::errs() <<
"Unsupported TXN binary version: " << major <<
"." << minor
402static LogicalResult generateTransactions(
AIERTControl &ctl,
403 const StringRef workDirPath,
404 DeviceOp &targetOp,
bool aieSim,
405 bool enableElfs,
bool enableInit,
407 bool skipCtrlPktOverlay =
false) {
408 if (enableElfs && !targetOp.getOps<CoreOp>().empty() &&
409 failed(ctl.
addAieElfs(targetOp, workDirPath, aieSim)))
411 if (enableInit && failed(ctl.
addInitConfig(targetOp, skipCtrlPktOverlay)))
413 if (enableCores && !targetOp.getOps<CoreOp>().empty() &&
424emitTransactionOps(OpBuilder &builder, Location fallbackLoc,
425 std::vector<TransactionBinaryOperation> &operations,
426 std::vector<memref::GlobalOp> &global_data) {
435 llvm::DenseMap<uint32_t, Value> constantCache;
436 auto constant = [&](Location loc, uint32_t value) {
437 Value &cached = constantCache[value];
444 for (
auto [op, payload] : llvm::zip(operations, global_data)) {
445 Location loc = op.sourceLoc.value_or(fallbackLoc);
447 if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_WRITE) {
448 Value address = constant(loc, op.cmd.RegOff);
449 Value value = constant(loc, op.cmd.Value);
450 AIEX::NpuWrite32Op::create(builder, loc, address, value,
nullptr,
nullptr,
452 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_BLOCKWRITE) {
453 auto memref = memref::GetGlobalOp::create(builder, loc, payload.getType(),
455 AIEX::NpuBlockWriteOp::create(
456 builder, loc, builder.getUI32IntegerAttr(op.cmd.RegOff),
457 memref.getResult(),
nullptr,
nullptr,
nullptr);
458 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_MASKWRITE) {
459 Value address = constant(loc, op.cmd.RegOff);
460 Value value = constant(loc, op.cmd.Value);
461 Value mask = constant(loc, op.cmd.Mask);
462 AIEX::NpuMaskWrite32Op::create(builder, loc, address, value, mask,
463 nullptr,
nullptr,
nullptr);
464 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_TCT) {
466 llvm::errs() <<
"Missing sync payload while emitting transaction\n";
470 Value column = constant(loc, sync.
column);
471 Value row = constant(loc, sync.
row);
472 Value direction = constant(loc, sync.
direction);
473 Value channel = constant(loc, sync.
channel);
474 Value columnCount = constant(loc, sync.
columnCount);
475 Value rowCount = constant(loc, sync.
rowCount);
476 AIEX::NpuSyncOp::create(builder, loc, column, row, direction, channel,
477 columnCount, rowCount);
478 }
else if (op.cmd.Opcode == 0x8 ) {
480 llvm::errs() <<
"Missing load_pdi payload while emitting transaction\n";
486 builder.getI32IntegerAttr(
static_cast<int32_t
>(payloadInfo.
id));
487 IntegerAttr sizeAttr =
488 builder.getI32IntegerAttr(
static_cast<int32_t
>(payloadInfo.
size));
491 IntegerType::get(builder.getContext(), 64, IntegerType::Unsigned);
492 IntegerAttr addressAttr =
493 IntegerAttr::get(ui64Ty, llvm::APInt(64, payloadInfo.
address));
495 AIEX::NpuLoadPdiOp::create(builder, loc,
nullptr, idAttr,
496 sizeAttr, addressAttr,
498 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_CUSTOM_OP_DDR_PATCH) {
499 if (!op.addressPatch) {
501 <<
"Missing address_patch payload while emitting transaction\n";
506 AIEX::NpuAddressPatchOp::create(
507 builder, loc, patch.
addr,
508 mlir::Value(),
static_cast<int32_t
>(patch.
argIdx),
510 }
else if (op.cmd.Opcode == 0x6 ) {
512 IntegerType::get(builder.getContext(), 8, IntegerType::Unsigned);
513 auto levelAttr = IntegerAttr::get(ui8Ty, llvm::APInt(8, op.cmd.Value));
514 AIEX::NpuPreemptOp::create(builder, loc, levelAttr);
516 llvm::errs() <<
"Unhandled txn opcode: " << op.cmd.Opcode <<
"\n";
525static llvm::DenseMap<uint32_t, uint32_t>
526registersWrittenSinceLoad(OpBuilder &builder) {
527 llvm::DenseMap<uint32_t, uint32_t> registers;
528 Block *block = builder.getInsertionBlock();
531 Block::iterator it = builder.getInsertionPoint();
532 while (it != block->begin()) {
533 Operation &op = *--it;
534 if (isa<AIEX::NpuLoadPdiOp>(op))
536 auto packet = dyn_cast<AIEX::NpuControlPacketOp>(op);
537 std::optional<ArrayRef<int32_t>> data =
538 packet ? packet.getData() : std::nullopt;
541 for (
auto [i, word] : llvm::enumerate(*data))
542 registers.try_emplace(packet.getAddress() + i *
sizeof(int32_t), word);
551static LogicalResult emitControlPacketOps(
552 OpBuilder &builder, Location fallbackLoc,
553 std::vector<TransactionBinaryOperation> &operations,
554 std::vector<memref::GlobalOp> &global_data,
555 const llvm::DenseMap<uint32_t, uint32_t> &initialRegisters) {
557 auto *ctx = builder.getContext();
561 llvm::DenseMap<uint32_t, uint32_t> registers =
562 registersWrittenSinceLoad(builder);
563 for (
auto [address, value] : initialRegisters)
564 registers.try_emplace(address, value);
567 for (
auto [op, payload] : llvm::zip(operations, global_data)) {
568 Location loc = op.sourceLoc.value_or(fallbackLoc);
570 if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_WRITE) {
571 registers[op.cmd.RegOff] = op.cmd.Value;
572 AIEX::NpuControlPacketOp::create(
573 builder, loc, builder.getUI32IntegerAttr(op.cmd.RegOff),
nullptr,
574 builder.getI32IntegerAttr(0),
575 builder.getI32IntegerAttr(0),
576 DenseI32ArrayAttr::get(ctx, ArrayRef<int32_t>(op.cmd.Value)));
577 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_BLOCKWRITE) {
578 auto initialValue = payload.getInitialValue();
581 auto blockWriteData = dyn_cast<DenseIntElementsAttr>(*initialValue);
582 if (!blockWriteData) {
584 "Global symbol initial value is not a dense int array");
587 auto blockWriteDataValues = blockWriteData.getValues<int32_t>();
589 int currAddr = op.cmd.RegOff;
590 for (
size_t i = 0; i < blockWriteDataValues.size(); i += 4) {
591 auto last = std::min(blockWriteDataValues.size(), i + 4);
592 SmallVector<int32_t> splitData =
593 SmallVector<int32_t>(blockWriteDataValues.begin() + i,
594 blockWriteDataValues.begin() + last);
595 for (
auto [j, word] : llvm::enumerate(splitData))
596 registers[currAddr + j *
sizeof(int32_t)] = word;
597 AIEX::NpuControlPacketOp::create(
598 builder, loc, builder.getUI32IntegerAttr(currAddr),
nullptr,
599 builder.getI32IntegerAttr(0),
600 builder.getI32IntegerAttr(0),
601 DenseI32ArrayAttr::get(ctx, ArrayRef<int32_t>(splitData)));
602 currAddr += splitData.size() *
sizeof(int32_t);
605 }
else if (op.cmd.Opcode == XAie_TxnOpcode::XAIE_IO_MASKWRITE) {
606 uint32_t ® = registers[op.cmd.RegOff];
607 reg = (reg & ~op.cmd.Mask) | (op.cmd.Value & op.cmd.Mask);
609 AIEX::NpuControlPacketOp::create(
610 builder, loc, builder.getUI32IntegerAttr(op.cmd.RegOff),
nullptr,
611 builder.getI32IntegerAttr(0),
612 builder.getI32IntegerAttr(0),
613 DenseI32ArrayAttr::get(ctx, ArrayRef<int32_t>(value)));
615 llvm::errs() <<
"Unhandled txn opcode: " << op.cmd.Opcode <<
"\n";
624static LogicalResult convertTransactionOpsToMLIR(
626 std::vector<TransactionBinaryOperation> &operations,
628 const llvm::DenseMap<uint32_t, uint32_t> &initialRegisters = {}) {
632 std::vector<memref::GlobalOp> global_data;
633 DeviceOp device = blockwriteData.
getDevice();
634 Location loc = device.getLoc();
636 OpBuilder::InsertionGuard guard(builder);
637 builder.setInsertionPointToStart(device.getBody());
638 for (
auto &op : operations) {
639 if (op.cmd.Opcode != XAIE_IO_BLOCKWRITE) {
640 global_data.emplace_back(
nullptr);
643 const uint32_t *d =
reinterpret_cast<const uint32_t *
>(op.cmd.DataPtr);
645 builder, loc, ArrayRef<uint32_t>(d, op.cmd.Size / 4)));
651 if (failed(emitTransactionOps(builder, loc, operations, global_data)))
654 if (failed(emitControlPacketOps(builder, loc, operations, global_data,
658 llvm_unreachable(
"bad output type");
668std::optional<mlir::ModuleOp>
670 std::vector<uint8_t> &binary) {
673 std::vector<TransactionBinaryOperation> operations;
676 llvm::errs() <<
"Failed to parse binary\n";
684 ctx->getOrLoadDialect<mlir::arith::ArithDialect>();
686 auto loc = mlir::UnknownLoc::get(ctx);
689 auto module = ModuleOp::create(loc);
690 OpBuilder builder(module.getBodyRegion());
691 builder.setInsertionPointToStart(module.getBody());
694 std::vector<AIEDevice> devices{AIEDevice::npu1_1col, AIEDevice::npu1_2col,
695 AIEDevice::npu1_3col, AIEDevice::npu1};
696 auto device = DeviceOp::create(builder, loc, devices[columns - 1],
697 DeviceOp::getDefaultDeviceName(),
nullptr);
698 device.getRegion().emplaceBlock();
699 DeviceOp::ensureTerminator(device.getBodyRegion(), builder, loc);
700 builder.setInsertionPointToStart(device.getBody());
704 if (failed(convertTransactionOpsToMLIR(
714insertRecordedTransaction(
AIERTControl &ctl, OpBuilder &builder,
717 DeviceOp configured = {}) {
722 std::vector<TransactionBinaryOperation> operations;
724 llvm::errs() <<
"Failed to parse binary\n";
733 const std::vector<mlir::Location> &opLocs = ctl.
getTxnOpLocs();
739 assert((opLocs.empty() || opLocs.size() == operations.size()) &&
740 "txn loc/op count mismatch: AIERTControl's command-to-operation "
741 "projection disagrees with the transaction parser; aie-rt's "
742 "serializer has drifted from projectCmdLocsOntoSerializedOps");
743 for (
size_t i = 0, e = std::min(opLocs.size(), operations.size()); i < e; ++i)
744 if (!isa<UnknownLoc>(opLocs[i]))
745 operations[i].sourceLoc = opLocs[i];
747 llvm::DenseMap<uint32_t, uint32_t> initialRegisters;
749 configured.getOverlayAttr()) {
750 auto overlay = SymbolTable::lookupNearestSymbolFrom<DeviceOp>(
751 configured->getParentOp(), configured.getOverlayAttr());
753 return configured.emitOpError(
"references an unknown overlay device");
755 if (failed(overlayCtl.setIOBackend(
false,
false)))
757 overlayCtl.startTransaction();
758 if (failed(overlayCtl.configureSwitches(overlay)))
760 auto overlayData = overlayCtl.exportSerializedTransaction();
761 std::vector<TransactionBinaryOperation> overlayOps;
765 for (
const auto &op : overlayOps) {
766 uint32_t &value = initialRegisters[op.cmd.RegOff];
767 if (op.cmd.Opcode == XAIE_IO_WRITE)
768 value = op.cmd.Value;
769 else if (op.cmd.Opcode == XAIE_IO_MASKWRITE)
770 value = (value & ~op.cmd.Mask) | (op.cmd.Value & op.cmd.Mask);
772 return overlay.emitOpError(
"has a non-register switch configuration");
775 return convertTransactionOpsToMLIR(builder, outputType, operations,
776 blockwriteData, initialRegisters);
780 OpBuilder &builder, xilinx::AIE::DeviceOp device,
790 bool xaieDebug =
false;
799 bool generateElfs =
true;
800 if (failed(generateTransactions(ctl, clElfDir, device, aieSim, generateElfs,
801 true,
true, skipCtrlPktOverlay)))
804 return insertRecordedTransaction(ctl, builder, outputType, blockwriteData,
805 skipCtrlPktOverlay ? device : DeviceOp{});
809 OpBuilder &builder, xilinx::AIE::DeviceOp device,
824 return insertRecordedTransaction(ctl, builder, outputType, blockwriteData,
825 skipCtrlPktOverlay ? device : DeviceOp{});
833 std::array<llvm::DenseMap<DeviceOp, AIEX::BlockwriteData>, 2> blockwriteData;
834 std::array<llvm::DenseMap<std::pair<Operation *, Operation *>,
835 std::unique_ptr<Block>>,
838 struct ConstantPool {
839 Operation *last =
nullptr;
840 llvm::DenseMap<Attribute, Value> values;
842 llvm::DenseMap<Operation *, ConstantPool> pools;
843 for (
auto [at, configured] : sites) {
844 auto device = at->getParentOfType<DeviceOp>();
846 return at->emitError(
"expected to be nested under an aie.device op");
847 bool teardown = isa<AIEX::NpuWriteTeardownOp>(at);
848 std::unique_ptr<Block> &config = configs[teardown][{configured, device}];
851 blockwriteData[teardown]
852 .try_emplace(device, device, teardown ?
"teardown_" :
"loadpdi_")
854 auto generated = std::make_unique<Block>();
855 OpBuilder configBuilder(at->getContext());
856 configBuilder.setInsertionPointToEnd(generated.get());
858 configBuilder, configured, payloads, outputType))
860 configBuilder, configured, payloads,
863 return at->emitError(teardown ?
"Failed to generate teardown operations"
864 :
"Failed to generate configuration "
866 config = std::move(generated);
868 auto seq = at->getParentOfType<RuntimeSequenceOp>();
869 ConstantPool *pool = seq ? &pools[seq] :
nullptr;
870 OpBuilder builder(at);
872 for (Operation &op : *config) {
874 if (
auto constant = dyn_cast<arith::ConstantOp>(op))
875 key = constant.getValue();
876 else if (
auto global = dyn_cast<memref::GetGlobalOp>(op))
877 key = global.getNameAttr();
879 builder.clone(op, mapping);
882 Value &pooled = pool->values[key];
884 OpBuilder::InsertionGuard guard(builder);
886 builder.setInsertionPointAfter(pool->last);
888 builder.setInsertionPointToStart(&seq.getBody().front());
889 pool->last = builder.clone(op);
890 pooled = pool->last->getResult(0);
892 mapping.map(op.getResult(0), pooled);
899 SmallVector<std::pair<Operation *, DeviceOp>> sites;
900 WalkResult walk =
module.walk([&](Operation *write) {
901 FlatSymbolRefAttr ref;
902 if (
auto config = dyn_cast<AIEX::NpuWriteConfigOp>(write))
903 ref = config.getDeviceRefAttr();
904 else if (
auto teardown = dyn_cast<AIEX::NpuWriteTeardownOp>(write))
905 ref = teardown.getDeviceRefAttr();
907 return WalkResult::advance();
908 auto configured =
module.lookupSymbol<DeviceOp>(ref);
910 write->emitOpError() << ref <<
" is not a device";
911 return WalkResult::interrupt();
913 sites.emplace_back(write, configured);
914 return WalkResult::advance();
916 if (walk.wasInterrupted() ||
919 for (
auto [write, configured] : sites)
925convertAIEToConfiguration(AIE::DeviceOp device, StringRef clElfDir,
928 OpBuilder builder(device.getBodyRegion());
933 auto loc = device.getLoc();
934 SmallVector<AIEX::ConfigureOp> configureOps;
935 device.walk([&](AIEX::ConfigureOp op) { configureOps.push_back(op); });
937 if (configureOps.empty()) {
940 std::string seq_name =
"configure";
941 while (device.lookupSymbol(seq_name))
942 seq_name =
"configure" + std::to_string(
id++);
943 StringAttr seq_sym_name = builder.getStringAttr(seq_name);
944 auto seq = AIE::RuntimeSequenceOp::create(
945 builder, loc, seq_sym_name, BoolAttr{}, AIE::TraceBufferAttr{},
946 ArrayAttr{}, ArrayAttr{});
947 seq.getBody().push_back(
new Block);
948 builder.setInsertionPointToStart(&seq.getBody().front());
950 builder.setInsertionPoint(configureOps.front());
956 clElfDir, outputType)))
961 if (!configureOps.empty()) {
963 builder.getBlock()->getOperations().splice(
964 builder.getInsertionPoint(),
965 configureOps.front().getBody().front().getOperations());
966 configureOps.front().erase();
974template <
typename BaseClass, AIE::AIEToConfigurationOutputType MyOutputType>
975struct ConvertAIEToConfigurationPass : BaseClass {
976 std::string &ref_clElfDir;
977 std::string &ref_clDeviceName;
978 ConvertAIEToConfigurationPass(std::string &clElfDir,
979 std::string &clDeviceName)
980 : ref_clElfDir(clElfDir), ref_clDeviceName(clDeviceName) {}
982 void getDependentDialects(DialectRegistry ®istry)
const override {
983 registry.insert<memref::MemRefDialect, AIEX::AIEXDialect,
984 arith::ArithDialect>();
987 void runOnOperation()
override {
988 AIE::DeviceOp deviceOp = BaseClass::getOperation();
989 if (!ref_clDeviceName.empty() &&
990 deviceOp.getSymName() != ref_clDeviceName) {
994 convertAIEToConfiguration(deviceOp, ref_clElfDir, MyOutputType))) {
995 return BaseClass::signalPassFailure();
1000struct ConvertAIEToTransactionPass
1001 : ConvertAIEToConfigurationPass<
1002 xilinx::impl::ConvertAIEToTransactionBase<
1003 ConvertAIEToTransactionPass>,
1004 AIE::AIEToConfigurationOutputType::Transaction> {
1005 ConvertAIEToTransactionPass()
1006 : ConvertAIEToConfigurationPass<
1007 xilinx::impl::ConvertAIEToTransactionBase<
1008 ConvertAIEToTransactionPass>,
1013struct ConvertAIEToControlPacketsPass
1014 : ConvertAIEToConfigurationPass<
1015 xilinx::impl::ConvertAIEToControlPacketsBase<
1016 ConvertAIEToControlPacketsPass>,
1017 AIE::AIEToConfigurationOutputType::ControlPacket> {
1018 ConvertAIEToControlPacketsPass()
1019 : ConvertAIEToConfigurationPass<
1020 xilinx::impl::ConvertAIEToControlPacketsBase<
1021 ConvertAIEToControlPacketsPass>,
1028std::unique_ptr<mlir::OperationPass<xilinx::AIE::DeviceOp>>
1030 return std::make_unique<ConvertAIEToTransactionPass>();
1033std::unique_ptr<mlir::OperationPass<xilinx::AIE::DeviceOp>>
1035 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::optional< int > parseTransactionBinary(const std::vector< uint8_t > &data, std::vector< TransactionBinaryOperation > &ops)
Parse an exported transaction.
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
mlir::LogicalResult generateAndInsertTeardownOps(mlir::OpBuilder &builder, xilinx::AIE::DeviceOp device, xilinx::AIEX::BlockwriteData &blockwriteData, AIEToConfigurationOutputType outputType=AIEToConfigurationOutputType::Transaction, bool skipCtrlPktOverlay=false)
std::optional< mlir::ModuleOp > convertTransactionBinaryToMLIR(mlir::MLIRContext *ctx, std::vector< uint8_t > &binary)
std::unique_ptr< mlir::OperationPass< xilinx::AIE::DeviceOp > > createConvertAIEToControlPacketsPass()
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, bool skipCtrlPktOverlay=false)
std::optional< LoadPdiPayload > loadPdi
std::optional< SyncPayload > sync
std::optional< AddressPatchPayload > addressPatch