feat(dma): graduate the dma driver from esp_hw_support to esp_driver_dma

This commit is contained in:
morris
2026-01-22 14:14:14 +08:00
parent b2507c3a82
commit 35bdd8c651
91 changed files with 139 additions and 133 deletions
@@ -0,0 +1,9 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(dma2d_test)
@@ -0,0 +1,2 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |
@@ -0,0 +1,10 @@
set(srcs "test_app_main.c"
"test_dma2d.c"
"dma2d_test_utils.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_mm esp_psram esp_driver_dma
WHOLE_ARCHIVE)
@@ -0,0 +1,162 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "unity.h"
#include "dma2d_test_utils.h"
#include "esp_private/dma2d.h"
#include "soc/dma2d_channel.h"
#include "esp_heap_caps.h"
#include "esp_memory_utils.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_log.h"
ESP_LOG_ATTR_TAG(TAG, "dma2d_m2m");
#if CONFIG_DMA2D_OPERATION_FUNC_IN_IRAM || CONFIG_DMA2D_ISR_IRAM_SAFE
#define DMA2D_M2M_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define DMA2D_M2M_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_DMA2D_ISR_IRAM_SAFE
#define DMA2D_M2M_ATTR IRAM_ATTR
#else
#define DMA2D_M2M_ATTR
#endif
typedef dma2d_m2m_trans_config_t dma2d_m2m_trans_desc_t;
typedef struct {
dma2d_m2m_trans_desc_t m2m_trans_desc;
dma2d_trans_config_t dma_chan_desc;
uint32_t dma_trans_placeholder_head; /* Head of the memory for storing the 2D-DMA transaction elm */
} dma2d_m2m_transaction_t;
static dma2d_pool_handle_t dma2d_pool_handle;
esp_err_t dma2d_m2m_init(void)
{
dma2d_pool_config_t dma2d_pool_config = {
.pool_id = 0,
};
return dma2d_acquire_pool(&dma2d_pool_config, &dma2d_pool_handle);
}
esp_err_t dma2d_m2m_deinit(void)
{
return dma2d_release_pool(dma2d_pool_handle);
}
static bool DMA2D_M2M_ATTR dma2d_m2m_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data)
{
bool need_yield = false;
dma2d_m2m_transaction_t *trans_config = (dma2d_m2m_transaction_t *)user_data;
dma2d_m2m_trans_desc_t *m2m_trans_desc = &trans_config->m2m_trans_desc;
if (m2m_trans_desc->trans_eof_cb) {
need_yield |= m2m_trans_desc->trans_eof_cb(m2m_trans_desc->user_data);
}
free(trans_config);
return need_yield;
}
static bool DMA2D_M2M_ATTR dma2d_m2m_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config)
{
assert(channel_num == 2 && dma2d_chans && user_config);
dma2d_m2m_transaction_t *trans_config = (dma2d_m2m_transaction_t *)user_config;
dma2d_m2m_trans_desc_t *m2m_trans_desc = &trans_config->m2m_trans_desc;
// Get the required 2D-DMA channel handles
uint32_t dma_tx_chan_idx = 0;
uint32_t dma_rx_chan_idx = 1;
if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX) {
dma_tx_chan_idx = 1;
dma_rx_chan_idx = 0;
}
dma2d_channel_handle_t dma_tx_chan = dma2d_chans[dma_tx_chan_idx].chan;
dma2d_channel_handle_t dma_rx_chan = dma2d_chans[dma_rx_chan_idx].chan;
dma2d_trigger_t trig_periph = {
.periph = DMA2D_TRIG_PERIPH_M2M,
.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_M2M_TX,
};
dma2d_connect(dma_tx_chan, &trig_periph);
trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_M2M_RX;
dma2d_connect(dma_rx_chan, &trig_periph);
if (m2m_trans_desc->transfer_ability_config) {
dma2d_set_transfer_ability(dma_tx_chan, m2m_trans_desc->transfer_ability_config);
dma2d_set_transfer_ability(dma_rx_chan, m2m_trans_desc->transfer_ability_config);
}
if (m2m_trans_desc->tx_strategy_config) {
dma2d_apply_strategy(dma_tx_chan, m2m_trans_desc->tx_strategy_config);
}
if (m2m_trans_desc->rx_strategy_config) {
dma2d_apply_strategy(dma_rx_chan, m2m_trans_desc->rx_strategy_config);
}
if (m2m_trans_desc->tx_csc_config) {
dma2d_configure_color_space_conversion(dma_tx_chan, m2m_trans_desc->tx_csc_config);
}
if (m2m_trans_desc->rx_csc_config) {
dma2d_configure_color_space_conversion(dma_rx_chan, m2m_trans_desc->rx_csc_config);
}
dma2d_rx_event_callbacks_t dma_cbs = {
.on_recv_eof = dma2d_m2m_transaction_done_cb,
};
dma2d_register_rx_event_callbacks(dma_rx_chan, &dma_cbs, (void *)trans_config);
dma2d_set_desc_addr(dma_tx_chan, m2m_trans_desc->tx_desc_base_addr);
dma2d_set_desc_addr(dma_rx_chan, m2m_trans_desc->rx_desc_base_addr);
dma2d_start(dma_tx_chan);
dma2d_start(dma_rx_chan);
// No need to yield
return false;
}
esp_err_t dma2d_m2m(const dma2d_m2m_trans_config_t *trans_config)
{
#if CONFIG_DMA2D_ISR_IRAM_SAFE
if (trans_config->trans_eof_cb) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(trans_config->trans_eof_cb),
ESP_ERR_INVALID_ARG, TAG, "trans_eof_cb not in IRAM");
}
if (trans_config->user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(trans_config->user_data),
ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
}
#endif
dma2d_m2m_transaction_t *dma2d_m2m_trans = (dma2d_m2m_transaction_t *)heap_caps_calloc(1, sizeof(dma2d_m2m_transaction_t) + SIZEOF_DMA2D_TRANS_T, DMA2D_M2M_MEM_ALLOC_CAPS);
TEST_ASSERT_NOT_NULL(dma2d_m2m_trans);
dma2d_m2m_trans->dma_chan_desc.tx_channel_num = 1;
dma2d_m2m_trans->dma_chan_desc.rx_channel_num = 1;
dma2d_m2m_trans->dma_chan_desc.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING;
dma2d_m2m_trans->dma_chan_desc.channel_flags |= (trans_config->tx_csc_config == NULL) ? 0 : DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC;
dma2d_m2m_trans->dma_chan_desc.channel_flags |= (trans_config->rx_csc_config == NULL) ? 0 : DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC;
dma2d_m2m_trans->dma_chan_desc.specified_tx_channel_mask = 0;
dma2d_m2m_trans->dma_chan_desc.specified_rx_channel_mask = 0;
memcpy(&dma2d_m2m_trans->m2m_trans_desc, trans_config, sizeof(dma2d_m2m_trans_config_t));
dma2d_m2m_trans->dma_chan_desc.user_config = (void *)dma2d_m2m_trans;
dma2d_m2m_trans->dma_chan_desc.on_job_picked = dma2d_m2m_transaction_on_picked;
esp_err_t ret = dma2d_enqueue(dma2d_pool_handle, &dma2d_m2m_trans->dma_chan_desc, (dma2d_trans_t *)&dma2d_m2m_trans->dma_trans_placeholder_head);
if (ret != ESP_OK) {
free(dma2d_m2m_trans);
}
return ret;
}
@@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_private/dma2d.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize the 2D-DMA module to perform memcopy operations
*/
esp_err_t dma2d_m2m_init(void);
/**
* @brief De-initialize the 2D-DMA module
*/
esp_err_t dma2d_m2m_deinit(void);
/**
* @brief Callback function when a memcopy operation is done
*
* @param user_data User registered data from `dma2d_m2m_trans_config_t`
*
* @return Whether a task switch is needed after the callback function returns,
* this is usually due to the callback wakes up some high priority task.
*/
typedef bool (*dma2d_m2m_trans_eof_callback_t)(void *user_data);
/**
* @brief A collection of configuration items for perferming a memcopy operation with 2D-DMA
*/
typedef struct {
intptr_t tx_desc_base_addr; /*!< 2D-DMA TX descriptor address */
intptr_t rx_desc_base_addr; /*!< 2D-DMA RX descriptor address */
dma2d_m2m_trans_eof_callback_t trans_eof_cb; /*!< Callback function to be called when the memcopy operation is done */
void *user_data; /*!< User registered data to be passed into `trans_eof_cb` callback */
dma2d_transfer_ability_t *transfer_ability_config; /*!< Pointer to a collection of 2D-DMA transfer ability configuration */
dma2d_strategy_config_t *tx_strategy_config; /*!< Pointer to a collection of 2D-DMA TX strategy configuration */
dma2d_strategy_config_t *rx_strategy_config; /*!< Pointer to a collection of 2D-DMA RX strategy configuration */
dma2d_csc_config_t *tx_csc_config; /*!< Pointer to a collection of 2D-DMA TX color space conversion configuration */
dma2d_csc_config_t *rx_csc_config; /*!< Pointer to a collection of 2D-DMA RX color space conversion configuration */
} dma2d_m2m_trans_config_t;
/**
* @brief Do a memcopy operation with 2D-DMA module
*
* @param trans_config Pointer to a collection of configurations for the memcopy operation
* @return
* - ESP_OK: Enqueue the transaction to 2D-DMA pool successfully
* - ESP_ERR_INVALID_ARG: Enqueue the transaction to 2D-DMA pool failed because of invalid argument
*/
esp_err_t dma2d_m2m(const dma2d_m2m_trans_config_t *trans_config);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#include "esp_newlib.h"
#include "unity_test_utils.h"
// Some resources are lazy allocated in the driver, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (200)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
printf(" ______ ___ ___ ______ _____ ______ \n");
printf("(______) (___)_(___) (______) _(_____) (______) \n");
printf("(_) (_) (_) (_) (_) (_)____(_) (_) _(_) (_) (_) \n");
printf("(_) (_)(_) (_) (_) (________) _(_) (_) (_)\n");
printf("(_)___(_) (_) (_) (_) (_) (_)___ (_)___(_) \n");
printf("(______) (_) (_) (_) (_) (_______) (______) \n");
unity_run_menu();
}
@@ -0,0 +1,690 @@
/*
* SPDX-FileCopyrightText: 2023-2025 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 (12)
// 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 esp_color_fourcc_t pixel_format = ESP_COLOR_FOURCC_ALPHA8;
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 esp_color_fourcc_t in_pixel_format = ESP_COLOR_FOURCC_BGR24;
const esp_color_fourcc_t out_pixel_format = ESP_COLOR_FOURCC_RGB16;
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 esp_color_fourcc_t pixel_format = ESP_COLOR_FOURCC_RGB16;
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());
}
@@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
@idf_parametrize('target', soc_filtered_targets('SOC_DMA2D_SUPPORTED == 1'), indirect=['target'])
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration')
def test_dma2d(dut: Dut) -> None:
dut.run_all_single_board_cases()
@@ -0,0 +1,6 @@
# set compiler optimization level
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
# we can silent the assertion to save the binary footprint
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
@@ -0,0 +1,3 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
@@ -0,0 +1,3 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_HEX=y
CONFIG_SPIRAM_SPEED_200M=y