/ components / freertos / test / test_spinlocks.c
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