refactor(bitscrambler): to use GDMA link API

This commit is contained in:
morris 2025-01-03 18:00:50 +08:00
parent 8de8558841
commit dc3f31adac
12 changed files with 177 additions and 145 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

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