blob: dea704c9cf40cf72aea8679fcf25e8d73b10c9bf [file] [log] [blame]
/*
* SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates
* <open-source-office@arm.com> SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ethosu_profiler.h"
#include "ethosu_cpu_cache.h"
#include "log_macros.h"
#include <string.h>
extern struct ethosu_driver ethosu_drv; /* Default Arm Ethos-U NPU device driver object */
static ethosu_pmu_counters s_npu_counters; /* NPU counter local instance */
static uint32_t s_evt_mask = 0; /* PMU event mask */
static const char* unit_beats = "beats";
static const char* unit_cycles = "cycles";
/**
* @brief Gets the npu counter instance to be used.
* @return Pointer to the npu counter instance.
*/
static ethosu_pmu_counters* get_counter_instance(void)
{
return &s_npu_counters;
}
/**
* @brief Gets the enabled event mask from the PMU driver.
* @return Event mask as an unsigned 32 bit integer.
*/
static uint32_t get_event_mask(void)
{
return s_evt_mask;
}
/**
* @brief Sets the enabled event mask for the PMU driver.
* @param[in] mask event mask as an unsigned 32 bit integer.
* @return none.
*/
static void set_event_mask(uint32_t mask)
{
s_evt_mask = mask;
}
/**
* @brief Checks if the counter has overflown.
* @param pmu_counter_mask Mask for the event counter.
* @return true if overflow is detected, false otherwise.
*/
static bool counter_overflow(uint32_t pmu_counter_mask)
{
/* Check for overflow: The idle counter is 32 bit while the
total cycle count is 64 bit. */
const uint32_t overflow_status = ETHOSU_PMU_Get_CNTR_OVS(&ethosu_drv);
return pmu_counter_mask & overflow_status ? true : false;
}
void ethosu_pmu_init(void)
{
uint32_t i = 0;
uint32_t evt_mask = ETHOSU_PMU_CCNT_Msk;
ethosu_pmu_counters* counters = get_counter_instance();
memset(counters, 0, sizeof(*counters));
/* Total counters = event counters + derived counters + total cycle count */
counters->num_total_counters = ETHOSU_PROFILER_NUM_COUNTERS;
#if ETHOSU_PMU_NCOUNTERS >= 4
counters->npu_evt_counters[0].event_type = ETHOSU_PMU_NPU_ACTIVE;
counters->npu_evt_counters[0].event_mask = ETHOSU_PMU_CNT1_Msk;
counters->npu_evt_counters[0].name = "NPU ACTIVE";
counters->npu_evt_counters[0].unit = unit_cycles;
counters->npu_evt_counters[1].event_type = ETHOSU_PMU_AXI0_RD_DATA_BEAT_RECEIVED;
counters->npu_evt_counters[1].event_mask = ETHOSU_PMU_CNT2_Msk;
counters->npu_evt_counters[1].name = "NPU AXI0_RD_DATA_BEAT_RECEIVED";
counters->npu_evt_counters[1].unit = unit_beats;
counters->npu_evt_counters[2].event_type = ETHOSU_PMU_AXI0_WR_DATA_BEAT_WRITTEN;
counters->npu_evt_counters[2].event_mask = ETHOSU_PMU_CNT3_Msk;
counters->npu_evt_counters[2].name = "NPU AXI0_WR_DATA_BEAT_WRITTEN";
counters->npu_evt_counters[2].unit = unit_beats;
counters->npu_evt_counters[3].event_type = ETHOSU_PMU_AXI1_RD_DATA_BEAT_RECEIVED;
counters->npu_evt_counters[3].event_mask = ETHOSU_PMU_CNT4_Msk;
counters->npu_evt_counters[3].name = "NPU AXI1_RD_DATA_BEAT_RECEIVED";
counters->npu_evt_counters[3].unit = unit_beats;
#else /* ETHOSU_PMU_NCOUNTERS >= 4 */
#error "NPU PMU expects a minimum of 4 available event triggered counters!"
#endif /* ETHOSU_PMU_NCOUNTERS >= 4 */
#if ETHOSU_DERIVED_NCOUNTERS >= 1
counters->npu_derived_counters[0].name = "NPU IDLE";
counters->npu_derived_counters[0].unit = unit_cycles;
#endif /* ETHOSU_DERIVED_NCOUNTERS >= 1 */
for (i = 0; i < ETHOSU_PMU_NCOUNTERS; ++i) {
ETHOSU_PMU_Set_EVTYPER(&ethosu_drv, i, counters->npu_evt_counters[i].event_type);
evt_mask |= counters->npu_evt_counters[i].event_mask;
}
set_event_mask(evt_mask);
/* Reset overflow status. */
ETHOSU_PMU_Set_CNTR_OVS(&ethosu_drv, get_event_mask());
/* Enable PMU. */
ETHOSU_PMU_Enable(&ethosu_drv);
/* Enable counters for cycle and event counters. */
ETHOSU_PMU_CNTR_Disable(&ethosu_drv, get_event_mask());
ethosu_pmu_reset_counters();
}
/**
* @brief Resets the Arm Ethos-U NPU PMU counters.
*/
void ethosu_pmu_reset_counters(void)
{
/* Reset all cycle and event counters. */
ETHOSU_PMU_CYCCNT_Reset(&ethosu_drv);
ETHOSU_PMU_EVCNTR_ALL_Reset(&ethosu_drv);
}
/**
* @brief Get the Arm Ethos-U NPU PMU counters
* @return ethosu_pmu_counters
*/
ethosu_pmu_counters ethosu_get_pmu_counters(void)
{
ethosu_pmu_counters* counters = get_counter_instance();
uint32_t i = 0;
/* Event counters */
for (i = 0; i < ETHOSU_PMU_NCOUNTERS; ++i) {
if (counter_overflow(counters->npu_evt_counters[i].event_mask)) {
warn("Counter overflow detected for %s.\n", counters->npu_evt_counters[i].name);
}
counters->npu_evt_counters[i].counter_value = ETHOSU_PMU_Get_EVCNTR(&ethosu_drv, i);
}
/* Total cycle count */
counters->npu_total_ccnt = ETHOSU_PMU_Get_CCNTR(&ethosu_drv);
/* Derived counters */
#if ETHOSU_DERIVED_NCOUNTERS >= 1
if (counters->npu_evt_counters[0].event_type == ETHOSU_PMU_NPU_ACTIVE) {
/* Compute the idle count */
counters->npu_derived_counters[0].counter_value =
counters->npu_total_ccnt - counters->npu_evt_counters[0].counter_value;
}
#endif /* ETHOSU_DERIVED_NCOUNTERS >= 1 */
return *counters;
}
void ethosu_inference_begin(struct ethosu_driver* drv, void* userArg)
{
UNUSED(userArg);
ethosu_clear_cache_states();
ETHOSU_PMU_CNTR_Disable(drv, get_event_mask());
ETHOSU_PMU_CNTR_Enable(drv, get_event_mask());
}
void ethosu_inference_end(struct ethosu_driver* drv, void* userArg)
{
UNUSED(userArg);
ETHOSU_PMU_CNTR_Disable(drv, get_event_mask());
}