1 /* 2 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 /** 8 * This file provides an abstract OS API for entering and exiting critical sections. 9 * It furthermore provides macros to define and initialize an optional spinlock 10 * if the used chip is a multi-core chip. If a single-core chip is used, just disabling interrupts 11 * is sufficient to guarantee consecutive, non-interrupted execution of a critical section. 12 * Hence, the spinlock is unneccessary and will be automatically ommitted by the macros. 13 */ 14 #pragma once 15 16 #include "freertos/FreeRTOS.h" 17 #include "spinlock.h" 18 19 #ifdef __cplusplus 20 extern "C" { 21 #endif 22 23 #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2 24 /** 25 * This macro also helps users switching between spinlock declarations/definitions for multi-/single core environments 26 * if the macros below aren't sufficient. 27 */ 28 #define OS_SPINLOCK 1 29 #else 30 #define OS_SPINLOCK 0 31 #endif 32 33 #if OS_SPINLOCK == 1 34 typedef spinlock_t esp_os_spinlock_t; 35 #endif 36 37 /** 38 * Define and initialize a static (internal linking) lock for entering critical sections. 39 * 40 * Use this when all the critical sections are local inside a file. 41 * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary. 42 * 43 * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical* 44 * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems. 45 * 46 * @param lock_name Variable name of the lock. This will later be used to reference the declared lock. 47 * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are 48 * required. 49 * 50 * Example usage: 51 * @code{c} 52 * ... 53 * #include "os/critical_section.h" 54 * ... 55 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 56 * ... 57 * esp_os_enter_critical(&my_lock); 58 * ... 59 * esp_os_exit_critical(&my_lock); 60 * @endcode 61 */ 62 #if OS_SPINLOCK == 1 63 #define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...) static optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER 64 #else 65 #define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...) 66 #endif 67 68 /** 69 * Define and initialize a non-static (external linking) lock for entering critical sections. 70 * 71 * Locks defined by this macro can be linked among object files but this rather exceptional. 72 * Prefer the static lock definition whenever possible. 73 * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary. 74 * 75 * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical* 76 * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems. 77 * 78 * @param lock_name Variable name of the lock. This will later be used to reference the declared lock. 79 * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are 80 * required. 81 * 82 * Example usage: 83 * @code{c} 84 * ... 85 * #include "os/critical_section.h" 86 * ... 87 * DEFINE_CRIT_SECTION_LOCK(my_lock); // will have external linking (non-static) 88 * ... 89 * esp_os_enter_critical(&my_lock); 90 * ... 91 * esp_os_exit_critical(&my_lock); 92 * @endcode 93 */ 94 #if OS_SPINLOCK == 1 95 #define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...) optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER 96 #else 97 #define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...) 98 #endif 99 100 /** 101 * @brief This macro initializes a critical section lock at runtime. 102 * 103 * This macro basically creates a member of the initialization list, including the trailing comma. 104 * If the lock is unnecessary because the architecture is single-core, this macro will not do anything. 105 * This is incompatible with a lock created by DEFINE_CRIT_SECTION_LOCK_STATIC from above. 106 * 107 * @param lock_name Pointer to the lock. 108 * 109 * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical* 110 * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core 111 * systems. 112 * 113 * Example usage: 114 * @code{c} 115 * ... 116 * #include "os/critical_section.h" 117 * ... 118 * typedef struct protected_struct_t { 119 * int member1; 120 * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) 121 * int another_member; 122 * }; 123 * ... 124 * protected_struct_t my_protected; 125 * INIT_CRIT_SECTION_LOCK_IN_STRUCT(&(my_protected.my_lock)); 126 * }; 127 * @endcode 128 */ 129 #if OS_SPINLOCK == 1 130 #define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name) spinlock_initialize(lock_name) 131 #else 132 #define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name) 133 #endif 134 135 /** 136 * @brief This macro declares a critical section lock as a member of a struct. 137 * 138 * The critical section lock member is only declared if built for multi-core systems, otherwise it is omitted. 139 * 140 * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical* 141 * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core 142 * systems. 143 * @note Do NOT add any semicolon after declaring the member with this macro. 144 * The trailing semicolon is included in the macro, otherwise -Wpedantic would complain about 145 * superfluous ";" if OS_SPINLOCK == 0. 146 * 147 * Example usage: 148 * @code{c} 149 * ... 150 * #include "os/critical_section.h" 151 * ... 152 * typedef struct protected_struct_t { 153 * int member1; 154 * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no semicolon! 155 * int another_member; 156 * }; 157 * @endcode 158 */ 159 #if OS_SPINLOCK == 1 160 #define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) esp_os_spinlock_t lock_name; 161 #else 162 #define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) 163 #endif 164 165 /** 166 * @brief This macro initializes a critical section lock as a member of a struct when using an list initialization. 167 * It has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to work. 168 * 169 * This macro basically creates a member of the initialization list, including the trailing comma. 170 * If the lock is unnecessary because the architecture is single-core, this macro will not do anything. 171 * This means that if \c lock_name is still a member of the struct, \c lock_name will be uninitialized. 172 * Hence, this macro has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to correctly to declare 173 * or omit the struct member \c lock_name. 174 * 175 * @param lock_name The field name of the lock inside the struct. 176 * 177 * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical* 178 * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core 179 * systems. 180 * @note Do NOT add any comma in the initializer list after using this macro. 181 * 182 * Example usage: 183 * @code{c} 184 * ... 185 * #include "os/critical_section.h" 186 * ... 187 * typedef struct protected_struct_t { 188 * int member1; 189 * DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) 190 * int another_member; 191 * }; 192 * ... 193 * protected_struct_t my_protected = { 194 * .member1 = 0, 195 * INIT_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no comma! 196 * another_member = 47, 197 * }; 198 * @endcode 199 */ 200 #if OS_SPINLOCK == 1 201 #define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) .lock_name = portMUX_INITIALIZER_UNLOCKED, 202 #else 203 #define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) 204 #endif 205 206 /** 207 * @brief Enter a critical section, i.e., a section that will not be interrupted by any other task or interrupt. 208 * 209 * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a 210 * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only. 211 * 212 * @note This macro MUST be used together with any of the initialization macros, e.g. 213 * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables. 214 * 215 * @param lock Pointer to the critical section lock. Ignored if build for single core system. 216 * 217 * Example usage with static locks: 218 * @code{c} 219 * ... 220 * #include "os/critical_section.h" 221 * ... 222 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 223 * ... 224 * esp_os_enter_critical(&my_lock); 225 * // code inside critical section 226 * esp_os_exit_critical(&my_lock); 227 * @endcode 228 */ 229 #if OS_SPINLOCK == 1 230 #define esp_os_enter_critical(lock) portENTER_CRITICAL(lock) 231 #else 232 #define esp_os_enter_critical(lock) vPortEnterCritical() 233 #endif 234 235 /** 236 * @brief Exit a critical section. 237 * 238 * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a 239 * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only. 240 * 241 * @note This macro MUST be used together with any of the initialization macros, e.g. 242 * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables. 243 * 244 * @param lock Pointer to the critical section lock. Ignored if build for single core system. 245 * 246 * Example usage with static locks: 247 * @code{c} 248 * ... 249 * #include "os/critical_section.h" 250 * ... 251 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 252 * ... 253 * esp_os_enter_critical(&my_lock); 254 * // code inside critical section 255 * esp_os_exit_critical(&my_lock); 256 * @endcode 257 */ 258 #if OS_SPINLOCK == 1 259 #define esp_os_exit_critical(lock) portEXIT_CRITICAL(lock) 260 #else 261 #define esp_os_exit_critical(lock) vPortExitCritical() 262 #endif 263 264 /** 265 * @brief Enter a critical section while from ISR. 266 * 267 * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a 268 * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only. 269 * 270 * @note This macro MUST be used together with any of the initialization macros, e.g. 271 * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables. 272 * 273 * @param lock Pointer to the critical section lock. Ignored if build for single core system. 274 * 275 * Example usage with static locks: 276 * @code{c} 277 * ... 278 * #include "os/critical_section.h" 279 * ... 280 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 281 * ... 282 * esp_os_enter_critical(&my_lock); 283 * // code inside critical section 284 * esp_os_exit_critical(&my_lock); 285 * @endcode 286 */ 287 #if OS_SPINLOCK == 1 288 #define esp_os_enter_critical_isr(lock) portENTER_CRITICAL_ISR(lock) 289 #else 290 #define esp_os_enter_critical_isr(lock) vPortEnterCritical() 291 #endif 292 293 /** 294 * @brief Exit a critical section after entering from ISR. 295 * 296 * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a 297 * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only. 298 * 299 * @note This macro MUST be used together with any of the initialization macros, e.g. 300 * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables. 301 * 302 * @param lock Pointer to the critical section lock. Ignored if build for single core system. 303 * 304 * Example usage with static locks: 305 * @code{c} 306 * ... 307 * #include "os/critical_section.h" 308 * ... 309 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 310 * ... 311 * esp_os_enter_critical(&my_lock); 312 * // code inside critical section 313 * esp_os_exit_critical(&my_lock); 314 * @endcode 315 */ 316 #if OS_SPINLOCK == 1 317 #define esp_os_exit_critical_isr(lock) portEXIT_CRITICAL_ISR(lock) 318 #else 319 #define esp_os_exit_critical_isr(lock) vPortExitCritical() 320 #endif 321 322 /** 323 * @brief Enter a critical section from normal task or ISR. This macro will check if the current CPU is processing 324 * an ISR or not and enter the critical section accordingly. 325 * 326 * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a 327 * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only. 328 * 329 * @note This macro MUST be used together with any of the initialization macros, e.g. 330 * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables. 331 * 332 * @param lock Pointer to the critical section lock. Ignored if build for single core system. 333 * 334 * Example usage with static locks: 335 * @code{c} 336 * ... 337 * #include "os/critical_section.h" 338 * ... 339 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 340 * ... 341 * esp_os_enter_critical(&my_lock); 342 * // code inside critical section 343 * esp_os_exit_critical(&my_lock); 344 * @endcode 345 */ 346 #if OS_SPINLOCK == 1 347 #define esp_os_enter_critical_safe(lock) portENTER_CRITICAL_SAFE(lock) 348 #else 349 #define esp_os_enter_critical_safe(lock) vPortEnterCritical() 350 #endif 351 352 /** 353 * @brief Exit a critical section after entering via esp_os_enter_critical_safe. 354 * 355 * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a 356 * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only. 357 * 358 * @note This macro MUST be used together with any of the initialization macros, e.g. 359 * DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables. 360 * 361 * @param lock Pointer to the critical section lock. Ignored if build for single core system. 362 * 363 * Example usage with static locks: 364 * @code{c} 365 * ... 366 * #include "os/critical_section.h" 367 * ... 368 * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static) 369 * ... 370 * esp_os_enter_critical(&my_lock); 371 * // code inside critical section 372 * esp_os_exit_critical(&my_lock); 373 * @endcode 374 */ 375 #if OS_SPINLOCK == 1 376 #define esp_os_exit_critical_safe(lock) portEXIT_CRITICAL_SAFE(lock) 377 #else 378 #define esp_os_exit_critical_safe(lock) vPortExitCritical() 379 #endif 380 381 #ifdef __cplusplus 382 } 383 #endif 384