1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/param.h>
5 #include "esp_heap_caps.h"
6 #include "esp_rom_sys.h"
7 #include "freertos/FreeRTOS.h"
8 #include "freertos/task.h"
9 #include "freertos/semphr.h"
10 #include "unity.h"
11 #include "test_utils.h"
12 #include "ccomp_timer.h"
13 #include "esp_async_memcpy.h"
14 #include "soc/soc_caps.h"
15 
16 #if SOC_CP_DMA_SUPPORTED || SOC_GDMA_SUPPORTED
17 
18 #define ALIGN_UP(addr, align) (((addr) + (align)-1) & ~((align)-1))
19 
async_memcpy_setup_testbench(uint32_t seed,uint32_t * buffer_size,uint8_t ** src_buf,uint8_t ** dst_buf,uint8_t ** from_addr,uint8_t ** to_addr,uint32_t align)20 static void async_memcpy_setup_testbench(uint32_t seed, uint32_t *buffer_size, uint8_t **src_buf, uint8_t **dst_buf, uint8_t **from_addr, uint8_t **to_addr, uint32_t align)
21 {
22     srand(seed);
23     printf("allocating memory buffer...\r\n");
24     // memory copy from/to PSRAM is not allowed
25     *src_buf = heap_caps_malloc(*buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
26     *dst_buf = heap_caps_calloc(1, *buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
27 
28     TEST_ASSERT_NOT_NULL_MESSAGE(*src_buf, "allocate source buffer failed");
29     TEST_ASSERT_NOT_NULL_MESSAGE(*dst_buf, "allocate destination buffer failed");
30 
31     *from_addr = (uint8_t *)ALIGN_UP((uint32_t)(*src_buf), 4);
32     *to_addr = (uint8_t *)ALIGN_UP((uint32_t)(*dst_buf), 4);
33     uint8_t gap = MAX(*from_addr - *src_buf, *to_addr - *dst_buf);
34     *buffer_size -= gap;
35 
36     *from_addr += align;
37     *to_addr += align;
38     *buffer_size -= align;
39 
40     printf("...size %d Bytes, src@%p, dst@%p\r\n", *buffer_size, *from_addr, *to_addr);
41 
42     printf("fill src buffer with random data\r\n");
43     for (int i = 0; i < *buffer_size; i++) {
44         (*from_addr)[i] = rand() % 256;
45     }
46 }
47 
async_memcpy_verify_and_clear_testbench(uint32_t seed,uint32_t buffer_size,uint8_t * src_buf,uint8_t * dst_buf,uint8_t * from_addr,uint8_t * to_addr)48 static void async_memcpy_verify_and_clear_testbench(uint32_t seed, uint32_t buffer_size, uint8_t *src_buf, uint8_t *dst_buf, uint8_t *from_addr, uint8_t *to_addr)
49 {
50     srand(seed);
51     for (int i = 0; i < buffer_size; i++) {
52         // check if source date has been copied to destination and source data not broken
53         TEST_ASSERT_EQUAL_MESSAGE(rand() % 256, to_addr[i], "destination data doesn't match generator data");
54     }
55     srand(seed);
56     for (int i = 0; i < buffer_size; i++) {
57         // check if source data has been copied to destination
58         TEST_ASSERT_EQUAL_MESSAGE(rand() % 256, to_addr[i], "destination data doesn't match source data");
59     }
60     free(src_buf);
61     free(dst_buf);
62 }
63 
64 TEST_CASE("memory copy by DMA one by one", "[async mcp]")
65 {
66     async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
67     config.backlog = 4;
68     async_memcpy_t driver = NULL;
69     TEST_ESP_OK(esp_async_memcpy_install(&config, &driver));
70 
71     uint32_t test_buffer_len[] = {256, 512, 1024, 2048, 4096, 5011};
72     uint8_t *sbuf = NULL;
73     uint8_t *dbuf = NULL;
74     uint8_t *from = NULL;
75     uint8_t *to = NULL;
76 
77     for (int i = 0; i < sizeof(test_buffer_len) / sizeof(test_buffer_len[0]); i++) {
78         // Test different align edge
79         for (int align = 0; align < 4; align++) {
80             async_memcpy_setup_testbench(i, &test_buffer_len[i], &sbuf, &dbuf, &from, &to, align);
81             TEST_ESP_OK(esp_async_memcpy(driver, to, from, test_buffer_len[i], NULL, NULL));
82             async_memcpy_verify_and_clear_testbench(i, test_buffer_len[i], sbuf, dbuf, from, to);
83 
84             vTaskDelay(pdMS_TO_TICKS(100));
85         }
86     }
87 
88     TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
89 }
90 
91 TEST_CASE("memory copy by DMA on the fly", "[async mcp]")
92 {
93     async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
94     async_memcpy_t driver = NULL;
95     TEST_ESP_OK(esp_async_memcpy_install(&config, &driver));
96 
97     uint32_t test_buffer_len[] = {512, 1024, 2048, 4096, 5011};
98     uint8_t *sbufs[] = {0, 0, 0, 0, 0};
99     uint8_t *dbufs[] = {0, 0, 0, 0, 0};
100     uint8_t *froms[] = {0, 0, 0, 0, 0};
101     uint8_t *tos[] = {0, 0, 0, 0, 0};
102 
103     // Aligned case
104     for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
105         async_memcpy_setup_testbench(i, &test_buffer_len[i], &sbufs[i], &dbufs[i], &froms[i], &tos[i], 0);
106     }
107     for (int i = 0; i < sizeof(test_buffer_len) / sizeof(test_buffer_len[0]); i++) {
108         TEST_ESP_OK(esp_async_memcpy(driver, tos[i], froms[i], test_buffer_len[i], NULL, NULL));
109     }
110     for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
111         async_memcpy_verify_and_clear_testbench(i, test_buffer_len[i], sbufs[i], dbufs[i], froms[i], tos[i]);
112     }
113 
114     // Non-aligned case
115     for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
116         async_memcpy_setup_testbench(i, &test_buffer_len[i], &sbufs[i], &dbufs[i], &froms[i], &tos[i], 3);
117     }
118     for (int i = 0; i < sizeof(test_buffer_len) / sizeof(test_buffer_len[0]); i++) {
119         TEST_ESP_OK(esp_async_memcpy(driver, tos[i], froms[i], test_buffer_len[i], NULL, NULL));
120     }
121     for (int i = 0; i < sizeof(sbufs) / sizeof(sbufs[0]); i++) {
122         async_memcpy_verify_and_clear_testbench(i, test_buffer_len[i], sbufs[i], dbufs[i], froms[i], tos[i]);
123     }
124 
125     TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
126 }
127 
128 #define TEST_ASYNC_MEMCPY_BENCH_COUNTS (16)
129 static uint32_t test_async_memcpy_bench_len = 4095;
130 static int count = 0;
131 
test_async_memcpy_isr_cb(async_memcpy_t mcp_hdl,async_memcpy_event_t * event,void * cb_args)132 static IRAM_ATTR bool test_async_memcpy_isr_cb(async_memcpy_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)
133 {
134     SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args;
135     BaseType_t high_task_wakeup = pdFALSE;
136     count++;
137     if (count == TEST_ASYNC_MEMCPY_BENCH_COUNTS) {
138         xSemaphoreGiveFromISR(sem, &high_task_wakeup);
139     }
140     return high_task_wakeup == pdTRUE;
141 }
142 
143 TEST_CASE("memory copy by DMA with callback", "[async mcp]")
144 {
145     SemaphoreHandle_t sem = xSemaphoreCreateBinary();
146 
147     async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
148     config.backlog = TEST_ASYNC_MEMCPY_BENCH_COUNTS;
149     async_memcpy_t driver = NULL;
150     TEST_ESP_OK(esp_async_memcpy_install(&config, &driver));
151 
152     uint8_t *sbuf = NULL;
153     uint8_t *dbuf = NULL;
154     uint8_t *from = NULL;
155     uint8_t *to = NULL;
156 
157     async_memcpy_setup_testbench(0, &test_async_memcpy_bench_len, &sbuf, &dbuf, &from, &to, 0);
158     count = 0;
159     ccomp_timer_start();
160     for (int i = 0; i < TEST_ASYNC_MEMCPY_BENCH_COUNTS; i++) {
161         TEST_ESP_OK(esp_async_memcpy(driver, to, from, test_async_memcpy_bench_len, test_async_memcpy_isr_cb, sem));
162     }
163 
164     // wait for done semaphore
165     TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(sem, pdMS_TO_TICKS(1000)));
166     esp_rom_printf("memcpy %d Bytes data by HW costs %lldus\r\n", test_async_memcpy_bench_len, ccomp_timer_stop() / TEST_ASYNC_MEMCPY_BENCH_COUNTS);
167 
168     ccomp_timer_start();
169     for (int i = 0; i < TEST_ASYNC_MEMCPY_BENCH_COUNTS; i++) {
170         memcpy(to, from, test_async_memcpy_bench_len);
171     }
172     esp_rom_printf("memcpy %d Bytes data by SW costs %lldus\r\n", test_async_memcpy_bench_len, ccomp_timer_stop() / TEST_ASYNC_MEMCPY_BENCH_COUNTS);
173 
174     async_memcpy_verify_and_clear_testbench(0, test_async_memcpy_bench_len, sbuf, dbuf, from, to);
175 
176     TEST_ESP_OK(esp_async_memcpy_uninstall(driver));
177     vSemaphoreDelete(sem);
178 }
179 
180 #endif //SOC_CP_DMA_SUPPORTED || SOC_GDMA_SUPPORTED
181