test_spinlocks.c 4.4 KB

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