blob: c8e7252af0709553830a723af5788524de559fc1 [file] [log] [blame]
/*
* Copyright (c) 2021 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 "bsp.h"
#include "timer.h"
#include <assert.h>
#include <string.h>
#include <inttypes.h>
#if defined (ARM_NPU)
#include "pmu_ethosu.h"
extern struct ethosu_driver ethosu_drv; /* Default Ethos-U55 device driver */
/**
* @brief Initialises the PMU and enables the cycle counter.
**/
static void _init_ethosu_cyclecounter(void);
/**
* @brief Gets the difference of total NPU cycle counts.
* (includes active and idle)
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return Total NPU cycle counts difference between the arguments expressed
* as unsigned 64 bit integer.
**/
static uint64_t bm_get_npu_total_cycle_diff(time_counter *st,
time_counter *end);
/**
* @brief Gets the difference in active NPU cycle counts.
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return Active NPU cycle counts difference between the arguments expressed
* as unsigned 64 bit integer.
**/
static uint64_t bm_get_npu_active_cycle_diff(time_counter *st,
time_counter *end);
/** @brief Gets the difference in idle NPU cycle counts
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return Idle NPU cycle counts difference between the arguments expressed
* as unsigned 64 bit integer.
**/
static uint64_t bm_get_npu_idle_cycle_diff(time_counter *st,
time_counter *end);
/** @brief Gets the difference in axi0 bus reads cycle counts
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return NPU AXI0 read cycle counts difference between the arguments expressed
* as unsigned 64 bit integer.
**/
static uint64_t bm_get_npu_axi0_read_cycle_diff(time_counter *st,
time_counter *end);
/** @brief Gets the difference in axi0 bus writes cycle counts
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return NPU AXI0 write cycle counts difference between the arguments expressed
* as unsigned 64 bit integer.
**/
static uint64_t bm_get_npu_axi0_write_cycle_diff(time_counter *st,
time_counter *end);
/** @brief Gets the difference in axi1 bus reads cycle counts
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return NPU AXI1 read cycle counts difference between the arguments expressed
* as unsigned 64 bit integer.
**/
static uint64_t bm_get_npu_axi1_read_cycle_diff(time_counter *st,
time_counter *end);
/** @brief Gets the difference for 6 collected cycle counts:
* 1) total NPU
* 2) active NPU
* 3) idle NPU
* 4) axi0 read
* 5) axi0 write
* 6) axi1 read
* */
static int bm_get_npu_cycle_diff(time_counter *st, time_counter *end,
uint64_t* pmu_counters_values, const size_t size);
#endif /* defined (ARM_NPU) */
#if defined(MPS3_PLATFORM)
/**
* @brief Wrapper for getting milliseconds duration between time counters
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return Difference in milliseconds between given time counters.
**/
static time_t bm_get_duration_ms(time_counter *st, time_counter *end);
/**
* @brief Wrapper for getting microseconds duration between time counters
* @param[in] st Pointer to time_counter value at start time.
* @param[in] end Pointer to time_counter value at end.
* @return Difference in microseconds between given time counters.
**/
static time_t bm_get_duration_us(time_counter *st, time_counter *end);
#endif /* defined(MPS3_PLATFORM) */
/**
* @brief Wrapper for resetting timer.
**/
static void bm_timer_reset(void);
/**
* @brief Wrapper for getting the current timer counter.
* @return Current time counter value.
**/
static time_counter bm_get_time_counter(void);
/**
* @brief Wrapper for profiler start.
* @return Current profiler start timer counter.
**/
static time_counter bm_start_profiling(void);
/**
* @brief Wrapper for profiler end.
* @return Current profiler end timer counter.
**/
static time_counter bm_stop_profiling(void);
/**
* @brief Wrapper for getting CPU cycle difference between time counters.
* @return CPU cycle difference between given time counters expressed
* as unsigned 32 bit integer.
**/
static uint64_t bm_get_cpu_cycles_diff(time_counter *st, time_counter *end);
/**
* @brief Initialiser for bare metal timer.
* @param[in] timer Platform timer to initialize.
**/
void init_timer(platform_timer *timer)
{
assert(timer);
memset(timer, 0, sizeof(*timer));
timer->reset = bm_timer_reset;
timer->get_time_counter = bm_get_time_counter;
timer->start_profiling = bm_start_profiling;
timer->stop_profiling = bm_stop_profiling;
timer->get_cpu_cycle_diff = bm_get_cpu_cycles_diff;
timer->cap.cpu_cycles = 1;
#if defined (MPS3_PLATFORM)
timer->cap.duration_ms = 1;
timer->cap.duration_us = 1;
timer->get_duration_ms = bm_get_duration_ms;
timer->get_duration_us = bm_get_duration_us;
#endif /* defined (MPS3_PLATFORM) */
#if defined (ARM_NPU)
/* We are capable of reporting npu cycle counts. */
timer->cap.npu_cycles = 1;
timer->get_npu_cycles_diff = bm_get_npu_cycle_diff;
_init_ethosu_cyclecounter();
#endif /* defined (ARM_NPU) */
timer->reset();
timer->inited = 1;
}
#if defined (ARM_NPU)
static void _reset_ethosu_counters()
{
/* Reset all cycle and event counters. */
ETHOSU_PMU_CYCCNT_Reset(&ethosu_drv);
ETHOSU_PMU_EVCNTR_ALL_Reset(&ethosu_drv);
}
static void _init_ethosu_cyclecounter()
{
/* Reset overflow status. */
ETHOSU_PMU_Set_CNTR_OVS(&ethosu_drv, ETHOSU_PMU_CNT1_Msk | ETHOSU_PMU_CCNT_Msk);
/* We can retrieve only 4 PMU counters: */
ETHOSU_PMU_Set_EVTYPER(&ethosu_drv, 0, ETHOSU_PMU_NPU_IDLE);
ETHOSU_PMU_Set_EVTYPER(&ethosu_drv, 1, ETHOSU_PMU_AXI0_RD_DATA_BEAT_RECEIVED);
ETHOSU_PMU_Set_EVTYPER(&ethosu_drv, 2, ETHOSU_PMU_AXI0_WR_DATA_BEAT_WRITTEN);
ETHOSU_PMU_Set_EVTYPER(&ethosu_drv, 3, ETHOSU_PMU_AXI1_RD_DATA_BEAT_RECEIVED);
/* Enable PMU. */
ETHOSU_PMU_Enable(&ethosu_drv);
/* Enable counters for cycle and counter# 0. */
ETHOSU_PMU_CNTR_Enable(&ethosu_drv, ETHOSU_PMU_CNT1_Msk | ETHOSU_PMU_CNT2_Msk | ETHOSU_PMU_CNT3_Msk | ETHOSU_PMU_CNT4_Msk| ETHOSU_PMU_CCNT_Msk);
_reset_ethosu_counters();
}
static int bm_get_npu_cycle_diff(time_counter *st, time_counter *end,
uint64_t* pmu_counters_values, const size_t size)
{
if (size == 6) {
pmu_counters_values[0] = bm_get_npu_total_cycle_diff(st, end);
pmu_counters_values[1] = bm_get_npu_active_cycle_diff(st, end);
pmu_counters_values[2] = bm_get_npu_idle_cycle_diff(st, end);
pmu_counters_values[3] = bm_get_npu_axi0_read_cycle_diff(st, end);
pmu_counters_values[4] = bm_get_npu_axi0_write_cycle_diff(st, end);
pmu_counters_values[5] = bm_get_npu_axi1_read_cycle_diff(st, end);
return 0;
} else {
return 1;
}
}
static uint64_t bm_get_npu_total_cycle_diff(time_counter *st, time_counter *end)
{
return end->npu_total_ccnt - st->npu_total_ccnt;
}
static uint32_t 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;
}
static uint64_t bm_get_npu_idle_cycle_diff(time_counter *st, time_counter *end)
{
if (counter_overflow(ETHOSU_PMU_CNT1_Msk)) {
printf_err("EthosU PMU idle counter overflow.\n");
return 0;
}
return (uint64_t)(end->npu_idle_ccnt - st->npu_idle_ccnt);
}
static uint64_t bm_get_npu_active_cycle_diff(time_counter *st, time_counter *end)
{
/* Active NPU time = total time - idle time */
return bm_get_npu_total_cycle_diff(st, end) - bm_get_npu_idle_cycle_diff(st, end);
}
static uint64_t bm_get_npu_axi0_read_cycle_diff(time_counter *st, time_counter *end)
{
if (counter_overflow(ETHOSU_PMU_CNT2_Msk)) {
printf_err("EthosU PMU axi0 read counter overflow.\n");
return 0;
}
return (uint64_t)(end->npu_axi0_read_beats - st->npu_axi0_read_beats);
}
static uint64_t bm_get_npu_axi0_write_cycle_diff(time_counter *st, time_counter *end)
{
if (counter_overflow(ETHOSU_PMU_CNT3_Msk)) {
printf_err("EthosU PMU axi0 write counter overflow.\n");
return 0;
}
return (uint64_t)(end->npu_axi0_write_beats - st->npu_axi0_write_beats);
}
static uint64_t bm_get_npu_axi1_read_cycle_diff(time_counter *st, time_counter *end)
{
if (counter_overflow(ETHOSU_PMU_CNT4_Msk)) {
printf_err("EthosU PMU axi1 read counter overflow.\n");
return 0;
}
return (uint64_t)(end->npu_axi1_read_beats - st->npu_axi1_read_beats);
}
#endif /* defined (ARM_NPU) */
static void bm_timer_reset(void)
{
#if defined (ARM_NPU)
_init_ethosu_cyclecounter();
#endif /* defined (ARM_NPU) */
timer_reset();
}
static time_counter bm_get_time_counter(void)
{
time_counter t = {
.counter = get_time_counter(),
#if defined (ARM_NPU)
.npu_total_ccnt = ETHOSU_PMU_Get_CCNTR(&ethosu_drv),
.npu_idle_ccnt = ETHOSU_PMU_Get_EVCNTR(&ethosu_drv, 0),
.npu_axi0_read_beats = ETHOSU_PMU_Get_EVCNTR(&ethosu_drv, 1),
.npu_axi0_write_beats = ETHOSU_PMU_Get_EVCNTR(&ethosu_drv, 2),
.npu_axi1_read_beats = ETHOSU_PMU_Get_EVCNTR(&ethosu_drv, 3)
#endif /* defined (ARM_NPU) */
};
#if defined (ARM_NPU)
debug("NPU total cc: %" PRIu64
"; NPU idle cc: %" PRIu32
"; NPU axi0 read beats: %" PRIu32
"; NPU axi0 write beats: %" PRIu32
"; NPU axi1 read beats: %" PRIu32 "\n",
t.npu_total_ccnt,
t.npu_idle_ccnt,
t.npu_axi0_read_beats,
t.npu_axi0_write_beats,
t.npu_axi1_read_beats);
#endif /* defined (ARM_NPU) */
return t;
}
static time_counter bm_start_profiling(void)
{
start_cycle_counter();
return bm_get_time_counter();
}
static time_counter bm_stop_profiling(void)
{
stop_cycle_counter();
return bm_get_time_counter();
}
static uint64_t bm_get_cpu_cycles_diff(time_counter *st, time_counter *end)
{
return get_cycle_count_diff(&(st->counter), &(end->counter));
}
#if defined(MPS3_PLATFORM)
static time_t bm_get_duration_ms(time_counter *st, time_counter *end)
{
return get_duration_milliseconds(&(st->counter), &(end->counter));
}
static time_t bm_get_duration_us(time_counter *st, time_counter *end)
{
return get_duration_microseconds(&(st->counter), &(end->counter));
}
#endif /* defined(MPS3_PLATFORM) */