From 301399d9e0b619fb2e229c9a9223aaf31ec6bcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonny=20Sv=C3=A4rd?= Date: Tue, 26 Apr 2022 18:31:24 +0200 Subject: Refactor power handling Add reference counted ethosu_request_power(), ethosu_release_power() functions. While there are active requests, disable the Q-channel power gating of the Ethos-U device, leaving the device powered on. Note that clock gating is implemented to follow the state of the power gating. Add ethosu_soft_reset() function. Restore power- and clock gating state after reset. Refactor and simplify driver code to use the new functions. Change-Id: I9756572c5c3e51b2be244bcea856d88e890e2d40 --- include/ethosu_driver.h | 35 +++++---- src/ethosu_device_u55_u65.c | 3 + src/ethosu_driver.c | 181 ++++++++++++++++---------------------------- src/ethosu_pmu.c | 6 +- 4 files changed, 91 insertions(+), 134 deletions(-) diff --git a/include/ethosu_driver.h b/include/ethosu_driver.h index 053b529..c48a3f2 100644 --- a/include/ethosu_driver.h +++ b/include/ethosu_driver.h @@ -74,11 +74,9 @@ struct ethosu_driver void *semaphore; uint64_t fast_memory; size_t fast_memory_size; + uint32_t power_request_counter; bool status_error; - bool dev_power_always_on; bool reserved; - uint8_t clock_request; - uint8_t power_request; }; struct ethosu_driver_version @@ -157,6 +155,24 @@ int ethosu_init(struct ethosu_driver *drv, */ void ethosu_deinit(struct ethosu_driver *drv); +/** + * Soft resets the Ethos-U device. + */ +bool ethosu_soft_reset(struct ethosu_driver *drv); + +/** + * Request to disable Q-channel power gating of the Ethos-U device. + * Power requests are ref.counted. Increases count. + * (Note: clock gating is made to follow power gating) + */ +bool ethosu_request_power(struct ethosu_driver *drv); + +/** + * Release disable request for Q-channel power gating of the Ethos-U device. + * Power requests are ref.counted. Decreases count. + */ +void ethosu_release_power(struct ethosu_driver *drv); + /** * Get Ethos-U driver version. */ @@ -208,11 +224,6 @@ int ethosu_invoke_async(struct ethosu_driver *drv, */ int ethosu_wait(struct ethosu_driver *drv, bool block); -/** - * Set Ethos-U power mode. - */ -void ethosu_set_power_mode(struct ethosu_driver *drv, bool always_on); - /** * Reserves a driver to execute inference with */ @@ -223,14 +234,6 @@ struct ethosu_driver *ethosu_reserve_driver(void); */ void ethosu_release_driver(struct ethosu_driver *drv); -/** - * Set clock and power request bits - */ -enum ethosu_error_codes set_clock_and_power_request(struct ethosu_driver *drv, - enum ethosu_request_clients client, - enum ethosu_clock_q_request clock_request, - enum ethosu_power_q_request power_request); - /** * Static inline for backwards-compatibility */ diff --git a/src/ethosu_device_u55_u65.c b/src/ethosu_device_u55_u65.c index 31379fc..f16f9f8 100644 --- a/src/ethosu_device_u55_u65.c +++ b/src/ethosu_device_u55_u65.c @@ -209,6 +209,9 @@ bool ethosu_dev_verify_access_state(struct ethosu_device *dev) enum ethosu_error_codes ethosu_dev_soft_reset(struct ethosu_device *dev) { + // Note that after a soft-reset, the NPU is unconditionally + // powered until the next CMD gets written. + struct reset_r reset; reset.word = 0; diff --git a/src/ethosu_driver.c b/src/ethosu_driver.c index 316ed4d..ee9d08c 100644 --- a/src/ethosu_driver.c +++ b/src/ethosu_driver.c @@ -320,8 +320,6 @@ static int handle_command_stream(struct ethosu_driver *drv, const uint8_t *cmd_s } } - drv->job.state = ETHOSU_JOB_RUNNING; - // Flush the cache if available on CPU. // The upcasting to uin32_t* is ok since the pointer never is dereferenced. // The base_addr_size is null if invoking from prior to invoke_V2, in that case @@ -341,17 +339,14 @@ static int handle_command_stream(struct ethosu_driver *drv, const uint8_t *cmd_s } // Request power gating disabled during inference run - if (!drv->dev_power_always_on) + if (!ethosu_request_power(drv)) { - // Will soft reset if security state or privilege level needs changing. - // Also note that any configurations done in the NPU prior to this point - // are lost in case power gating has been in effect. - set_clock_and_power_request(drv, ETHOSU_INFERENCE_REQUEST, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_DISABLE); - - // Make sure AXI settings are applied - ethosu_dev_axi_init(drv->dev); + LOG_ERR("Failed to request power"); + return -1; } + drv->job.state = ETHOSU_JOB_RUNNING; + // Inference begin callback ethosu_inference_begin(drv, drv->job.user_arg); @@ -406,8 +401,9 @@ int ethosu_init(struct ethosu_driver *drv, ethosu_semaphore = ethosu_semaphore_create(); } - drv->fast_memory = (uint32_t)fast_memory; - drv->fast_memory_size = fast_memory_size; + drv->fast_memory = (uint32_t)fast_memory; + drv->fast_memory_size = fast_memory_size; + drv->power_request_counter = 0; // Initialize the device and set requested security state and privilege mode drv->dev = ethosu_dev_init(base_address, secure_enable, privilege_enable); @@ -418,20 +414,11 @@ int ethosu_init(struct ethosu_driver *drv, return -1; } - // Power always ON requested - if (drv->dev_power_always_on) - { - if (set_clock_and_power_request(drv, ETHOSU_INFERENCE_REQUEST, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_DISABLE) != - ETHOSU_SUCCESS) - { - LOG_ERR("Failed to disable power-q for Ethos-U"); - return -1; - } - } - drv->semaphore = ethosu_semaphore_create(); drv->status_error = false; + ethosu_reset_job(drv); + ethosu_register_driver(drv); return 0; @@ -445,6 +432,56 @@ void ethosu_deinit(struct ethosu_driver *drv) drv->dev = NULL; } +bool ethosu_soft_reset(struct ethosu_driver *drv) +{ + // Soft reset the NPU + if (ethosu_dev_soft_reset(drv->dev) != ETHOSU_SUCCESS) + { + LOG_ERR("Failed to soft-reset NPU"); + return false; + } + + // Update power and clock gating after the soft reset + ethosu_dev_set_clock_and_power(drv->dev, + drv->power_request_counter > 0 ? ETHOSU_CLOCK_Q_DISABLE : ETHOSU_CLOCK_Q_ENABLE, + drv->power_request_counter > 0 ? ETHOSU_POWER_Q_DISABLE : ETHOSU_POWER_Q_ENABLE); + + return true; +} + +bool ethosu_request_power(struct ethosu_driver *drv) +{ + // Check if this is the first power request, increase counter + if (drv->power_request_counter++ == 0) + { + // Always reset to a known state. Changes to requested + // security state/privilege mode if necessary. + if (ethosu_soft_reset(drv) == false) + { + LOG_ERR("Failed to request power for Ethos-U"); + drv->power_request_counter--; + return false; + } + } + return true; +} + +void ethosu_release_power(struct ethosu_driver *drv) +{ + if (drv->power_request_counter == 0) + { + LOG_WARN("No power request left to release, reference counter is 0"); + } + else + { + // Decrement ref counter and enable power gating if no requests remain + if (--drv->power_request_counter == 0) + { + ethosu_dev_set_clock_and_power(drv->dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE); + } + } +} + void ethosu_get_driver_version(struct ethosu_driver_version *ver) { assert(ver != NULL); @@ -486,6 +523,9 @@ int ethosu_wait(struct ethosu_driver *drv, bool block) // Inference done callback ethosu_inference_end(drv, drv->job.user_arg); + // Relase power gating disabled requirement + ethosu_release_power(drv); + // Check NPU and interrupt status if (drv->status_error) { @@ -493,21 +533,13 @@ int ethosu_wait(struct ethosu_driver *drv, bool block) ethosu_dev_print_err_status(drv->dev); // Reset the NPU - (void)ethosu_dev_soft_reset(drv->dev); + (void)ethosu_soft_reset(drv); // NPU is no longer in error state drv->status_error = false; ret = -1; } - // Clear the clock/power gating disable request - if (!drv->dev_power_always_on) - { - // NOTE: Other requesters (like PMU) can be active, keeping - // clock/power gating disabled until no requests remain. - set_clock_and_power_request(drv, ETHOSU_INFERENCE_REQUEST, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE); - } - if (ret == 0) { // Invalidate cache @@ -664,31 +696,6 @@ int ethosu_invoke_v3(struct ethosu_driver *drv, return ethosu_wait(drv, true); } -void ethosu_set_power_mode(struct ethosu_driver *drv, bool always_on) -{ - drv->dev_power_always_on = always_on; - - if (always_on) - { - if (ethosu_dev_verify_access_state(drv->dev) == false) - { - // Reset to enter correct security state/privilege mode - if (ethosu_dev_soft_reset(drv->dev) == false) - { - LOG_ERR("Failed to set power mode for Ethos-U"); - return; - } - } - - ethosu_dev_set_clock_and_power(drv->dev, ETHOSU_CLOCK_Q_UNCHANGED, ETHOSU_POWER_Q_DISABLE); - ethosu_dev_axi_init(drv->dev); - } - else - { - ethosu_dev_set_clock_and_power(drv->dev, ETHOSU_CLOCK_Q_UNCHANGED, ETHOSU_POWER_Q_ENABLE); - } -} - struct ethosu_driver *ethosu_reserve_driver(void) { struct ethosu_driver *drv = NULL; @@ -727,13 +734,12 @@ void ethosu_release_driver(struct ethosu_driver *drv) if (ethosu_wait(drv, false) == 1) { // Still running, soft reset the NPU and reset driver - ethosu_dev_soft_reset(drv->dev); + drv->power_request_counter = 0; + ethosu_soft_reset(drv); ethosu_reset_job(drv); drv->status_error = false; /* TODO: feedback needed aout how to handle error (-1) return value */ ethosu_semaphore_give(drv->semaphore); - (void)set_clock_and_power_request( - drv, ETHOSU_INFERENCE_REQUEST, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE); } } @@ -745,58 +751,3 @@ void ethosu_release_driver(struct ethosu_driver *drv) /* TODO: feedback needed aout how to handle error (-1) return value */ ethosu_mutex_unlock(ethosu_mutex); } - -enum ethosu_error_codes set_clock_and_power_request(struct ethosu_driver *drv, - enum ethosu_request_clients client, - enum ethosu_clock_q_request clock_request, - enum ethosu_power_q_request power_request) -{ - // Keep track of which client requests clock gating to be disabled - if (clock_request == ETHOSU_CLOCK_Q_DISABLE) - { - drv->clock_request |= (1 << client); - } - else if (clock_request == ETHOSU_CLOCK_Q_ENABLE) // Remove client from bitmask - { - drv->clock_request &= ~(1 << client); - } - - // Only enable clock gating when no client has asked for it to be disabled - clock_request = drv->clock_request == 0 ? ETHOSU_CLOCK_Q_ENABLE : ETHOSU_CLOCK_Q_DISABLE; - - // Keep track of which client requests power gating to be disabled - if (power_request == ETHOSU_POWER_Q_DISABLE) - { - drv->power_request |= (1 << client); - } - else if (power_request == ETHOSU_POWER_Q_ENABLE) - { - drv->power_request &= ~(1 << client); - } - - // Override if power has been requested to be always on - if (drv->dev_power_always_on == true) - { - power_request = ETHOSU_POWER_Q_DISABLE; - } - else - { - // Only enable power gating when no client has asked for it to be disabled - power_request = drv->power_request == 0 ? ETHOSU_POWER_Q_ENABLE : ETHOSU_POWER_Q_DISABLE; - } - - // Verify security state and privilege mode if power is requested to be on - if (power_request == ETHOSU_POWER_Q_DISABLE) - { - if (ethosu_dev_verify_access_state(drv->dev) == false) - { - if (ethosu_dev_soft_reset(drv->dev) != ETHOSU_SUCCESS) - { - LOG_ERR("Failed to set clock and power q channels for Ethos-U"); - return ETHOSU_GENERIC_FAILURE; - } - } - } - // Set clock and power - return ethosu_dev_set_clock_and_power(drv->dev, clock_request, power_request); -} diff --git a/src/ethosu_pmu.c b/src/ethosu_pmu.c index e11636a..df69026 100644 --- a/src/ethosu_pmu.c +++ b/src/ethosu_pmu.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021 Arm Limited. All rights reserved. + * Copyright (c) 2019-2022 Arm Limited. * * SPDX-License-Identifier: Apache-2.0 * @@ -90,7 +90,7 @@ void ETHOSU_PMU_Enable(struct ethosu_driver *drv) LOG_DEBUG("Enable PMU"); struct pmcr_r pmcr = {0}; pmcr.cnt_en = 1; - set_clock_and_power_request(drv, ETHOSU_PMU_REQUEST, ETHOSU_CLOCK_Q_DISABLE, ETHOSU_POWER_Q_DISABLE); + ethosu_request_power(drv); drv->dev->reg->PMCR.word = pmcr.word; } @@ -98,7 +98,7 @@ void ETHOSU_PMU_Disable(struct ethosu_driver *drv) { LOG_DEBUG("Disable PMU"); drv->dev->reg->PMCR.word = 0; - set_clock_and_power_request(drv, ETHOSU_PMU_REQUEST, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE); + ethosu_release_power(drv); } uint32_t ETHOSU_PMU_Get_NumEventCounters(void) -- cgit v1.2.1