mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-30 20:51:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			145 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  Combined unit tests & benchmarking for spinlock "portMUX" functionality
 | |
| */
 | |
| 
 | |
| #include <esp_types.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #include "freertos/FreeRTOS.h"
 | |
| #include "freertos/task.h"
 | |
| #include "freertos/semphr.h"
 | |
| #include "freertos/queue.h"
 | |
| #include "freertos/xtensa_api.h"
 | |
| #include "unity.h"
 | |
| #include "soc/cpu.h"
 | |
| 
 | |
| #include "test_utils.h"
 | |
| 
 | |
| #define REPEAT_OPS 10000
 | |
| 
 | |
| static uint32_t start, end;
 | |
| 
 | |
| #define BENCHMARK_START() do {                  \
 | |
|         RSR(CCOUNT, start);                     \
 | |
|     } while(0)
 | |
| 
 | |
| #define BENCHMARK_END(OPERATION) do {                       \
 | |
|         RSR(CCOUNT, end);                                           \
 | |
|         printf("%s took %d cycles/op (%d cycles for %d ops)\n",     \
 | |
|                OPERATION, (end - start)/REPEAT_OPS,                 \
 | |
|                (end - start), REPEAT_OPS);                          \
 | |
|     } while(0)
 | |
| 
 | |
| TEST_CASE("portMUX spinlocks (no contention)", "[freertos]")
 | |
| {
 | |
|     portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
 | |
|     BENCHMARK_START();
 | |
| 
 | |
|     for (int i = 0; i < REPEAT_OPS; i++) {
 | |
|         portENTER_CRITICAL_ISR(&mux);
 | |
|         portEXIT_CRITICAL_ISR(&mux);
 | |
|     }
 | |
|     BENCHMARK_END("no contention lock");
 | |
| 
 | |
| #ifdef CONFIG_FREERTOS_UNICORE
 | |
|     TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP_UNICORE, "%d cycles/op", ((end - start)/REPEAT_OPS));
 | |
| #else
 | |
| #if CONFIG_SPIRAM
 | |
|     TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM, "%d cycles/op", ((end - start)/REPEAT_OPS));
 | |
| #else
 | |
|     TEST_PERFORMANCE_LESS_THAN(FREERTOS_SPINLOCK_CYCLES_PER_OP, "%d cycles/op", ((end - start)/REPEAT_OPS));
 | |
| #endif
 | |
| #endif
 | |
| }
 | |
| 
 | |
| TEST_CASE("portMUX recursive locks (no contention)", "[freertos]")
 | |
| {
 | |
|     portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
 | |
|     BENCHMARK_START();
 | |
| 
 | |
|     const int RECURSE_COUNT = 25;
 | |
| 
 | |
|     for (int i = 0; i < REPEAT_OPS / RECURSE_COUNT; i++) {
 | |
|         for (int j = 0; j < RECURSE_COUNT; j++) {
 | |
|             portENTER_CRITICAL(&mux);
 | |
|         }
 | |
|         for (int j = 0; j < RECURSE_COUNT; j++) {
 | |
|             portEXIT_CRITICAL(&mux);
 | |
|         }
 | |
|     }
 | |
|     BENCHMARK_END("no contention recursive");
 | |
| }
 | |
| 
 | |
| #if portNUM_PROCESSORS == 2
 | |
| 
 | |
| static volatile int shared_value;
 | |
| static portMUX_TYPE shared_mux;
 | |
| static xSemaphoreHandle done_sem;
 | |
| 
 | |
| static void task_shared_value_increment(void *ignore)
 | |
| {
 | |
|     for (int i = 0; i < REPEAT_OPS; i++) {
 | |
|         portENTER_CRITICAL(&shared_mux);
 | |
|         shared_value++;
 | |
|         portEXIT_CRITICAL(&shared_mux);
 | |
|     }
 | |
|     xSemaphoreGive(done_sem);
 | |
|     vTaskDelete(NULL);
 | |
| }
 | |
| 
 | |
| TEST_CASE("portMUX cross-core locking", "[freertos]")
 | |
| {
 | |
|     done_sem = xSemaphoreCreateCounting(2, 0);
 | |
|     vPortCPUInitializeMutex(&shared_mux);
 | |
|     shared_value = 0;
 | |
| 
 | |
|     BENCHMARK_START();
 | |
| 
 | |
|     xTaskCreatePinnedToCore(task_shared_value_increment, "INC0", 2048, NULL, UNITY_FREERTOS_PRIORITY + 1, NULL, UNITY_FREERTOS_CPU ? 0 : 1);
 | |
|     xTaskCreatePinnedToCore(task_shared_value_increment, "INC1", 2048, NULL, UNITY_FREERTOS_PRIORITY + 1, NULL, UNITY_FREERTOS_CPU);
 | |
| 
 | |
|     for(int i = 0; i < 2; i++) {
 | |
|         if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) {
 | |
|             TEST_FAIL_MESSAGE("done_sem not released by test task");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     BENCHMARK_END("cross-core incrementing");
 | |
|     vSemaphoreDelete(done_sem);
 | |
| 
 | |
|     TEST_ASSERT_EQUAL_INT(REPEAT_OPS * 2, shared_value);
 | |
| }
 | |
| 
 | |
| TEST_CASE("portMUX high contention", "[freertos]")
 | |
| {
 | |
|     const int TOTAL_TASKS = 8; /* half on each core */
 | |
|     done_sem = xSemaphoreCreateCounting(TOTAL_TASKS, 0);
 | |
|     vPortCPUInitializeMutex(&shared_mux);
 | |
|     shared_value = 0;
 | |
| 
 | |
|     BENCHMARK_START();
 | |
| 
 | |
|     for (int i = 0; i < TOTAL_TASKS / 2; i++) {
 | |
|         /* as each task has a higher priority than previous, expect
 | |
|            them to preempt the earlier created task, at least on the
 | |
|            other core (this core has the unity task, until that
 | |
|            blocks)... */
 | |
|         xTaskCreatePinnedToCore(task_shared_value_increment, "INC0", 2048, NULL, tskIDLE_PRIORITY + 1 + i, NULL, UNITY_FREERTOS_CPU ? 0 : 1);
 | |
|         xTaskCreatePinnedToCore(task_shared_value_increment, "INC1", 2048, NULL, tskIDLE_PRIORITY + 1 + i, NULL, UNITY_FREERTOS_CPU);
 | |
|     }
 | |
| 
 | |
|     for(int i = 0; i < TOTAL_TASKS; i++) {
 | |
|         if(!xSemaphoreTake(done_sem, 10000/portTICK_PERIOD_MS)) {
 | |
|             TEST_FAIL_MESSAGE("done_sem not released by test task");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     BENCHMARK_END("cross-core high contention");
 | |
|     vSemaphoreDelete(done_sem);
 | |
| 
 | |
|     TEST_ASSERT_EQUAL_INT(REPEAT_OPS * TOTAL_TASKS, shared_value);
 | |
| }
 | |
| 
 | |
| #endif // portNUM_PROCESSORS == 2
 | |
| 
 | 
