diff --git a/components/esp_driver_bitscrambler/CMakeLists.txt b/components/esp_driver_bitscrambler/CMakeLists.txt index b7b7ef6531..d1c7fa5a6e 100644 --- a/components/esp_driver_bitscrambler/CMakeLists.txt +++ b/components/esp_driver_bitscrambler/CMakeLists.txt @@ -1,29 +1,14 @@ idf_build_get_property(target IDF_TARGET) +if(${target} STREQUAL "linux") + return() # This component is not supported by the POSIX/Linux simulator +endif() set(srcs) -set(include_dirs) -set(priv_requires) - -set(my_priv_requires "soc" "hal" "esp_hw_support" "esp_mm") if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED) - list(APPEND srcs "bitscrambler.c" "bitscrambler_loopback.c") - list(APPEND include_dirs "include") + list(APPEND srcs "src/bitscrambler.c" "src/bitscrambler_loopback.c" "src/bitscrambler_${target}.c") endif() -# Note that (according to the docs) "The values of REQUIRES and PRIV_REQUIRES -# should not depend on any configuration choices (CONFIG_xxx macros)." We work -# around that by setting the actual priv_requires value in the target checks, -# rather than make it depend on CONFIG_SOC_BITSCRAMBLER_SUPPORTED. - -if(target STREQUAL "esp32p4") - list(APPEND srcs "bitscrambler_esp32p4.c") - set(priv_requires ${my_priv_requires}) -endif() - - idf_component_register(SRCS ${srcs} - PRIV_REQUIRES ${priv_requires} - INCLUDE_DIRS ${include_dirs} - PRIV_INCLUDE_DIRS "priv_include" - ) + PRIV_REQUIRES "esp_mm" + INCLUDE_DIRS "include") diff --git a/components/esp_driver_bitscrambler/bitscrambler_loopback_private.h b/components/esp_driver_bitscrambler/bitscrambler_loopback_private.h deleted file mode 100644 index ca0f6502a9..0000000000 --- a/components/esp_driver_bitscrambler/bitscrambler_loopback_private.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -//This file contains private functions for interop between bitscrambler.c -//and bitscrambler_loopback.c. - -#pragma once - -#include "bitscrambler_private.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void bitscrambler_loopback_free(bitscrambler_handle_t bs); - -#ifdef __cplusplus -} -#endif diff --git a/components/esp_driver_bitscrambler/bitscrambler_private.h b/components/esp_driver_bitscrambler/bitscrambler_private.h deleted file mode 100644 index 401e5346a9..0000000000 --- a/components/esp_driver_bitscrambler/bitscrambler_private.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -//This file contains private functions for interop between bitscrambler.c -//and bitscrambler_loopback.c. -#pragma once - -#include -#include "soc/bitscrambler_peri_select.h" -#include "hal/bitscrambler_ll.h" - -typedef struct bitscrambler_t bitscrambler_t; - -struct bitscrambler_t { - bitscrambler_config_t cfg; - bitscrambler_dev_t *hw; - bool loopback; //true if this is a loopback bitscrambler, i.e. the RX - //channel is also claimed -}; - -esp_err_t bitscrambler_init_loopback(bitscrambler_handle_t handle, const bitscrambler_config_t *config); diff --git a/components/esp_driver_bitscrambler/include/driver/bitscrambler.h b/components/esp_driver_bitscrambler/include/driver/bitscrambler.h index de8caac73c..4c13629e2b 100644 --- a/components/esp_driver_bitscrambler/include/driver/bitscrambler.h +++ b/components/esp_driver_bitscrambler/include/driver/bitscrambler.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,15 +7,36 @@ #include #include "esp_err.h" +#include "soc/soc_caps.h" #include "hal/bitscrambler_types.h" +#if SOC_BITSCRAMBLER_SUPPORTED #include "soc/bitscrambler_peri_select.h" +#endif #ifdef __cplusplus extern "C" { #endif +/** + * @brief Declare a BitScrambler binary program. + * + * This macro declares an external reference to a BitScrambler binary program. + * The binary program is expected to be linked into the binary with a specific + * naming convention. + * + * @param VAR The variable name to declare. + * @param NAME The name of the binary program. + */ #define BITSCRAMBLER_PROGRAM(VAR, NAME) extern const uint8_t VAR[] asm("_binary_bitscrambler_program_" NAME "_start") +/** + * @brief Handle for the bitscrambler instance. + * + * This typedef defines a handle for a bitscrambler instance, which is used to + * manage and interact with the bitscrambler. The handle is a pointer to an + * opaque structure, meaning that the internal details of the structure are + * hidden from the user. + */ typedef struct bitscrambler_t *bitscrambler_handle_t; /** diff --git a/components/esp_driver_bitscrambler/include/esp_private/bitscrambler.h b/components/esp_driver_bitscrambler/include/esp_private/bitscrambler.h new file mode 100644 index 0000000000..7ed4498dde --- /dev/null +++ b/components/esp_driver_bitscrambler/include/esp_private/bitscrambler.h @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "driver/bitscrambler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef esp_err_t (*bitscrambler_extra_clean_up_func_t)(bitscrambler_handle_t bs, void* user_ctx); + +esp_err_t bitscrambler_register_extra_clean_up(bitscrambler_handle_t handle, bitscrambler_extra_clean_up_func_t clean_up, void* user_ctx); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_bitscrambler/bitscrambler.c b/components/esp_driver_bitscrambler/src/bitscrambler.c similarity index 94% rename from components/esp_driver_bitscrambler/bitscrambler.c rename to components/esp_driver_bitscrambler/src/bitscrambler.c index e9ff25320f..8e9f17945c 100644 --- a/components/esp_driver_bitscrambler/bitscrambler.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,8 +8,6 @@ #include "esp_log.h" #include "driver/bitscrambler.h" #include "bitscrambler_private.h" -#include "bitscrambler_loopback_private.h" -#include "soc/soc.h" #include "hal/bitscrambler_ll.h" #include "esp_private/periph_ctrl.h" @@ -42,7 +40,7 @@ typedef struct { uint8_t unused; } bitscrambler_program_hdr_t; -#define INST_LEN_WORDS 9 //length of one instruction in 32-bit words as defined by HW +#define INST_LEN_WORDS BITSCRAMBLER_LL_INST_LEN_WORDS // For now, hardware only has one TX and on RX unit. Need to make this more flexible if we get // non-specific and/or more channels. @@ -251,18 +249,33 @@ esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t return ESP_OK; } +esp_err_t bitscrambler_register_extra_clean_up(bitscrambler_handle_t handle, bitscrambler_extra_clean_up_func_t clean_up, void* user_ctx) +{ + if (!handle) { + return ESP_ERR_INVALID_ARG; + } + handle->extra_clean_up = clean_up; + handle->clean_up_user_ctx = user_ctx; + return ESP_OK; +} + void bitscrambler_free(bitscrambler_handle_t handle) { + if (!handle) { + return; + } disable_clocks(handle); if (handle->loopback) { atomic_flag_clear(&tx_in_use); atomic_flag_clear(&rx_in_use); - bitscrambler_loopback_free(handle); } else if (handle->cfg.dir == BITSCRAMBLER_DIR_TX) { atomic_flag_clear(&tx_in_use); } else if (handle->cfg.dir == BITSCRAMBLER_DIR_RX) { atomic_flag_clear(&rx_in_use); } + if (handle->extra_clean_up) { + handle->extra_clean_up(handle, handle->clean_up_user_ctx); + } free(handle); } diff --git a/components/esp_driver_bitscrambler/bitscrambler_esp32p4.c b/components/esp_driver_bitscrambler/src/bitscrambler_esp32p4.c similarity index 100% rename from components/esp_driver_bitscrambler/bitscrambler_esp32p4.c rename to components/esp_driver_bitscrambler/src/bitscrambler_esp32p4.c diff --git a/components/esp_driver_bitscrambler/bitscrambler_loopback.c b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c similarity index 69% rename from components/esp_driver_bitscrambler/bitscrambler_loopback.c rename to components/esp_driver_bitscrambler/src/bitscrambler_loopback.c index 800406997f..b4f2c19938 100644 --- a/components/esp_driver_bitscrambler/bitscrambler_loopback.c +++ b/components/esp_driver_bitscrambler/src/bitscrambler_loopback.c @@ -1,40 +1,43 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include -#include "driver/bitscrambler.h" -#include "bitscrambler_private.h" -#include "bitscrambler_loopback_private.h" -#include "esp_private/gdma.h" -#include "hal/dma_types.h" -#include "hal/cache_ll.h" -#include "hal/gdma_ll.h" -#include "bitscrambler_soc_specific.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" +#include "driver/bitscrambler.h" +#include "esp_private/gdma.h" +#include "esp_private/gdma_link.h" +#include "esp_private/bitscrambler.h" +#include "hal/dma_types.h" +#include "bitscrambler_private.h" +#include "bitscrambler_soc_specific.h" #include "esp_err.h" #include "esp_check.h" -#include "soc/ahb_dma_struct.h" #include "esp_heap_caps.h" #include "esp_cache.h" #include "esp_dma_utils.h" const static char *TAG = "bs_loop"; -//Note: given that the first member is a bitscrambler_t, this can be safely passed to -//any of the non-loopback bitscrambler functions. typedef struct { bitscrambler_t bs; - dma_descriptor_t *tx_desc_link; // descriptor link list, the length of the link is determined by the copy buffer size - dma_descriptor_t *rx_desc_link; // descriptor link list, the length of the link is determined by the copy buffer size gdma_channel_handle_t tx_channel; // GDMA TX channel handle gdma_channel_handle_t rx_channel; // GDMA RX channel handle + gdma_link_list_handle_t tx_link_list; // GDMA TX link list handle, the length of the link is determined by the copy buffer size + gdma_link_list_handle_t rx_link_list; // GDMA RX link list handle, the length of the link is determined by the copy buffer size SemaphoreHandle_t sema_done; size_t max_transfer_sz_bytes; } bitscrambler_loopback_t; +/// @brief make sure bs is indeed the first member of bitscrambler_loopback_t so we can cast it to a bitscrambler_t and safely passed to +// any of the non-loopback bitscrambler functions. +_Static_assert(offsetof(bitscrambler_loopback_t, bs) == 0, "bs needs to be 1st member of bitscrambler_loopback_t"); + +static void bitscrambler_loopback_free(bitscrambler_loopback_t *bsl); +static esp_err_t bitscrambler_loopback_cleanup(bitscrambler_handle_t bs, void* user_ctx); + static esp_err_t new_dma_channel(const gdma_channel_alloc_config_t *cfg, gdma_channel_handle_t *handle, int bus) { esp_err_t ret = ESP_OK; @@ -63,8 +66,6 @@ static IRAM_ATTR bool trans_done_cb(gdma_channel_handle_t dma_chan, gdma_event_d esp_err_t bitscrambler_loopback_create(bitscrambler_handle_t *handle, int attach_to, size_t max_transfer_sz_bytes) { - ///make sure bs is indeed the first member of bitscrambler_loopback_t so we can cast it to a bitscrambler_t - _Static_assert(offsetof(bitscrambler_loopback_t, bs) == 0, "bs needs to be 1st member of bitscrambler_loopback_t"); if (!handle) { return ESP_ERR_INVALID_ARG; } @@ -85,6 +86,9 @@ esp_err_t bitscrambler_loopback_create(bitscrambler_handle_t *handle, int attach }; ESP_GOTO_ON_ERROR(bitscrambler_init_loopback(&bs->bs, &cfg), err, TAG, "failed bitscrambler init for loopback"); + // register extra cleanup function to free loopback resources + bitscrambler_register_extra_clean_up(&bs->bs, bitscrambler_loopback_cleanup, bs); + bs->sema_done = xSemaphoreCreateBinary(); if (!bs->sema_done) { goto err; @@ -93,19 +97,21 @@ esp_err_t bitscrambler_loopback_create(bitscrambler_handle_t *handle, int attach bs->max_transfer_sz_bytes = max_transfer_sz_bytes; int desc_ct = (max_transfer_sz_bytes + DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED - 1) / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; int bus = g_bitscrambler_periph_desc[attach_to].bus; - uint32_t caps = (bus == SOC_GDMA_BUS_AXI) ? MALLOC_CAP_DMA_DESC_AXI : MALLOC_CAP_DMA_DESC_AHB; size_t align = (bus == SOC_GDMA_BUS_AXI) ? 8 : 4; - bs->rx_desc_link = heap_caps_aligned_calloc(align, desc_ct, sizeof(dma_descriptor_t), caps); - bs->tx_desc_link = heap_caps_aligned_calloc(align, desc_ct, sizeof(dma_descriptor_t), caps); - if (!bs->rx_desc_link || !bs->tx_desc_link) { - ret = ESP_ERR_NO_MEM; - goto err; - } + + // create DMA link list for TX and RX + gdma_link_list_config_t dma_link_cfg = { + .buffer_alignment = 4, + .item_alignment = align, + .num_items = desc_ct, + }; + ESP_GOTO_ON_ERROR(gdma_new_link_list(&dma_link_cfg, &bs->tx_link_list), err, TAG, "failed to create TX link list"); + ESP_GOTO_ON_ERROR(gdma_new_link_list(&dma_link_cfg, &bs->rx_link_list), err, TAG, "failed to create RX link list"); // create TX channel and RX channel, they should reside in the same DMA pair gdma_channel_alloc_config_t tx_alloc_config = { - .flags.reserve_sibling = 1, .direction = GDMA_CHANNEL_DIRECTION_TX, + .flags.reserve_sibling = 1, }; ESP_GOTO_ON_ERROR(new_dma_channel(&tx_alloc_config, &bs->tx_channel, bus), err, TAG, "failed to create GDMA TX channel"); gdma_channel_alloc_config_t rx_alloc_config = { @@ -132,18 +138,13 @@ esp_err_t bitscrambler_loopback_create(bitscrambler_handle_t *handle, int attach return ESP_OK; err: - bitscrambler_loopback_free(&bs->bs); + bitscrambler_loopback_free(bs); free(bs); return ret; } -//note this is never called directly; bitscrambler_free calls this to clear -//the loopback-specific things of a loopback bitscrambler. -//bitscrambler_loopback_create also calls this in an error situation, so -//we should only delete not-NULL members. -void bitscrambler_loopback_free(bitscrambler_handle_t bs) +static void bitscrambler_loopback_free(bitscrambler_loopback_t *bsl) { - bitscrambler_loopback_t *bsl = (bitscrambler_loopback_t*)bs; if (bsl->rx_channel) { gdma_disconnect(bsl->rx_channel); gdma_del_channel(bsl->rx_channel); @@ -152,36 +153,22 @@ void bitscrambler_loopback_free(bitscrambler_handle_t bs) gdma_disconnect(bsl->tx_channel); gdma_del_channel(bsl->tx_channel); } + if (bsl->tx_link_list) { + gdma_del_link_list(bsl->tx_link_list); + } + if (bsl->rx_link_list) { + gdma_del_link_list(bsl->rx_link_list); + } if (bsl->sema_done) { vSemaphoreDelete(bsl->sema_done); } - free(bsl->rx_desc_link); - free(bsl->tx_desc_link); } -static int fill_dma_links(dma_descriptor_t *link, void *buffer, size_t len_bytes, int set_eof) +static esp_err_t bitscrambler_loopback_cleanup(bitscrambler_handle_t bs, void* user_ctx) { - uint8_t *buffer_p = (uint8_t*)buffer; - int link_ct = 0; - for (int p = 0; p < len_bytes; p += DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) { - int seg_len = len_bytes - p; - if (seg_len > DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) { - seg_len = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED; - } - link[link_ct].dw0.size = seg_len; - link[link_ct].dw0.length = seg_len; - link[link_ct].dw0.err_eof = 0; - link[link_ct].dw0.suc_eof = 0; - link[link_ct].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; - link[link_ct].buffer = &buffer_p[p]; - link[link_ct].next = &link[link_ct + 1]; - link_ct++; - } - link[link_ct - 1].next = NULL; //fix last entry to end transaction - if (set_eof) { - link[link_ct - 1].dw0.suc_eof = 1; - } - return link_ct; + bitscrambler_loopback_t *bsl = (bitscrambler_loopback_t*)user_ctx; + bitscrambler_loopback_free(bsl); + return ESP_OK; } /* @@ -232,18 +219,33 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s gdma_reset(bsl->tx_channel); bitscrambler_reset(bs); - int link_ct_in = fill_dma_links(bsl->tx_desc_link, buffer_in, length_bytes_in, 1); - int link_ct_out = fill_dma_links(bsl->rx_desc_link, buffer_out, length_bytes_out, 0); + // mount in and out buffer to the DMA link list + gdma_buffer_mount_config_t in_buf_mount_config = { + .buffer = buffer_in, + .length = length_bytes_in, + .flags = { + .mark_eof = true, + .mark_final = true, + } + }; + gdma_link_mount_buffers(bsl->tx_link_list, 0, &in_buf_mount_config, 1, NULL); + gdma_buffer_mount_config_t out_buf_mount_config = { + .buffer = buffer_out, + .length = length_bytes_out, + .flags = { + .mark_eof = false, + .mark_final = true, + } + }; + gdma_link_mount_buffers(bsl->rx_link_list, 0, &out_buf_mount_config, 1, NULL); //Note: we add the ESP_CACHE_MSYNC_FLAG_UNALIGNED flag for now as otherwise esp_cache_msync will complain about //the size not being aligned... we miss out on a check to see if the address is aligned this way. This needs to //be improved, but potentially needs a fix in esp_cache_msync not to check the size. - - esp_cache_msync(bsl->rx_desc_link, link_ct_out * sizeof(dma_descriptor_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - esp_cache_msync(bsl->tx_desc_link, link_ct_in * sizeof(dma_descriptor_t), ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); esp_cache_msync(buffer_in, length_bytes_in, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); - gdma_start(bsl->rx_channel, (intptr_t)bsl->rx_desc_link); - gdma_start(bsl->tx_channel, (intptr_t)bsl->tx_desc_link); + + gdma_start(bsl->rx_channel, gdma_link_get_head_addr(bsl->rx_link_list)); + gdma_start(bsl->tx_channel, gdma_link_get_head_addr(bsl->tx_link_list)); bitscrambler_start(bs); int timeout_ms = (length_bytes_out + length_bytes_in) / (BS_MIN_BYTES_PER_SEC / 1000); @@ -259,14 +261,7 @@ esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, s esp_cache_msync(buffer_out, length_bytes_out, ESP_CACHE_MSYNC_FLAG_DIR_M2C); if (bytes_written) { - size_t l = 0; - for (int i = 0; i < link_ct_out; i++) { - l += bsl->rx_desc_link[i].dw0.length; - if (bsl->rx_desc_link[i].dw0.suc_eof) { - break; - } - } - *bytes_written = l; + *bytes_written = gdma_link_count_buffer_size_till_eof(bsl->rx_link_list, 0); } return ret; diff --git a/components/esp_driver_bitscrambler/src/bitscrambler_private.h b/components/esp_driver_bitscrambler/src/bitscrambler_private.h new file mode 100644 index 0000000000..ba32697498 --- /dev/null +++ b/components/esp_driver_bitscrambler/src/bitscrambler_private.h @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "hal/bitscrambler_ll.h" +#include "esp_private/bitscrambler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct bitscrambler_t bitscrambler_t; + +struct bitscrambler_t { + bitscrambler_config_t cfg; + bitscrambler_dev_t *hw; + bitscrambler_extra_clean_up_func_t extra_clean_up; // Optional extra clean-up function + void* clean_up_user_ctx; // User context for extra clean-up function + bool loopback; //true if this is a loopback bitscrambler, i.e. the RX channel is also claimed +}; + +esp_err_t bitscrambler_init_loopback(bitscrambler_handle_t handle, const bitscrambler_config_t *config); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_bitscrambler/priv_include/bitscrambler_soc_specific.h b/components/esp_driver_bitscrambler/src/bitscrambler_soc_specific.h similarity index 67% rename from components/esp_driver_bitscrambler/priv_include/bitscrambler_soc_specific.h rename to components/esp_driver_bitscrambler/src/bitscrambler_soc_specific.h index 435324bae2..19261fc323 100644 --- a/components/esp_driver_bitscrambler/priv_include/bitscrambler_soc_specific.h +++ b/components/esp_driver_bitscrambler/src/bitscrambler_soc_specific.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,9 +8,17 @@ #include "esp_private/gdma.h" #include "soc/bitscrambler_peri_select.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { gdma_trigger_t dma_trigger; int bus; } bitscrambler_periph_desc_t; extern const bitscrambler_periph_desc_t g_bitscrambler_periph_desc[]; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c index 527c59917a..45a6035920 100644 --- a/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c +++ b/components/esp_driver_bitscrambler/test_apps/bitscrambler/main/test_bitscrambler.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,7 +9,7 @@ #include "unity_test_utils.h" #include "driver/bitscrambler.h" #include "driver/bitscrambler_loopback.h" -#include "esp_dma_utils.h" +#include "esp_heap_caps.h" BITSCRAMBLER_PROGRAM(bitscrambler_program_trivial, "trivial"); BITSCRAMBLER_PROGRAM(bitscrambler_program_timeout, "timeout"); @@ -33,6 +33,7 @@ TEST_CASE("Basic BitScrambler I/O", "[bs]") bitscrambler_free(bs); TEST_ASSERT_EQUAL_HEX8_ARRAY(data_in, data_out, len); + TEST_ASSERT_EQUAL(len, res_len); free(data_in); free(data_out); diff --git a/components/esp_hw_support/dma/gdma_link.c b/components/esp_hw_support/dma/gdma_link.c index 45923f57a9..5381b150e1 100644 --- a/components/esp_hw_support/dma/gdma_link.c +++ b/components/esp_hw_support/dma/gdma_link.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -141,13 +141,14 @@ esp_err_t gdma_del_link_list(gdma_link_list_handle_t list) return ESP_OK; } -esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, uint32_t start_item_index, const gdma_buffer_mount_config_t *buf_config_array, size_t num_buf, uint32_t *end_item_index) +esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_index, const gdma_buffer_mount_config_t *buf_config_array, size_t num_buf, int *end_item_index) { ESP_RETURN_ON_FALSE(list && buf_config_array && num_buf, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE(start_item_index < list->num_items, ESP_ERR_INVALID_ARG, TAG, "invalid start item index"); size_t buffer_alignment = list->buffer_alignment; size_t item_size = list->item_size; uint32_t list_item_capacity = list->num_items; + // ensure the start_item_index is between 0 and `list_item_capacity - 1` + start_item_index = (start_item_index % list_item_capacity + list_item_capacity) % list_item_capacity; size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment); uint32_t begin_item_idx = start_item_index; gdma_link_list_item_t *lli_nc = NULL; @@ -155,7 +156,7 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, uint32_t start_i uint32_t num_items_avail = 0; // if the link list is responsible for checking the ownership, we need to skip the items that are owned by the DMA if (list->flags.check_owner) { - for (uint32_t i = 0; i < list_item_capacity; i++) { + for (int i = 0; i < list_item_capacity; i++) { lli_nc = (gdma_link_list_item_t *)(list->items_nc + (i + start_item_index) % list_item_capacity * item_size); if (lli_nc->dw0.owner == GDMA_LLI_OWNER_CPU) { num_items_avail++; @@ -166,8 +167,8 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, uint32_t start_i } // check alignment and length for each buffer - for (size_t i = 0; i < num_buf; i++) { - const gdma_buffer_mount_config_t *config = &buf_config_array[i]; + for (size_t bi = 0; bi < num_buf; bi++) { + const gdma_buffer_mount_config_t *config = &buf_config_array[bi]; uint8_t *buf = (uint8_t *)config->buffer; size_t len = config->length; // check the buffer alignment @@ -183,8 +184,8 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, uint32_t start_i lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size); begin_item_idx = start_item_index; - for (size_t i = 0; i < num_buf; i++) { - const gdma_buffer_mount_config_t *config = &buf_config_array[i]; + for (size_t bi = 0; bi < num_buf; bi++) { + const gdma_buffer_mount_config_t *config = &buf_config_array[bi]; uint8_t *buf = (uint8_t *)config->buffer; size_t len = config->length; // skip zero-length buffer @@ -193,7 +194,7 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, uint32_t start_i } uint32_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length; // mount the buffer to the link list - for (uint32_t i = 0; i < num_items_need; i++) { + for (int i = 0; i < num_items_need; i++) { lli_nc = (gdma_link_list_item_t *)(list->items_nc + (i + begin_item_idx) % list_item_capacity * item_size); lli_nc->buffer = buf; lli_nc->dw0.length = len > max_buffer_mount_length ? max_buffer_mount_length : len; @@ -232,16 +233,25 @@ esp_err_t gdma_link_concat(gdma_link_list_handle_t first_link, int first_link_it { ESP_RETURN_ON_FALSE(first_link && second_link, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); gdma_link_list_item_t *lli_nc = NULL; - lli_nc = (gdma_link_list_item_t *)(first_link->items_nc + (first_link->num_items + first_link_item_index) % first_link->num_items * first_link->item_size); - lli_nc->next = (gdma_link_list_item_t *)(second_link->items + (second_link->num_items + second_link_item_index) % second_link->num_items * second_link->item_size); + // ensure the first_link_item_index is between 0 and `num_items - 1` + int num_items = first_link->num_items; + first_link_item_index = (first_link_item_index % num_items + num_items) % num_items; + lli_nc = (gdma_link_list_item_t *)(first_link->items_nc + first_link_item_index * first_link->item_size); + // ensure the second_link_item_index is between 0 and `num_items - 1` + num_items = second_link->num_items; + second_link_item_index = (second_link_item_index % num_items + num_items) % num_items; + // concatenate the two link lists + lli_nc->next = (gdma_link_list_item_t *)(second_link->items + second_link_item_index * second_link->item_size); return ESP_OK; } esp_err_t gdma_link_set_owner(gdma_link_list_handle_t list, int item_index, gdma_lli_owner_t owner) { ESP_RETURN_ON_FALSE_ISR(list, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE_ISR(item_index < list->num_items, ESP_ERR_INVALID_ARG, TAG, "invalid item index"); - gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + (list->num_items + item_index) % list->num_items * list->item_size); + int num_items = list->num_items; + // ensure the item_index is between 0 and `num_items - 1` + item_index = (item_index % num_items + num_items) % num_items; + gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + item_index * list->item_size); lli->dw0.owner = owner; return ESP_OK; } @@ -249,8 +259,31 @@ esp_err_t gdma_link_set_owner(gdma_link_list_handle_t list, int item_index, gdma esp_err_t gdma_link_get_owner(gdma_link_list_handle_t list, int item_index, gdma_lli_owner_t *owner) { ESP_RETURN_ON_FALSE_ISR(list && owner, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); - ESP_RETURN_ON_FALSE_ISR(item_index < list->num_items, ESP_ERR_INVALID_ARG, TAG, "invalid item index"); - gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + (list->num_items + item_index) % list->num_items * list->item_size); + int num_items = list->num_items; + // ensure the item_index is between 0 and `num_items - 1` + item_index = (item_index % num_items + num_items) % num_items; + gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + item_index * list->item_size); *owner = lli->dw0.owner; return ESP_OK; } + +size_t gdma_link_count_buffer_size_till_eof(gdma_link_list_handle_t list, int start_item_index) +{ + if (!list) { + return 0; + } + int num_items = list->num_items; + // ensure the start_item_index is between 0 and `num_items - 1` + start_item_index = (start_item_index % num_items + num_items) % num_items; + size_t buf_size = 0; + gdma_link_list_item_t *lli_nc = NULL; + for (int i = 0; i < num_items; i++) { + lli_nc = (gdma_link_list_item_t *)(list->items_nc + (start_item_index + i) % num_items * list->item_size); + buf_size += lli_nc->dw0.length; + // break if the current item is the last one or the EOF item + if (lli_nc->dw0.suc_eof || lli_nc->next == NULL) { + break; + } + } + return buf_size; +} diff --git a/components/esp_hw_support/dma/include/esp_private/gdma_link.h b/components/esp_hw_support/dma/include/esp_private/gdma_link.h index fa350e792a..6972d16d16 100644 --- a/components/esp_hw_support/dma/include/esp_private/gdma_link.h +++ b/components/esp_hw_support/dma/include/esp_private/gdma_link.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -91,7 +91,7 @@ typedef struct { * - ESP_ERR_INVALID_ARG: Mount the buffer failed because of invalid argument * - ESP_FAIL: Mount the buffer failed because of other error */ -esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, uint32_t start_item_index, const gdma_buffer_mount_config_t *buf_config_array, size_t num_buf, uint32_t *end_item_index); +esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_index, const gdma_buffer_mount_config_t *buf_config_array, size_t num_buf, int *end_item_index); /** * @brief Get the address of the head item in the link list @@ -164,6 +164,16 @@ esp_err_t gdma_link_set_owner(gdma_link_list_handle_t list, int item_index, gdma */ esp_err_t gdma_link_get_owner(gdma_link_list_handle_t list, int item_index, gdma_lli_owner_t *owner); +/** + * @brief Get the size of the buffer that is mounted to the link list until the eof item (inclusive) + * + * @param[in] list Link list handle, allocated by `gdma_new_link_list` + * @param[in] start_item_index Index of the first item in the link list to be calculated + * @return Size of the buffer that is mounted to the link list until the eof item (inclusive). + * If the link list is empty or invalid, return 0. + */ +size_t gdma_link_count_buffer_size_till_eof(gdma_link_list_handle_t list, int start_item_index); + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/bitscrambler_ll.h b/components/hal/esp32p4/include/hal/bitscrambler_ll.h index 33c6276c9e..78114082d1 100644 --- a/components/hal/esp32p4/include/hal/bitscrambler_ll.h +++ b/components/hal/esp32p4/include/hal/bitscrambler_ll.h @@ -22,6 +22,8 @@ extern "C" { #define BITSCRAMBLER_LL_GET_HW(num) (((num) == 0) ? (&BITSCRAMBLER) : NULL) +#define BITSCRAMBLER_LL_INST_LEN_WORDS 9 //length of one instruction in 32-bit words as defined by HW + /** * @brief Select peripheral BitScrambler is attached to * diff --git a/docs/en/api-reference/peripherals/bitscrambler.rst b/docs/en/api-reference/peripherals/bitscrambler.rst index ab38a37981..eb3d0a536e 100644 --- a/docs/en/api-reference/peripherals/bitscrambler.rst +++ b/docs/en/api-reference/peripherals/bitscrambler.rst @@ -193,6 +193,10 @@ Resource allocation and program loading In loopback mode, a BitScrambler object is created using :cpp:func:`bitscrambler_loopback_create`. If there is a BitScrambler peripheral matching the requested characteristics, this function will return a handle to it. You can then use :cpp:func:`bitscrambler_load_program` to load a program into it, then call :cpp:func:`bitscrambler_loopback_run` to transform a memory buffer using the loaded program. You can call :cpp:func:`bitscrambler_loopback_run` any number of times; it's also permissible to use :cpp:func:`bitscrambler_load_program` to change programs between calls. Finally, to free the hardware resources and clean up memory, call :cpp:func:`bitscrambler_free`. +Application Example +------------------- + +* :example:`peripherals/bitscrambler` demonstrates how to use the BitScrambler loopback mode to transform a buffer of data into a different format. API Reference ------------- diff --git a/examples/peripherals/bitscrambler/CMakeLists.txt b/examples/peripherals/bitscrambler/CMakeLists.txt index 62c7ef2272..8e78254ace 100644 --- a/examples/peripherals/bitscrambler/CMakeLists.txt +++ b/examples/peripherals/bitscrambler/CMakeLists.txt @@ -5,4 +5,8 @@ cmake_minimum_required(VERSION 3.16) 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(bitscrambler_example) diff --git a/examples/peripherals/bitscrambler/README.md b/examples/peripherals/bitscrambler/README.md index 2b1363121f..617285ed85 100644 --- a/examples/peripherals/bitscrambler/README.md +++ b/examples/peripherals/bitscrambler/README.md @@ -23,6 +23,15 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui ## Example Output +```text +I (305) bs_example: BitScrambler example main +BitScrambler program complete. Input 40, output 40 bytes: +FF 00 00 00 00 00 00 00 +80 80 80 80 80 80 80 80 +01 02 04 08 10 20 40 80 +00 FF 00 FF 00 FF 00 FF +FF 00 FF 00 FF 00 FF 00 +``` ## Troubleshooting diff --git a/examples/peripherals/bitscrambler/main/CMakeLists.txt b/examples/peripherals/bitscrambler/main/CMakeLists.txt index e59e3e6b3f..e78d9ab257 100644 --- a/examples/peripherals/bitscrambler/main/CMakeLists.txt +++ b/examples/peripherals/bitscrambler/main/CMakeLists.txt @@ -1,6 +1,5 @@ - idf_component_register(SRCS "bitscrambler_example_main.c" - PRIV_REQUIRES "esp_driver_bitscrambler" - INCLUDE_DIRS ".") + PRIV_REQUIRES "esp_driver_bitscrambler" + INCLUDE_DIRS ".") target_bitscrambler_add_src("example.bsasm") diff --git a/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c b/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c index 3b4970ecb2..26457f929a 100644 --- a/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c +++ b/examples/peripherals/bitscrambler/main/bitscrambler_example_main.c @@ -1,22 +1,17 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include #include -#include -#include -#include -#include "esp_err.h" -#include "esp_log.h" -#include "esp_check.h" -#include -#include "driver/bitscrambler_loopback.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "esp_err.h" +#include "esp_log.h" #include "esp_heap_caps.h" +#include "driver/bitscrambler_loopback.h" //Assign a symbol to the example bitscrambler program. Note that the actual //assembly and including in the binary happens in the CMakeLists.txt file. diff --git a/examples/peripherals/bitscrambler/main/example.bsasm b/examples/peripherals/bitscrambler/main/example.bsasm index 329ce6fb21..1c718f4d20 100644 --- a/examples/peripherals/bitscrambler/main/example.bsasm +++ b/examples/peripherals/bitscrambler/main/example.bsasm @@ -1,10 +1,13 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + # Example bitscrambler program. Reads in 8 bytes and spits out 8 bytes are # the 'rotated' version of the input bytes. Specifically, output byte 0 # consists of bit 0 of input byte 0, bit 0 of input byte 1, bit 0 of input # byte 2 etc. Output byte 1 consists of bit 1 of input byte 0, bit 1 of # input byte 1, bit 1 of input byte 2, etc. -cfg trailing_bytes 64 #If we have an EOF on the input, we still +cfg trailing_bytes 8 #If we have an EOF on the input, we still #need to process the 64 bits in M0/M1 cfg prefetch true #We expect M0/M1 to be filled cfg lut_width_bits 8 #Not really applicable here diff --git a/examples/peripherals/bitscrambler/pytest_bitscrambler_loopback_example.py b/examples/peripherals/bitscrambler/pytest_bitscrambler_loopback_example.py new file mode 100644 index 0000000000..d627629f97 --- /dev/null +++ b/examples/peripherals/bitscrambler/pytest_bitscrambler_loopback_example.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_bitscrambler_loopback_example(dut: Dut) -> None: + dut.expect_exact('BitScrambler example main', timeout=5) + dut.expect_exact('BitScrambler program complete. Input 40, output 40 bytes') + + expected_lines = [ + 'FF 00 00 00 00 00 00 00', + '80 80 80 80 80 80 80 80', + '01 02 04 08 10 20 40 80', + '00 FF 00 FF 00 FF 00 FF', + 'FF 00 FF 00 FF 00 FF 00', + ] + for line in expected_lines: + dut.expect_exact(line)