diff --git a/profile/plugin/aie_dtrace/aie_dtrace_metadata.cpp b/profile/plugin/aie_dtrace/aie_dtrace_metadata.cpp index d6e0872e..f0cd2902 100644 --- a/profile/plugin/aie_dtrace/aie_dtrace_metadata.cpp +++ b/profile/plugin/aie_dtrace/aie_dtrace_metadata.cpp @@ -51,6 +51,19 @@ namespace xdp { return false; } + // Mem tile (L2) metric sets other than L2-L2 transfers, which is selected by + // INPUT_PORTS_METRIC_SET above and handled separately. output_channels_details + // measures a single MM2S channel: port running, memory starvation, stream + // backpressure, raw lock stall, and lock stall excluding starvation and + // backpressure. mm2s_channels_details is an alias, matching the pair of names + // aie_profile uses for the same events. + static const std::set& memTileMetricSets() + { + static const std::set metrics = { + "output_channels_details", "mm2s_channels_details", "off"}; + return metrics; + } + AieDtraceMetadata::AieDtraceMetadata(uint64_t deviceID, void* handle) : deviceID(deviceID) , handle(handle) @@ -121,6 +134,12 @@ namespace xdp { && !ci.mem_tile->empty(); const bool memTileUsesBlob = usingBlob && (memTileFieldFromBlob || blobPortsSet); + // Mem tile settings that are not L2-L2 select a per-tile counter metric set such + // as output_channels_details. Both families program the same mem tile performance + // counters, so only one can be active: L2-L2 takes precedence and the other is + // refused rather than left to fight over counters 0-3. + std::vector memTileMetricsSettings; + bool l2L2FromBlob = false; if (memTileUsesBlob) { if (memTileFieldFromBlob && *ci.mem_tile == INPUT_PORTS_METRIC_SET) { @@ -131,14 +150,27 @@ namespace xdp { + "' from Debug.profiling_runtime_config."); } else if (memTileFieldFromBlob) { xrt_core::message::send(severity_level::info, "XRT", - "AIE dtrace: mem tile metric '" + *ci.mem_tile - + "' from profiling_runtime_config will be supported in a follow-up."); + "AIE dtrace: using mem_tile metric '" + *ci.mem_tile + + "' from Debug.profiling_runtime_config."); + memTileMetricsSettings = getSettingsVector("all:" + *ci.mem_tile); } } else { l2L2TransferEnabled = iniL2L2Enabled; + if (!iniL2L2Enabled && !memTileSettings.empty()) + memTileMetricsSettings = getSettingsVector(memTileSettings); } + if (l2L2TransferEnabled && !memTileMetricsSettings.empty()) { + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: L2-L2 transfers and a per-tile mem tile metric set cannot be " + "captured together, because both program the same mem tile performance " + "counters. Keeping L2-L2 and ignoring the other mem tile metric set."); + memTileMetricsSettings.clear(); + } + + getConfigMetricsForMemTiles(MEM_TILE_MODULE_IDX, memTileMetricsSettings); + if (blobPortsSet && !l2L2FromBlob) { xrt_core::message::send(severity_level::error, "XRT", "AIE dtrace: profiling_runtime_config.control_instrumentation.memory_tile_input_ports " @@ -236,6 +268,106 @@ namespace xdp { return coreMetricSets().count(metricSet) > 0; } + bool AieDtraceMetadata::isMemTileMetricSet(const std::string& metricSet) const + { + return memTileMetricSets().count(metricSet) > 0; + } + + void AieDtraceMetadata::getConfigMetricsForMemTiles(int moduleIdx, + const std::vector& metricsSettings) + { + if (metricsSettings.empty()) + return; + + // Accepted forms, mirroring the interface tile parser: + // all:[:] every mem tile column in the partition + // :[:] a single column + // [:] bare, treated as all + // Only one mem tile metric set is programmed per run: it consumes the whole + // combo block of every tile it touches, so a second set has nothing left. + std::string metricSet; + uint8_t channel = 0; + bool allColumns = true; + std::vector columns; + + for (const auto& setting : metricsSettings) { + std::vector parts; + boost::split(parts, setting, boost::is_any_of(":")); + + auto metricPos = std::find_if(parts.begin(), parts.end(), + [this](const std::string& part) { return isMemTileMetricSet(part); }); + if (metricPos == parts.end()) + continue; + + if (metricPos->compare("off") == 0) + return; + + metricSet = *metricPos; + + // Anything ahead of the metric selects columns; "all" means the partition. + if ((metricPos != parts.begin()) && (parts.front().compare("all") != 0)) { + try { + columns.push_back(aie::convertStringToUint8(parts.front())); + allColumns = false; + } + catch (std::invalid_argument const&) { + xrt_core::message::send(severity_level::warning, "XRT", + "Column specification in tile_based_memory_tile_metrics is not an " + "integer and hence ignored."); + } + } + + // Anything after the metric is the MM2S channel to monitor. + auto channelPos = std::next(metricPos); + if (channelPos != parts.end()) { + try { + uint8_t requested = aie::convertStringToUint8(*channelPos); + if (requested < NUM_MEM_TILE_DMA_CHANNELS) { + channel = requested; + } + else { + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: mem tile MM2S channel " + std::to_string(requested) + + " is out of range (0-" + + std::to_string(NUM_MEM_TILE_DMA_CHANNELS - 1) + + "). Using channel 0."); + } + } + catch (std::invalid_argument const&) { + xrt_core::message::send(severity_level::warning, "XRT", + "Channel ID specification in tile_based_memory_tile_metrics is not " + "an integer and hence ignored."); + } + } + break; + } + + if (metricSet.empty()) { + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: no valid mem tile metric set found in " + "tile_based_memory_tile_metrics. Supported: output_channels_details, " + "mm2s_channels_details, off."); + return; + } + + memTileAllColumns = allColumns; + if (allColumns) + columns.assign(1, MEM_TILE_METRIC_COL); + + const uint8_t row = metadataReader->getDriverConfig().mem_row_start; + for (uint8_t col : columns) { + tile_type tile; + tile.col = col; + tile.row = row; + configMetrics[moduleIdx][tile] = metricSet; + configChannel0[tile] = channel; + } + + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: enabled mem tile metric set '" + metricSet + + "' on MM2S channel " + std::to_string(channel) + "."); + } + void AieDtraceMetadata::getConfigMetricsForAIETiles(int moduleIdx, const std::vector& metricsSettings) { diff --git a/profile/plugin/aie_dtrace/aie_dtrace_metadata.h b/profile/plugin/aie_dtrace/aie_dtrace_metadata.h index db1b8d65..b9a07577 100644 --- a/profile/plugin/aie_dtrace/aie_dtrace_metadata.h +++ b/profile/plugin/aie_dtrace/aie_dtrace_metadata.h @@ -19,6 +19,7 @@ class AieDtraceMetadata { private: static constexpr int SHIM_MODULE_IDX = static_cast(module_type::shim); static constexpr int CORE_MODULE_IDX = static_cast(module_type::core); + static constexpr int MEM_TILE_MODULE_IDX = static_cast(module_type::mem_tile); static constexpr int NUM_MODULES = static_cast(module_type::num_types); // Placeholder tile used only as the config-map key that enables the metric. @@ -27,12 +28,25 @@ class AieDtraceMetadata { static constexpr uint8_t CORE_METRIC_COL = 0; static constexpr uint8_t CORE_METRIC_ROW = 3; + // Mem tile placeholder column, used when the setting asks for every column. + // The CT writer expands it against the partition and derives the rows from + // driver_config.mem_row_start / mem_num_rows. + static constexpr uint8_t MEM_TILE_METRIC_COL = 0; + + // A mem tile DMA has six MM2S channels, and the selection register field is + // three bits wide. + static constexpr uint8_t NUM_MEM_TILE_DMA_CHANNELS = 6; + uint64_t deviceID = 0; double clockFreqMhz = 0.0; void* handle = nullptr; bool configOnePartition = false; bool l2L2TransferEnabled = false; + // True when the mem tile setting asked for every column rather than naming + // one, in which case configMetrics holds a single placeholder entry. + bool memTileAllColumns = false; + std::vector> configMetrics; std::map configChannel0; std::map configChannel1; @@ -44,8 +58,11 @@ class AieDtraceMetadata { const std::vector& metricsSettings); void getConfigMetricsForAIETiles(int moduleIdx, const std::vector& metricsSettings); + void getConfigMetricsForMemTiles(int moduleIdx, + const std::vector& metricsSettings); bool isBandwidthMetricSet(const std::string& metricSet) const; bool isCoreMetricSet(const std::string& metricSet) const; + bool isMemTileMetricSet(const std::string& metricSet) const; public: AieDtraceMetadata(uint64_t deviceID, void* handle); @@ -59,13 +76,19 @@ class AieDtraceMetadata { && !configMetrics[SHIM_MODULE_IDX].empty(); const bool coreConfigured = CORE_MODULE_IDX < numModules && !configMetrics[CORE_MODULE_IDX].empty(); - return shimConfigured || coreConfigured; + const bool memTileConfigured = MEM_TILE_MODULE_IDX < numModules + && !configMetrics[MEM_TILE_MODULE_IDX].empty(); + return shimConfigured || coreConfigured || memTileConfigured; } bool isConfigOnePartition() const { return configOnePartition; } bool isL2L2Enabled() const { return l2L2TransferEnabled; } + // When true the mem tile metric applies to every column in the partition and + // the config map holds only a placeholder column. + bool isMemTileAllColumns() const { return memTileAllColumns; } + bool aieMetadataEmpty() { return metadataReader == nullptr; } std::vector getSettingsVector(std::string settingsString); diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp index 63d965c2..188676c3 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp @@ -1471,6 +1471,60 @@ void AieDtraceCTWriter::appendL2L2Config( } //============================================================================================================ +bool AieDtraceCTWriter::appendMemTileConfig( + void* hwctx, const std::string& metricSet, uint8_t channel, + std::vector& counters, std::vector& beginWrites) +{ + // Mem tiles occupy the same columns as the shim tiles, so the partition column + // discovery is shared. A setting that named one column instead of "all" leaves those + // columns in the config map. + std::vector columns; + if (metadata->isMemTileAllColumns()) { + columns = getShimTileColumns(hwctx); + } + else { + for (const auto& tileMetric : + metadata->getConfigMetricsVec(static_cast(module_type::mem_tile))) + columns.push_back(tileMetric.first.col); + } + + if (columns.empty()) { + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: No mem tile columns found in partition. Skipping mem tile counters."); + return false; + } + + auto rows = getMemTileRows(); + if (rows.empty()) { + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: No mem tile rows in driver config. Skipping mem tile counters."); + return false; + } + + auto memTileCounters = generateMemTileCounters(columns, rows, metricSet, channel); + if (memTileCounters.empty()) { + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: No mem tile counters generated"); + return false; + } + counters.insert(counters.end(), memTileCounters.begin(), memTileCounters.end()); + + for (uint8_t column : columns) { + for (uint8_t row : rows) { + auto channelWrites = generateMemTileChannelSelectConfig(column, row, channel); + beginWrites.insert(beginWrites.end(), channelWrites.begin(), channelWrites.end()); + + auto comboWrites = generateMemTileComboConfig(column, row); + beginWrites.insert(beginWrites.end(), comboWrites.begin(), comboWrites.end()); + + auto perfWrites = generateMemTilePerfCounterConfig(column, row); + beginWrites.insert(beginWrites.end(), perfWrites.begin(), perfWrites.end()); + } + } + + return true; +} + bool AieDtraceCTWriter::generateCT( const std::string& outputPath, void* hwctx, @@ -1478,7 +1532,9 @@ bool AieDtraceCTWriter::generateCT( bool includeBandwidth, const std::string& bandwidthMetricSet, uint8_t bandwidthChannel, - const std::string& coreMetricSet) + const std::string& coreMetricSet, + const std::string& memTileMetricSet, + uint8_t memTileChannel) { if (opLocations.empty()) { xrt_core::message::send(severity_level::debug, "XRT", @@ -1511,8 +1567,22 @@ bool AieDtraceCTWriter::generateCT( "AIE dtrace: Unsupported core (aie) tile metric set '" + coreMetricSet + "'; no core tile counters configured."); + // Both mem tile families program the same performance counters, so at most one of + // them is ever configured: the metadata resolves the contention and clears the + // per-tile metric set when L2-L2 wins. appendL2L2Config(hwctx, allCounters, beginBlockWrites); + // Mem tile counters live on rows between the shim and the core tiles, so they land in + // the same column-keyed UC groups as the other two families. + if ((memTileMetricSet == "output_channels_details") + || (memTileMetricSet == "mm2s_channels_details")) + appendMemTileConfig(hwctx, memTileMetricSet, memTileChannel, allCounters, + beginBlockWrites); + else if (!memTileMetricSet.empty()) + xrt_core::message::send(severity_level::warning, "XRT", + "AIE dtrace: Unsupported mem tile metric set '" + memTileMetricSet + + "'; no mem tile counters configured."); + if (allCounters.empty()) { xrt_core::message::send(severity_level::warning, "XRT", "AIE dtrace: No counters configured; CT file will not be generated."); @@ -1922,5 +1992,176 @@ std::vector AieDtraceCTWriter::generateLockStarvationMemoryConf return writes; } +namespace { + +// The mem tile module's performance control and combo input registers both use 8-bit +// event fields, unlike the 7-bit fields of the core and memory modules. +uint32_t +memTileCounterEventPair(uint8_t event, unsigned startShift) +{ + return (static_cast(event) << startShift) + | (static_cast(event) << (startShift + 8)); +} + +} // namespace + +std::vector AieDtraceCTWriter::getMemTileRows() +{ + std::vector rows; + + // Only the first mem tile row is instrumented, even on parts with several. Every row + // costs another five counter reads at every probe point, and the first row is the one + // the L2-L2 metric also measures, so the two stay comparable. + auto driverConfig = metadata->getAIEConfigMetadata(); + if (driverConfig.mem_num_rows > 0) + rows.push_back(driverConfig.mem_row_start); + + return rows; +} + +std::vector AieDtraceCTWriter::generateMemTileChannelSelectConfig( + uint8_t column, uint8_t row, uint8_t channel) +{ + std::vector writes; + + uint64_t tileAddress = (static_cast(column) << columnShift) | + (static_cast(row) << rowShift); + + // Selector 0 carries the channel, so every DMA_MM2S_SEL0_* event on this tile refers + // to it. The S2MM selectors and MM2S_SEL1 go to zero: this metric owns the register + // for the duration of the run and uses neither. + CTRegisterWrite w; + w.address = tileAddress + MT_DMA_EVENT_CHANNEL_SEL; + w.value = static_cast(channel) << MT_DMA_CHANNEL_SEL_MM2S_SEL0_SHIFT; + w.comment = "DMA_Event_Channel_Selection @ memtile (" + std::to_string(column) + "," + + std::to_string(row) + ") (mm2s sel0 = ch" + std::to_string(channel) + ")"; + writes.push_back(w); + + return writes; +} + +std::vector AieDtraceCTWriter::generateMemTileComboConfig( + uint8_t column, uint8_t row) +{ + std::vector writes; + + uint64_t tileAddress = (static_cast(column) << columnShift) | + (static_cast(row) << rowShift); + + auto addWrite = [&](uint64_t offset, uint32_t value, const std::string& comment) { + CTRegisterWrite w; + w.address = tileAddress + offset; + w.value = value; + w.comment = comment; + writes.push_back(w); + }; + + std::string loc = "memtile (" + std::to_string(column) + "," + std::to_string(row) + ")"; + + // Combo 0 pairs inputs A and B, combo 1 pairs C and D. The lock stall is fed into both + // A and B so combo 0 reduces to the lock stall itself, leaving C and D free for the + // two events it has to be measured against. + addWrite(MT_COMBO_EVENT_INPUTS, + static_cast(MT_DMA_MM2S_SEL0_STALLED_LOCK_EVENT) + | (static_cast(MT_DMA_MM2S_SEL0_STALLED_LOCK_EVENT) << 8) + | (static_cast(MT_DMA_MM2S_SEL0_MEMORY_STARVATION_EVENT) << 16) + | (static_cast(MT_DMA_MM2S_SEL0_STREAM_BACKPRESSURE_EVENT) << 24), + "Combo_Event_Inputs @ " + loc + " (A,B = mm2s lock stall, " + + "C = mm2s memory starvation, D = mm2s stream backpressure)"); + + // Combo 2 recombines the first two results, and the driver fixes its operands as + // Event1 = combo 0 and Event2 = combo 1, so AND NOT lands the right way round. + addWrite(MT_COMBO_EVENT_CONTROL, + COMBO_AND | (COMBO_OR << 8) | (COMBO_AND_NOT_E2 << 16), + "Combo_Event_Control @ " + loc + " (combo0 = A AND B = lock stall, " + + "combo1 = C OR D = starvation or backpressure, " + + "combo2 = combo0 AND NOT combo1)"); + + return writes; +} + +std::vector AieDtraceCTWriter::generateMemTilePerfCounterConfig( + uint8_t column, uint8_t row) +{ + std::vector writes; + + uint64_t tileAddress = (static_cast(column) << columnShift) | + (static_cast(row) << rowShift); + + auto addWrite = [&](uint64_t offset, uint32_t value, const std::string& comment) { + CTRegisterWrite w; + w.address = tileAddress + offset; + w.value = value; + w.comment = comment; + writes.push_back(w); + }; + + std::string loc = "memtile (" + std::to_string(column) + "," + std::to_string(row) + ")"; + + for (uint8_t i = 0; i < MT_NUM_PERF_COUNTERS; ++i) + addWrite(MT_PERF_COUNTER0 + 4 * i, 0, + "Reset PerfCounter" + std::to_string(i) + " @ " + loc); + + // Performance_Control0: [7:0]=Cnt0_Start, [15:8]=Cnt0_Stop, [23:16]=Cnt1_Start, + // [31:24]=Cnt1_Stop. Start == Stop makes each counter accumulate the cycles its event + // is asserted, the same convention aie_profile uses for its stall metric sets. + addWrite(MT_PERF_CTRL0, + memTileCounterEventPair(MT_DMA_MM2S_SEL0_MEMORY_STARVATION_EVENT, 0) + | memTileCounterEventPair(MT_DMA_MM2S_SEL0_STREAM_BACKPRESSURE_EVENT, 16), + "PerfCtrl0 @ " + loc + " (ctr0 = memory starvation, ctr1 = stream backpressure)"); + + // Performance_Control1 holds counters 2 and 3 on a mem tile, not Performance_Control2. + // Counter 3 is left unprogrammed. + addWrite(MT_PERF_CTRL1, + memTileCounterEventPair(MT_COMBO_EVENT_2_EVENT, 0), + "PerfCtrl1 @ " + loc + + " (ctr2 = Combo_Event_2 lock stall excluding starvation and backpressure)"); + + return writes; +} + +std::vector AieDtraceCTWriter::generateMemTileCounters( + const std::vector& columns, const std::vector& rows, + const std::string& metricSet, uint8_t channel) +{ + std::vector counters; + + struct MemTileCounterLayout { + uint8_t counterNumber; + const char* eventType; + }; + + // The labels are what vaianalyze keys on to tell the three apart within an interval. + const MemTileCounterLayout layout[] = { + {0, "memtile_memory_starvation"}, + {1, "memtile_stream_backpressure"}, + {2, "memtile_lock_excl"} + }; + + for (uint8_t column : columns) { + for (uint8_t row : rows) { + for (const auto& entry : layout) { + CTCounterInfo info; + info.column = column; + info.row = row; + info.counterNumber = entry.counterNumber; + info.channel = channel; + info.module = "memory_tile"; + info.address = calculateCounterAddress(column, row, entry.counterNumber, + "memory_tile"); + info.metricSet = metricSet; + // MM2S moves data out of the tile, so the direction is genuinely output. Consumers + // that read direction as a DDR read or write must gate on the module being a shim + // tile rather than on the direction alone. + info.portDirection = "output"; + info.eventType = entry.eventType; + counters.push_back(info); + } + } + } + + return counters; +} + } // namespace xdp diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h index f6591049..5274c3bb 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h @@ -199,6 +199,9 @@ class AieDtraceCTWriter { * @param bandwidthChannel DMA channel for detailed_ddr_*_bandwidth sets * @param coreMetricSet Core (aie) tile metric set to emit, or empty for none. * Supported: compute_io_bound + * @param memTileMetricSet Mem tile (L2) metric set to emit, or empty for none. + * Supported: output_channels_details, mm2s_channels_details + * @param memTileChannel MM2S channel (0-5) monitored by the mem tile metric set * @return true if CT file was generated successfully, false otherwise */ bool generateCT(const std::string& outputPath, @@ -207,7 +210,9 @@ class AieDtraceCTWriter { bool includeBandwidth, const std::string& bandwidthMetricSet, uint8_t bandwidthChannel, - const std::string& coreMetricSet); + const std::string& coreMetricSet, + const std::string& memTileMetricSet = "", + uint8_t memTileChannel = 0); private: /** @@ -370,6 +375,82 @@ class AieDtraceCTWriter { std::vector& counters, std::vector& beginWrites); + /** + * @brief Append the mem tile (L2) MM2S counters and begin-block writes + * + * Programs the first-row mem tile of every selected column with three counters + * measuring one MM2S channel: memory starvation, stream backpressure, and the lock + * stall that coincides with neither of them. + * + * @param hwctx Hardware context handle for partition column discovery + * @param metricSet Mem tile metric set + * @param channel MM2S channel (0-5) to monitor + * @param counters [in,out] Accumulated counter list + * @param beginWrites [in,out] Accumulated begin-block register writes + * @return true if mem tile config was appended + */ + bool appendMemTileConfig(void* hwctx, const std::string& metricSet, uint8_t channel, + std::vector& counters, std::vector& beginWrites); + + /** + * @brief Absolute mem tile rows, from driver_config mem_row_start / mem_num_rows + */ + std::vector getMemTileRows(); + + /** + * @brief Point the DMA event selector at one MM2S channel + * + * Selector 0 of DMA_Event_Channel_Selection carries the channel, so every + * DMA_MM2S_SEL0_* event on the tile refers to it. The register is written in full: the + * fields this metric does not use have no other owner while it is running. + * + * @param column Partition-relative mem tile column + * @param row Absolute mem tile row + * @param channel MM2S channel (0-5) + * @return Vector of register writes for the begin block + */ + std::vector generateMemTileChannelSelectConfig(uint8_t column, + uint8_t row, uint8_t channel); + + /** + * @brief Build the lock stall term that excludes starvation and backpressure + * + * COMBO0 ANDs the lock stall with itself, which is just the lock stall; COMBO1 ORs + * the channel's starvation and backpressure; COMBO2 is COMBO0 AND NOT COMBO1. The + * driver fixes that operand order (for XAIE_EVENT_COMBO2, Event1 is COMBO0 and Event2 + * is COMBO1) and COMBO2 consumes no input slots of its own. Every input is native to + * the mem tile module, so unlike compute_io_bound no broadcast relay is needed. + * + * @param column Partition-relative mem tile column + * @param row Absolute mem tile row + * @return Vector of register writes for the begin block + */ + std::vector generateMemTileComboConfig(uint8_t column, uint8_t row); + + /** + * @brief Zero the mem tile counters and point three of them at the metric's events + * + * Counters 0-2 take memory starvation, stream backpressure and the combo residual, + * each with Start == Stop so it accumulates the cycles its event is asserted. The + * remaining counters are left unprogrammed. + * + * @param column Partition-relative mem tile column + * @param row Absolute mem tile row + * @return Vector of register writes for the begin block + */ + std::vector generateMemTilePerfCounterConfig(uint8_t column, uint8_t row); + + /** + * @brief Build the counter metadata for every mem tile being programmed + * @param columns Partition-relative mem tile columns + * @param rows Absolute mem tile rows + * @param metricSet Mem tile metric set + * @param channel MM2S channel being monitored + * @return Vector of CTCounterInfo for all mem tile counters + */ + std::vector generateMemTileCounters(const std::vector& columns, + const std::vector& rows, const std::string& metricSet, uint8_t channel); + /** * @brief Generate the core module config for the compute_io_bound lock correlation tile * @@ -599,6 +680,35 @@ class AieDtraceCTWriter { static constexpr uint8_t CORE_COMBO_EVENT_0_EVENT = 9; static constexpr uint8_t CORE_COMBO_EVENT_1_EVENT = 10; + // Mem tile (L2) offsets (aie2ps). The bundled aie-codegen register database reports + // MaxCounterVal = 4 for the mem tile module, which is stale against the spec: there + // are six counters, with 4 and 5 controlled by Performance_Control3. Note the layout + // differs from the shim module, where counters 2 and 3 sit at +0xC from the control + // base; on a mem tile +0xC is counters 4 and 5. + static constexpr uint64_t MT_PERF_CTRL0 = 0x00091000; // Counters 0,1 start/stop + static constexpr uint64_t MT_PERF_CTRL1 = 0x00091004; // Counters 2,3 start/stop + static constexpr uint64_t MT_PERF_COUNTER0 = 0x00091020; // Counter 0 (Counter N at +4*N) + static constexpr uint8_t MT_NUM_PERF_COUNTERS = 3; + + static constexpr uint64_t MT_COMBO_EVENT_INPUTS = 0x00094400; + static constexpr uint64_t MT_COMBO_EVENT_CONTROL = 0x00094404; + static constexpr uint64_t MT_DMA_EVENT_CHANNEL_SEL = 0x000A06A0; + + // MM2S_SEL0 channel field of DMA_Event_Channel_Selection (3 bits at [18:16]). Only + // selector 0 is used, so the S2MM and SEL1 fields are written as zero. + static constexpr unsigned MT_DMA_CHANNEL_SEL_MM2S_SEL0_SHIFT = 16; + + // aie2ps mem tile events (xaie_events_aie2ps.h). The lock stall is never counted on its + // own; it is only a combo input, which is why it has no counter of its own below. + static constexpr uint8_t MT_COMBO_EVENT_2_EVENT = 11; + static constexpr uint8_t MT_DMA_MM2S_SEL0_STALLED_LOCK_EVENT = 35; + static constexpr uint8_t MT_DMA_MM2S_SEL0_STREAM_BACKPRESSURE_EVENT = 39; + static constexpr uint8_t MT_DMA_MM2S_SEL0_MEMORY_STARVATION_EVENT = 43; + + // XAie_EventComboOps: AND is 0 (COMBO_AND above), E1 AND NOT E2 is 1, OR is 2. + static constexpr uint32_t COMBO_AND_NOT_E2 = 1; + static constexpr uint32_t COMBO_OR = 2; + // Bandwidth monitoring constants static constexpr uint8_t NUM_BANDWIDTH_COUNTERS = 4; static constexpr uint8_t SHIM_ROW = 0; diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp index 1b4212c2..4d7fc5bf 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp @@ -25,6 +25,7 @@ namespace xdp { static constexpr int SHIM_MODULE_IDX = static_cast(module_type::shim); static constexpr int CORE_MODULE_IDX = static_cast(module_type::core); + static constexpr int MEM_TILE_MODULE_IDX = static_cast(module_type::mem_tile); AieDtrace_VE2Impl::AieDtrace_VE2Impl(VPDatabase* database, std::shared_ptr metadata, @@ -119,6 +120,27 @@ namespace xdp { break; } + // Mem tile (L2) metrics carry the MM2S channel to monitor in configChannel0, the + // same way the detailed_ddr_*_bandwidth sets carry theirs. Every configured mem tile + // shares one metric set and one channel, so the first entry speaks for all of them. + std::string memTileMetricSet; + uint8_t memTileChannel = 0; + auto memTileConfigMetrics = metadata->getConfigMetricsVec(MEM_TILE_MODULE_IDX); + if (!memTileConfigMetrics.empty()) { + memTileMetricSet = memTileConfigMetrics.front().second; + auto memTileChannels = metadata->getConfigChannel0(); + const auto& memTile = memTileConfigMetrics.front().first; + for (const auto& tc : memTileChannels) { + if ((tc.first.col == memTile.col) && (tc.first.row == memTile.row)) { + memTileChannel = tc.second; + break; + } + } + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: Using mem tile metric set '" + memTileMetricSet + "' (MM2S channel " + + std::to_string(memTileChannel) + ") from configuration"); + } + // Interface-tile bandwidth metrics are configured by default unless the user // turned interface tiles off (which leaves the shim config map empty). auto shimConfigMetrics = metadata->getConfigMetricsVec(SHIM_MODULE_IDX); @@ -142,7 +164,8 @@ namespace xdp { + std::to_string(bandwidthChannel) + ") from configuration"); } - if (!includeBandwidth && coreMetricSet.empty() && !metadata->isL2L2Enabled()) { + if (!includeBandwidth && coreMetricSet.empty() && memTileMetricSet.empty() + && !metadata->isL2L2Enabled()) { xrt_core::message::send(severity_level::info, "XRT", "AIE dtrace: No metrics configured; skipping CT generation."); return; @@ -150,7 +173,7 @@ namespace xdp { if (!ctWriter.generateCT(outputPath, hwctx, it->second, includeBandwidth, bandwidthMetricSet, bandwidthChannel, - coreMetricSet)) + coreMetricSet, memTileMetricSet, memTileChannel)) return; aie::dtrace::initDtraceOutputConfig(); @@ -167,6 +190,10 @@ namespace xdp { genMsg << ", "; if (metadata->isL2L2Enabled()) genMsg << "memtile=input_ports"; + if (!memTileMetricSet.empty()) + genMsg << ((includeBandwidth || !coreMetricSet.empty()) ? ", " : "") + << "memtile=" << memTileMetricSet << ":ch" + << static_cast(memTileChannel); genMsg << ")"; xrt_core::message::send(severity_level::debug, "XRT", genMsg.str());