1 /*
2  Unit tests for FreeRTOS preemption
3 */
4 
5 #include <esp_types.h>
6 #include <stdio.h>
7 
8 #include "freertos/FreeRTOS.h"
9 #include "freertos/task.h"
10 #include "freertos/semphr.h"
11 #include "freertos/queue.h"
12 #include "unity.h"
13 #include "soc/cpu.h"
14 #include "hal/cpu_hal.h"
15 #include "test_utils.h"
16 #include "sdkconfig.h"
17 
18 static volatile bool trigger;
19 static volatile bool flag;
20 
21 /* Task:
22    - Waits for 'trigger' variable to be set
23    - Reads the cycle count on this CPU
24    - Pushes it into a queue supplied as a param
25    - Busy-waits until the main task terminates it
26 */
task_send_to_queue(void * param)27 static void task_send_to_queue(void *param)
28 {
29     QueueHandle_t queue = (QueueHandle_t) param;
30     uint32_t ccount;
31 
32     while(!trigger) {}
33 
34     ccount = cpu_hal_get_cycle_count();
35     flag = true;
36     xQueueSendToBack(queue, &ccount, 0);
37     /* This is to ensure that higher priority task
38        won't wake anyhow, due to this task terminating.
39 
40        The task runs until terminated by the main task.
41     */
42     while(1) {}
43 }
44 
45 TEST_CASE("Yield from lower priority task, same CPU", "[freertos]")
46 {
47     /* Do this 3 times, mostly for the benchmark value - the first
48        run includes a cache miss so uses more cycles than it should. */
49     for (int i = 0; i < 3; i++) {
50         TaskHandle_t sender_task;
51         QueueHandle_t queue = xQueueCreate(1, sizeof(uint32_t));
52         flag = false;
53         trigger = false;
54 
55         /* "yield" task sits on our CPU, lower priority to us */
56         xTaskCreatePinnedToCore(task_send_to_queue, "YIELD", 2048, (void *)queue, UNITY_FREERTOS_PRIORITY - 1, &sender_task, UNITY_FREERTOS_CPU);
57 
58         vTaskDelay(1); /* make sure everything is set up */
59         trigger = true;
60 
61         uint32_t yield_ccount, now_ccount, delta;
62         TEST_ASSERT( xQueueReceive(queue, &yield_ccount, 100 / portTICK_PERIOD_MS) );
63         now_ccount = cpu_hal_get_cycle_count();
64         TEST_ASSERT( flag );
65 
66         delta = now_ccount - yield_ccount;
67         printf("Yielding from lower priority task took %u cycles\n", delta);
68         TEST_ASSERT(delta < 10000);
69 
70         vTaskDelete(sender_task);
71         vQueueDelete(queue);
72     }
73 }
74 
75 
76 #if (portNUM_PROCESSORS == 2) && !CONFIG_FREERTOS_TASK_FUNCTIONS_INTO_FLASH
77 TEST_CASE("Yield from lower priority task, other CPU", "[freertos]")
78 {
79     uint32_t trigger_ccount, yield_ccount, now_ccount, delta;
80 
81     /* Do this 3 times, mostly for the benchmark value - the first
82        run includes a cache miss so uses more cycles than it should. */
83     for (int i = 0; i < 3; i++) {
84         TaskHandle_t sender_task;
85         QueueHandle_t queue = xQueueCreate(1, sizeof(uint32_t));
86         trigger = false;
87         flag = false;
88 
89         /* "send_to_queue" task sits on the other CPU, lower priority to us */
90         xTaskCreatePinnedToCore(task_send_to_queue, "YIELD", 2048, (void *)queue, UNITY_FREERTOS_PRIORITY - 1,
91                                 &sender_task, !UNITY_FREERTOS_CPU);
92 
93         vTaskDelay(2); /* make sure everything is set up */
94         trigger = true;
95         trigger_ccount = cpu_hal_get_cycle_count();
96 
97         // yield_ccount is not useful in this test as it's the other core's CCOUNT
98         // so we use trigger_ccount instead
99         TEST_ASSERT( xQueueReceive(queue, &yield_ccount, 100 / portTICK_PERIOD_MS) );
100         now_ccount = cpu_hal_get_cycle_count();
101         TEST_ASSERT( flag );
102 
103         delta = now_ccount - trigger_ccount;
104         printf("Yielding from task on other core took %u cycles\n", delta);
105         TEST_ASSERT(delta < 10000);
106 
107         vQueueDelete(queue);
108         vTaskDelete(sender_task);
109     }
110 }
111 #endif
112