Просмотр исходного кода

Merge branch 'feature/critical_section_api' into 'master'

esp_system: critical section api

Closes IDF-4754

See merge request espressif/esp-idf!19019
Jakob Hasse 3 лет назад
Родитель
Сommit
bcb34ca7ae
1 измененных файлов с 383 добавлено и 0 удалено
  1. 383 0
      components/esp_system/include/esp_private/critical_section.h

+ 383 - 0
components/esp_system/include/esp_private/critical_section.h

@@ -0,0 +1,383 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This file provides an abstract OS API for entering and exiting critical sections.
+ * It furthermore provides macros to define and initialize an optional spinlock
+ * if the used chip is a multi-core chip. If a single-core chip is used, just disabling interrupts
+ * is sufficient to guarantee consecutive, non-interrupted execution of a critical section.
+ * Hence, the spinlock is unneccessary and will be automatically ommitted by the macros.
+ */
+#pragma once
+
+#include "freertos/portmacro.h"
+#include "spinlock.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if CONFIG_FREERTOS_UNICORE && !CONFIG_IDF_TARGET_ESP32S2
+/**
+ * This macro also helps users switching between spinlock declarations/definitions for multi-/single core environments
+ * if the macros below aren't sufficient.
+ */
+#define OS_SPINLOCK 0
+#else
+#define OS_SPINLOCK 1
+#endif
+
+#if OS_SPINLOCK == 1
+typedef spinlock_t esp_os_spinlock_t;
+#endif
+
+/**
+ * Define and initialize a static (internal linking) lock for entering critical sections.
+ *
+ * Use this when all the critical sections are local inside a file.
+ * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary.
+ *
+ * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
+ * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems.
+ *
+ * @param lock_name Variable name of the lock. This will later be used to reference the declared lock.
+ * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are
+ *        required.
+ *
+ * Example usage:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * ...
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...) static optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER
+#else
+#define DEFINE_CRIT_SECTION_LOCK_STATIC(lock_name, optional_qualifiers...)
+#endif
+
+/**
+ * Define and initialize a non-static (external linking) lock for entering critical sections.
+ *
+ * Locks defined by this macro can be linked among object files but this rather exceptional.
+ * Prefer the static lock definition whenever possible.
+ * The lock will only be defined if built for a multi-core system, otherwise it is unnecessary.
+ *
+ * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
+ * MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core systems.
+ *
+ * @param lock_name Variable name of the lock. This will later be used to reference the declared lock.
+ * @param optional_qualifiers Qualifiers such as DRAM_ATTR and other attributes. Can be omitted if no qualifiers are
+ *        required.
+ *
+ * Example usage:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK(my_lock); // will have external linking (non-static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * ...
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...) optional_qualifiers esp_os_spinlock_t lock_name = SPINLOCK_INITIALIZER
+#else
+#define DEFINE_CRIT_SECTION_LOCK(lock_name, optional_qualifiers...)
+#endif
+
+/**
+ * @brief This macro initializes a critical section lock at runtime.
+ *
+ * This macro basically creates a member of the initialization list, including the trailing comma.
+ * If the lock is unnecessary because the architecture is single-core, this macro will not do anything.
+ * This is incompatible with a lock created by DEFINE_CRIT_SECTION_LOCK_STATIC from above.
+ *
+ * @param lock_name Pointer to the lock.
+ *
+ * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
+ *       MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
+ *       systems.
+ *
+ * Example usage:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * typedef struct protected_struct_t {
+ *     int member1;
+ *     DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock)
+ *     int another_member;
+ * };
+ * ...
+ * protected_struct_t my_protected;
+ * INIT_CRIT_SECTION_LOCK_IN_STRUCT(&(my_protected.my_lock));
+ * };
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name) spinlock_initialize(lock_name)
+#else
+#define INIT_CRIT_SECTION_LOCK_RUNTIME(lock_name)
+#endif
+
+/**
+ * @brief This macro declares a critical section lock as a member of a struct.
+ *
+ * The critical section lock member is only declared if built for multi-core systems, otherwise it is omitted.
+ *
+ * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
+ *       MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
+ *       systems.
+ * @note Do NOT add any semicolon after declaring the member with this macro.
+ *       The trailing semicolon is included in the macro, otherwise -Wpedantic would complain about
+ *       superfluous ";" if OS_SPINLOCK == 0.
+ *
+ * Example usage:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * typedef struct protected_struct_t {
+ *     int member1;
+ *     DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no semicolon!
+ *     int another_member;
+ * };
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) esp_os_spinlock_t lock_name;
+#else
+#define DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(lock_name)
+#endif
+
+/**
+ * @brief This macro initializes a critical section lock as a member of a struct when using an list initialization.
+ *        It has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to work.
+ *
+ * This macro basically creates a member of the initialization list, including the trailing comma.
+ * If the lock is unnecessary because the architecture is single-core, this macro will not do anything.
+ * This means that if \c lock_name is still a member of the struct, \c lock_name will be uninitialized.
+ * Hence, this macro has to be used together with \c DECLARE_CRIT_SECTION_LOCK_IN_STRUCT() to correctly to declare
+ * or omit the struct member \c lock_name.
+ *
+ * @param lock_name The field name of the lock inside the struct.
+ *
+ * @note When using this macro, the critical section macros esp_os_enter_critical* and esp_os_exit_critical*
+ *       MUST be used, otherwise normal functions would be passed an undefined variable when build for single-core
+ *       systems.
+ * @note Do NOT add any comma in the initializer list after using this macro.
+ *
+ * Example usage:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * typedef struct protected_struct_t {
+ *     int member1;
+ *     DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(my_lock)
+ *     int another_member;
+ * };
+ * ...
+ * protected_struct_t my_protected = {
+ *     .member1 = 0,
+ *     INIT_CRIT_SECTION_LOCK_IN_STRUCT(my_lock) // no comma!
+ *     another_member = 47,
+ * };
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name) .lock_name = portMUX_INITIALIZER_UNLOCKED,
+#else
+#define INIT_CRIT_SECTION_LOCK_IN_STRUCT(lock_name)
+#endif
+
+/**
+ * @brief Enter a critical section, i.e., a section that will not be interrupted by any other task or interrupt.
+ *
+ * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
+ * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
+ *
+ * @note This macro MUST be used together with any of the initialization macros, e.g.
+ *       DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
+ *
+ * @param lock Pointer to the critical section lock. Ignored if build for single core system.
+ *
+ * Example usage with static locks:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * // code inside critical section
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define esp_os_enter_critical(lock)			portENTER_CRITICAL(lock)
+#else
+#define esp_os_enter_critical(lock)			vPortEnterCritical()
+#endif
+
+/**
+ * @brief Exit a critical section.
+ *
+ * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
+ * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
+ *
+ * @note This macro MUST be used together with any of the initialization macros, e.g.
+ *       DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
+ *
+ * @param lock Pointer to the critical section lock. Ignored if build for single core system.
+ *
+ * Example usage with static locks:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * // code inside critical section
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define esp_os_exit_critical(lock)			portEXIT_CRITICAL(lock)
+#else
+#define esp_os_exit_critical(lock)			vPortExitCritical()
+#endif
+
+/**
+ * @brief Enter a critical section while from ISR.
+ *
+ * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
+ * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
+ *
+ * @note This macro MUST be used together with any of the initialization macros, e.g.
+ *       DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
+ *
+ * @param lock Pointer to the critical section lock. Ignored if build for single core system.
+ *
+ * Example usage with static locks:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * // code inside critical section
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define esp_os_enter_critical_isr(lock)		portENTER_CRITICAL_ISR(lock)
+#else
+#define esp_os_enter_critical_isr(lock)		vPortEnterCritical()
+#endif
+
+/**
+ * @brief Exit a critical section after entering from ISR.
+ *
+ * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
+ * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
+ *
+ * @note This macro MUST be used together with any of the initialization macros, e.g.
+ *       DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
+ *
+ * @param lock Pointer to the critical section lock. Ignored if build for single core system.
+ *
+ * Example usage with static locks:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * // code inside critical section
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define esp_os_exit_critical_isr(lock)		portEXIT_CRITICAL_ISR(lock)
+#else
+#define esp_os_exit_critical_isr(lock)		vPortExitCritical()
+#endif
+
+/**
+ * @brief Enter a critical section from normal task or ISR. This macro will check if the current CPU is processing
+ *        an ISR or not and enter the critical section accordingly.
+ *
+ * On multi-core systems, this will disable interrupts and take the spinlock \c lock. On single core systems, a
+ * spinlock is unncessary, hence \c lock is ignored and interrupts are disabled only.
+ *
+ * @note This macro MUST be used together with any of the initialization macros, e.g.
+ *       DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
+ *
+ * @param lock Pointer to the critical section lock. Ignored if build for single core system.
+ *
+ * Example usage with static locks:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * // code inside critical section
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define esp_os_enter_critical_safe(lock)	portENTER_CRITICAL_SAFE(lock)
+#else
+#define esp_os_enter_critical_safe(lock)	vPortEnterCritical()
+#endif
+
+/**
+ * @brief Exit a critical section after entering via esp_os_enter_critical_safe.
+ *
+ * On multi-core systems, this will enable interrupts and release the spinlock \c lock. On single core systems, a
+ * spinlock is unncessary, hence \c lock is ignored and interrupts are enabled only.
+ *
+ * @note This macro MUST be used together with any of the initialization macros, e.g.
+ *       DEFINE_CRIT_SECTION_LOCK_STATIC. If not, there may be unused variables.
+ *
+ * @param lock Pointer to the critical section lock. Ignored if build for single core system.
+ *
+ * Example usage with static locks:
+ * @code{c}
+ * ...
+ * #include "os/critical_section.h"
+ * ...
+ * DEFINE_CRIT_SECTION_LOCK_STATIC(my_lock); // will have internal linking (static)
+ * ...
+ * esp_os_enter_critical(&my_lock);
+ * // code inside critical section
+ * esp_os_exit_critical(&my_lock);
+ * @endcode
+ */
+#if OS_SPINLOCK == 1
+#define esp_os_exit_critical_safe(lock)		portEXIT_CRITICAL_SAFE(lock)
+#else
+#define esp_os_exit_critical_safe(lock)		vPortExitCritical()
+#endif
+
+#ifdef __cplusplus
+}
+#endif