Merge branch 'change/bs_cmake_cleanup' into 'master'

change(bs): some refactor to the bitscrambler component

See merge request espressif/esp-idf!36148
This commit is contained in:
morris
2025-01-14 16:33:25 +08:00
21 changed files with 285 additions and 175 deletions

View File

@@ -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")

View File

@@ -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

View File

@@ -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 <stdbool.h>
#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);

View File

@@ -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 <stdbool.h>
#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;
/**

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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 <stddef.h>
#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;

View File

@@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
*

View File

@@ -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
-------------

View File

@@ -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)

View File

@@ -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

View File

@@ -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")

View File

@@ -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 <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include <stdio.h>
#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.

View File

@@ -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

View File

@@ -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)