test_spinlocks.c 5.1 KB

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