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

ulp-riscv: added lock API to provide mutual exclusion when sharing variables between the main CPU and the ULP.

Marius Vikhammer 3 лет назад
Родитель
Сommit
ffed60cc93

+ 3 - 1
components/ulp/CMakeLists.txt

@@ -24,10 +24,12 @@ if(CONFIG_SOC_ULP_SUPPORTED OR CONFIG_SOC_RISCV_COPROC_SUPPORTED)
     elseif(CONFIG_ULP_COPROC_TYPE_RISCV)
         list(APPEND srcs
             "ulp_riscv/ulp_riscv.c"
+            "ulp_riscv/ulp_riscv_lock.c"
             "ulp_riscv/ulp_riscv_adc.c")
 
         list(APPEND includes
-            ulp_riscv/include)
+            ulp_riscv/include
+            ulp_riscv/shared/include)
     endif()
 endif()
 

+ 3 - 1
components/ulp/cmake/CMakeLists.txt

@@ -75,6 +75,7 @@ if(ULP_COCPU_IS_RISCV)
     list(APPEND ULP_S_SOURCES
         "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/start.S"
         "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_adc.c"
+        "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c"
         "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c"
         "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_print.c"
         "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c")
@@ -100,7 +101,8 @@ if(ULP_COCPU_IS_RISCV)
     list(APPEND EXTRA_LINKER_ARGS "-Wl,--gc-sections")
     list(APPEND EXTRA_LINKER_ARGS "-Wl,-Map=\"${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map\"")
     #Makes the csr utillies for riscv visible:
-    target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/include")
+    target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/include"
+                                                       "${IDF_PATH}/components/ulp/ulp_riscv/shared/include")
     target_link_libraries(${ULP_APP_NAME} "-T \"${IDF_PATH}/components/ulp/ld/${IDF_TARGET}.periperals.ld\"")
     target_compile_definitions(${ULP_APP_NAME} PRIVATE IS_ULP_COCPU)
 

+ 1 - 0
components/ulp/test_apps/ulp_riscv/main/CMakeLists.txt

@@ -2,6 +2,7 @@ set(app_sources "test_app_main.c" "test_ulp_riscv.c")
 set(ulp_sources "ulp/test_main.c")
 
 idf_component_register(SRCS ${app_sources}
+                       INCLUDE_DIRS "ulp"
                        REQUIRES ulp unity
                        WHOLE_ARCHIVE)
 

+ 30 - 15
components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c

@@ -12,27 +12,14 @@
 #include "soc/sens_reg.h"
 #include "soc/rtc_periph.h"
 #include "ulp_riscv.h"
+#include "ulp_riscv_lock.h"
 #include "ulp_test_app.h"
+#include "ulp_test_shared.h"
 #include "unity.h"
 #include <sys/time.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 
-typedef enum{
-    RISCV_READ_WRITE_TEST = 1,
-    RISCV_DEEP_SLEEP_WAKEUP_TEST,
-    RISCV_LIGHT_SLEEP_WAKEUP_TEST,
-    RISCV_STOP_TEST,
-    RISCV_NO_COMMAND,
-} riscv_test_commands_t;
-
-typedef enum {
-    RISCV_COMMAND_OK = 1,
-    RISCV_COMMAND_NOK,
-    RISCV_COMMAND_INVALID,
-} riscv_test_command_reply_t;
-
-#define XOR_MASK 0xDEADBEEF
 #define ULP_WAKEUP_PERIOD 1000000 // 1 second
 
 extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_test_app_bin_start");
@@ -212,3 +199,31 @@ TEST_CASE("ULP-RISC-V is able to wakeup main CPU from deep sleep", "[ulp][reset=
     esp_deep_sleep_start();
     UNITY_TEST_FAIL(__LINE__, "Should not get here!");
 }
+
+TEST_CASE("ULP-RISC-V mutex", "[ulp]")
+{
+    /* Load ULP RISC-V firmware and start the ULP RISC-V Coprocessor */
+    load_and_start_ulp_firmware();
+
+    /* Setup test data */
+    ulp_riscv_incrementer = 0;
+    ulp_main_cpu_reply = RISCV_NO_COMMAND;
+    ulp_main_cpu_command = RISCV_MUTEX_TEST;
+
+    ulp_riscv_lock_t *lock = (ulp_riscv_lock_t*)&ulp_lock;
+
+    for (int i = 0; i < MUTEX_TEST_ITERATIONS; i++) {
+        ulp_riscv_lock_acquire(lock);
+        ulp_riscv_incrementer++;
+        ulp_riscv_lock_release(lock);
+    }
+
+    while(ulp_main_cpu_reply != RISCV_COMMAND_OK) {
+        // Wait for ULP to finish
+    }
+
+    /* If the variable is protected there should be no race conditions
+       results should be the sum of increments made by ULP and by main CPU
+    */
+    TEST_ASSERT_EQUAL(2*MUTEX_TEST_ITERATIONS, ulp_riscv_incrementer);
+}

+ 21 - 15
components/ulp/test_apps/ulp_riscv/main/ulp/test_main.c

@@ -9,22 +9,9 @@
 #include <stdbool.h>
 #include "ulp_riscv_utils.h"
 #include "ulp_riscv_gpio.h"
+#include "ulp_riscv_lock_ulp_core.h"
+#include "ulp_test_shared.h"
 
-typedef enum{
-    RISCV_READ_WRITE_TEST = 1,
-    RISCV_DEEP_SLEEP_WAKEUP_TEST,
-    RISCV_LIGHT_SLEEP_WAKEUP_TEST,
-    RISCV_STOP_TEST,
-    RISCV_NO_COMMAND,
-} riscv_test_commands_t;
-
-typedef enum {
-    RISCV_COMMAND_OK = 1,
-    RISCV_COMMAND_NOK,
-    RISCV_COMMAND_INVALID,
-} riscv_test_command_reply_t;
-
-#define XOR_MASK 0xDEADBEEF
 
 volatile riscv_test_commands_t main_cpu_command = RISCV_NO_COMMAND;
 volatile riscv_test_command_reply_t main_cpu_reply = RISCV_COMMAND_INVALID;
@@ -33,6 +20,9 @@ volatile uint32_t riscv_test_data_in = 0;
 volatile uint32_t riscv_test_data_out = 0;
 volatile uint32_t riscv_counter = 0;
 
+volatile uint32_t riscv_incrementer = 0;
+ulp_riscv_lock_t lock;
+
 void handle_commands(riscv_test_commands_t cmd)
 {
     riscv_counter++;
@@ -87,6 +77,21 @@ void handle_commands(riscv_test_commands_t cmd)
 
             break;
 
+        case RISCV_MUTEX_TEST:
+            /* Echo the command ID back to the main CPU */
+            command_resp = RISCV_MUTEX_TEST;
+
+            for (int i = 0; i < MUTEX_TEST_ITERATIONS; i++) {
+                ulp_riscv_lock_acquire(&lock);
+                riscv_incrementer++;
+                ulp_riscv_lock_release(&lock);
+            }
+            /* Set the command reply status */
+            main_cpu_reply = RISCV_COMMAND_OK;
+            main_cpu_command = RISCV_NO_COMMAND;
+
+            break;
+
         case RISCV_NO_COMMAND:
             main_cpu_reply = RISCV_COMMAND_OK;
             break;
@@ -99,6 +104,7 @@ void handle_commands(riscv_test_commands_t cmd)
 
 int main (void)
 {
+
     while (1) {
         handle_commands(main_cpu_command);
         break;

+ 24 - 0
components/ulp/test_apps/ulp_riscv/main/ulp/ulp_test_shared.h

@@ -0,0 +1,24 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#pragma once
+
+#define MUTEX_TEST_ITERATIONS 100000
+#define XOR_MASK 0xDEADBEEF
+
+typedef enum{
+    RISCV_READ_WRITE_TEST = 1,
+    RISCV_DEEP_SLEEP_WAKEUP_TEST,
+    RISCV_LIGHT_SLEEP_WAKEUP_TEST,
+    RISCV_STOP_TEST,
+    RISCV_MUTEX_TEST,
+    RISCV_NO_COMMAND,
+} riscv_test_commands_t;
+
+typedef enum {
+    RISCV_COMMAND_OK = 1,
+    RISCV_COMMAND_NOK,
+    RISCV_COMMAND_INVALID,
+} riscv_test_command_reply_t;

+ 38 - 0
components/ulp/ulp_riscv/include/ulp_riscv_lock.h

@@ -0,0 +1,38 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ulp_riscv_lock_shared.h"
+
+/**
+ * @brief Locks are based on the Peterson's algorithm, https://en.wikipedia.org/wiki/Peterson%27s_algorithm
+ *
+ */
+
+/**
+ * @brief Acquire the lock, preventing the ULP from taking until released. Spins until lock is acquired.
+ *
+ * @note  The lock is only designed for being used by a single thread on the main CPU,
+ *        it is not safe to try to acquire it from multiple threads.
+ *
+ * @param lock Pointer to lock struct, shared with ULP
+ */
+void ulp_riscv_lock_acquire(ulp_riscv_lock_t *lock);
+
+/**
+ * @brief Release the lock
+ *
+ * @param lock Pointer to lock struct, shared with ULP
+ */
+void ulp_riscv_lock_release(ulp_riscv_lock_t *lock);
+
+#ifdef __cplusplus
+}
+#endif

+ 35 - 0
components/ulp/ulp_riscv/shared/include/ulp_riscv_lock_shared.h

@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enum representing which processor is allowed to enter the critical section
+ *
+ */
+typedef enum {
+    ULP_RISCV_LOCK_TURN_ULP,        /*!< ULP's turn to enter the critical section */
+    ULP_RISCV_LOCK_TURN_MAIN_CPU,   /*!< Main CPU's turn to enter the critical section */
+} ulp_riscv_lock_turn_t;
+
+/**
+ * @brief Structure representing a lock shared between ULP and main CPU
+ *
+ */
+typedef struct {
+    volatile bool critical_section_flag_ulp;        /*!<  ULP wants to enter the critical sections */
+    volatile bool critical_section_flag_main_cpu;   /*!<  Main CPU wants to enter the critical sections */
+    volatile ulp_riscv_lock_turn_t turn;            /*!<  Which CPU is allowed to enter the critical section */
+} ulp_riscv_lock_t;
+
+#ifdef __cplusplus
+}
+#endif

+ 39 - 0
components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_lock_ulp_core.h

@@ -0,0 +1,39 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include "ulp_riscv_lock_shared.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @brief Locks are based on the Peterson's algorithm, https://en.wikipedia.org/wiki/Peterson%27s_algorithm
+ *
+ */
+
+/**
+ * @brief Acquire the lock, preventing the main CPU from taking until released. Spins until lock is acquired.
+ *
+ * @note  The lock is only designed for being used by a single thread on the ULP,
+ *        it is not safe to try to acquire it from multiple threads.
+ *
+ * @param lock Pointer to lock struct, shared with main CPU
+ */
+void ulp_riscv_lock_acquire(ulp_riscv_lock_t *lock);
+
+/**
+ * @brief Release the lock
+ *
+ * @param lock Pointer to lock struct, shared with main CPU
+ */
+void ulp_riscv_lock_release(ulp_riscv_lock_t *lock);
+
+#ifdef __cplusplus
+}
+#endif

+ 21 - 0
components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c

@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ulp_riscv_lock.h"
+#include "ulp_riscv_lock_shared.h"
+
+void ulp_riscv_lock_acquire(ulp_riscv_lock_t *lock)
+{
+    lock->critical_section_flag_ulp = true;
+    lock->turn = ULP_RISCV_LOCK_TURN_MAIN_CPU;
+
+    while (lock->critical_section_flag_main_cpu && (lock->turn == ULP_RISCV_LOCK_TURN_MAIN_CPU)) {
+    }
+}
+
+void ulp_riscv_lock_release(ulp_riscv_lock_t *lock)
+{
+    lock->critical_section_flag_ulp = false;
+}

+ 27 - 0
components/ulp/ulp_riscv/ulp_riscv_lock.c

@@ -0,0 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "ulp_riscv_lock.h"
+#include "ulp_riscv_lock_shared.h"
+
+#include <assert.h>
+
+void ulp_riscv_lock_acquire(ulp_riscv_lock_t *lock)
+{
+    assert(lock);
+
+    lock->critical_section_flag_main_cpu = true;
+    lock->turn = ULP_RISCV_LOCK_TURN_ULP;
+
+    while (lock->critical_section_flag_ulp && (lock->turn == ULP_RISCV_LOCK_TURN_ULP)) {
+    }
+}
+
+void ulp_riscv_lock_release(ulp_riscv_lock_t *lock)
+{
+    assert(lock);
+
+    lock->critical_section_flag_main_cpu = false;
+}

+ 2 - 0
docs/doxygen/Doxyfile_esp32s2

@@ -25,6 +25,8 @@ INPUT += \
          $(PROJECT_PATH)/components/touch_element/include/touch_element/touch_slider.h \
          $(PROJECT_PATH)/components/ulp/ulp_common/include/$(IDF_TARGET)/ulp_common_defs.h \
          $(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
+         $(PROJECT_PATH)/components/ulp/ulp_riscv/include/ulp_riscv_lock.h \
+         $(PROJECT_PATH)/components/ulp/ulp_riscv/shared/include/ulp_riscv_lock_shared.h \
          $(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \
          $(PROJECT_PATH)/components/usb/include/usb/usb_host.h \
          $(PROJECT_PATH)/components/usb/include/usb/usb_types_ch9.h \

+ 2 - 0
docs/doxygen/Doxyfile_esp32s3

@@ -31,6 +31,8 @@ INPUT += \
          $(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/touch_sensor_channel.h \
          $(PROJECT_PATH)/components/ulp/ulp_common/include/$(IDF_TARGET)/ulp_common_defs.h \
          $(PROJECT_PATH)/components/ulp/ulp_fsm/include/$(IDF_TARGET)/ulp.h \
+         $(PROJECT_PATH)/components/ulp/ulp_riscv/include/ulp_riscv_lock.h \
+         $(PROJECT_PATH)/components/ulp/ulp_riscv/shared/include/ulp_riscv_lock_shared.h \
          $(PROJECT_PATH)/components/usb/include/usb/usb_helpers.h \
          $(PROJECT_PATH)/components/usb/include/usb/usb_host.h \
          $(PROJECT_PATH)/components/usb/include/usb/usb_types_ch9.h \

+ 14 - 1
docs/en/api-reference/system/ulp-risc-v.rst

@@ -98,6 +98,18 @@ To access the ULP RISC-V program variables from the main program, the generated
         ulp_measurement_count = 64;
     }
 
+Mutual Exclusion
+^^^^^^^^^^^^^^^^
+
+If mutual exclusion is needed when accessing a variable shared between the main program and ULP then this can be achieved by using the ULP RISC-V lock API:
+
+ * :cpp:func:`ulp_riscv_lock_acquire`
+ * :cpp:func:`ulp_riscv_lock_release`
+
+The ULP does not have any hardware instructions to facilitate mutual exclusion so the lock API achieves this through a software algorithm (`Peterson's algorithm <https://en.wikipedia.org/wiki/Peterson%27s_algorithm>`_).
+
+The locks are intended to only be called from a single thread in the main program, and will not provide mutual exclusion if used simultaneously from multiple threads.
+
 Starting the ULP RISC-V Program
 -------------------------------
 
@@ -151,7 +163,6 @@ Keeping this in mind, here are some ways that may help you debug you ULP RISC-V
  * Trap signal: the ULP RISC-V has a hardware trap that will trigger under certain conditions, e.g. illegal instruction. This will cause the main CPU to be woken up with the wake-up cause :cpp:enumerator:`ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG`.
 
 
-
 Application Examples
 --------------------
 
@@ -163,3 +174,5 @@ API Reference
 -------------
 
 .. include-build-file:: inc/ulp_riscv.inc
+.. include-build-file:: inc/ulp_riscv_lock_shared.inc
+.. include-build-file:: inc/ulp_riscv_lock.inc