Merge branch 'feature/ulp_mutex' into 'master'

ulp-riscv: mutex/lock

Closes IDF-5754

See merge request espressif/esp-idf!19377
This commit is contained in:
Zim Kalinowski 2022-08-21 16:27:53 +08:00
commit f54972d95d
14 changed files with 260 additions and 33 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 \

View File

@ -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 \

View File

@ -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
-------------------------------
@ -152,7 +164,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
--------------------
@ -164,3 +175,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