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