/* * SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates * 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_cpu_cache.h" #include "RTE_Components.h" /* For CPU related defintiions */ #include "ethosu_driver.h" /* Arm Ethos-U driver header */ #include "log_macros.h" /* Logging macros */ /** Structure to maintain data cache states. */ typedef struct _cpu_cache_state { uint32_t dcache_invalidated : 1; uint32_t dcache_cleaned : 1; } cpu_cache_state; /** Static CPU cache state object. * @note This logic around flipping these states is based on the driver * calling the functions in this sequence: * * Cache flush (ethosu_flush_dcache) * ↓ * Start inference (ethosu_inference_begin) * ↓ * Inference (ethosu_dev_run_command_stream) * ↓ * End inference (ethosu_inference_end) * ↓ * Cache invalidate (ethosu_dcache_invalidate) **/ static cpu_cache_state s_cache_state = {.dcache_cleaned = 0, .dcache_invalidated = 0}; /** * @brief Gets the current CPU cache state. * @return Pointer to the CPU cache state object. */ static cpu_cache_state* ethosu_get_cpu_cache_state(void) { return &s_cache_state; } void ethosu_clear_cache_states(void) { cpu_cache_state* const state = ethosu_get_cpu_cache_state(); trace("Clearing cache state members\n"); state->dcache_invalidated = 0; state->dcache_cleaned = 0; } void ethosu_flush_dcache(uint32_t *p, size_t bytes) { UNUSED(p); UNUSED(bytes); #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) cpu_cache_state* const state = ethosu_get_cpu_cache_state(); if (SCB->CCR & SCB_CCR_DC_Msk) { /** * @note We could choose to call the `SCB_CleanDCache_by_Addr` function * here, but the sizes which this function is called for, can * cause unnecessary delays. It's worth noting that this function * is called from the Arm Ethos-U NPU drive repeatedly for each * region it accesses. This could even be RO memory which does * not need cache maintenance, along with parts of the input and * output tensors which rightly need to be cleaned. Therefore, to * reduce overhead of repeated calls for large memory sizes, we * call the clean and invalidation functions for whole cache. * * If the neural network to be executed is completely falling * onto the NPU, consider disabling the data cache altogether * for the duration of the inference to further reduce the cache * maintenance burden in these functions. */ /** Clean the cache if it hasn't been cleaned already */ if (!state->dcache_cleaned) { trace("Cleaning data cache\n"); SCB_CleanDCache(); /** Assert the cache cleaned state and clear the invalidation * state. */ state->dcache_cleaned = 1; state->dcache_invalidated = 0; } } #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */ } void ethosu_invalidate_dcache(uint32_t *p, size_t bytes) { UNUSED(p); UNUSED(bytes); #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) cpu_cache_state* const state = ethosu_get_cpu_cache_state(); if (SCB->CCR & SCB_CCR_DC_Msk) { /** * See note in ethosu_flush_dcache function for why we clean the whole * cache instead of calling it for specific addresses. **/ if (!state->dcache_invalidated) { trace("Invalidating data cache\n"); SCB_InvalidateDCache(); /** Assert the cache invalidation state and clear the clean * state. */ state->dcache_invalidated = 1; state->dcache_cleaned = 0; } } #endif /* defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) */ }