mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
feat(mspi): split dma and mspi alignment
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
|
||||
@@ -269,6 +271,47 @@ typedef struct {
|
||||
*/
|
||||
esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dma2d_transfer_ability_t *ability);
|
||||
|
||||
/**
|
||||
* @brief Get DMA2D buffer alignment constraint for a specific buffer
|
||||
*
|
||||
* @note On invalid arguments, returns an impossible alignment (BIT(31)).
|
||||
*
|
||||
* @param[in] buffer Buffer address
|
||||
* @return Alignment requirement in bytes
|
||||
*/
|
||||
size_t dma2d_get_buffer_alignment_constraint(const void *buffer);
|
||||
|
||||
/**
|
||||
* @brief Get alignment required when allocating a buffer for DMA2D access
|
||||
*
|
||||
* Use this before the buffer exists (e.g. `heap_caps_aligned_calloc`).
|
||||
* Unlike `dma2d_get_buffer_alignment_constraint`, this returns the worst-case
|
||||
* DMA2D/MSPI alignment rather than treating a NULL pointer as invalid.
|
||||
*
|
||||
* @return Alignment requirement in bytes (1 if no strict alignment is needed)
|
||||
*/
|
||||
size_t dma2d_get_alloc_alignment(void);
|
||||
|
||||
/**
|
||||
* @brief Check whether a 2D DMA transaction window satisfies DMA2D/MSPI alignment
|
||||
*
|
||||
* Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both
|
||||
* address and size. For a 2D transfer that means:
|
||||
* - buffer base address aligned to N bytes
|
||||
* - bytes-per-line (`pic_width * bpp/8`) aligned, so every next line starts on an N-byte boundary
|
||||
* - transfer width (`blk_width * bpp/8`) aligned
|
||||
* - horizontal window offset (`offset_x * bpp/8`) aligned
|
||||
*
|
||||
* @param[in] buf Buffer base address
|
||||
* @param[in] pic_width Picture / stride width in pixels
|
||||
* @param[in] blk_width Transfer block width in pixels
|
||||
* @param[in] offset_x Horizontal offset of the block in pixels
|
||||
* @param[in] bit_depth Bits per pixel
|
||||
* @return true if the transaction satisfies the alignment constraints
|
||||
*/
|
||||
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
|
||||
uint32_t offset_x, uint32_t bit_depth);
|
||||
|
||||
/**
|
||||
* @brief A collection of color space conversion (CSC) items that each 2D-DMA channel could apply
|
||||
*/
|
||||
|
||||
@@ -219,23 +219,58 @@ typedef struct {
|
||||
esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transfer_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Get the alignment constraints for internal and external memory
|
||||
* @brief Get the alignment constraints for a configured GDMA channel
|
||||
*
|
||||
* @note You should call this function after `gdma_config_transfer`, the later one can
|
||||
* adjust the alignment constraints based on various conditions, e.g. burst size, memory encryption, etc.
|
||||
* @note You can use returned alignment value to validate if a DMA buffer provided by the upper layer meets the constraints.
|
||||
* adjust the alignment constraints based on GDMA-specific conditions, e.g. burst size.
|
||||
* @note Prefer this when allocating DMA buffers. Once a concrete buffer address is available,
|
||||
* use `gdma_get_buffer_alignment_constraint` for the effective runtime constraint of that region.
|
||||
* @note For allocation from external memory:
|
||||
* - Use `ext_enc_mem_alignment` as the safe default (worst-case MSPI encryption/ECC).
|
||||
* - Use `ext_no_enc_mem_alignment` when intentionally targeting no-encryption external memory (e.g. no-enc PSRAM).
|
||||
* @note The returned alignment doesn't take the cache line size into account, if you want to do aligned memory allocation,
|
||||
* you should align the buffer size to the cache line size by yourself if the DMA buffer is behind a cache.
|
||||
*
|
||||
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
|
||||
* @param[out] int_mem_alignment Internal memory alignment
|
||||
* @param[out] ext_mem_alignment External memory alignment
|
||||
* @param[out] ext_enc_mem_alignment External memory alignment including MSPI encryption/ECC constraints
|
||||
* @param[out] ext_no_enc_mem_alignment External memory alignment without MSPI region-specific constraints.
|
||||
* Useful when allocating from no-encryption external memory. Set to NULL if unused.
|
||||
* @return
|
||||
* - ESP_OK: Get alignment constraints successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get alignment constraints failed because of invalid argument
|
||||
* - ESP_FAIL: Get alignment constraints failed because of other error
|
||||
*/
|
||||
esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment, size_t *ext_mem_alignment);
|
||||
esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment,
|
||||
size_t *ext_enc_mem_alignment, size_t *ext_no_enc_mem_alignment);
|
||||
|
||||
/**
|
||||
* @brief Check whether buffer sizes must meet the configured channel alignment
|
||||
*
|
||||
* @note Call this function after `gdma_config_transfer`.
|
||||
* @note This reports GDMA hardware constraints only. Region-specific MSPI constraints
|
||||
* are enforced independently when buffers are mounted to a GDMA link list.
|
||||
*
|
||||
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
|
||||
* @return True when buffer sizes must be aligned, otherwise false
|
||||
*/
|
||||
bool gdma_is_size_alignment_required(gdma_channel_handle_t dma_chan);
|
||||
|
||||
/**
|
||||
* @brief Get the effective alignment constraint for a specific DMA buffer
|
||||
*
|
||||
* @note You should call this function after `gdma_config_transfer`.
|
||||
* @note The returned alignment combines GDMA channel constraints with MSPI constraints
|
||||
* of the actual buffer region. This lets external no-encryption PSRAM buffers use
|
||||
* their real runtime constraint instead of a generic worst-case MSPI alignment.
|
||||
* @note The returned alignment doesn't take the cache line size into account.
|
||||
* @note On invalid arguments, returns an impossible alignment (BIT(31)).
|
||||
*
|
||||
* @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_ahb_channel/gdma_new_axi_channel`
|
||||
* @param[in] buffer DMA buffer address
|
||||
* @return Effective buffer alignment in bytes
|
||||
*/
|
||||
size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer);
|
||||
|
||||
/**
|
||||
* @brief Apply channel strategy for GDMA channel
|
||||
|
||||
@@ -84,6 +84,10 @@ typedef struct {
|
||||
Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */
|
||||
uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check.
|
||||
Only enable it when you know what you are doing. */
|
||||
uint32_t check_size_align: 1; /*!< Whether to check that `length` is aligned to the alignment.
|
||||
RX callers can query `gdma_is_size_alignment_required` to determine whether
|
||||
the configured channel requires this check. Under MSPI Flash Encryption /
|
||||
PSRAM ECC, length alignment is always enforced regardless of this flag. */
|
||||
} flags; //!< Flags for buffer mount configurations
|
||||
} gdma_buffer_mount_config_t;
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ entries:
|
||||
gdma: gdma_stop (noflash)
|
||||
gdma: gdma_append (noflash)
|
||||
gdma: gdma_reset (noflash)
|
||||
gdma: gdma_get_buffer_alignment_constraint (noflash)
|
||||
gdma: gdma_is_size_alignment_required (noflash)
|
||||
|
||||
[mapping:gdma_hal]
|
||||
archive: libesp_hal_dma.a
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/param.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_async_color_convert_priv.h"
|
||||
#include "esp_private/dma2d.h"
|
||||
#include "soc/dma2d_channel.h"
|
||||
#include "hal/dma2d_types.h"
|
||||
#include "hal/dma2d_ll.h"
|
||||
@@ -207,6 +209,17 @@ static esp_err_t validate_request(const async_color_convert_request_t *request)
|
||||
request->dst_height <= DMA2D_LL_DESC_2D_FIELD_MAX,
|
||||
ESP_ERR_INVALID_ARG, TAG, "dimension exceeds DMA2D descriptor field limit");
|
||||
|
||||
uint32_t src_bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(request->src_color_format);
|
||||
uint32_t dst_bit_depth = color_hal_pixel_format_fourcc_get_bit_depth(request->dst_color_format);
|
||||
ESP_RETURN_ON_FALSE(dma2d_check_transaction_alignment_constraint(request->src_buffer, request->src_stride,
|
||||
request->copy_width, request->src_x,
|
||||
src_bit_depth),
|
||||
ESP_ERR_INVALID_ARG, TAG, "source buffer or window is not aligned to DMA2D alignment");
|
||||
ESP_RETURN_ON_FALSE(dma2d_check_transaction_alignment_constraint(request->dst_buffer, request->dst_stride,
|
||||
request->copy_width, request->dst_x,
|
||||
dst_bit_depth),
|
||||
ESP_ERR_INVALID_ARG, TAG, "destination buffer or window is not aligned to DMA2D alignment");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_check.h"
|
||||
@@ -51,8 +52,6 @@ typedef struct {
|
||||
gdma_channel_handle_t rx_channel; // GDMA RX channel handle used to drain M2M data
|
||||
portMUX_TYPE spin_lock; // Spinlock for synchronization
|
||||
_Atomic async_crc_fsm_t fsm; // driver state machine, changing state should be atomic
|
||||
size_t tx_int_mem_alignment; // Required DMA buffer alignment for internal TX memory
|
||||
size_t tx_ext_mem_alignment; // Required DMA buffer alignment for external TX memory
|
||||
uint8_t *rx_sink_buffer; // Sink buffer used to drain the M2M RX path
|
||||
gdma_link_list_handle_t rx_link_list; // Self-loop DMA link list for the RX sink buffer, shared by all crc transactions
|
||||
uint32_t gdma_bus_id; // GDMA bus id (AHB, AXI, etc.)
|
||||
@@ -164,9 +163,8 @@ esp_err_t esp_async_crc_install_gdma_template(const async_crc_config_t *config,
|
||||
ESP_GOTO_ON_ERROR(gdma_config_transfer(crc_gdma->rx_channel, &transfer_cfg), err, TAG, "config RX DMA transfer failed");
|
||||
|
||||
// Get buffer alignment required by GDMA channel
|
||||
gdma_get_alignment_constraints(crc_gdma->tx_channel, &crc_gdma->tx_int_mem_alignment, &crc_gdma->tx_ext_mem_alignment);
|
||||
size_t rx_int_mem_alignment = 0;
|
||||
gdma_get_alignment_constraints(crc_gdma->rx_channel, &rx_int_mem_alignment, NULL);
|
||||
gdma_get_channel_alignment_constraints(crc_gdma->rx_channel, &rx_int_mem_alignment, NULL, NULL);
|
||||
size_t rx_buffer_size = (rx_int_mem_alignment > CRC_DMA_RX_SINK_BUFFER_SIZE) ?
|
||||
rx_int_mem_alignment : CRC_DMA_RX_SINK_BUFFER_SIZE;
|
||||
crc_gdma->rx_sink_buffer = heap_caps_aligned_calloc(rx_int_mem_alignment, 1, rx_buffer_size,
|
||||
@@ -191,6 +189,7 @@ esp_err_t esp_async_crc_install_gdma_template(const async_crc_config_t *config,
|
||||
.length = rx_buffer_size,
|
||||
.flags = {
|
||||
.mark_final = GDMA_FINAL_LINK_TO_HEAD,
|
||||
.check_size_align = gdma_is_size_alignment_required(crc_gdma->rx_channel),
|
||||
},
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gdma_link_mount_buffers(crc_gdma->rx_link_list, 0, &rx_buf_mount_config, 1, NULL),
|
||||
@@ -324,13 +323,7 @@ static esp_err_t async_crc_prepare_transaction(async_crc_gdma_context_t *crc_gdm
|
||||
uint32_t max_crc_bit_width = (crc_gdma->gdma_bus_id == SOC_GDMA_BUS_AXI) ? GDMA_LL_AXI_MAX_CRC_BIT_WIDTH : GDMA_LL_AHB_MAX_CRC_BIT_WIDTH;
|
||||
ESP_RETURN_ON_FALSE(trans->params.width <= max_crc_bit_width, ESP_ERR_INVALID_ARG, TAG, "invalid crc bit width %"PRIu32, trans->params.width);
|
||||
|
||||
// Get buffer alignment based on memory type
|
||||
size_t buffer_alignment = esp_ptr_internal(trans->data) ? crc_gdma->tx_int_mem_alignment : crc_gdma->tx_ext_mem_alignment;
|
||||
|
||||
// Verify user buffer satisfies DMA alignment requirements
|
||||
ESP_RETURN_ON_FALSE(((uintptr_t)trans->data % buffer_alignment) == 0, ESP_ERR_INVALID_ARG, TAG,
|
||||
"Data buffer not aligned to %zu bytes", buffer_alignment);
|
||||
|
||||
size_t buffer_alignment = gdma_get_buffer_alignment_constraint(crc_gdma->tx_channel, trans->data);
|
||||
// Calculate number of DMA nodes needed
|
||||
size_t tx_num_dma_nodes = esp_dma_calculate_node_count(trans->size, buffer_alignment, CRC_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||
|
||||
|
||||
@@ -47,10 +47,6 @@ typedef struct async_memcpy_transaction_t {
|
||||
/// @note - Number of transaction objects are determined by the backlog parameter
|
||||
typedef struct {
|
||||
async_memcpy_context_t parent; // Parent IO interface
|
||||
size_t rx_int_mem_alignment; // Required DMA buffer alignment for internal RX memory
|
||||
size_t rx_ext_mem_alignment; // Required DMA buffer alignment for external RX memory
|
||||
size_t tx_int_mem_alignment; // Required DMA buffer alignment for internal TX memory
|
||||
size_t tx_ext_mem_alignment; // Required DMA buffer alignment for external TX memory
|
||||
int gdma_bus_id; // GDMA bus id (AHB, AXI, etc.)
|
||||
gdma_channel_handle_t tx_channel; // GDMA TX channel handle
|
||||
gdma_channel_handle_t rx_channel; // GDMA RX channel handle
|
||||
@@ -149,10 +145,6 @@ static esp_err_t esp_async_memcpy_install_gdma_template(const async_memcpy_confi
|
||||
ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->tx_channel, &transfer_cfg), err, TAG, "config transfer for tx channel failed");
|
||||
ESP_GOTO_ON_ERROR(gdma_config_transfer(mcp_gdma->rx_channel, &transfer_cfg), err, TAG, "config transfer for rx channel failed");
|
||||
|
||||
// get the buffer alignment required by the GDMA channel
|
||||
gdma_get_alignment_constraints(mcp_gdma->rx_channel, &mcp_gdma->rx_int_mem_alignment, &mcp_gdma->rx_ext_mem_alignment);
|
||||
gdma_get_alignment_constraints(mcp_gdma->tx_channel, &mcp_gdma->tx_int_mem_alignment, &mcp_gdma->tx_ext_mem_alignment);
|
||||
|
||||
// register rx eof callback
|
||||
gdma_rx_event_callbacks_t cbs = {
|
||||
.on_recv_eof = mcp_gdma_rx_eof_callback,
|
||||
@@ -281,30 +273,6 @@ static async_memcpy_transaction_t *try_pop_trans_from_idle_queue(async_memcpy_gd
|
||||
return trans;
|
||||
}
|
||||
|
||||
/// @brief Check if the address and size can meet the requirement of the DMA engine
|
||||
static bool check_buffer_alignment(async_memcpy_gdma_context_t *mcp_gdma, void *src, void *dst, size_t n)
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
if (esp_ptr_external_ram(dst)) {
|
||||
valid = valid && (((uint32_t)dst & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0);
|
||||
valid = valid && ((n & (mcp_gdma->rx_ext_mem_alignment - 1)) == 0);
|
||||
} else {
|
||||
valid = valid && (((uint32_t)dst & (mcp_gdma->rx_int_mem_alignment - 1)) == 0);
|
||||
valid = valid && ((n & (mcp_gdma->rx_int_mem_alignment - 1)) == 0);
|
||||
}
|
||||
|
||||
if (esp_ptr_external_ram(src)) {
|
||||
valid = valid && (((uint32_t)src & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0);
|
||||
valid = valid && ((n & (mcp_gdma->tx_ext_mem_alignment - 1)) == 0);
|
||||
} else {
|
||||
valid = valid && (((uint32_t)src & (mcp_gdma->tx_int_mem_alignment - 1)) == 0);
|
||||
valid = valid && ((n & (mcp_gdma->tx_int_mem_alignment - 1)) == 0);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@@ -335,8 +303,6 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
|
||||
dma_link_item_alignment = GDMA_LL_AHB_DESC_ALIGNMENT;
|
||||
}
|
||||
#endif // SOC_HAS(LP_AHB_GDMA)
|
||||
// alignment check
|
||||
ESP_RETURN_ON_FALSE(check_buffer_alignment(mcp_gdma, src, dst, n), ESP_ERR_INVALID_ARG, TAG, "address|size not aligned: %p -> %p, sz=%zu", src, dst, n);
|
||||
|
||||
async_memcpy_transaction_t *trans = NULL;
|
||||
// pick one transaction node from idle queue
|
||||
@@ -368,7 +334,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
|
||||
size_t num_dma_nodes = 0;
|
||||
|
||||
// allocate gdma TX link, only the body is handled by the DMA
|
||||
buffer_alignment = esp_ptr_internal(split.body_src) ? mcp_gdma->tx_int_mem_alignment : mcp_gdma->tx_ext_mem_alignment;
|
||||
buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->tx_channel, split.body_src);
|
||||
num_dma_nodes = esp_dma_calculate_node_count(split.body_len, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||
gdma_link_list_config_t tx_link_cfg = {
|
||||
.item_alignment = dma_link_item_alignment,
|
||||
@@ -394,7 +360,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
|
||||
gdma_link_mount_buffers(trans->tx_link_list, 0, tx_buf_mount_config, 1, NULL);
|
||||
|
||||
// allocate gdma RX link, only the body is handled by the DMA
|
||||
buffer_alignment = esp_ptr_internal(split.body_dst) ? mcp_gdma->rx_int_mem_alignment : mcp_gdma->rx_ext_mem_alignment;
|
||||
buffer_alignment = gdma_get_buffer_alignment_constraint(mcp_gdma->rx_channel, split.body_dst);
|
||||
num_dma_nodes = esp_dma_calculate_node_count(split.body_len, buffer_alignment, MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE);
|
||||
gdma_link_list_config_t rx_link_cfg = {
|
||||
.item_alignment = dma_link_item_alignment,
|
||||
@@ -414,6 +380,7 @@ static esp_err_t mcp_gdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *s
|
||||
.flags = {
|
||||
.mark_eof = true,
|
||||
.mark_final = GDMA_FINAL_LINK_TO_NULL,
|
||||
.check_size_align = gdma_is_size_alignment_required(mcp_gdma->rx_channel),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "hal/dma2d_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
#include "esp_private/sleep_retention.h"
|
||||
|
||||
/**
|
||||
@@ -801,8 +801,12 @@ esp_err_t dma2d_set_desc_addr(dma2d_channel_handle_t dma2d_chan, intptr_t desc_b
|
||||
addr_in_spm = esp_ptr_in_spm((void *)desc_base_addr);
|
||||
#endif
|
||||
ESP_GOTO_ON_FALSE_ISR((desc_base_addr & 0x7) == 0 && !addr_in_spm, ESP_ERR_INVALID_ARG, err, TAG, "invalid descriptor base addr");
|
||||
// When flash encryption is enabled, the descriptor must be in internal RAM because descriptor size is not 16-byte aligned, which breaks flash encryption alignment restriction
|
||||
ESP_GOTO_ON_FALSE_ISR(!esp_efuse_is_flash_encryption_enabled() || esp_ptr_internal((void *)desc_base_addr), ESP_ERR_INVALID_ARG, err, TAG, "invalid description base addr");
|
||||
// If descriptors are placed in external memory, their size must meet MSPI alignment constraints;
|
||||
// otherwise, descriptors must be located in internal RAM.
|
||||
size_t mspi_align = esp_mspi_get_alignment((void *)desc_base_addr);
|
||||
bool desc_size_mspi_aligned = (sizeof(dma2d_descriptor_t) & (mspi_align - 1)) == 0;
|
||||
ESP_GOTO_ON_FALSE_ISR(desc_size_mspi_aligned || esp_ptr_internal((void *)desc_base_addr),
|
||||
ESP_ERR_INVALID_ARG, err, TAG, "invalid description base addr");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
@@ -920,20 +924,19 @@ esp_err_t dma2d_set_transfer_ability(dma2d_channel_handle_t dma2d_chan, const dm
|
||||
ESP_GOTO_ON_FALSE_ISR(dma2d_chan && ability, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(ability->data_burst_length && ((ability->data_burst_length & (ability->data_burst_length - 1)) == 0), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); // burst size must be power of 2
|
||||
ESP_GOTO_ON_FALSE_ISR(ability->mb_size < DMA2D_MACRO_BLOCK_SIZE_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE_ISR(!ability->access_ext_mem || (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH), ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
dma2d_group_t *group = dma2d_chan->group;
|
||||
int channel_id = dma2d_chan->channel_id;
|
||||
|
||||
// When flash encryption is enabled, and the channel is accessing external memory, burst length has to be as least as the encryption alignment restriction size
|
||||
uint32_t data_burst_length = ability->data_burst_length;
|
||||
#if SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH
|
||||
if (esp_efuse_is_flash_encryption_enabled() && ability->access_ext_mem) {
|
||||
if (data_burst_length < SOC_MEMSPI_ENCRYPTION_ALIGNMENT) {
|
||||
data_burst_length = SOC_MEMSPI_ENCRYPTION_ALIGNMENT;
|
||||
ESP_LOGW(TAG, "channel access encrypted external memory, adjust burst size to %d", SOC_MEMSPI_ENCRYPTION_ALIGNMENT);
|
||||
if (ability->access_ext_mem) {
|
||||
// If channel is accessing external memory, burst length has to be at least the MSPI alignment restriction size
|
||||
size_t mspi_alignment = dma2d_get_alloc_alignment();
|
||||
if (data_burst_length < mspi_alignment) {
|
||||
data_burst_length = mspi_alignment;
|
||||
ESP_LOGW(TAG, "requested burst size does not meet MSPI alignment constraint, adjust to %d", (int)mspi_alignment);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dma2d_chan->direction == DMA2D_CHANNEL_DIRECTION_TX) {
|
||||
dma2d_ll_tx_enable_descriptor_burst(group->hal.dev, channel_id, ability->desc_burst_en);
|
||||
@@ -949,6 +952,46 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t dma2d_get_buffer_alignment_constraint(const void *buffer)
|
||||
{
|
||||
if (!buffer) {
|
||||
return BIT(31);
|
||||
}
|
||||
|
||||
return esp_mspi_get_alignment(buffer);
|
||||
}
|
||||
|
||||
size_t dma2d_get_alloc_alignment(void)
|
||||
{
|
||||
// Worst-case alignment for buffers that may be accessed by DMA2D (MSPI FE/ECC, etc.)
|
||||
return esp_mspi_get_alignment(NULL);
|
||||
}
|
||||
|
||||
bool dma2d_check_transaction_alignment_constraint(const void *buf, uint32_t pic_width, uint32_t blk_width,
|
||||
uint32_t offset_x, uint32_t bit_depth)
|
||||
{
|
||||
if (!buf || bit_depth == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t alignment = dma2d_get_buffer_alignment_constraint(buf);
|
||||
if (alignment <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Under Flash Encryption / PSRAM ECC, MSPI requires each AXI access to be aligned in both address and size.
|
||||
// For 2D DMA that means:
|
||||
// - buffer base address aligned to N bytes
|
||||
// - bytes-per-line (pic_width * bpp/8) aligned, so every next line starts on an N-byte boundary
|
||||
// - transfer width (blk_width * bpp/8) aligned
|
||||
// - horizontal window offset (offset_x * bpp/8) aligned, so the first pixel of the window is aligned
|
||||
uint32_t alignment_bits = alignment * 8;
|
||||
return (((uintptr_t)buf & (alignment - 1)) == 0) &&
|
||||
(((uint64_t)pic_width * bit_depth) % alignment_bits == 0) &&
|
||||
(((uint64_t)blk_width * bit_depth) % alignment_bits == 0) &&
|
||||
(((uint64_t)offset_x * bit_depth) % alignment_bits == 0);
|
||||
}
|
||||
|
||||
esp_err_t dma2d_configure_color_space_conversion(dma2d_channel_handle_t dma2d_chan, const dma2d_csc_config_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
@@ -43,7 +43,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
|
||||
split_line_size = int_mem_cache_line_size;
|
||||
}
|
||||
bool align_required = split_line_size > 0;
|
||||
ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size);
|
||||
ESP_EARLY_LOGV(TAG, "split_line_size:%d", split_line_size);
|
||||
|
||||
if (*ret_stash_buffer == NULL) {
|
||||
// If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "gdma_priv.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
|
||||
#define GDMA_INVALID_PERIPH_TRIG (0x3F)
|
||||
#define SEARCH_REQUEST_RX_CHANNEL (1 << 0)
|
||||
@@ -419,87 +420,101 @@ esp_err_t gdma_config_transfer(gdma_channel_handle_t dma_chan, const gdma_transf
|
||||
if (!dma_chan || !config) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint32_t max_data_burst_size = config->max_data_burst_size;
|
||||
size_t int_mem_alignment = 1;
|
||||
size_t ext_enc_mem_alignment = 1;
|
||||
size_t ext_no_enc_mem_alignment = 1;
|
||||
|
||||
if (config->access_ext_mem) {
|
||||
#if (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH) && SOC_AHB_GDMA_VERSION != 1
|
||||
// Under Flash Encryption/PSRAM ECC, external DMA must use MSPI-aligned bursts.
|
||||
size_t mspi_alignment = esp_mspi_get_alignment(NULL);
|
||||
if (mspi_alignment > 1) {
|
||||
if (max_data_burst_size < mspi_alignment) {
|
||||
max_data_burst_size = mspi_alignment;
|
||||
ESP_LOGW(TAG, "max_data_burst_size is less than mspi_alignment, adjusted to %d", max_data_burst_size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) || GDMA_LL_GET(AXI_PSRAM_CAPABLE) || GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
|
||||
ESP_RETURN_ON_FALSE(max_data_burst_size <= GDMA_LL_MAX_BURST_SIZE_PSRAM, ESP_ERR_INVALID_ARG,
|
||||
TAG, "max_data_burst_size must not exceed %d when accessing external memory", GDMA_LL_MAX_BURST_SIZE_PSRAM);
|
||||
#endif
|
||||
}
|
||||
if (max_data_burst_size) {
|
||||
// burst size must be power of 2
|
||||
ESP_RETURN_ON_FALSE((max_data_burst_size & (max_data_burst_size - 1)) == 0, ESP_ERR_INVALID_ARG,
|
||||
TAG, "invalid max_data_burst_size: %"PRIu32, max_data_burst_size);
|
||||
#if GDMA_LL_GET(AHB_PSRAM_CAPABLE) || GDMA_LL_GET(AXI_PSRAM_CAPABLE) || GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
|
||||
if (config->access_ext_mem) {
|
||||
ESP_RETURN_ON_FALSE(max_data_burst_size <= GDMA_LL_MAX_BURST_SIZE_PSRAM, ESP_ERR_INVALID_ARG,
|
||||
TAG, "max_data_burst_size must not exceed %d when accessing external memory", GDMA_LL_MAX_BURST_SIZE_PSRAM);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
gdma_pair_t *pair = dma_chan->pair;
|
||||
gdma_group_t *group = pair->group;
|
||||
gdma_hal_context_t *hal = &group->hal;
|
||||
size_t int_mem_alignment = 1;
|
||||
size_t ext_mem_alignment = 1;
|
||||
|
||||
// always enable descriptor burst as the descriptor is always word aligned and is in the internal SRAM
|
||||
bool en_desc_burst = true;
|
||||
bool en_data_burst = max_data_burst_size > 0;
|
||||
|
||||
// There's auto alignment for AHB GDMA version 1, so we don't need to do anything here
|
||||
// While, for AHB GDMA version 2 and AXI GDMA, we need to ensure the alignment by software
|
||||
#if (SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH) && SOC_AHB_GDMA_VERSION != 1
|
||||
bool ext_mem_needs_mspi_alignment = esp_efuse_is_flash_encryption_enabled();
|
||||
#if CONFIG_SPIRAM_ECC_ENABLE
|
||||
ext_mem_needs_mspi_alignment = true;
|
||||
#endif
|
||||
// When MSPI encryption or PSRAM ECC address conversion is enabled, DMA accesses to
|
||||
// external memory need to follow the MSPI encryption alignment restriction.
|
||||
if (ext_mem_needs_mspi_alignment && config->access_ext_mem) {
|
||||
uint32_t mspi_mem_alignment = SOC_MEMSPI_ENCRYPTION_ALIGNMENT;
|
||||
ext_mem_alignment = MAX(ext_mem_alignment, mspi_mem_alignment);
|
||||
if (max_data_burst_size < mspi_mem_alignment) {
|
||||
ESP_LOGW(TAG, "GDMA channel access encrypted/ECC external memory, adjust burst size to %d", mspi_mem_alignment);
|
||||
en_data_burst = true;
|
||||
max_data_burst_size = mspi_mem_alignment;
|
||||
}
|
||||
}
|
||||
#endif // SOC_PSRAM_DMA_CAPABLE || SOC_DMA_CAN_ACCESS_FLASH
|
||||
|
||||
gdma_hal_enable_burst(hal, pair->pair_id, dma_chan->direction, en_data_burst, en_desc_burst);
|
||||
dma_chan->flags.size_alignment_required = false;
|
||||
if (en_data_burst) {
|
||||
gdma_hal_set_burst_size(hal, pair->pair_id, dma_chan->direction, max_data_burst_size);
|
||||
#if CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION
|
||||
// due to hardware limitation, if weighted arbitration is enabled, the data must be aligned to burst size
|
||||
int_mem_alignment = MAX(int_mem_alignment, max_data_burst_size);
|
||||
ext_mem_alignment = MAX(ext_mem_alignment, max_data_burst_size);
|
||||
ext_enc_mem_alignment = MAX(ext_enc_mem_alignment, max_data_burst_size);
|
||||
ext_no_enc_mem_alignment = MAX(ext_no_enc_mem_alignment, max_data_burst_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GDMA_LL_AHB_RX_BURST_NEEDS_ALIGNMENT
|
||||
if (en_data_burst && dma_chan->direction == GDMA_CHANNEL_DIRECTION_RX) {
|
||||
int_mem_alignment = MAX(int_mem_alignment, 4);
|
||||
ext_mem_alignment = MAX(ext_mem_alignment, max_data_burst_size);
|
||||
ext_enc_mem_alignment = MAX(ext_enc_mem_alignment, max_data_burst_size);
|
||||
ext_no_enc_mem_alignment = MAX(ext_no_enc_mem_alignment, max_data_burst_size);
|
||||
dma_chan->flags.size_alignment_required = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if the channel is not allowed to access external memory, set a super big (meaningless) alignment value
|
||||
// so when the upper layer checks the alignment with an external buffer, the check should fail
|
||||
if (!config->access_ext_mem) {
|
||||
ext_mem_alignment = BIT(31);
|
||||
if (config->access_ext_mem) {
|
||||
// ext_enc includes MSPI encryption/ECC constraints; ext_no_enc keeps DMA-only constraints.
|
||||
size_t mspi_alignment = esp_mspi_get_alignment(NULL);
|
||||
ext_enc_mem_alignment = MAX(ext_enc_mem_alignment, mspi_alignment);
|
||||
} else {
|
||||
// if the channel is not allowed to access external memory, set a super big (meaningless) alignment value
|
||||
// so when the upper layer checks the alignment with an external buffer, the check should fail
|
||||
ext_enc_mem_alignment = BIT(31);
|
||||
ext_no_enc_mem_alignment = BIT(31);
|
||||
}
|
||||
|
||||
gdma_pair_t *pair = dma_chan->pair;
|
||||
gdma_group_t *group = pair->group;
|
||||
gdma_hal_context_t *hal = &group->hal;
|
||||
|
||||
// always enable descriptor burst as the descriptor is always word aligned and is in the internal SRAM
|
||||
bool en_desc_burst = true;
|
||||
gdma_hal_enable_burst(hal, pair->pair_id, dma_chan->direction, en_data_burst, en_desc_burst);
|
||||
if (en_data_burst) {
|
||||
gdma_hal_set_burst_size(hal, pair->pair_id, dma_chan->direction, max_data_burst_size);
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S31 && SOC_HAS(LP_AHB_GDMA)
|
||||
// ESP32-S31 LP AHB GDMA can't burst-access encrypted external memory.
|
||||
// ESP32-S31 LP AHB GDMA can't burst-access external memory (with or without ECC/encryption).
|
||||
// Keep configuration/installation permissive for callers that only intend to
|
||||
// use internal buffers, but poison the external-memory alignment so any
|
||||
// later PSRAM use fails the caller-side validation.
|
||||
if (config->access_ext_mem && group->bus_id == SOC_GDMA_BUS_LP && esp_efuse_is_flash_encryption_enabled()) {
|
||||
ext_mem_alignment = BIT(31);
|
||||
if (config->access_ext_mem && group->bus_id == SOC_GDMA_BUS_LP) {
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
ext_enc_mem_alignment = BIT(31);
|
||||
}
|
||||
#if CONFIG_SPIRAM_ECC_ENABLE
|
||||
// ECC PSRAM is inaccessible to LP AHB regardless of flash encryption state.
|
||||
ext_enc_mem_alignment = BIT(31);
|
||||
ext_no_enc_mem_alignment = BIT(31);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
dma_chan->int_mem_alignment = int_mem_alignment;
|
||||
dma_chan->ext_mem_alignment = ext_mem_alignment;
|
||||
dma_chan->ext_enc_mem_alignment = ext_enc_mem_alignment;
|
||||
dma_chan->ext_no_enc_mem_alignment = ext_no_enc_mem_alignment;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment, size_t *ext_mem_alignment)
|
||||
esp_err_t gdma_get_channel_alignment_constraints(gdma_channel_handle_t dma_chan, size_t *int_mem_alignment,
|
||||
size_t *ext_enc_mem_alignment, size_t *ext_no_enc_mem_alignment)
|
||||
{
|
||||
if (!dma_chan) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
@@ -507,12 +522,35 @@ esp_err_t gdma_get_alignment_constraints(gdma_channel_handle_t dma_chan, size_t
|
||||
if (int_mem_alignment) {
|
||||
*int_mem_alignment = dma_chan->int_mem_alignment;
|
||||
}
|
||||
if (ext_mem_alignment) {
|
||||
*ext_mem_alignment = dma_chan->ext_mem_alignment;
|
||||
if (ext_enc_mem_alignment) {
|
||||
*ext_enc_mem_alignment = dma_chan->ext_enc_mem_alignment;
|
||||
}
|
||||
if (ext_no_enc_mem_alignment) {
|
||||
*ext_no_enc_mem_alignment = dma_chan->ext_no_enc_mem_alignment;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool gdma_is_size_alignment_required(gdma_channel_handle_t dma_chan)
|
||||
{
|
||||
return dma_chan && dma_chan->flags.size_alignment_required;
|
||||
}
|
||||
|
||||
size_t gdma_get_buffer_alignment_constraint(gdma_channel_handle_t dma_chan, const void *buffer)
|
||||
{
|
||||
if (!dma_chan || !buffer) {
|
||||
return BIT(31);
|
||||
}
|
||||
|
||||
size_t base_alignment = dma_chan->int_mem_alignment;
|
||||
if (esp_ptr_external_ram(buffer) || esp_ptr_in_drom(buffer)) {
|
||||
base_alignment = dma_chan->ext_no_enc_mem_alignment;
|
||||
}
|
||||
|
||||
size_t mspi_alignment = esp_mspi_get_alignment(buffer);
|
||||
return MAX(base_alignment, mspi_alignment);
|
||||
}
|
||||
|
||||
esp_err_t gdma_apply_strategy(gdma_channel_handle_t dma_chan, const gdma_strategy_config_t *config)
|
||||
{
|
||||
if (!dma_chan || !config) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_log.h"
|
||||
@@ -15,10 +16,8 @@
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_private/gdma_link.h"
|
||||
#include "hal/cache_hal.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "hal/cache_ll.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_macros.h"
|
||||
|
||||
@@ -76,10 +75,10 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li
|
||||
bool items_in_ext_mem = config->flags.items_in_ext_mem;
|
||||
uint32_t list_items_mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_DMA;
|
||||
if (items_in_ext_mem) {
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
if (esp_mspi_get_alignment(NULL) > 1) {
|
||||
items_in_ext_mem = false;
|
||||
list_items_mem_caps |= MALLOC_CAP_INTERNAL;
|
||||
ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when external memory encryption is enabled, using internal memory instead");
|
||||
ESP_LOGW(TAG, "DMA linked list items cannot be placed in PSRAM when MSPI strict alignment is required, using internal memory instead");
|
||||
} else {
|
||||
list_items_mem_caps |= MALLOC_CAP_SPIRAM;
|
||||
}
|
||||
@@ -90,12 +89,7 @@ esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_li
|
||||
ESP_GOTO_ON_FALSE(items, ESP_ERR_NO_MEM, err, TAG, "no mem for link list items");
|
||||
|
||||
// do memory sync if the list items are in the cache
|
||||
uint32_t data_cache_line_size = 0;
|
||||
if (items_in_ext_mem) {
|
||||
data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
|
||||
} else {
|
||||
data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
|
||||
}
|
||||
size_t data_cache_line_size = esp_cache_get_line_size_by_addr(items);
|
||||
if (data_cache_line_size) {
|
||||
// write back and then invalidate the cache, because later we will read/write the link list items by non-cached address
|
||||
ESP_GOTO_ON_ERROR(esp_cache_msync(items, ESP_ALIGN_UP(num_items * item_size, data_cache_line_size),
|
||||
@@ -190,12 +184,16 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
|
||||
}
|
||||
// alignment must be a power of 2
|
||||
ESP_RETURN_ON_FALSE_ISR((buffer_alignment & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "align err idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment);
|
||||
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment);
|
||||
size_t mspi_alignment = esp_mspi_get_alignment(buf);
|
||||
size_t effective_alignment = MAX(buffer_alignment, mspi_alignment);
|
||||
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, effective_alignment);
|
||||
if (!config->flags.bypass_buffer_align_check) {
|
||||
ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf misalign idx=%"PRIu32" align=%"PRIu32, bi, buffer_alignment);
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
// buffer size must be aligned to the encryption alignment which should be provided by the upper buffer_alignment
|
||||
ESP_RETURN_ON_FALSE_ISR((len & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf len misalign idx=%"PRIu32" len=%"PRIu32" align=%"PRIu32"", bi, len, buffer_alignment);
|
||||
ESP_RETURN_ON_FALSE_ISR(((uintptr_t)buf & (effective_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf misalign idx=%"PRIu32" align=%"PRIu32, bi, effective_alignment);
|
||||
// Length alignment:
|
||||
// - Always required under MSPI strict mode (Flash Encryption / PSRAM ECC): size must align.
|
||||
// - Also when check_size_align is set by a caller whose configured channel requires size alignment.
|
||||
if (mspi_alignment > 1 || config->flags.check_size_align) {
|
||||
ESP_RETURN_ON_FALSE_ISR((len & (effective_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buf len misalign idx=%"PRIu32" len=%"PRIu32" align=%"PRIu32"", bi, len, effective_alignment);
|
||||
}
|
||||
}
|
||||
size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length;
|
||||
@@ -217,7 +215,6 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
|
||||
if (buffer_alignment == 0) {
|
||||
buffer_alignment = 1;
|
||||
}
|
||||
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment);
|
||||
// skip zero-length buffer but scrub any stale descriptor to keep ring clean; no slot consumption
|
||||
if (len == 0 || buf == NULL) {
|
||||
lli_nc = (gdma_link_list_item_t *)(list->items_nc + begin_item_idx % list_item_capacity * item_size);
|
||||
@@ -225,6 +222,9 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
|
||||
memset(lli_nc, 0, item_size);
|
||||
continue;
|
||||
}
|
||||
size_t mspi_alignment = esp_mspi_get_alignment(buf);
|
||||
size_t effective_alignment = MAX(buffer_alignment, mspi_alignment);
|
||||
size_t max_buffer_mount_length = ESP_ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, effective_alignment);
|
||||
size_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length;
|
||||
// mount the buffer to the link list
|
||||
for (size_t i = 0; i < num_items_need; i++) {
|
||||
|
||||
@@ -90,11 +90,13 @@ struct gdma_channel_t {
|
||||
int periph_id; // Peripheral instance ID, indicates which peripheral is connected to this GDMA channel
|
||||
int intr_priority; // interrupt priority, if set to 0, the driver will use the default priority
|
||||
size_t int_mem_alignment; // alignment for memory in internal memory
|
||||
size_t ext_mem_alignment; // alignment for memory in external memory
|
||||
size_t ext_enc_mem_alignment; // alignment for external memory including MSPI encryption/ECC constraints
|
||||
size_t ext_no_enc_mem_alignment; // alignment for external memory without MSPI region-specific constraints
|
||||
esp_err_t (*del)(gdma_channel_t *channel); // channel deletion function, it's polymorphic, see `gdma_del_tx_channel` or `gdma_del_rx_channel`
|
||||
struct {
|
||||
uint32_t start_stop_by_etm: 1; // whether the channel is started/stopped by ETM
|
||||
uint32_t isr_cache_safe: 1; // whether the interrupt of this channel need to be cache safe
|
||||
uint32_t size_alignment_required: 1; // whether buffer size must meet the channel alignment
|
||||
} flags;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,16 +10,25 @@
|
||||
#include <stddef.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S31
|
||||
// ESP32-S31 LP AHB GDMA can't burst-access external PSRAM. Skip the
|
||||
// flash-encrypted PSRAM test paths because encrypted PSRAM requires burst
|
||||
// accesses aligned to the encryption block size.
|
||||
// MSPI-strict PSRAM test paths because Flash Encryption / PSRAM ECC require
|
||||
// burst accesses aligned to the MSPI block size.
|
||||
#define GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED 0
|
||||
#else
|
||||
#define GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Whether MSPI strict alignment is required (Flash Encryption and/or PSRAM ECC)
|
||||
*/
|
||||
static inline bool gdma_test_mspi_strict_alignment_required(void)
|
||||
{
|
||||
return esp_mspi_get_alignment(NULL) > 1;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include "freertos/semphr.h"
|
||||
#include "ccomp_timer.h"
|
||||
#include "esp_async_memcpy.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "gdma_test_utils.h"
|
||||
|
||||
#if SOC_GDMA_SUPPORTED
|
||||
@@ -168,7 +166,7 @@ static void test_memory_copy_blocking(async_memcpy_handle_t driver)
|
||||
for (int off = 0; off < 4; off++) {
|
||||
test_context.buffer_size = test_buffer_size[i];
|
||||
test_context.seed = i;
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
if (!gdma_test_mspi_strict_alignment_required()) {
|
||||
test_context.src_offset = off;
|
||||
test_context.dst_offset = off;
|
||||
}
|
||||
@@ -257,8 +255,8 @@ TEST_CASE("memory copy with dest address unaligned", "[async mcp]")
|
||||
};
|
||||
[[maybe_unused]] async_memcpy_handle_t driver = NULL;
|
||||
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
|
||||
if (gdma_test_mspi_strict_alignment_required()) {
|
||||
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
|
||||
}
|
||||
|
||||
#if SOC_CP_DMA_SUPPORTED
|
||||
@@ -441,8 +439,8 @@ TEST_CASE("memory copy performance 40KB: PSRAM->PSRAM", "[async mcp]")
|
||||
|
||||
#if SOC_HAS(LP_AHB_GDMA)
|
||||
#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
|
||||
if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
|
||||
TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under flash encryption");
|
||||
if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
|
||||
TEST_IGNORE_MESSAGE("Skipping LP AHB GDMA PSRAM->PSRAM under Flash Encryption / PSRAM ECC");
|
||||
} else {
|
||||
printf("Testing memcpy by LP AHB GDMA\r\n");
|
||||
TEST_ESP_OK(esp_async_memcpy_install_gdma_lp_ahb(&driver_config, &driver));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,10 +11,9 @@
|
||||
#include "unity.h"
|
||||
#include "esp_private/dw_gdma.h"
|
||||
#include "hal/dw_gdma_ll.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_private/esp_mspi_align.h"
|
||||
|
||||
TEST_CASE("DW_GDMA channel allocation", "[DW_GDMA]")
|
||||
{
|
||||
@@ -542,8 +541,8 @@ TEST_CASE("DW_GDMA M2M Test: memory set with fixed address", "[DW_GDMA]")
|
||||
size_t int_mem_alignment = 0;
|
||||
TEST_ESP_OK(esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &ext_mem_alignment));
|
||||
TEST_ESP_OK(esp_cache_get_alignment(0, &int_mem_alignment));
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
|
||||
if (esp_mspi_get_alignment(NULL) > 1) {
|
||||
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
|
||||
}
|
||||
uint8_t *src_buf = heap_caps_aligned_calloc(ext_mem_alignment, 1, 256, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
uint8_t *dst_buf = heap_caps_aligned_calloc(int_mem_alignment, 1, 256, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
#include "hal/gdma_ll.h"
|
||||
#include "hal/cache_ll.h"
|
||||
#include "hal/cache_hal.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "gdma_test_utils.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_macros.h"
|
||||
|
||||
TEST_CASE("GDMA channel allocation", "[GDMA]")
|
||||
@@ -275,7 +273,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe
|
||||
TEST_ASSERT_NOT_NULL(done_sem);
|
||||
TEST_ESP_OK(gdma_register_rx_event_callbacks(rx_chan, &rx_cbs, done_sem));
|
||||
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
if (gdma_test_mspi_strict_alignment_required()) {
|
||||
dma_link_in_ext_mem = false;
|
||||
}
|
||||
|
||||
@@ -285,7 +283,7 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe
|
||||
|
||||
size_t int_mem_alignment = 0;
|
||||
size_t ext_mem_alignment = 0;
|
||||
TEST_ESP_OK(gdma_get_alignment_constraints(tx_chan, &int_mem_alignment, &ext_mem_alignment));
|
||||
TEST_ESP_OK(gdma_get_channel_alignment_constraints(tx_chan, &int_mem_alignment, &ext_mem_alignment, NULL));
|
||||
|
||||
// allocate the source buffer from SRAM
|
||||
uint8_t *src_data = heap_caps_aligned_calloc(int_mem_alignment, 1, 128, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
@@ -352,6 +350,9 @@ static void test_gdma_m2m_transaction(gdma_channel_handle_t tx_chan, gdma_channe
|
||||
.buffer = dst_data,
|
||||
.buffer_alignment = sram_alignment, // RX buffer should be aligned to the cache line size, because we will do cache invalidate later
|
||||
.length = 256,
|
||||
.flags = {
|
||||
.check_size_align = gdma_is_size_alignment_required(rx_chan),
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL));
|
||||
|
||||
@@ -424,8 +425,8 @@ static void test_gdma_m2m_mode(bool trig_retention_backup)
|
||||
#endif // SOC_HAS(AXI_GDMA)
|
||||
|
||||
#if SOC_HAS(LP_AHB_GDMA)
|
||||
if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
|
||||
TEST_IGNORE_MESSAGE("Skip LP-AHB-GDMA GDMA M2M Mode under flash encryption");
|
||||
if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
|
||||
TEST_IGNORE_MESSAGE("Skip LP-AHB-GDMA GDMA M2M Mode under Flash Encryption / PSRAM ECC");
|
||||
} else {
|
||||
printf("Testing GDMA M2M Mode by LP-AHB GDMA%s\n", trig_retention_backup ? " with retention backup" : "");
|
||||
TEST_ESP_OK(gdma_new_lp_ahb_channel(&chan_alloc_config, &tx_chan, &rx_chan));
|
||||
@@ -500,6 +501,7 @@ static void test_gdma_m2m_desc_empty_event(gdma_channel_handle_t tx_chan, gdma_c
|
||||
.length = 64,
|
||||
.flags = {
|
||||
.mark_final = GDMA_FINAL_LINK_TO_NULL,
|
||||
.check_size_align = gdma_is_size_alignment_required(rx_chan),
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL));
|
||||
@@ -569,8 +571,18 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_
|
||||
gdma_link_list_handle_t rx_link_list = NULL;
|
||||
test_gdma_config_link_list(tx_chan, rx_chan, &tx_link_list, &rx_link_list, 0, false);
|
||||
|
||||
gdma_transfer_config_t transfer_config = {
|
||||
#if GDMA_LL_AHB_RX_BURST_NEEDS_ALIGNMENT || CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION
|
||||
.max_data_burst_size = 0,
|
||||
#else
|
||||
.max_data_burst_size = 16,
|
||||
#endif
|
||||
.access_ext_mem = false,
|
||||
};
|
||||
TEST_ESP_OK(gdma_config_transfer(rx_chan, &transfer_config));
|
||||
|
||||
size_t rx_mem_alignment = 0;
|
||||
TEST_ESP_OK(gdma_get_alignment_constraints(rx_chan, &rx_mem_alignment, NULL));
|
||||
TEST_ESP_OK(gdma_get_channel_alignment_constraints(rx_chan, &rx_mem_alignment, NULL, NULL));
|
||||
|
||||
// prepare the source data
|
||||
for (int i = 0; i < data_length; i++) {
|
||||
@@ -602,6 +614,7 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_
|
||||
rx_aligned_buf_mount_config[i].buffer = align_array.aligned_buffer[i].aligned_buffer;
|
||||
rx_aligned_buf_mount_config[i].buffer_alignment = MAX(sram_alignment, rx_mem_alignment);
|
||||
rx_aligned_buf_mount_config[i].length = align_array.aligned_buffer[i].length;
|
||||
rx_aligned_buf_mount_config[i].flags.check_size_align = gdma_is_size_alignment_required(rx_chan);
|
||||
}
|
||||
TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, rx_aligned_buf_mount_config, 3, NULL));
|
||||
|
||||
@@ -636,8 +649,8 @@ static void test_gdma_m2m_unaligned_buffer_test(uint8_t *dst_data, uint8_t *src_
|
||||
|
||||
TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]")
|
||||
{
|
||||
if (esp_efuse_is_flash_encryption_enabled()) {
|
||||
TEST_PASS_MESSAGE("Flash encryption is enabled, skip this test");
|
||||
if (gdma_test_mspi_strict_alignment_required()) {
|
||||
TEST_PASS_MESSAGE("MSPI strict alignment required (Flash Encryption / PSRAM ECC), skip this test");
|
||||
}
|
||||
|
||||
uint8_t *sbuf = heap_caps_aligned_calloc(64, 1, 10240, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
@@ -764,6 +777,7 @@ TEST_CASE("GDMA M2M Unaligned RX Buffer Test", "[GDMA][M2M]")
|
||||
.length = COPY_SIZE,
|
||||
.flags = {
|
||||
.mark_final = GDMA_FINAL_LINK_TO_NULL, // using singly list, so terminate the link here
|
||||
.check_size_align = gdma_is_size_alignment_required(rx_chan),
|
||||
}
|
||||
};
|
||||
TEST_ESP_OK(gdma_link_mount_buffers(rx_link_list, 0, &rx_buf_mount_config, 1, NULL));
|
||||
@@ -845,8 +859,8 @@ TEST_CASE("GDMA memory copy SRAM->PSRAM->SRAM", "[GDMA][M2M]")
|
||||
|
||||
#if SOC_HAS(LP_AHB_GDMA)
|
||||
#if GDMA_LL_GET(LP_AHB_PSRAM_CAPABLE)
|
||||
if (esp_efuse_is_flash_encryption_enabled() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
|
||||
TEST_IGNORE_MESSAGE("Skipping LP-AHB-GDMA SRAM->PSRAM->SRAM under flash encryption");
|
||||
if (gdma_test_mspi_strict_alignment_required() && !GDMA_TEST_LP_AHB_BURST_PSRAM_SUPPORTED) {
|
||||
TEST_IGNORE_MESSAGE("Skipping LP-AHB-GDMA SRAM->PSRAM->SRAM under Flash Encryption / PSRAM ECC");
|
||||
} else {
|
||||
printf("Testing LP-AHB-GDMA memory copy SRAM->PSRAM->SRAM\n");
|
||||
TEST_ESP_OK(gdma_new_lp_ahb_channel(&chan_alloc_config, &tx_chan, &rx_chan));
|
||||
|
||||
Reference in New Issue
Block a user