13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/IR/Attributes.h"
15#include "mlir/Pass/Pass.h"
17#include "llvm/ADT/STLExtras.h"
24#define GEN_PASS_DEF_AIEINSERTTRACEFLOWS
25#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
38 TracePacketType packetType;
43 std::optional<int> stopBroadcast;
47struct ChannelDescriptor {
56 std::vector<TraceInfo> traceSources;
57 std::optional<int> startBroadcast;
58 std::optional<int> stopBroadcast;
59 std::vector<ChannelDescriptor> channels;
60 std::vector<int> traceChannelAssignment;
65struct SequenceTraceConfig {
66 RuntimeSequenceOp seq;
68 bool reuseOutputBuffer;
76static bool isDynamicRuntimeSequence(RuntimeSequenceOp seq) {
77 return llvm::any_of(seq.getBody().getArguments(), [](BlockArgument arg) {
78 return !isa<MemRefType>(arg.getType());
82struct AIEInsertTraceFlowsPass
83 : xilinx::AIE::impl::AIEInsertTraceFlowsBase<AIEInsertTraceFlowsPass> {
85 void runOnOperation()
override {
86 DeviceOp device = getOperation();
87 OpBuilder builder(device);
88 const auto &targetModel = device.getTargetModel();
91 bool hasLogicalTile =
false;
92 device.walk([&](LogicalTileOp op) {
93 op.emitError() <<
"LogicalTileOp must be resolved to TileOp before "
94 "running -aie-insert-trace-flows (run -aie-place-tiles "
96 hasLogicalTile =
true;
99 return signalPassFailure();
102 SmallVector<TraceOp> traces;
103 device.walk([&](TraceOp trace) { traces.push_back(trace); });
111 SmallVector<RuntimeSequenceOp> runtimeSeqs;
112 SmallVector<TraceHostConfigOp> hostConfigs;
113 RuntimeSequenceOp firstSeq =
nullptr;
114 for (
auto seq : device.getBody()->getOps<RuntimeSequenceOp>()) {
117 for (
auto hc : seq.getBody().front().getOps<TraceHostConfigOp>()) {
118 runtimeSeqs.push_back(seq);
119 hostConfigs.push_back(hc);
127 <<
"aie.trace ops found but no runtime_sequence defined";
128 return signalPassFailure();
132 if (runtimeSeqs.empty()) {
134 <<
"runtime_sequence with traces requires aie.trace.host_config";
135 return signalPassFailure();
141 TraceHostConfigOp hostConfig = hostConfigs.front();
142 auto routing = hostConfig.getRouting();
143 int egressShimColFromIR = hostConfig.getEgressShimCol();
144 for (
auto other : ArrayRef(hostConfigs).drop_front()) {
145 if (other.getRouting() == routing &&
146 other.getEgressShimCol() == egressShimColFromIR)
148 InFlightDiagnostic err =
150 <<
"aie.trace.host_config routes trace data differently from another "
151 "runtime_sequence in this device; the routes live in the device's "
152 "stream switches, so every sequence must name the same routing "
154 err.attachNote(hostConfig.getLoc()) <<
"first aie.trace.host_config here";
155 return signalPassFailure();
170 SmallVector<SequenceTraceConfig> seqConfigs;
171 for (
auto [seq, hc] : llvm::zip(runtimeSeqs, hostConfigs)) {
172 SequenceTraceConfig cfg;
174 cfg.bufferSizeBytes = hc.getBufferSize();
175 cfg.reuseOutputBuffer = hc.getReuseOutputBuffer();
176 cfg.bufferOffset = 0;
177 if (cfg.reuseOutputBuffer) {
178 auto args = seq.getBody().getArguments();
180 seq.emitError() <<
"trace.host_config reuse_output_buffer "
181 "requires the runtime_sequence to have at "
182 "least one argument to reuse";
183 return signalPassFailure();
185 if (isDynamicRuntimeSequence(seq)) {
187 <<
"trace.host_config reuse_output_buffer=true cannot be used "
188 "with a dynamic (runtime-sized) runtime_sequence: the trace "
189 "offset would be computed from the last tensor's maximum "
190 "static size, not its runtime transfer size, so trace data "
191 "would be written past the output buffer. Use a separate "
192 "trace buffer instead (reuse_output_buffer=false)";
193 return signalPassFailure();
195 auto memrefType = cast<MemRefType>(args.back().getType());
196 cfg.bufferOffset = memrefType.getNumElements() *
197 (memrefType.getElementTypeBitWidth() / 8);
199 seqConfigs.push_back(cfg);
202 for (
auto hc : hostConfigs)
206 std::vector<TraceInfo> traceInfos;
217 llvm::DenseMap<Operation *, int> autoPacketIds;
220 const int kMaxPacketId = device.getTargetModel().getMaxPacketId();
225 SmallVector<TraceOp> autoIdTraces;
226 llvm::DenseMap<int, TracePacketOp> explicitIdOwner;
227 for (
auto trace : traces) {
228 TracePacketOp explicitPacket =
nullptr;
229 for (
auto &op : trace.getBody().getOps()) {
230 if (
auto p = dyn_cast<TracePacketOp>(op)) {
231 if (p.getId().has_value()) {
237 if (!explicitPacket) {
238 autoIdTraces.push_back(trace);
241 int id = *explicitPacket.getId();
242 auto [it, inserted] = explicitIdOwner.try_emplace(
id, explicitPacket);
244 InFlightDiagnostic err =
245 explicitPacket.emitError()
246 <<
"trace packet id " <<
id
247 <<
" is already used by another trace; explicit packet ids "
248 "must be unique across the device";
249 err.attachNote(it->second.getLoc())
250 <<
"previous use of packet id " << id;
251 return signalPassFailure();
257 llvm::stable_sort(autoIdTraces, [](TraceOp a, TraceOp b) {
258 auto ta = cast<TileOp>(a.getTile().getDefiningOp());
259 auto tb = cast<TileOp>(b.getTile().getDefiningOp());
260 if (ta.getCol() != tb.getCol())
261 return ta.getCol() < tb.getCol();
262 return ta.getRow() < tb.getRow();
266 PacketIdSpace held(device);
267 int next = clPacketIdStart;
268 for (
auto trace : autoIdTraces) {
269 while (next <= kMaxPacketId &&
270 (explicitIdOwner.count(next) || held.isTaken(next)))
272 if (next > kMaxPacketId) {
274 <<
"trace overlay needs " << autoIdTraces.size()
275 <<
" auto-allocated packet IDs starting at " << clPacketIdStart
276 <<
" (with " << explicitIdOwner.size()
277 <<
" id(s) reserved by explicit aie.trace.packet ops, and any "
278 "held by packet flows or pinned routes elsewhere in the "
279 "device), but the hardware packet-id field is 5 bits (max "
281 <<
"); reduce the number of traced tiles, free up an explicit "
282 "id, or raise -packet-id-start only if you can spare the "
284 return signalPassFailure();
286 autoPacketIds[trace.getOperation()] = next++;
294 std::set<std::tuple<int, int, int>> seenTraceUnits;
296 for (
auto trace : traces) {
297 auto tile = cast<TileOp>(trace.getTile().getDefiningOp());
300 std::optional<int> packetId;
301 std::optional<TracePacketType> packetType;
302 TracePacketOp existingPacketOp =
nullptr;
303 for (
auto &op : trace.getBody().getOps()) {
304 if (
auto packetOp = dyn_cast<TracePacketOp>(op)) {
305 existingPacketOp = packetOp;
306 if (
auto explicitId = packetOp.getId())
307 packetId = explicitId;
308 packetType = packetOp.getType();
315 if (tile.isShimTile()) {
316 packetType = TracePacketType::ShimTile;
317 }
else if (tile.isMemTile()) {
318 packetType = TracePacketType::MemTile;
321 packetType = TracePacketType::Core;
326 auto unitKey = std::make_tuple(tile.getCol(), tile.getRow(),
327 static_cast<int>(*packetType));
328 if (!seenTraceUnits.insert(unitKey).second) {
329 trace.emitError() <<
"tile (" << tile.getCol() <<
", " << tile.getRow()
330 <<
") is traced more than once on the same trace "
331 "unit; each unit can have only one trace "
333 return signalPassFailure();
339 packetId = autoPacketIds.lookup(trace.getOperation());
344 if (!existingPacketOp) {
345 OpBuilder traceBuilder(&trace.getBody().front(),
346 trace.getBody().front().begin());
347 TracePacketOp::create(traceBuilder, trace.getLoc(),
348 traceBuilder.getI32IntegerAttr(*packetId),
350 }
else if (!existingPacketOp.getId().has_value()) {
351 existingPacketOp.setIdAttr(
352 OpBuilder(existingPacketOp).getI32IntegerAttr(*packetId));
356 WireBundle tracePort = WireBundle::Trace;
357 int traceChannel = 0;
358 if (*packetType == TracePacketType::Mem) {
363 std::optional<int> startBroadcast;
364 std::optional<int> stopBroadcast;
365 bool hasStartConfig =
false;
366 bool hasStopConfig =
false;
367 for (
auto &op : trace.getBody().getOps()) {
368 if (
auto startOp = dyn_cast<TraceStartEventOp>(op)) {
369 hasStartConfig =
true;
370 if (startOp.getBroadcast())
371 startBroadcast = startOp.getBroadcast();
373 if (
auto stopOp = dyn_cast<TraceStopEventOp>(op)) {
374 hasStopConfig =
true;
375 if (stopOp.getBroadcast())
376 stopBroadcast = stopOp.getBroadcast();
381 if (!hasStartConfig) {
382 trace.emitError() <<
"trace is missing 'aie.trace.start'";
383 return signalPassFailure();
385 if (!hasStopConfig) {
386 trace.emitError() <<
"trace is missing 'aie.trace.stop'";
387 return signalPassFailure();
391 info.traceOp = trace;
393 info.packetId = *packetId;
394 info.packetType = *packetType;
395 info.tracePort = tracePort;
396 info.traceChannel = traceChannel;
397 info.startBroadcast = startBroadcast;
398 info.stopBroadcast = stopBroadcast;
399 traceInfos.push_back(info);
403 std::map<int, ShimInfo> shimInfos;
405 if (routing == TraceShimRouting::Single) {
408 int targetCol = egressShimColFromIR;
409 if (targetCol < 0 || targetCol >= targetModel.columns() ||
410 !targetModel.isShimNOCTile(targetCol, 0)) {
411 device.emitError() <<
"egress_shim_col " << targetCol
412 <<
" is not a valid shim NOC tile (device has "
413 << targetModel.columns() <<
" columns)";
414 return signalPassFailure();
416 TileOp shimTile =
nullptr;
417 for (
auto tile : device.getOps<TileOp>()) {
418 if (tile.getCol() == targetCol && tile.getRow() == 0) {
425 builder.setInsertionPointToStart(&device.getRegion().front());
426 shimTile = TileOp::create(builder, device.getLoc(), targetCol, 0);
430 shimInfo.shimTile = shimTile;
431 shimInfo.channel = clShimChannel;
432 shimInfo.bdId = clDefaultBdId;
433 shimInfo.traceSources = traceInfos;
435 for (
auto &trace : traceInfos) {
436 if (trace.startBroadcast && !shimInfo.startBroadcast)
437 shimInfo.startBroadcast = trace.startBroadcast;
438 if (trace.stopBroadcast && !shimInfo.stopBroadcast)
439 shimInfo.stopBroadcast = trace.stopBroadcast;
443 for (
auto &info : traceInfos) {
444 int col = info.tile.getCol();
445 if (shimInfos.find(col) == shimInfos.end()) {
446 shimInfos[col] = shimInfo;
449 if (shimInfos.find(targetCol) == shimInfos.end()) {
450 shimInfos[targetCol] = shimInfo;
458 if (clLateralRouting) {
459 std::set<int> activeColumns;
460 device.walk([&](CoreOp core) {
461 auto coreTile = cast<TileOp>(core.getTile().getDefiningOp());
462 activeColumns.insert(coreTile.getCol());
466 std::map<int, int> redirects;
467 for (
auto &[col, shimInfo] : shimInfos) {
468 int curTarget = shimInfo.shimTile.getCol();
469 if (redirects.count(curTarget))
473 if (clLateralTargetCol >= 0) {
475 if (clLateralTargetCol == curTarget)
478 int numCols = targetModel.columns();
479 if (clLateralTargetCol >= numCols ||
480 !targetModel.isShimNOCTile(clLateralTargetCol, 0)) {
481 device.emitError() <<
"lateral-target-col " << clLateralTargetCol
482 <<
" is not a valid shim NOC tile (device has "
483 << numCols <<
" columns)";
484 return signalPassFailure();
486 spare = clLateralTargetCol;
489 if (activeColumns.count(curTarget) == 0)
491 spare = findNearestSpareColumn(curTarget, activeColumns, targetModel);
494 redirects[curTarget] = spare;
498 if (!redirects.empty()) {
499 std::map<int, ShimInfo> newShimInfos;
500 for (
auto &[col, shimInfo] : shimInfos) {
501 int curTarget = shimInfo.shimTile.getCol();
502 auto it = redirects.find(curTarget);
503 if (it != redirects.end()) {
504 int newTarget = it->second;
505 shimInfo.shimTile = getOrCreateShim(device, builder, newTarget);
507 newShimInfos[col] = shimInfo;
509 shimInfos = std::move(newShimInfos);
516 auto usedChannels = scanUsedS2MMChannels(device);
518 for (
auto &[col, shimInfo] : shimInfos) {
519 int shimCol = shimInfo.shimTile.getCol();
520 auto usedIt = usedChannels.find(shimCol);
521 if (usedIt == usedChannels.end())
524 const auto &usedSet = usedIt->second;
525 int freeCount = 2 -
static_cast<int>(usedSet.size());
527 if (freeCount <= 0) {
529 if (clLateralRouting) {
530 std::set<int> activeColumns;
531 device.walk([&](CoreOp core) {
532 auto coreTile = cast<TileOp>(core.getTile().getDefiningOp());
533 activeColumns.insert(coreTile.getCol());
536 for (
auto &[fullCol, channels] : usedChannels) {
537 if (
static_cast<int>(channels.size()) >= 2)
538 activeColumns.insert(fullCol);
541 findNearestSpareColumn(shimCol, activeColumns, targetModel);
543 shimInfo.shimTile = getOrCreateShim(device, builder, spare);
546 shimInfo.channel = usedChannels[spare].count(clShimChannel)
554 <<
"no S2MM channels available on shim tile at column " << shimCol
555 <<
" (both channels in use by existing flows or objectFifos); "
556 "set egress_shim_col to a column with a free channel, or "
557 "enable lateral-routing to redirect to a spare column";
558 return signalPassFailure();
561 if (freeCount == 1) {
563 int freeChannel = -1;
564 for (
int ch = 0; ch < 2; ch++) {
565 if (usedSet.count(ch) == 0) {
570 shimInfo.channel = freeChannel;
575 for (
auto &[col, shimInfo] : shimInfos) {
576 int shimCol = shimInfo.shimTile.getCol();
577 std::set<int> available = {0, 1};
578 auto usedIt = usedChannels.find(shimCol);
579 if (usedIt != usedChannels.end()) {
580 for (
int ch : usedIt->second)
584 buildChannelDescriptors(shimInfo.traceSources.size(),
585 shimInfo.channel, shimInfo.bdId, available);
587 for (
size_t i = 0; i < shimInfo.traceSources.size(); i++) {
588 shimInfo.traceChannelAssignment.push_back(i % shimInfo.channels.size());
594 for (
auto &info : traceInfos) {
595 if (!info.tile.isShimTile())
598 int col = info.tile.getCol();
599 auto shimIt = shimInfos.find(col);
600 if (shimIt == shimInfos.end())
604 if (shimIt->second.shimTile.getTileID() != info.tile.getTileID())
609 for (
auto &op : info.traceOp.getBody().getOps()) {
610 if (
auto startOp = dyn_cast<TraceStartEventOp>(op)) {
611 if (startOp.getBroadcast()) {
613 startOp->removeAttr(
"broadcast");
614 startOp->setAttr(
"event", TraceEventAttr::get(builder.getContext(),
615 builder.getStringAttr(
619 if (
auto stopOp = dyn_cast<TraceStopEventOp>(op)) {
620 if (stopOp.getBroadcast()) {
622 stopOp->removeAttr(
"broadcast");
623 stopOp->setAttr(
"event", TraceEventAttr::get(builder.getContext(),
624 builder.getStringAttr(
630 info.startBroadcast = std::nullopt;
631 info.stopBroadcast = std::nullopt;
636 Block &deviceBlock = device.getRegion().front();
637 builder.setInsertionPoint(deviceBlock.getTerminator());
639 for (
auto &info : traceInfos) {
641 int col = info.tile.getCol();
642 ShimInfo &shimInfo = shimInfos[col];
646 for (
size_t i = 0; i < shimInfo.traceSources.size(); i++) {
647 if (shimInfo.traceSources[i].packetId == info.packetId) {
648 chanIdx = shimInfo.traceChannelAssignment[i];
652 auto &chanDesc = shimInfo.channels[chanIdx];
655 auto packetFlowOp = PacketFlowOp::create(
656 builder, device.getLoc(), builder.getI8IntegerAttr(info.packetId),
659 Block *flowBody =
new Block();
660 packetFlowOp.getPorts().push_back(flowBody);
661 OpBuilder flowBuilder = OpBuilder::atBlockEnd(flowBody);
663 PacketSourceOp::create(flowBuilder, device.getLoc(),
664 Value(info.tile.getResult()), info.tracePort,
667 PacketDestOp::create(flowBuilder, device.getLoc(),
668 Value(shimInfo.shimTile.getResult()),
669 WireBundle::DMA, chanDesc.channel);
671 EndOp::create(flowBuilder, device.getLoc());
673 packetFlowOp->setAttr(
"keep_pkt_header", builder.getBoolAttr(
true));
677 for (
const SequenceTraceConfig &cfg : seqConfigs) {
678 if (failed(emitSequenceTraceOps(cfg, traceInfos, shimInfos)))
679 return signalPassFailure();
686 LogicalResult emitSequenceTraceOps(
const SequenceTraceConfig &cfg,
687 std::vector<TraceInfo> &traceInfos,
688 std::map<int, ShimInfo> &shimInfos) {
689 DeviceOp device = getOperation();
690 OpBuilder builder(device);
691 const auto &targetModel = device.getTargetModel();
692 RuntimeSequenceOp runtimeSeq = cfg.seq;
693 int bufferSizeBytes = cfg.bufferSizeBytes;
698 size_t maxChannels = 1;
699 for (
auto &[col, shimInfo] : shimInfos)
700 maxChannels = std::max(maxChannels, shimInfo.channels.size());
701 int traceBytesClaimed =
static_cast<int>(maxChannels) * bufferSizeBytes;
706 BlockArgument appendedTraceArg;
707 if (cfg.reuseOutputBuffer) {
708 traceBuffer = runtimeSeq.getBody().getArguments().back();
712 appendedTraceArg = runtimeSeq.getBody().front().addArgument(
713 MemRefType::get({traceBytesClaimed},
714 IntegerType::get(device.getContext(), 8)),
715 runtimeSeq.getLoc());
716 traceBuffer = appendedTraceArg;
719 runtimeSeq.setTraceBufferAttr(TraceBufferAttr::get(
721 appendedTraceArg ? appendedTraceArg.getArgNumber()
722 : runtimeSeq.getBody().getNumArguments() - 1,
723 cfg.bufferOffset, traceBytesClaimed,
bool(appendedTraceArg)));
725 Block &seqBlock = runtimeSeq.getBody().front();
728 Operation *lastStartConfig =
nullptr;
729 for (
auto &op : seqBlock.getOperations()) {
730 if (isa<TraceStartConfigOp>(op)) {
731 lastStartConfig = &op;
736 if (lastStartConfig) {
737 builder.setInsertionPointAfter(lastStartConfig);
739 builder.setInsertionPointToStart(&seqBlock);
743 std::set<std::tuple<int, int, bool>> processedTiles;
744 for (
auto &info : traceInfos) {
745 if (!info.startBroadcast)
748 int col = info.tile.getCol();
749 int row = info.tile.getRow();
750 bool isMemTrace = info.packetType == TracePacketType::Mem;
752 if (processedTiles.count({col, row, isMemTrace}))
754 processedTiles.insert({col, row, isMemTrace});
757 if (info.tile.isShimTile()) {
758 auto shimIt = shimInfos.find(col);
759 if (shimIt != shimInfos.end() &&
760 shimIt->second.shimTile.getTileID() == info.tile.getTileID()) {
766 uint32_t timerCtrlAddr =
767 computeTimerCtrlAddress(info.tile, targetModel, isMemTrace);
769 std::string broadcastEventName;
770 if (info.tile.isShimTile()) {
772 "BROADCAST_A_" + std::to_string(*info.startBroadcast);
775 "BROADCAST_" + std::to_string(*info.startBroadcast);
778 auto broadcastEvent = targetModel.lookupEvent(
779 broadcastEventName, info.tile.getTileID(), isMemTrace);
780 if (!broadcastEvent) {
781 info.traceOp.emitError() <<
"Failed to lookup broadcast event '"
782 << broadcastEventName <<
"'";
785 const RegisterInfo *timerReg = targetModel.lookupRegister(
786 "Timer_Control", info.tile.getTileID(), isMemTrace);
788 llvm::report_fatal_error(
"Failed to lookup Timer_Control register");
789 const BitFieldInfo *resetField = timerReg->
getField(
"Reset_Event");
791 llvm::report_fatal_error(
792 "Failed to lookup Reset_Event field in Timer_Control");
793 auto timerCtrlValue =
794 targetModel.encodeFieldValue(*resetField, *broadcastEvent);
796 llvm::report_fatal_error(
"Broadcast event does not fit in "
797 "Timer_Control's Reset_Event field");
799 xilinx::AIEX::NpuWrite32Op::create(
800 builder, runtimeSeq.getLoc(),
804 nullptr, builder.getI32IntegerAttr(col),
805 builder.getI32IntegerAttr(row));
809 std::set<int> configuredShimCols;
810 for (
auto &[col, shimInfo] : shimInfos) {
811 int shimCol = shimInfo.shimTile.getCol();
812 if (!configuredShimCols.insert(shimCol).second)
815 for (
auto [chanIdx, chanDesc] : llvm::enumerate(shimInfo.channels)) {
817 int bufferLengthWords = bufferSizeBytes / 4;
820 xilinx::AIEX::NpuWriteBdOp::create(
821 builder, runtimeSeq.getLoc(),
844 mlir::IntegerAttr());
848 uint32_t bdAddress = computeBDAddress(shimCol, chanDesc.bdId,
849 shimInfo.shimTile, targetModel);
851 cfg.bufferOffset +
static_cast<int>(chanIdx) * bufferSizeBytes;
852 xilinx::AIEX::NpuAddressPatchOp::create(
853 builder, runtimeSeq.getLoc(), bdAddress, mlir::Value(),
859 computeCtrlAddress(DMAChannelDir::S2MM, chanDesc.channel,
860 shimInfo.shimTile, targetModel);
861 ensureControllerIdAttr(shimInfo.shimTile, targetModel);
863 shimInfo.shimTile->getAttrOfType<PacketInfoAttr>(
"controller_id");
864 std::string ctrlRegName =
865 (chanDesc.channel == 0) ?
"DMA_S2MM_0_Ctrl" :
"DMA_S2MM_1_Ctrl";
866 const RegisterInfo *ctrlReg = targetModel.lookupRegister(
867 ctrlRegName, shimInfo.shimTile.getTileID());
869 llvm::report_fatal_error(llvm::Twine(
"Failed to lookup ") +
871 const BitFieldInfo *ctrlIdField = ctrlReg->
getField(
"Controller_ID");
873 llvm::report_fatal_error(
"Failed to lookup Controller_ID field in " +
874 llvm::Twine(ctrlRegName));
876 targetModel.encodeFieldValue(*ctrlIdField, ctrlIdAttr.assignedId());
878 llvm::report_fatal_error(
"Controller ID does not fit in the "
879 "Controller_ID field");
880 auto ctrlIdMask = targetModel.getFieldMask(*ctrlIdField);
882 llvm::report_fatal_error(
883 "Controller_ID field does not fit in 32-bit register");
884 xilinx::AIEX::NpuMaskWrite32Op::create(
885 builder, runtimeSeq.getLoc(),
889 nullptr, builder.getI32IntegerAttr(shimCol),
890 builder.getI32IntegerAttr(0));
893 std::string taskQueueRegName = (chanDesc.channel == 0)
894 ?
"DMA_S2MM_0_Task_Queue"
895 :
"DMA_S2MM_1_Task_Queue";
896 const RegisterInfo *queueReg = targetModel.lookupRegister(
897 taskQueueRegName, shimInfo.shimTile.getTileID());
899 llvm::report_fatal_error(llvm::Twine(
"Failed to lookup ") +
901 const BitFieldInfo *tokenField =
902 queueReg->
getField(
"Enable_Token_Issue");
903 const BitFieldInfo *bdIdField = queueReg->
getField(
"Start_BD_ID");
904 if (!tokenField || !bdIdField)
905 llvm::report_fatal_error(
906 "Failed to lookup Enable_Token_Issue or Start_BD_ID fields");
907 auto tokenValue = targetModel.encodeFieldValue(*tokenField, 1);
909 targetModel.encodeFieldValue(*bdIdField, chanDesc.bdId);
911 llvm::report_fatal_error(
"Enable_Token_Issue does not fit in the "
913 llvm::Twine(taskQueueRegName));
915 llvm::report_fatal_error(
"BD ID does not fit in the Start_BD_ID "
917 llvm::Twine(taskQueueRegName));
918 uint32_t queueValue = *tokenValue | *bdIdValue;
919 xilinx::AIEX::NpuWrite32Op::create(
920 builder, runtimeSeq.getLoc(),
924 nullptr, builder.getI32IntegerAttr(shimCol),
925 builder.getI32IntegerAttr(0));
929 if (shimInfo.startBroadcast) {
931 auto userEvent1 = targetModel.lookupEvent(
932 "USER_EVENT_1", shimInfo.shimTile.getTileID(),
false);
934 llvm::report_fatal_error(
"Failed to lookup USER_EVENT_1 event");
936 uint32_t shimTimerCtrlAddr =
937 computeTimerCtrlAddress(shimInfo.shimTile, targetModel,
false);
938 const RegisterInfo *shimTimerReg = targetModel.lookupRegister(
939 "Timer_Control", shimInfo.shimTile.getTileID(),
false);
941 llvm::report_fatal_error(
"Failed to lookup shim Timer_Control");
942 const BitFieldInfo *shimResetField =
943 shimTimerReg->
getField(
"Reset_Event");
945 llvm::report_fatal_error(
946 "Failed to lookup Reset_Event in shim Timer_Control");
947 auto shimTimerCtrlValue =
948 targetModel.encodeFieldValue(*shimResetField, *userEvent1);
949 if (!shimTimerCtrlValue)
950 llvm::report_fatal_error(
"USER_EVENT_1 does not fit in shim "
951 "Timer_Control's Reset_Event field");
952 xilinx::AIEX::NpuWrite32Op::create(
953 builder, runtimeSeq.getLoc(),
957 *shimTimerCtrlValue),
958 nullptr, builder.getI32IntegerAttr(shimCol),
959 builder.getI32IntegerAttr(0));
962 std::string broadcastRegName =
963 "Event_Broadcast" + std::to_string(*shimInfo.startBroadcast) +
"_A";
964 const RegisterInfo *broadcastReg = targetModel.lookupRegister(
965 broadcastRegName, shimInfo.shimTile.getTileID());
967 llvm::report_fatal_error(llvm::Twine(
"Failed to lookup ") +
969 xilinx::AIEX::NpuWrite32Op::create(
970 builder, runtimeSeq.getLoc(),
974 nullptr, builder.getI32IntegerAttr(shimCol),
975 builder.getI32IntegerAttr(0));
978 const RegisterInfo *eventGenReg = targetModel.lookupRegister(
979 "Event_Generate", shimInfo.shimTile.getTileID());
981 llvm::report_fatal_error(
"Failed to lookup Event_Generate register");
982 xilinx::AIEX::NpuWrite32Op::create(
983 builder, runtimeSeq.getLoc(),
987 nullptr, builder.getI32IntegerAttr(shimCol),
988 builder.getI32IntegerAttr(0));
993 builder.setInsertionPointToEnd(&seqBlock);
995 std::set<int> stoppedShimCols;
996 for (
auto &[col, shimInfo] : shimInfos) {
997 if (!shimInfo.stopBroadcast)
1000 int shimCol = shimInfo.shimTile.getCol();
1001 if (!stoppedShimCols.insert(shimCol).second)
1004 auto userEvent0 = targetModel.lookupEvent(
1005 "USER_EVENT_0", shimInfo.shimTile.getTileID(),
false);
1007 llvm::report_fatal_error(
"Failed to lookup USER_EVENT_0 event");
1009 std::string broadcastRegName =
1010 "Event_Broadcast" + std::to_string(*shimInfo.stopBroadcast) +
"_A";
1011 const RegisterInfo *broadcastReg = targetModel.lookupRegister(
1012 broadcastRegName, shimInfo.shimTile.getTileID());
1014 llvm::report_fatal_error(llvm::Twine(
"Failed to lookup ") +
1016 xilinx::AIEX::NpuWrite32Op::create(
1017 builder, runtimeSeq.getLoc(),
1021 nullptr, builder.getI32IntegerAttr(shimCol),
1022 builder.getI32IntegerAttr(0));
1024 const RegisterInfo *stopEventGenReg = targetModel.lookupRegister(
1025 "Event_Generate", shimInfo.shimTile.getTileID());
1026 if (!stopEventGenReg)
1027 llvm::report_fatal_error(
"Failed to lookup Event_Generate register");
1028 xilinx::AIEX::NpuWrite32Op::create(
1029 builder, runtimeSeq.getLoc(),
1031 stopEventGenReg->
offset),
1033 nullptr, builder.getI32IntegerAttr(shimCol),
1034 builder.getI32IntegerAttr(0));
1043 void ensureControllerIdAttr(TileOp tile,
const AIETargetModel &tm) {
1044 if (tile->hasAttr(
"controller_id"))
1047 int pktId = idMap[{tile.colIndex(), tile.rowIndex()}];
1048 auto pktInfoAttr = PacketInfoAttr::get(tile->getContext(), 0, pktId);
1049 tile->setAttr(
"controller_id", pktInfoAttr);
1052 uint32_t computeBDAddress(
int col,
int bdId, TileOp shimTile,
1053 const AIETargetModel &tm) {
1054 int row = shimTile.getRow();
1060 uint32_t computeTaskQueueAddress(DMAChannelDir dir,
int channel,
1061 TileOp shimTile,
const AIETargetModel &tm) {
1062 std::string regName;
1063 if (dir == DMAChannelDir::S2MM) {
1065 (channel == 0) ?
"DMA_S2MM_0_Task_Queue" :
"DMA_S2MM_1_Task_Queue";
1068 (channel == 0) ?
"DMA_MM2S_0_Task_Queue" :
"DMA_MM2S_1_Task_Queue";
1070 const RegisterInfo *reg = tm.
lookupRegister(regName, shimTile.getTileID());
1072 llvm::report_fatal_error(llvm::Twine(
"Failed to lookup ") + regName);
1077 uint32_t computeCtrlAddress(DMAChannelDir dir,
int channel, TileOp shimTile,
1078 const AIETargetModel &tm) {
1079 std::string regName;
1080 if (dir == DMAChannelDir::S2MM) {
1081 regName = (channel == 0) ?
"DMA_S2MM_0_Ctrl" :
"DMA_S2MM_1_Ctrl";
1083 regName = (channel == 0) ?
"DMA_MM2S_0_Ctrl" :
"DMA_MM2S_1_Ctrl";
1085 const RegisterInfo *reg = tm.
lookupRegister(regName, shimTile.getTileID());
1087 llvm::report_fatal_error(llvm::Twine(
"Failed to lookup ") + regName);
1092 uint32_t computeTimerCtrlAddress(TileOp tile,
const AIETargetModel &tm,
1095 const RegisterInfo *reg =
1096 tm.
lookupRegister(
"Timer_Control", tile.getTileID(), isMemTrace);
1098 llvm::report_fatal_error(
"Failed to lookup Timer_Control register");
1105 std::vector<ChannelDescriptor>
1106 buildChannelDescriptors(
size_t numTraces,
int primaryChannel,
int primaryBdId,
1107 const std::set<int> &availableChannels) {
1108 std::vector<ChannelDescriptor> chans;
1109 chans.push_back({primaryChannel, primaryBdId});
1110 if (clDistributeChannels && numTraces > 1 && primaryBdId > 0) {
1111 int ch2 = (primaryChannel == 1) ? 0 : 1;
1114 if (availableChannels.count(ch2)) {
1115 chans.push_back({ch2, primaryBdId - 1});
1122 TileOp getOrCreateShim(DeviceOp device, OpBuilder &builder,
int col) {
1123 for (
auto tile : device.getOps<TileOp>()) {
1124 if (tile.getCol() == col && tile.getRow() == 0)
1127 OpBuilder::InsertionGuard guard(builder);
1128 builder.setInsertionPointToStart(&device.getRegion().front());
1129 return TileOp::create(builder, device.getLoc(), col, 0);
1135 std::map<int, std::set<int>> scanUsedS2MMChannels(DeviceOp device) {
1136 std::map<int, std::set<int>> used;
1138 device.walk([&](FlowOp flow) {
1139 auto destTile = cast<TileOp>(flow.getDest().getDefiningOp());
1140 if (destTile.isShimTile() && flow.getDestBundle() == WireBundle::DMA) {
1141 used[destTile.getCol()].insert(flow.getDestChannel());
1145 device.walk([&](PacketFlowOp pktFlow) {
1146 for (
auto &op : pktFlow.getPorts().front()) {
1147 if (
auto dest = dyn_cast<PacketDestOp>(op)) {
1148 auto destTile = cast<TileOp>(dest.getTile().getDefiningOp());
1149 if (destTile.isShimTile() && dest.getBundle() == WireBundle::DMA) {
1150 used[destTile.getCol()].insert(dest.getChannel());
1156 device.walk([&](ShimDMAAllocationOp alloc) {
1157 if (alloc.getChannelDir() == DMAChannelDir::S2MM) {
1158 auto tile = alloc.getTileOp();
1159 used[tile.getCol()].insert(alloc.getChannelIndex());
1167 SmallVector<TileOp> unpinned;
1168 device.walk([&](ObjectFifoCreateOp fifo) {
1171 for (
auto [i, consumer] : llvm::enumerate(fifo.getConsumerTiles())) {
1172 auto tile = dyn_cast_or_null<TileOp>(consumer.getDefiningOp());
1173 if (!tile || !tile.isShimTile())
1175 ObjectFifoEndPortAttr port = fifo.getConsumerPort(i);
1176 if (std::optional<int> pin = port ? port.channelIndex() : std::nullopt)
1177 used[tile.getCol()].insert(*pin);
1179 unpinned.push_back(tile);
1182 for (TileOp tile : unpinned) {
1183 auto &channels = used[tile.getCol()];
1185 while (channels.count(ch))
1187 channels.insert(ch);
1195 int findNearestSpareColumn(
int sourceCol,
const std::set<int> &activeColumns,
1196 const AIETargetModel &targetModel) {
1198 int bestDist = INT_MAX;
1199 int numCols = targetModel.
columns();
1200 for (
int c = 0; c < numCols; c++) {
1201 if (activeColumns.count(c) == 0 && targetModel.
isShimNOCTile(c, 0)) {
1202 int dist = std::abs(c - sourceCol);
1203 if (dist > 0 && dist < bestDist) {
1215std::unique_ptr<OperationPass<DeviceOp>>
1217 return std::make_unique<AIEInsertTraceFlowsPass>();
llvm::DenseMap< TileID, int > getTileToControllerIdMap(bool columnWiseUniqueIDs=true) const
Return a mapping from tile coordinates to controller IDs.
const RegisterInfo * lookupRegister(llvm::StringRef name, TileID tile, bool isMem=false) const
Register Database API - provides access to register and event information for trace configuration and...
virtual uint64_t getDmaBdAddress(int col, int row, uint32_t bd_id, int channel=-1, AIE::DMAChannelDir direction=AIE::DMAChannelDir::MM2S) const =0
Return the array address of the dma buffer descriptor for the given col, row, buffer descriptor id,...
virtual uint32_t getDmaBdAddressOffset(int col, int row) const =0
Return the offset of the base address field within the shim dma buffer descriptor.
bool isShimNOCTile(int col, int row) const
Return true if the given tile is a ShimNOC tile.
virtual int columns() const =0
Return the number of columns in the device.
mlir::Value createConstantI32(mlir::OpBuilder &builder, mlir::Location loc, uint32_t value)
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEInsertTraceFlowsPass()
const BitFieldInfo * getField(llvm::StringRef fieldName) const