1 /*
2 Combined unit tests & benchmarking for spinlock "portMUX" functionality
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
16 #include "test_utils.h"
17
18 #define REPEAT_OPS 10000
19
20 static uint32_t start, end;
21
22 #define BENCHMARK_START() do { \
23 start = cpu_hal_get_cycle_count(); \
24 } while(0)
25
26 #define BENCHMARK_END(OPERATION) do { \
27 end = cpu_hal_get_cycle_count(); \
28 printf("%s took %d cycles/op (%d cycles for %d ops)\n", \
29 OPERATION, (end - start)/REPEAT_OPS, \
30 (end - start), REPEAT_OPS); \
31 } while(0)
32
33 TEST_CASE("portMUX spinlocks (no contention)", "[freertos]")
34 {
35 portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
36 BENCHMARK_START();
37
38 for (int i = 0; i < REPEAT_OPS; i++) {
39 portENTER_CRITICAL_ISR(&mux);
40 portEXIT_CRITICAL_ISR(&mux);
41 }
42 BENCHMARK_END("no contention lock");
43
44 #ifdef CONFIG_FREERTOS_UNICORE
45 TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP_UNICORE, "%d cycles/op", ((end - start)/REPEAT_OPS));
46 #else
47 #if CONFIG_SPIRAM
48 TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM, "%d cycles/op", ((end - start)/REPEAT_OPS));
49 #else
50 TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP, "%d cycles/op", ((end - start)/REPEAT_OPS));
51 #endif
52 #endif
53 }
54
55 TEST_CASE("portMUX recursive locks (no contention)", "[freertos]")
56 {
57 portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
58 BENCHMARK_START();
59
60 const int RECURSE_COUNT = 25;
61
62 for (int i = 0; i < REPEAT_OPS / RECURSE_COUNT; i++) {
63 for (int j = 0; j < RECURSE_COUNT; j++) {
64 portENTER_CRITICAL(&mux);
65 }
66 for (int j = 0; j < RECURSE_COUNT; j++) {
67 portEXIT_CRITICAL(&mux);
68 }
69 }
70 BENCHMARK_END("no contention recursive");
71 }
72
73 #if portNUM_PROCESSORS == 2
74
75 static volatile int shared_value;
76 static portMUX_TYPE shared_mux;
77 static xSemaphoreHandle done_sem;
78
task_shared_value_increment(void * ignore)79 static void task_shared_value_increment(void *ignore)
80 {
81 for (int i = 0; i < REPEAT_OPS; i++) {
82 portENTER_CRITICAL(&shared_mux);
83 shared_value++;
84 portEXIT_CRITICAL(&shared_mux);
85 }
86 xSemaphoreGive(done_sem);
87 vTaskDelete(NULL);
88 }
89
90 TEST_CASE("portMUX cross-core locking", "[freertos]")
91 {
92 done_sem = xSemaphoreCreateCounting(2, 0);
93 vPortCPUInitializeMutex(&shared_mux);
94 shared_value = 0;
95
96 BENCHMARK_START();
97
98 xTaskCreatePinnedToCore(task_shared_value_increment, "INC0", 2048, NULL, UNITY_FREERTOS_PRIORITY + 1, NULL, UNITY_FREERTOS_CPU ? 0 : 1);
99 xTaskCreatePinnedToCore(task_shared_value_increment, "INC1", 2048, NULL, UNITY_FREERTOS_PRIORITY + 1, NULL, UNITY_FREERTOS_CPU);
100
101 for(int i = 0; i < 2; i++) {
102 if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) {
103 TEST_FAIL_MESSAGE("done_sem not released by test task");
104 }
105 }
106
107 BENCHMARK_END("cross-core incrementing");
108 vSemaphoreDelete(done_sem);
109
110 TEST_ASSERT_EQUAL_INT(REPEAT_OPS * 2, shared_value);
111 }
112
113 TEST_CASE("portMUX high contention", "[freertos]")
114 {
115 const int TOTAL_TASKS = 8; /* half on each core */
116 done_sem = xSemaphoreCreateCounting(TOTAL_TASKS, 0);
117 vPortCPUInitializeMutex(&shared_mux);
118 shared_value = 0;
119
120 BENCHMARK_START();
121
122 for (int i = 0; i < TOTAL_TASKS / 2; i++) {
123 /* as each task has a higher priority than previous, expect
124 them to preempt the earlier created task, at least on the
125 other core (this core has the unity task, until that
126 blocks)... */
127 xTaskCreatePinnedToCore(task_shared_value_increment, "INC0", 2048, NULL, tskIDLE_PRIORITY + 1 + i, NULL, UNITY_FREERTOS_CPU ? 0 : 1);
128 xTaskCreatePinnedToCore(task_shared_value_increment, "INC1", 2048, NULL, tskIDLE_PRIORITY + 1 + i, NULL, UNITY_FREERTOS_CPU);
129 }
130
131 for(int i = 0; i < TOTAL_TASKS; i++) {
132 if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) {
133 TEST_FAIL_MESSAGE("done_sem not released by test task");
134 }
135 }
136
137 BENCHMARK_END("cross-core high contention");
138 vSemaphoreDelete(done_sem);
139
140 TEST_ASSERT_EQUAL_INT(REPEAT_OPS * TOTAL_TASKS, shared_value);
141 }
142
143 #endif // portNUM_PROCESSORS == 2
144