ci freertos: Add test configs for some of the optional FreeRTOS 10 configurations

Also unit tests for the legacy hook functions.
pull/4512/merge
Angus Gratton 2020-10-16 13:58:11 +11:00
rodzic bb74334830
commit 8a70b1cdc9
9 zmienionych plików z 210 dodań i 24 usunięć

Wyświetl plik

@ -1,7 +1,8 @@
#include "sdkconfig.h"
#include "unity.h" #include "unity.h"
#include "esp_intr_alloc.h" #include "esp_intr_alloc.h"
#if defined(__XTENSA__) #if defined(__XTENSA__) && CONFIG_FREERTOS_CORETIMER_0
#include "xtensa/config/core-isa.h" #include "xtensa/config/core-isa.h"
#include "xtensa/hal.h" #include "xtensa/hal.h"
#if defined(XCHAL_HAVE_WINDOWED) #if defined(XCHAL_HAVE_WINDOWED)
@ -40,4 +41,4 @@ TEST_CASE("context save doesn't corrupt return address register", "[freertos]")
} }
#endif // XCHAL_HAVE_WINDOWED #endif // XCHAL_HAVE_WINDOWED
#endif // __XTENSA__ #endif // __XTENSA__ && CONFIG_FREERTOS_CORETIMER_0

Wyświetl plik

@ -216,6 +216,16 @@ TEST_CASE("Test FreeRTOS backported eventgroup functions", "[freertos]")
//The variables pointed to by Thread Local Storage Pointer //The variables pointed to by Thread Local Storage Pointer
static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0}; static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0};
/* If static task cleanup is defined, can't set index 0 even if the calling task is not a pthread,
as the cleanup is called for every task.
*/
#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP)
static const int skip_index = 0; /*PTHREAD_TLS_INDEX*/
#else
static const int skip_index = -1;
#endif
static void del_cb(int index, void *ptr) static void del_cb(int index, void *ptr)
{ {
*((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value *((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value
@ -225,11 +235,19 @@ static void task_cb(void *arg)
{ {
int core = xPortGetCoreID(); int core = xPortGetCoreID();
for(int i = 0; i < NO_OF_TLSP; i++){ for(int i = 0; i < NO_OF_TLSP; i++){
if (i == skip_index) {
continue;
}
task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number
vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element
} }
for(int i = 0; i < NO_OF_TLSP; i++){ for(int i = 0; i < NO_OF_TLSP; i++){
if (i == skip_index) {
continue;
}
uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i); uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i);
TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value
} }
@ -247,6 +265,9 @@ TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]"
for(int core = 0; core < portNUM_PROCESSORS; core++){ for(int core = 0; core < portNUM_PROCESSORS; core++){
for(int i = 0; i < NO_OF_TLSP; i++){ for(int i = 0; i < NO_OF_TLSP; i++){
if (i == skip_index) {
continue;
}
TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value
} }
} }

Wyświetl plik

@ -15,6 +15,8 @@
#include "xtensa/hal.h" #include "xtensa/hal.h"
#include "esp_rom_sys.h" #include "esp_rom_sys.h"
#if CONFIG_FREERTOS_CORETIMER_0
static volatile int in_int_context, int_handled; static volatile int in_int_context, int_handled;
@ -52,3 +54,5 @@ TEST_CASE("xPortInIsrContext test", "[freertos]")
#endif #endif
} }
#endif

Wyświetl plik

@ -4,6 +4,9 @@
#include "unity.h" #include "unity.h"
#include "test_utils.h" #include "test_utils.h"
/* If assertions aren't set to fail this code still crashes, but not with an abort... */
#if CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER && CONFIG_FREERTOS_ASSERT_FAIL_ABORT
static void mutex_release_task(void* arg) static void mutex_release_task(void* arg)
{ {
SemaphoreHandle_t mutex = (SemaphoreHandle_t) arg; SemaphoreHandle_t mutex = (SemaphoreHandle_t) arg;
@ -18,3 +21,5 @@ TEST_CASE("mutex released not by owner causes an assert", "[freertos][reset=abor
xTaskCreate(&mutex_release_task, "mutex_release", 2048, mutex, UNITY_FREERTOS_PRIORITY + 1, NULL); xTaskCreate(&mutex_release_task, "mutex_release", 2048, mutex, UNITY_FREERTOS_PRIORITY + 1, NULL);
vTaskDelay(1); vTaskDelay(1);
} }
#endif

Wyświetl plik

@ -0,0 +1,78 @@
/*
Test of FreeRTOS "legacy" hook functions, and delete hook
Only compiled in if the relevant options are enabled.
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <freertos/semphr.h>
#include "driver/timer.h"
#include "unity.h"
#include "test_utils.h"
#if CONFIG_FREERTOS_LEGACY_HOOKS
static volatile unsigned idle_count;
void vApplicationIdleHook(void)
{
idle_count++;
}
TEST_CASE("legacy idle hook is correctly called based on config", "[freertos]")
{
idle_count = 0;
vTaskDelay(10);
TEST_ASSERT_NOT_EQUAL(0, idle_count); // The legacy idle hook should be called at least once
}
static volatile unsigned tick_count;
void IRAM_ATTR vApplicationTickHook(void)
{
tick_count++;
}
TEST_CASE("legacy tick hook is called based on config", "[freertos]")
{
unsigned before = xTaskGetTickCount();
const unsigned SLEEP_FOR = 20;
tick_count = before;
vTaskDelay(SLEEP_FOR);
TEST_ASSERT_UINT32_WITHIN_MESSAGE(3 * portNUM_PROCESSORS, before + SLEEP_FOR * portNUM_PROCESSORS, tick_count,
"The legacy tick hook should have been called approx 1 time per tick per CPU");
}
#endif // CONFIG_FREERTOS_LEGACY_HOOKS
#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP
static volatile void *deleted_tcb;
static void taskDeletesItself(void *ignored)
{
vTaskDelete(NULL);
}
void vPortCleanUpTCB(void *pxTCB)
{
deleted_tcb = pxTCB;
}
TEST_CASE("static task cleanup hook is called based on config", "[freertos]")
{
for(int i = 0; i < portNUM_PROCESSORS; i++) {
printf("Creating task CPU %d\n", i);
TaskHandle_t new_task = NULL;
deleted_tcb = NULL;
xTaskCreatePinnedToCore(taskDeletesItself, "delete", 2048, NULL, UNITY_FREERTOS_PRIORITY, &new_task, i);
vTaskDelay(5);
TEST_ASSERT_EQUAL_PTR(deleted_tcb, new_task); // TCB & TaskHandle are the same in FreeRTOS
}
}
#endif // CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP

Wyświetl plik

@ -129,6 +129,7 @@ void IRAM_ATTR timer_group0_isr(void *vp_arg)
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0); timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
timer_isr_fired = true; timer_isr_fired = true;
TaskHandle_t handle = vp_arg; TaskHandle_t handle = vp_arg;
BaseType_t higherPriorityTaskWoken = pdFALSE; BaseType_t higherPriorityTaskWoken = pdFALSE;
higherPriorityTaskWoken = xTaskResumeFromISR(handle); higherPriorityTaskWoken = xTaskResumeFromISR(handle);
@ -137,44 +138,70 @@ void IRAM_ATTR timer_group0_isr(void *vp_arg)
} }
} }
/* Task suspends itself, then sets parameter value to the current timer group counter and deletes itself */
static void task_suspend_self_with_timer(void *vp_resumed)
{
volatile uint64_t *resumed_counter = vp_resumed;
*resumed_counter = 0;
vTaskSuspend(NULL);
timer_get_counter_value(TIMER_GROUP_0, TIMER_0, vp_resumed);
vTaskDelete(NULL);
}
/* Create a task which suspends itself, then resume it from a timer /* Create a task which suspends itself, then resume it from a timer
* interrupt. */ * interrupt. */
static void test_resume_task_from_isr(int target_core) static void test_resume_task_from_isr(int target_core)
{ {
volatile bool resumed = false; volatile uint64_t resumed_counter = 99;
TaskHandle_t suspend_task; TaskHandle_t suspend_task;
xTaskCreatePinnedToCore(task_suspend_self, "suspend_self", 2048, xTaskCreatePinnedToCore(task_suspend_self_with_timer, "suspend_self", 2048,
(void *)&resumed, UNITY_FREERTOS_PRIORITY + 1, (void *)&resumed_counter, UNITY_FREERTOS_PRIORITY + 1,
&suspend_task, target_core); &suspend_task, target_core);
vTaskDelay(1); vTaskDelay(1);
TEST_ASSERT_FALSE(resumed); TEST_ASSERT_EQUAL(0, resumed_counter);
const unsigned APB_CYCLES_PER_TICK = TIMER_BASE_CLK / configTICK_RATE_HZ;
const unsigned TEST_TIMER_DIV = 2;
const unsigned TEST_TIMER_CYCLES_PER_TICK = APB_CYCLES_PER_TICK / TEST_TIMER_DIV;
const unsigned TEST_TIMER_CYCLES_PER_MS = TIMER_BASE_CLK / 1000 / TEST_TIMER_DIV;
const unsigned TEST_TIMER_ALARM = TEST_TIMER_CYCLES_PER_TICK / 2; // half an RTOS tick
/* Configure timer ISR */ /* Configure timer ISR */
timer_isr_handle_t isr_handle;
const timer_config_t config = { const timer_config_t config = {
.alarm_en = 1, .alarm_en = 1,
.auto_reload = 0, .auto_reload = 0,
.counter_dir = TIMER_COUNT_UP, .counter_dir = TIMER_COUNT_UP,
.divider = 2, //Range is 2 to 65536 .divider = TEST_TIMER_DIV,
.intr_type = TIMER_INTR_LEVEL, .intr_type = TIMER_INTR_LEVEL,
.counter_en = TIMER_PAUSE, .counter_en = TIMER_PAUSE,
}; };
/*Configure timer*/ /*Configure timer*/
timer_init(TIMER_GROUP_0, TIMER_0, &config); ESP_ERROR_CHECK( timer_init(TIMER_GROUP_0, TIMER_0, &config) );
timer_pause(TIMER_GROUP_0, TIMER_0); ESP_ERROR_CHECK( timer_pause(TIMER_GROUP_0, TIMER_0) );
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); ESP_ERROR_CHECK( timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0) );
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000); ESP_ERROR_CHECK( timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TEST_TIMER_ALARM) );
timer_enable_intr(TIMER_GROUP_0, TIMER_0); ESP_ERROR_CHECK( timer_enable_intr(TIMER_GROUP_0, TIMER_0) );
timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_group0_isr, (void*)suspend_task, ESP_INTR_FLAG_IRAM, NULL); ESP_ERROR_CHECK( timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_group0_isr, (void*)suspend_task, ESP_INTR_FLAG_IRAM, &isr_handle) );
timer_isr_fired = false; timer_isr_fired = false;
timer_start(TIMER_GROUP_0, TIMER_0); vTaskDelay(1); // Make sure we're at the start of a new tick
vTaskDelay(1); ESP_ERROR_CHECK( timer_start(TIMER_GROUP_0, TIMER_0) );
vTaskDelay(1); // We expect timer group will fire half-way through this delay
timer_deinit(TIMER_GROUP_0, TIMER_0);
TEST_ASSERT_TRUE(timer_isr_fired); TEST_ASSERT_TRUE(timer_isr_fired);
TEST_ASSERT_TRUE(resumed); TEST_ASSERT_NOT_EQUAL(0, resumed_counter);
// The task should have woken within 500us of the timer interrupt event (note: task may be a flash cache miss)
printf("alarm value %u task resumed at %u\n", TEST_TIMER_ALARM, (unsigned)resumed_counter);
TEST_ASSERT_UINT32_WITHIN(TEST_TIMER_CYCLES_PER_MS/2, TEST_TIMER_ALARM, (unsigned)resumed_counter);
// clean up
timer_deinit(TIMER_GROUP_0, TIMER_0);
ESP_ERROR_CHECK( esp_intr_free(isr_handle) );
} }
TEST_CASE("Resume task from ISR (same core)", "[freertos]") TEST_CASE("Resume task from ISR (same core)", "[freertos]")
@ -293,11 +320,11 @@ TEST_CASE("Test the waiting task not missed due to scheduler suspension on one C
test_scheduler_suspend1(1); test_scheduler_suspend1(1);
} }
static uint32_t count_tick[2]; static uint32_t tick_hook_ms[2];
static void IRAM_ATTR tick_hook(void) static void IRAM_ATTR tick_hook(void)
{ {
++count_tick[xPortGetCoreID()]; tick_hook_ms[xPortGetCoreID()] += portTICK_PERIOD_MS;
} }
static void test_scheduler_suspend2(int cpu) static void test_scheduler_suspend2(int cpu)
@ -305,7 +332,7 @@ static void test_scheduler_suspend2(int cpu)
esp_register_freertos_tick_hook_for_cpu(tick_hook, 0); esp_register_freertos_tick_hook_for_cpu(tick_hook, 0);
esp_register_freertos_tick_hook_for_cpu(tick_hook, 1); esp_register_freertos_tick_hook_for_cpu(tick_hook, 1);
memset(count_tick, 0, sizeof(count_tick)); memset(tick_hook_ms, 0, sizeof(tick_hook_ms));
printf("Test for CPU%d\n", cpu); printf("Test for CPU%d\n", cpu);
xTaskCreatePinnedToCore(&control_task, "control_task", 8192, NULL, 5, NULL, cpu); xTaskCreatePinnedToCore(&control_task, "control_task", 8192, NULL, 5, NULL, cpu);
@ -313,10 +340,10 @@ static void test_scheduler_suspend2(int cpu)
vTaskDelay(waiting_ms * 2 / portTICK_PERIOD_MS); vTaskDelay(waiting_ms * 2 / portTICK_PERIOD_MS);
esp_deregister_freertos_tick_hook(tick_hook); esp_deregister_freertos_tick_hook(tick_hook);
printf("count_tick[cpu0] = %d, count_tick[cpu1] = %d\n", count_tick[0], count_tick[1]); printf("tick_hook_ms[cpu0] = %d, tick_hook_ms[cpu1] = %d\n", tick_hook_ms[0], tick_hook_ms[1]);
TEST_ASSERT_INT_WITHIN(1, waiting_ms * 2, count_tick[0]); TEST_ASSERT_INT_WITHIN(portTICK_PERIOD_MS, waiting_ms * 2, tick_hook_ms[0]);
TEST_ASSERT_INT_WITHIN(1, waiting_ms * 2, count_tick[1]); TEST_ASSERT_INT_WITHIN(portTICK_PERIOD_MS, waiting_ms * 2, tick_hook_ms[1]);
printf("\n"); printf("\n");
} }
@ -337,7 +364,7 @@ static int duration_timer_ms;
static void timer_callback(void *arg) static void timer_callback(void *arg)
{ {
++duration_timer_ms; duration_timer_ms += portTICK_PERIOD_MS;
} }
static void test_scheduler_suspend3(int cpu) static void test_scheduler_suspend3(int cpu)

Wyświetl plik

@ -506,6 +506,9 @@ and ``vApplicationTickHook()`` can only be defined once. However, the ESP32 is d
in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words, in nature, therefore same Idle Hook and Tick Hook are used for both cores (in other words,
the hooks are symmetrical for both cores). the hooks are symmetrical for both cores).
In a dual core system, ``vApplicationTickHook()`` must be located in IRAM (for example
by adding the IRAM_ATTR attribute).
ESP-IDF Idle and Tick Hooks ESP-IDF Idle and Tick Hooks
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Due to the the dual core nature of the ESP32, it may be necessary for some Due to the the dual core nature of the ESP32, it may be necessary for some

Wyświetl plik

@ -0,0 +1,24 @@
# This is a small set of tests where we enable as many as possible of the optional features
# in FreeRTOS that are gated behind config
CONFIG_IDF_TARGET="esp32"
TEST_COMPONENTS=freertos
CONFIG_FREERTOS_CORETIMER_1=y
CONFIG_FREERTOS_OPTIMIZED_SCHEDULER=n
CONFIG_FREERTOS_HZ=500
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_FREERTOS_INTERRUPT_BACKTRACE=n
CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE=y
CONFIG_FREERTOS_LEGACY_HOOKS=y
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=y
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=10
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_FPU_IN_ISR=y
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=16

Wyświetl plik

@ -0,0 +1,23 @@
# This is a small set of tests where we enable as many as possible of the optional features
# in FreeRTOS that are gated behind config
CONFIG_IDF_TARGET="esp32s2"
TEST_COMPONENTS=freertos
CONFIG_FREERTOS_CORETIMER_1=y
CONFIG_FREERTOS_OPTIMIZED_SCHEDULER=n
CONFIG_FREERTOS_HZ=500
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_FREERTOS_INTERRUPT_BACKTRACE=n
CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE=y
CONFIG_FREERTOS_LEGACY_HOOKS=y
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=y
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=10
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_FPU_IN_ISR=y