Merge branch 'fix/spi_encryption_test_and_guide' into 'master'

fix(driver_spi): add flash encryption test and guide

Closes IDF-15060

See merge request espressif/esp-idf!51261
This commit is contained in:
morris
2026-09-14 14:45:18 +08:00
32 changed files with 164 additions and 22 deletions
@@ -483,7 +483,9 @@ esp_err_t SPI_COMMON_ISR_ATTR spicommon_dma_setup_priv_buffer(spi_host_device_t
need_malloc |= (use_psram || bus_attr->cache_align_int > 1) ? (((uint32_t)buffer | len) & (alignment - 1)) : (((uint32_t)buffer) & (alignment - 1));
uint32_t align_len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
ESP_EARLY_LOGV(SPI_TAG, "SPI%d %s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", host_id + 1, is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal");
#if CONFIG_SECURE_FLASH_ENC_ENABLED || CONFIG_SPIRAM_ECC_ENABLE
ESP_RETURN_ON_FALSE_ISR(!(use_psram && (len & (alignment - 1))), ESP_ERR_INVALID_ARG, SPI_TAG, "len %d must align to alignment %d when using psram buffer with encryption or ECC", len, alignment);
#endif
if (need_malloc) {
ESP_RETURN_ON_FALSE_ISR(auto_malloc, ESP_ERR_INVALID_STATE, SPI_TAG, "%s addr&len not align to %d, or not dma_capable, suggest use 'heap_caps_malloc' or enable auto_align", is_tx ? "TX" : "RX", alignment);
uint32_t *temp = heap_caps_aligned_alloc(alignment, align_len, mem_cap);
@@ -430,12 +430,14 @@ static esp_err_t SPI_SLAVE_ATTR spi_slave_setup_priv_trans(spi_host_device_t hos
}
bool auto_malloc = (trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO);
esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, ((trans->length ? trans->length : trans->tx_length) + 7) / 8, true, true, auto_malloc, &priv_trans->tx_buffer);
size_t tx_bytes_len = ((trans->length ? trans->length : trans->tx_length) + 7) / 8;
size_t rx_bytes_len = ((trans->length ? trans->length : trans->rx_length) + 7) / 8;
esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, tx_bytes_len, true, true, auto_malloc, &priv_trans->tx_buffer);
if (ret != ESP_OK) {
spi_slave_uninstall_priv_trans(host, priv_trans);
return ret;
}
ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, ((trans->length ? trans->length : trans->rx_length) + 7) / 8, false, true, auto_malloc, &priv_trans->rx_buffer);
ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, rx_bytes_len, false, true, auto_malloc, &priv_trans->rx_buffer);
if (ret != ESP_OK) {
spi_slave_uninstall_priv_trans(host, priv_trans);
}
@@ -2151,7 +2151,7 @@ TEST_CASE("test_spi_master_auto_sleep_retention", "[spi]")
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
#define TEST_EDMA_PSRAM_TRANS_NUM 5
#define TEST_EDMA_TRANS_LEN 20000
#define TEST_EDMA_TRANS_LEN 20480
#define TEST_EDMA_BUFFER_SZ (TEST_EDMA_PSRAM_TRANS_NUM * TEST_EDMA_TRANS_LEN)
void test_spi_psram_trans(spi_device_handle_t dev_handle, void *tx, void *rx)
@@ -2163,6 +2163,9 @@ void test_spi_psram_trans(spi_device_handle_t dev_handle, void *tx, void *rx)
int trans_len = TEST_EDMA_TRANS_LEN - TEST_EDMA_PSRAM_TRANS_NUM / 2;
for (uint8_t cnt = 0; cnt < TEST_EDMA_PSRAM_TRANS_NUM; cnt ++) {
#if CONFIG_SECURE_FLASH_ENC_ENABLED
trans_len = TEST_EDMA_TRANS_LEN; // encrypted chip don't support unaligned psram transfer
#endif
trans_cfg.length = trans_len * 8;
trans_cfg.rxlength = trans_len * 8;
trans_cfg.flags = (cnt % 2) ? 0 : SPI_TRANS_DMA_USE_PSRAM;
@@ -2211,11 +2214,13 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]")
printf("\n==== %s ====\n", i ? "EDMA" : "Auto Malloc");
trans_cfg.flags = i ? SPI_TRANS_DMA_USE_PSRAM : 0;
uint32_t before = esp_get_free_heap_size();
spi_device_polling_start(dev_handle, &trans_cfg, portMAX_DELAY);
TEST_ESP_OK(spi_device_polling_start(dev_handle, &trans_cfg, portMAX_DELAY));
uint32_t after = esp_get_free_heap_size();
printf("mem_diff: %ld, trans_len: %d\n", after - before, TEST_EDMA_TRANS_LEN);
#if !CONFIG_SECURE_FLASH_ENC_ENABLED
// rx buffer still potential re-malloc from psram even if SPI_TRANS_DMA_USE_PSRAM is set
TEST_ASSERT(i ? (before - after) < 2 * TEST_EDMA_TRANS_LEN : (before - after) > 2 * TEST_EDMA_TRANS_LEN);
#endif
spi_device_polling_end(dev_handle, portMAX_DELAY);
printf("TX fail: %d, RX fail: %d\n", !!(trans_cfg.flags & SPI_TRANS_DMA_TX_FAIL), !!(trans_cfg.flags & SPI_TRANS_DMA_RX_FAIL));
if (!i) { // data should be correct if using auto malloc
@@ -57,6 +57,27 @@ def test_master_single_dev_esp32c5_rev1(case_tester) -> None: # type: ignore
case_tester.run_normal_case(case=case, reset=True)
def get_runner_psram_marks(target: str) -> tuple[pytest.MarkDecorator, ...]:
if target == 'esp32s3':
return (pytest.mark.flash_encryption_f8r8,)
return (pytest.mark.flash_encryption,)
@pytest.mark.parametrize(
'config, target',
[
pytest.param('flash_enc', target, marks=get_runner_psram_marks(target))
for target in soc_filtered_targets('SOC_PSRAM_DMA_CAPABLE == 1 and SOC_GPSPI_SUPPORTED == 1')
],
indirect=True,
)
def test_spi_master_flash_encryption(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if 'test_env' in case.attributes:
continue # If `test_env` is defined, should not run on generic runner
case_tester.run_normal_case(case=case, reset=True)
# Job for test_env `external_flash` just for esp32 only
@pytest.mark.flash_multi
@pytest.mark.parametrize(
@@ -0,0 +1,10 @@
CONFIG_SPIRAM=y
CONFIG_PARTITION_TABLE_OFFSET=0x9000
CONFIG_SECURE_FLASH_ENC_ENABLED=y
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y
CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y
CONFIG_SECURE_BOOT_ALLOW_JTAG=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y
CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y
@@ -0,0 +1 @@
CONFIG_SPIRAM_MODE_OCT=y
@@ -282,8 +282,8 @@ TEST_CASE("test slave using external ram", "[spi]")
{
custom_setup((TEST_SPI_PERIPH_NUM >= 2) ? TEST_SLAVE_HOST : 0, TEST_SPI_HOST);
uint8_t *slave_ext_tx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
uint8_t *slave_ext_rx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
uint8_t *slave_ext_tx = heap_caps_calloc(1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
uint8_t *slave_ext_rx = heap_caps_calloc(1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
uint8_t *master_tx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA);
uint8_t *master_rx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA);
@@ -300,7 +300,9 @@ TEST_CASE("test slave using external ram", "[spi]")
for (int i = 0; i < 6; i ++) {
test_fill_random_to_buffers_dualboard(7 + i, master_tx, slave_ext_tx, PSRAM_TRANS_LEN);
#if !CONFIG_SECURE_FLASH_ENC_ENABLED // encrypted chip don't support unaligned psram transfer
slave_tans.length -= i * 8;
#endif
master_tans.length = slave_tans.length;
master_tans.rxlength = slave_tans.length;
ESP_LOGI(SLAVE_TAG, "Test freq: %ld, tx: %p, rx: %p, len: %d", master_tans.override_freq_hz, slave_tans.tx_buffer, slave_tans.rx_buffer, slave_tans.length / 8);
@@ -315,7 +317,9 @@ TEST_CASE("test slave using external ram", "[spi]")
spi_master_trans_impl_gpio(buscfg, PIN_NUM_CS, 0, (uint8_t *)master_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8, false);
#endif
ESP_LOGI(SLAVE_TAG, "slave malloc: %ld", after - before);
#if !CONFIG_SECURE_FLASH_ENC_ENABLED
TEST_ASSERT(i ? (before - after) > PSRAM_TRANS_LEN : (before - after) < PSRAM_TRANS_LEN);
#endif
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY));
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
@@ -31,6 +31,24 @@ def test_slave_single_dev_esp32c5_rev1(case_tester) -> None: # type: ignore
case_tester.run_all_normal_cases(reset=True)
def get_runner_psram_marks(target: str) -> tuple[pytest.MarkDecorator, ...]:
if target == 'esp32s3':
return (pytest.mark.flash_encryption_f8r8,)
return (pytest.mark.flash_encryption,)
@pytest.mark.parametrize(
'config, target',
[
pytest.param('flash_enc', target, marks=get_runner_psram_marks(target))
for target in soc_filtered_targets('SOC_PSRAM_DMA_CAPABLE == 1 and SOC_GPSPI_SUPPORTED == 1')
],
indirect=True,
)
def test_spi_slave_flash_encryption(case_tester) -> None: # type: ignore
case_tester.run_all_normal_cases(reset=True)
@pytest.mark.generic_multi_device
@pytest.mark.parametrize('count, config', [(2, 'release'), (2, 'iram_safe')], indirect=True)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
@@ -0,0 +1,9 @@
CONFIG_PARTITION_TABLE_OFFSET=0x9000
CONFIG_SECURE_FLASH_ENC_ENABLED=y
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y
CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y
CONFIG_SECURE_BOOT_ALLOW_JTAG=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y
CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y
@@ -0,0 +1,3 @@
# don't enable s3 psram in default because s3 burger runner don't has psram
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
@@ -6,3 +6,6 @@ CONFIG_COMPILER_OPTIMIZATION_NONE=y
CONFIG_ESP_IPC_TASK_STACK_SIZE=2048
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
# ESP32-P4 PSRAM halfsleep code/data is placed in SPM, which overflows with -O0 in this config.
CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED=n
@@ -532,10 +532,10 @@ TEST_CASE("test spi slave hd segment mode, master too long", "[spi][spi_slv_hd]"
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
const int send_buf_size = 1024;
WORD_ALIGNED_ATTR uint8_t* slave_send_buf = malloc(send_buf_size * 2);
WORD_ALIGNED_ATTR uint8_t* master_send_buf = malloc(send_buf_size * 2);
WORD_ALIGNED_ATTR uint8_t* slave_recv_buf = malloc(send_buf_size * 2);
WORD_ALIGNED_ATTR uint8_t* master_recv_buf = malloc(send_buf_size * 2);
uint8_t* slave_send_buf = heap_caps_malloc(send_buf_size * 2, MALLOC_CAP_DMA);
uint8_t* master_send_buf = heap_caps_malloc(send_buf_size * 2, MALLOC_CAP_DMA);
uint8_t* slave_recv_buf = heap_caps_malloc(send_buf_size * 2, MALLOC_CAP_DMA);
uint8_t* master_recv_buf = heap_caps_malloc(send_buf_size * 2, MALLOC_CAP_DMA);
memset(slave_recv_buf, 0xcc, send_buf_size * 2);
memset(master_recv_buf, 0xcc, send_buf_size * 2);
@@ -1095,16 +1095,19 @@ static esp_err_t (*hd_get_trans_res[2])(spi_host_device_t host_id, spi_slave_cha
spi_slave_hd_get_trans_res, spi_slave_hd_get_append_trans_res
};
#define TEST_PSRAM_TRANS_LEN 1000
#define TEST_PSRAM_TRANS_LEN 1600
TEST_CASE("test slave hd edma segment and append mode", "[spi]")
{
uint8_t *mst_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_DEFAULT);
uint8_t *mst_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_DEFAULT);
uint8_t *slv_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
uint8_t *slv_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
uint8_t *slv_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
uint8_t *slv_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
spi_slave_hd_data_t *ret_trans, tx_data = {
.data = slv_tx,
.len = TEST_PSRAM_TRANS_LEN,
#if CONFIG_SECURE_FLASH_ENC_ENABLED
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, // encrypted chip has different alignment
#endif
}, rx_data = {
.data = slv_rx,
.len = TEST_PSRAM_TRANS_LEN,
@@ -1120,26 +1123,26 @@ TEST_CASE("test slave hd edma segment and append mode", "[spi]")
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
slave_hd_cfg.flags |= i ? SPI_SLAVE_HD_APPEND_MODE : 0;
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
TEST_ESP_OK(spi_slave_hd_init(TEST_SPI_HOST, &bus_cfg, &slave_hd_cfg));
same_pin_func_sel(0, TEST_SPI_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
vTaskDelay(1);
TEST_ESP_OK(hd_trans[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &tx_data, portMAX_DELAY));
TEST_ESP_OK(hd_trans[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &rx_data, portMAX_DELAY));
TEST_ESP_OK(hd_trans[i](TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &tx_data, portMAX_DELAY));
TEST_ESP_OK(hd_trans[i](TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &rx_data, portMAX_DELAY));
// tx append transaction
printf("tx %d bytes\n", TEST_PSRAM_TRANS_LEN);
essl_sspi_hd_dma_trans_seg(bus_cfg, slave_hd_cfg.spics_io_num, 0, false, mst_tx, TEST_PSRAM_TRANS_LEN, -1);
TEST_ESP_OK(hd_get_trans_res[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
TEST_ESP_OK(hd_get_trans_res[i](TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
// rx append transaction
printf("rx %d bytes\n", TEST_PSRAM_TRANS_LEN);
essl_sspi_hd_dma_trans_seg(bus_cfg, slave_hd_cfg.spics_io_num, 0, true, mst_rx, TEST_PSRAM_TRANS_LEN, -1);
TEST_ESP_OK(hd_get_trans_res[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
TEST_ESP_OK(hd_get_trans_res[i](TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
spitest_cmp_or_dump(slv_rx, mst_tx, TEST_PSRAM_TRANS_LEN);
spitest_cmp_or_dump(mst_rx, slv_tx, TEST_PSRAM_TRANS_LEN);
spi_slave_hd_deinit(TEST_SLAVE_HOST);
spi_slave_hd_deinit(TEST_SPI_HOST);
printf("test done\n");
}
free(mst_tx);
@@ -25,6 +25,24 @@ def test_slave_hd_single_dev_esp32c5_rev1(case_tester) -> None: # type: ignore
case_tester.run_all_normal_cases(reset=True, timeout=180)
def get_runner_psram_marks(target: str) -> tuple[pytest.MarkDecorator, ...]:
if target == 'esp32s3':
return (pytest.mark.flash_encryption_f8r8,)
return (pytest.mark.flash_encryption,)
@pytest.mark.parametrize(
'config, target',
[
pytest.param('flash_enc', target, marks=get_runner_psram_marks(target))
for target in soc_filtered_targets('SOC_PSRAM_DMA_CAPABLE == 1 and SOC_SPI_SUPPORT_SLAVE_HD_VER2 == 1')
],
indirect=True,
)
def test_spi_slave_hd_flash_encryption(case_tester) -> None: # type: ignore
case_tester.run_all_normal_cases(reset=True, timeout=180)
@pytest.mark.generic_multi_device
@pytest.mark.parametrize('count, config', [(2, 'release')], indirect=True)
@idf_parametrize('target', soc_filtered_targets('SOC_SPI_SUPPORT_SLAVE_HD_VER2 == 1'), indirect=['target'])
@@ -0,0 +1,9 @@
CONFIG_PARTITION_TABLE_OFFSET=0x9000
CONFIG_SECURE_FLASH_ENC_ENABLED=y
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y
CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y
CONFIG_SECURE_BOOT_ALLOW_JTAG=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y
CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y
@@ -0,0 +1,3 @@
# don't enable s3 psram in default because s3 burger runner don't has psram
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
@@ -0,0 +1,2 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -0,0 +1 @@
CONFIG_SPIRAM=y
@@ -369,6 +369,10 @@ The example code for the SPI Master driver can be found in the :example:`periphe
Note that this feature shares bandwidth (bus frequency * bus bits width) with MSPI bus, so GPSPI transfer bandwidth should be less than PSRAM bandwidth, **otherwise transmission data may be lost**. You can check the return value or :c:macro:`SPI_TRANS_DMA_RX_FAIL` and :c:macro:`SPI_TRANS_DMA_TX_FAIL` flags after the transaction is finished to check if error occurs during the transmission. If the transaction returns :c:macro:`ESP_ERR_INVALID_STATE` error, the transaction fails.
.. note::
When encryption is enabled, there are stricter alignment requirements for PSRAM buffer transfers, usually only supporting 16-byte alignment. For unaligned transfers, :c:macro:`ESP_ERR_INVALID_ARG` error will be returned. You can switch to internal memory or remove the :c:macro:`SPI_TRANS_DMA_USE_PSRAM` flag.
Transactions with Data Not Exceeding 32 Bits
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -88,6 +88,10 @@ As not every transaction requires both writing and reading data, you can choose
Note that this feature shares the MSPI bus bandwidth (bus frequency * bus width), so the transmission bandwidth of the host to this device should be less than the PSRAM bandwidth, otherwise **data may be lost**, and the ``spi_slave_transmit`` function will return the :c:macro:`ESP_ERR_INVALID_STATE` error.
.. note::
When encryption is enabled, there are stricter alignment requirements for PSRAM buffer transfers, usually only supporting 16-byte alignment. For unaligned transfers, :c:macro:`ESP_ERR_INVALID_ARG` error will be returned.
Driver Usage
------------
@@ -71,6 +71,10 @@ To send data to the master through the sending DMA channel, the application shou
Note that this feature shares the MSPI bus bandwidth (bus frequency * bus width), so the transmission bandwidth of the host to this device should be less than the PSRAM bandwidth, otherwise **data may be lost**, and then getting the transmission result will return the :c:macro:`ESP_ERR_INVALID_STATE` error.
.. note::
When encryption is enabled, there are stricter alignment requirements for PSRAM buffer transfers, usually only supporting 16-byte alignment. For unaligned transfers, :c:macro:`ESP_ERR_INVALID_ARG` error will be returned.
The application should check the result of data sending by calling :cpp:func:`spi_slave_hd_get_trans_res` with the channel set as :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. This function blocks until the transaction with the command ``Rd_DMA`` from the master successfully completes (or timeout). The ``out_trans`` argument of the function outputs the pointer of the data descriptor which is just finished, providing information about the sending.
Receiving data from the master through the receiving DMA channel is quite similar. The application calls :cpp:func:`spi_slave_hd_queue_trans` with proper data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_RX`. And the application calls the :cpp:func:`spi_slave_hd_get_trans_res` later to get the descriptor to the receiving buffer before it handles the data in the receiving buffer.
@@ -365,10 +365,14 @@ SPI 主机驱动程序的示例代码存放在 ESP-IDF 示例项目的 :example:
使用 PSRAM 的传输事务
^^^^^^^^^^^^^^^^^^^^^^
{IDF_TARGET_NAME} 支持 GPSPI Master 通过 DMA 直接传输 PSRAM 存储的数据而不用内部额外的零时拷贝,应此可以节省内存,在传输配置中添加 :c:macro:`SPI_TRANS_DMA_USE_PSRAM` 标志信号即可使用。
{IDF_TARGET_NAME} 支持 GPSPI Master 通过 DMA 直接传输 PSRAM 存储的数据而不用内部额外的零时拷贝,因此可以节省内存,在传输配置中添加 :c:macro:`SPI_TRANS_DMA_USE_PSRAM` 标志信号即可使用。
请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此 GPSPI 传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**。可通过在传输结束时检查返回值或 :c:macro:`SPI_TRANS_DMA_RX_FAIL` 和 :c:macro:`SPI_TRANS_DMA_TX_FAIL` 标志信号来判断传输是否发生了错误。若传输事务返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误,则传输事务失败。
.. note::
当开启加密功能时,使用 PSRAM Buffer 的传输有更严格的对齐要求,通常为仅支持 16 字节对齐的传输。对于不对齐的传输,会返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。可改为使用内部内存,或取消 :c:macro:`SPI_TRANS_DMA_USE_PSRAM` 标志。
传输数据小于 32 位的传输事务
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -88,6 +88,10 @@ SPI 传输事务
请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此主机对该设备的传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**,此时 ``spi_slave_transmit`` 函数将会返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。
.. note::
当开启加密功能时,使用 PSRAM Buffer 的传输有更严格的对齐要求,通常为仅支持 16 字节对齐的传输。对于不对齐的传输,会返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。
使用驱动程序
------------
@@ -71,6 +71,10 @@ SPI 从机半双工模式
请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此主机对该设备的传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**,此时获取传输结果会返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。
.. note::
当开启加密功能时,使用 PSRAM Buffer 的传输有更严格的对齐要求,通常为仅支持 16 字节对齐的传输。对于不对齐的传输,会返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。
应用程序需要检查数据发送的结果。为此,应用程序可以调用 :cpp:func:`spi_slave_hd_get_trans_res`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_TX`。该函数将阻塞程序,直到主设备发起的 Rd_DMA 命令事务成功完成或超时。函数中的参数 ``out_trans`` 将输出刚刚完成的数据描述符的指针,从而提供有关已完成的发送操作的信息。
通过 DMA 通道从主设备接收数据的操作与发送数据类似。应用程序需要使用正确的数据描述符调用 :cpp:func:`spi_slave_hd_queue_trans`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_RX`。随后,应用程序调用 :cpp:func:`spi_slave_hd_get_trans_res` 获取接收 buffer 的描述符,然后处理接收 buffer 中的数据。