diff --git a/components/esp_hw_support/test_apps/dma/main/test_gdma_crc.c b/components/esp_hw_support/test_apps/dma/main/test_gdma_crc.c index b704aa9e66..fd0e75a5fd 100644 --- a/components/esp_hw_support/test_apps/dma/main/test_gdma_crc.c +++ b/components/esp_hw_support/test_apps/dma/main/test_gdma_crc.c @@ -11,6 +11,7 @@ #include "freertos/semphr.h" #include "unity.h" #include "esp_heap_caps.h" +#include "esp_memory_utils.h" #include "esp_private/gdma.h" #include "hal/dma_types.h" #include "soc/soc_caps.h" @@ -62,6 +63,8 @@ static void test_gdma_crc_calculation(gdma_channel_handle_t tx_chan, int test_nu uint32_t crc_result = 0; const char *test_input_string = "Share::Connect::Innovate"; size_t input_data_size = strlen(test_input_string); + // this test case also test the GDMA can fetch data from MSPI Flash + TEST_ASSERT_TRUE(esp_ptr_in_drom(test_input_string)); printf("Calculate CRC value for string: \"%s\"\r\n", test_input_string); gdma_trigger_t m2m_trigger = GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_M2M, 0); @@ -71,27 +74,22 @@ static void test_gdma_crc_calculation(gdma_channel_handle_t tx_chan, int test_nu m2m_trigger.instance_id = __builtin_ctz(free_m2m_id_mask); TEST_ESP_OK(gdma_connect(tx_chan, m2m_trigger)); - // allocate the source and destination buffer from SRAM - // |--------------------------------------------------| - // | 128 bytes DMA descriptor | 128 bytes data buffer | - // |--------------------------------------------------| - size_t sram_alignment = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); - uint8_t *src_buf = heap_caps_aligned_calloc(sram_alignment, 1, 256, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - TEST_ASSERT_NOT_NULL(src_buf); - dma_descriptor_align8_t *tx_descs = (dma_descriptor_align8_t *) src_buf; - uint8_t *src_data = src_buf + 64; - memcpy(src_data, test_input_string, input_data_size); + size_t sram_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA); + size_t alignment = MAX(sram_cache_line_size, 8); + dma_descriptor_align8_t *tx_descs = heap_caps_aligned_calloc(alignment, 1, sizeof(dma_descriptor_align8_t), + MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_NULL(tx_descs); - tx_descs->buffer = src_data; - tx_descs->dw0.size = 256 - 64; + tx_descs->buffer = (void *)test_input_string; + tx_descs->dw0.size = input_data_size + 1; // +1 for '\0' tx_descs->dw0.length = input_data_size; tx_descs->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; tx_descs->dw0.suc_eof = 1; tx_descs->next = NULL; - if (sram_alignment) { + if (sram_cache_line_size) { // do write-back for the buffer because it's in the cache - TEST_ESP_OK(esp_cache_msync((void *)src_buf, 256, ESP_CACHE_MSYNC_FLAG_DIR_C2M)); + TEST_ESP_OK(esp_cache_msync((void *)tx_descs, sizeof(dma_descriptor_align8_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED)); } for (int i = 0; i < test_num_crc_algorithm; i++) { @@ -111,7 +109,7 @@ static void test_gdma_crc_calculation(gdma_channel_handle_t tx_chan, int test_nu TEST_ASSERT_EQUAL(crc_test_cases[i].expected_result, crc_result); } - free(src_buf); + free(tx_descs); } TEST_CASE("GDMA CRC Calculation", "[GDMA][CRC]") diff --git a/components/hal/esp32p4/include/hal/ahb_dma_ll.h b/components/hal/esp32p4/include/hal/ahb_dma_ll.h index b7a4667ed1..acf76923e0 100644 --- a/components/hal/esp32p4/include/hal/ahb_dma_ll.h +++ b/components/hal/esp32p4/include/hal/ahb_dma_ll.h @@ -57,6 +57,18 @@ static inline void ahb_dma_ll_reset_fsm(ahb_dma_dev_t *dev) dev->misc_conf.ahbm_rst_inter = 0; } +/** + * @brief Preset valid memory range for AHB-DMA + * + * @param dev DMA register base address + */ +static inline void ahb_dma_ll_set_default_memory_range(ahb_dma_dev_t *dev) +{ + // AHB-DMA can access L2MEM, L2ROM, MSPI Flash, MSPI PSRAM + dev->intr_mem_start_addr.val = 0x40000000; + dev->intr_mem_end_addr.val = 0x4FFC0000; +} + ///////////////////////////////////// RX ///////////////////////////////////////// /** * @brief Get DMA RX channel interrupt status word diff --git a/components/hal/esp32p4/include/hal/axi_dma_ll.h b/components/hal/esp32p4/include/hal/axi_dma_ll.h index 171b204527..5cd6808872 100644 --- a/components/hal/esp32p4/include/hal/axi_dma_ll.h +++ b/components/hal/esp32p4/include/hal/axi_dma_ll.h @@ -59,6 +59,20 @@ static inline void axi_dma_ll_reset_fsm(axi_dma_dev_t *dev) dev->misc_conf.axim_rst_wr_inter = 0; } +/** + * @brief Preset valid memory range for AXI-DMA + * + * @param dev DMA register base address + */ +static inline void axi_dma_ll_set_default_memory_range(axi_dma_dev_t *dev) +{ + // AXI-DMA can access L2MEM, L2ROM, MSPI Flash, MSPI PSRAM + dev->intr_mem_start_addr.val = 0x4FC00000; + dev->intr_mem_end_addr.val = 0x4FFC0000; + dev->extr_mem_start_addr.val = 0x40000000; + dev->extr_mem_end_addr.val = 0x4C000000; +} + ///////////////////////////////////// RX ///////////////////////////////////////// /** * @brief Get DMA RX channel interrupt status word diff --git a/components/hal/gdma_hal_ahb_v2.c b/components/hal/gdma_hal_ahb_v2.c index 8d1892edab..b3178ed09f 100644 --- a/components/hal/gdma_hal_ahb_v2.c +++ b/components/hal/gdma_hal_ahb_v2.c @@ -246,4 +246,5 @@ void gdma_ahb_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config) #if SOC_GDMA_SUPPORT_ETM hal->enable_etm_task = gdma_ahb_hal_enable_etm_task; #endif // SOC_GDMA_SUPPORT_ETM + ahb_dma_ll_set_default_memory_range(hal->ahb_dma_dev); } diff --git a/components/hal/gdma_hal_axi.c b/components/hal/gdma_hal_axi.c index 5e6cdbc4ee..83a359f989 100644 --- a/components/hal/gdma_hal_axi.c +++ b/components/hal/gdma_hal_axi.c @@ -246,4 +246,5 @@ void gdma_axi_hal_init(gdma_hal_context_t *hal, const gdma_hal_config_t *config) #if SOC_GDMA_SUPPORT_ETM hal->enable_etm_task = gdma_axi_hal_enable_etm_task; #endif // SOC_GDMA_SUPPORT_ETM + axi_dma_ll_set_default_memory_range(hal->axi_dma_dev); } diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index ce2cc8d3bc..0f7765acc0 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -451,6 +451,10 @@ config SOC_DS_KEY_CHECK_MAX_WAIT_US int default 1100 +config SOC_DMA_CAN_ACCESS_MSPI_MEM + bool + default y + config SOC_AHB_GDMA_VERSION int default 2 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 9a36ea3946..929a5dc9f3 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -182,6 +182,9 @@ See TRM DS chapter for more details */ #define SOC_DS_KEY_CHECK_MAX_WAIT_US (1100) +/*-------------------------- DMA Common CAPS ----------------------------------------*/ +#define SOC_DMA_CAN_ACCESS_MSPI_MEM 1 /*!< DMA can access MSPI memory (e.g. Flash, PSRAM) */ + /*-------------------------- GDMA CAPS -------------------------------------*/ #define SOC_AHB_GDMA_VERSION 2 #define SOC_GDMA_SUPPORT_CRC 1 diff --git a/docs/en/api-reference/peripherals/rmt.rst b/docs/en/api-reference/peripherals/rmt.rst index b49a209932..5ecea35c0c 100644 --- a/docs/en/api-reference/peripherals/rmt.rst +++ b/docs/en/api-reference/peripherals/rmt.rst @@ -93,7 +93,7 @@ To install an RMT TX channel, there is a configuration structure that needs to b - :cpp:member:`rmt_tx_channel_config_t::io_loop_back` enables both input and output capabilities on the channel's assigned GPIO. Thus, by binding a TX and RX channel to the same GPIO, loopback can be achieved. - :cpp:member:`rmt_tx_channel_config_t::io_od_mode` configures the channel's assigned GPIO as open-drain. When combined with :cpp:member:`rmt_tx_channel_config_t::io_loop_back`, a bi-directional bus (e.g., 1-wire) can be achieved. - :cpp:member:`rmt_tx_channel_config_t::intr_priority` Set the priority of the interrupt. If set to ``0`` , then the driver will use a interrupt with low or medium priority (priority level may be one of 1,2 or 3), otherwise use the priority indicated by :cpp:member:`rmt_tx_channel_config_t::intr_priority`. Please use the number form (1,2,3) , not the bitmask form ((1<<1),(1<<2),(1<<3)). Please pay attention that once the interrupt priority is set, it cannot be changed until :cpp:func:`rmt_del_channel` is called. -- :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` enables the backup of the RMT registers before entering sleep mode. This option implies an balance between power consumption and memory usage. If the power consumption is not a concern, you can disable this option to save memory. But if you want to save power, you should enable this option to save the RMT registers before entering sleep mode, and restore them after waking up. This feature depends on specific hardware module, if you enable this flag on an unsupported chip, you will get an error message like ``register back up is not supported``. +- :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` enables the backup of the RMT registers before entering sleep mode. This option implies a tradeoff between power consumption and memory consumption. If the power consumption is not a concern, you can disable this option to save memory. But if you want to save power, you should enable this option to save the RMT registers before entering sleep mode, and restore them after waking up. This feature depends on specific hardware module, if you enable this flag on an unsupported chip, you will get an error message like ``register back up is not supported``. Once the :cpp:type:`rmt_tx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_tx_channel` to allocate and initialize a TX channel. This function returns an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function returns :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g., DMA backend) is not supported by the hardware, it returns :c:macro:`ESP_ERR_NOT_SUPPORTED` error. diff --git a/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst b/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst index dcd5198928..b20dc9bb82 100644 --- a/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst +++ b/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst @@ -77,6 +77,13 @@ Non-IRAM-Safe Interrupt Handlers If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt handler will not get executed when the caches are disabled. Once the caches are restored, the non-IRAM-safe interrupts will be re-enabled. After this moment, the interrupt handler will run normally again. This means that as long as caches are disabled, users will not see the corresponding hardware event happening. +.. only:: SOC_DMA_CAN_ACCESS_MSPI_MEM + + When DMA Read Data from Flash + ----------------------------- + + When DMA is reading data from Flash, erase/write operations from SPI1 take higher priority in hardware, resulting in unpredictable data read by DMA. It is recommended to stop DMA access to Flash before erasing or writing to it. If DMA cannot be stopped (for example, the LCD needs to continuously refresh image data stored in Flash), it is advisable to copy such data to PSRAM or internal SRAM. + .. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND diff --git a/docs/zh_CN/api-reference/peripherals/rmt.rst b/docs/zh_CN/api-reference/peripherals/rmt.rst index 27de2a544e..dff8002360 100644 --- a/docs/zh_CN/api-reference/peripherals/rmt.rst +++ b/docs/zh_CN/api-reference/peripherals/rmt.rst @@ -93,7 +93,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式, - :cpp:member:`rmt_tx_channel_config_t::io_loop_back` 启用通道所分配的 GPIO 上的输入和输出功能,将发送通道和接收通道绑定到同一个 GPIO 上,从而实现回环功能。 - :cpp:member:`rmt_tx_channel_config_t::io_od_mode` 配置通道分配的 GPIO 为开漏模式 (open-drain)。当与 :cpp:member:`rmt_tx_channel_config_t::io_loop_back` 结合使用时,可以实现双向总线,如 1-wire。 - :cpp:member:`rmt_tx_channel_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0`` ,驱动将会使用一个中低优先级的中断(优先级可能为 1,2 或 3),否则会使用 :cpp:member:`rmt_tx_channel_config_t::intr_priority` 指定的优先级。请使用优先级序号 (1, 2, 3),而不是 bitmask 的形式((1<<1),(1<<2),(1<<3))。请注意,中断优先级一旦设置,在 :cpp:func:`rmt_del_channel` 被调用之前不可再次修改。 -- :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` 用于使能在进入睡眠模式前备份 RMT 寄存器。这个选项需要用户在功耗和内存使用之间取得平衡。如果功耗不是一个问题,可以禁用这个选项来节省内存。但如果想要节省功耗,应该使能这个选项,在进入睡眠模式前保存 RMT 寄存器,并在唤醒后恢复它们。这个功能依赖于特定的硬件模块,如果你在不支持的芯片上启用这个标志,你会得到一个错误信息,如 ``register back up is not supported``。 +- :cpp:member:`rmt_tx_channel_config_t::backup_before_sleep` 用于使能在进入睡眠模式前备份 RMT 寄存器。这个选项需要用户在功耗和内存使用之间做取舍。如果功耗不是一个问题,可以禁用这个选项来节省内存。但如果想要节省功耗,应该使能这个选项,在进入睡眠模式前保存 RMT 寄存器,并在唤醒后恢复它们。这个功能依赖于特定的硬件模块,如果你在不支持的芯片上启用这个标志,你会得到一个错误信息,如 ``register back up is not supported``。 将必要参数填充到结构体 :cpp:type:`rmt_tx_channel_config_t` 后,可以调用 :cpp:func:`rmt_new_tx_channel` 来分配和初始化 TX 通道。如果函数运行正确,会返回 RMT 通道句柄;如果 RMT 资源池内缺少空闲通道,会返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误;如果硬件不支持 DMA 后端等部分功能,则返回 :c:macro:`ESP_ERR_NOT_SUPPORTED` 错误。 diff --git a/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst b/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst index a222654aa8..7e45a61373 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst @@ -77,6 +77,13 @@ IRAM 安全中断处理程序 如果在注册时没有设置 ``ESP_INTR_FLAG_IRAM`` 标志,当禁用 cache 时,将不会执行中断处理程序。一旦 cache 恢复,非 IRAM 安全的中断将重新启用,中断处理程序随即再次正常运行。这意味着,只要禁用了 cache,就不会发生相应的硬件事件。 +.. only:: SOC_DMA_CAN_ACCESS_MSPI_MEM + + 当 DMA 也可以访问 Flash 中的数据时 + ---------------------------------- + + 当 DMA 正在从 Flash 中读取数据时,来自 SPI1 的擦/写操作优先级会更高,导致 DMA 读到错误的数据。建议在擦写 Flash 之前先停止 DMA 对 Flash 的访问。如果 DMA 不可以停止,比如 LCD 需要持续刷新保存在 Flash 中的图像数据,建议将此类数据拷贝到 PSRAM 或者内部的 SRAM 中。 + .. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND