MLIR-AIE
AIETraceOps.cpp
Go to the documentation of this file.
1//===- AIETraceOps.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// Copyright (C) 2025, Advanced Micro Devices, Inc.
8//
9//===----------------------------------------------------------------------===//
10// Implementation of AIE trace operations
11//===----------------------------------------------------------------------===//
12
14
15#include "mlir/IR/OpImplementation.h"
16#include "mlir/IR/SymbolTable.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringRef.h"
19
20using namespace mlir;
21using namespace xilinx;
22using namespace xilinx::AIE;
23
24//===----------------------------------------------------------------------===//
25// TraceEventAttr Helper Methods
26//===----------------------------------------------------------------------===//
27
28std::string TraceEventAttr::getEventName() const {
29 if (auto strAttr = llvm::dyn_cast<StringAttr>(getValue())) {
30 // If the string is fully qualified (contains '::'), extract just the name
31 StringRef strValue = strAttr.getValue();
32 size_t pos = strValue.find("::");
33 if (pos != StringRef::npos) {
34 return strValue.substr(pos + 2).str();
35 }
36 return strAttr.getValue().str();
37 }
38
39 // Fallback: unexpected attribute kind.
40 return "";
41}
42
43bool TraceEventAttr::isStringAttr() const {
44 return llvm::isa<StringAttr>(getValue());
45}
46
47std::optional<int64_t> TraceEventAttr::getEnumValue() const {
48 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(getValue())) {
49 return intAttr.getInt();
50 }
51 // String values don't have enum values - they're event names
52 return std::nullopt;
53}
54
55//===----------------------------------------------------------------------===//
56// Tile Type Validation Helper
57//===----------------------------------------------------------------------===//
58
59static bool isValidEventForTile(TileLike tile, Attribute eventAttr,
60 const AIETargetModel &targetModel) {
61 // Determine tile type from TileLike interface
62 bool isShimTile = tile.isShimTile();
63 bool isMemTile = tile.isMemTile();
64 bool isCoreTile = tile.isCoreTile();
65 AIEArch arch = targetModel.getTargetArch();
66
67 // If eventAttr is a TraceEventAttr, extract the inner attribute
68 Attribute innerAttr = eventAttr;
69 if (auto traceEvent = llvm::dyn_cast<TraceEventAttr>(eventAttr)) {
70 innerAttr = traceEvent.getValue();
71 }
72
73 // Enum-qualified strings (e.g. "CoreEventAIE2P::INSTR_EVENT_0") encode both
74 // architecture and tile type in the prefix. We validate the prefix against
75 // the device arch and tile kind here. Plain strings (no "::") are deferred
76 // to lowering where the event database is available.
77 //
78 // NOTE: The parser stores enum events as strings rather than typed
79 // I32EnumAttrs because AIE2 and AIE2P enums share identical integer values,
80 // making isa<> checks ambiguous across architectures.
81 if (auto strAttr = llvm::dyn_cast<StringAttr>(innerAttr)) {
82 StringRef val = strAttr.getValue();
83 size_t sep = val.find("::");
84 if (sep == StringRef::npos)
85 return true; // plain string — validated during lowering
86
87 StringRef prefix = val.substr(0, sep);
88
89 // Map (arch, tileKind) → set of allowed prefixes.
90 if (isCoreTile) {
91 switch (arch) {
92 case AIEArch::AIE1:
93 return prefix == "CoreEventAIE" || prefix == "MemEventAIE";
94 case AIEArch::AIE2:
95 return prefix == "CoreEventAIE2" || prefix == "MemEventAIE2";
96 case AIEArch::AIE2p:
97 return prefix == "CoreEventAIE2P" || prefix == "MemEventAIE2P";
98 default:
99 return false;
100 }
101 }
102 if (isMemTile) {
103 switch (arch) {
104 case AIEArch::AIE2:
105 return prefix == "MemTileEventAIE2";
106 case AIEArch::AIE2p:
107 return prefix == "MemTileEventAIE2P";
108 default:
109 return false;
110 }
111 }
112 if (isShimTile) {
113 switch (arch) {
114 case AIEArch::AIE1:
115 return prefix == "ShimTileEventAIE";
116 case AIEArch::AIE2:
117 return prefix == "ShimTileEventAIE2";
118 case AIEArch::AIE2p:
119 return prefix == "ShimTileEventAIE2P";
120 default:
121 return false;
122 }
123 }
124 return false;
125 }
126
127 return false;
128}
129
130//===----------------------------------------------------------------------===//
131// TraceOp
132//===----------------------------------------------------------------------===//
133
134void TraceOp::getAsmResultNames(
135 function_ref<void(Value, StringRef)> setNameFn) {
136 // No results for this operation
137}
138
139LogicalResult TraceOp::verify() {
140 // Count trace events
141 int eventCount = 0;
142 for (auto &op : getBody().getOps()) {
143 if (isa<TraceEventOp>(op)) {
144 eventCount++;
145 }
146 }
147
148 // Check max 8 events
149 if (eventCount > 8) {
150 return emitOpError("trace unit supports maximum 8 events, got ")
151 << eventCount;
152 }
153
154 // Verify tile operand is a TileOp or LogicalTileOp
155 if (!llvm::dyn_cast<TileLike>(getTile().getDefiningOp())) {
156 return emitOpError("tile operand must be a TileOp or LogicalTileOp");
157 }
158
159 // Track combo/edge slot usage within this trace
160 llvm::DenseSet<uint32_t> comboSlots;
161 llvm::DenseSet<uint32_t> edgeSlots;
162
163 for (auto &op : getBody().getOps()) {
164 if (auto comboOp = dyn_cast<TraceComboEventOp>(op)) {
165 uint32_t slot = comboOp.getSlot();
166 if (!comboSlots.insert(slot).second) {
167 return comboOp.emitOpError("combo event slot ")
168 << slot << " already in use in this trace";
169 }
170 } else if (auto edgeOp = dyn_cast<TraceEdgeEventOp>(op)) {
171 uint32_t slot = edgeOp.getSlot();
172 if (!edgeSlots.insert(slot).second) {
173 return edgeOp.emitOpError("edge detection slot ")
174 << slot << " already in use in this trace";
175 }
176 }
177 }
178
179 return success();
180}
181
182//===----------------------------------------------------------------------===//
183// Helper function for parsing event values (string or typed enum)
184// Made available in xilinx::AIE namespace for reuse in AIEDialect.cpp
185//===----------------------------------------------------------------------===//
186
187ParseResult xilinx::AIE::parseTraceEvent(AsmParser &parser, Attribute &result) {
188 MLIRContext *ctx = parser.getContext();
189
190 // Try to parse as string first
191 std::string strValue;
192 if (succeeded(parser.parseOptionalString(&strValue))) {
193 result = StringAttr::get(ctx, strValue);
194 return success();
195 }
196
197 // Try to parse as enum (EnumType::VALUE)
198 StringRef enumTypeName;
199 llvm::SMLoc loc = parser.getCurrentLocation();
200
201 if (failed(parser.parseKeyword(&enumTypeName))) {
202 return parser.emitError(loc, "expected string or enum event");
203 }
204
205 if (failed(parser.parseColon()) || failed(parser.parseColon())) {
206 return parser.emitError(loc, "expected '::' after enum type name");
207 }
208
209 StringRef caseName;
210 if (failed(parser.parseKeyword(&caseName))) {
211 return parser.emitError(parser.getCurrentLocation(),
212 "expected enum case name");
213 }
214 // Store as a qualified string ("EnumType::CaseName") rather than a typed
215 // I32EnumAttr because different architecture enums share identical integer
216 // values, making isa<> checks ambiguous. The enum prefix is validated
217 // against the device architecture in isValidEventForTile().
218
219 // Define a helper struct for enum validation
220 struct EnumValidator {
221 StringRef name;
222 std::function<bool(StringRef)> symbolizer;
223 };
224
225 // Table of supported enum types and their symbolizer functions
226 static const EnumValidator validators[] = {
227 // AIE2 event enums
228 {"CoreEventAIE2",
229 [](StringRef s) { return symbolizeCoreEventAIE2(s).has_value(); }},
230 {"MemEventAIE2",
231 [](StringRef s) {
232 auto result = symbolizeMemEventAIE2(s);
233 return result.has_value();
234 }},
235 {"MemTileEventAIE2",
236 [](StringRef s) {
237 auto result = symbolizeMemTileEventAIE2(s);
238 return result.has_value();
239 }},
240 {"ShimTileEventAIE2",
241 [](StringRef s) {
242 auto result = symbolizeShimTileEventAIE2(s);
243 return result.has_value();
244 }},
245 // AIE event enums
246 {"CoreEventAIE",
247 [](StringRef s) {
248 auto result = symbolizeCoreEventAIE(s);
249 return result.has_value();
250 }},
251 {"MemEventAIE",
252 [](StringRef s) {
253 auto result = symbolizeMemEventAIE(s);
254 return result.has_value();
255 }},
256 {"ShimTileEventAIE",
257 [](StringRef s) {
258 auto result = symbolizeShimTileEventAIE(s);
259 return result.has_value();
260 }},
261 // AIE2P event enums
262 {"CoreEventAIE2P",
263 [](StringRef s) {
264 auto result = symbolizeCoreEventAIE2P(s);
265 return result.has_value();
266 }},
267 {"MemEventAIE2P",
268 [](StringRef s) {
269 auto result = symbolizeMemEventAIE2P(s);
270 return result.has_value();
271 }},
272 {"MemTileEventAIE2P",
273 [](StringRef s) {
274 auto result = symbolizeMemTileEventAIE2P(s);
275 return result.has_value();
276 }},
277 {"ShimTileEventAIE2P",
278 [](StringRef s) {
279 auto result = symbolizeShimTileEventAIE2P(s);
280 return result.has_value();
281 }},
282 };
283
284 // Look up and validate the enum type
285 for (const auto &validator : validators) {
286 if (enumTypeName != validator.name)
287 continue;
288 if (!validator.symbolizer(caseName))
289 return parser.emitError(loc, "unknown ")
290 << enumTypeName << " value: " << caseName;
291 result = StringAttr::get(ctx, enumTypeName + "::" + caseName);
292 return success();
293 }
294
295 return parser.emitError(loc, "unknown event enum type: ") << enumTypeName;
296}
297
298void xilinx::AIE::printTraceEventEnum(AsmPrinter &printer, Attribute attr) {
299 if (auto traceAttr = llvm::dyn_cast<TraceEventAttr>(attr)) {
300 printTraceEventEnum(printer, traceAttr.getValue());
301 return;
302 }
303 if (auto strAttr = llvm::dyn_cast<StringAttr>(attr)) {
304 // If string contains "::" (enum format), print without quotes
305 if (strAttr.getValue().contains("::")) {
306 printer << strAttr.getValue();
307 return;
308 }
309 printer << "\"" << strAttr.getValue() << "\"";
310 }
311 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
312 printer << intAttr.getInt();
313 return;
314 }
315}
316
317//===----------------------------------------------------------------------===//
318// TraceEventOp
319//===----------------------------------------------------------------------===//
320
321LogicalResult TraceEventOp::verify() {
322 // Get event name/value
323 auto eventAttr = getEvent();
324
325 // Basic validation - event should not be empty
326 std::string eventName = eventAttr.getEventName();
327 if (eventName.empty()) {
328 return emitOpError("event name cannot be empty");
329 }
330
331 // Validate event type matches tile type
332 auto trace = (*this)->getParentOfType<TraceOp>();
333 if (!trace) {
334 return emitOpError("must be nested in aie.trace");
335 }
336
337 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
338 if (!tileLike) {
339 return emitOpError("trace tile must be a TileOp or LogicalTileOp");
340 }
341
342 auto device = trace->getParentOfType<DeviceOp>();
343 const auto &targetModel = device.getTargetModel();
344
345 if (!isValidEventForTile(tileLike, eventAttr.getValue(), targetModel)) {
346 std::string tileTypeStr;
347 if (tileLike.isCoreTile())
348 tileTypeStr = "core tile";
349 else if (tileLike.isMemTile())
350 tileTypeStr = "mem tile";
351 else
352 tileTypeStr = "shim tile";
353
354 // Use the full string value (including enum prefix) for clarity.
355 std::string fullEventName;
356 if (auto strAttr = llvm::dyn_cast<StringAttr>(eventAttr.getValue()))
357 fullEventName = strAttr.getValue().str();
358 else
359 fullEventName = eventName;
360
361 auto col = tileLike.tryGetCol();
362 auto row = tileLike.tryGetRow();
363 auto fmtCoord = [](std::optional<int> v) -> std::string {
364 return v ? std::to_string(*v) : "?";
365 };
366 return emitOpError("event '")
367 << fullEventName << "' is not valid for " << tileTypeStr << " ("
368 << stringifyAIEArch(targetModel.getTargetArch()) << ") at ("
369 << fmtCoord(col) << ", " << fmtCoord(row) << ")";
370 }
371
372 return success();
373}
374
375//===----------------------------------------------------------------------===//
376// TracePacketOp
377//===----------------------------------------------------------------------===//
378
379LogicalResult TracePacketOp::verify() {
380 // Packet ID range is already enforced by Confined constraint in TableGen
381 // Just verify it's within valid range
382 int32_t id = getId();
383 if (id < 1 || id > 31) {
384 return emitOpError("packet ID must be in range [1, 31], got ") << id;
385 }
386
387 return success();
388}
389
390//===----------------------------------------------------------------------===//
391// TracePortOp
392//===----------------------------------------------------------------------===//
393
394LogicalResult TracePortOp::verify() {
395 // Get parent trace and tile
396 auto trace = (*this)->getParentOfType<TraceOp>();
397 if (!trace) {
398 return emitOpError("must be nested in aie.trace");
399 }
400
401 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
402 if (!tileLike) {
403 return emitOpError("trace tile must be a TileOp or LogicalTileOp");
404 }
405
406 // Get target model
407 auto device = trace->getParentOfType<DeviceOp>();
408 const auto &targetModel = device.getTargetModel();
409
410 // Convert DMAChannelDir to master flag: S2MM=master, MM2S=slave
411 bool isMaster = (getDirection() == DMAChannelDir::S2MM);
412
413 // Verify port is valid for this tile (only when placed with known col/row)
414 auto col = tileLike.tryGetCol();
415 auto row = tileLike.tryGetRow();
416 if (col && row) {
417 if (!targetModel
418 .getStreamSwitchPortIndex(*col, *row, getPort(), getChannel(),
419 isMaster)
420 .has_value()) {
421 return emitOpError("invalid stream switch port configuration for tile (")
422 << *col << ", " << *row << ")";
423 }
424 }
425
426 // Check for duplicate slots within same trace
427 for (auto &op : trace.getBody().getOps()) {
428 if (auto otherPort = dyn_cast<TracePortOp>(op)) {
429 if (otherPort != *this && otherPort.getSlot() == getSlot()) {
430 return emitOpError("duplicate port slot ")
431 << getSlot() << " in trace " << trace.getSymName();
432 }
433 }
434 }
435
436 return success();
437}
438
439//===----------------------------------------------------------------------===//
440// TraceStartEventOp
441//===----------------------------------------------------------------------===//
442
443LogicalResult TraceStartEventOp::verify() {
444 // Must have either broadcast or event, but not both
445 bool hasBroadcast = getBroadcast().has_value();
446 bool hasEvent = getEvent().has_value();
447
448 if (!hasBroadcast && !hasEvent) {
449 return emitOpError("must specify either broadcast or event");
450 }
451
452 if (hasBroadcast && hasEvent) {
453 return emitOpError("cannot specify both broadcast and event");
454 }
455
456 return success();
457}
458
459//===----------------------------------------------------------------------===//
460// TraceStopEventOp
461//===----------------------------------------------------------------------===//
462
463LogicalResult TraceStopEventOp::verify() {
464 // Must have either broadcast or event, but not both
465 bool hasBroadcast = getBroadcast().has_value();
466 bool hasEvent = getEvent().has_value();
467
468 if (!hasBroadcast && !hasEvent) {
469 return emitOpError("must specify either broadcast or event");
470 }
471
472 if (hasBroadcast && hasEvent) {
473 return emitOpError("cannot specify both broadcast and event");
474 }
475
476 return success();
477}
478
479//===----------------------------------------------------------------------===//
480// TraceStartEventOp and TraceStopEventOp
481//===----------------------------------------------------------------------===//
482
483void TraceStartEventOp::print(OpAsmPrinter &p) {
484 if (auto broadcast = getBroadcast()) {
485 p << " broadcast = " << broadcast.value();
486 }
487 if (auto event = getEvent()) {
488 p << " event = <";
489 printTraceEventEnum(p, event->getValue());
490 p << ">";
491 }
492 p.printOptionalAttrDict((*this)->getAttrs(),
493 /*elidedAttrs=*/{"broadcast", "event"});
494}
495
496ParseResult TraceStartEventOp::parse(OpAsmParser &parser,
497 OperationState &result) {
498 // Parse optional broadcast
499 if (succeeded(parser.parseOptionalKeyword("broadcast"))) {
500 IntegerAttr broadcast;
501 if (parser.parseEqual() ||
502 parser.parseAttribute(broadcast, parser.getBuilder().getI32Type(),
503 "broadcast", result.attributes))
504 return failure();
505 }
506
507 // Parse optional event
508 if (succeeded(parser.parseOptionalKeyword("event"))) {
509 if (parser.parseEqual() || parser.parseLess())
510 return failure();
511
512 Attribute innerValue;
513 if (failed(parseTraceEvent(parser, innerValue)))
514 return failure();
515
516 auto traceEvent = TraceEventAttr::get(parser.getContext(), innerValue);
517 result.attributes.set("event", traceEvent);
518
519 if (parser.parseGreater())
520 return failure();
521 }
522
523 if (parser.parseOptionalAttrDict(result.attributes))
524 return failure();
525
526 return success();
527}
528
529void TraceStopEventOp::print(OpAsmPrinter &p) {
530 if (auto broadcast = getBroadcast()) {
531 p << " broadcast = " << broadcast.value();
532 }
533 if (auto event = getEvent()) {
534 p << " event = <";
535 printTraceEventEnum(p, event->getValue());
536 p << ">";
537 }
538 p.printOptionalAttrDict((*this)->getAttrs(),
539 /*elidedAttrs=*/{"broadcast", "event"});
540}
541
542ParseResult TraceStopEventOp::parse(OpAsmParser &parser,
543 OperationState &result) {
544 // Parse optional broadcast
545 if (succeeded(parser.parseOptionalKeyword("broadcast"))) {
546 IntegerAttr broadcast;
547 if (parser.parseEqual() ||
548 parser.parseAttribute(broadcast, parser.getBuilder().getI32Type(),
549 "broadcast", result.attributes))
550 return failure();
551 }
552
553 // Parse optional event
554 if (succeeded(parser.parseOptionalKeyword("event"))) {
555 if (parser.parseEqual() || parser.parseLess())
556 return failure();
557
558 Attribute innerValue;
559 if (failed(parseTraceEvent(parser, innerValue)))
560 return failure();
561
562 auto traceEvent = TraceEventAttr::get(parser.getContext(), innerValue);
563 result.attributes.set("event", traceEvent);
564
565 if (parser.parseGreater())
566 return failure();
567 }
568
569 if (parser.parseOptionalAttrDict(result.attributes))
570 return failure();
571
572 return success();
573}
574
575//===----------------------------------------------------------------------===//
576// TraceComboEventOp
577//===----------------------------------------------------------------------===//
578
579LogicalResult TraceComboEventOp::verify() {
580 uint32_t slot = getSlot();
581
582 // Check slot is valid (0, 1, or 2)
583 if (slot > 2) {
584 return emitOpError("combo event slot must be 0, 1, or 2, got ") << slot;
585 }
586
587 // Get parent trace and tile for validation
588 auto trace = (*this)->getParentOfType<TraceOp>();
589 if (!trace) {
590 return emitOpError("must be nested in aie.trace");
591 }
592
593 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
594 if (!tileLike) {
595 return emitOpError("trace tile must be a TileOp or LogicalTileOp");
596 }
597
598 // Validate event types match tile
599 auto device = trace->getParentOfType<DeviceOp>();
600 const auto &targetModel = device.getTargetModel();
601
602 if (!isValidEventForTile(tileLike, getEventA().getValue(), targetModel)) {
603 return emitOpError("eventA is not valid for this tile type");
604 }
605 if (!isValidEventForTile(tileLike, getEventB().getValue(), targetModel)) {
606 return emitOpError("eventB is not valid for this tile type");
607 }
608
609 // Validate event selection based on slot
610 std::string eventAName = getEventA().getEventName();
611 std::string eventBName = getEventB().getEventName();
612
613 if (slot == 0) {
614 // Combo 0: should not use eventC/D or combo results
615 if (eventAName.find("COMBO_EVENT") != std::string::npos ||
616 eventBName.find("COMBO_EVENT") != std::string::npos) {
617 return emitOpError("combo slot 0 should use regular events, not "
618 "COMBO_EVENT_* (uses eventA/B)");
619 }
620 } else if (slot == 1) {
621 // Combo 1: should not use combo results
622 if (eventAName.find("COMBO_EVENT") != std::string::npos ||
623 eventBName.find("COMBO_EVENT") != std::string::npos) {
624 return emitOpError("combo slot 1 should use regular events, not "
625 "COMBO_EVENT_* (uses eventC/D)");
626 }
627 } else if (slot == 2) {
628 // Combo 2 is hierarchical - must use COMBO_EVENT_0 and COMBO_EVENT_1
629 if (eventAName != "COMBO_EVENT_0") {
630 return emitOpError("combo slot 2 first event must be COMBO_EVENT_0 "
631 "(hierarchical), got ")
632 << eventAName;
633 }
634 if (eventBName != "COMBO_EVENT_1") {
635 return emitOpError("combo slot 2 second event must be COMBO_EVENT_1 "
636 "(hierarchical), got ")
637 << eventBName;
638 }
639 }
640
641 return success();
642}
643
644//===----------------------------------------------------------------------===//
645// TraceEdgeEventOp
646//===----------------------------------------------------------------------===//
647
648LogicalResult TraceEdgeEventOp::verify() {
649 uint32_t slot = getSlot();
650
651 // Check slot is valid (0 or 1)
652 if (slot > 1) {
653 return emitOpError("edge detection slot must be 0 or 1, got ") << slot;
654 }
655
656 // Get parent trace and tile for validation
657 auto trace = (*this)->getParentOfType<TraceOp>();
658 if (!trace) {
659 return emitOpError("must be nested in aie.trace");
660 }
661
662 auto tileLike = dyn_cast<TileLike>(trace.getTile().getDefiningOp());
663 if (!tileLike) {
664 return emitOpError("trace tile must be a TileOp or LogicalTileOp");
665 }
666
667 // Validate event type matches tile
668 auto device = trace->getParentOfType<DeviceOp>();
669 const auto &targetModel = device.getTargetModel();
670
671 if (!isValidEventForTile(tileLike, getEvent().getValue(), targetModel)) {
672 return emitOpError("event is not valid for this tile type");
673 }
674
675 // Edge events should not be other edge/combo events
676 std::string eventName = getEvent().getEventName();
677 if (eventName.find("EDGE_DETECTION_EVENT") != std::string::npos) {
678 return emitOpError("edge detection source should be a regular event, not "
679 "another EDGE_DETECTION_EVENT");
680 }
681 if (eventName.find("COMBO_EVENT") != std::string::npos) {
682 return emitOpError("edge detection source should be a regular event, not "
683 "a COMBO_EVENT (combo events can be used but may have "
684 "unexpected behavior)");
685 }
686
687 return success();
688}
689
690void TraceEdgeEventOp::print(OpAsmPrinter &p) {
691 p << "<" << getSlot() << "> event = <";
692 printTraceEventEnum(p, getEvent().getValue());
693 p << "> trigger = " << getTrigger();
694 p.printOptionalAttrDict((*this)->getAttrs(),
695 /*elidedAttrs=*/{"slot", "event", "trigger"});
696}
697
698ParseResult TraceEdgeEventOp::parse(OpAsmParser &parser,
699 OperationState &result) {
700 IntegerAttr slot;
701 Attribute eventValue;
702 EdgeTriggerAttr trigger;
703
704 if (parser.parseLess() ||
705 parser.parseAttribute(slot, parser.getBuilder().getI32Type(), "slot",
706 result.attributes) ||
707 parser.parseGreater() || parser.parseKeyword("event") ||
708 parser.parseEqual() || parser.parseLess())
709 return failure();
710
711 // Parse event value (string or enum)
712 if (failed(parseTraceEvent(parser, eventValue)))
713 return failure();
714
715 auto event = TraceEventAttr::get(parser.getContext(), eventValue);
716 result.attributes.set("event", event);
717
718 if (parser.parseGreater() || parser.parseKeyword("trigger") ||
719 parser.parseEqual())
720 return failure();
721
722 // Parse trigger as keyword (RISING, FALLING, BOTH)
723 StringRef triggerStr;
724 if (failed(parser.parseKeyword(&triggerStr)))
725 return failure();
726
727 auto triggerEnum = symbolizeEdgeTrigger(triggerStr);
728 if (!triggerEnum) {
729 return parser.emitError(parser.getCurrentLocation(),
730 "unknown edge trigger: ")
731 << triggerStr;
732 }
733 trigger = EdgeTriggerAttr::get(parser.getContext(), *triggerEnum);
734 result.attributes.set("trigger", trigger);
735
736 if (parser.parseOptionalAttrDict(result.attributes))
737 return failure();
738
739 return success();
740}
741
742//===----------------------------------------------------------------------===//
743// TraceHostConfigOp
744//===----------------------------------------------------------------------===//
745
746void TraceHostConfigOp::print(OpAsmPrinter &p) {
747 p << " buffer_size = " << getBufferSize();
748
749 // Only print non-default values
750 if (getArgIdx() != 4)
751 p << " arg_idx = " << getArgIdx();
752
753 if (getRouting() != TraceShimRouting::Single)
754 p << " routing = " << stringifyTraceShimRouting(getRouting());
755
756 p.printOptionalAttrDict(
757 (*this)->getAttrs(),
758 /*elidedAttrs=*/{"buffer_size", "arg_idx", "routing"});
759}
760
761ParseResult TraceHostConfigOp::parse(OpAsmParser &parser,
762 OperationState &result) {
763 // Parse required buffer_size
764 IntegerAttr bufferSize;
765 if (parser.parseKeyword("buffer_size") || parser.parseEqual() ||
766 parser.parseAttribute(bufferSize, parser.getBuilder().getI32Type(),
767 "buffer_size", result.attributes))
768 return failure();
769
770 // Parse arg_idx (default: 4)
771 int32_t argIdxVal = 4;
772 if (succeeded(parser.parseOptionalKeyword("arg_idx"))) {
773 IntegerAttr argIdx;
774 if (parser.parseEqual() ||
775 parser.parseAttribute(argIdx, parser.getBuilder().getI32Type(),
776 "arg_idx", result.attributes))
777 return failure();
778 } else {
779 result.attributes.set("arg_idx",
780 parser.getBuilder().getI32IntegerAttr(argIdxVal));
781 }
782
783 // Parse routing (default: single)
784 TraceShimRouting routingVal = TraceShimRouting::Single;
785 if (succeeded(parser.parseOptionalKeyword("routing"))) {
786 if (parser.parseEqual())
787 return failure();
788 StringRef routingStr;
789 if (failed(parser.parseKeyword(&routingStr)))
790 return failure();
791 auto routing = symbolizeTraceShimRouting(routingStr);
792 if (!routing)
793 return parser.emitError(parser.getCurrentLocation(),
794 "unknown routing strategy: ")
795 << routingStr;
796 routingVal = *routing;
797 }
798 result.attributes.set(
799 "routing", TraceShimRoutingAttr::get(parser.getContext(), routingVal));
800
801 if (parser.parseOptionalAttrDict(result.attributes))
802 return failure();
803
804 return success();
805}
806
807LogicalResult TraceHostConfigOp::verify() {
808 // arg_idx=-1 means "append after last tensor", only valid with single shim
809 if (getArgIdx() == -1) {
810 if (getRouting() != TraceShimRouting::Single) {
811 return emitOpError("arg_idx=-1 (append trace after last tensor) "
812 "only works with single shim destination strategy "
813 "(routing=single)");
814 }
815 }
816
817 // Validate buffer_size is positive
818 if (getBufferSize() <= 0) {
819 return emitOpError("buffer_size must be positive");
820 }
821
822 return success();
823}
824
825//===----------------------------------------------------------------------===//
826// TraceStartConfigOp
827//===----------------------------------------------------------------------===//
828
829LogicalResult TraceStartConfigOp::verify() {
830 // Verify that the referenced symbol exists
831 // This will be checked more thoroughly during lowering
832 auto symbolName = getTraceConfig();
833 if (symbolName.empty()) {
834 return emitOpError("trace config symbol name cannot be empty");
835 }
836
837 return success();
838}
virtual AIEArch getTargetArch() const =0
Return the target architecture.
Include the generated interface declarations.
mlir::ParseResult parseTraceEvent(mlir::AsmParser &parser, mlir::Attribute &result)
void printTraceEventEnum(mlir::AsmPrinter &printer, mlir::Attribute attr)
AIEArch
Definition Passes.h:21