From 6fb1ee2e40d76a131c0a6e77db3530a9f8d1155e Mon Sep 17 00:00:00 2001 From: yenjames Date: Fri, 6 Mar 2026 13:53:05 -0700 Subject: [PATCH] Implement a port-level router visualization with channel-based routing and tile components. - Uses standard GraphViz dot format. - Grid layout and tile components placed in AIEVisualShared.h/.cpp for future placement visualization. - Features visualization for shared memory, circuit-switch, and packet-switch connections. - Dot file can be viewed using vscode extension or online dot viewer. --- include/aie/Targets/AIETargets.h | 3 + include/aie/Targets/AIEVisualShared.h | 85 +++ lib/Targets/AIEFlowsToDOT.cpp | 927 +++++++++++++++++++++++++ lib/Targets/AIEFlowsToJSON.cpp | 17 +- lib/Targets/AIETargets.cpp | 11 +- lib/Targets/AIEVisualShared.cpp | 112 +++ lib/Targets/CMakeLists.txt | 2 + test/Targets/AIEFlowsToDOT/simple.mlir | 80 +++ 8 files changed, 1218 insertions(+), 19 deletions(-) create mode 100644 include/aie/Targets/AIEVisualShared.h create mode 100644 lib/Targets/AIEFlowsToDOT.cpp create mode 100644 lib/Targets/AIEVisualShared.cpp create mode 100644 test/Targets/AIEFlowsToDOT/simple.mlir diff --git a/include/aie/Targets/AIETargets.h b/include/aie/Targets/AIETargets.h index 0fcb978d6e0..78fef783a1b 100644 --- a/include/aie/Targets/AIETargets.h +++ b/include/aie/Targets/AIETargets.h @@ -29,6 +29,9 @@ mlir::LogicalResult AIETranslateToHSA(mlir::ModuleOp module, mlir::LogicalResult AIEFlowsToJSON(mlir::ModuleOp module, llvm::raw_ostream &output, llvm::StringRef deviceName = ""); +mlir::LogicalResult AIEFlowsToDOT(mlir::ModuleOp module, + llvm::raw_ostream &output, + llvm::StringRef deviceName = ""); mlir::LogicalResult ADFGenerateCPPGraph(mlir::ModuleOp module, llvm::raw_ostream &output); mlir::LogicalResult AIETranslateSCSimConfig(mlir::ModuleOp module, diff --git a/include/aie/Targets/AIEVisualShared.h b/include/aie/Targets/AIEVisualShared.h new file mode 100644 index 00000000000..63234e16108 --- /dev/null +++ b/include/aie/Targets/AIEVisualShared.h @@ -0,0 +1,85 @@ +//===- AIEVisualShared.h ----------------------------------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// (c) Copyright 2026 Advanced Micro Devices, Inc. +// +//===----------------------------------------------------------------------===// + +#ifndef AIE_TARGETS_AIEVISUALSHARED_H +#define AIE_TARGETS_AIEVISUALSHARED_H + +#include "aie/Dialect/AIE/IR/AIEDialect.h" +#include "aie/Dialect/AIE/IR/AIETargetModel.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +namespace xilinx::AIE { + +/// Color scheme for visualization (fill and stroke colors) +struct ColorScheme { + std::string fill; + std::string stroke; +}; + +/// Grid position for DOT layout +struct GridPosition { + double x; + double y; +}; + +/// Get color scheme for a tile type +const ColorScheme &getTileColorScheme(AIETileType type); + +/// Get color for a route (cycles through color-blind friendly palette) +std::string getRouteColor(int routeIndex); + +/// Get color for shared-memory connections (distinct purple) +std::string getSharedMemoryColor(); + +/// Get style for shared-memory connections (dashed) +std::string getSharedMemoryStyle(); + +/// Emit DOT file header with graph name and layout engine +void emitDOTHeader(llvm::raw_ostream &output, llvm::StringRef graphName, + llvm::StringRef layout = "neato"); + +/// Emit DOT file footer +void emitDOTFooter(llvm::raw_ostream &output); + +/// Get node name for switchbox +std::string getSwitchboxNodeName(int col, int row); + +/// Get node name for core component (or shim tile box) +std::string getCoreNodeName(int col, int row); + +/// Get node name for memory/buffer component +std::string getBufferNodeName(int col, int row); + +/// Get node name for DMA port +/// @param isS2MM true for S2MM (stream-to-memory, input), false for MM2S +/// (memory-to-stream, output) +/// @param channel DMA channel index +std::string getDMANodeName(int col, int row, bool isS2MM, int channel); + +/// Get the next tile coordinates when moving in the specified direction +TileID getNextCoords(int col, int row, WireBundle bundle); + +// Grid layout constants for routing and placement visualization +extern const double kSwitchboxWidth; +extern const double kTileWidth; +extern const double kTileHeight; +extern const double kInternalGap; +extern const double kGridWidth; +extern const double kGridHeight; +extern const double kPortSize; +extern const double kDMAPortHeight; +extern const double kChannelSpacing; + +} // namespace xilinx::AIE + +#endif // AIE_TARGETS_AIEVISUALSHARED_H diff --git a/lib/Targets/AIEFlowsToDOT.cpp b/lib/Targets/AIEFlowsToDOT.cpp new file mode 100644 index 00000000000..69ec31c5fd6 --- /dev/null +++ b/lib/Targets/AIEFlowsToDOT.cpp @@ -0,0 +1,927 @@ +//===- AIEFlowsToDOT.cpp ----------------------------------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// (c) Copyright 2026 Advanced Micro Devices, Inc. +// +//===----------------------------------------------------------------------===// + +#include "aie/Targets/AIETargets.h" +#include "aie/Targets/AIEVisualShared.h" + +#include "aie/Dialect/AIE/IR/AIEDialect.h" +#include "aie/Dialect/AIE/IR/AIETargetModel.h" + +#include "llvm/Support/Debug.h" + +#include +#include +#include +#include + +#define DEBUG_TYPE "aie-flows-to-dot" + +using namespace mlir; +using namespace xilinx; +using namespace xilinx::AIE; + +namespace xilinx::AIE { + +//===----------------------------------------------------------------------===// +// Routing Reconstruction +//===----------------------------------------------------------------------===// + +struct PathSegment { + TileID from, to; + Port fromPort; + Port toPort; +}; + +struct FlowPath { + TileID src, dst; + Port srcPort, dstPort; + std::vector path; + std::vector segments; +}; + +struct SharedMemConnection { + TileID src, dst, allocTile; +}; + +static std::pair +getDMAChannelCounts(const AIETargetModel &targetModel, int col, int row) { + AIETileType type = targetModel.getTileType(col, row); + + if (type == AIETileType::ShimNOCTile || type == AIETileType::ShimPLTile) { + return { + targetModel.getNumDestShimMuxConnections(col, row, WireBundle::DMA), + targetModel.getNumSourceShimMuxConnections(col, row, WireBundle::DMA)}; + } + + return { + targetModel.getNumDestSwitchboxConnections(col, row, WireBundle::DMA), + targetModel.getNumSourceSwitchboxConnections(col, row, WireBundle::DMA)}; +} + +static std::pair, std::vector> +traceThroughSwitchboxes(DeviceOp device, TileOp startTile, Port startPort) { + std::vector path; + std::vector segments; + path.push_back({startTile.colIndex(), startTile.rowIndex()}); + + SwitchboxOp currSwitchbox = nullptr; + for (auto sb : device.getOps()) { + if (sb.colIndex() == startTile.colIndex() && + sb.rowIndex() == startTile.rowIndex()) { + currSwitchbox = sb; + break; + } + } + + if (!currSwitchbox) + return {path, segments}; + + Port currPort = startPort; + + if (startTile.rowIndex() == 0) { + for (auto shimMux : device.getOps()) { + if (shimMux.colIndex() == startTile.colIndex() && + shimMux.rowIndex() == startTile.rowIndex()) { + for (auto connectOp : shimMux.getOps()) { + if (connectOp.getSourceBundle() == currPort.bundle && + connectOp.getSourceChannel() == currPort.channel) { + currPort.bundle = getConnectingBundle(connectOp.getDestBundle()); + currPort.channel = connectOp.getDestChannel(); + break; + } + } + break; + } + } + } + + bool done = false; + while (!done) { + bool foundNext = false; + + for (auto connectOp : currSwitchbox.getOps()) { + if (connectOp.getDestBundle() == WireBundle::DMA || + connectOp.getDestBundle() == WireBundle::Core || + (currSwitchbox.rowIndex() == 0 && + connectOp.getDestBundle() == WireBundle::South)) + continue; + + if (connectOp.getSourceBundle() == currPort.bundle && + connectOp.getSourceChannel() == currPort.channel) { + TileID currTile = {currSwitchbox.colIndex(), currSwitchbox.rowIndex()}; + TileID nextCoords = + getNextCoords(currSwitchbox.colIndex(), currSwitchbox.rowIndex(), + connectOp.getDestBundle()); + if (path.empty() || path.back() != nextCoords) + path.push_back(nextCoords); + + Port outPort = {connectOp.getDestBundle(), connectOp.getDestChannel()}; + Port inPort = {getConnectingBundle(connectOp.getDestBundle()), + connectOp.getDestChannel()}; + segments.push_back({currTile, nextCoords, outPort, inPort}); + + SwitchboxOp nextSwitchbox = nullptr; + for (auto sb : device.getOps()) { + if (sb.colIndex() == nextCoords.col && + sb.rowIndex() == nextCoords.row) { + nextSwitchbox = sb; + break; + } + } + + if (!nextSwitchbox) { + done = true; + break; + } + + currSwitchbox = nextSwitchbox; + currPort.bundle = getConnectingBundle(connectOp.getDestBundle()); + currPort.channel = connectOp.getDestChannel(); + foundNext = true; + break; + } + } + + if (foundNext) + continue; + + for (auto pktRulesOp : currSwitchbox.getOps()) { + if (pktRulesOp.sourcePort().bundle != currPort.bundle || + pktRulesOp.sourcePort().channel != currPort.channel) + continue; + + Region &r = pktRulesOp.getRules(); + Block &b = r.front(); + + for (auto ruleOp : b.getOps()) { + Value amsel = ruleOp.getAmsel(); + + for (auto masterSetOp : currSwitchbox.getOps()) { + for (Value masterAmsel : masterSetOp.getAmsels()) { + if (masterAmsel == amsel) { + Port destPort = masterSetOp.destPort(); + if (destPort.bundle == WireBundle::DMA || + destPort.bundle == WireBundle::Core || + (currSwitchbox.rowIndex() == 0 && + destPort.bundle == WireBundle::South)) { + done = true; + break; + } + + TileID currTile = {currSwitchbox.colIndex(), + currSwitchbox.rowIndex()}; + TileID nextCoords = + getNextCoords(currSwitchbox.colIndex(), + currSwitchbox.rowIndex(), destPort.bundle); + + if (path.empty() || path.back() != nextCoords) + path.push_back(nextCoords); + + Port outPort = destPort; + Port inPort = {getConnectingBundle(destPort.bundle), + destPort.channel}; + segments.push_back({currTile, nextCoords, outPort, inPort}); + + SwitchboxOp nextSwitchbox = nullptr; + for (auto sb : device.getOps()) { + if (sb.colIndex() == nextCoords.col && + sb.rowIndex() == nextCoords.row) { + nextSwitchbox = sb; + break; + } + } + + if (!nextSwitchbox) { + done = true; + break; + } + + currSwitchbox = nextSwitchbox; + currPort.bundle = getConnectingBundle(destPort.bundle); + currPort.channel = destPort.channel; + foundNext = true; + break; + } + } + if (foundNext) + break; + } + if (foundNext) + break; + } + if (foundNext) + break; + } + + if (!foundNext) + done = true; + } + + return {path, segments}; +} + +static std::vector detectCircuitFlows(DeviceOp device) { + std::vector connections; + std::set> processedFlows; + + for (auto flowOp : device.getOps()) { + TileOp source = cast(flowOp.getSource().getDefiningOp()); + Port sourcePort = {flowOp.getSourceBundle(), flowOp.getSourceChannel()}; + Port destPort = {flowOp.getDestBundle(), flowOp.getDestChannel()}; + + if (processedFlows.count({source, sourcePort})) + continue; + processedFlows.insert({source, sourcePort}); + + auto [path, segments] = traceThroughSwitchboxes(device, source, sourcePort); + + if (path.size() >= 2) { + connections.push_back( + {path.front(), path.back(), sourcePort, destPort, path, segments}); + } + } + + return connections; +} + +static std::vector detectPacketFlows(DeviceOp device) { + std::vector connections; + std::set> processedFlows; + + for (auto pktFlowOp : device.getOps()) { + Region &r = pktFlowOp.getPorts(); + Block &b = r.front(); + + TileOp source = nullptr; + TileOp dest = nullptr; + Port sourcePort; + Port destPort; + + for (Operation &op : b.getOperations()) { + if (auto pktSource = dyn_cast(op)) { + source = dyn_cast(pktSource.getTile().getDefiningOp()); + sourcePort = pktSource.port(); + } else if (auto pktDest = dyn_cast(op)) { + dest = dyn_cast(pktDest.getTile().getDefiningOp()); + destPort = pktDest.port(); + } + } + + if (!source) + continue; + + if (processedFlows.count({source, sourcePort})) + continue; + processedFlows.insert({source, sourcePort}); + + auto [path, segments] = traceThroughSwitchboxes(device, source, sourcePort); + + if (path.size() >= 2) { + TileID dstTile = + dest ? TileID{dest.getCol(), dest.getRow()} : path.back(); + connections.push_back( + {path.front(), dstTile, sourcePort, destPort, path, segments}); + } + } + + return connections; +} + +static std::vector +detectSharedMemoryConnections(DeviceOp device, + const AIETargetModel &targetModel) { + std::vector connections; + std::map, TileOp> sharedMemConnect; + + for (auto coreOp : device.getOps()) { + auto coreTileOp = coreOp.getTileOp(); + + coreOp.walk([&](UseLockOp useLockOp) { + auto lock = useLockOp.getLockOp(); + auto lockTileOp = lock.getTileOp(); + + if (coreTileOp == lockTileOp) + return; + if (coreTileOp.isShimTile() != lockTileOp.isShimTile()) + return; + if (coreTileOp.isMemTile() != lockTileOp.isMemTile()) + return; + if (!targetModel.isLegalMemAffinity( + coreTileOp.getCol(), coreTileOp.getRow(), lockTileOp.getCol(), + lockTileOp.getRow())) + return; + + if (!lock.hasName()) + return; + + llvm::StringRef lockName = lock.name().getValue(); + bool isProdLock = lockName.contains("_prod_lock"); + bool isConsLock = lockName.contains("_cons_lock"); + + if (!isProdLock && !isConsLock) + return; + + if (useLockOp.release()) { + std::pair edge = + isConsLock ? std::make_pair(lockTileOp, coreTileOp) + : std::make_pair(coreTileOp, lockTileOp); + + if (sharedMemConnect.count(edge) == 0) { + sharedMemConnect[edge] = lockTileOp; + } + } + }); + } + + for (auto &[tiles, allocTile] : sharedMemConnect) { + auto [srcTile, dstTile] = tiles; + connections.push_back({{srcTile.getCol(), srcTile.getRow()}, + {dstTile.getCol(), dstTile.getRow()}, + {allocTile.getCol(), allocTile.getRow()}}); + } + + LLVM_DEBUG({ + llvm::dbgs() << "Detected " << connections.size() + << " shared-memory connections\n"; + for (const auto &conn : connections) { + llvm::dbgs() << " (" << conn.src.col << "," << conn.src.row << ") -> (" + << conn.dst.col << "," << conn.dst.row << ") alloc at (" + << conn.allocTile.col << "," << conn.allocTile.row << ")\n"; + } + }); + + return connections; +} + +//===----------------------------------------------------------------------===// +// DOT Visualization +//===----------------------------------------------------------------------===// + +struct TileCoords { + double gridX, gridY; + double centerX, centerY; + double left, right, top, bottom; +}; + +static TileCoords getTileCoords(int col, int row) { + double gridX = col * kGridWidth; + double gridY = row * kGridHeight; + double centerX = + gridX + kInternalGap + kSwitchboxWidth + kInternalGap + kTileWidth / 2.0; + double centerY = gridY + kInternalGap + kTileHeight / 2.0; + return {gridX, + gridY, + centerX, + centerY, + centerX - kTileWidth / 2.0, + centerX + kTileWidth / 2.0, + centerY + kTileHeight / 2.0, + centerY - kTileHeight / 2.0}; +} + +struct DMAPortLayout { + double memCenterX; + double portsCenterY; + double portGap; + int numS2MM, numMM2S; +}; + +static DMAPortLayout getDMAPortLayout(const AIETargetModel &targetModel, + const TileCoords &tile, int col, + int row) { + auto [numS2MM, numMM2S] = getDMAChannelCounts(targetModel, col, row); + AIETileType type = targetModel.getTileType(col, row); + + double memCenterX, boxSize; + + if (type == AIETileType::CoreTile) { + boxSize = 1.5; + double coreCenterX = + tile.left + kInternalGap + kPortSize + kInternalGap + boxSize / 2.0; + memCenterX = coreCenterX + boxSize / 2.0 + kInternalGap + kPortSize + + kInternalGap + boxSize / 2.0; + } else { + memCenterX = tile.centerX; + boxSize = kTileWidth - 2 * kInternalGap; + } + + int totalPorts = numS2MM + numMM2S; + double portGap = totalPorts > 1 + ? (boxSize - totalPorts * kPortSize) / (totalPorts - 1) + : 0; + double portsCenterY = tile.top - kInternalGap - kDMAPortHeight / 2.0; + + return {memCenterX, portsCenterY, portGap, numS2MM, numMM2S}; +} + +static double getDMAPortXPosition(const DMAPortLayout &layout, int portIndex) { + int totalPorts = layout.numS2MM + layout.numMM2S; + double totalWidth = + (totalPorts * kPortSize) + ((totalPorts - 1) * layout.portGap); + double startX = layout.memCenterX - (totalWidth / 2.0) + (kPortSize / 2.0); + return startX + portIndex * (kPortSize + layout.portGap); +} + +static void emitBox(llvm::raw_ostream &output, const std::string &nodeName, + const std::string &label, double x, double y, double w, + double h, int fontSize = 28) { + output << " " << nodeName << " ["; + output << "label=\"" << label << "\"; "; + output << llvm::format("pos=\"%.2f,%.2f!\"; ", x, y); + output << "shape=rectangle; fixedsize=true; "; + output << llvm::format("width=%.2f; height=%.2f; ", w, h); + output + << "fillcolor=\"white\"; color=\"black\"; style=filled; penwidth=1.5; "; + output << "fontsize=" << fontSize << "; fontname=\"Helvetica\"];\n"; +} + +static void emitSharedMemPort(llvm::raw_ostream &output, int col, int row, + char dir, double x, double y) { + char dirLower = std::tolower(dir); + output << " tile_" << col << "_" << row << "_smport_" << dirLower << " ["; + output << "label=\"" << dir << "\"; shape=rectangle; fixedsize=true; "; + output << llvm::format("width=%.2f; height=%.2f; ", kPortSize, kPortSize); + output << llvm::format("pos=\"%.2f,%.2f!\"; ", x, y); + output << "fillcolor=\"white\"; color=\"black\"; style=filled; penwidth=1.2; " + "fontsize=12; fontname=\"Helvetica\"];\n"; +} + +static void emitHelperPoint(llvm::raw_ostream &output, const std::string &name, + double x, double y) { + output << " " << name << " ["; + output << llvm::format("pos=\"%.2f,%.2f!\"; ", x, y); + output << "shape=point; width=0.01; height=0.01];\n"; +} + +static std::string getSwitchboxTileCornerName(int col, int row) { + return "sb_" + std::to_string(col) + "_" + std::to_string(row) + + "_tile_corner"; +} + +static std::string getSwitchboxChannelHelperName(int col, int row, + WireBundle bundle, + int channel) { + std::string bundleName; + switch (bundle) { + case WireBundle::North: + bundleName = "n"; + break; + case WireBundle::South: + bundleName = "s"; + break; + case WireBundle::East: + bundleName = "e"; + break; + case WireBundle::West: + bundleName = "w"; + break; + case WireBundle::DMA: + bundleName = "dma"; + break; + case WireBundle::Core: + bundleName = "core"; + break; + default: + bundleName = "unk"; + break; + } + return "sb_" + std::to_string(col) + "_" + std::to_string(row) + "_ch_" + + bundleName + "_" + std::to_string(channel); +} + +static GridPosition getSwitchboxChannelPoint(int col, int row, + WireBundle bundle, int channel) { + double gridX = col * kGridWidth; + double gridY = row * kGridHeight; + double sbCenterX = gridX + kInternalGap + kSwitchboxWidth / 2.0; + double sbCenterY = + gridY + kInternalGap + kTileHeight + kInternalGap + kSwitchboxWidth / 2.0; + + switch (bundle) { + case WireBundle::North: { + double xOffset = + -kSwitchboxWidth / 2.0 + kChannelSpacing + channel * kChannelSpacing; + return {sbCenterX + xOffset, sbCenterY + kSwitchboxWidth / 2.0}; + } + case WireBundle::South: { + double xOffset = + -kSwitchboxWidth / 2.0 + kChannelSpacing + channel * kChannelSpacing; + return {sbCenterX + xOffset, sbCenterY - kSwitchboxWidth / 2.0}; + } + case WireBundle::East: { + double yOffset = + kSwitchboxWidth / 2.0 - kChannelSpacing - channel * kChannelSpacing; + return {sbCenterX + kSwitchboxWidth / 2.0, sbCenterY + yOffset}; + } + case WireBundle::West: { + double yOffset = + kSwitchboxWidth / 2.0 - kChannelSpacing - channel * kChannelSpacing; + return {sbCenterX - kSwitchboxWidth / 2.0, sbCenterY + yOffset}; + } + case WireBundle::DMA: + case WireBundle::Core: { + // Position at bottom edge to avoid confusion with East/West + double xOffset = + kSwitchboxWidth / 2.0 - kChannelSpacing - channel * kChannelSpacing; + return {sbCenterX + xOffset, sbCenterY - kSwitchboxWidth / 2.0}; + } + default: + return {sbCenterX, sbCenterY}; + } +} + +static void emitTileBackground(llvm::raw_ostream &output, int col, int row, + const AIETargetModel &targetModel) { + auto tile = getTileCoords(col, row); + + output << " tile_" << col << "_" << row << "_bg ["; + output << "label=\"(" << col << "," << row << ")\"; "; + output << "labelloc=b; labeljust=l; "; + output << llvm::format("pos=\"%.2f,%.2f!\"; ", tile.centerX, tile.centerY); + output << "shape=rectangle; fixedsize=true; "; + output << llvm::format("width=%.2f; height=%.2f; ", kTileWidth, kTileHeight); + output << "fillcolor=\"white\"; color=\"black\"; "; + output + << "style=filled; penwidth=1.5; fontsize=34; fontname=\"Helvetica\"];\n"; +} + +static void emitDMAPortRow(llvm::raw_ostream &output, int col, int row, + int numS2MM, int numMM2S, double centerX, + double centerY, double portSize, double portGap) { + int totalPorts = numS2MM + numMM2S; + if (totalPorts == 0) + return; + + double totalPortsWidth = + (totalPorts * portSize) + ((totalPorts - 1) * portGap); + double startX = centerX - (totalPortsWidth / 2.0) + (portSize / 2.0); + + int portIndex = 0; + + for (int i = 0; i < numS2MM; i++) { + double xPos = startX + (portIndex * (portSize + portGap)); + output << " " << getDMANodeName(col, row, true, i) << " ["; + output << "label=\"S\\n2\\nM\\nM\\n" << i << "\"; "; + output << llvm::format("pos=\"%.2f,%.2f!\"; ", xPos, centerY); + output << llvm::format( + "shape=rectangle; fixedsize=true; width=%.2f; height=%.2f; ", portSize, + kDMAPortHeight); + output << "fillcolor=\"white\"; color=\"black\"; style=filled; " + "penwidth=1.2; fontsize=13; fontname=\"Helvetica\"];\n"; + portIndex++; + } + + for (int i = 0; i < numMM2S; i++) { + double xPos = startX + (portIndex * (portSize + portGap)); + output << " " << getDMANodeName(col, row, false, i) << " ["; + output << "label=\"M\\nM\\n2\\nS\\n" << i << "\"; "; + output << llvm::format("pos=\"%.2f,%.2f!\"; ", xPos, centerY); + output << llvm::format( + "shape=rectangle; fixedsize=true; width=%.2f; height=%.2f; ", portSize, + kDMAPortHeight); + output << "fillcolor=\"white\"; color=\"black\"; style=filled; " + "penwidth=1.2; fontsize=13; fontname=\"Helvetica\"];\n"; + portIndex++; + } +} + +static std::string getFlowEndpointNode(int col, int row, const Port &port, + bool isSource) { + if (port.bundle == WireBundle::DMA) { + return getDMANodeName(col, row, !isSource, port.channel); + } + return getCoreNodeName(col, row); +} +static void emitCoreTileComponents(llvm::raw_ostream &output, int col, int row, + const AIETargetModel &targetModel) { + auto tile = getTileCoords(col, row); + auto dma = getDMAPortLayout(targetModel, tile, col, row); + + double boxSize = 1.5; + double coreCenterX = + tile.left + kInternalGap + kPortSize + kInternalGap + boxSize / 2.0; + double memCenterX = coreCenterX + boxSize / 2.0 + kInternalGap + kPortSize + + kInternalGap + boxSize / 2.0; + + emitBox(output, getCoreNodeName(col, row), "Core", coreCenterX, tile.centerY, + boxSize, boxSize); + emitBox(output, getBufferNodeName(col, row), "Memory", memCenterX, + tile.centerY, boxSize, boxSize); + + emitDMAPortRow(output, col, row, dma.numS2MM, dma.numMM2S, dma.memCenterX, + dma.portsCenterY, kPortSize, dma.portGap); + + emitSharedMemPort(output, col, row, 'W', + tile.left + kInternalGap + kPortSize / 2.0, tile.centerY); + emitSharedMemPort(output, col, row, 'E', + coreCenterX + boxSize / 2.0 + kInternalGap + + kPortSize / 2.0, + tile.centerY); + emitSharedMemPort(output, col, row, 'N', coreCenterX, + tile.top - kInternalGap - kPortSize / 2.0); + emitSharedMemPort(output, col, row, 'S', coreCenterX, + tile.bottom + kInternalGap + kPortSize / 2.0); +} + +static void emitMemTileComponents(llvm::raw_ostream &output, int col, int row, + const AIETargetModel &targetModel) { + auto tile = getTileCoords(col, row); + auto dma = getDMAPortLayout(targetModel, tile, col, row); + + double memWidth = kTileWidth - 2 * kInternalGap; + double memHeight = 1.0; + + emitBox(output, getBufferNodeName(col, row), "Memory", tile.centerX, + tile.centerY, memWidth / 2.0, memHeight); + + emitDMAPortRow(output, col, row, dma.numS2MM, dma.numMM2S, dma.memCenterX, + dma.portsCenterY, kPortSize, dma.portGap); +} + +static void emitShimTileComponents(llvm::raw_ostream &output, int col, int row, + const AIETargetModel &targetModel) { + auto tile = getTileCoords(col, row); + auto dma = getDMAPortLayout(targetModel, tile, col, row); + + emitDMAPortRow(output, col, row, dma.numS2MM, dma.numMM2S, dma.memCenterX, + dma.portsCenterY, kPortSize, dma.portGap); +} + +static std::map>> +collectUsedChannels(const std::vector &allFlows) { + std::map>> usedChannels; + + for (const auto &flow : allFlows) { + for (const auto &seg : flow.segments) { + usedChannels[seg.from].insert( + {seg.fromPort.bundle, seg.fromPort.channel}); + usedChannels[seg.to].insert({seg.toPort.bundle, seg.toPort.channel}); + } + usedChannels[flow.src].insert({flow.srcPort.bundle, flow.srcPort.channel}); + usedChannels[flow.dst].insert({flow.dstPort.bundle, flow.dstPort.channel}); + } + + return usedChannels; +} + +//===----------------------------------------------------------------------===// +// DOT Visualization - Main Generation +//===----------------------------------------------------------------------===// + +static void +emitDetailedDOT(llvm::raw_ostream &output, DeviceOp device, + const AIETargetModel &targetModel, + const std::vector &circuitFlows, + const std::vector &packetFlows, + const std::vector &sharedMemConns) { + + emitDOTHeader(output, "AIE_Routing", "neato"); + + output << " // Background tiles\n"; + for (int col = 0; col < targetModel.columns(); col++) { + for (int row = 0; row < targetModel.rows(); row++) { + emitTileBackground(output, col, row, targetModel); + } + } + + output << "\n // Switchboxes\n"; + ColorScheme sbColor = {"#F5F5F5", "#666666"}; + for (int col = 0; col < targetModel.columns(); col++) { + for (int row = 0; row < targetModel.rows(); row++) { + double sbCenterX = + col * kGridWidth + kInternalGap + kSwitchboxWidth / 2.0; + double sbCenterY = row * kGridHeight + kInternalGap + kTileHeight + + kInternalGap + kSwitchboxWidth / 2.0; + + output << " " << getSwitchboxNodeName(col, row) << " ["; + output << "label=\"\"; "; + output << llvm::format("pos=\"%.2f,%.2f!\"; ", sbCenterX, sbCenterY); + output << "fillcolor=\"" << sbColor.fill << "\"; color=\"" + << sbColor.stroke << "\"; "; + output << "style=filled; pin=true; shape=square; "; + output << llvm::format( + "width=%.2f; fontsize=10; fontname=\"Helvetica\"];\n", + kSwitchboxWidth); + } + } + + output << "\n // Tile components\n"; + for (int col = 0; col < targetModel.columns(); col++) { + for (int row = 0; row < targetModel.rows(); row++) { + AIETileType type = targetModel.getTileType(col, row); + + if (type == AIETileType::CoreTile) { + emitCoreTileComponents(output, col, row, targetModel); + } else if (type == AIETileType::MemTile) { + emitMemTileComponents(output, col, row, targetModel); + } else if (type == AIETileType::ShimNOCTile || + type == AIETileType::ShimPLTile) { + emitShimTileComponents(output, col, row, targetModel); + } + } + } + + std::vector allFlows; + allFlows.insert(allFlows.end(), circuitFlows.begin(), circuitFlows.end()); + allFlows.insert(allFlows.end(), packetFlows.begin(), packetFlows.end()); + auto usedChannels = collectUsedChannels(allFlows); + + output << "\n // Helper points for DMA port tops\n"; + for (const auto &flow : allFlows) { + if (flow.srcPort.bundle == WireBundle::DMA) { + auto tile = getTileCoords(flow.src.col, flow.src.row); + auto dma = + getDMAPortLayout(targetModel, tile, flow.src.col, flow.src.row); + double helperX = + getDMAPortXPosition(dma, flow.srcPort.channel + dma.numS2MM); + double helperY = tile.top - kInternalGap; + emitHelperPoint(output, + getDMANodeName(flow.src.col, flow.src.row, false, + flow.srcPort.channel) + + "_top", + helperX, helperY); + } + if (flow.dstPort.bundle == WireBundle::DMA) { + auto tile = getTileCoords(flow.dst.col, flow.dst.row); + auto dma = + getDMAPortLayout(targetModel, tile, flow.dst.col, flow.dst.row); + double helperX = getDMAPortXPosition(dma, flow.dstPort.channel); + double helperY = tile.top - kInternalGap; + emitHelperPoint(output, + getDMANodeName(flow.dst.col, flow.dst.row, true, + flow.dstPort.channel) + + "_top", + helperX, helperY); + } + } + + output << "\n // Helper points for switchbox tile corners\n"; + std::set tilesWithDMAFlows; + for (const auto &flow : allFlows) { + if (flow.srcPort.bundle == WireBundle::DMA || + flow.srcPort.bundle == WireBundle::Core) + tilesWithDMAFlows.insert(flow.src); + if (flow.dstPort.bundle == WireBundle::DMA || + flow.dstPort.bundle == WireBundle::Core) + tilesWithDMAFlows.insert(flow.dst); + } + + for (const auto &tileID : tilesWithDMAFlows) { + double sbCenterX = + tileID.col * kGridWidth + kInternalGap + kSwitchboxWidth / 2.0; + double sbCenterY = tileID.row * kGridHeight + kInternalGap + kTileHeight + + kInternalGap + kSwitchboxWidth / 2.0; + double cornerX = sbCenterX + kSwitchboxWidth / 2.0; + double cornerY = sbCenterY - kSwitchboxWidth / 2.0; + + emitHelperPoint(output, getSwitchboxTileCornerName(tileID.col, tileID.row), + cornerX, cornerY); + } + + output << "\n // Helper points for switchbox channels\n"; + for (const auto &[tile, channels] : usedChannels) { + for (const auto &[bundle, channel] : channels) { + if (bundle == WireBundle::DMA || bundle == WireBundle::Core) + continue; + + GridPosition pt = + getSwitchboxChannelPoint(tile.col, tile.row, bundle, channel); + emitHelperPoint( + output, + getSwitchboxChannelHelperName(tile.col, tile.row, bundle, channel), + pt.x, pt.y); + } + } + + output << "\n // Flow edges\n"; + int flowIndex = 0; + auto emitFlowEdges = [&](const auto &flows) { + for (const auto &conn : flows) { + std::string color = getRouteColor(flowIndex); + std::string label = "route" + std::to_string(flowIndex); + + std::string srcNode = + getFlowEndpointNode(conn.src.col, conn.src.row, conn.srcPort, true); + std::string srcSB = getSwitchboxNodeName(conn.src.col, conn.src.row); + + if (conn.srcPort.bundle == WireBundle::DMA) { + std::string srcHelper = getDMANodeName(conn.src.col, conn.src.row, + false, conn.srcPort.channel) + + "_top"; + std::string sbCorner = + getSwitchboxTileCornerName(conn.src.col, conn.src.row); + output << " " << srcNode << " -> " << srcHelper + << " [style=invis; weight=10];\n"; + output << " " << srcHelper << " -> " << sbCorner; + output << " [label=\"" << label << "\"; color=\"" << color + << "\"; penwidth=4.5];\n"; + output << " " << sbCorner << " -> " << srcSB + << " [style=invis; weight=10];\n"; + } else { + output << " " << srcNode << " -> " << srcSB; + output << " [label=\"" << label << "\"; color=\"" << color + << "\"; penwidth=4.5];\n"; + } + + for (const auto &seg : conn.segments) { + std::string fromSB = getSwitchboxNodeName(seg.from.col, seg.from.row); + std::string toSB = getSwitchboxNodeName(seg.to.col, seg.to.row); + std::string fromHelper = getSwitchboxChannelHelperName( + seg.from.col, seg.from.row, seg.fromPort.bundle, + seg.fromPort.channel); + std::string toHelper = getSwitchboxChannelHelperName( + seg.to.col, seg.to.row, seg.toPort.bundle, seg.toPort.channel); + + output << " " << fromSB << " -> " << fromHelper + << " [style=invis; weight=10];\n"; + output << " " << fromHelper << " -> " << toHelper; + output << " [label=\"" << label << "\"; color=\"" << color + << "\"; penwidth=4.5];\n"; + output << " " << toHelper << " -> " << toSB + << " [style=invis; weight=10];\n"; + } + + std::string dstNode = + getFlowEndpointNode(conn.dst.col, conn.dst.row, conn.dstPort, false); + std::string dstSB = getSwitchboxNodeName(conn.dst.col, conn.dst.row); + + if (conn.dstPort.bundle == WireBundle::DMA) { + std::string dstHelper = getDMANodeName(conn.dst.col, conn.dst.row, true, + conn.dstPort.channel) + + "_top"; + std::string sbCorner = + getSwitchboxTileCornerName(conn.dst.col, conn.dst.row); + output << " " << dstSB << " -> " << sbCorner + << " [style=invis; weight=10];\n"; + output << " " << sbCorner << " -> " << dstHelper; + output << " [label=\"" << label << "\"; color=\"" << color + << "\"; penwidth=4.5];\n"; + output << " " << dstHelper << " -> " << dstNode + << " [style=invis; weight=10];\n"; + } else { + output << " " << dstSB << " -> " << dstNode; + output << " [label=\"" << label << "\"; color=\"" << color + << "\"; penwidth=4.5];\n"; + } + + flowIndex++; + } + }; + + emitFlowEdges(circuitFlows); + emitFlowEdges(packetFlows); + + output << "\n // Shared-memory edges\n"; + + for (const auto &conn : sharedMemConns) { + int colDiff = conn.dst.col - conn.allocTile.col; + int rowDiff = conn.dst.row - conn.allocTile.row; + + std::string smPortSuffix; + if (colDiff == 1 && rowDiff == 0) { + smPortSuffix = "_smport_w"; + } else if (colDiff == 0 && rowDiff == -1) { + smPortSuffix = "_smport_n"; + } else if (colDiff == 0 && rowDiff == 1) { + smPortSuffix = "_smport_s"; + } else { + llvm_unreachable("Invalid shared memory connection direction"); + } + output << " " << getBufferNodeName(conn.allocTile.col, conn.allocTile.row); + output << " -> tile_" << conn.dst.col << "_" << conn.dst.row + << smPortSuffix; + output << " [label=\"shared_mem\"; "; + output << "color=\"" << getSharedMemoryColor() << "\"; "; + output << "style=" << getSharedMemoryStyle() << "; "; + output << "penwidth=4.5];\n"; + } + + emitDOTFooter(output); +} + +LogicalResult AIEFlowsToDOT(ModuleOp module, llvm::raw_ostream &output, + llvm::StringRef deviceName) { + DeviceOp device = AIE::DeviceOp::getForSymbolInModule(module, deviceName); + if (!device) + return module.emitOpError("expected AIE.device operation"); + + const AIETargetModel &targetModel = device.getTargetModel(); + + std::vector circuitFlows = detectCircuitFlows(device); + std::vector packetFlows = detectPacketFlows(device); + std::vector sharedMemConns = + detectSharedMemoryConnections(device, targetModel); + emitDetailedDOT(output, device, targetModel, circuitFlows, packetFlows, + sharedMemConns); + + return success(); +} + +} // namespace xilinx::AIE diff --git a/lib/Targets/AIEFlowsToJSON.cpp b/lib/Targets/AIEFlowsToJSON.cpp index 61048eaf938..e0b9aaa8401 100644 --- a/lib/Targets/AIEFlowsToJSON.cpp +++ b/lib/Targets/AIEFlowsToJSON.cpp @@ -14,6 +14,7 @@ */ #include "aie/Targets/AIETargets.h" +#include "aie/Targets/AIEVisualShared.h" #include "aie/Dialect/AIE/IR/AIEDialect.h" @@ -30,22 +31,6 @@ using namespace xilinx::AIE; namespace xilinx::AIE { -// returns coordinates in the direction indicated by bundle -TileID getNextCoords(int col, int row, WireBundle bundle) { - switch (bundle) { - case WireBundle::North: - return {col, row + 1}; - case WireBundle::South: - return {col, row - 1}; - case WireBundle::East: - return {col + 1, row}; - case WireBundle::West: - return {col - 1, row}; - default: - return {col, row}; - } -} - void translateSwitchboxes(DeviceOp targetOp, raw_ostream &output) { // count flow sources and destinations std::map sourceCounts; diff --git a/lib/Targets/AIETargets.cpp b/lib/Targets/AIETargets.cpp index fa771150368..101bb89ceb4 100644 --- a/lib/Targets/AIETargets.cpp +++ b/lib/Targets/AIETargets.cpp @@ -104,9 +104,8 @@ void writeBufferMap(raw_ostream &output, BufferOp buf, int offset) { std::string bufName(buf.name().getValue()); int bufferBaseAddr = getBufferBaseAddress(buf); int numBytes = buf.getAllocationSize(); - output << "_symbol " << bufName << " " - << "0x" << llvm::utohexstr(offset + bufferBaseAddr) << " " << numBytes - << '\n'; + output << "_symbol " << bufName << " " << "0x" + << llvm::utohexstr(offset + bufferBaseAddr) << " " << numBytes << '\n'; } LogicalResult AIETranslateToTargetArch(ModuleOp module, raw_ostream &output, @@ -329,6 +328,12 @@ void registerAIETranslations() { return AIEFlowsToJSON(module, output, deviceName); }, registerDialects); + TranslateFromMLIRRegistration registrationFlowsDOT( + "aie-flows-to-dot", "Translate AIE flows to GraphViz DOT format", + [](ModuleOp module, raw_ostream &output) { + return AIEFlowsToDOT(module, output, deviceName); + }, + registerDialects); TranslateFromMLIRRegistration registrationXPE( "aie-mlir-to-xpe", "Translate AIE design to XPE file for simulation", [](ModuleOp module, raw_ostream &output) { diff --git a/lib/Targets/AIEVisualShared.cpp b/lib/Targets/AIEVisualShared.cpp new file mode 100644 index 00000000000..1792d5dc387 --- /dev/null +++ b/lib/Targets/AIEVisualShared.cpp @@ -0,0 +1,112 @@ +//===- AIEVisualShared.cpp --------------------------------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// (c) Copyright 2026 Advanced Micro Devices, Inc. +// +//===----------------------------------------------------------------------===// + +#include "aie/Targets/AIEVisualShared.h" + +using namespace xilinx::AIE; + +namespace xilinx::AIE { + +static const std::map kTileColorSchemes = { + {AIETileType::CoreTile, {"#DAE8FC", "#6C8EBF"}}, // Blue + {AIETileType::MemTile, {"#D5E8D4", "#82B366"}}, // Green + {AIETileType::ShimNOCTile, {"#FFE6CC", "#D79B00"}}, // Orange + {AIETileType::ShimPLTile, {"#E1D5E7", "#9673A6"}}, // Purple +}; + +// Color-blind friendly palette for routes +// From Wong's color-blind friendly palette +static const std::vector kRouteColorPalette = { + "steelblue3", // Steel blue + "#D55E00", // Vermillion + "#009E73", // Bluish green + "#F0E442", // Yellow + "#0072B2", // Blue + "#E69F00", // Orange + "#56B4E9", // Sky blue + "#CC79A7" // Reddish purple +}; + +// Special color for shared memory connections +static const std::string kSharedMemoryColor = "#CC0000"; // Red +static const std::string kSharedMemoryStyle = "dashed"; + +const ColorScheme &getTileColorScheme(AIETileType type) { + auto it = kTileColorSchemes.find(type); + if (it != kTileColorSchemes.end()) + return it->second; + + // Default color for unknown tile types + static const ColorScheme defaultColor = {"#F5F5F5", "#666666"}; // Grey + return defaultColor; +} + +std::string getRouteColor(int routeIndex) { + return kRouteColorPalette[routeIndex % kRouteColorPalette.size()]; +} + +std::string getSharedMemoryColor() { return kSharedMemoryColor; } + +std::string getSharedMemoryStyle() { return kSharedMemoryStyle; } + +void emitDOTHeader(llvm::raw_ostream &output, llvm::StringRef graphName, + llvm::StringRef layout) { + output << "digraph " << graphName << " {\n"; + output << " layout=" << layout << ";\n"; + output << " graph[outputMode=nodesfirst; splines=false];\n"; + output << " node[shape=square; style=filled; penwidth=2.5; fontsize=35];\n"; + output << "\n"; +} + +void emitDOTFooter(llvm::raw_ostream &output) { output << "}\n"; } + +std::string getSwitchboxNodeName(int col, int row) { + return "tile_" + std::to_string(col) + "_" + std::to_string(row) + "_sb"; +} + +std::string getCoreNodeName(int col, int row) { + return "tile_" + std::to_string(col) + "_" + std::to_string(row) + "_core"; +} + +std::string getBufferNodeName(int col, int row) { + return "tile_" + std::to_string(col) + "_" + std::to_string(row) + "_memory"; +} + +std::string getDMANodeName(int col, int row, bool isS2MM, int channel) { + return "tile_" + std::to_string(col) + "_" + std::to_string(row) + "_dma_" + + (isS2MM ? "s2mm_" : "mm2s_") + std::to_string(channel); +} + +TileID getNextCoords(int col, int row, WireBundle bundle) { + switch (bundle) { + case WireBundle::North: + return {col, row + 1}; + case WireBundle::South: + return {col, row - 1}; + case WireBundle::East: + return {col + 1, row}; + case WireBundle::West: + return {col - 1, row}; + default: + return {col, row}; + } +} + +const double kSwitchboxWidth = 1.5; +const double kTileWidth = 5.0; +const double kTileHeight = 4.5; +const double kInternalGap = 0.2; +const double kGridWidth = kTileWidth + kSwitchboxWidth + 2 * kInternalGap; +const double kGridHeight = kTileHeight + kSwitchboxWidth + 2 * kInternalGap; +const double kPortSize = 0.25; +const double kDMAPortHeight = 1.0; +const double kChannelSpacing = 0.15; + +} // namespace xilinx::AIE diff --git a/lib/Targets/CMakeLists.txt b/lib/Targets/CMakeLists.txt index 05ebf380365..14628615c53 100644 --- a/lib/Targets/CMakeLists.txt +++ b/lib/Targets/CMakeLists.txt @@ -42,6 +42,8 @@ add_mlir_library(AIETargets AIETargetSimulationFiles.cpp ADFGenerateCppGraph.cpp AIEFlowsToJSON.cpp + AIEVisualShared.cpp + AIEFlowsToDOT.cpp AIELLVMLink.cpp PARTIAL_SOURCES_INTENDED diff --git a/test/Targets/AIEFlowsToDOT/simple.mlir b/test/Targets/AIEFlowsToDOT/simple.mlir new file mode 100644 index 00000000000..d18fb3fe7a7 --- /dev/null +++ b/test/Targets/AIEFlowsToDOT/simple.mlir @@ -0,0 +1,80 @@ +//===- simple.mlir ---------------------------------------------*- MLIR -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (C) 2026, Advanced Micro Devices, Inc. +// +//===----------------------------------------------------------------------===// + +// RUN: aie-translate --aie-flows-to-dot %s | FileCheck %s + +// CHECK: digraph AIE_Routing { +// CHECK-DAG: tile_0_0_bg [label="(0,0)" +// CHECK-DAG: tile_0_1_bg [label="(0,1)" +// CHECK-DAG: tile_0_2_bg [label="(0,2)" + +// CHECK-DAG: tile_0_0_sb +// CHECK-DAG: tile_0_1_sb +// CHECK-DAG: tile_0_2_sb + +// CHECK-DAG: tile_0_0_dma_mm2s_0 [label="M\nM\n2\nS\n0" +// CHECK-DAG: tile_0_0_dma_s2mm_1 [label="S\n2\nM\nM\n1" +// CHECK-DAG: tile_0_1_dma_s2mm_0 [label="S\n2\nM\nM\n0" +// CHECK-DAG: tile_0_2_dma_mm2s_1 [label="M\nM\n2\nS\n1" +// CHECK-DAG: tile_0_2_core [label="Core" +// CHECK-DAG: tile_0_2_memory [label="Memory" + +// Circuit flow (route0): tile_0_0 DMA:0 -> tile_0_1 DMA:0 +// CHECK-DAG: tile_0_0_dma_mm2s_0_top -> {{.*}} [label="route0" +// CHECK-DAG: tile_0_1_dma_s2mm_0_top [{{.*}}shape=point + +// Packet flow (route1): tile_0_2 DMA:1 -> tile_0_0 DMA:1 +// CHECK-DAG: tile_0_2_dma_mm2s_1_top -> {{.*}} [label="route1" +// CHECK-DAG: tile_0_0_dma_s2mm_1_top [{{.*}}shape=point + +module @aie_module { + aie.device(npu2) { + %tile_0_0 = aie.tile(0, 0) + %tile_0_1 = aie.tile(0, 1) + %tile_0_2 = aie.tile(0, 2) + %switchbox_0_2 = aie.switchbox(%tile_0_2) { + %0 = aie.amsel<0> (0) + %1 = aie.masterset(South : 3, %0) + aie.packet_rules(DMA : 1) { + aie.rule(31, 1, %0) + } + } + aie.packet_flow(1) { + aie.packet_source<%tile_0_2, DMA : 1> + aie.packet_dest<%tile_0_0, DMA : 1> + } + %switchbox_0_0 = aie.switchbox(%tile_0_0) { + aie.connect + %0 = aie.amsel<0> (0) + %1 = aie.masterset(South : 3, %0) + aie.packet_rules(North : 3) { + aie.rule(31, 1, %0) + } + } + %shim_mux_0_0 = aie.shim_mux(%tile_0_0) { + aie.connect + aie.connect + } + %switchbox_0_1 = aie.switchbox(%tile_0_1) { + aie.connect + %0 = aie.amsel<0> (0) + %1 = aie.masterset(South : 3, %0) + aie.packet_rules(North : 3) { + aie.rule(31, 1, %0) + } + } + aie.flow(%tile_0_0, DMA : 0, %tile_0_1, DMA : 0) + aie.wire(%shim_mux_0_0 : North, %switchbox_0_0 : South) + aie.wire(%tile_0_0 : DMA, %shim_mux_0_0 : DMA) + aie.wire(%tile_0_1 : Core, %switchbox_0_1 : Core) + aie.wire(%tile_0_1 : DMA, %switchbox_0_1 : DMA) + aie.wire(%switchbox_0_0 : North, %switchbox_0_1 : South) + } +}