esp-idf/components/esp_hw_support/test_apps/dma2d/main/test_dma2d.c

703 wiersze
29 KiB
C

/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include "unity.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_private/dma2d.h"
#include "dma2d_test_utils.h"
#include "hal/dma2d_types.h"
#include "hal/color_types.h"
#include "esp_heap_caps.h"
#include "esp_cache.h"
// All test will perform `M2M_TRANS_TIMES` times memcpy transactions, utilizing all available 2D-DMA channels.
// This tests the hardware capability of multiple 2D-DMA transactions running together, and the driver capbility of
// transactions being send to a queue, and waiting for free channels becoming available, and being picked to start the
// real hardware operation.
#define M2M_TRANS_TIMES (8)
// Descriptor and buffer address and size should aligned to 64 bytes (the cacheline size alignment restriction) to be used by CPU
static dma2d_descriptor_t *tx_dsc[M2M_TRANS_TIMES];
static dma2d_descriptor_t *rx_dsc[M2M_TRANS_TIMES];
static dma2d_m2m_trans_config_t m2m_trans_config[M2M_TRANS_TIMES];
static void dma2d_link_dscr_init(uint32_t *head, uint32_t *next, void *buf_ptr,
uint32_t ha, uint32_t va, uint32_t hb, uint32_t vb,
uint32_t eof, uint32_t en_2d, uint32_t pbyte, uint32_t mod,
uint32_t bias_x, uint32_t bias_y)
{
dma2d_descriptor_t *dma2d = (dma2d_descriptor_t *)head;
memset(dma2d, 0, sizeof(dma2d_descriptor_t));
dma2d->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA;
dma2d->suc_eof = eof;
dma2d->dma2d_en = en_2d;
dma2d->err_eof = 0;
dma2d->hb_length = hb;
dma2d->vb_size = vb;
dma2d->pbyte = pbyte;
dma2d->ha_length = ha;
dma2d->va_size = va;
dma2d->mode = mod;
dma2d->y = bias_y;
dma2d->x = bias_x;
dma2d->buffer = buf_ptr;
dma2d->next = (dma2d_descriptor_t *)next;
}
static bool IRAM_ATTR dma2d_m2m_suc_eof_event_cb(void *user_data)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
SemaphoreHandle_t sem = (SemaphoreHandle_t)user_data;
xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken);
return (xHigherPriorityTaskWoken == pdTRUE);
}
TEST_CASE("DMA2D_M2M_1D_basic", "[DMA2D]")
{
// Test a 16KB data block pure memcopy
const uint32_t data_size = 16 * 1024; // unit: byte
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
TEST_ESP_OK(dma2d_m2m_init());
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(tx_link_buffer);
TEST_ASSERT_NOT_NULL(rx_link_buffer);
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
}
uint8_t *prtx;
uint8_t *prrx;
uint8_t *tx_buf = heap_caps_aligned_calloc(64, data_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
uint8_t *rx_buf = heap_caps_aligned_calloc(64, data_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(tx_buf);
TEST_ASSERT_NOT_NULL(rx_buf);
dma2d_transfer_ability_t transfer_ability_config = {
.data_burst_length = DMA2D_DATA_BURST_LENGTH_64,
.desc_burst_en = true,
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
};
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
// Preparation
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
// Buffer data preparation
prtx = tx_buf + i * data_size;
prrx = rx_buf + i * data_size;
for (int idx = 0; idx < data_size; idx++) {
prtx[idx] = (i + idx + 0x45) & 0xFF;
prrx[idx] = 0;
}
// Writeback TX and RX buffers
esp_cache_msync((void *)prtx, data_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)prrx, data_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// DMA description preparation
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
data_size >> 14, data_size >> 14,
data_size & 0x3FFF, data_size & 0x3FFF,
1, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
0, data_size >> 14,
0, data_size & 0x3FFF,
0, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
// Writeback the DMA descriptors
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// Construct dma2d_m2m_trans_config_t structure
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
m2m_trans_config[i].user_data = (void *)counting_sem;
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
printf("trans %d\n", i);
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
printf("trans %d done\n", i);
}
printf("All transactions done!\n");
// Check result
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
prtx = tx_buf + i * data_size;
prrx = rx_buf + i * data_size;
// Invalidate TX and RX buffers
esp_cache_msync((void *)prtx, data_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)prrx, data_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
for (int idx = 0; idx < data_size; idx++) {
TEST_ASSERT_EQUAL(prtx[idx], prrx[idx]);
TEST_ASSERT_EQUAL(prtx[idx], (i + idx + 0x45) & 0xFF);
}
}
free(tx_link_buffer);
free(rx_link_buffer);
free(tx_buf);
free(rx_buf);
vSemaphoreDelete(counting_sem);
TEST_ESP_OK(dma2d_m2m_deinit());
}
static void rgb565_to_rgb888(uint16_t rgb565, void *__r, void *__g, void *__b)
{
uint8_t *r = (uint8_t *)__r;
uint8_t *g = (uint8_t *)__g;
uint8_t *b = (uint8_t *)__b;
uint32_t _rgb565 = rgb565;
uint8_t _b = (_rgb565>>8) & 0xF8;
uint8_t _g = (_rgb565>>3) & 0xFC;
uint8_t _r = (_rgb565<<3) & 0xF8;
// *r = (_r & 0x08) ? (_r | 0x1) : (_r);
// *g = (_g & 0x04) ? (_g | 0x1) : (_g);
// *b = (_b & 0x08) ? (_b | 0x1) : (_b);
*r = _r | ( (_r >>3) & 0x7);
*g = _g | ( (_g >>2) & 0x3);
*b = _b | ( (_b >>3) & 0x7);
}
static int rgb565_to_rgb888_and_cmp(void *_rgb565, void *__rgb888, int pix)
{
uint16_t *rgb565 = (uint16_t *)_rgb565;
uint8_t *_rgb888 = (uint8_t *)__rgb888;
uint8_t _r,_g,_b;
for (int i = 0; i < pix; i++) {
rgb565_to_rgb888(rgb565[i], &_r, &_g, &_b);
if (_r != _rgb888[0] || _g != _rgb888[1] || _b != _rgb888[2]) {
printf("idx %d - conv fail, %x:%x:%x, rgb565:%x, _rgb888:%x:%x:%x\r\n",
i, _r, _g, _b, rgb565[i], _rgb888[0], _rgb888[1] ,_rgb888[2]);
return -1;
}
_rgb888 += 3;
}
return 0;
}
TEST_CASE("DMA2D_M2M_1D_RGB565_to_RGB888", "[DMA2D]")
{
// Test a 4K pixel 1D buffer (original pixel in RGB565 format, convert to RGB888 format)
const uint32_t item_size = 1024 * 4;
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
TEST_ESP_OK(dma2d_m2m_init());
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(tx_link_buffer);
TEST_ASSERT_NOT_NULL(rx_link_buffer);
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
}
uint8_t *prtx;
uint8_t *prrx;
uint8_t *tx_buf = heap_caps_aligned_calloc(64, item_size * 2 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
uint8_t *rx_buf = heap_caps_aligned_calloc(64, item_size * 3 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(tx_buf);
TEST_ASSERT_NOT_NULL(rx_buf);
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
dma2d_transfer_ability_t transfer_ability_config = {
.data_burst_length = DMA2D_DATA_BURST_LENGTH_128,
.desc_burst_en = true,
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
};
dma2d_csc_config_t m2m_dma2d_tx_csc = {
.tx_csc_option = DMA2D_CSC_TX_RGB565_TO_RGB888,
.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
};
// Preparation
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
// Buffer data preparation
prtx = tx_buf + i * item_size * 2;
prrx = rx_buf + i * item_size * 3;
for (int idx = 0; idx < item_size; idx++) {
uint32_t r = (idx * 32 / item_size) & 0x1F;
uint32_t g = (idx * 64 / item_size) & 0x3F;
uint32_t b = (idx * 32 / item_size) & 0x1F;
prtx[idx * 2] = (r << 3) || (g >> 3);
prtx[idx * 2 + 1] = (g << 5) || b;
prrx[idx * 3] = 0;
prrx[idx * 3 + 1] = 0;
prrx[idx * 3 + 2] = 0;
}
// Writeback TX and RX buffers
esp_cache_msync((void *)prtx, item_size * 2, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)prrx, item_size * 3, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// DMA description preparation
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
(item_size * 2) >> 14, (item_size * 2) >> 14,
(item_size * 2) & 0x3FFF, (item_size * 2) & 0x3FFF,
1, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
0, (item_size * 3) >> 14,
0, (item_size * 3) & 0x3FFF,
0, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL,
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
// Writeback the DMA descriptors
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// Construct dma2d_m2m_trans_config_t structure
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
m2m_trans_config[i].user_data = (void *)counting_sem;
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
m2m_trans_config[i].tx_csc_config = &m2m_dma2d_tx_csc;
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
printf("trans %d\n", i);
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
printf("trans %d done\n", i);
}
printf("All transactions done!\n");
// Check result
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
prtx = tx_buf + i * item_size * 2;
prrx = rx_buf + i * item_size * 3;
// Invalidate TX and RX buffers
esp_cache_msync((void *)prtx, item_size * 2, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)prrx, item_size * 3, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
TEST_ASSERT_EQUAL(0, rgb565_to_rgb888_and_cmp(prtx, prrx, item_size));
}
free(tx_link_buffer);
free(rx_link_buffer);
free(tx_buf);
free(rx_buf);
vSemaphoreDelete(counting_sem);
TEST_ESP_OK(dma2d_m2m_deinit());
}
TEST_CASE("DMA2D_M2M_2D_basic", "[DMA2D]")
{
// Test a 128 x 128 pixel data block (one byte per pixel - assume A8)
const color_space_pixel_format_t pixel_format = {
.color_space = COLOR_SPACE_ALPHA,
.pixel_format = COLOR_PIXEL_A8,
};
const uint32_t stripe_size = 128; // unit: bytes
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
TEST_ESP_OK(dma2d_m2m_init());
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(tx_link_buffer);
TEST_ASSERT_NOT_NULL(rx_link_buffer);
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
}
uint8_t *prtx;
uint8_t *prrx;
uint8_t *tx_buf = heap_caps_aligned_calloc(64, stripe_size * stripe_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
uint8_t *rx_buf = heap_caps_aligned_calloc(64, stripe_size * stripe_size * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(tx_buf);
TEST_ASSERT_NOT_NULL(rx_buf);
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
dma2d_transfer_ability_t transfer_ability_config = {
.data_burst_length = DMA2D_DATA_BURST_LENGTH_128,
.desc_burst_en = true,
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
};
// Preparation
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
// Buffer data preparation
prtx = tx_buf + i * stripe_size * stripe_size;
prrx = rx_buf + i * stripe_size * stripe_size;
for (int idx = 0; idx < stripe_size * stripe_size; idx++) {
prtx[idx] = (i + idx + 0x45) & 0xFF;
prrx[idx] = 0;
}
// Writeback TX and RX buffers
esp_cache_msync((void *)prtx, stripe_size * stripe_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)prrx, stripe_size * stripe_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// DMA description preparation
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
stripe_size, stripe_size,
stripe_size, stripe_size,
1, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format), // 1 bytes/pixel
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
stripe_size, stripe_size,
stripe_size, stripe_size,
0, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format), // 1 bytes/pixel
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
// Writeback the DMA descriptors
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// Construct dma2d_m2m_trans_config_t structure
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
m2m_trans_config[i].user_data = (void *)counting_sem;
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
printf("trans %d\n", i);
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
printf("trans %d done\n", i);
}
printf("All transactions done!\n");
// Check result
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
prtx = tx_buf + i * stripe_size * stripe_size;
prrx = rx_buf + i * stripe_size * stripe_size;
// Invalidate TX and RX buffers
esp_cache_msync((void *)prtx, stripe_size * stripe_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)prrx, stripe_size * stripe_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
for (int idx = 0; idx < stripe_size * stripe_size; idx++) {
TEST_ASSERT_EQUAL(prtx[idx], prrx[idx]);
TEST_ASSERT_EQUAL(prtx[idx], (i + idx + 0x45) & 0xFF);
}
}
free(tx_link_buffer);
free(rx_link_buffer);
free(tx_buf);
free(rx_buf);
vSemaphoreDelete(counting_sem);
TEST_ESP_OK(dma2d_m2m_deinit());
}
static void rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b, uint16_t *rgb565)
{
uint16_t _rgb565 = (b >> 3);
_rgb565 = (_rgb565 << 6) | (g >>2);
_rgb565 = (_rgb565 << 5) | (r >>3);
*rgb565 = _rgb565;
}
static int rgb888_to_rgb565_and_cmp(void *__rgb888, void *__rgb565, int pix)
{
uint8_t *_rgb888 = (uint8_t *)__rgb888;
uint16_t *rgb565 = (uint16_t *)__rgb565;
uint16_t _rgb565 = 0;
uint8_t *rgb888 = _rgb888;
for (int i = 0; i < pix; i++) {
rgb888_to_rgb565(rgb888[0], rgb888[1], rgb888[2], &_rgb565);
if (_rgb565 != rgb565[0]) {
printf("conv fail, r:%x, g:%x, b:%x, rgb565:%x, _rgb565:%x\r\n",
rgb888[0], rgb888[1], rgb888[2], rgb565[0], _rgb565);
return -1;
}
rgb888 += 3;
rgb565++;
}
return 0;
}
TEST_CASE("DMA2D_M2M_2D_RGB888_to_RGB565", "[DMA2D]")
{
// Test a 64 x 64 pixel data block (original pixel in RGB888 format, convert to RGB565 format)
const color_space_pixel_format_t in_pixel_format = {
.color_space = COLOR_SPACE_RGB,
.pixel_format = COLOR_PIXEL_RGB888,
};
const color_space_pixel_format_t out_pixel_format = {
.color_space = COLOR_SPACE_RGB,
.pixel_format = COLOR_PIXEL_RGB565,
};
const uint32_t stripe_pixel_size = 64; // unit: pixel
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
TEST_ESP_OK(dma2d_m2m_init());
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(tx_link_buffer);
TEST_ASSERT_NOT_NULL(rx_link_buffer);
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
}
uint8_t *prtx;
uint8_t *prrx;
uint8_t *tx_buf = heap_caps_aligned_calloc(64, stripe_pixel_size * stripe_pixel_size * 3 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
uint8_t *rx_buf = heap_caps_aligned_calloc(64, stripe_pixel_size * stripe_pixel_size * 2 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(tx_buf);
TEST_ASSERT_NOT_NULL(rx_buf);
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
dma2d_transfer_ability_t transfer_ability_config = {
.data_burst_length = DMA2D_DATA_BURST_LENGTH_16,
.desc_burst_en = true,
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
};
dma2d_csc_config_t m2m_dma2d_tx_csc = {
.tx_csc_option = DMA2D_CSC_TX_RGB888_TO_RGB565,
.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0,
};
// Preparation
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
// Buffer data preparation
prtx = tx_buf + i * stripe_pixel_size * stripe_pixel_size * 3;
prrx = rx_buf + i * stripe_pixel_size * stripe_pixel_size * 2;
for (int idx = 0; idx < stripe_pixel_size * stripe_pixel_size; idx++) {
uint32_t r = (i + idx + 0x5A) & 0xFF;
uint32_t g = (i + idx + 0x4C) & 0xFF;
uint32_t b = (i + idx + 0x9E) & 0xFF;
prtx[idx * 3] = r;
prtx[idx * 3 + 1] = g;
prtx[idx * 3 + 2] = b;
prrx[idx * 2] = 0;
prrx[idx * 2 + 1] = 0;
}
// Writeback TX and RX buffers
esp_cache_msync((void *)prtx, stripe_pixel_size * stripe_pixel_size * 3, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)prrx, stripe_pixel_size * stripe_pixel_size * 2, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// DMA description preparation
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
stripe_pixel_size, stripe_pixel_size,
stripe_pixel_size, stripe_pixel_size,
1, 1, dma2d_desc_pixel_format_to_pbyte_value(in_pixel_format),
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
stripe_pixel_size, stripe_pixel_size,
stripe_pixel_size, stripe_pixel_size,
0, 1, dma2d_desc_pixel_format_to_pbyte_value(out_pixel_format),
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
// Writeback the DMA descriptors
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// Construct dma2d_m2m_trans_config_t structure
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
m2m_trans_config[i].user_data = (void *)counting_sem;
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
m2m_trans_config[i].tx_csc_config = &m2m_dma2d_tx_csc;
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
printf("trans %d\n", i);
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
printf("trans %d done\n", i);
}
printf("All transactions done!\n");
// Check result
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
prtx = tx_buf + i * stripe_pixel_size * stripe_pixel_size * 3;
prrx = rx_buf + i * stripe_pixel_size * stripe_pixel_size * 2;
// Invalidate TX and RX buffers
esp_cache_msync((void *)prtx, stripe_pixel_size * stripe_pixel_size * 3, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)prrx, stripe_pixel_size * stripe_pixel_size * 2, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
TEST_ASSERT_EQUAL(0, rgb888_to_rgb565_and_cmp(prtx, prrx, stripe_pixel_size * stripe_pixel_size));
}
free(tx_link_buffer);
free(rx_link_buffer);
free(tx_buf);
free(rx_buf);
vSemaphoreDelete(counting_sem);
TEST_ESP_OK(dma2d_m2m_deinit());
}
TEST_CASE("DMA2D_M2M_2D_window", "[DMA2D]")
{
// Test 2D memcpy to a 2 x 2 block at (2, 4) in a 8 x 8 picture (pixel in RGB565 format)
const color_space_pixel_format_t pixel_format = {
.color_space = COLOR_SPACE_RGB,
.pixel_format = COLOR_PIXEL_RGB565,
};
const uint32_t va = 8, ha = 8; // Define picture height and width (unit: pixel)
const uint32_t vb = 2, hb = 2; // Define block height and width (unit: pixel)
const uint32_t x_offset = 2, y_offset = 4; // Define block location in the picture (unit: pixel)
memset(m2m_trans_config, 0, M2M_TRANS_TIMES * sizeof(dma2d_m2m_trans_config_t));
TEST_ESP_OK(dma2d_m2m_init());
dma2d_descriptor_t *tx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
dma2d_descriptor_t *rx_link_buffer = (dma2d_descriptor_t *)heap_caps_aligned_calloc(64, M2M_TRANS_TIMES, 64, MALLOC_CAP_DEFAULT);
TEST_ASSERT_NOT_NULL(tx_link_buffer);
TEST_ASSERT_NOT_NULL(rx_link_buffer);
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
tx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)tx_link_buffer + 64 * i);
rx_dsc[i] = (dma2d_descriptor_t *)((uint32_t)rx_link_buffer + 64 * i);
}
uint8_t *prtx;
uint8_t *prrx;
uint8_t *tx_buf = heap_caps_aligned_calloc(64, 64 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_INTERNAL);
uint8_t *rx_buf = heap_caps_aligned_calloc(64, va * ha * 2 * M2M_TRANS_TIMES, sizeof(uint8_t), MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(tx_buf);
TEST_ASSERT_NOT_NULL(rx_buf);
SemaphoreHandle_t counting_sem = xSemaphoreCreateCounting(M2M_TRANS_TIMES, 0);
dma2d_transfer_ability_t transfer_ability_config = {
.data_burst_length = DMA2D_DATA_BURST_LENGTH_128,
.desc_burst_en = true,
.mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE,
};
// Preparation
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
// Buffer data preparation
prtx = tx_buf + i * 64;
prrx = rx_buf + i * va * ha * 2;
for (int idx = 0; idx < vb * hb; idx++) {
prtx[idx * 2] = 0x55 + idx + i;
prtx[idx * 2 + 1] = 0xAA + idx + i;
}
for (int idx = 0; idx < va * ha; idx++) {
prrx[idx * 2] = 0xFF;
prrx[idx * 2 + 1] = 0xFF;
}
// Writeback TX and RX buffers
esp_cache_msync((void *)prtx, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)prrx, va * ha * 2, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// DMA description preparation
dma2d_link_dscr_init((uint32_t *)tx_dsc[i], NULL, (void *)prtx,
hb, vb,
hb, vb,
1, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format),
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0);
dma2d_link_dscr_init((uint32_t *)rx_dsc[i], NULL, (void *)prrx,
ha, va,
hb, vb,
0, 1, dma2d_desc_pixel_format_to_pbyte_value(pixel_format),
DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, x_offset, y_offset);
// Writeback the DMA descriptors
esp_cache_msync((void *)tx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
esp_cache_msync((void *)rx_dsc[i], 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
// Construct dma2d_m2m_trans_config_t structure
m2m_trans_config[i].tx_desc_base_addr = (intptr_t)tx_dsc[i];
m2m_trans_config[i].rx_desc_base_addr = (intptr_t)rx_dsc[i];
m2m_trans_config[i].trans_eof_cb = dma2d_m2m_suc_eof_event_cb;
m2m_trans_config[i].user_data = (void *)counting_sem;
m2m_trans_config[i].transfer_ability_config = &transfer_ability_config;
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
printf("trans %d\n", i);
TEST_ESP_OK(dma2d_m2m(&m2m_trans_config[i]));
}
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
xSemaphoreTake(counting_sem, portMAX_DELAY);
printf("trans %d done\n", i);
}
printf("All transactions done!\n");
// Print the picture and check result
for (int i = 0; i < M2M_TRANS_TIMES; i++) {
prtx = tx_buf + i * 64;
prrx = rx_buf + i * va * ha * 2;
// Invalidate TX and RX buffers
esp_cache_msync((void *)prtx, 64, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
esp_cache_msync((void *)prrx, va * ha * 2, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
printf("pic:\n");
for (int idx = 0; idx < va * ha; idx++) {
printf("%02X%02X ", prrx[idx * 2], prrx[idx * 2 + 1]);
if (idx % ha == (ha - 1)) {
printf("\n");
}
bool pixel_in_window = false;
for (int window_y = 0; window_y < vb; window_y++) {
if (idx >= (ha * (y_offset + window_y) + x_offset) && idx < (ha * (y_offset + window_y) + x_offset + hb)) {
uint32_t window_x = idx - ha * (y_offset + window_y) - x_offset;
TEST_ASSERT_EQUAL(prtx[(window_y * hb + window_x) * 2], prrx[idx * 2]);
TEST_ASSERT_EQUAL(prtx[(window_y * hb + window_x) * 2 + 1], prrx[idx * 2 + 1]);
pixel_in_window = true;
break;
}
}
if (!pixel_in_window) {
TEST_ASSERT_EQUAL(0XFF, prrx[idx * 2]);
TEST_ASSERT_EQUAL(0XFF, prrx[idx * 2 + 1]);
}
}
}
free(tx_link_buffer);
free(rx_link_buffer);
free(tx_buf);
free(rx_buf);
vSemaphoreDelete(counting_sem);
TEST_ESP_OK(dma2d_m2m_deinit());
}