From 03f400426a92164a631aa956dfa5c83dba45fa28 Mon Sep 17 00:00:00 2001 From: Romain Mahoux Date: Thu, 3 Sep 2026 11:17:16 +0200 Subject: [PATCH 1/3] CMSIS: share L1 cache maintenance support across A and R profiles Extract the common L1 cache maintenance implementation into l1_cache.h and reuse it for Armv7-A, Armv7-R and Armv8-R. --- CMSIS/Core/Include/a-profile/armv7a.h | 163 +---------------- CMSIS/Core/Include/a-profile/l1_cache.h | 192 +++++++++++++++++++++ CMSIS/Core/Include/r-profile/armv7r.h | 4 +- CMSIS/Core/Include/r-profile/armv8r.h | 6 +- CMSIS/Core/Include/r-profile/armv8r_cp15.h | 8 +- CMSIS/Core/Include/r-profile/l1_cache.h | 19 ++ 6 files changed, 226 insertions(+), 166 deletions(-) create mode 100644 CMSIS/Core/Include/a-profile/l1_cache.h create mode 100644 CMSIS/Core/Include/r-profile/l1_cache.h diff --git a/CMSIS/Core/Include/a-profile/armv7a.h b/CMSIS/Core/Include/a-profile/armv7a.h index 2de64dfb6..cf99d25f5 100644 --- a/CMSIS/Core/Include/a-profile/armv7a.h +++ b/CMSIS/Core/Include/a-profile/armv7a.h @@ -831,20 +831,6 @@ typedef struct /* ########################## L1 Cache functions ################################# */ -/** \brief Enable Caches by setting I and C bits in SCTLR register. -*/ -__STATIC_FORCEINLINE void L1C_EnableCaches(void) { - __set_SCTLR( __get_SCTLR() | SCTLR_I_Msk | SCTLR_C_Msk); - __ISB(); -} - -/** \brief Disable Caches by clearing I and C bits in SCTLR register. -*/ -__STATIC_FORCEINLINE void L1C_DisableCaches(void) { - __set_SCTLR( __get_SCTLR() & (~SCTLR_I_Msk) & (~SCTLR_C_Msk)); - __ISB(); -} - /** \brief Enable Branch Prediction by setting Z bit in SCTLR register. */ __STATIC_FORCEINLINE void L1C_EnableBTAC(void) { @@ -867,154 +853,7 @@ __STATIC_FORCEINLINE void L1C_InvalidateBTAC(void) { __ISB(); //ensure instruction fetch path sees new state } -/** \brief Clean instruction cache line by address. -* \param [in] va Pointer to instructions to clear the cache for. -*/ -__STATIC_FORCEINLINE void L1C_InvalidateICacheMVA(void *va) { - __set_ICIMVAC((uint32_t)va); - __DSB(); //ensure completion of the invalidation - __ISB(); //ensure instruction fetch path sees new I cache state -} - -/** \brief Invalidate the whole instruction cache -*/ -__STATIC_FORCEINLINE void L1C_InvalidateICacheAll(void) { - __set_ICIALLU(0); - __DSB(); //ensure completion of the invalidation - __ISB(); //ensure instruction fetch path sees new I cache state -} - -/** \brief Clean data cache line by address. -* \param [in] va Pointer to data to clear the cache for. -*/ -__STATIC_FORCEINLINE void L1C_CleanDCacheMVA(void *va) { - __set_DCCMVAC((uint32_t)va); - __DMB(); //ensure the ordering of data cache maintenance operations and their effects -} - -/** \brief Invalidate data cache line by address. -* \param [in] va Pointer to data to invalidate the cache for. -*/ -__STATIC_FORCEINLINE void L1C_InvalidateDCacheMVA(void *va) { - __set_DCIMVAC((uint32_t)va); - __DMB(); //ensure the ordering of data cache maintenance operations and their effects -} - -/** \brief Clean and Invalidate data cache by address. -* \param [in] va Pointer to data to invalidate the cache for. -*/ -__STATIC_FORCEINLINE void L1C_CleanInvalidateDCacheMVA(void *va) { - __set_DCCIMVAC((uint32_t)va); - __DMB(); //ensure the ordering of data cache maintenance operations and their effects -} - -/** \brief Calculate log2 rounded up -* - log(0) => 0 -* - log(1) => 0 -* - log(2) => 1 -* - log(3) => 2 -* - log(4) => 2 -* - log(5) => 3 -* : : -* - log(16) => 4 -* - log(32) => 5 -* : : -* \param [in] n input value parameter -* \return log2(n) -*/ -__STATIC_FORCEINLINE uint8_t __log2_up(uint32_t n) -{ - if (n < 2U) { - return 0U; - } - uint8_t log = 0U; - uint32_t t = n; - while(t > 1U) - { - log++; - t >>= 1U; - } - if ((n & (n - 1U)) != 0U) { log++; } - return log; -} - -/** \brief Apply cache maintenance to given cache level. -* \param [in] level cache level to be maintained -* \param [in] maint 0 - invalidate, 1 - clean, otherwise - invalidate and clean -*/ -__STATIC_FORCEINLINE void __L1C_MaintainDCacheSetWay(uint32_t level, uint32_t maint) -{ - uint32_t Dummy; - uint32_t ccsidr; - uint32_t num_sets; - uint32_t num_ways; - uint32_t shift_way; - uint32_t log2_linesize; - uint8_t log2_num_ways; - - Dummy = level << 1U; - /* set csselr, select ccsidr register */ - __set_CSSELR(Dummy); - /* get current ccsidr register */ - ccsidr = __get_CCSIDR(); - num_sets = ((ccsidr & 0x0FFFE000U) >> 13U) + 1U; - num_ways = ((ccsidr & 0x00001FF8U) >> 3U) + 1U; - log2_linesize = (ccsidr & 0x00000007U) + 2U + 2U; - log2_num_ways = __log2_up(num_ways); - if (log2_num_ways > 32U) { - return; // FATAL ERROR - } - shift_way = 32U - log2_num_ways; - for(int32_t way = num_ways-1; way >= 0; way--) - { - for(int32_t set = num_sets-1; set >= 0; set--) - { - Dummy = (level << 1U) | (((uint32_t)set) << log2_linesize) | (((uint32_t)way) << shift_way); - switch (maint) - { - case 0U: __set_DCISW(Dummy); break; - case 1U: __set_DCCSW(Dummy); break; - default: __set_DCCISW(Dummy); break; - } - } - } - __DMB(); -} - -/** \brief Clean and Invalidate the entire data or unified cache -* \param [in] op 0 - invalidate, 1 - clean, otherwise - invalidate and clean -*/ -__STATIC_FORCEINLINE void L1C_CleanInvalidateCache(uint32_t op) { - uint32_t clidr; - uint32_t cache_type; - clidr = __get_CLIDR(); - for(uint32_t i = 0U; i<7U; i++) - { - cache_type = (clidr >> i*3U) & 0x7UL; - if ((cache_type >= 2U) && (cache_type <= 4U)) - { - __L1C_MaintainDCacheSetWay(i, op); - } - } -} - -/** \brief Invalidate the whole data cache. -*/ -__STATIC_FORCEINLINE void L1C_InvalidateDCacheAll(void) { - L1C_CleanInvalidateCache(0); -} - -/** \brief Clean the whole data cache. - */ -__STATIC_FORCEINLINE void L1C_CleanDCacheAll(void) { - L1C_CleanInvalidateCache(1); -} - -/** \brief Clean and invalidate the whole data cache. - */ -__STATIC_FORCEINLINE void L1C_CleanInvalidateDCacheAll(void) { - L1C_CleanInvalidateCache(2); -} +#include "l1_cache.h" /* ########################## L2 Cache functions ################################# */ #if (defined(__L2C_PRESENT) && (__L2C_PRESENT == 1U)) || \ diff --git a/CMSIS/Core/Include/a-profile/l1_cache.h b/CMSIS/Core/Include/a-profile/l1_cache.h new file mode 100644 index 000000000..04b3d9734 --- /dev/null +++ b/CMSIS/Core/Include/a-profile/l1_cache.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2009-2026 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 + * + * 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. + */ + +/* + * L1 Cache functions + */ + +#ifndef __ARM_V7A_L1_CACHE__ +#define __ARM_V7A_L1_CACHE__ + +/** \brief Enable Caches by setting I and C bits in SCTLR register. +*/ +__STATIC_FORCEINLINE void L1C_EnableCaches(void) { + __set_SCTLR( __get_SCTLR() | SCTLR_I_Msk | SCTLR_C_Msk); + __ISB(); +} + +/** \brief Disable Caches by clearing I and C bits in SCTLR register. +*/ +__STATIC_FORCEINLINE void L1C_DisableCaches(void) { + __set_SCTLR( __get_SCTLR() & (~SCTLR_I_Msk) & (~SCTLR_C_Msk)); + __ISB(); +} + +/** \brief Clean instruction cache line by address. +* \param [in] va Pointer to instructions to clear the cache for. +*/ +__STATIC_FORCEINLINE void L1C_InvalidateICacheMVA(void *va) { + __set_ICIMVAC((uint32_t)va); + __DSB(); //ensure completion of the invalidation + __ISB(); //ensure instruction fetch path sees new I cache state +} + +/** \brief Invalidate the whole instruction cache +*/ +__STATIC_FORCEINLINE void L1C_InvalidateICacheAll(void) { + __set_ICIALLU(0); + __DSB(); //ensure completion of the invalidation + __ISB(); //ensure instruction fetch path sees new I cache state +} + +/** \brief Clean data cache line by address. +* \param [in] va Pointer to data to clear the cache for. +*/ +__STATIC_FORCEINLINE void L1C_CleanDCacheMVA(void *va) { + __set_DCCMVAC((uint32_t)va); + __DMB(); //ensure the ordering of data cache maintenance operations and their effects +} + +/** \brief Invalidate data cache line by address. +* \param [in] va Pointer to data to invalidate the cache for. +*/ +__STATIC_FORCEINLINE void L1C_InvalidateDCacheMVA(void *va) { + __set_DCIMVAC((uint32_t)va); + __DMB(); //ensure the ordering of data cache maintenance operations and their effects +} + +/** \brief Clean and Invalidate data cache by address. +* \param [in] va Pointer to data to invalidate the cache for. +*/ +__STATIC_FORCEINLINE void L1C_CleanInvalidateDCacheMVA(void *va) { + __set_DCCIMVAC((uint32_t)va); + __DMB(); //ensure the ordering of data cache maintenance operations and their effects +} + +/** \brief Calculate log2 rounded up +* - log(0) => 0 +* - log(1) => 0 +* - log(2) => 1 +* - log(3) => 2 +* - log(4) => 2 +* - log(5) => 3 +* : : +* - log(16) => 4 +* - log(32) => 5 +* : : +* \param [in] n input value parameter +* \return log2(n) +*/ +__STATIC_FORCEINLINE uint8_t __log2_up(uint32_t n) +{ + if (n < 2U) { + return 0U; + } + uint8_t log = 0U; + uint32_t t = n; + while(t > 1U) + { + log++; + t >>= 1U; + } + if ((n & (n - 1U)) != 0U) { log++; } + return log; +} + +/** \brief Apply cache maintenance to given cache level. +* \param [in] level cache level to be maintained +* \param [in] maint 0 - invalidate, 1 - clean, otherwise - invalidate and clean +*/ +__STATIC_FORCEINLINE void __L1C_MaintainDCacheSetWay(uint32_t level, uint32_t maint) +{ + uint32_t Dummy; + uint32_t ccsidr; + uint32_t num_sets; + uint32_t num_ways; + uint32_t shift_way; + uint32_t log2_linesize; + uint8_t log2_num_ways; + + Dummy = level << 1U; + /* set csselr, select ccsidr register */ + __set_CSSELR(Dummy); + /* get current ccsidr register */ + ccsidr = __get_CCSIDR(); + num_sets = ((ccsidr & 0x0FFFE000U) >> 13U) + 1U; + num_ways = ((ccsidr & 0x00001FF8U) >> 3U) + 1U; + log2_linesize = (ccsidr & 0x00000007U) + 2U + 2U; + log2_num_ways = __log2_up(num_ways); + if (log2_num_ways > 32U) { + return; // FATAL ERROR + } + /* A direct-mapped cache has no way field; avoid a shift by 32. */ + shift_way = (log2_num_ways == 0U) ? 0U : 32U - log2_num_ways; + for(uint32_t way = num_ways; way > 0U; ) + { + --way; + for(uint32_t set = num_sets; set > 0U; ) + { + --set; + Dummy = (level << 1U) | (set << log2_linesize) | (way << shift_way); + switch (maint) + { + case 0U: __set_DCISW(Dummy); break; + case 1U: __set_DCCSW(Dummy); break; + default: __set_DCCISW(Dummy); break; + } + } + } + __DMB(); +} + +/** \brief Clean and Invalidate the entire data or unified cache +* \param [in] op 0 - invalidate, 1 - clean, otherwise - invalidate and clean +*/ +__STATIC_FORCEINLINE void L1C_CleanInvalidateCache(uint32_t op) { + uint32_t clidr; + uint32_t cache_type; + clidr = __get_CLIDR(); + for(uint32_t i = 0U; i<7U; i++) + { + cache_type = (clidr >> i*3U) & 0x7UL; + if ((cache_type >= 2U) && (cache_type <= 4U)) + { + __L1C_MaintainDCacheSetWay(i, op); + } + } +} + +/** \brief Invalidate the whole data cache. +*/ +__STATIC_FORCEINLINE void L1C_InvalidateDCacheAll(void) { + L1C_CleanInvalidateCache(0); +} + +/** \brief Clean the whole data cache. + */ +__STATIC_FORCEINLINE void L1C_CleanDCacheAll(void) { + L1C_CleanInvalidateCache(1); +} + +/** \brief Clean and invalidate the whole data cache. + */ +__STATIC_FORCEINLINE void L1C_CleanInvalidateDCacheAll(void) { + L1C_CleanInvalidateCache(2); +} + +#endif /* __ARM_V7A_L1_CACHE__ */ diff --git a/CMSIS/Core/Include/r-profile/armv7r.h b/CMSIS/Core/Include/r-profile/armv7r.h index 506a92ad1..8c6ecffae 100644 --- a/CMSIS/Core/Include/r-profile/armv7r.h +++ b/CMSIS/Core/Include/r-profile/armv7r.h @@ -460,9 +460,11 @@ typedef union #include "gicv2.h" #endif /* (__GIC_PRESENT == 1U) || defined(DOXYGEN) */ +#include "l1_cache.h" + #ifdef __cplusplus } #endif #endif /* __ARM_V7A_DEPENDANT */ -#endif /* __CMSIS_GENERIC */ \ No newline at end of file +#endif /* __CMSIS_GENERIC */ diff --git a/CMSIS/Core/Include/r-profile/armv8r.h b/CMSIS/Core/Include/r-profile/armv8r.h index 6597adcac..533d512e8 100644 --- a/CMSIS/Core/Include/r-profile/armv8r.h +++ b/CMSIS/Core/Include/r-profile/armv8r.h @@ -384,10 +384,14 @@ typedef union #define SCTLR_M_Pos 0U /*!< \brief SCTLR: M Position */ #define SCTLR_M_Msk (1UL << SCTLR_M_Pos) /*!< \brief SCTLR: M Mask */ +#ifndef __aarch64__ +#include "l1_cache.h" +#endif + #ifdef __cplusplus } #endif #endif /* __ARM_V8R_DEPENDANT */ -#endif /* __CMSIS_GENERIC */ \ No newline at end of file +#endif /* __CMSIS_GENERIC */ diff --git a/CMSIS/Core/Include/r-profile/armv8r_cp15.h b/CMSIS/Core/Include/r-profile/armv8r_cp15.h index 1322e25cb..00b90fe54 100644 --- a/CMSIS/Core/Include/r-profile/armv8r_cp15.h +++ b/CMSIS/Core/Include/r-profile/armv8r_cp15.h @@ -17,5 +17,9 @@ */ -// The gic-interface is located in the profile folder for cortex-a devices -#include "../a-profile/armv8a_cp15.h" \ No newline at end of file +// The cp15-interface is located in the profile folder for cortex-a devices +#ifdef __aarch64__ +#include "../a-profile/armv8a_cp15.h" +#else +#include "../a-profile/armv7a_cp15.h" +#endif diff --git a/CMSIS/Core/Include/r-profile/l1_cache.h b/CMSIS/Core/Include/r-profile/l1_cache.h new file mode 100644 index 000000000..7c20d7d46 --- /dev/null +++ b/CMSIS/Core/Include/r-profile/l1_cache.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2009-2026 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 + * + * 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 "../a-profile/l1_cache.h" From e3fefc4e5d357dc3c088576e00f8c91cfb521a89 Mon Sep 17 00:00:00 2001 From: Romain Mahoux Date: Thu, 3 Sep 2026 16:39:37 +0200 Subject: [PATCH 2/3] Fix L1C_InvalidateICacheMVA API documentation --- CMSIS/Core/Include/a-profile/l1_cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMSIS/Core/Include/a-profile/l1_cache.h b/CMSIS/Core/Include/a-profile/l1_cache.h index 04b3d9734..b0046ed16 100644 --- a/CMSIS/Core/Include/a-profile/l1_cache.h +++ b/CMSIS/Core/Include/a-profile/l1_cache.h @@ -37,7 +37,7 @@ __STATIC_FORCEINLINE void L1C_DisableCaches(void) { __ISB(); } -/** \brief Clean instruction cache line by address. +/** \brief Invalidate instruction cache line by address. * \param [in] va Pointer to instructions to clear the cache for. */ __STATIC_FORCEINLINE void L1C_InvalidateICacheMVA(void *va) { From eb412c3849212d14ca2c1717e45774f04a951f5f Mon Sep 17 00:00:00 2001 From: Romain Mahoux Date: Thu, 22 Oct 2026 10:02:51 +0200 Subject: [PATCH 3/3] Synchronize the cache selection before reading CCSIDR __L1C_MaintainDCacheSetWay() selects a cache by writing CSSELR and then reads CCSIDR to get the number of sets, the number of ways and the line size. An ISB is required between the two so that the new selection is used by the CCSIDR read (see ARM DDI 0406C.d, section B2.2). __set_CSSELR() is a bare CP15 MCR and does not imply an ISB. Co-authored-by: Jonatan Antoni --- CMSIS/Core/Include/a-profile/l1_cache.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMSIS/Core/Include/a-profile/l1_cache.h b/CMSIS/Core/Include/a-profile/l1_cache.h index b0046ed16..f7bad67c9 100644 --- a/CMSIS/Core/Include/a-profile/l1_cache.h +++ b/CMSIS/Core/Include/a-profile/l1_cache.h @@ -125,6 +125,8 @@ __STATIC_FORCEINLINE void __L1C_MaintainDCacheSetWay(uint32_t level, uint32_t ma Dummy = level << 1U; /* set csselr, select ccsidr register */ __set_CSSELR(Dummy); + /* Synchronize cache selection before reading CCSIDR. */ + __ISB(); /* get current ccsidr register */ ccsidr = __get_CCSIDR(); num_sets = ((ccsidr & 0x0FFFE000U) >> 13U) + 1U;