test_spinlocks.c
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 "freertos/xtensa_api.h" 13 #include "unity.h" 14 #include "soc/cpu.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 RSR(CCOUNT, start); \ 24 } while(0) 25 26 #define BENCHMARK_END(OPERATION) do { \ 27 RSR(CCOUNT, end); \ 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 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