diff --git a/libnvsochwpm/Makefile b/libnvsochwpm/Makefile new file mode 100644 index 0000000..a298a25 --- /dev/null +++ b/libnvsochwpm/Makefile @@ -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* diff --git a/libnvsochwpm/Makefile.umbrella.tmk b/libnvsochwpm/Makefile.umbrella.tmk new file mode 100644 index 0000000..2686f41 --- /dev/null +++ b/libnvsochwpm/Makefile.umbrella.tmk @@ -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: diff --git a/libnvsochwpm/common/bit.h b/libnvsochwpm/common/bit.h new file mode 100644 index 0000000..9e37db1 --- /dev/null +++ b/libnvsochwpm/common/bit.h @@ -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 + +#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__*/ \ No newline at end of file diff --git a/libnvsochwpm/common/log.c b/libnvsochwpm/common/log.c new file mode 100644 index 0000000..ed75e2d --- /dev/null +++ b/libnvsochwpm/common/log.c @@ -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 \ No newline at end of file diff --git a/libnvsochwpm/common/log.h b/libnvsochwpm/common/log.h new file mode 100644 index 0000000..fbd7648 --- /dev/null +++ b/libnvsochwpm/common/log.h @@ -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 +#include + +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__*/ \ No newline at end of file diff --git a/libnvsochwpm/common/register_util.h b/libnvsochwpm/common/register_util.h new file mode 100644 index 0000000..bcee4e9 --- /dev/null +++ b/libnvsochwpm/common/register_util.h @@ -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__ \ No newline at end of file diff --git a/libnvsochwpm/common/types.h b/libnvsochwpm/common/types.h new file mode 100644 index 0000000..e39b76f --- /dev/null +++ b/libnvsochwpm/common/types.h @@ -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__*/ \ No newline at end of file diff --git a/libnvsochwpm/include/nv_soc_hwpm.h b/libnvsochwpm/include/nv_soc_hwpm.h new file mode 100644 index 0000000..65d2e5b --- /dev/null +++ b/libnvsochwpm/include/nv_soc_hwpm.h @@ -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 +#include + +#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__*/ \ No newline at end of file diff --git a/libnvsochwpm/include/nv_soc_hwpm_loader.hpp b/libnvsochwpm/include/nv_soc_hwpm_loader.hpp new file mode 100644 index 0000000..8516561 --- /dev/null +++ b/libnvsochwpm/include/nv_soc_hwpm_loader.hpp @@ -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 +#include +#include + +#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__ */ diff --git a/libnvsochwpm/nv_soc_hwpm.c b/libnvsochwpm/nv_soc_hwpm.c new file mode 100644 index 0000000..72911a7 --- /dev/null +++ b/libnvsochwpm/nv_soc_hwpm.c @@ -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 +#include +#include + +#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 \ No newline at end of file diff --git a/libnvsochwpm/os/lnx/libnvsochwpm.export b/libnvsochwpm/os/lnx/libnvsochwpm.export new file mode 100644 index 0000000..b7ae298 --- /dev/null +++ b/libnvsochwpm/os/lnx/libnvsochwpm.export @@ -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 \ No newline at end of file diff --git a/libnvsochwpm/os/lnx/libnvsochwpm.map b/libnvsochwpm/os/lnx/libnvsochwpm.map new file mode 100644 index 0000000..47a6414 --- /dev/null +++ b/libnvsochwpm/os/lnx/libnvsochwpm.map @@ -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: + *; +}; \ No newline at end of file diff --git a/libnvsochwpm/os/lnx/nv_soc_hwpm_lnx.c b/libnvsochwpm/os/lnx/nv_soc_hwpm_lnx.c new file mode 100644 index 0000000..9714512 --- /dev/null +++ b/libnvsochwpm/os/lnx/nv_soc_hwpm_lnx.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ¶ms); + 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, + ¶m); + 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, ¶m); + 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, + ¶ms); + 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, ¶ms); + 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, + ¶ms); + 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, + ¶ms); + 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__ */ \ No newline at end of file diff --git a/libnvsochwpm/os/nv_soc_hwpm_os.h b/libnvsochwpm/os/nv_soc_hwpm_os.h new file mode 100644 index 0000000..366d29d --- /dev/null +++ b/libnvsochwpm/os/nv_soc_hwpm_os.h @@ -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__*/ diff --git a/libnvsochwpm/test/Makefile b/libnvsochwpm/test/Makefile new file mode 100644 index 0000000..8dc5383 --- /dev/null +++ b/libnvsochwpm/test/Makefile @@ -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 diff --git a/libnvsochwpm/test/hwpm_record_format.h b/libnvsochwpm/test/hwpm_record_format.h new file mode 100644 index 0000000..b8c9d90 --- /dev/null +++ b/libnvsochwpm/test/hwpm_record_format.h @@ -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 +#include + +// 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 diff --git a/libnvsochwpm/test/nv_soc_hwpm_test.cpp b/libnvsochwpm/test/nv_soc_hwpm_test.cpp new file mode 100644 index 0000000..3025c6f --- /dev/null +++ b/libnvsochwpm/test/nv_soc_hwpm_test.cpp @@ -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)); +} \ No newline at end of file diff --git a/libnvsochwpm/test/nv_soc_hwpm_test.h b/libnvsochwpm/test/nv_soc_hwpm_test.h new file mode 100644 index 0000000..64de52d --- /dev/null +++ b/libnvsochwpm/test/nv_soc_hwpm_test.h @@ -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 +#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 diff --git a/libnvsochwpm/test/soc_mode_e_buffer.cpp b/libnvsochwpm/test/soc_mode_e_buffer.cpp new file mode 100644 index 0000000..452d22b --- /dev/null +++ b/libnvsochwpm/test/soc_mode_e_buffer.cpp @@ -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 +#include +#include +#include +#include +#include + +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, ¶m)) { + 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 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 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]; + } +} diff --git a/libnvsochwpm/test/soc_mode_e_buffer.h b/libnvsochwpm/test/soc_mode_e_buffer.h new file mode 100644 index 0000000..6f467f0 --- /dev/null +++ b/libnvsochwpm/test/soc_mode_e_buffer.h @@ -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 +#include +#include +#include + +#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 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(); +}; + diff --git a/libnvsochwpm/test/t241_test.cpp b/libnvsochwpm/test/t241_test.cpp new file mode 100644 index 0000000..8ae04da --- /dev/null +++ b/libnvsochwpm/test/t241_test.cpp @@ -0,0 +1,1755 @@ +/* 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 "t241_test.h" +#include "soc_mode_e_buffer.h" +#include "common/register_util.h" +#include "th500/nv_ref_dev_perf.h" + +#include + +static const char* kIpNames[] = { + "VI", + "ISP", + "VIC", + "OFA", + "PVA", + "NVDLA", + "MGBE", + "SCF", + "NVDEC", + "NVENC", + "PCIE", + "DISPLAY", + "MSS_CHANNEL", + "MSS_GPU_HUB", + "MSS_ISO_NISO_HUBS", + "MSS_MCF", + "APE", + "C2C", + "SMMU", + "CL2", + "NVLCTRL", + "NVLRX", + "NVLTX", + "MSS_HUB", + "MCF_SOC", + "MCF_C2C", + "MCF_CLINK", + "MCF_CORE", + "MCF_OCU", + "PCIE_XTLQ", + "PCIE_XTLRC", + "PCIE_XALRC", + "UCF_MSW", + "UCF_PSW", + "UCF_CSW", + "UCF_HUB", + "UCF_SCB", + "CPU", +}; + +static const char* kResourceNames[] = { + "VI", + "ISP", + "VIC", + "OFA", + "PVA", + "NVDLA", + "MGBE", + "SCF", + "NVDEC", + "NVENC", + "PCIE", + "DISPLAY", + "MSS_CHANNEL", + "MSS_GPU_HUB", + "MSS_ISO_NISO_HUBS", + "MSS_MCF", + "PMA", + "CMD_SLICE_RTR", + "APE", + "C2C", + "SMMU", + "CL2", + "NVLCTRL", + "NVLRX", + "NVLTX", + "MSS_HUB", + "MCF_SOC", + "MCF_C2C", + "MCF_CLINK", + "MCF_CORE", + "MCF_OCU", + "PCIE_XTLQ", + "PCIE_XTLRC", + "PCIE_XALRC", + "UCF_MSW", + "UCF_PSW", + "UCF_CSW", + "UCF_HUB", + "UCF_SCB", + "CPU", +}; + +T241Tests::T241Tests() : NvSocHwpmTests(), t241_dev_count(0) +{ +} + +T241Tests::~T241Tests() +{ +} + +void T241Tests::SetUp() +{ + NvSocHwpmTests::SetUp(); + ASSERT_EQ(0, api_table.nv_soc_hwpm_init_fn()); +} + +void T241Tests::TearDown() +{ + api_table.nv_soc_hwpm_exit_fn(); + NvSocHwpmTests::TearDown(); +} + +void T241Tests::GetDevices() +{ + t241_dev_count = 0; + + ASSERT_EQ(0, api_table.nv_soc_hwpm_get_devices_fn(&t241_dev_count, NULL)); + ASSERT_EQ(1U, t241_dev_count); + + ASSERT_EQ(0, api_table.nv_soc_hwpm_get_devices_fn(&t241_dev_count, t241_dev)); +} + +TEST_F(T241Tests, EnumerateDevices) +{ + uint32_t chip_id, i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + dev = t241_dev[i]; + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_SOC_CHIP_ID; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(chip_id), &chip_id)); + ASSERT_EQ(TEGRA_SOC_HWPM_CHIP_ID_T241, chip_id); + + } +} + +TEST_F(T241Tests, EnumerateDevicesNegative) +{ + t241_dev_count = 0; + + // Should fail with invalid dev_count ptr. + ASSERT_NE(0, api_table.nv_soc_hwpm_get_devices_fn(NULL, t241_dev)); + + // Get valid count. + ASSERT_EQ(0, api_table.nv_soc_hwpm_get_devices_fn(&t241_dev_count, NULL)); + ASSERT_EQ(1U, t241_dev_count); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, api_table.nv_soc_hwpm_get_devices_fn(&t241_dev_count, NULL)); +} + +TEST_F(T241Tests, EnumerateIPs) +{ + uint32_t ip_count, i, j; + uint64_t fs_mask; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + nv_soc_hwpm_ip ip[NV_SOC_HWPM_NUM_IPS], cur_ip; + nv_soc_hwpm_ip_attribute ip_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(ip_count), &ip_count)); + ASSERT_GT(ip_count, 0U); + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_LIST; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*ip) * ip_count, ip)); + + for (j = 0; j < ip_count; j++) { + cur_ip = ip[j]; + printf("\t%d - ip id = %u, name: %s", j, cur_ip, kIpNames[cur_ip]); + + ip_attr = NV_SOC_HWPM_IP_ATTRIBUTE_FS_MASK; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_ip_get_info_fn( + dev, cur_ip, ip_attr, sizeof(fs_mask), &fs_mask)); + printf(", fs_mask = 0x%lx\n", fs_mask); + ASSERT_GT(fs_mask, 0U); + } + } +} + +TEST_F(T241Tests, EnumerateIPsNegative) +{ + uint32_t ip_count, i, j; + uint64_t fs_mask; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + nv_soc_hwpm_ip ip[NV_SOC_HWPM_NUM_IPS], cur_ip; + nv_soc_hwpm_ip_attribute ip_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_COUNT; + + // Should fail with invalid buffer size. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(uint8_t), &ip_count)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(ip_count), NULL)); + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(ip_count), &ip_count)); + ASSERT_GT(ip_count, 0U); + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_IP_AVAILABLE_LIST; + + // Should fail with invalid buffer size. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(uint8_t) * ip_count, ip)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*ip) * ip_count, NULL)); + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*ip) * ip_count, ip)); + + for (j = 0; j < ip_count; j++) { + cur_ip = ip[j]; + printf("\t%d - ip id = %u, name: %s", j, cur_ip, kIpNames[cur_ip]); + + // Should fail with invalid attribute. + ip_attr = (nv_soc_hwpm_ip_attribute)0xffffffff; + size_t dummy; + ASSERT_NE(0, + api_table.nv_soc_hwpm_ip_get_info_fn( + dev, cur_ip, ip_attr, sizeof(dummy), &dummy)); + + ip_attr = NV_SOC_HWPM_IP_ATTRIBUTE_FS_MASK; + + // Should fail with invalid buffer size. + ASSERT_NE(0, + api_table.nv_soc_hwpm_ip_get_info_fn( + dev, cur_ip, ip_attr, sizeof(uint8_t), &fs_mask)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_ip_get_info_fn( + dev, cur_ip, ip_attr, sizeof(fs_mask), NULL)); + } + } + +} + +TEST_F(T241Tests, EnumerateResources) +{ + uint32_t res_count, res_available, i, j; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + nv_soc_hwpm_resource res[NV_SOC_HWPM_NUM_RESOURCES], cur_res; + nv_soc_hwpm_resource_attribute res_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(res_count), &res_count)); + ASSERT_GT(res_count, 0U); + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*res) * res_count, res)); + + for (j = 0; j < res_count; j++) { + cur_res = res[j]; + printf("\t%d - res id = %u, name: %s", + j, cur_res, kResourceNames[cur_res]); + + res_attr = NV_SOC_HWPM_RESOURCE_ATTRIBUTE_IS_AVAILABLE; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_resource_get_info_fn( + dev, cur_res, res_attr, sizeof(res_available), + &res_available)); + ASSERT_EQ(1U, res_available); + printf(", available = %u\n", res_available); + } + } +} + +TEST_F(T241Tests, EnumerateResourcesNegative) +{ + uint32_t res_count, res_available, i, j; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + nv_soc_hwpm_resource res[NV_SOC_HWPM_NUM_RESOURCES], cur_res; + nv_soc_hwpm_resource_attribute res_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT; + + // Should fail with invalid buffer size. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(uint8_t), &res_count)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(res_count), NULL)); + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(res_count), &res_count)); + ASSERT_GT(res_count, 0U); + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST; + + // Should fail with invalid buffer size. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(uint8_t) * res_count, res)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*res) * res_count, NULL)); + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*res) * res_count, res)); + + for (j = 0; j < res_count; j++) { + cur_res = res[j]; + printf("\t%d - res id = %u, name: %s", + j, cur_res, kResourceNames[cur_res]); + + // Should fail with invalid attribute. + res_attr = (nv_soc_hwpm_resource_attribute)0xffffffff; + size_t dummy; + ASSERT_NE(0, + api_table.nv_soc_hwpm_resource_get_info_fn( + dev, cur_res, res_attr, sizeof(dummy), &dummy)); + + res_attr = NV_SOC_HWPM_RESOURCE_ATTRIBUTE_IS_AVAILABLE; + + // Should fail with invalid buffer size. + ASSERT_NE(0, + api_table.nv_soc_hwpm_resource_get_info_fn( + dev, cur_res, res_attr, sizeof(uint8_t), &res_available)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_resource_get_info_fn( + dev, cur_res, res_attr, sizeof(res_available), NULL)); + } + } +} + +TEST_F(T241Tests, SessionAlloc) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_DEVICE; + nv_soc_hwpm_device dev_test; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(dev_test), &dev_test)); + ASSERT_EQ(dev.handle, dev_test.handle); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_SESSION_STARTED; + uint32_t session_started; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(session_started), &session_started)); + ASSERT_EQ(0U, session_started); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_BUFFER_ALLOCATED; + uint32_t pma_buffer_allocated; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_buffer_allocated), + &pma_buffer_allocated)); + ASSERT_EQ(0U, pma_buffer_allocated); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE; + size_t pma_record_buffer_size; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_record_buffer_size), + &pma_record_buffer_size)); + ASSERT_EQ(0U, pma_record_buffer_size); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA; + void* pma_record_buffer_cpu_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_record_buffer_cpu_va), + &pma_record_buffer_cpu_va)); + ASSERT_TRUE(NULL == pma_record_buffer_cpu_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA; + uint64_t pma_record_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_record_buffer_pma_va), + &pma_record_buffer_pma_va)); + ASSERT_EQ(0U, pma_record_buffer_pma_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_HANDLE; + uint32_t pma_record_buffer_handle; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_record_buffer_handle), + &pma_record_buffer_handle)); + ASSERT_EQ(0U, pma_record_buffer_handle); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_SIZE; + size_t pma_mem_bytes_buffer_size; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_mem_bytes_buffer_size), + &pma_mem_bytes_buffer_size)); + ASSERT_EQ(0U, pma_mem_bytes_buffer_size); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA; + void* pma_mem_bytes_buffer_cpu_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_mem_bytes_buffer_cpu_va), + &pma_mem_bytes_buffer_cpu_va)); + ASSERT_TRUE(NULL == pma_mem_bytes_buffer_cpu_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_PMA_VA; + uint64_t pma_mem_bytes_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_mem_bytes_buffer_pma_va), + &pma_mem_bytes_buffer_pma_va)); + ASSERT_EQ(0U, pma_mem_bytes_buffer_pma_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_HANDLE; + uint32_t pma_mem_bytes_buffer_handle; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(pma_mem_bytes_buffer_handle), + &pma_mem_bytes_buffer_handle)); + + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, SessionAllocNegative) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + + GetDevices(); + + // Should fail with invalid device handle. + dev.handle = 0xffffffff; + ASSERT_NE(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Should fail with invalid attribute. + session_attr = (nv_soc_hwpm_session_attribute)0xffffffff; + size_t dummy; + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(dummy), &dummy)); + + // Should fail with invalid buffer size. + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_DEVICE; + nv_soc_hwpm_device dev_test; + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(uint8_t), &dev_test)); + + // Should fail with invalid buffer ptr. + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, session_attr, sizeof(dev_test), NULL)); + + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + + +TEST_F(T241Tests, SessionReserveResources) +{ + uint32_t res_count, i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + nv_soc_hwpm_resource res[NV_SOC_HWPM_NUM_RESOURCES]; + nv_soc_hwpm_session session; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Get available resources. + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(res_count), &res_count)); + ASSERT_GT(res_count, 1U); + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*res) * res_count, res)); + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Reserve one resource. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_resources_fn(session, 1, res)); + + // Reserve all resources. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_all_resources_fn(session)); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, SessionReserveResourcesNegative) +{ + uint32_t res_count, i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_device_attribute dev_attr; + nv_soc_hwpm_resource res[NV_SOC_HWPM_NUM_RESOURCES]; + nv_soc_hwpm_session session; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Get available resources. + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_COUNT; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(res_count), &res_count)); + ASSERT_GT(res_count, 1U); + + dev_attr = NV_SOC_HWPM_DEVICE_ATTRIBUTE_RESOURCE_AVAILABLE_LIST; + + ASSERT_EQ(0, + api_table.nv_soc_hwpm_device_get_info_fn( + dev, dev_attr, sizeof(*res) * res_count, res)); + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Should fail with invalid resource count. + ASSERT_NE(0, api_table.nv_soc_hwpm_session_reserve_resources_fn(session, 0, res)); + + // Should fail with invalid resource buffer ptr. + ASSERT_NE(0, api_table.nv_soc_hwpm_session_reserve_resources_fn(session, 1, NULL)); + + // Should fail with invalid resource id. + res[0] = (nv_soc_hwpm_resource)0xffffffff; + ASSERT_NE(0, api_table.nv_soc_hwpm_session_reserve_resources_fn(session, 1, res)); + + // Should fail with invalid session handle. + res[0] = NV_SOC_HWPM_RESOURCE_PMA; // Use a guaranteed available resource. + uint64_t session_handle = session.handle; + session.handle = 0xffffffff; + ASSERT_NE(0, api_table.nv_soc_hwpm_session_reserve_resources_fn(session, 1, res)); + + // Should be successful now. + session.handle = session_handle; + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_resources_fn(session, 1, res)); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, SessionAllocPma) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Maximum CMA size for NVIDIA config is 1024MB. + static const uint32_t kNumSizes = 3; + static const uint32_t kSizes[kNumSizes] = {64 *1024, 256 * 1024, 768 * 1024}; + for (uint32_t j = 0; j < kNumSizes; j++) { + printf("PMA size: %u\n", kSizes[j]); + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Allocate PMA buffers. + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = kSizes[j] * 1024; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_BUFFER_ALLOCATED; + uint32_t pma_buffer_allocated; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_buffer_allocated), + &pma_buffer_allocated)); + ASSERT_EQ(1U, pma_buffer_allocated); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE; + size_t pma_record_buffer_size; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_size), + &pma_record_buffer_size)); + ASSERT_EQ(record_buffer_params.size, pma_record_buffer_size); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA; + void* pma_record_buffer_cpu_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_cpu_va), + &pma_record_buffer_cpu_va)); + ASSERT_TRUE(NULL != pma_record_buffer_cpu_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA; + uint64_t pma_record_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_pma_va), + &pma_record_buffer_pma_va)); + ASSERT_NE(0U, pma_record_buffer_pma_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_HANDLE; + uint32_t pma_record_buffer_handle; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_handle), + &pma_record_buffer_handle)); + ASSERT_NE(0U, pma_record_buffer_handle); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_SIZE; + size_t pma_mem_bytes_buffer_size; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_mem_bytes_buffer_size), + &pma_mem_bytes_buffer_size)); + ASSERT_EQ(4096U, pma_mem_bytes_buffer_size); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA; + void* pma_mem_bytes_buffer_cpu_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_mem_bytes_buffer_cpu_va), + &pma_mem_bytes_buffer_cpu_va)); + ASSERT_TRUE(NULL != pma_mem_bytes_buffer_cpu_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_PMA_VA; + uint64_t pma_mem_bytes_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_mem_bytes_buffer_pma_va), + &pma_mem_bytes_buffer_pma_va)); + // Kernel doesnt populate this for mem bytes buffer. + ASSERT_EQ(0U, pma_mem_bytes_buffer_pma_va); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_HANDLE; + uint32_t pma_mem_bytes_buffer_handle; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_mem_bytes_buffer_handle), + &pma_mem_bytes_buffer_handle)); + ASSERT_NE(0U, pma_mem_bytes_buffer_handle); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } + } +} + +TEST_F(T241Tests, SessionAllocPmaNegative) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = 1024 * 1024; + + ////// Multiple PMA allocations. ////// + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + // Allocate PMA buffers. + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + // Should fail second time. + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + + ////// PMA allocation after session start. ////// + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + // Start session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_start_fn(session)); + // Allocate PMA buffers. + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, SessionSetGetPmaState) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Allocate PMA buffers. + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = 1024 * 1024; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA; + void* pma_mem_bytes_buffer_cpu_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_mem_bytes_buffer_cpu_va), + &pma_mem_bytes_buffer_cpu_va)); + ASSERT_TRUE(NULL != pma_mem_bytes_buffer_cpu_va); + + *(uint64_t*)pma_mem_bytes_buffer_cpu_va = 0x1234; + + // Start session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_start_fn(session)); + + // Set and get PMA state. + nv_soc_hwpm_pma_channel_state_params param = {}; + param.in_check_overflow = 1; + param.in_mem_bump = 0; + param.in_stream_mem_bytes = 1; + param.in_read_mem_head = 1; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_set_get_pma_state_fn( + session, ¶m)); + EXPECT_EQ(0U, param.out_overflowed); + EXPECT_NE(0U, param.out_mem_head); + + EXPECT_EQ(0U, *(uint64_t*)pma_mem_bytes_buffer_cpu_va); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, SessionSetGetPmaStateNegative) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Should fail since session not started. + nv_soc_hwpm_pma_channel_state_params param = {}; + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_set_get_pma_state_fn( + session, ¶m)); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, DISABLED_SessionGetSetCredits) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Reserve all resources. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_all_resources_fn(session)); + + // Allocate PMA buffers. + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = 1024 * 1024; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + // Start session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_start_fn(session)); + + // Get HS credits. + uint32_t total_hs_credits; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_hs_credits_fn( + session, TEGRA_SOC_HWPM_GET_TYPE_TOTAL_HS_CREDITS, &total_hs_credits)); + EXPECT_GT(total_hs_credits, 0U); + + nv_soc_hwpm_config_hs_credit_params config_hs_credit_params = {}; + config_hs_credit_params.num_credits_per_chiplet = total_hs_credits; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_config_hs_credits_fn( + session, 1, &config_hs_credit_params)); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, DISABLED_SessionGetSetCreditsNegative) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Get HS credits. + uint32_t total_hs_credits = 0; + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_get_hs_credits_fn( + session, TEGRA_SOC_HWPM_GET_TYPE_TOTAL_HS_CREDITS, + &total_hs_credits)); + + nv_soc_hwpm_config_hs_credit_params config_hs_credit_params = {}; + config_hs_credit_params.num_credits_per_chiplet = total_hs_credits; + ASSERT_NE(0, + api_table.nv_soc_hwpm_session_config_hs_credits_fn( + session, 1, &config_hs_credit_params)); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +void T241Tests::TestRegopsRead(nv_soc_hwpm_session session, + uint64_t pma_record_buffer_pma_va, + size_t record_buffer_size) +{ + int all_reg_ops_passed = 0; + static const uint32_t kRegOpsCount = 4; + nv_soc_hwpm_reg_ops_params reg_ops_params[kRegOpsCount]; + + // profiler cap + reg_ops_params[0].in_offset = NV_PERF_PMASYS_PROFILER_CAPABILITIES; + reg_ops_params[0].in_out_val32 = 0; + reg_ops_params[0].in_mask32 = 0xFFFFFFFF; + reg_ops_params[0].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[0].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; // Initialize with def. value + + // stream buf base lo + reg_ops_params[1].in_offset = NV_PERF_PMASYS_CHANNEL_OUTBASE(0); + reg_ops_params[1].in_out_val32 = 0; + reg_ops_params[1].in_mask32 = 0xFFFFFFFF; + reg_ops_params[1].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[1].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + // stream buf base hi + reg_ops_params[2].in_offset = NV_PERF_PMASYS_CHANNEL_OUTBASEUPPER(0); + reg_ops_params[2].in_out_val32 = 0; + reg_ops_params[2].in_mask32 = 0xFFFFFFFF; + reg_ops_params[2].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[2].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + // stream buf size + reg_ops_params[3].in_offset = NV_PERF_PMASYS_CHANNEL_OUTSIZE(0); + reg_ops_params[3].in_out_val32 = 0; + reg_ops_params[3].in_mask32 = 0xFFFFFFFF; + reg_ops_params[3].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[3].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + // Read registers. + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + kRegOpsCount, + reg_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); + + // Verify the register value. + printf("profiler cap: 0x%x\n", reg_ops_params[0].in_out_val32); + ASSERT_EQ(0x1000101U, reg_ops_params[0].in_out_val32); + + printf("pma_record_buffer_pma_va: 0x%lx\n", pma_record_buffer_pma_va); + printf("stream buf base lo: 0x%x\n", reg_ops_params[1].in_out_val32); + ASSERT_EQ(pma_record_buffer_pma_va & 0xffffffffULL, reg_ops_params[1].in_out_val32); + + printf("stream buf base hi: 0x%x\n", reg_ops_params[2].in_out_val32); + ASSERT_EQ(pma_record_buffer_pma_va >> 32, reg_ops_params[2].in_out_val32); + + printf("stream buf size: 0x%x\n", reg_ops_params[3].in_out_val32); + ASSERT_EQ(record_buffer_size, reg_ops_params[3].in_out_val32); +} + +void T241Tests::TestRegopsWrite(nv_soc_hwpm_session session) +{ + uint32_t i; + int all_reg_ops_passed = 0; + static const uint32_t kRegOpsCount = 3; + uint32_t reg_values[kRegOpsCount] = {}; + nv_soc_hwpm_reg_ops_params reg_ops_params[kRegOpsCount]; + + // stream buf base lo + reg_ops_params[0].in_offset = NV_PERF_PMASYS_CHANNEL_OUTBASE(0); + reg_ops_params[0].in_out_val32 = 0; + reg_ops_params[0].in_mask32 = 0xFFFFFFFF; + reg_ops_params[0].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[0].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + // stream buf base hi + reg_ops_params[1].in_offset = NV_PERF_PMASYS_CHANNEL_OUTBASEUPPER(0); + reg_ops_params[1].in_out_val32 = 0; + reg_ops_params[1].in_mask32 = 0xFFFFFFFF; + reg_ops_params[1].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[1].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + // stream buf size + reg_ops_params[2].in_offset = NV_PERF_PMASYS_CHANNEL_OUTSIZE(0); + reg_ops_params[2].in_out_val32 = 0; + reg_ops_params[2].in_mask32 = 0xFFFFFFFF; + reg_ops_params[2].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params[2].out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + // Read registers. + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + kRegOpsCount, + reg_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); + + for (i = 0; i < kRegOpsCount; i++) { + reg_values[i] = reg_ops_params[i].in_out_val32; + + // Change command to write. + reg_ops_params[i].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_WRITE32; + // Set an arbitrary value. + // Some of the registers are writable from bit 5 onwards. + reg_ops_params[i].in_out_val32 = (i + 1) * 32; + } + + // Write registers. + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + kRegOpsCount, + reg_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); + + // Set ops command to read. + for (i = 0; i < kRegOpsCount; i++) { + reg_ops_params[i].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + } + + // Read registers again. + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + kRegOpsCount, + reg_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); + + for (i = 0; i < kRegOpsCount; i++) { + EXPECT_EQ((i + 1) * 32, reg_ops_params[i].in_out_val32); + + // Change command to write. + reg_ops_params[i].in_cmd = NV_SOC_HWPM_REG_OPS_CMD_WRITE32; + // Restore original value. + reg_ops_params[i].in_out_val32 = reg_values[i]; + } + + // Write registers again. + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + kRegOpsCount, + reg_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); +} + +TEST_F(T241Tests, SessionRegOps) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Reserve all resources. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_all_resources_fn(session)); + + // Allocate PMA buffers. + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = 1024 * 1024; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA; + uint64_t pma_record_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_pma_va), + &pma_record_buffer_pma_va)); + ASSERT_NE(0U, pma_record_buffer_pma_va); + + // Start session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_start_fn(session)); + + TestRegopsRead(session, pma_record_buffer_pma_va, record_buffer_params.size); + TestRegopsWrite(session); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +void T241Tests::RegOpWrite32( + nv_soc_hwpm_session session, uint64_t address, uint32_t value, uint32_t mask) +{ + nv_soc_hwpm_reg_ops_params reg_ops_params = {}; + reg_ops_params.in_offset = address; + reg_ops_params.in_out_val32 = value; + reg_ops_params.in_mask32 = mask; + reg_ops_params.in_cmd = NV_SOC_HWPM_REG_OPS_CMD_WRITE32; + + int all_reg_ops_passed = 0; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + 1, + ®_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); + EXPECT_EQ(NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS, reg_ops_params.out_status); +} + +void T241Tests::RegOpRead32( + nv_soc_hwpm_session session, uint64_t address, uint32_t *value) +{ + nv_soc_hwpm_reg_ops_params reg_ops_params = {}; + reg_ops_params.in_offset = address; + reg_ops_params.in_out_val32 = 0; + reg_ops_params.in_mask32 = 0xFFFFFFFF; + reg_ops_params.in_cmd = NV_SOC_HWPM_REG_OPS_CMD_READ32; + reg_ops_params.out_status = NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS; + + int all_reg_ops_passed = 0; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_regops_fn( + session, + 1, + ®_ops_params, + NV_SOC_HWPM_REG_OPS_VALIDATION_MODE_CONTINUE_ON_ERROR, + &all_reg_ops_passed)); + EXPECT_EQ(1, all_reg_ops_passed); + EXPECT_EQ(NV_SOC_HWPM_REG_OPS_STATUS_SUCCESS, reg_ops_params.out_status); + + *value = reg_ops_params.in_out_val32; +} + +void T241Tests::SetupPma(nv_soc_hwpm_session session, const PmaConfigurationParams& params) +{ + // Taken from Perfkit\Shared\Perfkit\Tests\Emulation\SOC\Tests\SocSignalTest\Src\TH500HwpmHal.cpp + + const uint32_t enable_hs = NV_PERF_PMMSYSROUTER_GLOBAL_SECURE_CONFIG_HS_STREAM_ENABLE; + const uint32_t config_credits = 0xFF; // FIXME: credit count must be queried! + RegOpWrite32(session, NV_PERF_PMMSYSROUTER_GLOBAL_SECURE_CONFIG, enable_hs, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYSROUTER_USER_CHANNEL_CONFIG_SECURE(0), config_credits, 0xFFFFFFFF); + + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONFIG_TESLA_MODE0(0), 0xFFFFFFFF, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONFIG_TESLA_MODE1(0), 0xFFFFFFFF, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONFIG_MIXED_MODE0(0), 0x00000000, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONFIG_MIXED_MODE1(0), 0x00000000, 0xFFFFFFFF); + + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_MASK_SECURE0(0), 0xFFFFFFFF, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_MASK_SECURE1(0), 0xFFFFFFFF, 0xFFFFFFFF); + + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_START_MASK0(0), 0xFFFFFFFF, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_START_MASK1(0), 0xFFFFFFFF, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_STOP_MASK0(0), 0xFFFFFFFF, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_STOP_MASK1(0), 0xFFFFFFFF, 0xFFFFFFFF); + + if (params.enable_streaming) { + EnablePmaStreaming(session, params); + } + + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_STATUS0(0), 0x00000000, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_STATUS1(0), 0x00000000, 0xFFFFFFFF); +} + +void T241Tests::EnablePmaStreaming(nv_soc_hwpm_session session, const PmaConfigurationParams& params) +{ + uint32_t keep_latest_config = + (params.keep_latest) ? + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_KEEP_LATEST_ENABLE : + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_KEEP_LATEST_DISABLE; + uint32_t channel_config_user = + 0 + | REG32_WR( + 0, + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_STREAM, + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_STREAM_ENABLE) + | REG32_WR( + 0, + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_KEEP_LATEST, + keep_latest_config) + | REG32_WR( + 0, + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_COALESCE_TIMEOUT_CYCLES, + NV_PERF_PMASYS_CHANNEL_CONFIG_USER_COALESCE_TIMEOUT_CYCLES_1K); + + RegOpWrite32( + session, + NV_PERF_PMASYS_CHANNEL_CONFIG_USER(0), + channel_config_user, + 0xFFFFFFFF); +} + +// FIXME: Import pm_signals.h +#define NV_PERF_PMMSYS_SYS0_SIGVAL_ZERO 0 +#define NV_PERF_PMMSYS_SYS0_SIGVAL_ONE 1 +#define NV_PERF_PMMSYS_SYS0_SIGVAL_PMA_TRIGGER 2 + +// armss.h +#define MC_MCC_CTL_PERFMUX_0 0x0000000000008914 +#define MC_MCC_CTL_PERFMUX_0_MCC_CTL_PERFMUX_EN 31:31 +#define MC_MCC_CTL_PERFMUX_0_MCC_CTL_PERFMUX_SEL 7:0 +#define NV_ADDRESS_MAP_MC0_BASE 0x0000000004040000 + +void T241Tests::SetupPmm(nv_soc_hwpm_session session, const PmmConfigurationParams& params) +{ + const uint32_t perfmon_idx = params.perfmon_idx; + uint32_t mode; + + if (params.mode == PmmConfigurationParams::Mode::MODE_C) { + // Not supporting mode C testing for now. + ASSERT_TRUE(false); + return; + } + + // Enable command packet encoding. Seems like this is needed so perfmon + // can process trigger command from PMA. + const uint32_t secure_config = + REG32_WR( + 0, + NV_PERF_PMMSYS_SECURE_CONFIG_COMMAND_PKT_DECODER, + NV_PERF_PMMSYS_SECURE_CONFIG_COMMAND_PKT_DECODER_ENABLE); + RegOpWrite32( + session, NV_PERF_PMMSYS_SECURE_CONFIG(perfmon_idx), secure_config, 0xFFFFFFFF); + + // Set ENGINE_SEL to pma_trigger + RegOpWrite32( + session, + NV_PERF_PMMSYS_ENGINE_SEL(perfmon_idx), + NV_PERF_PMMSYS_SYS0_SIGVAL_PMA_TRIGGER, + 0xFFFFFFFF); + + // Enable GCM, local triggering. + uint32_t control_b = + REG32_WR( + 0, + NV_PERF_PMMSYS_CONTROLB_COUNTING_MODE, + NV_PERF_PMMSYS_CONTROLB_COUNTING_MODE_GENERAL); + if (params.enable_local_triggering) + { + control_b |= REG32_WR( + 0, + NV_PERF_PMMSYS_CONTROLB_PMLOCALTRIGA_EN, + NV_PERF_PMMSYS_CONTROLB_PMLOCALTRIGA_EN_ENABLE); + control_b |= REG32_WR( + 0, + NV_PERF_PMMSYS_CONTROLB_PMLOCALTRIGB_EN, + NV_PERF_PMMSYS_CONTROLB_PMLOCALTRIGB_EN_ENABLE); + } + RegOpWrite32(session, NV_PERF_PMMSYS_CONTROLB(perfmon_idx), control_b, 0xFFFFFFFF); + + // Set perfmon id + const uint32_t soc_perfmon_prefix = 0x700; // TODO: Temporary identifier + const uint32_t perfmon_id = perfmon_idx | soc_perfmon_prefix; + RegOpWrite32(session, NV_PERF_PMMSYS_PERFMONID(perfmon_idx), perfmon_id, 0xFFFFFFFF); + + // Reset CYA control + RegOpWrite32(session, NV_PERF_PMMSYS_CLAMP_CYA_CONTROL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_WBSKEW0(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_WBSKEW1(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_WBSKEW2(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_WBSKEW_CHAIN0(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_WBSKEW_CHAIN1(perfmon_idx), 0x0, 0xFFFFFFFF); + + // Do not use truth tables + RegOpWrite32(session, NV_PERF_PMMSYS_TRIG0_OP(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_TRIG1_OP(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_EVENT_OP(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_SAMPLE_OP(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_FT0_INPUTSEL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_FT1_INPUTSEL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_FT2_INPUTSEL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_FT3_INPUTSEL(perfmon_idx), 0x0, 0xFFFFFFFF); + + if (params.mode == PmmConfigurationParams::Mode::MODE_B) { + mode = NV_PERF_PMMSYS_CONTROL_MODE_B; + + // Configure counters by INC and assuming the signals are at least 16-bits. + // This will increment the counter by 0xE on each cycle + const bool use_pop_count = false; + uint32_t counter_mode = + (use_pop_count) ? + NV_PERF_PMMSYS_CNTR0_INC_INCFN_POPCOUNT : + NV_PERF_PMMSYS_CNTR0_INC_INCFN_NOP; + const uint32_t counter0_inc = 0 + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_INCMASK, 0xF) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_INCBASE, 2) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_FUNCSEL, NV_PERF_PMMSYS_CNTR0_INC_FUNCSEL_TRUE) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_INCFN, counter_mode); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR0_INC(perfmon_idx), counter0_inc, 0xFFFFFFFF); + const uint32_t counter1_inc = 0 + | REG32_WR(0, NV_PERF_PMMSYS_CNTR1_INC_INCMASK, 0xF) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR1_INC_INCBASE, 6) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR1_INC_FUNCSEL, NV_PERF_PMMSYS_CNTR1_INC_FUNCSEL_TRUE) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR1_INC_INCFN, counter_mode); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR1_INC(perfmon_idx), counter1_inc, 0xFFFFFFFF); + const uint32_t counter2_inc = 0 + | REG32_WR(0, NV_PERF_PMMSYS_CNTR2_INC_INCMASK, 0xF) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR2_INC_INCBASE, 10) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR2_INC_FUNCSEL, NV_PERF_PMMSYS_CNTR2_INC_FUNCSEL_TRUE) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR2_INC_INCFN, counter_mode); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR2_INC(perfmon_idx), counter2_inc, 0xFFFFFFFF); + const uint32_t counter3_inc = 0 + | REG32_WR(0, NV_PERF_PMMSYS_CNTR3_INC_INCMASK, 0xF) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR3_INC_INCBASE, 14) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR3_INC_FUNCSEL, NV_PERF_PMMSYS_CNTR3_INC_FUNCSEL_TRUE) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR3_INC_INCFN, counter_mode); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR3_INC(perfmon_idx), counter3_inc, 0xFFFFFFFF); + } else if (params.mode == PmmConfigurationParams::Mode::MODE_E) { + mode = NV_PERF_PMMSYS_CONTROL_MODE_E; + + // Reset RAWCNT, except if they are to be primed for overflow (useful in Mode E) + uint32_t rawCntVal = params.enable_overflow_priming ? 0xEFFFFF00 : 0; + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT0(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT1(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT2(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT3(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT4(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT5(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT6(perfmon_idx), rawCntVal, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_RAWCNT7(perfmon_idx), rawCntVal, 0xFFFFFFFF); + + // Configure counters by INC + const uint32_t signal_sel = (params.collect_one) ? 1 : 0; + const uint32_t counter_inc = 0 + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_INCMASK, 0x1) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_INCBASE, signal_sel) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_FUNCSEL, NV_PERF_PMMSYS_CNTR0_INC_FUNCSEL_TRUE) + | REG32_WR(0, NV_PERF_PMMSYS_CNTR0_INC_INCFN, NV_PERF_PMMSYS_CNTR0_INC_INCFN_NOP); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR0_INC(perfmon_idx), counter_inc, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR1_INC(perfmon_idx), counter_inc, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR2_INC(perfmon_idx), counter_inc, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_CNTR3_INC(perfmon_idx), counter_inc, 0xFFFFFFFF); + } + + // Finally, program CONTROL register + uint32_t pmm_control = 0 + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_MODE, mode) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_ADDTOCTR, NV_PERF_PMMSYS_CONTROL_ADDTOCTR_INCR) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_CLEAR_EVENT_ONCE, NV_PERF_PMMSYS_CONTROL_CLEAR_EVENT_ONCE_DISABLE) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_EVENT_SYNC_MODE, NV_PERF_PMMSYS_CONTROL_EVENT_SYNC_MODE_LEVEL) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_FLAG_SYNC_MODE, NV_PERF_PMMSYS_CONTROL_FLAG_SYNC_MODE_LEVEL) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_MODEC_4X16_4X32, NV_PERF_PMMSYS_CONTROL_MODEC_4X16_4X32_DISABLE) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_CTXSW_MODE, NV_PERF_PMMSYS_CONTROL_CTXSW_MODE_DISABLED) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_TIMEBASE_CYCLES, NV_PERF_PMMSYS_CONTROL_TIMEBASE_CYCLES_DISABLED) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_SHADOW_STATE, NV_PERF_PMMSYS_CONTROL_SHADOW_STATE_INVALID) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_DRIVE_DEBUG_PORT, NV_PERF_PMMSYS_CONTROL_DRIVE_DEBUG_PORT_NORMAL) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_CTXSW_TIMER, NV_PERF_PMMSYS_CONTROL_CTXSW_TIMER_ENABLE) + | REG32_WR(0, NV_PERF_PMMSYS_CONTROL_WBMASK, NV_PERF_PMMSYS_CONTROL_WBMASK_DISABLE); + RegOpWrite32(session, NV_PERF_PMMSYS_CONTROL(perfmon_idx), pmm_control, 0xFFFFFFFF); +} + +void T241Tests::SetupWatchbus(nv_soc_hwpm_session session, const PmmConfigurationParams& params) +{ + const uint32_t perfmon_idx = params.perfmon_idx; + + if (params.mode == PmmConfigurationParams::Mode::MODE_C) { + // Not supporting mode C testing for now. + ASSERT_TRUE(false); + return; + } else if (params.mode == PmmConfigurationParams::Mode::MODE_B) { + // PMA perfmux. + // SIGNAL(name/width/domain/instancetype):--/sys0.pma2pm_0_static_pattern_11111111_32/32/sys0/sys/ + // ROUTE(index/registers):--/0/2/ + // DESTINATION(lsb_bitposition/watchbus_readback_index/watchbus_readback_lsb):--/22/0/0/ + // Source: //hw/tools/perfalyze/chips/th500/pml_files/soc_perf/pm_programming_guide.txt + const uint32_t channel_perfmux_sel = 0 + | REG32_WR( + 0, + NV_PERF_PMASYS_PERFMUX_CONFIG_SECURE_PMCONTROL_0_GRP, + NV_PERF_PMASYS_PERFMUX_CONFIG_SECURE_PMCONTROL_0_GRP_STATIC_PATTERN_EEEEEEEE_32_GROUP) + | REG32_WR(0, NV_PERF_PMASYS_PERFMUX_CONFIG_SECURE_PMCONTROL_ENABLE, 0x1); + RegOpWrite32(session, NV_PERF_PMASYS_PERFMUX_CONFIG_SECURE, channel_perfmux_sel, 0xFFFFFFFF); + + // Map 16-bit signals onto reduced watchbus by SEL + // See above comment, the start of the signal is targeted to watchbus bit 22. + RegOpWrite32(session, NV_PERF_PMMSYS_EVENT_SEL(perfmon_idx), 0x19181716, 0xFFFFFFFF); // 'E' from EEEE + RegOpWrite32(session, NV_PERF_PMMSYS_TRIG0_SEL(perfmon_idx), 0x1D1C1B1A, 0xFFFFFFFF); // 'E' from EEEE + RegOpWrite32(session, NV_PERF_PMMSYS_TRIG1_SEL(perfmon_idx), 0x21201F1E, 0xFFFFFFFF); // 'E' from EEEE + RegOpWrite32(session, NV_PERF_PMMSYS_SAMPLE_SEL(perfmon_idx), 0x25242322, 0xFFFFFFFF); // 'E' from EEEE + } else if (params.mode == PmmConfigurationParams::Mode::MODE_E) { + RegOpWrite32(session, NV_PERF_PMMSYS_TRIG0_SEL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_TRIG1_SEL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_EVENT_SEL(perfmon_idx), 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_SAMPLE_SEL(perfmon_idx), 0x0, 0xFFFFFFFF); + } +} + +void T241Tests::TeardownPma(nv_soc_hwpm_session session) +{ + // Clear NV_PERF_PMASYS_CHANNEL_STATUS_MEMBUF_STATUS + const uint32_t pma_control_user = 0 + | REG32_WR( + 0, + NV_PERF_PMASYS_CHANNEL_CONTROL_USER_MEMBUF_CLEAR_STATUS, + NV_PERF_PMASYS_CHANNEL_CONTROL_USER_MEMBUF_CLEAR_STATUS_DOIT); + RegOpWrite32(session, NV_PERF_PMASYS_CHANNEL_CONTROL_USER(0), pma_control_user, 0xFFFFFFFF); + + RegOpWrite32(session, NV_PERF_PMMSYSROUTER_GLOBAL_CNTRL, 0x0, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMASYS_CHANNEL_CONTROL_USER(0), 0x0, 0xFFFFFFFF); +} + +void T241Tests::TeardownPmm( + nv_soc_hwpm_session session, const PmmConfigurationParams& params) +{ + const uint32_t perfmon_idx = params.perfmon_idx; + + // Disable PMMs and reset ENGINE_SELs + const uint32_t pmm_engine_sel = 0 + | REG32_WR(0, NV_PERF_PMMSYS_ENGINE_SEL_START, 0xFF); // ZERO + RegOpWrite32(session, NV_PERF_PMMSYS_ENGINE_SEL(perfmon_idx), pmm_engine_sel, 0xFFFFFFFF); + RegOpWrite32(session, NV_PERF_PMMSYS_CONTROL(perfmon_idx), 0x0, 0xFFFFFFFF); + + // Disable command packet encoding. Seems like this is needed so perfmon + // can process trigger command from PMA. + const uint32_t secure_config = + REG32_WR( + 0, + NV_PERF_PMMSYS_SECURE_CONFIG_COMMAND_PKT_DECODER, + NV_PERF_PMMSYS_SECURE_CONFIG_COMMAND_PKT_DECODER_DISABLE); + RegOpWrite32(session, NV_PERF_PMMSYS_SECURE_CONFIG(perfmon_idx), secure_config, 0xFFFFFFFF); +} + +void T241Tests::TeardownPerfmux(nv_soc_hwpm_session session) +{ + RegOpWrite32(session, NV_PERF_PMASYS_PERFMUX_CONFIG_SECURE, 0, 0xFFFFFFFF); +} + +void T241Tests::IssuePmaTrigger(nv_soc_hwpm_session session) +{ + // This will issue PMA trigger to the perfmon. + // The perfmon then will snapshot the counter value into shadow regiters + uint32_t pma_global_trigger = 0 + | REG32_WR( + 0, + NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONTROL_GLOBAL_MANUAL_START, + NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONTROL_GLOBAL_MANUAL_START_PULSE); + RegOpWrite32(session, NV_PERF_PMASYS_COMMAND_SLICE_TRIGGER_CONTROL(0), pma_global_trigger, 0xFFFFFFFF); +} + +void T241Tests::HarvestCounters( + nv_soc_hwpm_session session, const PmmConfigurationParams& params, const uint32_t sig_val[4]) +{ + const uint32_t perfmon_idx = params.perfmon_idx; + + uint32_t read_value = 0; + + RegOpRead32(session, NV_PERF_PMMSYS_EVENTCNT(perfmon_idx), &read_value); + uint32_t event = read_value; + + RegOpRead32(session, NV_PERF_PMMSYS_TRIGGERCNT(perfmon_idx), &read_value); + uint32_t trig0 = read_value; + + RegOpRead32(session, NV_PERF_PMMSYS_THRESHCNT(perfmon_idx), &read_value); + uint32_t trig1 = read_value; + + RegOpRead32(session, NV_PERF_PMMSYS_SAMPLECNT(perfmon_idx), &read_value); + uint32_t sample = read_value; + + RegOpRead32(session, NV_PERF_PMMSYS_ELAPSED(perfmon_idx), &read_value); + uint32_t elapsed = read_value; + + printf("Event: %u, Trig0: %u, Trig1: %u, Sample: %u, Elapsed: %u\n", + event, trig0, trig1, sample, elapsed); + + ASSERT_GT(sig_val[0], 0U); + ASSERT_EQ((uint64_t)sig_val[0] * elapsed, event); + + ASSERT_GT(sig_val[1], 0U); + ASSERT_EQ((uint64_t)sig_val[1] * elapsed, trig0); + + ASSERT_GT(sig_val[2], 0U); + ASSERT_EQ((uint64_t)sig_val[2] * elapsed, trig1); + + ASSERT_GT(sig_val[3], 0U); + ASSERT_EQ((uint64_t)sig_val[3] * elapsed, sample); +} + + +TEST_F(T241Tests, SessionSignalTestPmaPerfmux) +{ + uint32_t i; + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Reserve all resources. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_all_resources_fn(session)); + + // Allocate PMA buffers. + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = 1024 * 1024; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA; + uint64_t pma_record_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_pma_va), + &pma_record_buffer_pma_va)); + ASSERT_NE(0U, pma_record_buffer_pma_va); + + // Start session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_start_fn(session)); + + // From //hw/tools/perfalyze/chips/th500/pml_files/soc_perf/pm_programming_guide.txt + // Perfmon domain offset sys0 + const uint32_t perfmon_idx = 30; + + PmaConfigurationParams pma_params; + SetupPma(session, pma_params); + + PmmConfigurationParams pmm_params; + pmm_params.perfmon_idx = perfmon_idx; + pmm_params.mode = PmmConfigurationParams::Mode::MODE_B; + SetupPmm(session, pmm_params); + SetupWatchbus(session, pmm_params); + + // http://nvbugs/3091420: Ignore the first snapshot as it could + // contain some undesired noises between perfmux and perfmon. + IssuePmaTrigger(session); + const uint32_t sig_val[4] = { 0xE, 0xE, 0xE, 0xE }; + for (int i = 0; i < 5; i++) { + IssuePmaTrigger(session); + HarvestCounters(session, pmm_params, sig_val); + } + + TeardownPerfmux(session); + TeardownPmm(session, pmm_params); + TeardownPma(session); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} + +TEST_F(T241Tests, SessionStreamoutTestModeEBasicStreaming) +{ + nv_soc_hwpm_device dev; + nv_soc_hwpm_session session; + nv_soc_hwpm_session_attribute session_attr; + uint32_t i, num_mem_bytes, num_triggers, num_perfmons; + + // From //hw/tools/perfalyze/chips/th500/pml_files/soc_perf/pm_programming_guide.txt + // Perfmon domain offset sys0 + const uint32_t perfmon_idx = 30; + num_perfmons = 1; + + GetDevices(); + + for (i = 0; i < t241_dev_count; i++) { + printf("Device %d:\n", i); + dev = t241_dev[i]; + + // Allocate session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_alloc_fn(dev, &session)); + + // Reserve all resources. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_reserve_all_resources_fn(session)); + + // Allocate PMA buffers. + nv_soc_hwpm_pma_buffer_params record_buffer_params = {}; + record_buffer_params.size = 100 * 1024 * 1024; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_alloc_pma_fn( + session, &record_buffer_params)); + + session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_PMA_VA; + uint64_t pma_record_buffer_pma_va; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_get_info_fn( + session, + session_attr, + sizeof(pma_record_buffer_pma_va), + &pma_record_buffer_pma_va)); + ASSERT_NE(0U, pma_record_buffer_pma_va); + + // Start session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_start_fn(session)); + + // Flush leftover records at the beginning of each subtest + nv_soc_hwpm_pma_channel_state_params set_get_state_param = {}; + set_get_state_param.in_mem_bump = 0; + set_get_state_param.in_stream_mem_bytes = 1; + set_get_state_param.in_check_overflow = 1; + set_get_state_param.in_read_mem_head = 1; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_set_get_pma_state_fn( + session, &set_get_state_param)); + SocModeEBuffer soc_mode_e_buffer(api_table, session); + soc_mode_e_buffer.Initialize(); + soc_mode_e_buffer.SetRecordFormat(RecordFormatType::ModeE); + num_mem_bytes = soc_mode_e_buffer.GetMemBytes(); + soc_mode_e_buffer.FlushRecordsInBuffer(num_mem_bytes); + + PmaConfigurationParams pma_params; + pma_params.enable_streaming = true; + SetupPma(session, pma_params); + + PmmConfigurationParams pmm_params; + pmm_params.perfmon_idx = perfmon_idx; + pmm_params.mode = PmmConfigurationParams::Mode::MODE_E; + pmm_params.collect_one = true; + SetupPmm(session, pmm_params); + SetupWatchbus(session, pmm_params); + + num_triggers = 5; + usleep(1000000); // 1 second + for (i = 0; i < num_triggers; i++) { + IssuePmaTrigger(session); + } + usleep(100000); // 100 milisecond + + TeardownPerfmux(session); + TeardownPmm(session, pmm_params); + TeardownPma(session); + + ASSERT_EQ(num_triggers * num_perfmons, soc_mode_e_buffer.GetNumValidRecords()); + ASSERT_EQ(num_triggers * num_perfmons, soc_mode_e_buffer.GetNumSamples()); + ASSERT_EQ(num_perfmons, soc_mode_e_buffer.GetNumUniquePerfmonID()); + ASSERT_GT(soc_mode_e_buffer.GetCounterValueSum(), 0U); + ASSERT_EQ(0, soc_mode_e_buffer.IsZeroTimestampDetected()); + ASSERT_EQ(0, soc_mode_e_buffer.IsReversedTriggerCountDetected()); + + // Stream & verify membytes + set_get_state_param.in_mem_bump = 0; + set_get_state_param.in_stream_mem_bytes = 1; + set_get_state_param.in_check_overflow = 1; + set_get_state_param.in_read_mem_head = 1; + ASSERT_EQ(0, + api_table.nv_soc_hwpm_session_set_get_pma_state_fn( + session, &set_get_state_param)); + num_mem_bytes = soc_mode_e_buffer.GetMemBytes(); + printf("num_mem_bytes: %u\n", num_mem_bytes); + ASSERT_GT(num_mem_bytes, 0U); + ASSERT_EQ(num_mem_bytes, + soc_mode_e_buffer.GetNumValidRecords() * sizeof(ModeERecordRaw)); + + soc_mode_e_buffer.PrintRecords(100); + + printf("================ BEGIN BUFFER DUMP ================\n"); + soc_mode_e_buffer.DumpBuffer(); + printf("================ END BUFFER DUMP ================\n"); + + // Free session. + ASSERT_EQ(0, api_table.nv_soc_hwpm_session_free_fn(session)); + } +} \ No newline at end of file diff --git a/libnvsochwpm/test/t241_test.h b/libnvsochwpm/test/t241_test.h new file mode 100644 index 0000000..10b33d0 --- /dev/null +++ b/libnvsochwpm/test/t241_test.h @@ -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 \ No newline at end of file diff --git a/libnvsochwpm/tmake/lib/Makefile.interface.tmk b/libnvsochwpm/tmake/lib/Makefile.interface.tmk new file mode 100644 index 0000000..69aa9f2 --- /dev/null +++ b/libnvsochwpm/tmake/lib/Makefile.interface.tmk @@ -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 \ No newline at end of file diff --git a/libnvsochwpm/tmake/lib/Makefile.tmk b/libnvsochwpm/tmake/lib/Makefile.tmk new file mode 100644 index 0000000..dd5d144 --- /dev/null +++ b/libnvsochwpm/tmake/lib/Makefile.tmk @@ -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: diff --git a/libnvsochwpm/tmake/test/Makefile.tmk b/libnvsochwpm/tmake/test/Makefile.tmk new file mode 100644 index 0000000..f2fdfcd --- /dev/null +++ b/libnvsochwpm/tmake/test/Makefile.tmk @@ -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 \ No newline at end of file