|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | +// Copyright (C) 2025 Advanced Micro Devices, Inc. All rights reserved |
| 3 | + |
| 4 | +#define XDP_CORE_SOURCE |
| 5 | + |
| 6 | +#include "aie_trace_config_v3_filetype.h" |
| 7 | +#include "core/common/message.h" |
| 8 | +#include "xdp/profile/database/static_info/aie_util.h" |
| 9 | +#include "xdp/profile/plugin/vp_base/utility.h" |
| 10 | + |
| 11 | +#include <boost/algorithm/string.hpp> |
| 12 | +#include <boost/property_tree/json_parser.hpp> |
| 13 | +#include <boost/property_tree/ptree.hpp> |
| 14 | +#include <set> |
| 15 | +#include <algorithm> |
| 16 | + |
| 17 | +namespace xdp::aie { |
| 18 | +namespace pt = boost::property_tree; |
| 19 | +using severity_level = xrt_core::message::severity_level; |
| 20 | + |
| 21 | +AIETraceConfigV3Filetype::AIETraceConfigV3Filetype(boost::property_tree::ptree& aie_project) |
| 22 | +: AIETraceConfigFiletype(aie_project) {} |
| 23 | + |
| 24 | +std::vector<std::string> |
| 25 | +AIETraceConfigV3Filetype::getValidKernels() const |
| 26 | +{ |
| 27 | + std::vector<std::string> kernels; |
| 28 | + |
| 29 | + // Collect all kernel to tile mappings |
| 30 | + auto kernelToTileMapping = aie_meta.get_child_optional("aie_metadata.TileMapping.AIEKernelToTileMapping"); |
| 31 | + if (!kernelToTileMapping) { |
| 32 | + xrt_core::message::send(severity_level::info, "XRT", getMessage("TileMapping.AIEKernelToTileMapping")); |
| 33 | + return {}; |
| 34 | + } |
| 35 | + |
| 36 | + std::set<std::string> uniqueKernels; // Use set to avoid duplicates |
| 37 | + |
| 38 | + for (auto const &mapping : kernelToTileMapping.get()) { |
| 39 | + auto functionStr = mapping.second.get<std::string>("function"); |
| 40 | + if (functionStr.empty()) |
| 41 | + continue; |
| 42 | + |
| 43 | + // Extract kernel names from function string |
| 44 | + std::vector<std::string> names; |
| 45 | + boost::split(names, functionStr, boost::is_any_of(".")); |
| 46 | + |
| 47 | + // Add individual kernel components |
| 48 | + for (const auto& name : names) { |
| 49 | + if (!name.empty()) |
| 50 | + uniqueKernels.insert(name); |
| 51 | + } |
| 52 | + |
| 53 | + // Also store the complete function name |
| 54 | + uniqueKernels.insert(functionStr); |
| 55 | + } |
| 56 | + |
| 57 | + // Convert set to vector |
| 58 | + kernels.assign(uniqueKernels.begin(), uniqueKernels.end()); |
| 59 | + return kernels; |
| 60 | +} |
| 61 | + |
| 62 | +std::vector<std::string> |
| 63 | +AIETraceConfigV3Filetype::getValidGraphs() const |
| 64 | +{ |
| 65 | + std::vector<std::string> graphs; |
| 66 | + |
| 67 | + // Collect all kernel to tile mappings |
| 68 | + auto kernelToTileMapping = aie_meta.get_child_optional("aie_metadata.TileMapping.AIEKernelToTileMapping"); |
| 69 | + if (!kernelToTileMapping) { |
| 70 | + xrt_core::message::send(severity_level::info, "XRT", getMessage("TileMapping.AIEKernelToTileMapping")); |
| 71 | + return {}; |
| 72 | + } |
| 73 | + |
| 74 | + std::set<std::string> uniqueGraphs; // Use set to avoid duplicates |
| 75 | + |
| 76 | + for (auto const &mapping : kernelToTileMapping.get()) { |
| 77 | + auto graphStr = mapping.second.get<std::string>("graph"); |
| 78 | + if (graphStr.empty()) |
| 79 | + continue; |
| 80 | + |
| 81 | + // Extract subgraph names from complete graph string |
| 82 | + std::vector<std::string> names; |
| 83 | + boost::split(names, graphStr, boost::is_any_of(".")); |
| 84 | + |
| 85 | + // Add individual subgraph components |
| 86 | + for (const auto& name : names) { |
| 87 | + if (!name.empty()) |
| 88 | + uniqueGraphs.insert(name); |
| 89 | + } |
| 90 | + |
| 91 | + // Add the complete graph name |
| 92 | + uniqueGraphs.insert(graphStr); |
| 93 | + } |
| 94 | + |
| 95 | + // Convert set to vector |
| 96 | + graphs.assign(uniqueGraphs.begin(), uniqueGraphs.end()); |
| 97 | + return graphs; |
| 98 | +} |
| 99 | + |
| 100 | +// Find all AIE or memory tiles associated with a graph and kernel/buffer |
| 101 | +// kernel_name = all : all tiles in graph |
| 102 | +// kernel_name = <kernel> : only tiles used by that specific kernel |
| 103 | +std::vector<tile_type> |
| 104 | +AIETraceConfigV3Filetype::getTiles(const std::string& graph_name, |
| 105 | + module_type type, |
| 106 | + const std::string& kernel_name) const |
| 107 | +{ |
| 108 | + if (type == module_type::mem_tile) |
| 109 | + return getMemoryTiles(graph_name, kernel_name); |
| 110 | + |
| 111 | + auto kernelToTileMapping = aie_meta.get_child_optional("aie_metadata.TileMapping.AIEKernelToTileMapping"); |
| 112 | + if (!kernelToTileMapping) { |
| 113 | + xrt_core::message::send(severity_level::info, "XRT", getMessage("TileMapping.AIEKernelToTileMapping")); |
| 114 | + return {}; |
| 115 | + } |
| 116 | + |
| 117 | + std::map<std::pair<uint8_t, uint8_t>, tile_type> tileMap; // Use map to handle unique tiles by location |
| 118 | + auto rowOffset = getAIETileRowOffset(); |
| 119 | + |
| 120 | + // Parse all kernel mappings |
| 121 | + for (auto const &mapping : kernelToTileMapping.get()) { |
| 122 | + auto graphStr = mapping.second.get<std::string>("graph", ""); |
| 123 | + auto functionStr = mapping.second.get<std::string>("function", ""); |
| 124 | + |
| 125 | + if (graphStr.empty() || functionStr.empty()) |
| 126 | + continue; |
| 127 | + |
| 128 | + // Check if graph matches |
| 129 | + bool foundGraph = (graph_name == "all") || (graphStr.find(graph_name) != std::string::npos); |
| 130 | + if (!foundGraph) |
| 131 | + continue; |
| 132 | + |
| 133 | + // Check if kernel/function matches using precise pattern matching |
| 134 | + bool foundKernel = (kernel_name == "all") || matchesKernelPattern(functionStr, kernel_name); |
| 135 | + if (!foundGraph || !foundKernel) |
| 136 | + continue; |
| 137 | + |
| 138 | + // Get core tile location |
| 139 | + auto coreCol = mapping.second.get<uint8_t>("column"); |
| 140 | + auto coreRow = static_cast<uint8_t>(mapping.second.get<uint8_t>("row") + rowOffset); |
| 141 | + |
| 142 | + // Create or get existing core tile |
| 143 | + auto coreKey = std::make_pair(coreCol, coreRow); |
| 144 | + if (tileMap.find(coreKey) == tileMap.end()) { |
| 145 | + tile_type coreTile; |
| 146 | + coreTile.col = coreCol; |
| 147 | + coreTile.row = coreRow; |
| 148 | + coreTile.active_core = (mapping.second.get<std::string>("tile", "") == "aie"); |
| 149 | + coreTile.active_memory = false; // Will be set to true if DMA channels exist |
| 150 | + tileMap[coreKey] = coreTile; |
| 151 | + } |
| 152 | + |
| 153 | + // Process DMA channels |
| 154 | + auto dmaChannelsTree = mapping.second.get_child_optional("dmaChannels"); |
| 155 | + if (dmaChannelsTree) { |
| 156 | + for (auto const &channel : dmaChannelsTree.get()) { |
| 157 | + uint8_t dmaCol = xdp::aie::convertStringToUint8(channel.second.get<std::string>("column")); |
| 158 | + uint8_t dmaRow = static_cast<uint8_t>(xdp::aie::convertStringToUint8(channel.second.get<std::string>("row")) + rowOffset); |
| 159 | + |
| 160 | + // If channel matches core tile location, add to core tile |
| 161 | + if (dmaCol == coreCol && dmaRow == coreRow) { |
| 162 | + tileMap[coreKey].active_memory = true; |
| 163 | + populateDMAChannelNames(tileMap[coreKey], channel.second); |
| 164 | + } else { |
| 165 | + // Otherwise create/update separate DMA tile |
| 166 | + auto dmaKey = std::make_pair(dmaCol, dmaRow); |
| 167 | + if (tileMap.find(dmaKey) == tileMap.end()) { |
| 168 | + tile_type dmaTile; |
| 169 | + dmaTile.col = dmaCol; |
| 170 | + dmaTile.row = dmaRow; |
| 171 | + dmaTile.active_core = false; |
| 172 | + dmaTile.active_memory = true; |
| 173 | + tileMap[dmaKey] = dmaTile; |
| 174 | + } |
| 175 | + populateDMAChannelNames(tileMap[dmaKey], channel.second); |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + // Convert map to vector |
| 182 | + std::vector<tile_type> tiles; |
| 183 | + for (const auto& pair : tileMap) { |
| 184 | + tiles.push_back(pair.second); |
| 185 | + } |
| 186 | + |
| 187 | + return tiles; |
| 188 | +} |
| 189 | + |
| 190 | +// Helper method to match kernel patterns with ordered substring matching |
| 191 | +bool AIETraceConfigV3Filetype::matchesKernelPattern(const std::string& function, const std::string& kernel_name) const |
| 192 | +{ |
| 193 | + if ((kernel_name == "all") || (kernel_name.empty())) { |
| 194 | + return true; |
| 195 | + } |
| 196 | + |
| 197 | + // Split function and kernel pattern by dots |
| 198 | + std::vector<std::string> functionParts; |
| 199 | + boost::split(functionParts, function, boost::is_any_of(".")); |
| 200 | + |
| 201 | + std::vector<std::string> kernelParts; |
| 202 | + boost::split(kernelParts, kernel_name, boost::is_any_of(".")); |
| 203 | + |
| 204 | + // Remove empty parts |
| 205 | + functionParts.erase(std::remove_if(functionParts.begin(), functionParts.end(), |
| 206 | + [](const std::string& s) { return s.empty(); }), |
| 207 | + functionParts.end()); |
| 208 | + kernelParts.erase(std::remove_if(kernelParts.begin(), kernelParts.end(), |
| 209 | + [](const std::string& s) { return s.empty(); }), |
| 210 | + kernelParts.end()); |
| 211 | + |
| 212 | + // If kernel has more parts than function, it can't match |
| 213 | + if (kernelParts.size() > functionParts.size()) { |
| 214 | + return false; |
| 215 | + } |
| 216 | + |
| 217 | + // Look for contiguous subsequence of kernel parts in function parts |
| 218 | + for (size_t i = 0; i <= functionParts.size() - kernelParts.size(); ++i) { |
| 219 | + bool matches = true; |
| 220 | + for (size_t j = 0; j < kernelParts.size(); ++j) { |
| 221 | + if (functionParts[i + j] != kernelParts[j]) { |
| 222 | + matches = false; |
| 223 | + break; |
| 224 | + } |
| 225 | + } |
| 226 | + if (matches) { |
| 227 | + return true; |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + return false; |
| 232 | +} |
| 233 | + |
| 234 | +// Helper method to populate DMA channel names from metadata |
| 235 | +void AIETraceConfigV3Filetype::populateDMAChannelNames(tile_type& tile, const boost::property_tree::ptree& channelNode) const |
| 236 | +{ |
| 237 | + auto portName = channelNode.get<std::string>("portName", ""); |
| 238 | + auto channelNum = channelNode.get<uint8_t>("channel", 0); |
| 239 | + auto direction = channelNode.get<std::string>("direction", ""); |
| 240 | + |
| 241 | + if (portName.empty() || direction.empty()) |
| 242 | + return; |
| 243 | + |
| 244 | + if (direction == "s2mm") { |
| 245 | + if (channelNum < tile.s2mm_names.size()) { |
| 246 | + tile.s2mm_names[channelNum] = portName; |
| 247 | + } |
| 248 | + } else if (direction == "mm2s") { |
| 249 | + if (channelNum < tile.mm2s_names.size()) { |
| 250 | + tile.mm2s_names[channelNum] = portName; |
| 251 | + } |
| 252 | + } |
| 253 | +} |
| 254 | + |
| 255 | +// ================================================================= |
| 256 | +// UNSUPPORTED METHODS - Throw runtime exceptions for V3 format |
| 257 | +// ================================================================= |
| 258 | + |
| 259 | +std::vector<tile_type> |
| 260 | +AIETraceConfigV3Filetype::getAIETiles(const std::string&) const |
| 261 | +{ |
| 262 | + throw std::runtime_error("getAIETiles() is not supported in V3 metadata format. " |
| 263 | + "Use getTiles() with module_type::core instead."); |
| 264 | +} |
| 265 | + |
| 266 | +std::vector<tile_type> |
| 267 | +AIETraceConfigV3Filetype::getAllAIETiles(const std::string&) const |
| 268 | +{ |
| 269 | + throw std::runtime_error("getAllAIETiles() is not supported in V3 metadata format. " |
| 270 | + "Use getTiles() with module_type::core instead."); |
| 271 | +} |
| 272 | + |
| 273 | +std::vector<tile_type> |
| 274 | +AIETraceConfigV3Filetype::getEventTiles(const std::string&, module_type) const |
| 275 | +{ |
| 276 | + throw std::runtime_error("getEventTiles() is not supported in V3 metadata format. " |
| 277 | + "Use getTiles() with the appropriate module_type instead."); |
| 278 | +} |
| 279 | + |
| 280 | +} // namespace xdp::aie |
0 commit comments