From 4dc73dcea25d951971ae306481d1f201c8d6ebdd Mon Sep 17 00:00:00 2001 From: Kristofer Jonsson Date: Fri, 16 Oct 2020 12:33:47 +0200 Subject: PMU counter shadow Storing PMU counters in shadow variables, in case the PMU was powered off or soft reset. Change-Id: I64ccf3fb6195f9be2d8315891ec612bb75404885 --- CMakeLists.txt | 5 - include/ethosu_device.h | 21 +++- include/ethosu_driver.h | 6 ++ include/pmu_ethosu.h | 254 ++++++++++++++++++++++++------------------------ src/ethosu_common.h | 1 - src/ethosu_config.h | 83 ---------------- src/ethosu_device.c | 52 +++++++--- src/ethosu_driver.c | 9 +- src/ethosu_pmu.c | 171 +++++++++++++------------------- 9 files changed, 264 insertions(+), 338 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff099c6..a4589ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,11 +62,6 @@ if (${LOG_SEVERITY} EQUAL -1) message(FATAL_ERROR "Unsupported log level ${ETHOSU_LOG_SEVERITY}") endif() -# Enable PMU boot auto-initialization -if(DRIVER_PMU_AUTOINIT) - add_compile_definitions(PMU_AUTOINIT) -endif() - # Make include directories available for current- and sub projects include_directories(include src) include_directories(${CMSIS_PATH}/CMSIS/Core/Include) diff --git a/include/ethosu_device.h b/include/ethosu_device.h index 5edba94..91aa877 100644 --- a/include/ethosu_device.h +++ b/include/ethosu_device.h @@ -54,15 +54,15 @@ enum ethosu_error_codes struct ethosu_device { - uintptr_t base_address; + volatile uint32_t *base_address; uint32_t reset; uint32_t pmcr; - uint64_t pmccntr; + uint32_t pmccntr[2]; uint32_t pmcnten; uint32_t pmint; uint32_t pmccntr_cfg; uint32_t pmu_evcntr[ETHOSU_PMU_NCOUNTERS]; - enum ethosu_pmu_event_type pmu_evtypr[ETHOSU_PMU_NCOUNTERS]; + uint32_t pmu_evtypr[ETHOSU_PMU_NCOUNTERS]; }; struct ethosu_id @@ -377,6 +377,13 @@ uint32_t ethosu_read_reg(struct ethosu_device *dev, uint32_t address); */ void ethosu_write_reg(struct ethosu_device *dev, uint32_t address, uint32_t value); +/** + * Write register with shadow variable. + * \param[in] address Address to read. + * \param[in] value Value to be written. + */ +void ethosu_write_reg_shadow(struct ethosu_device *dev, uint32_t address, uint32_t value, uint32_t *shadow); + /** * Save the PMU configuration to ethosu_device struct. * \param[in] dev Ethos-U device where the PMU configuration is @@ -393,6 +400,14 @@ enum ethosu_error_codes ethosu_save_pmu_config(struct ethosu_device *dev); */ enum ethosu_error_codes ethosu_restore_pmu_config(struct ethosu_device *dev); +/** + * Save PMU counters to shadow variables in memory. + * \param[in] dev Ethos-U device where the PMU configuration is + * stored. + * \return \ref ethosu_error_codes + */ +enum ethosu_error_codes ethosu_save_pmu_counters(struct ethosu_device *dev); + /** * Check if the STATUS register has any error bits set or not. * \param[in] dev Ethos-U device to check. diff --git a/include/ethosu_driver.h b/include/ethosu_driver.h index 2594fbe..433ad06 100644 --- a/include/ethosu_driver.h +++ b/include/ethosu_driver.h @@ -76,6 +76,12 @@ struct ethosu_version struct ethosu_version_config cfg; }; +/****************************************************************************** + * Variables + ******************************************************************************/ + +extern struct ethosu_driver ethosu_drv; + /****************************************************************************** * Prototypes ******************************************************************************/ diff --git a/include/pmu_ethosu.h b/include/pmu_ethosu.h index 7eb5c09..78d46ee 100644 --- a/include/pmu_ethosu.h +++ b/include/pmu_ethosu.h @@ -19,14 +19,32 @@ #ifndef PMU_ETHOSU_H #define PMU_ETHOSU_H +/***************************************************************************** + * Includes + *****************************************************************************/ + #include #ifdef __cplusplus extern "C" { #endif +/***************************************************************************** + * Defines + *****************************************************************************/ + #define ETHOSU_PMU_NCOUNTERS 4 +#define ETHOSU_PMU_CNT1_Msk (1UL << 0) +#define ETHOSU_PMU_CNT2_Msk (1UL << 1) +#define ETHOSU_PMU_CNT3_Msk (1UL << 2) +#define ETHOSU_PMU_CNT4_Msk (1UL << 3) +#define ETHOSU_PMU_CCNT_Msk (1UL << 31) + +/***************************************************************************** + * Types + *****************************************************************************/ + /** \brief HW Supported ETHOSU PMU Events * * Note: These values are symbolic. Actual HW-values may change. I.e. always use API @@ -112,193 +130,179 @@ enum ethosu_pmu_event_type ETHOSU_PMU_SENTINEL // End-marker (not event) }; -#define ETHOSU_PMU_CNT1_Msk (1UL << 0) -#define ETHOSU_PMU_CNT2_Msk (1UL << 1) -#define ETHOSU_PMU_CNT3_Msk (1UL << 2) -#define ETHOSU_PMU_CNT4_Msk (1UL << 3) -#define ETHOSU_PMU_CCNT_Msk (1UL << 31) - -/* Transpose functions between HW-event-type and event-id*/ -enum ethosu_pmu_event_type pmu_event_type(uint32_t); -uint32_t pmu_event_value(enum ethosu_pmu_event_type); - -/* Initialize the PMU driver */ -void ethosu_pmu_driver_init(void); - -void ethosu_pmu_driver_exit(void); - -// CMSIS ref API -/** \brief PMU Functions */ +/***************************************************************************** + * Functions + *****************************************************************************/ /** - \brief Enable the PMU -*/ + * \brief Enable the PMU + */ void ETHOSU_PMU_Enable(void); /** - \brief Disable the PMU -*/ + * \brief Disable the PMU + */ void ETHOSU_PMU_Disable(void); /** - \brief Set event to count for PMU eventer counter - \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) to configure - \param [in] type Event to count -*/ + * \brief Set event to count for PMU eventer counter + * \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) to configure + * \param [in] type Event to count + */ void ETHOSU_PMU_Set_EVTYPER(uint32_t num, enum ethosu_pmu_event_type type); /** - \brief Get event to count for PMU eventer counter - \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) to configure - \return type Event to count -*/ + * \brief Get event to count for PMU eventer counter + * \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) to configure + * \return type Event to count + */ enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER(uint32_t num); /** - \brief Reset cycle counter -*/ + * \brief Reset cycle counter + */ void ETHOSU_PMU_CYCCNT_Reset(void); /** - \brief Reset all event counters -*/ + * \brief Reset all event counters + */ void ETHOSU_PMU_EVCNTR_ALL_Reset(void); /** - \brief Enable counters - \param [in] mask Counters to enable - \note Enables one or more of the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) -*/ + * \brief Enable counters + * \param [in] mask Counters to enable + * \note Enables one or more of the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + */ void ETHOSU_PMU_CNTR_Enable(uint32_t mask); /** - \brief Disable counters - \param [in] mask Counters to disable - \note Disables one or more of the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) -*/ + * \brief Disable counters + * \param [in] mask Counters to disable + * \note Disables one or more of the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + */ void ETHOSU_PMU_CNTR_Disable(uint32_t mask); /** - \brief Determine counters activation - - \return Event count - \param [in] mask Counters to enable - \return a bitmask where bit-set means: - - event counters activated (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter activate (bit 31) - \note ETHOSU specific. Usage breaks CMSIS complience -*/ + * \brief Determine counters activation + * + * \return Event count + * \param [in] mask Counters to enable + * \return a bitmask where bit-set means: + * - event counters activated (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter activate (bit 31) + * \note ETHOSU specific. Usage breaks CMSIS complience + */ uint32_t ETHOSU_PMU_CNTR_Status(void); /** - \brief Read cycle counter (64 bit) - \return Cycle count - \note Two HW 32-bit registers that can increment independently in-between reads. - To work-around raciness yet still avoid turning - off the event both are read as one value twice. If the latter read - is not greater than the former, it means overflow of LSW without - incrementing MSW has occurred, in which case the former value is used. -*/ + * \brief Read cycle counter (64 bit) + * \return Cycle count + * \note Two HW 32-bit registers that can increment independently in-between reads. + * To work-around raciness yet still avoid turning + * off the event both are read as one value twice. If the latter read + * is not greater than the former, it means overflow of LSW without + * incrementing MSW has occurred, in which case the former value is used. + */ uint64_t ETHOSU_PMU_Get_CCNTR(void); /** - \brief Set cycle counter (64 bit) - \param [in] val Conter value - \note Two HW 32-bit registers that can increment independently in-between reads. - To work-around raciness, counter is temporary disabled if enabled. - \note ETHOSU specific. Usage breaks CMSIS complience -*/ + * \brief Set cycle counter (64 bit) + * \param [in] val Conter value + * \note Two HW 32-bit registers that can increment independently in-between reads. + * To work-around raciness, counter is temporary disabled if enabled. + * \note ETHOSU specific. Usage breaks CMSIS complience + */ void ETHOSU_PMU_Set_CCNTR(uint64_t val); /** - \brief Read event counter - \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) - \return Event count -*/ + * \brief Read event counter + * \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) + * \return Event count + */ uint32_t ETHOSU_PMU_Get_EVCNTR(uint32_t num); /** - \brief Set event counter value - \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) - \param [in] val Conter value - \note ETHOSU specific. Usage breaks CMSIS complience -*/ + * \brief Set event counter value + * \param [in] num Event counter (0-ETHOSU_PMU_NCOUNTERS) + * \param [in] val Conter value + * \note ETHOSU specific. Usage breaks CMSIS complience + */ void ETHOSU_PMU_Set_EVCNTR(uint32_t num, uint32_t val); /** - \brief Read counter overflow status - \return Counter overflow status bits for the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)) - - cycle counter (bit 31) -*/ + * \brief Read counter overflow status + * \return Counter overflow status bits for the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)) + * - cycle counter (bit 31) + */ uint32_t ETHOSU_PMU_Get_CNTR_OVS(void); /** - \brief Clear counter overflow status - \param [in] mask Counter overflow status bits to clear - \note Clears overflow status bits for one or more of the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) -*/ + * \brief Clear counter overflow status + * \param [in] mask Counter overflow status bits to clear + * \note Clears overflow status bits for one or more of the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + */ void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask); /** - \brief Enable counter overflow interrupt request - \param [in] mask Counter overflow interrupt request bits to set - \note Sets overflow interrupt request bits for one or more of the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) -*/ + * \brief Enable counter overflow interrupt request + * \param [in] mask Counter overflow interrupt request bits to set + * \note Sets overflow interrupt request bits for one or more of the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + */ void ETHOSU_PMU_Set_CNTR_IRQ_Enable(uint32_t mask); /** - \brief Disable counter overflow interrupt request - \param [in] mask Counter overflow interrupt request bits to clear - \note Clears overflow interrupt request bits for one or more of the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) -*/ + * \brief Disable counter overflow interrupt request + * \param [in] mask Counter overflow interrupt request bits to clear + * \note Clears overflow interrupt request bits for one or more of the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + */ void ETHOSU_PMU_Set_CNTR_IRQ_Disable(uint32_t mask); /** - \brief Get counters overflow interrupt request stiinings - \return mask Counter overflow interrupt request bits - \note Sets overflow interrupt request bits for one or more of the following: - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) - \note ETHOSU specific. Usage breaks CMSIS compliance -*/ + * \brief Get counters overflow interrupt request stiinings + * \return mask Counter overflow interrupt request bits + * \note Sets overflow interrupt request bits for one or more of the following: + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + * \note ETHOSU specific. Usage breaks CMSIS compliance + */ uint32_t ETHOSU_PMU_Get_IRQ_Enable(void); /** - \brief Software increment event counter - \param [in] mask Counters to increment - - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) - - cycle counter (bit 31) - \note Software increment bits for one or more event counters. -*/ + * \brief Software increment event counter + * \param [in] mask Counters to increment + * - event counters (bit 0-ETHOSU_PMU_NCOUNTERS) + * - cycle counter (bit 31) + * \note Software increment bits for one or more event counters. + */ void ETHOSU_PMU_CNTR_Increment(uint32_t mask); /** - \brief Set start event number for the cycle counter - \param [in] start_event Event number - - Start event (bits [9:0]) - \note Sets the event number that starts the cycle counter. - - Event number in the range 0..1023 -*/ + * \brief Set start event number for the cycle counter + * \param [in] start_event Event number + * - Start event (bits [9:0]) + * \note Sets the event number that starts the cycle counter. + * - Event number in the range 0..1023 + */ void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event(uint32_t start_event); /** - \brief Set stop event number for the cycle counter - \param [in] stop_event Event number - - Stop event (bits [25:16]) - \note Sets the event number that stops the cycle counter. - - Event number in the range 0..1023 -*/ + * \brief Set stop event number for the cycle counter + * \param [in] stop_event Event number + * - Stop event (bits [25:16]) + * \note Sets the event number that stops the cycle counter. + * - Event number in the range 0..1023 + */ void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event(uint32_t stop_event); #ifdef __cplusplus diff --git a/src/ethosu_common.h b/src/ethosu_common.h index 04acea2..30f07be 100644 --- a/src/ethosu_common.h +++ b/src/ethosu_common.h @@ -24,7 +24,6 @@ ******************************************************************************/ #include "ethosu55_interface.h" -#include "ethosu_device.h" #include diff --git a/src/ethosu_config.h b/src/ethosu_config.h index 91fe660..7a9fae2 100644 --- a/src/ethosu_config.h +++ b/src/ethosu_config.h @@ -116,87 +116,4 @@ #define BASE_POINTER_OFFSET 0 #endif -#ifdef PMU_AUTOINIT -/* - * Register control - * b0 = CNT_EN = Enable counters (RW) - * b1 = EVENT_CNT_RST = Reset event counters (WO) - * b2 = CYCLE_CNT_RST = Reset cycle counter (WO) - * b[15:11] = Number of event counters (RO) - */ -#ifndef INIT_PMCR -#define INIT_PMCR 0x0 -#endif - -/* - * Bit k enables event counter k - * k=31 enables the cycle counter - * Read value is current status - */ -#ifndef INIT_PMCNTENSET -#define INIT_PMCNTENSET 0x0 -#endif - -/* - * Bit k disables event counter k - * k=31 disables the cycle counter - * Read value is current status - */ -#ifndef INIT_PMCNTENCLR -#define INIT_PMCNTENCLR 0x0 -#endif - -/* - * Overflow detection set - * Bit k is for counter k - * k=31 is cycle counter - */ -#ifndef INIT_PMOVSSET -#define INIT_PMOVSSET 0x0 -#endif - -/* - * Overflow detection clear - * Bit k is for counter k - * k=31 is cycle counter - */ -#ifndef INIT_PMOVSCLR -#define INIT_PMOVSCLR 0x0 -#endif - -/* - * Interrupt set - * Bit k is for counter k - * k=31 is cycle counter - */ -#ifndef INIT_PMINTSET -#define INIT_PMINTSET 0x0 -#endif - -/* - * Interrupt clear - * Bit k is for counter k - * k=31 is cycle counter - */ -#ifndef INIT_PMINTCLR -#define INIT_PMINTCLR 0x8003 -#endif - -/* Cycle counter - * 48 bits value - */ -#ifndef INIT_PMCCNTR -#define INIT_PMCCNTR 0x0 -#endif - -/* - * b[9:0] Start Event – this event number starts the cycle counter - * b[25:16] Stop Event – this event number stops the cycle counter - */ -#ifndef INIT_PMCCNTR_CFG -#define INIT_PMCCNTR_CFG 0x0 -#endif - -#endif /* #ifdef PMU_AUTOINIT */ - #endif /* #ifndef ETHOSU_CONFIG_H */ diff --git a/src/ethosu_device.c b/src/ethosu_device.c index 85dc022..8fee3f4 100644 --- a/src/ethosu_device.c +++ b/src/ethosu_device.c @@ -37,7 +37,7 @@ static uint32_t stream_length = 0; enum ethosu_error_codes ethosu_dev_init(struct ethosu_device *dev, const void *base_address) { #if !defined(ARM_NPU_STUB) - dev->base_address = (uintptr_t)base_address; + dev->base_address = (volatile uint32_t *)base_address; ethosu_save_pmu_config(dev); #else UNUSED(dev); @@ -189,12 +189,21 @@ enum ethosu_error_codes ethosu_soft_reset(struct ethosu_device *dev) // Register access not permitted return ETHOSU_GENERIC_FAILURE; } + // Reset and set security level ethosu_write_reg(dev, NPU_REG_RESET, reset.word); + // Wait for reset to complete return_code = ethosu_wait_for_reset(dev); + // Save the proto register dev->reset = ethosu_read_reg(dev, NPU_REG_PROT); + + // Soft reset will clear the PMU configuration and counters. The shadow PMU counters + // are cleared by saving the PMU counters to ram, which will read back zeros. + // The PMU configuration will be restored in the invoke function after power save + // has been disabled. + ethosu_save_pmu_counters(dev); #else UNUSED(dev); #endif @@ -534,8 +543,9 @@ uint32_t ethosu_read_reg(struct ethosu_device *dev, uint32_t address) { #if !defined(ARM_NPU_STUB) ASSERT(dev->base_address != 0); + ASSERT(address % 4 == 0); - volatile uint32_t *reg = (uint32_t *)(uintptr_t)(dev->base_address + address); + volatile uint32_t *reg = dev->base_address + address / sizeof(uint32_t); return *reg; #else UNUSED(dev); @@ -549,8 +559,9 @@ void ethosu_write_reg(struct ethosu_device *dev, uint32_t address, uint32_t valu { #if !defined(ARM_NPU_STUB) ASSERT(dev->base_address != 0); + ASSERT(address % 4 == 0); - volatile uint32_t *reg = (uint32_t *)(uintptr_t)(dev->base_address + address); + volatile uint32_t *reg = dev->base_address + address / sizeof(uint32_t); *reg = value; #else UNUSED(dev); @@ -559,6 +570,12 @@ void ethosu_write_reg(struct ethosu_device *dev, uint32_t address, uint32_t valu #endif } +void ethosu_write_reg_shadow(struct ethosu_device *dev, uint32_t address, uint32_t value, uint32_t *shadow) +{ + ethosu_write_reg(dev, address, value); + *shadow = ethosu_read_reg(dev, address); +} + enum ethosu_error_codes ethosu_save_pmu_config(struct ethosu_device *dev) { #if !defined(ARM_NPU_STUB) @@ -574,13 +591,9 @@ enum ethosu_error_codes ethosu_save_pmu_config(struct ethosu_device *dev) // Save start and stop event dev->pmccntr_cfg = ethosu_read_reg(dev, NPU_REG_PMCCNTR_CFG); - // Save the cycle counter - dev->pmccntr = ETHOSU_PMU_Get_CCNTR(); - // Save the event settings and counters for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) { - dev->pmu_evcntr[i] = ethosu_read_reg(dev, NPU_REG_PMEVCNTR0 + i * sizeof(uint32_t)); dev->pmu_evtypr[i] = ethosu_read_reg(dev, NPU_REG_PMEVTYPER0 + i * sizeof(uint32_t)); } #else @@ -605,13 +618,9 @@ enum ethosu_error_codes ethosu_restore_pmu_config(struct ethosu_device *dev) // Restore start and stop event ethosu_write_reg(dev, NPU_REG_PMCCNTR_CFG, dev->pmccntr_cfg); - // Restore the cycle counter - ETHOSU_PMU_Set_CCNTR(dev->pmccntr); - - // Restore event settings and counters + // Save the event settings and counters for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) { - ethosu_write_reg(dev, NPU_REG_PMEVCNTR0 + i * sizeof(uint32_t), dev->pmu_evcntr[i]); ethosu_write_reg(dev, NPU_REG_PMEVTYPER0 + i * sizeof(uint32_t), dev->pmu_evtypr[i]); } #else @@ -621,6 +630,25 @@ enum ethosu_error_codes ethosu_restore_pmu_config(struct ethosu_device *dev) return ETHOSU_SUCCESS; } +enum ethosu_error_codes ethosu_save_pmu_counters(struct ethosu_device *dev) +{ +#if !defined(ARM_NPU_STUB) + // Save the cycle counter + dev->pmccntr[0] = ethosu_read_reg(dev, NPU_REG_PMCCNTR_LO); + dev->pmccntr[1] = ethosu_read_reg(dev, NPU_REG_PMCCNTR_HI); + + // Save the event settings and counters + for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) + { + dev->pmu_evcntr[i] = ethosu_read_reg(dev, NPU_REG_PMEVCNTR0 + i * sizeof(uint32_t)); + } +#else + UNUSED(dev); +#endif + + return ETHOSU_SUCCESS; +} + bool ethosu_status_has_error(struct ethosu_device *dev) { bool status_error = false; diff --git a/src/ethosu_driver.c b/src/ethosu_driver.c index 6c55b0b..77b8727 100644 --- a/src/ethosu_driver.c +++ b/src/ethosu_driver.c @@ -149,7 +149,7 @@ struct opt_cfg_s ******************************************************************************/ struct ethosu_driver ethosu_drv = { - .dev = {.base_address = NULL, .reset = 0, .pmccntr = 0, .pmu_evcntr = {0, 0, 0, 0}, .pmu_evtypr = {0, 0, 0, 0}}, + .dev = {.base_address = NULL, .reset = 0, .pmccntr = {0}, .pmu_evcntr = {0, 0, 0, 0}, .pmu_evtypr = {0, 0, 0, 0}}, .abort_inference = false, .status_error = false}; @@ -419,7 +419,7 @@ int ethosu_invoke_v2(const void *custom_data_ptr, if (!ethosu_drv.status_error) { - ethosu_save_pmu_config(ðosu_drv.dev); + ethosu_save_pmu_counters(ðosu_drv.dev); ethosu_set_clock_and_power(ðosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE); } @@ -580,17 +580,14 @@ static int handle_command_stream(struct ethosu_driver *drv, return -1; } - // ETHOSU would have been reset in the IRQ handler if there were - // status error(s). So don't read the QREAD register. (void)ethosu_get_qread(&drv->dev, &qread); if (qread != cms_bytes) { - LOG_ERR( + LOG_WARN( "Failure: IRQ received but qread (%" PRIu32 ") not at end of stream (%" PRIu32 ").\n", qread, cms_bytes); return -1; } - // TODO Power off return 0; } diff --git a/src/ethosu_pmu.c b/src/ethosu_pmu.c index 7ca35fc..f7cb957 100644 --- a/src/ethosu_pmu.c +++ b/src/ethosu_pmu.c @@ -42,8 +42,6 @@ #define EVID(A, name) (PMU_EVENT_TYPE_##name) -#define ETHOSU_PMCCNTR_CFG_START_STOP_EVENT_MASK (0x3FF) - #define NPU_REG_PMEVCNTR(x) (NPU_REG_PMEVCNTR0 + ((x) * sizeof(uint32_t))) #define NPU_REG_PMEVTYPER(x) (NPU_REG_PMEVTYPER0 + ((x) * sizeof(uint32_t))) @@ -51,20 +49,13 @@ * Variables *****************************************************************************/ -/** - * NOTE: A pointer to ethosu_driver will be added to the PMU functions - * when multi-NPU functionality is implemented later. We shall use a - * shared ethosu_driver instance till then. - * */ -extern struct ethosu_driver ethosu_drv; - static const enum pmu_event_type eventbyid[] = {EXPAND_PMU_EVENT_TYPE(EVID, COMMA)}; /***************************************************************************** - * Functions + * Static functions *****************************************************************************/ -enum ethosu_pmu_event_type pmu_event_type(uint32_t id) +static enum ethosu_pmu_event_type pmu_event_type(uint32_t id) { switch (id) { @@ -76,7 +67,7 @@ enum ethosu_pmu_event_type pmu_event_type(uint32_t id) return ETHOSU_PMU_SENTINEL; } -uint32_t pmu_event_value(enum ethosu_pmu_event_type event) +static uint32_t pmu_event_value(enum ethosu_pmu_event_type event) { int a = event; if ((a < ETHOSU_PMU_SENTINEL) && (a >= ETHOSU_PMU_NO_EVENT)) @@ -89,29 +80,9 @@ uint32_t pmu_event_value(enum ethosu_pmu_event_type event) } } -void ethosu_pmu_driver_init(void) -{ -#ifdef PMU_AUTOINIT - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCR, INIT_PMCR); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCNTENSET, INIT_PMCNTENSET); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCNTENCLR, INIT_PMCNTENCLR); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMOVSSET, INIT_PMOVSSET); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMOVSCLR, INIT_PMOVSCLR); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMINTSET, INIT_PMINTSET); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMINTCLR, INIT_PMINTCLR); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_LO, INIT_PMCCNTR); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_HI, INIT_PMCCNTR); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_CFG, INIT_PMCCNTR_CFG); - - for (int i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) - { - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMEVCNTR(i), 0); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMEVTYPER(i), 0); - } -#endif -} - -void ethosu_pmu_driver_exit(void) {} +/***************************************************************************** + * Functions + *****************************************************************************/ void ETHOSU_PMU_Enable(void) { @@ -119,8 +90,7 @@ void ETHOSU_PMU_Enable(void) struct pmcr_r pmcr; pmcr.word = ethosu_drv.dev.pmcr; pmcr.cnt_en = 1; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word); - ethosu_drv.dev.pmcr = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCR); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word, ðosu_drv.dev.pmcr); } void ETHOSU_PMU_Disable(void) @@ -129,8 +99,7 @@ void ETHOSU_PMU_Disable(void) struct pmcr_r pmcr; pmcr.word = ethosu_drv.dev.pmcr; pmcr.cnt_en = 0; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word); - ethosu_drv.dev.pmcr = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCR); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word, ðosu_drv.dev.pmcr); } void ETHOSU_PMU_Set_EVTYPER(uint32_t num, enum ethosu_pmu_event_type type) @@ -138,14 +107,13 @@ void ETHOSU_PMU_Set_EVTYPER(uint32_t num, enum ethosu_pmu_event_type type) ASSERT(num < ETHOSU_PMU_NCOUNTERS); uint32_t val = pmu_event_value(type); LOG_DEBUG("%s: num=%u, type=%d, val=%u\n", __FUNCTION__, num, type, val); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMEVTYPER(num), val); - ethosu_drv.dev.pmu_evtypr[num] = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMEVTYPER(num)); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMEVTYPER(num), val, ðosu_drv.dev.pmu_evtypr[num]); } enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER(uint32_t num) { ASSERT(num < ETHOSU_PMU_NCOUNTERS); - uint32_t val = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMEVTYPER(num)); + uint32_t val = ethosu_drv.dev.pmu_evtypr[num]; enum ethosu_pmu_event_type type = pmu_event_type(val); LOG_DEBUG("%s: num=%u, type=%d, val=%u\n", __FUNCTION__, num, type, val); return type; @@ -157,9 +125,9 @@ void ETHOSU_PMU_CYCCNT_Reset(void) struct pmcr_r pmcr; pmcr.word = ethosu_drv.dev.pmcr; pmcr.cycle_cnt_rst = 1; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word); - ethosu_drv.dev.pmcr = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCR); - ethosu_drv.dev.pmccntr = 0; + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word, ðosu_drv.dev.pmcr); + ethosu_drv.dev.pmccntr[0] = 0; + ethosu_drv.dev.pmccntr[1] = 0; } void ETHOSU_PMU_EVCNTR_ALL_Reset(void) @@ -168,8 +136,7 @@ void ETHOSU_PMU_EVCNTR_ALL_Reset(void) struct pmcr_r pmcr; pmcr.word = ethosu_drv.dev.pmcr; pmcr.event_cnt_rst = 1; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word); - ethosu_drv.dev.pmcr = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCR); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCR, pmcr.word, ðosu_drv.dev.pmcr); for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) { @@ -180,55 +147,59 @@ void ETHOSU_PMU_EVCNTR_ALL_Reset(void) void ETHOSU_PMU_CNTR_Enable(uint32_t mask) { LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCNTENSET, mask); - ethosu_drv.dev.pmcnten = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCNTENSET); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCNTENSET, mask, ðosu_drv.dev.pmcnten); } void ETHOSU_PMU_CNTR_Disable(uint32_t mask) { LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCNTENCLR, mask); - ethosu_drv.dev.pmcnten = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCNTENSET); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCNTENCLR, mask, ðosu_drv.dev.pmcnten); } uint32_t ETHOSU_PMU_CNTR_Status(void) { - uint32_t val = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCNTENSET); - LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, val); - return val; + LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, ethosu_drv.dev.pmcnten); + return ethosu_drv.dev.pmcnten; } uint64_t ETHOSU_PMU_Get_CCNTR(void) { - uint64_t val = (((uint64_t)ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_HI)) << 32) | - ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_LO); + uint32_t val_lo = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_HI); + uint32_t val_hi = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_LO); + uint64_t val = ((uint64_t)val_hi << 32) | val_lo; + uint64_t shadow = ((uint64_t)ethosu_drv.dev.pmccntr[1] << 32) | ethosu_drv.dev.pmccntr[0]; - LOG_DEBUG("%s: val=%llu, pmccntr=%llu\n", __FUNCTION__, val, ethosu_drv.dev.pmccntr); + LOG_DEBUG("%s: val=%llu, shadow=%llu\n", __FUNCTION__, val, shadow); - // Return the cached value in case the NPU was powered off - if (ethosu_drv.dev.pmccntr > val) + // Return the shadow variable in case the NPU was powered off and lost the cycle count + if (shadow > val) { - return ethosu_drv.dev.pmccntr; + return shadow; } + // Update the shadow variable + ethosu_drv.dev.pmccntr[0] = val_lo; + ethosu_drv.dev.pmccntr[1] = val_hi; + return val; } void ETHOSU_PMU_Set_CCNTR(uint64_t val) { - uint32_t mask = ETHOSU_PMU_CNTR_Status(); + uint32_t active = ETHOSU_PMU_CNTR_Status() & ETHOSU_PMU_CCNT_Msk; LOG_DEBUG("%s: val=%llu\n", __FUNCTION__, val); - if (mask & ETHOSU_PMU_CCNT_Msk) + if (active) { ETHOSU_PMU_CNTR_Disable(ETHOSU_PMU_CCNT_Msk); } - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_LO, (val & MASK_0_31_BITS)); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCCNTR_LO, val & MASK_0_31_BITS, ðosu_drv.dev.pmccntr[0]); + ethosu_write_reg_shadow( + ðosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32, ðosu_drv.dev.pmccntr[1]); - if (mask & ETHOSU_PMU_CCNT_Msk) + if (active) { ETHOSU_PMU_CNTR_Enable(ETHOSU_PMU_CCNT_Msk); } @@ -238,14 +209,17 @@ uint32_t ETHOSU_PMU_Get_EVCNTR(uint32_t num) { ASSERT(num < ETHOSU_PMU_NCOUNTERS); uint32_t val = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMEVCNTR(num)); - LOG_DEBUG("%s: num=%u, val=%u, pmu_evcntr=%u\n", __FUNCTION__, num, val, ethosu_drv.dev.pmu_evcntr[num]); + LOG_DEBUG("%s: num=%u, val=%u, shadow=%u\n", __FUNCTION__, num, val, ethosu_drv.dev.pmu_evcntr[num]); - // Return the cached value in case the NPU was powered off + // Return the shadow variable in case the NPU was powered off and lost the event count if (ethosu_drv.dev.pmu_evcntr[num] > val) { return ethosu_drv.dev.pmu_evcntr[num]; } + // Update the shadow variable + ethosu_drv.dev.pmu_evcntr[num] = val; + return val; } @@ -262,8 +236,6 @@ uint32_t ETHOSU_PMU_Get_CNTR_OVS(void) return ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMOVSSET); } -// TODO: check if this function name match with the description & -// implementation. void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask) { LOG_DEBUG("%s:\n", __FUNCTION__); @@ -273,22 +245,19 @@ void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask) void ETHOSU_PMU_Set_CNTR_IRQ_Enable(uint32_t mask) { LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMINTSET, mask); - ethosu_drv.dev.pmint = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMINTSET); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMINTSET, mask, ðosu_drv.dev.pmint); } void ETHOSU_PMU_Set_CNTR_IRQ_Disable(uint32_t mask) { LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMINTCLR, mask); - ethosu_drv.dev.pmint = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMINTSET); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMINTCLR, mask, ðosu_drv.dev.pmint); } uint32_t ETHOSU_PMU_Get_IRQ_Enable(void) { - uint32_t mask = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMINTSET); - LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask); - return mask; + LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, ethosu_drv.dev.pmint); + return ethosu_drv.dev.pmint; } void ETHOSU_PMU_CNTR_Increment(uint32_t mask) @@ -296,49 +265,45 @@ void ETHOSU_PMU_CNTR_Increment(uint32_t mask) LOG_DEBUG("%s:\n", __FUNCTION__); uint32_t cntrs_active = ETHOSU_PMU_CNTR_Status(); + // Disable counters + ETHOSU_PMU_CNTR_Disable(mask); + + // Increment cycle counter if (mask & ETHOSU_PMU_CCNT_Msk) { - if (mask & ETHOSU_PMU_CCNT_Msk) - { - ETHOSU_PMU_CNTR_Disable(ETHOSU_PMU_CCNT_Msk); - uint64_t val = ETHOSU_PMU_Get_CCNTR() + 1; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_LO, (val & MASK_0_31_BITS)); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32); - if (cntrs_active & ETHOSU_PMU_CCNT_Msk) - { - ETHOSU_PMU_CNTR_Enable(ETHOSU_PMU_CCNT_Msk); - } - } + uint64_t val = ETHOSU_PMU_Get_CCNTR() + 1; + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCCNTR_LO, val & MASK_0_31_BITS, ðosu_drv.dev.pmccntr[0]); + ethosu_write_reg_shadow( + ðosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32, ðosu_drv.dev.pmccntr[1]); } + for (int i = 0; i < ETHOSU_PMU_NCOUNTERS; i++) { - uint32_t cntr = (0x0001 << i); - - if (mask & cntr) + if (mask & (1 << i)) { - ETHOSU_PMU_CNTR_Disable(cntr); - uint32_t val = ethosu_read_reg(ðosu_drv.dev, NPU_REG_PMEVCNTR(i)); - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMEVCNTR(i), val + 1); - if (cntrs_active & cntr) - { - ETHOSU_PMU_CNTR_Enable(cntr); - } + uint32_t val = ETHOSU_PMU_Get_EVCNTR(i); + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMEVCNTR(i), val + 1, ðosu_drv.dev.pmu_evcntr[i]); } } + + // Reenable the active counters + ETHOSU_PMU_CNTR_Enable(cntrs_active); } void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event(uint32_t start_event) { LOG_DEBUG("%s: start_event=%u\n", __FUNCTION__, start_event); - struct pmccntr_cfg_r *cfg = (struct pmccntr_cfg_r *)ðosu_drv.dev.pmccntr_cfg; - cfg->CYCLE_CNT_CFG_START = start_event & ETHOSU_PMCCNTR_CFG_START_STOP_EVENT_MASK; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg->word); + struct pmccntr_cfg_r cfg; + cfg.word = ethosu_drv.dev.pmccntr_cfg; + cfg.CYCLE_CNT_CFG_START = start_event; + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg.word, ðosu_drv.dev.pmccntr_cfg); } void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event(uint32_t stop_event) { LOG_DEBUG("%s: stop_event=%u\n", __FUNCTION__, stop_event); - struct pmccntr_cfg_r *cfg = (struct pmccntr_cfg_r *)ðosu_drv.dev.pmccntr_cfg; - cfg->CYCLE_CNT_CFG_STOP = stop_event & ETHOSU_PMCCNTR_CFG_START_STOP_EVENT_MASK; - ethosu_write_reg(ðosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg->word); + struct pmccntr_cfg_r cfg; + cfg.word = ethosu_drv.dev.pmccntr_cfg; + cfg.CYCLE_CNT_CFG_STOP = stop_event; + ethosu_write_reg_shadow(ðosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg.word, ðosu_drv.dev.pmccntr_cfg); } -- cgit v1.2.1