MLIR-AIE
AIEInsertTraceFlows.cpp
Go to the documentation of this file.
1//===- AIEInsertTraceFlows.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) 2026, Advanced Micro Devices, Inc.
8//
9//===----------------------------------------------------------------------===//
10
14
15#include "mlir/IR/Attributes.h"
16#include "mlir/Pass/Pass.h"
17
18#include <map>
19#include <set>
20
21namespace xilinx::AIE {
22#define GEN_PASS_DEF_AIEINSERTTRACEFLOWS
23#include "aie/Dialect/AIE/Transforms/AIEPasses.h.inc"
24} // namespace xilinx::AIE
25
26using namespace mlir;
27using namespace xilinx;
28using namespace xilinx::AIE;
29
30namespace {
31
32struct TraceInfo {
33 TraceOp traceOp;
34 TileOp tile;
35 int packetId;
36 TracePacketType packetType;
37 WireBundle tracePort; // Trace:0 (core) or Trace:1 (mem)
38 int traceChannel; // Port number (0 for core, 1 for mem)
39 std::optional<int>
40 startBroadcast; // Broadcast channel for timer sync (if any)
41 std::optional<int> stopBroadcast; // Broadcast channel for trace stop (if any)
42};
43
44struct ShimInfo {
45 TileOp shimTile;
46 int channel; // S2MM channel
47 int bdId; // Buffer descriptor ID
48 int argIdx; // Runtime sequence argument index
49 int bufferOffset; // Offset in bytes (non-zero when arg_idx=-1)
50 std::vector<TraceInfo> traceSources; // All traces routed to this shim
51 std::optional<int> startBroadcast; // Broadcast to trigger for start
52 std::optional<int> stopBroadcast; // Broadcast to trigger for stop
53};
54
55struct AIEInsertTraceFlowsPass
56 : xilinx::AIE::impl::AIEInsertTraceFlowsBase<AIEInsertTraceFlowsPass> {
57
58 void runOnOperation() override {
59 DeviceOp device = getOperation();
60 OpBuilder builder(device);
61 const auto &targetModel = device.getTargetModel();
62
63 // Verify no LogicalTileOps remain — placement must run before this pass
64 bool hasLogicalTile = false;
65 device.walk([&](LogicalTileOp op) {
66 op.emitError() << "LogicalTileOp must be resolved to TileOp before "
67 "running -aie-insert-trace-flows (run -aie-place-tiles "
68 "first)";
69 hasLogicalTile = true;
70 });
71 if (hasLogicalTile)
72 return signalPassFailure();
73
74 // Phase 1: Collect all trace operations
75 SmallVector<TraceOp> traces;
76 device.walk([&](TraceOp trace) { traces.push_back(trace); });
77
78 if (traces.empty())
79 return;
80
81 // Phase 1b: Find runtime_sequence and trace.host_config within it
82 RuntimeSequenceOp runtimeSeq = nullptr;
83 TraceHostConfigOp hostConfig = nullptr;
84 for (auto &op : device.getBody()->getOperations()) {
85 if (auto seq = dyn_cast<RuntimeSequenceOp>(&op)) {
86 runtimeSeq = seq;
87 for (auto &subOp : seq.getBody().front().getOperations()) {
88 if (auto hc = dyn_cast<TraceHostConfigOp>(&subOp)) {
89 hostConfig = hc;
90 break;
91 }
92 }
93 break;
94 }
95 }
96
97 // Require runtime_sequence when trace ops are present
98 if (!runtimeSeq) {
99 device.emitError()
100 << "aie.trace ops found but no runtime_sequence defined";
101 return signalPassFailure();
102 }
103
104 // Require trace.host_config in runtime_sequence
105 if (!hostConfig) {
106 runtimeSeq.emitError()
107 << "runtime_sequence with traces requires aie.trace.host_config";
108 return signalPassFailure();
109 }
110
111 // Get configuration from host_config
112 int bufferSizeBytes = hostConfig.getBufferSize();
113 int traceArgIdx = hostConfig.getArgIdx();
114 auto routing = hostConfig.getRouting();
115
116 // arg_idx=-1 means "append trace after last tensor"
117 int traceBufferOffset = 0; // in bytes
118 if (traceArgIdx == -1) {
119 auto args = runtimeSeq.getBody().getArguments();
120 assert(!args.empty() && "runtime_sequence must have args for arg_idx=-1");
121
122 Value lastArg = args.back();
123 traceArgIdx = args.size() - 1;
124
125 auto memrefType = cast<MemRefType>(lastArg.getType());
126 traceBufferOffset = memrefType.getNumElements() *
127 (memrefType.getElementTypeBitWidth() / 8);
128 }
129
130 // Remove host_config op
131 hostConfig.erase();
132
133 // Phase 2: Analyze traces and allocate resources
134 std::vector<TraceInfo> traceInfos;
135 int nextPacketId = clPacketIdStart;
136
137 for (auto trace : traces) {
138 auto tile = cast<TileOp>(trace.getTile().getDefiningOp());
139
140 // Find packet ID and type from trace body
141 std::optional<int> packetId;
142 std::optional<TracePacketType> packetType;
143 TracePacketOp existingPacketOp = nullptr;
144 for (auto &op : trace.getBody().getOps()) {
145 if (auto packetOp = dyn_cast<TracePacketOp>(op)) {
146 existingPacketOp = packetOp;
147 packetId = packetOp.getId();
148 packetType = packetOp.getType();
149 break;
150 }
151 }
152
153 // Determine packet type from tile type if not specified
154 if (!packetType) {
155 if (tile.isShimTile()) {
156 packetType = TracePacketType::ShimTile;
157 } else if (tile.isMemTile()) {
158 packetType = TracePacketType::MemTile;
159 } else {
160 // Core tile defaults to core type
161 packetType = TracePacketType::Core;
162 }
163 }
164
165 // Allocate packet ID if not specified
166 if (!packetId) {
167 packetId = nextPacketId++;
168 }
169
170 // If there was no explicit TracePacketOp, materialize one so that
171 // downstream passes (e.g., -aie-trace-to-config) see consistent info.
172 if (!existingPacketOp) {
173 OpBuilder traceBuilder(&trace.getBody().front(),
174 trace.getBody().front().begin());
175 TracePacketOp::create(traceBuilder, trace.getLoc(), *packetId,
176 *packetType);
177 }
178
179 // Determine trace port based on packet type
180 WireBundle tracePort = WireBundle::Trace;
181 int traceChannel = 0;
182 if (*packetType == TracePacketType::Mem) {
183 traceChannel = 1; // Mem trace uses port 1
184 }
185
186 // Find start/stop configuration (broadcast or event)
187 std::optional<int> startBroadcast;
188 std::optional<int> stopBroadcast;
189 bool hasStartConfig = false;
190 bool hasStopConfig = false;
191 for (auto &op : trace.getBody().getOps()) {
192 if (auto startOp = dyn_cast<TraceStartEventOp>(op)) {
193 hasStartConfig = true;
194 if (startOp.getBroadcast())
195 startBroadcast = *startOp.getBroadcast();
196 }
197 if (auto stopOp = dyn_cast<TraceStopEventOp>(op)) {
198 hasStopConfig = true;
199 if (stopOp.getBroadcast())
200 stopBroadcast = *stopOp.getBroadcast();
201 }
202 }
203
204 // Require explicit start/stop configuration
205 if (!hasStartConfig) {
206 trace.emitError() << "trace is missing 'aie.trace.start'";
207 return signalPassFailure();
208 }
209 if (!hasStopConfig) {
210 trace.emitError() << "trace is missing 'aie.trace.stop'";
211 return signalPassFailure();
212 }
213
214 TraceInfo info;
215 info.traceOp = trace;
216 info.tile = tile;
217 info.packetId = *packetId;
218 info.packetType = *packetType;
219 info.tracePort = tracePort;
220 info.traceChannel = traceChannel;
221 info.startBroadcast = startBroadcast;
222 info.stopBroadcast = stopBroadcast;
223 traceInfos.push_back(info);
224 }
225
226 // Phase 2b: Select shim tiles based on routing strategy
227 std::map<int, ShimInfo> shimInfos; // col -> ShimInfo
228
229 if (routing == TraceShimRouting::Single) {
230 // All traces route to column 0 shim
231 int targetCol = 0;
232 TileOp shimTile = nullptr;
233 for (auto tile : device.getOps<TileOp>()) {
234 if (tile.getCol() == targetCol && tile.getRow() == 0) {
235 shimTile = tile;
236 break;
237 }
238 }
239
240 if (!shimTile) {
241 builder.setInsertionPointToStart(&device.getRegion().front());
242 shimTile = TileOp::create(builder, device.getLoc(), targetCol, 0);
243 }
244
245 ShimInfo shimInfo;
246 shimInfo.shimTile = shimTile;
247 shimInfo.channel = clShimChannel;
248 shimInfo.bdId = clDefaultBdId;
249 shimInfo.argIdx = traceArgIdx;
250 shimInfo.bufferOffset = traceBufferOffset;
251 shimInfo.traceSources = traceInfos;
252 // Collect broadcast channels from traces that use them
253 for (auto &trace : traceInfos) {
254 if (trace.startBroadcast && !shimInfo.startBroadcast)
255 shimInfo.startBroadcast = trace.startBroadcast;
256 if (trace.stopBroadcast && !shimInfo.stopBroadcast)
257 shimInfo.stopBroadcast = trace.stopBroadcast;
258 }
259
260 // Map all source columns to the single shim
261 for (auto &info : traceInfos) {
262 int col = info.tile.getCol();
263 if (shimInfos.find(col) == shimInfos.end()) {
264 shimInfos[col] = shimInfo;
265 }
266 }
267 if (shimInfos.find(targetCol) == shimInfos.end()) {
268 shimInfos[targetCol] = shimInfo;
269 }
270 }
271
272 // Phase 2c: Rewrite broadcast to USER_EVENT for destination shim tiles
273 // (shim can't listen for its own broadcast)
274 for (auto &info : traceInfos) {
275 if (!info.tile.isShimTile())
276 continue;
277
278 int col = info.tile.getCol();
279 auto shimIt = shimInfos.find(col);
280 if (shimIt == shimInfos.end())
281 continue;
282
283 // Check if this traced shim tile IS the destination shim
284 if (shimIt->second.shimTile.getTileID() != info.tile.getTileID())
285 continue;
286
287 // This shim is both traced and the destination - rewrite start/stop ops
288 // to use USER_EVENT_1/0 instead of broadcast
289 for (auto &op : info.traceOp.getBody().getOps()) {
290 if (auto startOp = dyn_cast<TraceStartEventOp>(op)) {
291 if (startOp.getBroadcast()) {
292 // Replace broadcast with USER_EVENT_1
293 startOp->removeAttr("broadcast");
294 startOp->setAttr("event", TraceEventAttr::get(builder.getContext(),
295 builder.getStringAttr(
296 "USER_EVENT_1")));
297 }
298 }
299 if (auto stopOp = dyn_cast<TraceStopEventOp>(op)) {
300 if (stopOp.getBroadcast()) {
301 // Replace broadcast with USER_EVENT_0
302 stopOp->removeAttr("broadcast");
303 stopOp->setAttr("event", TraceEventAttr::get(builder.getContext(),
304 builder.getStringAttr(
305 "USER_EVENT_0")));
306 }
307 }
308 }
309
310 info.startBroadcast = std::nullopt;
311 info.stopBroadcast = std::nullopt;
312 }
313
314 // Phase 3: Insert packet flows
315 // Insert before the device terminator
316 Block &deviceBlock = device.getRegion().front();
317 builder.setInsertionPoint(deviceBlock.getTerminator());
318
319 for (auto &info : traceInfos) {
320 // Find target shim for this trace
321 int col = info.tile.getCol();
322 ShimInfo &shimInfo = shimInfos[col];
323
324 // Create packet flow
325 auto packetFlowOp = PacketFlowOp::create(
326 builder, device.getLoc(), builder.getI8IntegerAttr(info.packetId),
327 nullptr, nullptr);
328
329 Block *flowBody = new Block();
330 packetFlowOp.getPorts().push_back(flowBody);
331 OpBuilder flowBuilder = OpBuilder::atBlockEnd(flowBody);
332
333 PacketSourceOp::create(flowBuilder, device.getLoc(),
334 Value(info.tile.getResult()), info.tracePort,
335 info.traceChannel);
336
337 PacketDestOp::create(flowBuilder, device.getLoc(),
338 Value(shimInfo.shimTile.getResult()),
339 WireBundle::DMA, shimInfo.channel);
340
341 EndOp::create(flowBuilder, device.getLoc());
342
343 packetFlowOp->setAttr("keep_pkt_header", builder.getBoolAttr(true));
344 }
345
346 // Phase 4: Insert runtime sequence operations
347 Block &seqBlock = runtimeSeq.getBody().front();
348
349 // Find the last TraceStartConfigOp in the runtime sequence
350 Operation *lastStartConfig = nullptr;
351 for (auto &op : seqBlock.getOperations()) {
352 if (isa<TraceStartConfigOp>(op)) {
353 lastStartConfig = &op;
354 }
355 }
356
357 // Insert after the last start_config op, or at start if none found
358 if (lastStartConfig) {
359 builder.setInsertionPointAfter(lastStartConfig);
360 } else {
361 builder.setInsertionPointToStart(&seqBlock);
362 }
363
364 // 4b. Configure timer sync for tiles using broadcast-based start
365 std::set<std::tuple<int, int, bool>> processedTiles;
366 for (auto &info : traceInfos) {
367 if (!info.startBroadcast)
368 continue;
369
370 int col = info.tile.getCol();
371 int row = info.tile.getRow();
372 bool isMemTrace = info.packetType == TracePacketType::Mem;
373
374 if (processedTiles.count({col, row, isMemTrace}))
375 continue;
376 processedTiles.insert({col, row, isMemTrace});
377
378 // Skip destination shim tiles (handled in 4f)
379 if (info.tile.isShimTile()) {
380 auto shimIt = shimInfos.find(col);
381 if (shimIt != shimInfos.end() &&
382 shimIt->second.shimTile.getTileID() == info.tile.getTileID()) {
383 continue;
384 }
385 }
386
387 // Compute timer control address
388 uint32_t timerCtrlAddr =
389 computeTimerCtrlAddress(info.tile, targetModel, isMemTrace);
390
391 std::string broadcastEventName;
392 if (info.tile.isShimTile()) {
393 broadcastEventName =
394 "BROADCAST_A_" + std::to_string(*info.startBroadcast);
395 } else {
396 broadcastEventName =
397 "BROADCAST_" + std::to_string(*info.startBroadcast);
398 }
399
400 auto broadcastEvent = targetModel.lookupEvent(
401 broadcastEventName, info.tile.getTileID(), isMemTrace);
402 if (!broadcastEvent) {
403 info.traceOp.emitError() << "Failed to lookup broadcast event '"
404 << broadcastEventName << "'";
405 return signalPassFailure();
406 }
407 uint32_t timerCtrlValue = *broadcastEvent << 8;
408
409 xilinx::AIEX::NpuWrite32Op::create(
410 builder, runtimeSeq.getLoc(), timerCtrlAddr, timerCtrlValue, nullptr,
411 builder.getI32IntegerAttr(col), builder.getI32IntegerAttr(row));
412 }
413
414 // 4c-4f. Insert per-shim configurations
415 std::set<int> configuredShimCols;
416 for (auto &[col, shimInfo] : shimInfos) {
417 int shimCol = shimInfo.shimTile.getCol();
418 if (!configuredShimCols.insert(shimCol).second)
419 continue;
420
421 // Convert buffer size (bytes) to 32-bit words for buffer_length parameter
422 int bufferLengthWords = bufferSizeBytes / 4;
423
424 // 4c. Write buffer descriptor
425 xilinx::AIEX::NpuWriteBdOp::create(
426 builder, runtimeSeq.getLoc(),
427 shimCol, // column
428 shimInfo.bdId, // bd_id
429 bufferLengthWords, // buffer_length (in 32-bit words)
430 0, // buffer_offset
431 1, // enable_packet
432 0, // out_of_order_id
433 0, // packet_id (not used for reception)
434 0, // packet_type (not used for reception)
435 0, 0, 0, 0, 0,
436 0, // d0_size, d0_stride, d1_size, d1_stride, d2_size, d2_stride
437 0, 0, 0, // iteration_current, iteration_size, iteration_stride
438 0, // next_bd
439 0, // row
440 0, // use_next_bd
441 1, // valid_bd
442 0, 0, 0, 0, 0, // lock_rel_val, lock_rel_id, lock_acq_enable,
443 // lock_acq_val, lock_acq_id
444 0, 0, 0, 0, 0, 0, // d0_zero_before, d1_zero_before, d2_zero_before,
445 // d0_zero_after, d1_zero_after, d2_zero_after
446 clTraceBurstLength // burst_length
447 );
448
449 // 4d. Address patch (arg_plus = offset when arg_idx=-1)
450 uint32_t bdAddress = computeBDAddress(shimCol, shimInfo.bdId,
451 shimInfo.shimTile, targetModel);
452 xilinx::AIEX::NpuAddressPatchOp::create(builder, runtimeSeq.getLoc(),
453 bdAddress, shimInfo.argIdx,
454 shimInfo.bufferOffset);
455
456 // 4e. DMA channel configuration
457 uint32_t ctrlAddr =
458 computeCtrlAddress(DMAChannelDir::S2MM, shimInfo.channel,
459 shimInfo.shimTile, targetModel);
460 xilinx::AIEX::NpuMaskWrite32Op::create(
461 builder, runtimeSeq.getLoc(), ctrlAddr, 3840, 7936, // value, mask
462 nullptr, builder.getI32IntegerAttr(shimCol),
463 builder.getI32IntegerAttr(0));
464
465 // Push BD to task queue
466 uint32_t taskQueueAddr =
467 computeTaskQueueAddress(DMAChannelDir::S2MM, shimInfo.channel,
468 shimInfo.shimTile, targetModel);
469 uint32_t bdIdWithToken = (1U << 31) | shimInfo.bdId; // enable_token = 1
470 xilinx::AIEX::NpuWrite32Op::create(
471 builder, runtimeSeq.getLoc(), taskQueueAddr, bdIdWithToken, nullptr,
472 builder.getI32IntegerAttr(shimCol), builder.getI32IntegerAttr(0));
473
474 // 4f. Shim timer and broadcast control (only if start broadcast is used)
475 if (shimInfo.startBroadcast) {
476 // Look up USER_EVENT_1 from target model (trigger event)
477 auto userEvent1 = targetModel.lookupEvent(
478 "USER_EVENT_1", shimInfo.shimTile.getTileID(), false);
479 if (!userEvent1)
480 llvm::report_fatal_error("Failed to lookup USER_EVENT_1 event");
481
482 uint32_t shimTimerCtrlAddr =
483 computeTimerCtrlAddress(shimInfo.shimTile, targetModel, false);
484 xilinx::AIEX::NpuWrite32Op::create(
485 builder, runtimeSeq.getLoc(), shimTimerCtrlAddr, *userEvent1 << 8,
486 nullptr, builder.getI32IntegerAttr(shimCol),
487 builder.getI32IntegerAttr(0));
488
489 // Configure broadcast register with USER_EVENT_1
490 std::string broadcastRegName =
491 "Event_Broadcast" + std::to_string(*shimInfo.startBroadcast) + "_A";
492 const RegisterInfo *broadcastReg = targetModel.lookupRegister(
493 broadcastRegName, shimInfo.shimTile.getTileID());
494 if (!broadcastReg)
495 llvm::report_fatal_error(llvm::Twine("Failed to lookup ") +
496 broadcastRegName);
497 xilinx::AIEX::NpuWrite32Op::create(
498 builder, runtimeSeq.getLoc(), broadcastReg->offset, *userEvent1,
499 nullptr, builder.getI32IntegerAttr(shimCol),
500 builder.getI32IntegerAttr(0));
501
502 // Generate USER_EVENT_1 to trigger the broadcast
503 const RegisterInfo *eventGenReg = targetModel.lookupRegister(
504 "Event_Generate", shimInfo.shimTile.getTileID());
505 if (!eventGenReg)
506 llvm::report_fatal_error("Failed to lookup Event_Generate register");
507 xilinx::AIEX::NpuWrite32Op::create(
508 builder, runtimeSeq.getLoc(), eventGenReg->offset, *userEvent1,
509 nullptr, builder.getI32IntegerAttr(shimCol),
510 builder.getI32IntegerAttr(0));
511 }
512 }
513
514 // Phase 4g: Insert trace stop at end of runtime sequence
515 builder.setInsertionPointToEnd(&seqBlock);
516
517 std::set<int> stoppedShimCols;
518 for (auto &[col, shimInfo] : shimInfos) {
519 if (!shimInfo.stopBroadcast)
520 continue;
521
522 int shimCol = shimInfo.shimTile.getCol();
523 if (!stoppedShimCols.insert(shimCol).second)
524 continue;
525
526 auto userEvent0 = targetModel.lookupEvent(
527 "USER_EVENT_0", shimInfo.shimTile.getTileID(), false);
528 if (!userEvent0)
529 llvm::report_fatal_error("Failed to lookup USER_EVENT_0 event");
530
531 std::string broadcastRegName =
532 "Event_Broadcast" + std::to_string(*shimInfo.stopBroadcast) + "_A";
533 const RegisterInfo *broadcastReg = targetModel.lookupRegister(
534 broadcastRegName, shimInfo.shimTile.getTileID());
535 if (!broadcastReg)
536 llvm::report_fatal_error(llvm::Twine("Failed to lookup ") +
537 broadcastRegName);
538 xilinx::AIEX::NpuWrite32Op::create(
539 builder, runtimeSeq.getLoc(), broadcastReg->offset, *userEvent0,
540 nullptr, builder.getI32IntegerAttr(shimCol),
541 builder.getI32IntegerAttr(0));
542
543 const RegisterInfo *stopEventGenReg = targetModel.lookupRegister(
544 "Event_Generate", shimInfo.shimTile.getTileID());
545 if (!stopEventGenReg)
546 llvm::report_fatal_error("Failed to lookup Event_Generate register");
547 xilinx::AIEX::NpuWrite32Op::create(
548 builder, runtimeSeq.getLoc(), stopEventGenReg->offset, *userEvent0,
549 nullptr, builder.getI32IntegerAttr(shimCol),
550 builder.getI32IntegerAttr(0));
551 }
552 }
553
554private:
555 // Compute buffer descriptor base address for the buffer address field
556 uint32_t computeBDAddress(int col, int bdId, TileOp shimTile,
557 const AIETargetModel &tm) {
558 // Use register database to lookup BD0 address, then add stride * bdId
559 // The buffer address field is at offset +4 within each BD descriptor
560 const RegisterInfo *bdReg =
561 tm.lookupRegister("DMA_BD0_0", shimTile.getTileID());
562 if (!bdReg)
563 llvm::report_fatal_error("Failed to lookup DMA_BD0_0 register");
564 const uint32_t BD_STRIDE = 0x20;
565 const uint32_t BUFFER_ADDR_OFFSET = 4; // buffer address is 2nd word in BD
566 return (col << tm.getColumnShift()) |
567 (bdReg->offset + bdId * BD_STRIDE + BUFFER_ADDR_OFFSET);
568 }
569
570 // Compute DMA task queue address
571 uint32_t computeTaskQueueAddress(DMAChannelDir dir, int channel,
572 TileOp shimTile, const AIETargetModel &tm) {
573 std::string regName;
574 if (dir == DMAChannelDir::S2MM) {
575 regName =
576 (channel == 0) ? "DMA_S2MM_0_Task_Queue" : "DMA_S2MM_1_Task_Queue";
577 } else { // MM2S
578 regName =
579 (channel == 0) ? "DMA_MM2S_0_Task_Queue" : "DMA_MM2S_1_Task_Queue";
580 }
581 const RegisterInfo *reg = tm.lookupRegister(regName, shimTile.getTileID());
582 if (!reg)
583 llvm::report_fatal_error(llvm::Twine("Failed to lookup ") + regName);
584 return reg->offset;
585 }
586
587 // Compute DMA control register address
588 uint32_t computeCtrlAddress(DMAChannelDir dir, int channel, TileOp shimTile,
589 const AIETargetModel &tm) {
590 std::string regName;
591 if (dir == DMAChannelDir::S2MM) {
592 regName = (channel == 0) ? "DMA_S2MM_0_Ctrl" : "DMA_S2MM_1_Ctrl";
593 } else { // MM2S
594 regName = (channel == 0) ? "DMA_MM2S_0_Ctrl" : "DMA_MM2S_1_Ctrl";
595 }
596 const RegisterInfo *reg = tm.lookupRegister(regName, shimTile.getTileID());
597 if (!reg)
598 llvm::report_fatal_error(llvm::Twine("Failed to lookup ") + regName);
599 return reg->offset;
600 }
601
602 // Compute timer control address based on tile type
603 uint32_t computeTimerCtrlAddress(TileOp tile, const AIETargetModel &tm,
604 bool isMemTrace) {
605 // Use register database to lookup Timer_Control for the appropriate module
606 const RegisterInfo *reg =
607 tm.lookupRegister("Timer_Control", tile.getTileID(), isMemTrace);
608 if (!reg)
609 llvm::report_fatal_error("Failed to lookup Timer_Control register");
610 return reg->offset;
611 }
612};
613
614} // namespace
615
616std::unique_ptr<OperationPass<DeviceOp>>
618 return std::make_unique<AIEInsertTraceFlowsPass>();
619}
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 uint32_t getColumnShift() const =0
Include the generated interface declarations.
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEInsertTraceFlowsPass()