test_suspend_scheduler.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /* Tests for FreeRTOS scheduler suspend & resume all tasks */
  2. #include <stdio.h>
  3. #include "freertos/FreeRTOS.h"
  4. #include "freertos/task.h"
  5. #include "freertos/semphr.h"
  6. #include "freertos/queue.h"
  7. #include "freertos/xtensa_api.h"
  8. #include "unity.h"
  9. #include "soc/cpu.h"
  10. #include "test_utils.h"
  11. #include "driver/timer.h"
  12. #include "sdkconfig.h"
  13. #ifdef CONFIG_IDF_TARGET_ESP32S2
  14. #define int_clr_timers int_clr
  15. #define update update.update
  16. #define int_st_timers int_st
  17. #endif
  18. static SemaphoreHandle_t isr_semaphore;
  19. static volatile unsigned isr_count;
  20. /* Timer ISR increments an ISR counter, and signals a
  21. mutex semaphore to wake up another counter task */
  22. static void timer_group0_isr(void *vp_arg)
  23. {
  24. timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
  25. timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0);
  26. portBASE_TYPE higher_awoken = pdFALSE;
  27. isr_count++;
  28. xSemaphoreGiveFromISR(isr_semaphore, &higher_awoken);
  29. if (higher_awoken == pdTRUE) {
  30. portYIELD_FROM_ISR();
  31. }
  32. }
  33. typedef struct {
  34. SemaphoreHandle_t trigger_sem;
  35. volatile unsigned counter;
  36. } counter_config_t;
  37. static void counter_task_fn(void *vp_config)
  38. {
  39. counter_config_t *config = (counter_config_t *)vp_config;
  40. printf("counter_task running...\n");
  41. while(1) {
  42. xSemaphoreTake(config->trigger_sem, portMAX_DELAY);
  43. config->counter++;
  44. }
  45. }
  46. /* This test verifies that an interrupt can wake up a task while the scheduler is disabled.
  47. In the FreeRTOS implementation, this exercises the xPendingReadyList for that core.
  48. */
  49. TEST_CASE("Scheduler disabled can handle a pending context switch on resume", "[freertos]")
  50. {
  51. isr_count = 0;
  52. isr_semaphore = xSemaphoreCreateMutex();
  53. TaskHandle_t counter_task;
  54. intr_handle_t isr_handle = NULL;
  55. counter_config_t count_config = {
  56. .trigger_sem = isr_semaphore,
  57. .counter = 0,
  58. };
  59. xTaskCreatePinnedToCore(counter_task_fn, "counter", 2048,
  60. &count_config, UNITY_FREERTOS_PRIORITY + 1,
  61. &counter_task, UNITY_FREERTOS_CPU);
  62. /* Configure timer ISR */
  63. const timer_config_t timer_config = {
  64. .alarm_en = 1,
  65. .auto_reload = 1,
  66. .counter_dir = TIMER_COUNT_UP,
  67. .divider = 2, //Range is 2 to 65536
  68. .intr_type = TIMER_INTR_LEVEL,
  69. .counter_en = TIMER_PAUSE,
  70. };
  71. /* Configure timer */
  72. timer_init(TIMER_GROUP_0, TIMER_0, &timer_config);
  73. timer_pause(TIMER_GROUP_0, TIMER_0);
  74. timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
  75. timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000);
  76. timer_enable_intr(TIMER_GROUP_0, TIMER_0);
  77. timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_group0_isr, NULL, 0, &isr_handle);
  78. timer_start(TIMER_GROUP_0, TIMER_0);
  79. vTaskDelay(5);
  80. // Check some counts have been triggered via the ISR
  81. TEST_ASSERT(count_config.counter > 10);
  82. TEST_ASSERT(isr_count > 10);
  83. for (int i = 0; i < 20; i++) {
  84. vTaskSuspendAll();
  85. esp_intr_noniram_disable();
  86. unsigned no_sched_task = count_config.counter;
  87. // scheduler off on this CPU...
  88. ets_delay_us(20 * 1000);
  89. //TEST_ASSERT_NOT_EQUAL(no_sched_isr, isr_count);
  90. TEST_ASSERT_EQUAL(count_config.counter, no_sched_task);
  91. // disable timer interrupts
  92. timer_disable_intr(TIMER_GROUP_0, TIMER_0);
  93. // When we resume scheduler, we expect the counter task
  94. // will preempt and count at least one more item
  95. esp_intr_noniram_enable();
  96. timer_enable_intr(TIMER_GROUP_0, TIMER_0);
  97. xTaskResumeAll();
  98. TEST_ASSERT_NOT_EQUAL(count_config.counter, no_sched_task);
  99. }
  100. esp_intr_free(isr_handle);
  101. timer_disable_intr(TIMER_GROUP_0, TIMER_0);
  102. vTaskDelete(counter_task);
  103. vSemaphoreDelete(isr_semaphore);
  104. }
  105. /* Multiple tasks on different cores can be added to the pending ready list
  106. while scheduler is suspended, and should be started once the scheduler
  107. resumes.
  108. */
  109. TEST_CASE("Scheduler disabled can wake multiple tasks on resume", "[freertos]")
  110. {
  111. #define TASKS_PER_PROC 4
  112. TaskHandle_t tasks[portNUM_PROCESSORS][TASKS_PER_PROC] = { 0 };
  113. counter_config_t counters[portNUM_PROCESSORS][TASKS_PER_PROC] = { 0 };
  114. /* Start all the tasks, they will block on isr_semaphore */
  115. for (int p = 0; p < portNUM_PROCESSORS; p++) {
  116. for (int t = 0; t < TASKS_PER_PROC; t++) {
  117. counters[p][t].trigger_sem = xSemaphoreCreateMutex();
  118. TEST_ASSERT_NOT_NULL( counters[p][t].trigger_sem );
  119. TEST_ASSERT( xSemaphoreTake(counters[p][t].trigger_sem, 0) );
  120. xTaskCreatePinnedToCore(counter_task_fn, "counter", 2048,
  121. &counters[p][t], UNITY_FREERTOS_PRIORITY + 1,
  122. &tasks[p][t], p);
  123. TEST_ASSERT_NOT_NULL( tasks[p][t] );
  124. }
  125. }
  126. /* takes a while to initialize tasks on both cores, sometimes... */
  127. vTaskDelay(TASKS_PER_PROC * portNUM_PROCESSORS * 3);
  128. /* Check nothing is counting, each counter should be blocked on its trigger_sem */
  129. for (int p = 0; p < portNUM_PROCESSORS; p++) {
  130. for (int t = 0; t < TASKS_PER_PROC; t++) {
  131. TEST_ASSERT_EQUAL(0, counters[p][t].counter);
  132. }
  133. }
  134. /* Suspend scheduler on this CPU */
  135. vTaskSuspendAll();
  136. /* Give all the semaphores once. This will wake tasks immediately on the other
  137. CPU, but they are deferred here until the scheduler resumes.
  138. */
  139. for (int p = 0; p < portNUM_PROCESSORS; p++) {
  140. for (int t = 0; t < TASKS_PER_PROC; t++) {
  141. xSemaphoreGive(counters[p][t].trigger_sem);
  142. }
  143. }
  144. ets_delay_us(200); /* Let the other CPU do some things */
  145. for (int p = 0; p < portNUM_PROCESSORS; p++) {
  146. for (int t = 0; t < TASKS_PER_PROC; t++) {
  147. int expected = (p == UNITY_FREERTOS_CPU) ? 0 : 1; // Has run if it was on the other CPU
  148. ets_printf("Checking CPU %d task %d (expected %d actual %d)\n", p, t, expected, counters[p][t].counter);
  149. TEST_ASSERT_EQUAL(expected, counters[p][t].counter);
  150. }
  151. }
  152. /* Resume scheduler */
  153. xTaskResumeAll();
  154. /* Now the tasks on both CPUs should have been woken once and counted once. */
  155. for (int p = 0; p < portNUM_PROCESSORS; p++) {
  156. for (int t = 0; t < TASKS_PER_PROC; t++) {
  157. ets_printf("Checking CPU %d task %d (expected 1 actual %d)\n", p, t, counters[p][t].counter);
  158. TEST_ASSERT_EQUAL(1, counters[p][t].counter);
  159. }
  160. }
  161. /* Clean up */
  162. for (int p = 0; p < portNUM_PROCESSORS; p++) {
  163. for (int t = 0; t < TASKS_PER_PROC; t++) {
  164. vTaskDelete(tasks[p][t]);
  165. vSemaphoreDelete(counters[p][t].trigger_sem);
  166. }
  167. }
  168. }
  169. #ifndef CONFIG_FREERTOS_UNICORE
  170. static volatile bool sched_suspended;
  171. static void suspend_scheduler_5ms_task_fn(void *ignore)
  172. {
  173. vTaskSuspendAll();
  174. sched_suspended = true;
  175. for (int i = 0; i <5; i++) {
  176. ets_delay_us(1000);
  177. }
  178. xTaskResumeAll();
  179. sched_suspended = false;
  180. vTaskDelete(NULL);
  181. }
  182. /* If the scheduler is disabled on one CPU (A) with a task blocked on something, and a task
  183. on B (where scheduler is running) wakes it, then the task on A should be woken on resume.
  184. */
  185. TEST_CASE("Scheduler disabled on CPU B, tasks on A can wake", "[freertos]")
  186. {
  187. TaskHandle_t counter_task;
  188. SemaphoreHandle_t wake_sem = xSemaphoreCreateMutex();
  189. xSemaphoreTake(wake_sem, 0);
  190. counter_config_t count_config = {
  191. .trigger_sem = wake_sem,
  192. .counter = 0,
  193. };
  194. xTaskCreatePinnedToCore(counter_task_fn, "counter", 2048,
  195. &count_config, UNITY_FREERTOS_PRIORITY + 1,
  196. &counter_task, !UNITY_FREERTOS_CPU);
  197. xTaskCreatePinnedToCore(suspend_scheduler_5ms_task_fn, "suspender", 2048,
  198. NULL, UNITY_FREERTOS_PRIORITY - 1,
  199. NULL, !UNITY_FREERTOS_CPU);
  200. /* counter task is now blocked on other CPU, waiting for wake_sem, and we expect
  201. that this CPU's scheduler will be suspended for 5ms shortly... */
  202. while(!sched_suspended) { }
  203. xSemaphoreGive(wake_sem);
  204. ets_delay_us(1000);
  205. // Bit of a race here if the other CPU resumes its scheduler, but 5ms is a long time... */
  206. TEST_ASSERT(sched_suspended);
  207. TEST_ASSERT_EQUAL(0, count_config.counter); // the other task hasn't woken yet, because scheduler is off
  208. TEST_ASSERT(sched_suspended);
  209. /* wait for the rest of the 5ms... */
  210. while(sched_suspended) { }
  211. ets_delay_us(100);
  212. TEST_ASSERT_EQUAL(1, count_config.counter); // when scheduler resumes, counter task should immediately count
  213. vTaskDelete(counter_task);
  214. }
  215. #endif