[aqlprofile] Enable SPM support for MI200/MI300 (#1768)

* [SPM] Enable legacy SPM aqlprofile API

* [SPM] Enable SPM aqlprofile_v2 API

* [NPI][SPM] Fix crash from ctrl test

* Adding decode v1 (#189)

Co-authored-by: Giovanni baraldi <gbaraldi@amd.com>

* Fix various issues on MI200
1. RLC_SPM_PERFMON_SEGMENT_SIZE_CORE1 support
2. ActiveCU patch for SPM delay table

* [SPM] Fix wrong SPM counter values on MI3xx

* Add mode and query blocks (#196)

Co-authored-by: Giovanni baraldi <gbaraldi@amd.com>

* [aqlprofile][spm] Use existing SpmBlockId enum info for delay table size

* [aqlprofile][spm] Remove obsolete logic

* Update projects/aqlprofile/src/core/include/aqlprofile-sdk/aql_profile_v2.h

---------

Co-authored-by: Baraldi, Giovanni <Giovanni.Baraldi@amd.com>
Co-authored-by: Giovanni baraldi <gbaraldi@amd.com>
This commit is contained in:
Bing Ma
2025-11-19 11:17:01 -08:00
committed by GitHub
parent 9efd330fae
commit 171a5f5bda
18 changed files with 1619 additions and 59 deletions
+106 -27
View File
@@ -32,6 +32,7 @@
#include "pm4/cmd_config.h"
#include "pm4/cmd_builder.h"
#include "src/core/include/spm_common.hpp"
namespace pm4_builder {
class CmdBuffer;
@@ -80,6 +81,14 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
const uint64_t buffer_ptr = reinterpret_cast<uint64_t>(config->data_buffer_ptr);
const uint32_t buffer_size = config->data_buffer_size;
// Initialize SPM counter buffer metadata.
// counter_map takes the index of counters_vector as input, and output an index to
// the 16bit SPM counter buffer
SpmBufferDesc* spm_buffer_desc = (SpmBufferDesc*)config->data_buffer_ptr;
spm_buffer_desc->version = 1;
uint16_t* counter_map = spm_buffer_desc->get_counter_map();
memset(counter_map, 0, SPM_DESC_SIZE - sizeof(SpmBufferDesc));
// On Vega this is needed to collect Perf Cntrs: enable clock for performance counters
if (Primitives::GFXIP_LEVEL == 9)
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::RLC_PERFMON_CLK_CNTL_ADDR, 1);
@@ -89,20 +98,29 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
Primitives::grbm_broadcast_value());
// Issue a CSPartialFlush cmd including cache flush
builder.BuildWriteWaitIdlePacket(cmd_buffer);
// SPM counters reset
// SPM counters stop
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::CP_PERFMON_CNTL_ADDR,
Primitives::cp_perfmon_cntl_reset_value());
Primitives::cp_perfmon_cntl_spm_stop_value());
// Initialize the [BLK]_SAMPLE_DLY_SEL registers
// These registers are layout-dependent and allow all the blocks to receive
// the sample signals on a specified cycle
// global: CPC, CPF, GDS, TCC, TCA
// SE: SX, TA, TD, TCP, SPI
// SPM counters reset
//
// We cannot call 'SPM counters reset' in user mode because it will reset WPTR of the
// SPM ring buffer, RPTR must be adjusted as well but it can only be adjusted in KFD.
// Also we don't need to reset SPM counter the same way as we do for legacy PMC,
// because SPM counter will reset upon each new sample.
//
// The first reset after aqlprofile acquires SPM from KFD will be done in KFD.
// Also each time when user mode buffer is no longer made available to KFD, KFD will
// reset SPM counters.
//
// builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::CP_PERFMON_CNTL_ADDR,
// Primitives::cp_perfmon_cntl_reset_value());
// Initialize the Performance Counter Ring Structure in memory
// 1. Program the RLC_RING_BASE_H1/LO registers.
// 2. Program the RLC_RING_SIZE register.
// 3. Program the RLC_PERFMON_SEGMENT_SIZE register.
// Issue a CSPartialFlush cmd including cache flush
builder.BuildWriteWaitIdlePacket(cmd_buffer);
// Hardcode PERFMON_RING_MODE to 3 (Stall and send interrupt) to match KFD
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::RLC_SPM_PERFMON_CNTL__ADDR,
Primitives::rlc_spm_perfmon_cntl_value(sampling_rate));
@@ -129,6 +147,25 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
}
}
// Sort counter_info_even and counter_info_odd by instance
auto compare = [&counters_vec](std::pair<int, int> a, std::pair<int, int> b) {
auto index_a = a.second;
auto index_b = b.second;
auto& counter_des_a = counters_vec[index_a];
auto& counter_des_b = counters_vec[index_b];
return (counter_des_a.block_des.index < counter_des_b.block_des.index) ||
((counter_des_a.block_des.index == counter_des_b.block_des.index) &&
(counter_des_a.index < counter_des_b.index));
};
for (size_t i = 0; i < Primitives::NUMBER_OF_BLOCKS; ++i) {
if (!counter_info_even[i].empty()) {
sort(counter_info_even[i].begin(), counter_info_even[i].end(), compare);
}
if (!counter_info_odd[i].empty()) {
sort(counter_info_odd[i].begin(), counter_info_odd[i].end(), compare);
}
}
// compute segment size for global(0) and se(1)
uint32_t ss_even[2] = {};
uint32_t ss_odd[2] = {};
@@ -192,13 +229,17 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
const auto* block_info = counter_des.block_info;
if (block_info->attr & CounterBlockSpmGlobalAttr) {
for (size_t k = 0; k < counter_info_even[j].size(); ++k) {
const auto& counter_des = counters_vec[counter_info_even[j][k].second];
const auto index = counter_info_even[j][k].second;
const auto& counter_des = counters_vec[index];
mux_ram[0][even_idx] = Primitives::spm_mux_ram_value(counter_des);
counter_map[index] = even_idx | 0x8000;
even_idx = Primitives::spm_mux_ram_idx_incr(even_idx);
}
for (size_t k = 0; k < counter_info_odd[j].size(); ++k) {
const auto& counter_des = counters_vec[counter_info_odd[j][k].second];
const auto index = counter_info_odd[j][k].second;
const auto& counter_des = counters_vec[index];
mux_ram[0][odd_idx] = Primitives::spm_mux_ram_value(counter_des);
counter_map[index] = odd_idx | 0x8000;
odd_idx = Primitives::spm_mux_ram_idx_incr(odd_idx);
}
}
@@ -211,15 +252,18 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
// Use this code to do 32-bit SQ profiling
if (j == Primitives::SQ_BLOCK_ID && config->spm_sq_32bit_mode) {
for (size_t k = 0; k < counter_info_even[j].size(); ++k) {
const auto& counter_des = counters_vec[counter_info_even[j][k].second];
const auto index = counter_info_even[j][k].second;
const auto& counter_des = counters_vec[index];
const auto counter = uint16_t(counter_des.index) * 2;
const auto block = Primitives::SQ_BLOCK_SPM_ID;
const auto instance = uint16_t(counter_des.block_des.index);
mux_ram[1][even_idx] = Primitives::spm_mux_ram_value(counter, block, instance);
counter_map[index] = even_idx;
even_idx = Primitives::spm_mux_ram_idx_incr(even_idx);
}
for (size_t k = 0; k < counter_info_odd[j].size(); ++k) {
const auto& counter_des = counters_vec[counter_info_odd[j][k].second];
const auto index = counter_info_odd[j][k].second;
const auto& counter_des = counters_vec[index];
const auto counter = uint16_t(counter_des.index) * 2 + 1;
const auto block = Primitives::SQ_BLOCK_SPM_ID;
const auto instance = uint16_t(counter_des.block_des.index);
@@ -234,13 +278,17 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
const auto* block_info = counter_des.block_info;
if (!(block_info->attr & CounterBlockSpmGlobalAttr)) {
for (size_t k = 0; k < counter_info_even[j].size(); ++k) {
const auto& counter_des = counters_vec[counter_info_even[j][k].second];
const auto index = counter_info_even[j][k].second;
const auto& counter_des = counters_vec[index];
mux_ram[1][even_idx] = Primitives::spm_mux_ram_value(counter_des);
counter_map[index] = even_idx;
even_idx = Primitives::spm_mux_ram_idx_incr(even_idx);
}
for (size_t k = 0; k < counter_info_odd[j].size(); ++k) {
const auto& counter_des = counters_vec[counter_info_odd[j][k].second];
const auto index = counter_info_odd[j][k].second;
const auto& counter_des = counters_vec[index];
mux_ram[1][odd_idx] = Primitives::spm_mux_ram_value(counter_des);
counter_map[index] = odd_idx;
odd_idx = Primitives::spm_mux_ram_idx_incr(odd_idx);
}
}
@@ -248,6 +296,13 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
}
}
if (config->spm_sample_delay_max) {
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::GRBM_GFX_INDEX_ADDR,
Primitives::grbm_broadcast_value());
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::RLC_SPM_PERFMON_SAMPLE_DELAY_MAX__ADDR,
config->spm_sample_delay_max);
}
for (const auto& counter_des : counters_vec) {
const auto* block_info = counter_des.block_info;
const auto& reg_info = block_info->counter_reg_info[counter_des.index];
@@ -300,27 +355,41 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
for (size_t i = 0; i < Primitives::NUMBER_OF_BLOCKS; ++i) {
if (i == Primitives::SQ_BLOCK_ID) continue;
for (size_t j = 0; j < counter_info_even[i].size(); ++j) {
int instance = 0;
int je, jo, j; // je & jo store even/odd array index, j stores index of counter registers
for (je = jo = j = 0; je < counter_info_even[i].size(); ++je, ++j) {
// get 16-bit SPM select value for even counters
const auto& counter_des = counters_vec[counter_info_even[i][j].second];
const auto& counter_des = counters_vec[counter_info_even[i][je].second];
uint32_t spm_select_value = Primitives::spm_even_select_value(counter_des);
if (counter_des.block_des.index != instance) {
instance = counter_des.block_des.index;
// Reset counter register index when instance switches
j = 0;
}
if (j + 1 <= counter_info_odd[i].size()) {
const auto& counter_des = counters_vec[counter_info_odd[i][j].second];
spm_select_value |= Primitives::spm_odd_select_value(counter_des);
// get 16-bit SPM select value for odd counters
if (jo < counter_info_odd[i].size()) {
const auto& counter_des = counters_vec[counter_info_odd[i][jo].second];
if (counter_des.block_des.index == instance) {
spm_select_value |= Primitives::spm_odd_select_value(counter_des);
jo++;
}
}
const auto* block_info = counter_des.block_info;
int index = j >> 1;
int offset = j % 2;
uint32_t spm_select_addr =
builder.get_addr(block_info->counter_reg_info[index].select_addr) + offset;
int select = j % 2;
Register spm_select_addr = (select == 0) ?
block_info->counter_reg_info[index].select_addr :
block_info->counter_reg_info[index].select1_addr;
builder.BuildWriteUConfigRegPacket(
cmd_buffer, Primitives::GRBM_GFX_INDEX_ADDR,
Primitives::grbm_inst_index_value(counter_des.block_des.index));
builder.BuildWriteConfigRegPacket(cmd_buffer, spm_select_addr, spm_select_value);
}
}
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::GRBM_GFX_INDEX_ADDR,
Primitives::grbm_broadcast_value());
// Set segment size
uint32_t global_count = ss[0];
@@ -333,6 +402,13 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
cmd_buffer, Primitives::RLC_SPM_PERFMON_SEGMENT_SIZE_CORE1__ADDR,
Primitives::rlc_spm_perfmon_segment_size_core1_value(se_count));
}
spm_buffer_desc->global_num_line = global_count;
spm_buffer_desc->se_num_line = se_count;
spm_buffer_desc->num_se = config->se_number;
spm_buffer_desc->num_sa = config->sa_number;
spm_buffer_desc->num_xcc = config->xcc_number;
spm_buffer_desc->num_events = counters_vec.size();
// Finish MUXSEL RAM
// 5. Program the RLC_[GLOBAL/SE]_MUXSEL_ADDR register with the starting address, likely zero.
if (!mux_ram[0].empty()) {
@@ -374,8 +450,11 @@ class GpuSpmBuilder : public SpmBuilder, protected Primitives {
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::CP_PERFMON_CNTL_ADDR,
Primitives::cp_perfmon_cntl_spm_stop_value());
// SPM counters reset
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::CP_PERFMON_CNTL_ADDR,
Primitives::cp_perfmon_cntl_reset_value());
// 'SPM counters reset' must be done in KFD. See comments in Begin() for more details
//
// builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::CP_PERFMON_CNTL_ADDR,
// Primitives::cp_perfmon_cntl_reset_value());
// On Vega this disable clock for performance counters
if (Primitives::GFXIP_LEVEL == 9)
builder.BuildWriteUConfigRegPacket(cmd_buffer, Primitives::RLC_PERFMON_CLK_CNTL_ADDR, 0);
@@ -90,6 +90,8 @@ target_sources(spm-builder-test PRIVATE ${AQLPROFILE_SPM_BUILDER_SOURCES})
target_include_directories(spm-builder-test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIB_DIR} ${LIB_DIR}/core/include)
target_link_libraries(
spm-builder-test
PUBLIC
aqlprofile::headers
PRIVATE
hsa-runtime64::hsa-runtime64
GTest::gtest