| /* |
| * Copyright (c) 2022 Arm Limited. All rights reserved. |
| * 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 "log_macros.h" |
| |
| #include <string.h> |
| |
| extern struct ethosu_driver ethosu_drv; /* Default Arm Ethos-U NPU device driver object */ |
| static ethosu_pmu_counters npu_counters; /* NPU counter local instance */ |
| 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 &npu_counters; |
| } |
| |
| /** |
| * @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(ðosu_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_IDLE; |
| counters->npu_evt_counters[0].event_mask = ETHOSU_PMU_CNT1_Msk; |
| counters->npu_evt_counters[0].name = "NPU IDLE"; |
| 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 ACTIVE"; |
| 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(ðosu_drv, i, counters->npu_evt_counters[i].event_type); |
| evt_mask |= counters->npu_evt_counters[i].event_mask; |
| } |
| |
| /* Reset overflow status. */ |
| ETHOSU_PMU_Set_CNTR_OVS(ðosu_drv, evt_mask); |
| |
| /* Enable PMU. */ |
| ETHOSU_PMU_Enable(ðosu_drv); |
| |
| /* Enable counters for cycle and event counters. */ |
| ETHOSU_PMU_CNTR_Disable(ðosu_drv, evt_mask); |
| ethosu_pmu_reset_counters(); |
| ETHOSU_PMU_CNTR_Enable(ðosu_drv, evt_mask); |
| } |
| |
| /** |
| * @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(ðosu_drv); |
| ETHOSU_PMU_EVCNTR_ALL_Reset(ðosu_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(ðosu_drv, i); |
| } |
| |
| /* Total cycle count */ |
| counters->npu_total_ccnt = ETHOSU_PMU_Get_CCNTR(ðosu_drv); |
| |
| /* Derived counters */ |
| #if ETHOSU_DERIVED_NCOUNTERS >= 1 |
| if (counters->npu_evt_counters[0].event_type == ETHOSU_PMU_NPU_IDLE) { |
| 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; |
| } |