tegra: hwpm: add initial userspace lib

Initial change for libnvsochwpm userspace library.

Change-Id: I20b11f9d253b65583db97dfebd9ff78b4d33d50c
Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-hwpm/+/3267999
Reviewed-by: Vasuki Shankar <vasukis@nvidia.com>
Reviewed-by: Yifei Wan <ywan@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Besar Wicaksono
2024-12-13 23:29:00 +00:00
committed by mobile promotions
parent f1de425d35
commit 7f1249c9e9
25 changed files with 6846 additions and 0 deletions

60
libnvsochwpm/Makefile Normal file
View File

@@ -0,0 +1,60 @@
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
################################################################################
# Borrow the make environment from Linux kernel to support cross compilation.
ifneq ($(KERNEL_SOURCE),)
export srctree := $(KERNEL_SOURCE)
include $(srctree)/tools/scripts/Makefile.include
include $(srctree)/tools/scripts/Makefile.arch
else
CC = gcc
endif
SOURCES = \
common/log.c \
os/lnx/nv_soc_hwpm_lnx.c \
nv_soc_hwpm.c
OBJECTS=$(foreach x, $(basename $(SOURCES)), $(x).o)
CFLAGS = -Wall -Wextra -Werror=missing-prototypes -Wswitch -Wformat
CFLAGS += -Wchar-subscripts -Wparentheses -Wtrigraphs -Wpointer-arith
CFLAGS += -Wmissing-declarations -Wredundant-decls -Wundef -Wmain
CFLAGS += -Wreturn-type -Wmultichar -Wunused -Wmissing-braces -Werror
LDFLAGS = -Wall -Wl,--version-script=os/lnx/libnvsochwpm.map
INCLUDES = -I. -I./include -I../include
# This library requires dma_buf headers, which may not be available by default
# under linux include dir. User can specify this argument to point the dma_buf
# header files.
ifneq ($(NV_SOURCE),)
INCLUDES += -I$(NV_SOURCE)/core/include
endif
all: libnvsochwpm.so
# Shared Libs
libnvsochwpm.so: $(OBJECTS)
$(CC) $(LDFLAGS) -shared -fpic -Wl,-soname,libnvsochwpm.so $^ -o $@
rm -f $(OBJECTS)
# Objects
$(OBJECTS): %.o: %.c
$(CC) $(CFLAGS) -std=gnu99 -fpic -c $(INCLUDES) $< -o $@
clean:
rm -f $(OBJECTS)
rm -f libnvsochwpm.so*

View File

@@ -0,0 +1,33 @@
################################### tell Emacs this is a -*- makefile-gmake -*-
#
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# tmake for SW Mobile
#
# Repository umbrella makefile fragment for "libnvsochwpm"
#
###############################################################################
#
# Build tools in this repository
#
#NV_REPOSITORY_BUILD_TOOLS :=
#
# Components in this repository
#
NV_REPOSITORY_COMPONENTS := \
tmake/lib
# Local Variables:
# indent-tabs-mode: t
# tab-width: 8
# End:

31
libnvsochwpm/common/bit.h Normal file
View File

@@ -0,0 +1,31 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __BIT_H__
#define __BIT_H__
#include <stdint.h>
#define FIELD_GET(v, m, s) ((v & m) >> s)
#define FIELD_SET(v, m, s) ((v & m) << s)
#define VAL_64(lo, hi) (uint64_t)lo | ((uint64_t)hi << 32U)
#define VAL_LO64(v) FIELD_GET(v, 0xFFFFFFFFULL, 0U)
#define VAL_HI64(v) FIELD_GET(v, 0xFFFFFFFF00000000ULL, 32U)
/* Align value up to the next multiple of alignment */
#define ALIGN_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment) - 1))
#endif /*__BIT_H__*/

37
libnvsochwpm/common/log.c Normal file
View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "common/log.h"
#ifdef __cplusplus
extern "C" {
#endif
static const char* kLvlString[LOG_COUNT] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};
void nv_soc_hwpm_log(int lvl, const char *file, int line, const char *fmt, ...)
{
// TODO: actual implementation with different log levels.
printf("[%s] %s:%d - ", kLvlString[lvl], file, line);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
#ifdef __cplusplus
} /* extern "C" */
#endif

32
libnvsochwpm/common/log.h Normal file
View File

@@ -0,0 +1,32 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __LOG_H__
#define __LOG_H__
#include <stdio.h>
#include <stdarg.h>
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL, LOG_COUNT };
#define log_trace(...) nv_soc_hwpm_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) nv_soc_hwpm_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) nv_soc_hwpm_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) nv_soc_hwpm_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) nv_soc_hwpm_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define log_fatal(...) nv_soc_hwpm_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
void nv_soc_hwpm_log(int lvl, const char *file, int line, const char *fmt, ...);
#endif /*__LOG_H__*/

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __UTIL_REGISTER_UTIL_H__
#define __UTIL_REGISTER_UTIL_H__
#define DRF_SHIFT32(drf) ((0?drf) % 32)
#define DRF_MASK32(drf) (0xFFFFFFFFU>>(31-((1?drf) % 32)+((0?drf) % 32)))
#define DRF_SHIFT64(drf) ((0?drf) % 64)
#define DRF_MASK64(drf) (0xFFFFFFFFFFFFFFFFULL>>(63-((1?drf) % 64)+((0?drf) % 64)))
#define DRF_BITRANGE(drf) ((1?drf) - (0?drf) + 1)
#define REG32_RD(reg32, range) (reg32 >> DRF_SHIFT32(range)) & DRF_MASK32(range)
#define REG32_WR(reg32, range, val) (reg32 & ~(DRF_MASK32(range) << DRF_SHIFT32(range))) | ((val & DRF_MASK32(range)) << DRF_SHIFT32(range))
#define REG32_RD_MS(reg32, mask, shift) (reg32 >> shift) & mask
#define REG32_WR_MS(reg32, mask, shift, val) (reg32 & ~(mask << shift)) | ((val & mask) << shift)
#define REG64_RD(reg64, range) (reg64 >> DRF_SHIFT64(range)) & DRF_MASK64(range)
#define REG64_WR(reg64, range, val) (reg64 & ~(DRF_MASK64(range) << DRF_SHIFT64(range))) | ((val & DRF_MASK64(range)) << DRF_SHIFT64(range))
#define REG64_RD_MS(reg64, mask, shift) (reg64 >> shift) & mask
#define REG64_WR_MS(reg64, mask, shift, val) (reg64 & ~(mask << shift)) | ((val & mask) << shift)
#endif // __UTIL_REGISTER_UTIL_H__

153
libnvsochwpm/common/types.h Normal file
View File

@@ -0,0 +1,153 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __TYPES_H__
#define __TYPES_H__
#include "nv_soc_hwpm.h"
#include "common/bit.h"
#define MAX_SOCKET_COUNT 1
#define MAX_DEVICE_COUNT 1
#define MAX_SESSION_COUNT 1
#define MAX_PATH_LENGTH 256
#define MAX_FS_MASK_SIZE 256
#define DEV_HANDLE_SHIFT 0U
#define DEV_HANDLE_MASK 0xFFULL /* Up to 256 HWPM devices */
#define SESSION_HANDLE_SHIFT 16U
#define SESSION_HANDLE_MASK 0xFF00ULL /* Up to 256 HWPM sessions per device */
static inline uint32_t to_dev_idx(uint64_t handle) {
return FIELD_GET(handle, DEV_HANDLE_MASK, DEV_HANDLE_SHIFT);
}
static inline uint64_t to_dev_handle(uint32_t dev_idx) {
uint64_t handle = FIELD_SET(dev_idx, DEV_HANDLE_MASK, DEV_HANDLE_SHIFT);
return handle;
}
static inline uint32_t to_session_idx(uint64_t handle) {
return FIELD_GET(handle, SESSION_HANDLE_MASK, SESSION_HANDLE_SHIFT);
}
static inline uint64_t to_session_handle(uint32_t dev_idx, uint32_t session_idx) {
uint64_t handle = 0;
handle |= FIELD_SET(dev_idx, DEV_HANDLE_MASK, DEV_HANDLE_SHIFT);
handle |= FIELD_SET(session_idx, SESSION_HANDLE_MASK, SESSION_HANDLE_SHIFT);
return handle;
}
/**
* @brief Struct containing NV_SOC_HWPM allocated PMA buffer.
*/
typedef struct {
size_t size;
uint8_t* buffer;
uint64_t pma_va;
uint32_t handle;
uint32_t heap_fd;
} nv_soc_hwpm_pma_buffer;
/**
* @brief Struct containing NV_SOC_HWPM session attributes.
*/
typedef struct {
nv_soc_hwpm_device device;
nv_soc_hwpm_pma_buffer record_buffer;
nv_soc_hwpm_pma_buffer mem_bytes_buffer;
uint32_t resource_count;
nv_soc_hwpm_resource resource_ids[NV_SOC_HWPM_NUM_RESOURCES];
uint8_t is_resource_reserved;
uint8_t is_pma_buffer_allocated;
uint8_t is_session_started;
} nv_soc_hwpm_session_info;
/**
* @brief Internal NV_SOC_HWPM session structure.
*/
typedef struct
{
int fd;
nv_soc_hwpm_session_info info;
struct nv_soc_hwpm_device_int* dev_int;
} nv_soc_hwpm_session_int;
/**
* @brief Struct containing NV_SOC_HWPM device attributes.
*/
typedef struct {
uint32_t soc_chip_id;
uint32_t soc_revision;
uint32_t soc_platform;
uint32_t soc_socket;
uint32_t ip_available_count;
nv_soc_hwpm_ip ip_available_list[NV_SOC_HWPM_NUM_IPS];
uint32_t res_available_count;
nv_soc_hwpm_resource res_available_list[NV_SOC_HWPM_NUM_RESOURCES];
} nv_soc_hwpm_device_info;
/**
* @brief Struct containing NV_SOC_HWPM IP attributes.
*/
typedef struct {
/* IP availability status. */
uint8_t is_available;
/* IP instance/element max count. */
uint32_t inst_max_count;
/* IP floorsweeping mask. */
uint8_t fs_mask[MAX_FS_MASK_SIZE/sizeof(uint8_t)];
} nv_soc_hwpm_ip_info;
/**
* @brief Struct containing NV_SOC_HWPM resource attributes.
*/
typedef struct {
/* Resource availability status. */
uint8_t is_available;
/* Resource reservation status. */
uint8_t is_reserved;
} nv_soc_hwpm_resource_info;
/**
* @brief Internal NV_SOC_HWPM device structure for Linux.
*/
typedef struct
{
const char* dev_path;
nv_soc_hwpm_device_info dev_info;
nv_soc_hwpm_ip_info ip_info[NV_SOC_HWPM_NUM_IPS];
nv_soc_hwpm_resource_info res_info[NV_SOC_HWPM_NUM_RESOURCES];
nv_soc_hwpm_session_int* session_int[MAX_SESSION_COUNT];
uint32_t session_count;
uint32_t session_count_max;
} nv_soc_hwpm_device_int;
nv_soc_hwpm_device_int* to_device_int(uint64_t handle);
nv_soc_hwpm_session_int* to_session_int(uint64_t handle);
#endif /*__TYPES_H__*/

View File

@@ -0,0 +1,799 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* nv_soc_hwpm.h:
* This is the userspace API header for the Tegra SOC HWPM driver.
*/
#ifndef __NV_SOC_HWPM_H__
#define __NV_SOC_HWPM_H__
#include <stddef.h>
#include <stdint.h>
#define NV_SOC_HWPM_API_EXPORT
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief List of IPs supported by HWPM.
*
* The list of supported IPs are varied across different SoCs. This enum
* provides a flattened list of all IPs supported by HWPM.
*
* @note this enum is duplicated from driver header.
*/
typedef enum {
NV_SOC_HWPM_IP_VI,
NV_SOC_HWPM_IP_ISP,
NV_SOC_HWPM_IP_VIC,
NV_SOC_HWPM_IP_OFA,
NV_SOC_HWPM_IP_PVA,
NV_SOC_HWPM_IP_NVDLA,
NV_SOC_HWPM_IP_MGBE,
NV_SOC_HWPM_IP_SCF,
NV_SOC_HWPM_IP_NVDEC,
NV_SOC_HWPM_IP_NVENC,
NV_SOC_HWPM_IP_PCIE,
NV_SOC_HWPM_IP_DISPLAY,
NV_SOC_HWPM_IP_MSS_CHANNEL,
NV_SOC_HWPM_IP_MSS_GPU_HUB,
NV_SOC_HWPM_IP_MSS_ISO_NISO_HUBS,
NV_SOC_HWPM_IP_MSS_MCF,
NV_SOC_HWPM_IP_APE,
NV_SOC_HWPM_IP_C2C,
NV_SOC_HWPM_IP_SMMU,
NV_SOC_HWPM_IP_CL2,
NV_SOC_HWPM_IP_NVLCTRL,
NV_SOC_HWPM_IP_NVLRX,
NV_SOC_HWPM_IP_NVLTX,
NV_SOC_HWPM_IP_MSS_HUB,
NV_SOC_HWPM_IP_MCF_SOC,
NV_SOC_HWPM_IP_MCF_C2C,
NV_SOC_HWPM_IP_MCF_CLINK,
NV_SOC_HWPM_IP_MCF_CORE,
NV_SOC_HWPM_IP_MCF_OCU,
NV_SOC_HWPM_IP_PCIE_XTLQ,
NV_SOC_HWPM_IP_PCIE_XTLRC,
NV_SOC_HWPM_IP_PCIE_XALRC,
NV_SOC_HWPM_IP_UCF_MSW,
NV_SOC_HWPM_IP_UCF_PSW,
NV_SOC_HWPM_IP_UCF_CSW,
NV_SOC_HWPM_IP_UCF_HUB,
NV_SOC_HWPM_IP_UCF_SCB,
NV_SOC_HWPM_IP_CPU,
NV_SOC_HWPM_NUM_IPS
} nv_soc_hwpm_ip;
/**
* @brief The resources which can be reserved for profiling.
*
* The list of supported resources are varied across different SoCs. This enum
* provides a flattened list of all resources supported by HWPM.
*
* @note this enum is duplicated from driver header.
*/
typedef enum {
NV_SOC_HWPM_RESOURCE_VI,
NV_SOC_HWPM_RESOURCE_ISP,
NV_SOC_HWPM_RESOURCE_VIC,
NV_SOC_HWPM_RESOURCE_OFA,
NV_SOC_HWPM_RESOURCE_PVA,
NV_SOC_HWPM_RESOURCE_NVDLA,
NV_SOC_HWPM_RESOURCE_MGBE,
NV_SOC_HWPM_RESOURCE_SCF,
NV_SOC_HWPM_RESOURCE_NVDEC,
NV_SOC_HWPM_RESOURCE_NVENC,
NV_SOC_HWPM_RESOURCE_PCIE,
NV_SOC_HWPM_RESOURCE_DISPLAY,
NV_SOC_HWPM_RESOURCE_MSS_CHANNEL,
NV_SOC_HWPM_RESOURCE_MSS_GPU_HUB,
NV_SOC_HWPM_RESOURCE_MSS_ISO_NISO_HUBS,
NV_SOC_HWPM_RESOURCE_MSS_MCF,
/*
* - SYS0 PERMON in RPG_PMG
* - PERFMUX: PMA_CHANNEL_PERFMUX_CONFIG_SECURE
*/
NV_SOC_HWPM_RESOURCE_PMA,
/*
* - PMA: Everything except PMA_CHANNEL_PERFMUX_CONFIG_SECURE
* - RTR: Entire aperture
*/
NV_SOC_HWPM_RESOURCE_CMD_SLICE_RTR,
NV_SOC_HWPM_RESOURCE_APE,
NV_SOC_HWPM_RESOURCE_C2C,
NV_SOC_HWPM_RESOURCE_SMMU,
NV_SOC_HWPM_RESOURCE_CL2,
NV_SOC_HWPM_RESOURCE_NVLCTRL,
NV_SOC_HWPM_RESOURCE_NVLRX,
NV_SOC_HWPM_RESOURCE_NVLTX,
NV_SOC_HWPM_RESOURCE_MSS_HUB,
NV_SOC_HWPM_RESOURCE_MCF_SOC,
NV_SOC_HWPM_RESOURCE_MCF_C2C,
NV_SOC_HWPM_RESOURCE_MCF_CLINK,
NV_SOC_HWPM_RESOURCE_MCF_CORE,
NV_SOC_HWPM_RESOURCE_MCF_OCU,
NV_SOC_HWPM_RESOURCE_PCIE_XTLQ,
NV_SOC_HWPM_RESOURCE_PCIE_XTLRC,
NV_SOC_HWPM_RESOURCE_PCIE_XALRC,
NV_SOC_HWPM_RESOURCE_UCF_MSW,
NV_SOC_HWPM_RESOURCE_UCF_PSW,
NV_SOC_HWPM_RESOURCE_UCF_CSW,
NV_SOC_HWPM_RESOURCE_UCF_HUB,
NV_SOC_HWPM_RESOURCE_UCF_SCB,
NV_SOC_HWPM_RESOURCE_CPU,
NV_SOC_HWPM_NUM_RESOURCES
} nv_soc_hwpm_resource;
/**
* @brief Initialize the NV_SOC_HWPM library.
*
* The call to this library will enumerate the supported devices. This function
* should be the first to be called before other API. There is no reference
* counting here, so caller needs to make sure it is only called once.
*
* Internally it will enumerate the available HWPM driver device nodes, hence it
* will need to open connection to the driver and makes some IOCTLs.
* After it's done enumerating, the driver connection will be closed.
*
* @returns This function returns 0 on success, negative errno on failure:
* -ENOMEM: unable to allocate memory for internal data structures.
* -ENOENT: can't find HWPM device node.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_init(void);
/**
* @brief Finalize the NV_SOC_HWPM library.
*
* It will cleanup internal data structures, including any allocated session, and
* close all connections to driver.
*
* This function should be called at the end of execution.
*/
NV_SOC_HWPM_API_EXPORT void nv_soc_hwpm_exit(void);
/**
* @brief General system attributes. See \ref nv_soc_hwpm_system_get_info.
*/
typedef enum {
/* The major version of the library. */
NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MAJOR,
/* The minor version of the library. */
NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MINOR,
} nv_soc_hwpm_system_attribute;
/**
* @brief Get system-level information about the SOC HWPM library.
*
* This function retrieves system-level information based on the specified
* attribute.
*
* @param[in] attribute The system attribute to query. See @ref
* nv_soc_hwpm_system_attribute.
* - NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MAJOR: info buffer
* should fit uint16_t.
* - NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MINOR: info buffer
* should fit uint16_t.
* @param[in] info_size The size of the \p info buffer.
* @param[out] info Pointer to user/application allocated buffer to store the
* attribute value.
*
* @return 0 on success, negative errno on failure:
* -EINVAL: if attribute is invalid or info is NULL.
*/
int nv_soc_hwpm_system_get_info(
nv_soc_hwpm_system_attribute attribute, uint32_t info_size, void* info);
/**
* @brief Struct containing opaque handle to an NV_SOC_HWPM device.
*
* An NV_SOC_HWPM device corresponds to a HWPM device node in the system.
*
* The user will need to use this handle to do the following:
* - gather information such as the SOC information, the socket the node is
* associated with, and the IP, resources available in the socket.
* - create a HWPM session in the socket.
*/
typedef struct { uint64_t handle; } nv_soc_hwpm_device;
/**
* @brief Get list of NV_SOC_HWPM devices.
*
* User will need to call this function twice:
* - first by giving a non NULL count and set its value to zero. This will
* populate count with the number of available devices.
* - second by giving non NULL valid pointers on both parameters, with non
* zero count. This will populate devices array with the SOC HWPM device
* handles for subsequent usage.
*
* @param[inout] count Pointer to variable to store the number of available
* devices.
*
* @param[out] devices NV_SOC_HWPM device array. The size of the array should
* be equal or greater than the value in \p count.
*
* @return 0 on success, negative errno on failure:
* -EINVAL: if one of the arguments is invalid.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_get_devices(
uint32_t *count, nv_soc_hwpm_device *devices);
/* Tegra SOC chip id */
typedef enum {
TEGRA_SOC_HWPM_CHIP_ID_T241 = 0x500,
TEGRA_SOC_HWPM_CHIP_ID_T410 = 0x410,
TEGRA_SOC_HWPM_CHIP_ID_COUNT = 1
} tegra_soc_hwpm_chip_id;
/* Tegra SOC platform type */
typedef enum {
TEGRA_SOC_HWPM_PLATFORM_SILICON = 0x0,
TEGRA_SOC_HWPM_PLATFORM_PRESI_QT = 0x1,
TEGRA_SOC_HWPM_PLATFORM_PRESI_VDK = 0x8,
TEGRA_SOC_HWPM_PLATFORM_PRESI_VSP = 0x9
} tegra_soc_hwpm_platform;
/**
* @brief Device attributes. See \ref nv_soc_hwpm_device_get_info.
*/
typedef enum {
/* The Tegra SOC chip id. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_CHIP_ID,
/* The Tegra SOC revision number. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_REVISION,
/* The Tegra SOC platform type. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_PLATFORM,
/* The Tegra SOC socket id. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_SOCKET,
/* The number of available IPs. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT,
/* Array containing the id of available IPs. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_LIST,
/* The number of available resources. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT,
/* Array containing the id of available resources. */
NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST,
} nv_soc_hwpm_device_attribute;
/**
* @brief Get device information.
*
* This function retrieves device-specific information based on the specified
* attribute.
*
* @param[in] device Handle to NV_SOC_HWPM device.
*
* @param[in] attribute The device attribute to query. See @ref
* nv_soc_hwpm_device_attribute.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_CHIP_ID: the info
* buffer should fit @ref tegra_soc_hwpm_chip_id.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_REVISION: the info
* buffer should fit uint32_t.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_PLATFORM: the info
* buffer should fit @ref tegra_soc_hwpm_platform.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_SOCKET: the info
* buffer should fit uint32_t.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT: the
* info buffer should fit uint32_t.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_LIST: the
* info buffer should point to an array of
* nv_soc_hwpm_ip. The size of the array should fit
* the value of @ref
* NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT:
* the info buffer should fit uint32_t.
* - NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST:
* the info buffer should point to an array of
* nv_soc_hwpm_resource. The size of the array should
* fit the value of @ref
* NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT.
* @param[in] info_size The size of the \p info buffer.
* @param[out] info Pointer to user/application allocated buffer to store the
* attribute value.
*
* @return 0 on success, negative errno on failure:
* -EINVAL: if device is invalid, attribute is invalid, info_size does
* not fit the attribute value, info is NULL.
*/
int nv_soc_hwpm_device_get_info(
nv_soc_hwpm_device device, nv_soc_hwpm_device_attribute attribute,
uint32_t info_size, void* info);
/**
* @brief IP attributes. See \ref nv_soc_hwpm_ip_get_info.
*/
typedef enum {
/**
* The IP availability status. 1 means IP is available, 0 means IP is
* unavailable.
*/
NV_SOC_HWPM_IP_ATTRIBUTE_IS_AVAILABLE,
/* The IP instance/element max count. */
NV_SOC_HWPM_IP_ATTRIBUTE_INST_MAX_COUNT,
/**
* The flatten IP instance/element floorsweeping mask. The LSB is the
* lowest element/instance. The number of bits is equal to the
* @ref NV_SOC_HWPM_IP_ATTRIBUTE_INST_MAX_COUNT.
*/
NV_SOC_HWPM_IP_ATTRIBUTE_FS_MASK,
} nv_soc_hwpm_ip_attribute;
/**
* @brief Get information about a specific IP.
*
* @param[in] device The SOC HWPM device handle.
* @param[in] ip The IP to query information about.
* @param[in] attribute The IP attribute to query. See @ref nv_soc_hwpm_ip_attribute.
* - NV_SOC_HWPM_IP_ATTRIBUTE_IS_AVAILABLE: value should fit
* uint32_t.
* - NV_SOC_HWPM_IP_ATTRIBUTE_INST_MAX_COUNT: value should
* fit uint32_t.
* - NV_SOC_HWPM_IP_ATTRIBUTE_FS_MASK: value should fit
* the number of bits equal to the value of @ref
* NV_SOC_HWPM_IP_ATTRIBUTE_INST_MAX_COUNT.
* @param[in] info_size The size of the \p info buffer.
* @param[out] info Pointer to user/application allocated buffer to store the
* attribute value.
*
* @return 0 on success, negative errno on failure:
* -EINVAL: if device is invalid, ip is invalid, attribute is invalid,
* info_size does not fit the attribute value, or info is NULL.
*/
int nv_soc_hwpm_ip_get_info(
nv_soc_hwpm_device device, nv_soc_hwpm_ip ip,
nv_soc_hwpm_ip_attribute attribute, uint32_t info_size, void* info);
/**
* @brief Resource attributes. See \ref nv_soc_hwpm_resource_get_info.
*/
typedef enum {
/**
* The resource availability status. The output type of this attribute is
* uint32_t. 1 means resource is available, 0 means unavailable.
*/
NV_SOC_HWPM_RESOURCE_ATTRIBUTE_IS_AVAILABLE,
} nv_soc_hwpm_resource_attribute;
/**
* @brief Get information about a specific resource.
*
* @param[in] device The SOC HWPM device handle.
* @param[in] resource The resource to query information about.
* @param[in] attribute The resource attribute to query.
* See @ref nv_soc_hwpm_resource_attribute.
* - NV_SOC_HWPM_RESOURCE_ATTRIBUTE_IS_AVAILABLE: value should
* fit uint32_t.
* @param[in] info_size The size of the \p info buffer.
* @param[out] info Pointer to user/application allocated buffer to store the
* attribute value.
*
* @return 0 on success, negative errno on failure:
* -EINVAL: if device is invalid, resource is invalid, attribute is
* invalid, info_size does not fit the attribute value, or info is NULL.
*/
int nv_soc_hwpm_resource_get_info(
nv_soc_hwpm_device device, nv_soc_hwpm_resource resource,
nv_soc_hwpm_resource_attribute attribute, uint32_t info_size, void* info);
/**
* @brief Struct containing opaque handle to an NV_SOC_HWPM session.
*
* The user will need this handle to do the following:
* - reserve the resources that will be used in the experiment
* - manage the PMA buffer
* - perform regops
* - get PMA channel state
* - working with cross trigger
* - credit programming
*/
typedef struct { uint64_t handle; } nv_soc_hwpm_session;
/**
* @brief Create a session on a device.
*
* Internally this will open connection to the corresponding driver device node
* and keep it open until the session is freed.
*
* @param[in] device Handle to NV_SOC_HWPM device.
*
* @param[out] session Pointer to NV_SOC_HWPM session handle.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: one of the arguments is invalid.
* -ENOMEM: unable to create new session since the maximum session count
* is reached.
* Other non zero status corresponds to failure when opening the driver
* device node.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_alloc(
nv_soc_hwpm_device device, nv_soc_hwpm_session *session);
/**
* @brief Free a session on a device.
*
* This will reset the state of a session, cleanup any allocated buffer, and
* close connection to the driver device node.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_free(
nv_soc_hwpm_session session);
/**
* @brief Session attributes. See \ref nv_soc_hwpm_session_get_info.
*/
typedef enum {
/* The device handle associated with the session. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_DEVICE,
/**
* The session's start status. 1 means the session is started. 0 means
* session is not started.
*/
NV_SOC_HWPM_SESSION_ATTRIBUTE_SESSION_STARTED,
/**
* The session's PMA buffer allocation status. 1 means the buffers
* are allocated. 0 means buffers are not allocated.
*/
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_BUFFER_ALLOCATED,
/* Size of the session's record buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE,
/* CPU accessible address of the session's record buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA,
/* PMA accessible addres of the session's record buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA,
/* Opaque handle of the session's record buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_HANDLE,
/* Size of the session's mem bytes buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_SIZE,
/* CPU accessible address of the session's mem bytes buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA,
/* PMA accessible addres of the session's mem bytes buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_PMA_VA,
/* Opaque handle of the session's mem bytes buffer. */
NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_HANDLE,
} nv_soc_hwpm_session_attribute;
/**
* @brief Get info of an NV_SOC_HWPM session.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] attribute The session attribute to query.
* See @ref nv_soc_hwpm_session_attribute.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_DEVICE: the info buffer
* should fit @ref nv_soc_hwpm_device.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_SESSION_STARTED: the info
* buffer should fit uint32_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_BUFFER_ALLOCATED: the
* info buffer should fit uint32_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE:
* the info buffer should fit size_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA:
* the info buffer should fit (void*).
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA:
* the info buffer should fit uint64_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_HANDLE:
* the info buffer should fit uint32_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_SIZE:
* the info buffer should fit size_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA:
* the info buffer should fit (void*).
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_PMA_VA:
* the info buffer should fit uint64_t.
* - NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_HANDLE:
* the info buffer should fit uint32_t.
* @param[in] info_size The size of the \p info buffer.
* @param[out] info Pointer to user/application allocated buffer to store the
* attribute value.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, attribute is invalid, info_size does
* not fit the attribute value, or info is NULL.
*/
int nv_soc_hwpm_session_get_info(
nv_soc_hwpm_session session, nv_soc_hwpm_session_attribute attribute,
uint32_t info_size, void* info);
/**
* @brief Reserve the resources that will be monitored by a session.
*
* This will perform the IOCTLs to reserve the resources to the driver.
* Note that since the driver does not expose interface to release a resource,
* the reserved resources can not be changed until the session is freed.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] res_count The number of element in \p res_ids
*
* @param[in] res_ids Array containing the resource id.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, res_count is invalid, res_ids is
* NULL, res_ids contain unavailable resource id, or the session
* has already started.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_reserve_resources(
nv_soc_hwpm_session session, uint32_t res_count,
const nv_soc_hwpm_resource *res_ids);
/**
* @brief Set all supported resources to be monitored to a session.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, or the session has already started.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_reserve_all_resources(
nv_soc_hwpm_session session);
/* Tegra SOC HWPM PMA buffer cache coherency type */
typedef enum
{
TEGRA_SOC_HWPM_COHERENCY_TYPE_UNCACHED,
TEGRA_SOC_HWPM_COHERENCY_TYPE_CACHED,
TEGRA_SOC_HWPM_COHERENCY_TYPE_COUNT
} tegra_soc_hwpm_coherency_type;
/**
* @brief Struct containing NV_SOC_HWPM PMA buffer allocation parameters.
*/
typedef struct {
size_t size;
void* cpu_va;
tegra_soc_hwpm_coherency_type coherency_type;
} nv_soc_hwpm_pma_buffer_params;
/**
* @brief Allocate memory for PMA channel.
*
* After a successful allocation, the user can call nv_soc_hwpm_session_get_info
* to get the allocation result, i.e nv_soc_hwpm_session_info::record_buffer
* and nv_soc_hwpm_session_info::mem_bytes_buffer.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] record_buffer_params Pointer to PMA record buffer allocation
* param structure.
*
* @param[in] mem_bytes_buffer_params Pointer to PMA mem_bytes buffer allocation
* param structure.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, a prior allocation has been made, or
* the session has already started.
* -EIO: error on IOCTLs to the driver.
* -ENOMEM: failure on allocating memory.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_alloc_pma(
nv_soc_hwpm_session session,
const nv_soc_hwpm_pma_buffer_params *record_buffer_params);
/**
* @brief Struct containing parameter to update and query PMA channel state.
*/
typedef struct {
uint64_t in_mem_bump;
uint8_t in_stream_mem_bytes;
uint8_t in_read_mem_head;
uint8_t in_check_overflow;
uint64_t out_mem_head;
uint8_t out_overflowed;
} nv_soc_hwpm_pma_channel_state_params;
/**
* @brief Set and get PMA channel state.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] param Structure containing the input/output arguments.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, param is NULL, or the session has
* not started yet.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_set_get_pma_state(
nv_soc_hwpm_session session,
nv_soc_hwpm_pma_channel_state_params* param);
/* Tegra SOC HWPM enums for querying various info */
typedef enum
{
/* Read credits information */
TEGRA_SOC_HWPM_GET_TYPE_HS_CREDITS,
/* Read total HS credits*/
TEGRA_SOC_HWPM_GET_TYPE_TOTAL_HS_CREDITS,
TEGRA_SOC_HWPM_GET_TYPE_COUNT,
} tegra_soc_hwpm_get_type;
/**
* @brief Credit programming interface to get HS credits.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] type credit or total HS credits.
*
* @param[in] num_hs_credits Pointer to output variable containing HS credits.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, type is invalid, num_hs_credits
* is NULL, or the session has not started yet.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_get_hs_credits(
nv_soc_hwpm_session session,
tegra_soc_hwpm_get_type type,
uint32_t* num_hs_credits);
/**
* @brief Struct containing parameter to configure HS credit.
*/
typedef struct {
uint8_t cblock_idx;
uint32_t num_credits_per_chiplet;
} nv_soc_hwpm_config_hs_credit_params;
/**
* @brief Credit programming interface to configure HS credits.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] param_count Number of element in \p params.
*
* @param[in] params Structure containing the array of credit param.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, param_count is invalid, params is
* NULL, or the session has not started yet.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_config_hs_credits(
nv_soc_hwpm_session session,
uint32_t param_count,
const nv_soc_hwpm_config_hs_credit_params* params);
/**
* @brief Starting session by binding the resources.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, or the session has already started.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_start(
nv_soc_hwpm_session session);
/* Tegra SOC HWPM enums for regops cmd */
typedef enum {
NV_SOC_HWPM_REG_OPS_CMD_INVALID,
NV_SOC_HWPM_REG_OPS_CMD_READ32,
NV_SOC_HWPM_REG_OPS_CMD_READ64,
NV_SOC_HWPM_REG_OPS_CMD_WRITE32,
NV_SOC_HWPM_REG_OPS_CMD_WRITE64,
NV_SOC_HWPM_REG_OPS_CMD_COUNT
} nv_soc_hwpm_reg_ops_cmd;
/* Tegra SOC HWPM enums for regops status */
typedef enum {
NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS,
NV_SOC_HWPM_REG_OPS_STATUS_INVALID_CMD,
NV_SOC_HWPM_REG_OPS_STATUS_INVALID_OFFSET,
NV_SOC_HWPM_REG_OPS_STATUS_INSUFFICIENT_PERMISSIONS,
NV_SOC_HWPM_REG_OPS_STATUS_COUNT
} nv_soc_hwpm_reg_ops_status;
/* Tegra SOC HWPM enums for regops validation */
typedef enum {
NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_INVALID,
NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_FAIL_ON_FIRST_ERROR,
NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR,
NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_COUNT
} nv_soc_hwpm_reg_ops_validation_mode;
/**
* @brief Struct containing parameter to perform regops.
*/
typedef struct {
/* register offset, as per kernel team this can be 40 bits */
uint64_t in_offset;
union {
uint32_t in_out_val32;
uint64_t in_out_val64;
};
union {
uint32_t in_mask32;
uint64_t in_mask64;
};
nv_soc_hwpm_reg_ops_cmd in_cmd;
nv_soc_hwpm_reg_ops_status out_status;
} nv_soc_hwpm_reg_ops_params;
/**
* @brief Function to read/write HWPM registers.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] param_count Number of element in \p params.
*
* @param[in] params Structure containing the array of regops param.
*
* @param[in] mode Validation mode.
*
* @param[out] all_reg_ops_passed Pointer to output variable, 1 means all the
* operations are successful, 0 means one or
* more are failed.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, param_count is invalid, params is
* NULL, or the session has not started yet.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_regops(
nv_soc_hwpm_session session,
const size_t param_count,
nv_soc_hwpm_reg_ops_params* params,
nv_soc_hwpm_reg_ops_validation_mode mode,
int* all_reg_ops_passed);
/* Tegra SOC HWPM trigger session type */
typedef enum {
NV_SOC_HWPM_TRIGGER_SESSION_TYPE_INVALID,
NV_SOC_HWPM_TRIGGER_SESSION_TYPE_PROFILER, /* maps to Start-Stop triggers */
NV_SOC_HWPM_TRIGGER_SESSION_TYPE_SAMPLER, /* maps to Periodic triggers */
NV_SOC_HWPM_TRIGGER_SESSION_TYPE_COUNT
} nv_soc_hwpm_trigger_session_type;
/**
* @brief Trigger mask setup interface.
*
* Used for programming cross-trigger config in the SECURE trigger mask
* registers.
*
* @param[in] session Handle to NV_SOC_HWPM session.
*
* @param[in] enable_cross_trigger 1 to enable and 0 to disable cross trigger.
*
* @param[in] session_type Trigger session type.
*
* @returns This function returns 0 on success, negative errno on failure:
* -EINVAL: if session is invalid, or the session has not started yet.
* -EIO: error on IOCTLs to the driver.
*/
NV_SOC_HWPM_API_EXPORT int nv_soc_hwpm_session_setup_trigger(
nv_soc_hwpm_session session,
int enable_cross_trigger,
nv_soc_hwpm_trigger_session_type session_type);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*__NV_SOC_HWPM_H__*/

View File

@@ -0,0 +1,107 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __NV_SOC_HWPM_LOADER_H__
#define __NV_SOC_HWPM_LOADER_H__
#include <dlfcn.h>
#include <stdio.h>
#include <errno.h>
#include "nv_soc_hwpm.h"
typedef struct {
void* handle;
decltype(nv_soc_hwpm_init)* nv_soc_hwpm_init_fn;
decltype(nv_soc_hwpm_exit)* nv_soc_hwpm_exit_fn;
decltype(nv_soc_hwpm_system_get_info)* nv_soc_hwpm_system_get_info_fn;
decltype(nv_soc_hwpm_get_devices)* nv_soc_hwpm_get_devices_fn;
decltype(nv_soc_hwpm_device_get_info)* nv_soc_hwpm_device_get_info_fn;
decltype(nv_soc_hwpm_ip_get_info)* nv_soc_hwpm_ip_get_info_fn;
decltype(nv_soc_hwpm_resource_get_info)* nv_soc_hwpm_resource_get_info_fn;
decltype(nv_soc_hwpm_session_alloc)* nv_soc_hwpm_session_alloc_fn;
decltype(nv_soc_hwpm_session_free)* nv_soc_hwpm_session_free_fn;
decltype(nv_soc_hwpm_session_get_info)* nv_soc_hwpm_session_get_info_fn;
decltype(nv_soc_hwpm_session_reserve_resources)* nv_soc_hwpm_session_reserve_resources_fn;
decltype(nv_soc_hwpm_session_reserve_all_resources)* nv_soc_hwpm_session_reserve_all_resources_fn;
decltype(nv_soc_hwpm_session_alloc_pma)* nv_soc_hwpm_session_alloc_pma_fn;
decltype(nv_soc_hwpm_session_set_get_pma_state)* nv_soc_hwpm_session_set_get_pma_state_fn;
decltype(nv_soc_hwpm_session_get_hs_credits)* nv_soc_hwpm_session_get_hs_credits_fn;
decltype(nv_soc_hwpm_session_config_hs_credits)* nv_soc_hwpm_session_config_hs_credits_fn;
decltype(nv_soc_hwpm_session_start)* nv_soc_hwpm_session_start_fn;
decltype(nv_soc_hwpm_session_regops)* nv_soc_hwpm_session_regops_fn;
decltype(nv_soc_hwpm_session_setup_trigger)* nv_soc_hwpm_session_setup_trigger_fn;
} nv_soc_hwpm_api_table;
#define XSTR(a) STR(a)
#define STR(a) #a
#define GET_HWPM_SYM(table, name) GET_HWPM_SYM_FN(table, name, name ## _fn)
#define GET_HWPM_SYM_FN(table, name, name_fn) \
table->name_fn = (decltype(name)* )dlsym(table->handle, XSTR(name)); \
if (table->name_fn == NULL) { \
fprintf (stderr, "%s\n", dlerror()); \
return -EINVAL; \
}
static inline int load_nv_soc_hwpm(const char* path, nv_soc_hwpm_api_table* table) {
if (table == NULL)
return -EINVAL;
if (table->handle != NULL) {
fprintf (stderr, "NvSocHwpm already loaded\n");
return -EINVAL;
}
table->handle = dlopen (path, RTLD_LAZY);
if (!table->handle) {
fprintf (stderr, "Failed to load NvSocHwpm: %s\n", dlerror());
return -EINVAL;
}
GET_HWPM_SYM(table, nv_soc_hwpm_init);
GET_HWPM_SYM(table, nv_soc_hwpm_exit);
GET_HWPM_SYM(table, nv_soc_hwpm_system_get_info);
GET_HWPM_SYM(table, nv_soc_hwpm_get_devices);
GET_HWPM_SYM(table, nv_soc_hwpm_device_get_info);
GET_HWPM_SYM(table, nv_soc_hwpm_ip_get_info);
GET_HWPM_SYM(table, nv_soc_hwpm_resource_get_info);
GET_HWPM_SYM(table, nv_soc_hwpm_session_alloc);
GET_HWPM_SYM(table, nv_soc_hwpm_session_free);
GET_HWPM_SYM(table, nv_soc_hwpm_session_get_info);
GET_HWPM_SYM(table, nv_soc_hwpm_session_reserve_resources);
GET_HWPM_SYM(table, nv_soc_hwpm_session_reserve_all_resources);
GET_HWPM_SYM(table, nv_soc_hwpm_session_alloc_pma);
GET_HWPM_SYM(table, nv_soc_hwpm_session_set_get_pma_state);
GET_HWPM_SYM(table, nv_soc_hwpm_session_get_hs_credits);
GET_HWPM_SYM(table, nv_soc_hwpm_session_config_hs_credits);
GET_HWPM_SYM(table, nv_soc_hwpm_session_start);
GET_HWPM_SYM(table, nv_soc_hwpm_session_regops);
GET_HWPM_SYM(table, nv_soc_hwpm_session_setup_trigger);
return 0;
}
static inline int unload_nv_soc_hwpm(nv_soc_hwpm_api_table* table) {
if (table == NULL)
return -EINVAL;
if (table->handle == NULL)
return 0;
dlclose(table->handle);
table->handle = NULL;
return 0;
}
#endif /* __NV_SOC_HWPM_LOADER_H__ */

879
libnvsochwpm/nv_soc_hwpm.c Normal file
View File

@@ -0,0 +1,879 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "nv_soc_hwpm.h"
#include "common/log.h"
#include "os/nv_soc_hwpm_os.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define LIB_VERSION_MAJOR 0
#define LIB_VERSION_MINOR 1
/* Internal tracker of the number of enumerated NV_SOC_HWPM devices. */
static uint32_t g_device_count = 0;
/* Internal NV_SOC_HWPM device array. */
static nv_soc_hwpm_device_int g_devices_int[MAX_DEVICE_COUNT] = {{ 0 }};
/* The nv_soc_hwpm_device handle contains the index in g_devices_int array. */
nv_soc_hwpm_device_int* to_device_int(uint64_t handle) {
const uint64_t dev_idx = to_dev_idx(handle);
if (dev_idx >= g_device_count) {
log_error("Invalid device handle: %llu\n", handle);
return NULL;
}
return &g_devices_int[dev_idx];
}
/**
* The nv_soc_hwpm_session handle contains the index in
* nv_soc_hwpm_device_int::session array.
*/
nv_soc_hwpm_session_int* to_session_int(uint64_t handle) {
nv_soc_hwpm_device_int* dev_int = to_device_int(handle);
if (dev_int == NULL) {
log_error("Invalid device session: %llu\n", handle);
return NULL;
}
const uint32_t session_idx = to_session_idx(handle);
if (session_idx >= dev_int->session_count_max) {
log_error("Invalid session: %llu\n", handle);
return NULL;
}
return dev_int->session_int[session_idx];
}
int nv_soc_hwpm_init(void) {
int ret = 0;
if (g_device_count == 0) {
ret = nv_soc_hwpm_os_enumerate_device(
MAX_DEVICE_COUNT, &g_device_count, g_devices_int);
}
return ret;
}
void nv_soc_hwpm_exit(void) {
uint32_t i, s;
for (i = 0; i < g_device_count; ++i) {
for (s = 0; s < g_devices_int[i].session_count_max; ++s) {
if (g_devices_int[i].session_int[s]) {
nv_soc_hwpm_session session =
{.handle = to_session_handle(i, s)};
nv_soc_hwpm_session_free(session);
}
}
}
g_device_count = 0;
memset(&g_devices_int, 0, sizeof(g_devices_int));
}
int nv_soc_hwpm_system_get_info(
nv_soc_hwpm_system_attribute attribute, uint32_t info_size, void* info)
{
if (info == NULL) {
log_error("Invalid info param\n");
return -EINVAL;
}
switch (attribute) {
case NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MAJOR:
if (info_size < sizeof(uint16_t)) {
log_error("Invalid info size. Expected %d, got %d\n",
sizeof(uint16_t), info_size);
return -EINVAL;
}
*((uint16_t*)info) = LIB_VERSION_MAJOR;
break;
case NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MINOR:
if (info_size < sizeof(uint16_t)) {
log_error("Invalid info size. Expected %d, got %d\n",
sizeof(uint16_t), info_size);
return -EINVAL;
}
*((uint16_t*)info) = LIB_VERSION_MINOR;
break;
default:
return -EINVAL;
}
return 0;
}
int nv_soc_hwpm_get_devices(uint32_t *count, nv_soc_hwpm_device *devices)
{
uint32_t i, dev_count;
if (count == NULL) {
log_error("Invalid count param\n");
return -EINVAL;
}
if (*count == 0) {
*count = g_device_count;
return 0;
}
if (devices == NULL) {
log_error("Invalid devices param\n");
return -EINVAL;
}
dev_count = (*count < g_device_count) ? *count : g_device_count;
for (i = 0; i < dev_count; ++i) {
devices[i].handle = to_dev_handle(i);
}
return 0;
}
int nv_soc_hwpm_device_get_info(
nv_soc_hwpm_device device, nv_soc_hwpm_device_attribute attribute,
uint32_t info_size, void* info)
{
uint32_t size_expected;
nv_soc_hwpm_device_int* dev_int = to_device_int(device.handle);
if (dev_int == NULL) {
log_error("Invalid device handle: %llu\n", device.handle);
return -EINVAL;
}
if (info == NULL) {
log_error("Invalid info param\n");
return -EINVAL;
}
memset(info, 0, info_size);
switch (attribute) {
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_CHIP_ID:
size_expected = sizeof(tegra_soc_hwpm_chip_id);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((tegra_soc_hwpm_chip_id*)info) =
dev_int->dev_info.soc_chip_id;
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_REVISION:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->dev_info.soc_revision;
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_PLATFORM:
size_expected = sizeof(tegra_soc_hwpm_platform);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((tegra_soc_hwpm_platform*)info) =
dev_int->dev_info.soc_platform;
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_SOCKET:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->dev_info.soc_socket;
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->dev_info.ip_available_count;
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_LIST:
size_expected = sizeof(nv_soc_hwpm_ip) * dev_int->dev_info.ip_available_count;
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
memcpy(info, dev_int->dev_info.ip_available_list, size_expected);
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT:
if (info_size < sizeof(uint32_t)) {
log_error("Invalid info size. Expected %d, got %d\n",
sizeof(uint32_t), info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->dev_info.res_available_count;
break;
case NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST:
size_expected =
sizeof(nv_soc_hwpm_resource) * dev_int->dev_info.res_available_count;
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
memcpy(info, dev_int->dev_info.res_available_list, size_expected);
break;
default:
log_error("Invalid attribute: %d\n", attribute);
return -EINVAL;
}
return 0;
}
int nv_soc_hwpm_ip_get_info(
nv_soc_hwpm_device device, nv_soc_hwpm_ip ip,
nv_soc_hwpm_ip_attribute attribute, uint32_t info_size, void* info)
{
uint32_t size_expected;
nv_soc_hwpm_device_int* dev_int = to_device_int(device.handle);
if (dev_int == NULL) {
log_error("Invalid device handle: %llu\n", device.handle);
return -EINVAL;
}
if (info == NULL) {
log_error("Invalid info param\n");
return -EINVAL;
}
if (ip >= NV_SOC_HWPM_NUM_IPS) {
log_error("Invalid IP ID: %d\n", ip);
return -EINVAL;
}
memset(info, 0, info_size);
switch (attribute) {
case NV_SOC_HWPM_IP_ATTRIBUTE_IS_AVAILABLE:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->ip_info[ip].is_available;
break;
case NV_SOC_HWPM_IP_ATTRIBUTE_INST_MAX_COUNT:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->ip_info[ip].inst_max_count;
break;
case NV_SOC_HWPM_IP_ATTRIBUTE_FS_MASK:
/* Get the minimal byte size to fit the fs_mask. */
size_expected =
ALIGN_UP(dev_int->ip_info[ip].inst_max_count, 8) / 8;
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
memcpy(info, dev_int->ip_info[ip].fs_mask, size_expected);
break;
default:
log_error("Invalid attribute: %d\n", attribute);
return -EINVAL;
}
return 0;
}
int nv_soc_hwpm_resource_get_info(
nv_soc_hwpm_device device, nv_soc_hwpm_resource resource,
nv_soc_hwpm_resource_attribute attribute, uint32_t info_size, void* info)
{
uint32_t size_expected;
nv_soc_hwpm_device_int* dev_int = to_device_int(device.handle);
if (dev_int == NULL) {
log_error("Invalid device handle: %llu\n", device.handle);
return -EINVAL;
}
if (info == NULL) {
log_error("Invalid info param\n");
return -EINVAL;
}
if (resource >= NV_SOC_HWPM_NUM_RESOURCES) {
log_error("Invalid resource ID: %d\n", resource);
return -EINVAL;
}
memset(info, 0, info_size);
switch (attribute) {
case NV_SOC_HWPM_RESOURCE_ATTRIBUTE_IS_AVAILABLE:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) =
dev_int->res_info[resource].is_available;
break;
default:
log_error("Invalid attribute: %d\n", attribute);
return -EINVAL;
}
return 0;
}
int nv_soc_hwpm_session_alloc(
nv_soc_hwpm_device device, nv_soc_hwpm_session *session)
{
uint32_t session_idx;
int fd, ret;
if (session == NULL) {
log_error("Invalid null session\n");
return -EINVAL;
}
nv_soc_hwpm_device_int* dev_int = to_device_int(device.handle);
if (dev_int == NULL) {
return -EINVAL;
}
/* Find empty session slot. */
for (session_idx = 0; session_idx < dev_int->session_count_max; ++session_idx) {
if (!dev_int->session_int[session_idx]) {
break;
}
}
if (session_idx >= dev_int->session_count_max) {
log_error("Maximum session count is reached, dev: %s\n",
dev_int->dev_path);
return -ENOMEM;
}
/* Open connection to driver. */
fd = nv_soc_hwpm_os_open_hwpm_device(dev_int->dev_path);
if (fd < 0) {
log_error("Failed opening HWPM device: %s, errno: %s\n",
dev_int->dev_path, strerror(errno));
ret = -errno;
return ret;
}
nv_soc_hwpm_session_int* session_int = malloc(sizeof(nv_soc_hwpm_session_int));
memset(session_int, 0, sizeof(nv_soc_hwpm_session_int));
session_int->fd = fd;
session_int->dev_int = (void*)dev_int;
session_int->info.device = device;
dev_int->session_int[session_idx] = session_int;
dev_int->session_count += 1;
session->handle = to_session_handle(to_dev_idx(device.handle), session_idx);
return 0;
}
static void update_resource_reservation_status(
nv_soc_hwpm_session_int* session_int, uint8_t is_reserved)
{
uint32_t i;
nv_soc_hwpm_device_int* dev_int = (void*)session_int->dev_int;
nv_soc_hwpm_resource_info *res_info = dev_int->res_info;
nv_soc_hwpm_session_info *session_info = &session_int->info;
for (i = 0; i < session_info->resource_count; ++i) {
const uint32_t res_id = session_info->resource_ids[i];
res_info[res_id].is_reserved = is_reserved;
}
}
int nv_soc_hwpm_session_free(
nv_soc_hwpm_session session)
{
nv_soc_hwpm_device_int* dev_int;
nv_soc_hwpm_session_int *session_int;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return 0;
}
/* Mark the resources as unreserved. */
update_resource_reservation_status(session_int, 0);
/* Release the buffers.*/
nv_soc_hwpm_os_session_free_pma(session_int);
/* Close driver connection. */
nv_soc_hwpm_os_close_hwpm_device(session_int->fd);
/* Cleanup session object. */
dev_int = (void*)session_int->dev_int;
const uint32_t session_idx = to_session_idx(session.handle);
free(session_int);
dev_int->session_int[session_idx] = NULL;
dev_int->session_count -= 1;
return 0;
}
int nv_soc_hwpm_session_get_info(
nv_soc_hwpm_session session, nv_soc_hwpm_session_attribute attribute,
uint32_t info_size, void* info)
{
uint32_t size_expected;
nv_soc_hwpm_session_int *session_int;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
log_error("Invalid session handle: %llu\n", session.handle);
return -EINVAL;
}
if (info == NULL) {
log_error("Invalid info param\n");
return -EINVAL;
}
memset(info, 0, info_size);
switch (attribute) {
case NV_SOC_HWPM_SESSION_ATTRIBUTE_DEVICE:
size_expected = sizeof(nv_soc_hwpm_device);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
memcpy(info, &session_int->info.device, size_expected);
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_SESSION_STARTED:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) = session_int->info.is_session_started;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_BUFFER_ALLOCATED:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) = session_int->info.is_pma_buffer_allocated;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE:
size_expected = sizeof(size_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((size_t*)info) = session_int->info.record_buffer.size;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA:
size_expected = sizeof(void*);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((void**)info) = session_int->info.record_buffer.buffer;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA:
size_expected = sizeof(uint64_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint64_t*)info) = session_int->info.record_buffer.pma_va;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_HANDLE:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) = session_int->info.record_buffer.handle;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_SIZE:
size_expected = sizeof(size_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((size_t*)info) = session_int->info.mem_bytes_buffer.size;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA:
size_expected = sizeof(void*);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((void**)info) = session_int->info.mem_bytes_buffer.buffer;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_PMA_VA:
size_expected = sizeof(uint64_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint64_t*)info) = session_int->info.mem_bytes_buffer.pma_va;
break;
case NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_HANDLE:
size_expected = sizeof(uint32_t);
if (info_size < size_expected) {
log_error("Invalid info size. Expected %d, got %d\n",
size_expected, info_size);
return -EINVAL;
}
*((uint32_t*)info) = session_int->info.mem_bytes_buffer.handle;
break;
default:
log_error("Invalid attribute: %d\n", attribute);
return -EINVAL;
}
return 0;
}
int nv_soc_hwpm_session_reserve_resources(
nv_soc_hwpm_session session, uint32_t res_count,
const nv_soc_hwpm_resource *res_ids)
{
int ret;
uint32_t i;
nv_soc_hwpm_device_int* dev_int;
nv_soc_hwpm_resource_info* res_info;
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
if (res_count == 0 ||
res_count > NV_SOC_HWPM_NUM_RESOURCES ||
res_ids == NULL) {
log_error("Invalid resource count: %u or resource id array: %p\n",
res_count, res_ids);
return -EINVAL;
}
/* Check if the session is already started. */
session_info = &session_int->info;
if (session_info->is_session_started) {
log_error("Unable to reserve resources. Session is already started\n");
return -EINVAL;
}
dev_int = (void*)session_int->dev_int;
res_info = dev_int->res_info;
/* Check the status of the requested resource(s) */
for (i = 0; i < res_count; ++i) {
const uint32_t res_id = res_ids[i];
if (res_id >= NV_SOC_HWPM_NUM_RESOURCES) {
log_error("Invalid resource id: %u on index: %u\n",
res_id, i);
return -EINVAL;
}
if (res_info[res_id].is_available != 1) {
log_error("Resource id: %u is not available\n", res_id);
return -EINVAL;
}
}
/* Make the request to the driver to reserve the resources. */
for (i = 0; i < res_count; ++i) {
const uint32_t res_id = res_ids[i];
ret = nv_soc_hwpm_os_session_reserve_resources(session_int, res_id);
if (ret) {
log_error("Failed to reserve resource id: %u\n", res_id);
return ret;
}
}
/* Update the session's resource list. */
memset(session_info->resource_ids, 0, sizeof(session_info->resource_ids));
memcpy(session_info->resource_ids, res_ids, res_count * sizeof(*session_info->resource_ids));
session_info->resource_count = res_count;
/* Update the reservation status of the resources. */
update_resource_reservation_status(session_int, 1);
session_info->is_resource_reserved = 1;
return 0;
}
int nv_soc_hwpm_session_reserve_all_resources(
nv_soc_hwpm_session session)
{
nv_soc_hwpm_device_int* dev_int;
const nv_soc_hwpm_device_info* dev_info;
dev_int = to_device_int(session.handle);
if (dev_int == NULL) {
return -EINVAL;
}
dev_info = &dev_int->dev_info;
return nv_soc_hwpm_session_reserve_resources(
session, dev_info->res_available_count, dev_info->res_available_list);
}
int nv_soc_hwpm_session_alloc_pma(
nv_soc_hwpm_session session,
const nv_soc_hwpm_pma_buffer_params *record_buffer_params)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
if (record_buffer_params == NULL) {
log_error("Invalid record buffer param: %p\n", record_buffer_params);
return -EINVAL;
}
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (session_info->is_session_started) {
log_error("Session is already started\n");
return -EINVAL;
}
if (session_info->is_pma_buffer_allocated == 1) {
log_error("PMA buffers are already allocated\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_alloc_pma(session_int, record_buffer_params);
}
int nv_soc_hwpm_session_set_get_pma_state(
nv_soc_hwpm_session session, nv_soc_hwpm_pma_channel_state_params* param)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (!session_info->is_session_started) {
log_error("Session is not started\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_set_get_pma_state(session_int, param);
}
int nv_soc_hwpm_session_get_hs_credits(
nv_soc_hwpm_session session,
tegra_soc_hwpm_get_type type,
uint32_t* num_hs_credits)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (!session_info->is_session_started) {
log_error("Session is not started\n");
return -EINVAL;
}
if (num_hs_credits == NULL) {
log_error("Invalid null num_hs_credits\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_get_hs_credits(
session_int, type, num_hs_credits);
}
int nv_soc_hwpm_session_config_hs_credits(
nv_soc_hwpm_session session,
uint32_t param_count,
const nv_soc_hwpm_config_hs_credit_params* params)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (!session_info->is_session_started) {
log_error("Session is not started\n");
return -EINVAL;
}
if (param_count == 0) {
log_error("Invalid 0 param_count\n");
return -EINVAL;
}
if (params == NULL) {
log_error("Invalid null params\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_config_hs_credits(
session_int, param_count, params);
}
int nv_soc_hwpm_session_start(nv_soc_hwpm_session session)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (session_info->is_session_started) {
log_error("Session is already started\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_start(session_int);
}
int nv_soc_hwpm_session_regops(
nv_soc_hwpm_session session,
const size_t param_count,
nv_soc_hwpm_reg_ops_params* params,
nv_soc_hwpm_reg_ops_validation_mode mode,
int* all_reg_ops_passed)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
if (param_count == 0) {
log_error("Invalid 0 param_count\n");
return -EINVAL;
}
if (params == NULL) {
log_error("Invalid null params\n");
return -EINVAL;
}
if (mode >= NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_COUNT) {
log_error("Invalid mode: %u\n", mode);
return -EINVAL;
}
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (!session_info->is_session_started) {
log_error("Session is not started\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_regops(
session_int, param_count, params, mode, all_reg_ops_passed);
}
int nv_soc_hwpm_session_setup_trigger(
nv_soc_hwpm_session session,
int enable_cross_trigger,
nv_soc_hwpm_trigger_session_type session_type)
{
nv_soc_hwpm_session_int *session_int;
nv_soc_hwpm_session_info *session_info;
if (session_type >= NV_SOC_HWPM_TRIGGER_SESSION_TYPE_COUNT) {
log_error("Invalid trigger session type: %u\n", session_type);
return -EINVAL;
}
session_int = to_session_int(session.handle);
if (session_int == NULL) {
return -EINVAL;
}
session_info = &session_int->info;
if (!session_info->is_session_started) {
log_error("Session is not started\n");
return -EINVAL;
}
return nv_soc_hwpm_os_session_setup_trigger(
session_int, enable_cross_trigger, session_type);
}
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
nv_soc_hwpm_init
nv_soc_hwpm_exit
nv_soc_hwpm_get_devices
nv_soc_hwpm_get_device_info
nv_soc_hwpm_get_ip_info
nv_soc_hwpm_get_ip_info_list
nv_soc_hwpm_get_resource_info
nv_soc_hwpm_get_resource_info_list
nv_soc_hwpm_session_alloc
nv_soc_hwpm_session_free
nv_soc_hwpm_session_get_info
nv_soc_hwpm_session_set_resources
nv_soc_hwpm_session_set_all_resources
nv_soc_hwpm_session_reserve_resources
nv_soc_hwpm_session_alloc_pma
nv_soc_hwpm_session_set_get_pma_state
nv_soc_hwpm_session_get_hs_credits
nv_soc_hwpm_session_config_hs_credits
nv_soc_hwpm_session_start
nv_soc_hwpm_session_regops
nv_soc_hwpm_session_setup_trigger

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
LIBNVSOCHWPM {
global:
nv_soc_hwpm_init;
nv_soc_hwpm_exit;
nv_soc_hwpm_system_get_info;
nv_soc_hwpm_get_devices;
nv_soc_hwpm_device_get_info;
nv_soc_hwpm_ip_get_info;
nv_soc_hwpm_resource_get_info;
nv_soc_hwpm_session_alloc;
nv_soc_hwpm_session_free;
nv_soc_hwpm_session_get_info;
nv_soc_hwpm_session_reserve_resources;
nv_soc_hwpm_session_reserve_all_resources;
nv_soc_hwpm_session_alloc_pma;
nv_soc_hwpm_session_set_get_pma_state;
nv_soc_hwpm_session_get_hs_credits;
nv_soc_hwpm_session_config_hs_credits;
nv_soc_hwpm_session_start;
nv_soc_hwpm_session_regops;
nv_soc_hwpm_session_setup_trigger;
local:
*;
};

View File

@@ -0,0 +1,836 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#if !defined (__QNX__)
#include <linux/dma-heap.h>
#include <linux/dma-buf.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "uapi/linux/tegra-soc-hwpm-uapi.h"
#include "os/nv_soc_hwpm_os.h"
#include "common/log.h"
#ifdef __cplusplus
extern "C" {
#endif
static int get_device_info(int fd, nv_soc_hwpm_device_info* dev_info)
{
struct tegra_soc_hwpm_device_info params;
int ret;
ret = ioctl(fd, TEGRA_CTRL_CMD_SOC_HWPM_DEVICE_INFO, &params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_DEVICE_INFO, ret: %d, errno: %s\n",
ret,
strerror(errno));
ret = -EIO;
return ret;
}
dev_info->soc_chip_id = (params.chip << 4) | params.chip_revision;
dev_info->soc_revision = params.revision;
dev_info->soc_platform = params.platform;
dev_info->soc_socket = 0; /* TODO: only single socket support for now. */
return ret;
}
static int get_ip_floorsweep_info(
int fd, nv_soc_hwpm_ip_info* ip_info, uint32_t* ip_available_count,
nv_soc_hwpm_ip* ip_available_list)
{
uint32_t ip, available_count, query_count, q;
int ret = 0;
/* Query all IPs. */
ip = 0;
available_count = 0;
while (ip < TERGA_SOC_HWPM_NUM_IPS) {
struct tegra_soc_hwpm_ip_floorsweep_info param = {};
query_count = MIN((TERGA_SOC_HWPM_NUM_IPS - ip),
TEGRA_SOC_HWPM_IP_QUERIES_MAX);
param.num_queries = query_count;
for (q = 0; q < query_count; ++q) {
param.ip_fsinfo[q].ip = ip + q;
param.ip_fsinfo[q].ip_inst_mask = 0;
}
ret = ioctl(fd, TEGRA_CTRL_CMD_SOC_HWPM_IP_FLOORSWEEP_INFO,
&param);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_IP_FLOORSWEEP_INFO, ip: %d, ret: %d, errno: %s\n",
ip, ret, strerror(errno));
ret = -EIO;
return ret;
}
for (q = 0; q < query_count; ++q, ++ip) {
ip_info[ip].is_available =
(param.ip_fsinfo[q].status ==
TEGRA_SOC_HWPM_IP_STATUS_VALID) ? 1 : 0;
*((uint64_t*)ip_info[ip].fs_mask) = param.ip_fsinfo[q].ip_inst_mask;
if (ip_info[ip].is_available)
ip_available_list[available_count++] = ip;
}
}
*ip_available_count = available_count;
return ret;
}
/* TODO: get the actual IP instance/element max count. */
static uint32_t get_ip_max_instances(
tegra_soc_hwpm_chip_id chip_id, nv_soc_hwpm_ip ip)
{
switch (chip_id) {
case TEGRA_SOC_HWPM_CHIP_ID_T241:
switch (ip) {
case NV_SOC_HWPM_IP_MSS_CHANNEL:
return 64;
case NV_SOC_HWPM_IP_PCIE_XALRC:
case NV_SOC_HWPM_IP_PCIE_XTLRC:
case NV_SOC_HWPM_IP_PCIE_XTLQ:
return 4;
default:
break;
}
break;
case TEGRA_SOC_HWPM_CHIP_ID_T410:
break;
default:
break;
}
return 0;
}
static int get_resource_info(
int fd, nv_soc_hwpm_resource_info* res_info, uint32_t* res_available_count,
nv_soc_hwpm_resource* res_available_list)
{
uint32_t res, available_count, query_count, q;
int ret = 0;
/* Query all resources. */
res = 0;
available_count = 0;
while (res < TERGA_SOC_HWPM_NUM_RESOURCES) {
struct tegra_soc_hwpm_resource_info param = {};
query_count = MIN((TERGA_SOC_HWPM_NUM_RESOURCES - res),
TEGRA_SOC_HWPM_RESOURCE_QUERIES_MAX);
param.num_queries = query_count;
for (q = 0; q < query_count; ++q) {
param.resource_info[q].resource = res + q;
param.resource_info[q].status = TEGRA_SOC_HWPM_RESOURCE_STATUS_INVALID;
}
ret = ioctl(fd, TEGRA_CTRL_CMD_SOC_HWPM_RESOURCE_INFO, &param);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_RESOURCE_INFO, res: %d, ret: %d, errno: %s\n",
res, ret, strerror(errno));
ret = -EIO;
return ret;
}
for (q = 0; q < query_count; ++q, ++res) {
res_info[res].is_available =
(param.resource_info[q].status ==
TEGRA_SOC_HWPM_RESOURCE_STATUS_VALID) ? 1 : 0;
if (res_info[res].is_available)
res_available_list[available_count++] = res;
}
}
*res_available_count = available_count;
return ret;
}
int nv_soc_hwpm_os_open_hwpm_device(const char* dev_path)
{
int fd;
fd = open(dev_path, O_RDWR);
return fd;
}
int nv_soc_hwpm_os_close_hwpm_device(int fd)
{
close(fd);
return 0;
}
static int enumerate_socket(uint32_t socket, nv_soc_hwpm_device_int* device_int)
{
int fd, ret = 0;
uint32_t i;
const char* hwpm_path;
hwpm_path = TEGRA_SOC_HWPM_DEV_NODE;
fd = nv_soc_hwpm_os_open_hwpm_device(hwpm_path);
if (fd < 0) {
log_error("Failed opening HWPM socket: %u, device: %s, errno: %s\n",
socket, hwpm_path, strerror(errno));
ret = -errno;
goto done;
}
nv_soc_hwpm_device_info* dev_info = &device_int->dev_info;
ret = get_device_info(fd, dev_info);
if (ret) {
log_error("Failed get_device_info, dev: %s\n", hwpm_path);
goto fail;
}
nv_soc_hwpm_ip_info* ip_info = device_int->ip_info;
ret = get_ip_floorsweep_info(fd, ip_info, &dev_info->ip_available_count,
dev_info->ip_available_list);
if (ret) {
log_error("Failed get_ip_floorsweep_info, dev: %s\n", hwpm_path);
goto fail;
}
for (i = 0; i < dev_info->ip_available_count; i++) {
nv_soc_hwpm_ip cur_ip = dev_info->ip_available_list[i];
ip_info[cur_ip].inst_max_count =
get_ip_max_instances(dev_info->soc_chip_id, cur_ip);
}
nv_soc_hwpm_resource_info* res_info = device_int->res_info;
ret = get_resource_info(fd, res_info, &dev_info->res_available_count,
dev_info->res_available_list);
if (ret) {
log_error("Failed get_resource_info, dev: %s\n", hwpm_path);
goto fail;
}
device_int->dev_path = hwpm_path;
device_int->session_count_max = 1; /* TODO: only single session now */
device_int->session_count = 0;
memset(device_int->session_int, 0, sizeof(device_int->session_int));
if (device_int->session_count_max > MAX_SESSION_COUNT) {
log_error("Session count max: %u is greater than MAX_SESSION_COUNT: %u\n",
device_int->session_count_max, MAX_SESSION_COUNT);
ret = -EINVAL;
goto fail;
}
fail:
nv_soc_hwpm_os_close_hwpm_device(fd);
done:
return ret;
}
/**
* @brief Enumerate NV_SOC_HWPM device in the system.
*
* @return 0 if successful.
*
*/
int nv_soc_hwpm_os_enumerate_device(
uint32_t max_count, uint32_t* actual_count,
nv_soc_hwpm_device_int* devices_int)
{
int ret = 0;
uint32_t socket;
memset(devices_int, 0, sizeof(nv_soc_hwpm_device_int) * max_count);
/**
* TODO: only single socket support for now.
*/
socket = 0;
ret = enumerate_socket(socket, &devices_int[socket]);
if (ret) {
log_error("Failed enumerate device on socket: %u, ret: %d\n", socket, ret);
goto fail;
}
*actual_count = 1;
goto done;
fail:
*actual_count = 0;
done:
return ret;
}
int nv_soc_hwpm_os_session_reserve_resources(
nv_soc_hwpm_session_int* session_int, nv_soc_hwpm_resource res_id)
{
int ret;
struct tegra_soc_hwpm_reserve_resource params = {};
params.resource = (enum tegra_soc_hwpm_resource)res_id;
ret = ioctl(session_int->fd,
TEGRA_CTRL_CMD_SOC_HWPM_RESERVE_RESOURCE,
&params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_RESERVE_RESOURCE res id: %u, ret: %d, "
"errno: %s\n",
params.resource,
ret,
strerror(errno));
return -EIO;
}
return 0;
}
static int unmap_dma_heap(int handle, void *va, size_t size)
{
int ret;
if (!handle || !va || !size) {
log_error("Invalid handle: %d, va: %p, size: %llu\n", handle, va, size);
return -EINVAL;
}
ret = munmap(va, size);
if (ret < 0) {
log_error("munmap failed : %s\n", strerror(errno));
return -EINVAL;
}
return 0;
}
static int alloc_dma_heap(
const char* heap_path,
size_t size,
tegra_soc_hwpm_coherency_type coherency,
uint32_t flags,
void* cpu_va_user,
void** cpu_va,
uint32_t* mem_handle,
uint32_t* mem_fd,
uint32_t* heap_fd)
{
int ret, fd;
/* Coherency flag not used in DMA heap implementation. */
(void)coherency;
(void)flags;
if (size == 0 || cpu_va == NULL || mem_handle == NULL || mem_fd == NULL || heap_fd == NULL) {
log_error("Invalid size: %llu, cpu_va: %p, mem_handle: %p, mem_fd: %p, heap_fd: %p\n",
size, cpu_va, mem_handle, mem_fd, heap_fd);
return -EINVAL;
}
*heap_fd = 0;
fd = open(heap_path, O_RDWR | O_SYNC | O_CLOEXEC);
if (fd < 0) {
log_error("open %s failed [%s]\n", heap_path, strerror(errno));
return -ENOMEM;
}
struct dma_heap_allocation_data data = {
.len = size,
.fd = 0,
.fd_flags = O_RDWR | O_CLOEXEC,
.heap_flags = 0,
};
ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
if (ret) {
log_error("dma heap Alloc failed [%s]\n", strerror(errno));
ret = -ENOMEM;
goto error;
}
*mem_handle = (uint32_t)data.fd;
*cpu_va = mmap(*cpu_va,
size,
(PROT_READ | PROT_WRITE),
MAP_SHARED,
data.fd,
0);
if (*cpu_va == MAP_FAILED) {
log_error("mmap failed : %s\n", strerror(errno));
*cpu_va = NULL;
ret = -ENOMEM;
goto error;
}
memset(*cpu_va, 0, size);
if ((cpu_va_user != NULL) && (*cpu_va != cpu_va_user)) {
ret = -EINVAL;
goto error_unmap;
}
/* Retrieve memHandle's FD */
*mem_fd = data.fd;
*heap_fd = fd;
return 0;
error_unmap:
unmap_dma_heap(data.fd, *cpu_va, size);
error:
close(fd);
return ret;
}
static int free_dma_heap(
uint64_t mem_handle, void *cpu_va, size_t size, uint32_t heap_fd)
{
if ((cpu_va != NULL) && (mem_handle != 0)) {
unmap_dma_heap((int)mem_handle, cpu_va, size);
}
if (mem_handle != 0) {
close((int)mem_handle);
}
if (heap_fd != 0) {
close(heap_fd);
}
return 0;
}
static int alloc_pma(
nv_soc_hwpm_session_int* session_int,
const char* heap_path,
const nv_soc_hwpm_pma_buffer_params *record_buffer_params)
{
int ret;
nv_soc_hwpm_session_info *session_info = &session_int->info;
/* Allocate PMA record buffer */
void* record_buffer = NULL;
uint32_t record_buffer_handle = 0;
uint32_t record_buffer_fd = 0;
uint32_t record_buffer_heap_fd = 0;
const size_t record_buffer_size = record_buffer_params->size;
void* record_buffer_cpu_va = record_buffer_params->cpu_va;
tegra_soc_hwpm_coherency_type record_buffer_coherency = record_buffer_params->coherency_type;
ret = alloc_dma_heap(
heap_path,
record_buffer_size,
record_buffer_coherency,
0, /* TODO: for other allocator may need to pass read/write property*/
record_buffer_cpu_va,
&record_buffer,
&record_buffer_handle,
&record_buffer_fd,
&record_buffer_heap_fd);
if (ret) {
log_error("Failed allocating PMA buffer\n");
goto error;
}
/* Allocate mem bytes buffer */
void* mem_bytes_buffer = NULL;
uint32_t mem_bytes_buffer_handle = 0;
uint32_t mem_bytes_buffer_fd = 0;
uint32_t mem_bytes_buffer_heap_fd = 0;
const size_t mem_bytes_buffer_size = 4096; // ignore size passed by caller
void* mem_bytes_buffer_cpu_va = NULL;
tegra_soc_hwpm_coherency_type mem_bytes_buffer_coherency =
record_buffer_coherency; // same with record buffer
ret = alloc_dma_heap(
heap_path,
mem_bytes_buffer_size,
mem_bytes_buffer_coherency,
0, /* TODO: for other allocator may need to pass read/write property*/
mem_bytes_buffer_cpu_va,
&mem_bytes_buffer,
&mem_bytes_buffer_handle,
&mem_bytes_buffer_fd,
&mem_bytes_buffer_heap_fd);
if (ret) {
log_error("Failed allocating mem bytes buffer\n");
goto error_free_record;
}
struct tegra_soc_hwpm_alloc_pma_stream params = {};
params.stream_buf_fd = record_buffer_fd;
params.mem_bytes_buf_fd = mem_bytes_buffer_fd;
params.stream_buf_size = record_buffer_size;
/* Register the buffers to the driver */
ret = ioctl(session_int->fd, TEGRA_CTRL_CMD_SOC_HWPM_ALLOC_PMA_STREAM, &params);
if (ret) {
log_error("Failed TEGRA_CTRL_CMD_SOC_HWPM_ALLOC_PMA_STREAM with errno=%s.\n",
strerror(errno));
ret = -EIO;
goto error_free_mem_bytes;
}
session_info->record_buffer.buffer = (uint8_t*)record_buffer;
session_info->record_buffer.size = record_buffer_size;
session_info->record_buffer.pma_va = params.stream_buf_pma_va;
session_info->record_buffer.handle = record_buffer_handle;
session_info->record_buffer.heap_fd = record_buffer_heap_fd;
session_info->mem_bytes_buffer.buffer = (uint8_t*)mem_bytes_buffer;
session_info->mem_bytes_buffer.size = mem_bytes_buffer_size;
session_info->mem_bytes_buffer.pma_va = 0; /* TODO: currently, kernel doesn't provide this. */
session_info->mem_bytes_buffer.handle = mem_bytes_buffer_handle;
session_info->mem_bytes_buffer.heap_fd = mem_bytes_buffer_heap_fd;
session_info->is_pma_buffer_allocated = 1;
return 0;
error_free_mem_bytes:
free_dma_heap(
mem_bytes_buffer_handle, mem_bytes_buffer, mem_bytes_buffer_size,
mem_bytes_buffer_heap_fd);
error_free_record:
free_dma_heap(
record_buffer_handle, record_buffer, record_buffer_size,
record_buffer_heap_fd);
error:
return ret;
}
static int path_exists(const char* filename) {
struct stat buffer;
return (stat(filename, &buffer) == 0) ? 1 : 0;
}
int nv_soc_hwpm_os_session_alloc_pma(
nv_soc_hwpm_session_int* session_int,
const nv_soc_hwpm_pma_buffer_params *record_buffer_params)
{
static const uint32_t kHeapPathCount = 3;
static const char* kHeapPath[] = {
"/dev/dma_heap/cma",
"/dev/dma_heap/reserved",
"/dev/dma_heap/system",
};
int ret;
for (uint32_t i = 0; i < kHeapPathCount; i++) {
log_debug("Trying to allocate PMA buffer from %s\n", kHeapPath[i]);
if (path_exists(kHeapPath[i]) == 0) {
log_debug("Heap %s does not exist\n", kHeapPath[i]);
continue;
}
ret = alloc_pma(session_int, kHeapPath[i], record_buffer_params);
if (!ret)
return ret;
}
return ret;
}
void nv_soc_hwpm_os_session_free_pma(nv_soc_hwpm_session_int* session_int)
{
nv_soc_hwpm_session_info *session_info = &session_int->info;
/* Cleanup PMA buffer. */
if (session_info->record_buffer.handle != 0) {
free_dma_heap(
session_info->record_buffer.handle,
session_info->record_buffer.buffer,
session_info->record_buffer.size,
session_info->record_buffer.heap_fd);
}
if (session_info->mem_bytes_buffer.handle != 0) {
free_dma_heap(
session_info->mem_bytes_buffer.handle,
session_info->mem_bytes_buffer.buffer,
session_info->mem_bytes_buffer.size,
session_info->mem_bytes_buffer.heap_fd);
}
}
int nv_soc_hwpm_os_session_set_get_pma_state(
nv_soc_hwpm_session_int *session_int,
nv_soc_hwpm_pma_channel_state_params* params)
{
int ret;
struct tegra_soc_hwpm_update_get_put io_params = {};
io_params.mem_bump = params->in_mem_bump;
if (params->in_check_overflow)
{
io_params.b_check_overflow = 1;
}
if (params->in_read_mem_head)
{
io_params.b_read_mem_head = true;
}
io_params.b_stream_mem_bytes = (params->in_stream_mem_bytes != 0);
ret = ioctl(session_int->fd,
TEGRA_CTRL_CMD_SOC_HWPM_UPDATE_GET_PUT,
&io_params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_UPDATE_GET_PUT, ret: %d, errno: %s\n",
ret, strerror(errno));
return -EIO;
}
if (params->in_check_overflow)
{
params->out_overflowed = !!io_params.b_overflowed;
}
if (params->in_read_mem_head)
{
params->out_mem_head = io_params.mem_head;
}
return 0;
}
int nv_soc_hwpm_os_session_get_hs_credits(
nv_soc_hwpm_session_int* session_int,
tegra_soc_hwpm_get_type type,
uint32_t* num_hs_credits)
{
int ret;
uint16_t credit_cmd;
switch (type) {
case TEGRA_SOC_HWPM_GET_TYPE_HS_CREDITS:
credit_cmd = TEGRA_SOC_HWPM_CMD_GET_HS_CREDITS;
break;
case TEGRA_SOC_HWPM_GET_TYPE_TOTAL_HS_CREDITS:
credit_cmd = TEGRA_SOC_HWPM_CMD_GET_TOTAL_HS_CREDITS;
break;
default:
log_error("Invalid credit type: %u\n", type);
return -EINVAL;
}
struct tegra_soc_hwpm_exec_credit_program params = { 0 };
params.num_entries = 1;
params.credit_cmd = credit_cmd;
params.credit_info[0].num_credits = ~0;
ret = ioctl(session_int->fd,
TEGRA_CTRL_CMD_SOC_HWPM_CREDIT_PROGRAM,
&params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_CREDIT_PROGRAM, ret: %d, errno: %s\n",
ret, strerror(errno));
return -EIO;
}
/* Total credits are available in first entry */
*num_hs_credits = params.credit_info[0].num_credits;
return 0;
}
int nv_soc_hwpm_os_session_config_hs_credits(
nv_soc_hwpm_session_int* session_int,
uint32_t param_count,
const nv_soc_hwpm_config_hs_credit_params* params)
{
int ret;
uint32_t i;
i = 0;
while (i < param_count)
{
/* TODO set params.pmaChannelIdx for multi-channel support (currently 0) */
uint32_t b, batch_count;
struct tegra_soc_hwpm_exec_credit_program io_params = { 0 };
batch_count = MIN((param_count - i),
TEGRA_SOC_HWPM_MAX_CREDIT_INFO_ENTRIES);
io_params.credit_cmd = TEGRA_SOC_HWPM_CMD_SET_HS_CREDITS;
io_params.num_entries = batch_count;
for (b = 0; b < batch_count; ++b) {
const uint32_t param_idx = i + b;;
io_params.credit_info[b].cblock_idx = params[param_idx].cblock_idx;
io_params.credit_info[b].num_credits = params[param_idx].num_credits_per_chiplet;
}
ret = ioctl(session_int->fd,
TEGRA_CTRL_CMD_SOC_HWPM_CREDIT_PROGRAM,
&io_params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_CREDIT_PROGRAM, ret: %d, errno: %s\n",
ret, strerror(errno));
return -EIO;
}
/* TODO get info regarding failed credit programming from params.statusInfo (currently unused) */
i += batch_count;
}
return 0;
}
int nv_soc_hwpm_os_session_start(nv_soc_hwpm_session_int* session_int)
{
int ret;
nv_soc_hwpm_session_info *session_info;
ret = ioctl(session_int->fd, TEGRA_CTRL_CMD_BIND);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_BIND, ret: %d, errno: %s\n",
ret, strerror(errno));
return -EIO;
}
session_info = &session_int->info;
session_info->is_session_started = 1;
return 0;
}
int nv_soc_hwpm_os_session_regops(
nv_soc_hwpm_session_int* session_int,
const size_t param_count,
nv_soc_hwpm_reg_ops_params* params,
nv_soc_hwpm_reg_ops_validation_mode mode,
int* all_reg_ops_passed)
{
int ret, all_passed;
uint32_t i;
all_passed = 1;
i = 0;
while (i < param_count)
{
/* TODO set params.pmaChannelIdx for multi-channel support (currently 0) */
uint32_t b, batch_count;
struct tegra_soc_hwpm_exec_reg_ops io_params = { 0 };
batch_count = MIN((param_count - i),
TEGRA_SOC_HWPM_REG_OPS_SIZE);
io_params.mode = (uint8_t)mode;
io_params.op_count = batch_count;
for (b = 0; b < batch_count; ++b) {
const uint32_t param_idx = i + b;;
io_params.ops[b].phys_addr = params[param_idx].in_offset;
io_params.ops[b].reg_val_lo = VAL_LO64(params[param_idx].in_out_val64);
io_params.ops[b].reg_val_hi = VAL_HI64(params[param_idx].in_out_val64);
io_params.ops[b].mask_lo = VAL_LO64(params[param_idx].in_mask64);
io_params.ops[b].mask_hi = VAL_HI64(params[param_idx].in_mask64);
io_params.ops[b].cmd = params[param_idx].in_cmd;
}
ret = ioctl(session_int->fd,
TEGRA_CTRL_CMD_SOC_HWPM_EXEC_REG_OPS,
&io_params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_EXEC_REG_OPS, ret: %d, errno: %s\n",
ret, strerror(errno));
return -EIO;
}
all_passed &= !!io_params.b_all_reg_ops_passed;
// Read back the result.
for (b = 0; b < batch_count; ++b) {
const uint32_t param_idx = i + b;
params[param_idx].in_out_val64 =
VAL_64(io_params.ops[b].reg_val_lo,
io_params.ops[b].reg_val_hi);
params[param_idx].out_status = io_params.ops[b].status;
}
i += batch_count;
}
if (all_reg_ops_passed != NULL)
*all_reg_ops_passed = all_passed;
return 0;
}
int nv_soc_hwpm_os_session_setup_trigger(
nv_soc_hwpm_session_int* session_int,
int enable_cross_trigger,
nv_soc_hwpm_trigger_session_type session_type)
{
int ret;
/**
* TODO:
* set params.cblockIdx and params.pmaChannelIdx for multi-channel
* support (currently 0)
*/
struct tegra_soc_hwpm_setup_trigger params = { 0 };
params.enable_cross_trigger = (uint8_t)enable_cross_trigger;
params.session_type = (uint8_t)session_type;
ret = ioctl(session_int->fd,
TEGRA_CTRL_CMD_SOC_HWPM_SETUP_TRIGGER,
&params);
if (ret) {
log_error(
"Failed TEGRA_CTRL_CMD_SOC_HWPM_SETUP_TRIGGER, ret: %d, errno: %s\n",
ret, strerror(errno));
return -EIO;
}
return 0;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* !__QNX__ */

View File

@@ -0,0 +1,75 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __NV_SOC_HWPM_OS__
#define __NV_SOC_HWPM_OS__
#include "nv_soc_hwpm.h"
#include "common/types.h"
#ifdef __cplusplus
extern "C" {
#endif
int nv_soc_hwpm_os_open_hwpm_device(const char* dev_path);
int nv_soc_hwpm_os_close_hwpm_device(int fd);
int nv_soc_hwpm_os_enumerate_device(
uint32_t max_count, uint32_t* actual_count,
nv_soc_hwpm_device_int* devices_int);
int nv_soc_hwpm_os_session_reserve_resources(
nv_soc_hwpm_session_int* session_int, nv_soc_hwpm_resource res_id);
int nv_soc_hwpm_os_session_alloc_pma(
nv_soc_hwpm_session_int* session_int,
const nv_soc_hwpm_pma_buffer_params *record_buffer_params);
void nv_soc_hwpm_os_session_free_pma(
nv_soc_hwpm_session_int* session_int);
int nv_soc_hwpm_os_session_set_get_pma_state(
nv_soc_hwpm_session_int* session_int,
nv_soc_hwpm_pma_channel_state_params* param);
int nv_soc_hwpm_os_session_get_hs_credits(
nv_soc_hwpm_session_int* session_int,
tegra_soc_hwpm_get_type type,
uint32_t* num_hs_credits);
int nv_soc_hwpm_os_session_config_hs_credits(
nv_soc_hwpm_session_int* session_int,
uint32_t param_count,
const nv_soc_hwpm_config_hs_credit_params* params);
int nv_soc_hwpm_os_session_start(nv_soc_hwpm_session_int* session_int);
int nv_soc_hwpm_os_session_regops(
nv_soc_hwpm_session_int* session_int,
const size_t param_count,
nv_soc_hwpm_reg_ops_params* params,
nv_soc_hwpm_reg_ops_validation_mode mode,
int* all_reg_ops_passed);
int nv_soc_hwpm_os_session_setup_trigger(
nv_soc_hwpm_session_int* session_int,
int enable_cross_trigger,
nv_soc_hwpm_trigger_session_type session_type);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*__NV_SOC_HWPM_OS__*/

View File

@@ -0,0 +1,72 @@
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
################################################################################
# Borrow the make environment from Linux kernel to support cross compilation.
ifneq ($(KERNEL_SOURCE),)
export srctree := $(KERNEL_SOURCE)
include $(srctree)/tools/scripts/Makefile.include
include $(srctree)/tools/scripts/Makefile.arch
else
CXX = g++
endif
SOURCES = \
nv_soc_hwpm_test.cpp \
t241_test.cpp \
soc_mode_e_buffer.cpp
OBJECTS=$(foreach x, $(basename $(SOURCES)), $(x).o)
CXXFLAGS = -Wall -Wextra -Werror -std=c++14 -g
CXXFLAGS += -I. -I../include -I./include -I../
ifneq ($(NV_SOURCE),)
# Need to be built using this command:
# ARCH=arm64 CROSS_COMPILE=$P4_GCC/bin/aarch64-linux-gnu- NV_SOURCE=$TEGRA_TOP KERNEL_SOURCE=$TEGRA_TOP/kernel/kernel-pset make
# Example:
# ARCH=arm64 \
# CROSS_COMPILE=$P4ROOT/sw/mobile/tools/linux/linaro//gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- \
# NV_SOURCE=~/repo/platform-enablement-dev-main-20250212 \
# KERNEL_SOURCE=~/repo/platform-enablement-dev-main-20250212/kernel/kernel-pset make
CXXFLAGS += -I$(NV_SOURCE)/3rdparty/google/googletest/googletest/include
CXXFLAGS += -I$(NV_SOURCE)/3rdparty/google/googletest/googletest/
CXXFLAGS += -I$(NV_SOURCE)/hwinc-private/th500/66838280
GTEST_SOURCES += \
$(NV_SOURCE)/3rdparty/google/googletest/googletest/src/gtest-all.cc \
$(NV_SOURCE)/3rdparty/google/googletest/googletest/src/gtest_main.cc
GTEST_OBJECTS=$(foreach x, $(basename $(GTEST_SOURCES)), $(x).o)
else
# Need to install libgtest-dev
LDFLAGS += -lgtest -lgtest_main
endif
LDFLAGS += -lpthread -ldl
all: nv_soc_hwpm_test
nv_soc_hwpm_test: $(GTEST_OBJECTS) $(OBJECTS)
$(CXX) $(OBJECTS) $(GTEST_OBJECTS) $(LDFLAGS) -o $@
rm -f $(OBJECTS) $(GTEST_OBJECTS)
$(OBJECTS): %.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
$(GTEST_OBJECTS): %.o: %.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJECTS) $(GTEST_OBJECTS)
rm -f nv_soc_hwpm_test

View File

@@ -0,0 +1,423 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef HWPM_RECORD_FORMAT_H
#define HWPM_RECORD_FORMAT_H
#include <assert.h>
#include <stdint.h>
// Copied from /sw/devtools/Agora/Dev/Perfkit/Shared/Perfkit/Modules/Target/Counters/Hwpm/Inc/Perfkit/Counters/Hwpm/TargetMicroPassHwpm.h
// =============================================================================
// Mode E Record
// =============================================================================
// Basic mode E record in the native HW layout
struct ModeERecordRaw
{
union
{
uint64_t timestamp;
struct
{
uint32_t timestamp_lo;
uint8_t timestamp_hi;
uint8_t perfmon_id;
uint16_t smplcnt_ds_sz;
};
};
// counter results
union
{
uint32_t counter[4];
struct
{
uint32_t event;
uint32_t trig0;
uint32_t trig1;
uint32_t sampl;
};
};
uint32_t zero2;
uint32_t zero3;
};
/* Imported from \\hw\nvgpu_gvlit1\ip\perf\hwpm\2.0\defs\public\registers\pri_perf_pmm.ref with below corrections
1) timestamp[40:32] => timestamp[39:32]
15 8 7 0
.--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----.
0x00 | timestamp[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x02 | timestamp[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x04 | PERFMONID[7:0] | timestamp[39:32] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x06 |0 |0 |SZ|DS|PMID[10:8]| SMPLCNT[8:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x08 | count0[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0A | count0[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0C | count1[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0E | count1[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x10 | count2[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x12 | count2[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x14 | count3[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x16 | count3[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x18 | pm local trigger B count | <-- or TOTAL_TRIG_RCV[15:0] if PMLOCALTRIGB_EN_DISABLE
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1A | bookmark B | <-- or TOTAL_TRIG_RCV[31:0] if PMLOCALTRIGB_EN_DISABLE
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1C | pm local trigger A count |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1E | bookmark A |
`--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----'
*/
struct ModeERecordVolta : ModeERecordRaw
{
static const uint64_t TimestampMask = 0x000000ffffffffffull; // lower 40 bits
uint64_t GetTimestamp() const
{
const uint64_t timestampMasked = timestamp & TimestampMask;
return timestampMasked;
}
uint16_t GetSampleCount() const
{
uint16_t sampleCount = smplcnt_ds_sz & 0x01FF;
return sampleCount;
}
uint32_t GetPerfmonId() const
{
uint32_t perfmonId_lsb = perfmon_id;
uint32_t perfmonId_msb = (smplcnt_ds_sz & 0xe00) >> 1;
uint32_t perfmonId = perfmonId_msb | perfmonId_lsb;
return perfmonId;
}
uint32_t GetTriggerCount() const
{
return zero2;
}
bool IsDelayedSampled() const
{
uint8_t ds = (smplcnt_ds_sz >> 12) & 0x1;
return !!ds;
}
bool Is32B() const
{
uint8_t sz = (smplcnt_ds_sz >> 13) & 0x1;
return !sz;
}
};
// =============================================================================
// Mode C Record
// =============================================================================
// Basic Mode C record in the native HW layout
struct ModeCRecordRaw
{
uint16_t timestamp_15_0;
union
{
uint16_t timestamp_31_16;
struct
{
uint8_t timestamp_23_16;
uint8_t total_trig_rcv_7_0;
};
};
union
{
uint8_t timestamp_39_32;
uint8_t total_trig_rcv_15_8;
};
uint8_t perfmon_id;
uint16_t smplcnt_ds_sz_or_pmid;
union
{
uint16_t counter[12];
struct
{
uint16_t counter_2B[4];
uint32_t counter_4B[4];
};
};
};
/* https://p4viewer.nvidia.com/get//hw/nvgpu/ip/perf/hwpm/4.1/defs/public/registers/pri_perf_pmm.ref
_MODEC_4X16_4X32_DISABLE _MODEC_4X16_4X32_RECONFIGURE
15 8 7 0 15 8 7 0
.--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----. .--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----.
0x00 | timestamp[15:0] | | timestamp[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x02 | timestamp[31:16] | | timestamp[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x02 | TOTAL_TRIG_RCV[7:0] | timestamp[23:16] | | TOTAL_TRIG_RCV[7:0] | timestamp[23:16] | <-- or TOTAL_TRIG_RCV[7:0] only if TRIGRCV_IN_MODEC is set
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x04 | PERFMONID[7:0] | timestamp[39:32] | | PERFMONID[7:0] | timestamp[39:32] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x04 | PERFMONID[7:0] | TOTAL_TRIG_RCV[15:8] | | PERFMONID[7:0] | TOTAL_TRIG_RCV[15:8] | <-- or TOTAL_TRIG_RCV[15:8] only if TRIGRCV_IN_MODEC is set
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x06 |0 |0 |0 |DS|PMID[10:8]| SMPLCNT[8:0] | |0 |0 |0 |DS|PMID[10:8]| SMPLCNT[8:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x08 | count0[15:0] | | count0[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0A | count1[15:0] | | count1[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0C | count2[15:0] | | count2[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0E | count3[15:0] | | count3[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x10 | count4[15:0] | | count4[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x12 | count5[15:0] | | count4[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x14 | count6[15:0] | | count6[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x16 | count7[15:0] | | count6[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x18 | count8[15:0] | | count8[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1A | count9[15:0] | | count8[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1C | count10[15:0] | | count10[15:0] | <-- or 'pm local trigger A count' if NV_PERF_PMM_CONTROLB_PMLOCALTRIGA_EN is enabled
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1E | count11[15:0] | | count10[31:16] | <-- or 'bookmark A' if NV_PERF_PMM_CONTROLB_PMLOCALTRIGA_EN is enabled
`--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----' `--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----'
*/
struct ModeCRecordVolta_12x16 : public ModeCRecordRaw
{
static const uint64_t TimestampMask = 0x000000ffffffffffull; // lower 40 bits(TRIGRCV_IN_MODEC_DISABLE)
uint64_t GetTimestamp() const
{
const uint64_t timestamp64 = *(uint64_t*)(this);
const uint64_t timestampMasked = timestamp64 & TimestampMask;
return timestampMasked;
}
uint16_t GetSampleCount() const
{
uint16_t sampleCount = smplcnt_ds_sz_or_pmid & 0x01FF;
return sampleCount;
}
uint32_t GetPerfmonId() const
{
const uint32_t perfmonId_lsb = perfmon_id;
const uint32_t perfmonId_msb = (smplcnt_ds_sz_or_pmid & 0xe00) >> 1;
const uint32_t perfmonId = perfmonId_msb | perfmonId_lsb;
return perfmonId;
}
bool IsDelayedSampled() const
{
uint8_t ds = (smplcnt_ds_sz_or_pmid >> 12) & 0x1;
return !!ds;
}
};
/*
//hw/nvgpu/ip/perf/hwpm/4.1/defs/public/registers/pri_perf_pma.ref
APPENDIX D - PMA RECORD FORMAT
PMA PERFMONID = 0x7FF Other Perfmons should not be programmed to use that ID.
15 8 7 0
.--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----.
0x00 | timestamp[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x02 | timestamp[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x04 | PERFMONID[7:0] | timestamp[40:32] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x06 |0 |0 |SZ|DR| PMID[10:8]| unused |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x08 | start[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0A | start[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0C | end[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x0E | end[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x10 | total[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x12 | total[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x14 | bookmark[15:0] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x16 | bookmark[31:16] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x18 | PTIMER[10:0] | 0 0 0 0 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1A | PTIMER[26:11] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1C | PTIMER[42:27] |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x1E | 0 0 0| PTIMER[55:43] |
`--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----'
PTIMER - 56 bits (32 bit granulariy) global timer on fixed frequency clock
incoming from the SOC in gray code format.
The DR bit stands for dropped record. The PMA can only process 1 packet at a time,
until it goes into the record buffer. All the triggers which come in during this period
will not have packets generated for each of them.
If the PMA gets multiple triggers in a short window, then it only sends the packet for the first trigger.
The trigger counts are updated to account for the missed triggers.
After it completes processing the first packet, it then sends a packet when a new trigger comes in.
To indicate this, the DR bit will be set to 1 in the packet that is sent next.
The DR bit will be seen as set when the difference in total trigger counts is more than 1
in the current and the previous packet.
*/
// copied from //sw/devtools/Agora/Dev/Perfkit/Shared/Perfkit/Modules/Target/Counters/Hwpm/Inc/Perfkit/Counters/Hwpm/TargetMicroPassHwpm.h
// PMA Record
enum { PMA_PerfmonId = 0x7FF }; // Other perfmons should not be programmed to use this ID.
struct PmaRecordRaw
{
uint32_t timestamp_lo;
uint8_t timestamp_hi;
uint8_t perfmon_id;
uint16_t sz_dr_pmid; // "dr" stands for dropped record
uint32_t start_trigger_count;
uint32_t stop_trigger_count;
uint32_t total_trigger_count;
uint32_t bookmark;
uint64_t ptimer;
};
// adapted from PmaRecordTuring in //sw/devtools/Agora/Dev/Perfkit/Shared/Perfkit/Modules/Target/Counters/Hwpm/Inc/Perfkit/Counters/Hwpm/TargetMicroPassHwpm.h
struct PmaRecordSoc : public PmaRecordRaw
{
uint32_t GetPerfmonId() const
{
uint32_t perfmonId_lsb = perfmon_id;
uint32_t perfmonId_msb = (sz_dr_pmid & 0xe00) >> 1; // there is an unused bit in position 8
uint32_t perfmonId = perfmonId_msb | perfmonId_lsb;
return perfmonId;
}
bool IsDroppedRecord() const
{
const uint8_t dr = (sz_dr_pmid >> 12) & 0x1;
return !!dr;
}
// Number of start triggers sent to perfmons
uint32_t GetStartTriggerCount() const
{
return start_trigger_count;
}
// Number of end/stop triggers sent to perfmons
uint32_t GetStopTriggerCount() const
{
return stop_trigger_count;
}
// The total number of triggers including PMA pulses sent to perfmons
uint32_t GetTotalTriggerCount() const
{
return total_trigger_count;
}
// The last value from the PmTrigger packet
uint32_t GetBookmark() const
{
return bookmark;
}
// TODO: is the PTIMER value in gray code?
// From empirical testing, it doesn't look that way.
uint64_t GetPtimer() const
{
uint64_t ptimer_val = (ptimer & 0x1fffffffffffffe0ull) >> 5;
#if 0
// http://morwenn.github.io/cpp-gray/converting-to-and-from-gray-code/
ptimer_val = ptimer_val ^ (ptimer_val >> 32);
ptimer_val = ptimer_val ^ (ptimer_val >> 16);
ptimer_val = ptimer_val ^ (ptimer_val >> 8);
ptimer_val = ptimer_val ^ (ptimer_val >> 4);
ptimer_val = ptimer_val ^ (ptimer_val >> 2);
ptimer_val = ptimer_val ^ (ptimer_val >> 1);
#endif
return ptimer_val;
}
};
// adapted from PmRecordVoltaCommonPrefix in //sw/devtools/Agora/Dev/Perfkit/Shared/Perfkit/Modules/Target/Counters/Hwpm/Inc/Perfkit/Counters/Hwpm/TargetMicroPassHwpm.h
struct PmRecordSocCommonPrefix
{
/*
15 8 7 0
.--+--+--+--+--+--+--+--+--+--+--+--+--+--+-----.
0x00 | data0 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x02 | data1 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x04 | PERFMONID[7:0] | data2 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
0x06 | data4 |PMID[10:8]| data3 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
uint16_t data0_2B;
uint16_t data1_2B;
uint8_t data2_1B;
uint8_t perfmon_id;
uint8_t data3_1B;
uint8_t data4_pmid;
uint32_t GetPerfmonId() const
{
const uint32_t perfmonId_lsb = perfmon_id;
const uint32_t perfmonId_msb = (data4_pmid & 0xe) << 7;
const uint32_t perfmonId = perfmonId_msb | perfmonId_lsb;
return perfmonId;
}
};
static_assert(sizeof(PmRecordSocCommonPrefix) == 8, "sizeof(PmRecordSocCommonPrefix) == 8B");
#endif

View File

@@ -0,0 +1,92 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "nv_soc_hwpm_test.h"
NvSocHwpmTests::NvSocHwpmTests() : testing::Test(), api_table()
{}
NvSocHwpmTests::~NvSocHwpmTests()
{}
// Called once before the tests start to run.
void NvSocHwpmTests::SetUpTestCase()
{
}
// Called after all tests are done.
void NvSocHwpmTests::TearDownTestCase()
{
}
// Called before each test is run.
void NvSocHwpmTests::SetUp()
{
LoadNvSocHwpm();
}
// Called after each test is run.
void NvSocHwpmTests::TearDown()
{
UnloadNvSocHwpm();
}
void NvSocHwpmTests::LoadNvSocHwpm()
{
static const char* kLibName = "libnvsochwpm.so";
ASSERT_EQ(0, load_nv_soc_hwpm(kLibName, &api_table));
}
void NvSocHwpmTests::UnloadNvSocHwpm()
{
ASSERT_EQ(0, unload_nv_soc_hwpm(&api_table));
}
TEST_F(NvSocHwpmTests, MultipleInitExit)
{
ASSERT_EQ(0, api_table.nv_soc_hwpm_init_fn());
ASSERT_EQ(0, api_table.nv_soc_hwpm_init_fn());
api_table.nv_soc_hwpm_exit_fn();
api_table.nv_soc_hwpm_exit_fn();
}
// Make sure we can get the lib major/minor version without calling init/exit
TEST_F(NvSocHwpmTests, EnumerateSystemInfo)
{
uint32_t major, minor;
nv_soc_hwpm_system_attribute attr = NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MAJOR;
ASSERT_EQ(0, api_table.nv_soc_hwpm_system_get_info_fn(attr, sizeof(major), &major));
attr = NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MINOR;
ASSERT_EQ(0, api_table.nv_soc_hwpm_system_get_info_fn(attr, sizeof(minor), &minor));
}
TEST_F(NvSocHwpmTests, EnumerateSystemInfoNegative)
{
uint32_t dummy, minor;
nv_soc_hwpm_system_attribute attr;
// Should fail with invalid attribute.
attr = (nv_soc_hwpm_system_attribute)0xffffffff;
ASSERT_NE(0, api_table.nv_soc_hwpm_system_get_info_fn(attr, sizeof(dummy), &dummy));
attr = NV_SOC_HWPM_SYSTEM_ATTRIBUTE_VERSION_MAJOR;
// Should fail with invalid buffer size.
ASSERT_NE(0, api_table.nv_soc_hwpm_system_get_info_fn(attr, sizeof(uint8_t), &minor));
// Should fail with invalid buffer ptr.
ASSERT_NE(0, api_table.nv_soc_hwpm_system_get_info_fn(attr, sizeof(minor), NULL));
}

View File

@@ -0,0 +1,41 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef NV_SOC_HWPM_NV_SOC_HWPM_TEST_H
#define NV_SOC_HWPM_NV_SOC_HWPM_TEST_H
#include <gtest/gtest.h>
#include "nv_soc_hwpm_loader.hpp"
class NvSocHwpmTests : public ::testing::Test
{
public:
NvSocHwpmTests();
~NvSocHwpmTests() override;
//global start and stop
static void SetUpTestCase();
static void TearDownTestCase();
protected:
//individual start and stop
void SetUp() override;
void TearDown() override;
void LoadNvSocHwpm();
void UnloadNvSocHwpm();
nv_soc_hwpm_api_table api_table;
};
#endif //NV_SOC_HWPM_NV_SOC_HWPM_TEST_H

View File

@@ -0,0 +1,935 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "soc_mode_e_buffer.h"
#include "hwpm_record_format.h"
#include <stdio.h>
#include <inttypes.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <deque>
SocModeEBuffer::SocModeEBuffer(nv_soc_hwpm_api_table& api_table, nv_soc_hwpm_session session)
: m_api_table(api_table)
, m_session(session)
, m_record_format(RecordFormatType::ModeE)
, m_num_valid_records(0)
, m_num_overflow_records(0)
, m_num_pma_records(0)
, m_num_samples(0)
, m_delayed_sample_detected(false)
, m_merged_samples_detected(false)
, m_sum_counter_values(0)
, m_zero_timestamp_detected(false)
, m_reversed_trigger_count_detected(false)
, m_local_trigger_bookmark_mismatch(false)
, m_first_pma_timestamp(0)
, m_last_pma_timestamp(0)
, m_first_sys0_timestamp(0)
, m_last_sys0_timestamp(0)
, m_pma_buffer(nullptr)
, m_membytes_buffer(nullptr)
, m_max_records(0)
, m_unread_head(0)
, m_pma_buffer_size(0)
, m_pma_buffer_cpu_va(nullptr)
, m_mem_bytes_buffer_cpu_va(nullptr)
{
}
SocModeEBuffer::~SocModeEBuffer()
{
}
bool SocModeEBuffer::Initialize()
{
nv_soc_hwpm_session_attribute session_attr;
session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE;
if (m_api_table.nv_soc_hwpm_session_get_info_fn(
m_session,
session_attr,
sizeof(m_pma_buffer_size),
&m_pma_buffer_size)) {
printf("ERROR: SOC HWPM session get info buffer size failed!\n");
return false;
}
session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA;
if (m_api_table.nv_soc_hwpm_session_get_info_fn(
m_session,
session_attr,
sizeof(m_pma_buffer_cpu_va),
&m_pma_buffer_cpu_va)) {
printf("ERROR: SOC HWPM session get info stream buffer ptr failed!\n");
return false;
}
session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA;
if (m_api_table.nv_soc_hwpm_session_get_info_fn(
m_session,
session_attr,
sizeof(m_mem_bytes_buffer_cpu_va),
&m_mem_bytes_buffer_cpu_va)) {
printf("ERROR: SOC HWPM session get info mem bytes buffer ptr failed!\n");
return false;
}
m_max_records = m_pma_buffer_size / sizeof(ModeERecordRaw);
return true;
}
void SocModeEBuffer::ResetParsedData()
{
m_num_valid_records = 0;
m_num_overflow_records = 0;
m_num_pma_records = 0;
m_num_samples = 0;
m_delayed_sample_detected = false;
m_merged_samples_detected = false;
m_sum_counter_values = 0;
m_num_local_triggers = 0;
m_zero_timestamp_detected = false;
m_reversed_trigger_count_detected = false;
m_local_trigger_bookmark_mismatch = false;
m_perfmon_id_trigger_count_map.clear();
m_first_pma_timestamp = 0;
m_last_pma_timestamp = 0;
m_first_sys0_timestamp = 0;
m_last_sys0_timestamp = 0;
}
void SocModeEBuffer::SetRecordFormat(const RecordFormatType record_format_type)
{
m_record_format = record_format_type;
}
// Copied from Perfkit\Shared\Perfkit\Tests\Emulation\SOC\Tests\SocCrossTriggerTest\Src\SocModeEBuffer.cpp
#define LOCAL_TRIGGER_BOOKMARK_VAL 0xBEEF
void SocModeEBuffer::ParseRecords()
{
ResetParsedData();
// init buffer parsing variables
uint32_t record_idx_lo = m_unread_head;
uint32_t record_idx_hi = m_max_records;
bool valid_record_end_detected = false;
// Buffer wraparound requires two iterations
for (uint32_t circular_buffer_segment = 0; circular_buffer_segment < 2;
circular_buffer_segment++) {
// Iterate records in buffer
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++) {
auto p_record = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii;
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_record);
uint32_t record_perfmon_id = p_record_common_prefix->GetPerfmonId();
if (record_perfmon_id == PMA_PerfmonId) { // PMA Record
const PmaRecordSoc* p_pma_record =
(const PmaRecordSoc*)(p_record_common_prefix);
const uint64_t timestamp = p_pma_record->GetPtimer();
if (!m_first_pma_timestamp) {
m_first_pma_timestamp = timestamp;
}
m_last_pma_timestamp = timestamp;
m_num_pma_records++;
} else if (record_perfmon_id) { // Mode C/E Record
if (m_record_format == RecordFormatType::ModeC) {
auto p_mode_c_record = (ModeCRecordVolta_12x16*)p_record;
uint16_t sample_count = p_mode_c_record->GetSampleCount();
bool delayed_sampled = p_mode_c_record->IsDelayedSampled();
// Mode C record does not have TOTAL_TRIG_RCV
if (!sample_count && !delayed_sampled) {
m_num_overflow_records++;
}
m_num_valid_records++;
m_num_samples += sample_count;
if (sample_count > 1) {
m_merged_samples_detected = true;
}
if (delayed_sampled) {
m_delayed_sample_detected = true;
}
for (uint32_t counter_idx = 0; counter_idx < 12;
counter_idx++) {
m_sum_counter_values += p_mode_c_record->counter[counter_idx];
}
// Mode C record does not have TOTAL_TRIG_RCV
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id, 0);
// Local trigger field checks
const uint16_t local_trigger_count =
p_mode_c_record->counter[10];
m_num_local_triggers += local_trigger_count;
const uint16_t local_trigger_bookmark =
p_mode_c_record->counter[11];
if (local_trigger_bookmark != LOCAL_TRIGGER_BOOKMARK_VAL) {
m_local_trigger_bookmark_mismatch = true;
}
// TODO: Timestamp monotonic increment check
if (!p_mode_c_record->GetTimestamp()) {
m_zero_timestamp_detected = true;
}
} else {
auto p_mode_e_record = (ModeERecordVolta*)p_record;
uint16_t sample_count = p_mode_e_record->GetSampleCount();
bool delayed_sampled = p_mode_e_record->IsDelayedSampled();
const uint32_t total_trigger_count = p_mode_e_record->zero2;
auto map_entry = m_perfmon_id_trigger_count_map.find(
record_perfmon_id);
// sys0 timestamp
if (record_perfmon_id == 0x70B) { // FIXME: halify!!!
uint64_t timestamp = p_mode_e_record->GetTimestamp();
if (!m_first_sys0_timestamp) {
m_first_sys0_timestamp = timestamp;
}
m_last_sys0_timestamp = timestamp;
}
if (!sample_count && !delayed_sampled &&
(map_entry == m_perfmon_id_trigger_count_map.end() ||
map_entry->second == total_trigger_count)) {
m_num_overflow_records++;
}
m_num_valid_records++;
m_num_samples += sample_count;
if (sample_count > 1) {
m_merged_samples_detected = true;
}
if (delayed_sampled) {
m_delayed_sample_detected = true;
}
m_sum_counter_values += p_mode_e_record->counter[0] +
p_mode_e_record->counter[1] +
p_mode_e_record->counter[2] +
p_mode_e_record->counter[3];
// TOTAL_TRIG_RCV monotonic check
if (map_entry != m_perfmon_id_trigger_count_map.end()) {
if (map_entry->second >= total_trigger_count) {
m_reversed_trigger_count_detected = true;
}
map_entry->second = total_trigger_count;
} else {
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id,
total_trigger_count);
}
// Local trigger field checks
const uint16_t local_trigger_count =
p_mode_e_record->zero3 & 0xFFFF;
m_num_local_triggers += local_trigger_count;
const uint16_t local_trigger_bookmark =
p_mode_e_record->zero3 >> 16;
if (local_trigger_bookmark != LOCAL_TRIGGER_BOOKMARK_VAL) {
m_local_trigger_bookmark_mismatch = true;
}
// TODO: Timestamp monotonic increment check
if (!p_mode_e_record->GetTimestamp()) {
m_zero_timestamp_detected = true;
}
}
} else {
// Reach the end of valid records
valid_record_end_detected = true;
break;
}
}
if (valid_record_end_detected) {
// Early escape when buffer wraparound does not occur
break;
} else {
// Buffer wraparound detected! Alter buffer segment bounds
record_idx_lo = 0;
record_idx_hi = m_unread_head;
}
}
}
uint32_t SocModeEBuffer::GetNumValidRecords()
{
ParseRecords();
return m_num_valid_records;
}
uint32_t SocModeEBuffer::GetNumOverflowRecords()
{
ParseRecords();
return m_num_overflow_records;
}
uint32_t SocModeEBuffer::GetNumPmaRecords()
{
ParseRecords();
return m_num_pma_records;
}
uint32_t SocModeEBuffer::GetNumSamples()
{
ParseRecords();
return m_num_samples;
}
bool SocModeEBuffer::IsDelayedSampleDetected()
{
ParseRecords();
return m_delayed_sample_detected;
}
bool SocModeEBuffer::IsMergedSamplesDetected()
{
ParseRecords();
return m_merged_samples_detected;
}
uint64_t SocModeEBuffer::GetCounterValueSum()
{
ParseRecords();
return m_sum_counter_values;
}
uint32_t SocModeEBuffer::GetNumUniquePerfmonID()
{
ParseRecords();
return m_perfmon_id_trigger_count_map.size();
}
uint32_t SocModeEBuffer::GetNumLocalTriggers()
{
ParseRecords();
return m_num_local_triggers;
}
bool SocModeEBuffer::IsZeroTimestampDetected()
{
ParseRecords();
return m_zero_timestamp_detected;
}
bool SocModeEBuffer::IsReversedTriggerCountDetected()
{
ParseRecords();
return m_reversed_trigger_count_detected;
}
bool SocModeEBuffer::IsLocalTriggerBookmarkMismatchDetected()
{
ParseRecords();
return m_local_trigger_bookmark_mismatch;
}
uint32_t SocModeEBuffer::GetMemBytes()
{
// Emulation: Wait for in-flight mem_bytes to arrive.
usleep(10000);
auto* p_mem_bytes_addr = (uint32_t*)(m_mem_bytes_buffer_cpu_va);
return *p_mem_bytes_addr;
}
uint64_t SocModeEBuffer::GetPmaRecordElapsedTime()
{
ParseRecords();
return (m_last_pma_timestamp - m_first_pma_timestamp);
}
uint64_t SocModeEBuffer::GetSysRecordElapsedCycles()
{
ParseRecords();
return (m_last_sys0_timestamp - m_first_sys0_timestamp);
}
uint64_t SocModeEBuffer::GetFirstPmaTimestamp()
{
ParseRecords();
return m_first_pma_timestamp;
}
uint64_t SocModeEBuffer::GetLastPmaTimestamp()
{
ParseRecords();
return m_last_pma_timestamp;
}
void SocModeEBuffer::PrintRecord(PmRecordSocCommonPrefix* record, bool is_pma_record, bool is_mode_c)
{
char str_buffer[256];
if (is_pma_record)
{
auto p_pma_record = (PmaRecordSoc*)(record);
sprintf(str_buffer,
"[ PMA ] PTIMER %9" PRIu64 ", DR %d, TRIG %d, START %d, END %d, BKMRK %d\n",
p_pma_record->GetPtimer(),
p_pma_record->IsDroppedRecord(),
p_pma_record->GetTotalTriggerCount(),
p_pma_record->GetStartTriggerCount(),
p_pma_record->GetStopTriggerCount(),
p_pma_record->GetBookmark()
);
}
else if (is_mode_c)
{
auto p_mode_c_record = (ModeCRecordVolta_12x16*)(record);
sprintf(str_buffer,
"[MODEC] PERFMON %3x, TS %9" PRIu64 ", DS %d, SMPLCNT %d, C0 %d, C1 %d, C2 %d, C3 %d, C4 %d, C5 %d, C6 %d, C7 %d, C8 %d, C9 %d, C10 %d, C11 %d\n",
p_mode_c_record->GetPerfmonId(),
p_mode_c_record->GetTimestamp(),
p_mode_c_record->IsDelayedSampled(),
p_mode_c_record->GetSampleCount(),
p_mode_c_record->counter[0],
p_mode_c_record->counter[1],
p_mode_c_record->counter[2],
p_mode_c_record->counter[3],
p_mode_c_record->counter[4],
p_mode_c_record->counter[5],
p_mode_c_record->counter[6],
p_mode_c_record->counter[7],
p_mode_c_record->counter[8],
p_mode_c_record->counter[9],
p_mode_c_record->counter[10],
p_mode_c_record->counter[11]
);
}
else
{
auto p_mode_e_record = (ModeERecordVolta*)(record);
sprintf(str_buffer,
"[MODEE] PERFMON %3x, TS %9" PRIu64 ", DS %d, SMPLCNT %d, EVENT %d, TRIG0 %d, TRIG1 %d, SAMPL %d, ZERO2 %x, ZERO3 %x\n",
p_mode_e_record->GetPerfmonId(),
p_mode_e_record->GetTimestamp(),
p_mode_e_record->IsDelayedSampled(),
p_mode_e_record->GetSampleCount(),
p_mode_e_record->event,
p_mode_e_record->trig0,
p_mode_e_record->trig1,
p_mode_e_record->sampl,
p_mode_e_record->zero2,
p_mode_e_record->zero3
);
}
std::cerr << str_buffer;
}
// This function parses the unread records in the PMA buffer and flushes them
// as a part of the KEEP_LATEST + buffer wraparound testing. Note that the
// parsing/flush operations are done in real time without stopping the PMA
// pulse and without doing MEM_BYTES and MEM_BUMP operations.
bool SocModeEBuffer::RealtimeParseFlush(SocRealtimeParseFlushData& stats, bool verbose)
{
// clear output stats
stats = {};
// initialize buffer parsing and bookkeeping variables
uint32_t record_idx_lo = m_unread_head;
uint32_t record_idx_hi = m_max_records;
bool valid_record_end_detected = false;
uint64_t last_pma_timestamp = 0;
uint32_t last_trigger_count = 0;
// PART 1: Parse unread records
// ============================
// Buffer wraparound requires two iterations
auto p_records = (ModeERecordRaw*)(m_pma_buffer_cpu_va); // FIXME: always Mode E?
for (uint32_t circular_buffer_segment = 0; circular_buffer_segment < 2; circular_buffer_segment++)
{
// Iterate records in buffer
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++)
{
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_records + ii);
uint32_t record_perfmon_id = p_record_common_prefix->GetPerfmonId();
if (record_perfmon_id)
{
if (record_perfmon_id == PMA_PerfmonId)
{
auto p_pma_record = (PmaRecordSoc*)p_record_common_prefix;
uint64_t curr_ptimer = p_pma_record->GetPtimer();
uint32_t total_trig_cnt = p_pma_record->GetTotalTriggerCount();
if (!curr_ptimer)
{
// Assume that complete record is not streamed
stats.m_incomplete_record_detected = true;
if (verbose)
{
std::cerr << "Incomplete PMA record: ptimer == 0\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ true);
}
break;
}
else if (!total_trig_cnt)
{
// Assume that complete record is not streamed
stats.m_incomplete_record_detected = true;
if (verbose)
{
std::cerr << "Incomplete PMA record: totalTrigCnt == 0\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ true);
}
break;
}
else if (curr_ptimer <= last_pma_timestamp)
{
stats.m_malformed_record = true;
if (verbose)
{
std::cerr << "Malformed PMA record: ptimer " << curr_ptimer << " <= lastPtimer " << last_pma_timestamp << "\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ true);
}
break;
}
else if (total_trig_cnt <= last_trigger_count)
{
stats.m_malformed_record = true;
if (verbose)
{
std::cerr << "Malformed PMA record: totalTrigCnt " << (int)total_trig_cnt << " <= lastTriggerCount " << (int)last_trigger_count << "\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ true);
}
break;
}
// Update Ptimer and trigger count for bookkeeping
last_pma_timestamp = curr_ptimer;
last_trigger_count = total_trig_cnt;
}
else
{
auto p_mode_e_record = (ModeERecordVolta*)p_record_common_prefix;
uint64_t timestamp = p_mode_e_record->GetTimestamp();
const uint32_t total_trigger_count = p_mode_e_record->zero2;
auto map_entry = m_perfmon_id_trigger_count_map.find(record_perfmon_id);
if (!timestamp)
{
// Assume that complete record is not streamed
stats.m_incomplete_record_detected = true;
if (verbose)
{
std::cerr << "Incomplete ModeE record: timestamp == 0\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ false);
}
break;
}
else if (!total_trigger_count)
{
// Assume that complete record is not streamed
stats.m_incomplete_record_detected = true;
if (verbose)
{
std::cerr << "Incomplete ModeE record: totalTriggerCount == 0\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ false);
}
break;
}
if (map_entry != m_perfmon_id_trigger_count_map.end())
{
if (map_entry->second >= total_trigger_count)
{
stats.m_malformed_record = true;
if (verbose)
{
std::cerr << "Malformed ModeE record: totalTriggerCount " << (int)total_trigger_count << " <= lastTriggerCount " << (int)map_entry->second << "\n";
PrintRecord(p_record_common_prefix, /*is_pma_record*/ false);
}
break;
}
map_entry->second = total_trigger_count;
}
else
{
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id, total_trigger_count);
}
}
// If we reached here, we consumed one more record
stats.m_num_records_consumed++;
}
else
{
// Reach the end of valid records
valid_record_end_detected = true;
break;
}
}
if (stats.m_malformed_record || stats.m_incomplete_record_detected || valid_record_end_detected)
{
// Early escape when incomplete/malformed record is read, or buffer wraparound does not occur
break;
}
else
{
// Buffer wraparound detected! Alter buffer segment bounds for the second parsing loop
stats.m_buffer_wraparound = true;
record_idx_lo = 0;
record_idx_hi = m_unread_head;
}
}
// PART 2: Flush
// =============
if (stats.m_malformed_record || !stats.m_num_records_consumed)
{
return false;
}
uint32_t bytes_to_flush = stats.m_num_records_consumed * sizeof(ModeERecordRaw);
if (m_unread_head + stats.m_num_records_consumed <= m_max_records) // Single segment
{
memset(&p_records[m_unread_head], 0, bytes_to_flush);
// Move unread head back to 0 if unreadHead + numRecordsConsumed == maxRecords
m_unread_head = (m_unread_head + stats.m_num_records_consumed) % m_max_records;
}
else // Two segments
{
int num_records_first_segment = m_max_records - m_unread_head;
int num_records_second_segment = stats.m_num_records_consumed - num_records_first_segment;
memset(&p_records[m_unread_head], 0, sizeof(ModeERecordRaw) * num_records_first_segment);
memset(&p_records[0], 0, sizeof(ModeERecordRaw) * num_records_second_segment);
m_unread_head = num_records_second_segment;
}
return true;
}
bool SocModeEBuffer::FlushRecordsInBuffer(const uint32_t bytes_to_flush)
{
// Empty flush
if (!bytes_to_flush) {
return true;
}
const uint32_t num_records_to_flush = bytes_to_flush / sizeof(ModeERecordRaw);
auto p_records = (ModeERecordRaw*)(m_pma_buffer_cpu_va);
if (m_unread_head + num_records_to_flush <= m_max_records) // Single segment
{
memset(&p_records[m_unread_head], 0, bytes_to_flush);
// Move unread head back to 0 if unreadHead + numRecordsToFlush == maxRecords
m_unread_head = (m_unread_head + num_records_to_flush) % m_max_records;
}
else // Two segments
{
int num_records_first_segment = m_max_records - m_unread_head;
int num_records_second_segment = num_records_to_flush - num_records_first_segment;
memset(&p_records[m_unread_head], 0, sizeof(ModeERecordRaw) * num_records_first_segment);
memset(&p_records[0], 0, sizeof(ModeERecordRaw) * num_records_second_segment);
m_unread_head = num_records_second_segment;
}
if (bytes_to_flush == m_pma_buffer_size) {
std::cout << "WARNING: Buffer is full! Reset PMA MEM_HEAD to guarantee HW-SW consistency...\n";
// TODO: reset PMA MEM_HEAD need regops
return false;
}
// Report flushed bytes to HW
nv_soc_hwpm_pma_channel_state_params param = {};
param.in_mem_bump = bytes_to_flush;
param.in_stream_mem_bytes = 0;
param.in_check_overflow = 0;
param.in_read_mem_head = 0;
if (m_api_table.nv_soc_hwpm_session_set_get_pma_state_fn(
m_session, &param)) {
std::cerr << "ERROR: SOC HWPM session set get pma state mem bump failed!\n";
return false;
}
return true;
}
void SocModeEBuffer::PrintRecords(const size_t num_records_to_print) const
{
char str_buffer[256];
std::deque<std::string> record_strings;
if (m_record_format == RecordFormatType::ModeC)
{
printf("No. PerfmonID Elaps_cyc DS SmpCt C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 \n");
printf("---- --------- --------- -- ----- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----\n");
}
else
{
printf("No. PerfmonID Elaps_cyc DS SmpCt Count0 Count1 Count2 Count3 TrgB TrgA \n");
printf("---- --------- --------- -- ----- ------- ------- ------- ------- -------- --------\n");
}
uint32_t record_idx_lo = m_unread_head;
uint32_t record_idx_hi = m_max_records;
bool valid_record_end_detected = false;
for (uint32_t circular_buffer_segment = 0; circular_buffer_segment < 2; circular_buffer_segment++)
{
// Iterate records in buffer
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++)
{
auto p_record = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii;
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_record);
uint32_t perfmon_id = p_record_common_prefix->GetPerfmonId();
if (perfmon_id && perfmon_id != PMA_PerfmonId) // Mode C/E Record
{
if (m_record_format == RecordFormatType::ModeC)
{
auto p_mode_c_record = (ModeCRecordVolta_12x16*)p_record;
uint64_t timestamp = p_mode_c_record->GetTimestamp();
bool is_delayed_sampled = p_mode_c_record->IsDelayedSampled();
uint32_t sample_cnt = p_mode_c_record->GetSampleCount();
sprintf(str_buffer,
"%4d %9x %9" PRIu64 " %2x %5d %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x\n",
ii,
perfmon_id,
timestamp,
is_delayed_sampled,
sample_cnt,
p_mode_c_record->counter[0],
p_mode_c_record->counter[1],
p_mode_c_record->counter[2],
p_mode_c_record->counter[3],
p_mode_c_record->counter[4],
p_mode_c_record->counter[5],
p_mode_c_record->counter[6],
p_mode_c_record->counter[7],
p_mode_c_record->counter[8],
p_mode_c_record->counter[9],
p_mode_c_record->counter[10],
p_mode_c_record->counter[11]
);
}
else
{
auto p_mode_e_record = (ModeERecordVolta*)p_record;
uint64_t timestamp = p_mode_e_record->GetTimestamp();
bool is_delayed_sampled = p_mode_e_record->IsDelayedSampled();
uint32_t sample_cnt = p_mode_e_record->GetSampleCount();
sprintf(str_buffer,
"%4d %9x %9" PRIu64 " %2x %5d %7d %7d %7d %7d %8x %8x\n",
ii,
perfmon_id,
timestamp,
is_delayed_sampled,
sample_cnt,
p_mode_e_record->event,
p_mode_e_record->trig0,
p_mode_e_record->trig1,
p_mode_e_record->sampl,
p_mode_e_record->zero2,
p_mode_e_record->zero3
);
}
record_strings.emplace_back(std::string(str_buffer));
if (record_strings.size() > num_records_to_print)
{
record_strings.pop_front();
}
}
else if (perfmon_id == PMA_PerfmonId)
{
// Do not print PMA records in this function
continue;
}
else
{
// Reach the end of valid records
valid_record_end_detected = true;
break;
}
}
if (valid_record_end_detected)
{
// Early escape when buffer wraparound does not occur
break;
}
else
{
// Buffer wraparound
record_idx_lo = 0;
record_idx_hi = m_unread_head;
}
}
for (size_t ii = 0; ii < record_strings.size(); ii++)
{
std::cout << record_strings[ii];
}
}
// Dump the complete buffer starting from beginning to end. Try to parse
// records if possible, but don't count on it. ONLY FOR DEBUG!
void SocModeEBuffer::DumpBuffer()
{
auto p_records = (ModeERecordRaw*)(m_pma_buffer_cpu_va);
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_records);
auto perfmon_id = p_record_common_prefix->GetPerfmonId();
// Print the first record
if (perfmon_id == PMA_PerfmonId)
{
PrintRecord(p_record_common_prefix, true /* isPmaRecord */);
}
else
{
PrintRecord(p_record_common_prefix, false /* isPmaRecord */, m_record_format == RecordFormatType::ModeC);
}
// Iterate through remaining records
uint32_t matching_records = 0;
for (uint32_t ii = 1; ii < m_max_records; ii++)
{
auto p_record = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii;
auto p_record_last = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii - 1;
if (memcmp(p_record, p_record_last, sizeof(ModeERecordRaw)) == 0)
{
// One more record identical to the previous one: just count it and continue
++matching_records;
}
else
{
// Record mismatch
if (matching_records)
{
// If we have counted identical records, dump the count and reset it
std::cout << matching_records << " more records identical to the previous one...\n";
matching_records = 0;
}
// Print the new record
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_record);
auto perfmon_id = p_record_common_prefix->GetPerfmonId();
if (perfmon_id == PMA_PerfmonId)
{
PrintRecord(p_record_common_prefix, true /* isPmaRecord */);
}
else
{
PrintRecord(p_record_common_prefix, false /* isPmaRecord */, m_record_format == RecordFormatType::ModeC);
}
}
}
// If we have accumulated any identical records at the end, print it
if (matching_records)
{
std::cout << matching_records << " more records identical to the previous one...\n";
}
}
void SocModeEBuffer::PrintPmaRecords(const size_t num_records_to_print) const
{
char str_buffer[256];
std::deque<std::string> record_strings;
printf("No. Ptimer DR TotalTrig StartTrig StopTrig Bookmark \n");
printf("---- --------- -- --------- --------- --------- ---------\n");
uint32_t record_idx_lo = m_unread_head;
uint32_t record_idx_hi = m_max_records;
bool valid_record_end_detected = false;
for (uint32_t circular_buffer_segment = 0;
circular_buffer_segment < 2;
circular_buffer_segment++)
{
// Iterate records in buffer
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++)
{
auto p_records = (ModeERecordVolta*)(m_pma_buffer_cpu_va);
auto p_record_common_prefix =
(PmRecordSocCommonPrefix*)(&p_records[ii]);
uint32_t perfmon_id = p_record_common_prefix->GetPerfmonId();
if (perfmon_id == PMA_PerfmonId) // PMA Record
{
const PmaRecordSoc* p_pma_record =
(const PmaRecordSoc*)(p_record_common_prefix);
uint64_t timestamp = p_pma_record->GetPtimer();
uint32_t total_trig_cnt =
p_pma_record->GetTotalTriggerCount();
uint32_t start_trig_cnt =
p_pma_record->GetStartTriggerCount();
uint32_t stop_trig_cnt =
p_pma_record->GetStopTriggerCount();
uint32_t bookmark = p_pma_record->GetBookmark();
bool is_dropped_record =
p_pma_record->IsDroppedRecord();
sprintf(str_buffer,
"%4d %9" PRIu64 " %2x %9d %9d %9d %9d\n",
ii,
timestamp,
is_dropped_record,
total_trig_cnt,
start_trig_cnt,
stop_trig_cnt,
bookmark
);
record_strings.emplace_back(std::string(str_buffer));
if (record_strings.size() > num_records_to_print)
{
record_strings.pop_front();
}
}
else if (perfmon_id) // Mode C/E Record
{
}
else
{
// Reach the end of valid records
valid_record_end_detected = true;
break;
}
}
if (valid_record_end_detected)
{
// Early escape when buffer wraparound does not occur
break;
}
else
{
// Buffer wraparound
record_idx_lo = 0;
record_idx_hi = m_unread_head;
}
}
for (size_t ii = 0; ii < record_strings.size(); ii++)
{
std::cout << record_strings[ii];
}
}

View File

@@ -0,0 +1,116 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#pragma once
#include <vector>
#include <functional>
#include <unordered_map>
#include <cstdint>
#include "nv_soc_hwpm_loader.hpp"
#include "hwpm_record_format.h"
enum RecordFormatType
{
ModeC,
ModeE,
};
// realtime parse-flush
struct SocRealtimeParseFlushData
{
uint32_t m_num_records_consumed;
bool m_buffer_wraparound;
bool m_incomplete_record_detected;
bool m_malformed_record;
};
class SocModeEBuffer
{
private:
void ResetParsedData();
void ParseRecords();
void PrintRecord(PmRecordSocCommonPrefix* record, bool is_pma_record,
bool is_mode_c = false);
nv_soc_hwpm_api_table m_api_table;
nv_soc_hwpm_session m_session;
RecordFormatType m_record_format;
// Parsed data
uint32_t m_num_valid_records;
uint32_t m_num_overflow_records;
uint32_t m_num_pma_records;
uint32_t m_num_samples;
bool m_delayed_sample_detected;
bool m_merged_samples_detected;
uint64_t m_sum_counter_values;
uint32_t m_num_local_triggers;
bool m_zero_timestamp_detected;
bool m_reversed_trigger_count_detected;
bool m_local_trigger_bookmark_mismatch;
std::unordered_map<uint32_t, uint32_t> m_perfmon_id_trigger_count_map;
uint64_t m_first_pma_timestamp;
uint64_t m_last_pma_timestamp;
uint64_t m_first_sys0_timestamp;
uint64_t m_last_sys0_timestamp;
uint8_t* m_pma_buffer;
uint8_t* m_membytes_buffer;
protected:
uint32_t m_max_records;
uint32_t m_unread_head;
size_t m_pma_buffer_size;
public:
void* m_pma_buffer_cpu_va;
void* m_mem_bytes_buffer_cpu_va;
SocModeEBuffer(
nv_soc_hwpm_api_table& api_table,
nv_soc_hwpm_session session);
~SocModeEBuffer();
bool Initialize();
void SetRecordFormat(const RecordFormatType record_format_type);
uint32_t GetNumValidRecords();
uint32_t GetNumOverflowRecords();
uint32_t GetNumPmaRecords();
uint32_t GetNumSamples();
bool IsDelayedSampleDetected();
bool IsMergedSamplesDetected();
uint64_t GetCounterValueSum();
uint32_t GetNumUniquePerfmonID();
uint32_t GetNumLocalTriggers();
bool IsZeroTimestampDetected();
bool IsReversedTriggerCountDetected();
bool IsLocalTriggerBookmarkMismatchDetected();
uint32_t GetMemBytes();
uint64_t GetPmaRecordElapsedTime();
uint64_t GetSysRecordElapsedCycles();
uint64_t GetFirstPmaTimestamp();
uint64_t GetLastPmaTimestamp();
bool FlushRecordsInBuffer(const uint32_t bytes_to_flush);
bool RealtimeParseFlush(SocRealtimeParseFlushData& stats, bool verbose);
void PrintRecords(const size_t num_records_to_print) const;
void PrintPmaRecords(const size_t num_records_to_print) const;
void DumpBuffer();
};

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef T241_TEST_H
#define T241_TEST_H
#include "nv_soc_hwpm_test.h"
#define T241_MAX_SOCKETS 4
class T241Tests : public NvSocHwpmTests
{
public:
T241Tests();
~T241Tests() override;
protected:
struct PmaConfigurationParams
{
PmaConfigurationParams()
{
enable_streaming = false;
pulse_interval = 0;
enable_pma_record = false;
keep_latest = false;
}
bool enable_streaming;
uint32_t pulse_interval;
bool enable_pma_record;
bool keep_latest;
};
struct PmmConfigurationParams
{
enum Mode {
MODE_B,
MODE_C,
MODE_E
};
PmmConfigurationParams()
{
mode = MODE_B;
perfmon_idx = 0;
enable_local_triggering = false;
enable_overflow_priming = false;
collect_one = false;
}
Mode mode;
uint32_t perfmon_idx;
bool enable_local_triggering;
bool enable_overflow_priming;
bool collect_one;
};
void SetUp() override;
void TearDown() override;
void GetDevices();
void TestRegopsRead(nv_soc_hwpm_session session,
uint64_t pma_record_buffer_pma_va,
size_t record_buffer_size);
void TestRegopsWrite(nv_soc_hwpm_session session);
void RegOpWrite32(
nv_soc_hwpm_session session, uint64_t address, uint32_t value, uint32_t mask);
void RegOpRead32(
nv_soc_hwpm_session session, uint64_t address, uint32_t *value);
void SetupPma(nv_soc_hwpm_session session, const PmaConfigurationParams& params);
void EnablePmaStreaming(nv_soc_hwpm_session session, const PmaConfigurationParams& params);
void SetupPmm(nv_soc_hwpm_session session, const PmmConfigurationParams& params);
void SetupWatchbus(nv_soc_hwpm_session session, const PmmConfigurationParams& params);
void TeardownPma(nv_soc_hwpm_session session);
void TeardownPmm(nv_soc_hwpm_session session, const PmmConfigurationParams& params);
void TeardownPerfmux(nv_soc_hwpm_session session);
void IssuePmaTrigger(nv_soc_hwpm_session session);
void HarvestCounters(
nv_soc_hwpm_session session,
const PmmConfigurationParams& params,
const uint32_t sig_val[4]);
nv_soc_hwpm_device t241_dev[T241_MAX_SOCKETS];
uint32_t t241_dev_count;
};
#endif // T241_TEST_H

View File

@@ -0,0 +1,31 @@
################################### tell Emacs this is a -*- makefile-gmake -*-
#
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# tmake for SW Mobile component makefile
#
# libnvsochwpm: shared and static library interface
#
###############################################################################
ifdef NV_INTERFACE_FLAG_SHARED_LIBRARY_SECTION
NV_INTERFACE_NAME := nvsochwpm
NV_INTERFACE_EXPORTS := ../../os/lnx/libnvsochwpm
NV_INTERFACE_PUBLIC_INCLUDES := ../../include
NV_INTERFACE_COMPONENT_DIR := .
endif
ifdef NV_INTERFACE_FLAG_STATIC_LIBRARY_SECTION
NV_INTERFACE_NAME := nvsochwpm_static
NV_INTERFACE_COMPONENT_DIR := .
endif

View File

@@ -0,0 +1,71 @@
################################### tell Emacs this is a -*- makefile-gmake -*-
#
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# tmake for SW Mobile component makefile
#
# libnvsochwpm: shared and static library
#
###############################################################################
# Common part of shared and static library build
define nvsochwpm_common_make
NV_COMPONENT_OWN_INTERFACE_DIR := .
_top_hwpm_dir = ../../../
_soc_hwpm_dir = ../../
NV_COMPONENT_SOURCES := \
$$(_soc_hwpm_dir)/common/log.c \
$$(_soc_hwpm_dir)/os/lnx/nv_soc_hwpm_lnx.c \
$$(_soc_hwpm_dir)/nv_soc_hwpm.c
NV_COMPONENT_INCLUDES += \
$$(_soc_hwpm_dir) \
$$(_soc_hwpm_dir)/include \
$$(_top_hwpm_dir)/include \
# clear variables
_soc_hwpm_dir :=
endef
# Build shared library
ifdef NV_COMPONENT_FLAG_SHARED_LIBRARY_SECTION
include $(NV_BUILD_START_COMPONENT)
NV_COMPONENT_NAME := nvsochwpm
$(eval $(nvsochwpm_common_make))
include $(NV_BUILD_SHARED_LIBRARY)
endif
# Build static library for test
ifdef NV_COMPONENT_FLAG_STATIC_LIBRARY_SECTION
include $(NV_BUILD_START_COMPONENT)
NV_COMPONENT_NAME := nvsochwpm_static
$(eval $(nvsochwpm_common_make))
include $(NV_BUILD_STATIC_LIBRARY)
endif
# Local Variables:
# indent-tabs-mode: t
# tab-width: 8
# End:
# vi: set tabstop=8 noexpandtab:

View File

@@ -0,0 +1,56 @@
################################### tell Emacs this is a -*- makefile-gmake -*-
#
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: GPL-2.0-only
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# tmake for SW Mobile component makefile
#
# nv_soc_test: unit testing for libnvsochwpm
#
###############################################################################
ifdef NV_COMPONENT_FLAG_NVTEST_EXECUTABLE_SECTION
include $(NV_BUILD_START_COMPONENT)
NV_COMPONENT_NAME := nv_soc_hwpm_test
NV_COMPONENT_SOURCES := \
nv_soc_hwpm_test.cpp \
t241_test.cpp
_soc_hwpm_dir = ../../
NV_COMPONENT_CFLAGS := -DNV_IS_LDK=1 -Wall
NV_COMPONENT_INCLUDES := \
$(_common_includes) \
$(NV_SOURCE)/3rdparty/google/googletest/googletest/include \
$(NV_SOURCE)/3rdparty/google/googletest/googlemock/include \
$$(_soc_hwpm_dir)/include
NV_COMPONENT_NEEDED_STATIC_INTERFACE_DIRS := \
$(NV_SOURCE)/3rdparty/google/googletest/googletest \
$(NV_SOURCE)/3rdparty/google/googletest/googlemock
ifeq ($(NV_BUILD_CONFIGURATION_IS_COVERAGE),1)
NV_COMPONENT_CFLAGS += --coverage
endif
NV_COMPONENT_SYSTEM_SHARED_LIBRARIES := \
stdc++ \
dl \
pthread
NV_COMPONENT_CODE_GENERATION := c++11
include $(NV_BUILD_NVTEST_EXECUTABLE)
endif