esp-idf/components/freertos/test/test_spinlocks.c
Darian Leung 9b3796d2f1 freertos: Add portTRY_ENTRY_CRITICAL() and deprecate legacy spinlock fucntions
Add TRY_ENTRY_CRITICAL() API to all for timeouts when entering critical sections.
The following port API were added:
- portTRY_ENTER_CRITICAL()
- portTRY_ENTER_CRITICAL_ISR()
- portTRY_ENTER_CRITICAL_SAFE()

Deprecated legacy spinlock API in favor of spinlock.h. The following API were deprecated:
- vPortCPUInitializeMutex()
- vPortCPUAcquireMutex()
- vPortCPUAcquireMutexTimeout()
- vPortCPUReleaseMutex()

Other Changes:
- Added portMUX_INITIALIZE() to replace vPortCPUInitializeMutex()
- The assembly of the critical section functions ends up being about 50 instructions longer,
  thus the spinlock test pass threshold had to be increased to account for the extra runtime.

Closes https://github.com/espressif/esp-idf/issues/5301
2021-11-22 13:28:39 +08:00

144 lines
4.4 KiB
C

/*
Combined unit tests & benchmarking for spinlock "portMUX" functionality
*/
#include <esp_types.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "unity.h"
#include "soc/cpu.h"
#include "hal/cpu_hal.h"
#include "test_utils.h"
#define REPEAT_OPS 10000
static uint32_t start, end;
#define BENCHMARK_START() do { \
start = cpu_hal_get_cycle_count(); \
} while(0)
#define BENCHMARK_END(OPERATION) do { \
end = cpu_hal_get_cycle_count(); \
printf("%s took %d cycles/op (%d cycles for %d ops)\n", \
OPERATION, (end - start)/REPEAT_OPS, \
(end - start), REPEAT_OPS); \
} while(0)
TEST_CASE("portMUX spinlocks (no contention)", "[freertos]")
{
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
BENCHMARK_START();
for (int i = 0; i < REPEAT_OPS; i++) {
portENTER_CRITICAL_ISR(&mux);
portEXIT_CRITICAL_ISR(&mux);
}
BENCHMARK_END("no contention lock");
#ifdef CONFIG_FREERTOS_UNICORE
TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP_UNICORE, "%d cycles/op", ((end - start)/REPEAT_OPS));
#else
#if CONFIG_SPIRAM
TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM, "%d cycles/op", ((end - start)/REPEAT_OPS));
#else
TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP, "%d cycles/op", ((end - start)/REPEAT_OPS));
#endif
#endif
}
TEST_CASE("portMUX recursive locks (no contention)", "[freertos]")
{
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
BENCHMARK_START();
const int RECURSE_COUNT = 25;
for (int i = 0; i < REPEAT_OPS / RECURSE_COUNT; i++) {
for (int j = 0; j < RECURSE_COUNT; j++) {
portENTER_CRITICAL(&mux);
}
for (int j = 0; j < RECURSE_COUNT; j++) {
portEXIT_CRITICAL(&mux);
}
}
BENCHMARK_END("no contention recursive");
}
#if portNUM_PROCESSORS == 2
static volatile int shared_value;
static portMUX_TYPE shared_mux;
static xSemaphoreHandle done_sem;
static void task_shared_value_increment(void *ignore)
{
for (int i = 0; i < REPEAT_OPS; i++) {
portENTER_CRITICAL(&shared_mux);
shared_value++;
portEXIT_CRITICAL(&shared_mux);
}
xSemaphoreGive(done_sem);
vTaskDelete(NULL);
}
TEST_CASE("portMUX cross-core locking", "[freertos]")
{
done_sem = xSemaphoreCreateCounting(2, 0);
portMUX_INITIALIZE(&shared_mux);
shared_value = 0;
BENCHMARK_START();
xTaskCreatePinnedToCore(task_shared_value_increment, "INC0", 2048, NULL, UNITY_FREERTOS_PRIORITY + 1, NULL, UNITY_FREERTOS_CPU ? 0 : 1);
xTaskCreatePinnedToCore(task_shared_value_increment, "INC1", 2048, NULL, UNITY_FREERTOS_PRIORITY + 1, NULL, UNITY_FREERTOS_CPU);
for(int i = 0; i < 2; i++) {
if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) {
TEST_FAIL_MESSAGE("done_sem not released by test task");
}
}
BENCHMARK_END("cross-core incrementing");
vSemaphoreDelete(done_sem);
TEST_ASSERT_EQUAL_INT(REPEAT_OPS * 2, shared_value);
}
TEST_CASE("portMUX high contention", "[freertos]")
{
const int TOTAL_TASKS = 8; /* half on each core */
done_sem = xSemaphoreCreateCounting(TOTAL_TASKS, 0);
portMUX_INITIALIZE(&shared_mux);
shared_value = 0;
BENCHMARK_START();
for (int i = 0; i < TOTAL_TASKS / 2; i++) {
/* as each task has a higher priority than previous, expect
them to preempt the earlier created task, at least on the
other core (this core has the unity task, until that
blocks)... */
xTaskCreatePinnedToCore(task_shared_value_increment, "INC0", 2048, NULL, tskIDLE_PRIORITY + 1 + i, NULL, UNITY_FREERTOS_CPU ? 0 : 1);
xTaskCreatePinnedToCore(task_shared_value_increment, "INC1", 2048, NULL, tskIDLE_PRIORITY + 1 + i, NULL, UNITY_FREERTOS_CPU);
}
for(int i = 0; i < TOTAL_TASKS; i++) {
if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) {
TEST_FAIL_MESSAGE("done_sem not released by test task");
}
}
BENCHMARK_END("cross-core high contention");
vSemaphoreDelete(done_sem);
TEST_ASSERT_EQUAL_INT(REPEAT_OPS * TOTAL_TASKS, shared_value);
}
#endif // portNUM_PROCESSORS == 2