You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
12 KiB

12 months ago
  1. /**
  2. ******************************************************************************
  3. * @file stm32_lock.h
  4. * @author STMicroelectronics
  5. * @brief STMicroelectronics lock mechanisms
  6. *
  7. * @details
  8. * This implementation supports the following strategies for handling
  9. * thread-safe locks. The strategy can be explicitly selected by
  10. * defining <tt>\STM32_THREAD_SAFE_STRATEGY = \<number></tt> in the project.
  11. * Please look at the '<toolchain/library>_lock_glue.c' file for more details.
  12. *
  13. * 1. User defined thread-safe implementation.
  14. * User defined solution for handling thread-safety.
  15. * <br>
  16. * <b>NOTE:</b> The stubs in stm32_lock_user.h needs to be implemented to gain
  17. * thread-safety.
  18. *
  19. * 2. [<b>DEFAULT</b>] Allow lock usage from interrupts.
  20. * This implementation will ensure thread-safety by disabling all interrupts
  21. * during e.g. calls to malloc.
  22. * <br>
  23. * <b>NOTE:</b> Disabling all interrupts creates interrupt latency which
  24. * might not be desired for this application!
  25. *
  26. * 3. Deny lock usage from interrupts.
  27. * This implementation assumes single thread of execution.
  28. * <br>
  29. * <b>NOTE:</b> Thread-safety dependent functions will enter an infinity loop
  30. * if used in interrupt context.
  31. *
  32. * 4. Allow lock usage from interrupts. Implemented using FreeRTOS locks.
  33. * This implementation will ensure thread-safety by entering RTOS ISR capable
  34. * critical sections during e.g. calls to malloc.
  35. * By default this implementation supports 2 levels of recursive locking.
  36. * Adding additional levels requires 4 bytes per lock per level of RAM.
  37. * <br>
  38. * <b>NOTE:</b> Interrupts with high priority are not disabled. This implies
  39. * that the lock is not thread-safe from high priority interrupts!
  40. *
  41. * 5. Deny lock usage from interrupts. Implemented using FreeRTOS locks.
  42. * This implementation will ensure thread-safety by suspending all tasks
  43. * during e.g. calls to malloc.
  44. * <br>
  45. * <b>NOTE:</b> Thread-safety dependent functions will enter an infinity loop
  46. * if used in interrupt context.
  47. *
  48. ******************************************************************************
  49. * @attention
  50. *
  51. * Copyright (c) 2024 STMicroelectronics.
  52. * All rights reserved.
  53. *
  54. * This software is licensed under terms that can be found in the LICENSE file
  55. * in the root directory of this software component.
  56. * If no LICENSE file comes with this software, it is provided AS-IS.
  57. *
  58. ******************************************************************************
  59. */
  60. #ifndef __STM32_LOCK_H__
  61. #define __STM32_LOCK_H__
  62. /* Includes ------------------------------------------------------------------*/
  63. #include <stdint.h>
  64. #include <stddef.h>
  65. #include <cmsis_compiler.h>
  66. #ifndef STM32_THREAD_SAFE_STRATEGY
  67. #define STM32_THREAD_SAFE_STRATEGY 2 /**< Assume strategy 2 if not specified */
  68. #endif /* STM32_THREAD_SAFE_STRATEGY */
  69. #ifdef __cplusplus
  70. extern "C" {
  71. #endif /* __cplusplus */
  72. /* Function prototypes -------------------------------------------------------*/
  73. void Error_Handler(void);
  74. /* Public macros -------------------------------------------------------------*/
  75. /** Blocks execution */
  76. #define STM32_LOCK_BLOCK() \
  77. do \
  78. { \
  79. __disable_irq(); \
  80. Error_Handler(); \
  81. while (1); \
  82. } while (0)
  83. /** Blocks execution if argument is NULL */
  84. #define STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(x) \
  85. do \
  86. { \
  87. if ((x) == NULL) \
  88. { \
  89. STM32_LOCK_BLOCK(); \
  90. } \
  91. } while (0)
  92. /** Blocks execution if in interrupt context */
  93. #define STM32_LOCK_BLOCK_IF_INTERRUPT_CONTEXT() \
  94. do \
  95. { \
  96. if (__get_IPSR()) \
  97. { \
  98. STM32_LOCK_BLOCK(); \
  99. } \
  100. } while (0)
  101. /** Hide unused parameter warning from compiler */
  102. #define STM32_LOCK_UNUSED(var) (void)var
  103. /** Size of array */
  104. #define STM32_LOCK_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
  105. #if STM32_THREAD_SAFE_STRATEGY == 1
  106. /*
  107. * User defined thread-safe implementation.
  108. */
  109. /* Includes ----------------------------------------------------------------*/
  110. /** STM32 lock API version */
  111. #define STM32_LOCK_API 1
  112. #include "stm32_lock_user.h"
  113. #undef STM32_LOCK_API
  114. #elif STM32_THREAD_SAFE_STRATEGY == 2
  115. /*
  116. * Allow lock usage from interrupts.
  117. */
  118. /* Private defines ---------------------------------------------------------*/
  119. /** Initialize members in instance of <code>LockingData_t</code> structure */
  120. #define LOCKING_DATA_INIT { 0, 0 }
  121. /* Private typedef ---------------------------------------------------------*/
  122. typedef struct
  123. {
  124. uint8_t flag; /**< Backup of PRIMASK.PM at nesting level 0 */
  125. uint8_t counter; /**< Nesting level */
  126. } LockingData_t;
  127. /* Private functions -------------------------------------------------------*/
  128. /**
  129. * @brief Initialize STM32 lock
  130. * @param lock The lock to init
  131. */
  132. static inline void stm32_lock_init(LockingData_t *lock)
  133. {
  134. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  135. lock->flag = 0;
  136. lock->counter = 0;
  137. }
  138. /**
  139. * @brief Acquire STM32 lock
  140. * @param lock The lock to acquire
  141. */
  142. static inline void stm32_lock_acquire(LockingData_t *lock)
  143. {
  144. uint8_t flag = (uint8_t)(__get_PRIMASK() & 0x1); /* PRIMASK.PM */
  145. __disable_irq();
  146. __DSB();
  147. __ISB();
  148. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  149. if (lock->counter == 0)
  150. {
  151. lock->flag = flag;
  152. }
  153. else if (lock->counter == UINT8_MAX)
  154. {
  155. STM32_LOCK_BLOCK();
  156. }
  157. lock->counter++;
  158. }
  159. /**
  160. * @brief Release STM32 lock
  161. * @param lock The lock to release
  162. */
  163. static inline void stm32_lock_release(LockingData_t *lock)
  164. {
  165. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  166. if (lock->counter == 0)
  167. {
  168. STM32_LOCK_BLOCK();
  169. }
  170. lock->counter--;
  171. if (lock->counter == 0 && lock->flag == 0)
  172. {
  173. __enable_irq();
  174. }
  175. }
  176. #elif STM32_THREAD_SAFE_STRATEGY == 3
  177. /*
  178. * Deny lock usage from interrupts.
  179. */
  180. /* Private defines ---------------------------------------------------------*/
  181. /** Initialize members in instance of <code>LockingData_t</code> structure */
  182. #define LOCKING_DATA_INIT 0
  183. /* Private typedef ---------------------------------------------------------*/
  184. typedef uint8_t LockingData_t; /**< Unused */
  185. /* Private functions -------------------------------------------------------*/
  186. /**
  187. * @brief Initialize STM32 lock
  188. * @param lock The lock to init
  189. */
  190. static inline void stm32_lock_init(LockingData_t *lock)
  191. {
  192. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  193. }
  194. /**
  195. * @brief Acquire STM32 lock
  196. * @param lock The lock to acquire
  197. */
  198. static inline void stm32_lock_acquire(LockingData_t *lock)
  199. {
  200. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  201. STM32_LOCK_BLOCK_IF_INTERRUPT_CONTEXT();
  202. }
  203. /**
  204. * @brief Release ST lock
  205. * @param lock The lock to release
  206. */
  207. static inline void stm32_lock_release(LockingData_t *lock)
  208. {
  209. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  210. STM32_LOCK_BLOCK_IF_INTERRUPT_CONTEXT();
  211. }
  212. #elif STM32_THREAD_SAFE_STRATEGY == 4
  213. /*
  214. * Allow lock usage from interrupts. Implemented using FreeRTOS locks.
  215. */
  216. /* Includes ----------------------------------------------------------------*/
  217. #include <FreeRTOS.h>
  218. #include <task.h>
  219. #if defined (__GNUC__) && !defined (__CC_ARM) && configUSE_NEWLIB_REENTRANT == 0
  220. #warning Please set configUSE_NEWLIB_REENTRANT to 1 in FreeRTOSConfig.h, otherwise newlib will not be thread-safe
  221. #endif /* defined (__GNUC__) && !defined (__CC_ARM) && configUSE_NEWLIB_REENTRANT == 0 */
  222. /* Private defines ---------------------------------------------------------*/
  223. /** Initialize members in instance of <code>LockingData_t</code> structure */
  224. #define LOCKING_DATA_INIT { {0, 0}, 0 }
  225. #define STM32_LOCK_MAX_NESTED_LEVELS 2 /**< Max nesting level of interrupts */
  226. typedef struct
  227. {
  228. uint32_t basepri[STM32_LOCK_MAX_NESTED_LEVELS];
  229. uint8_t nesting_level;
  230. } LockingData_t;
  231. /* Private macros ----------------------------------------------------------*/
  232. /** Blocks execution if reached max nesting level */
  233. #define STM32_LOCK_ASSERT_VALID_NESTING_LEVEL(lock) \
  234. do \
  235. { \
  236. if (lock->nesting_level >= STM32_LOCK_ARRAY_SIZE(lock->basepri)) \
  237. { \
  238. STM32_LOCK_BLOCK(); \
  239. } \
  240. } while (0)
  241. /* Private functions -------------------------------------------------------*/
  242. /**
  243. * @brief Initialize STM32 lock
  244. * @param lock The lock to init
  245. */
  246. static inline void stm32_lock_init(LockingData_t *lock)
  247. {
  248. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  249. for (size_t i = 0; i < STM32_LOCK_ARRAY_SIZE(lock->basepri); i++)
  250. {
  251. lock->basepri[i] = 0;
  252. }
  253. lock->nesting_level = 0;
  254. }
  255. /**
  256. * @brief Acquire STM32 lock
  257. * @param lock The lock to acquire
  258. */
  259. static inline void stm32_lock_acquire(LockingData_t *lock)
  260. {
  261. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  262. STM32_LOCK_ASSERT_VALID_NESTING_LEVEL(lock);
  263. lock->basepri[lock->nesting_level++] = taskENTER_CRITICAL_FROM_ISR();
  264. }
  265. /**
  266. * @brief Release STM32 lock
  267. * @param lock The lock to release
  268. */
  269. static inline void stm32_lock_release(LockingData_t *lock)
  270. {
  271. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  272. lock->nesting_level--;
  273. STM32_LOCK_ASSERT_VALID_NESTING_LEVEL(lock);
  274. taskEXIT_CRITICAL_FROM_ISR(lock->basepri[lock->nesting_level]);
  275. }
  276. #undef STM32_LOCK_ASSERT_VALID_NESTING_LEVEL
  277. #undef STM32_LOCK_MAX_NESTED_LEVELS
  278. #elif STM32_THREAD_SAFE_STRATEGY == 5
  279. /*
  280. * Deny lock usage from interrupts. Implemented using FreeRTOS locks.
  281. */
  282. /* Includes ----------------------------------------------------------------*/
  283. #include <FreeRTOS.h>
  284. #include <task.h>
  285. #if defined (__GNUC__) && !defined (__CC_ARM) && configUSE_NEWLIB_REENTRANT == 0
  286. #warning Please set configUSE_NEWLIB_REENTRANT to 1 in FreeRTOSConfig.h, otherwise newlib will not be thread-safe
  287. #endif /* defined (__GNUC__) && !defined (__CC_ARM) && configUSE_NEWLIB_REENTRANT == 0 */
  288. /* Private defines ---------------------------------------------------------*/
  289. /** Initialize members in instance of <code>LockingData_t</code> structure */
  290. #define LOCKING_DATA_INIT 0
  291. /* Private typedef ---------------------------------------------------------*/
  292. typedef uint8_t LockingData_t; /**< Unused */
  293. /* Private functions -------------------------------------------------------*/
  294. /**
  295. * @brief Initialize STM32 lock
  296. * @param lock The lock to init
  297. */
  298. static inline void stm32_lock_init(LockingData_t *lock)
  299. {
  300. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  301. }
  302. /**
  303. * @brief Acquire STM32 lock
  304. * @param lock The lock to acquire
  305. */
  306. static inline void stm32_lock_acquire(LockingData_t *lock)
  307. {
  308. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  309. STM32_LOCK_BLOCK_IF_INTERRUPT_CONTEXT();
  310. vTaskSuspendAll();
  311. }
  312. /**
  313. * @brief Release STM32 lock
  314. * @param lock The lock to release
  315. */
  316. static inline void stm32_lock_release(LockingData_t *lock)
  317. {
  318. STM32_LOCK_BLOCK_IF_NULL_ARGUMENT(lock);
  319. STM32_LOCK_BLOCK_IF_INTERRUPT_CONTEXT();
  320. xTaskResumeAll();
  321. }
  322. #else
  323. #error Invalid STM32_THREAD_SAFE_STRATEGY specified
  324. #endif /* STM32_THREAD_SAFE_STRATEGY */
  325. #ifdef __cplusplus
  326. } /* extern "C" */
  327. #endif /* __cplusplus */
  328. #endif /* __STM32_LOCK_H__ */