mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
feat(driver): BitScrambler support
This adds an assembler for the BitScrambler assembly language, plus unit tests for it. It also adds the loopback driver, which can do BitScrambler operations on memory-to-memory transfers. Documentation is also included.
This commit is contained in:
29
components/esp_driver_bitscrambler/CMakeLists.txt
Normal file
29
components/esp_driver_bitscrambler/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
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")
|
||||
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"
|
||||
)
|
300
components/esp_driver_bitscrambler/bitscrambler.c
Normal file
300
components/esp_driver_bitscrambler/bitscrambler.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#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"
|
||||
|
||||
static const char *TAG = "bitscrambler";
|
||||
|
||||
#define BITSCRAMBLER_BINARY_VER 1 //max version we're compatible with
|
||||
#define BITSCRAMBLER_HW_REV 0
|
||||
|
||||
// After a reset, it can take a few cycles for the BitScrambler to actually be
|
||||
// reset. We check this many times for this; if it takes longer the hardware
|
||||
// is broken or something.
|
||||
#define BITSCRAMBLER_RESET_ITERATIONS 10000
|
||||
|
||||
/*
|
||||
Format of a V1 BitScrambler program image:
|
||||
- Header, as defined by bitscrambler_program_hdr_t below. Size is hdr->hdr_len words.
|
||||
- Program lines. A line is 9 32-bit words, we have hdr->inst_ct lines.
|
||||
- LUT data. LUT is hdr->lut_word_ct 32-bit words in size.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t version;
|
||||
uint8_t hw_rev;
|
||||
uint8_t hdr_len; //in 32-bit words
|
||||
uint8_t inst_ct; //0-8
|
||||
uint16_t lut_word_ct; //in 32-bit words
|
||||
uint8_t lut_width; //0, 1, 2
|
||||
uint8_t prefetch; //prefetch enabled?
|
||||
uint16_t trailing_bits; //in bits
|
||||
uint8_t eof_on;
|
||||
uint8_t unused;
|
||||
} bitscrambler_program_hdr_t;
|
||||
|
||||
#define INST_LEN_WORDS 9 //length of one instruction in 32-bit words as defined by HW
|
||||
|
||||
// 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.
|
||||
atomic_flag tx_in_use = ATOMIC_FLAG_INIT;
|
||||
atomic_flag rx_in_use = ATOMIC_FLAG_INIT;
|
||||
|
||||
// Claim both TX and RX channels for loopback use
|
||||
// Returns true on success, false if any of the two directions already is claimed.
|
||||
static bool claim_channel_loopback(void)
|
||||
{
|
||||
bool old_val_tx = atomic_flag_test_and_set(&tx_in_use);
|
||||
if (old_val_tx) {
|
||||
return false;
|
||||
}
|
||||
bool old_val_rx = atomic_flag_test_and_set(&rx_in_use);
|
||||
if (old_val_rx) {
|
||||
atomic_flag_clear(&tx_in_use);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Claim a channel using the direction it indicated.
|
||||
// Returns true on success, false if the direction already is claimed
|
||||
static bool claim_channel(bitscrambler_direction_t dir)
|
||||
{
|
||||
if (dir == BITSCRAMBLER_DIR_TX) {
|
||||
bool old_val = atomic_flag_test_and_set(&tx_in_use);
|
||||
if (old_val) {
|
||||
return false;
|
||||
}
|
||||
} else if (dir == BITSCRAMBLER_DIR_RX) {
|
||||
bool old_val = atomic_flag_test_and_set(&rx_in_use);
|
||||
if (old_val) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Initialize the BitScrambler object and hardware using the given config.
|
||||
static esp_err_t init_from_config(bitscrambler_t *bs, const bitscrambler_config_t *config)
|
||||
{
|
||||
bs->cfg = *config; //Copy config over
|
||||
bs->hw = BITSCRAMBLER_LL_GET_HW(0); //there's only one as of now; if there's more, we need to handle them as a pool.
|
||||
|
||||
//Attach to indicated peripheral.
|
||||
bitscrambler_ll_select_peripheral(bs->hw, bs->cfg.dir, config->attach_to);
|
||||
bitscrambler_ll_enable(bs->hw, bs->cfg.dir);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void enable_clocks(bitscrambler_t *bs)
|
||||
{
|
||||
PERIPH_RCC_ACQUIRE_ATOMIC(PERIPH_BITSCRAMBLER_MODULE, ref_count) {
|
||||
if (ref_count == 0) { //we're the first to enable the BitScrambler module
|
||||
bitscrambler_ll_set_bus_clock_sys_enable(1);
|
||||
bitscrambler_ll_reset_sys();
|
||||
}
|
||||
if (bs->cfg.dir == BITSCRAMBLER_DIR_RX || bs->loopback) {
|
||||
bitscrambler_ll_set_bus_clock_rx_enable(1);
|
||||
bitscrambler_ll_reset_rx();
|
||||
}
|
||||
if (bs->cfg.dir == BITSCRAMBLER_DIR_TX || bs->loopback) {
|
||||
bitscrambler_ll_set_bus_clock_tx_enable(1);
|
||||
bitscrambler_ll_reset_tx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void disable_clocks(bitscrambler_t *bs)
|
||||
{
|
||||
PERIPH_RCC_RELEASE_ATOMIC(PERIPH_BITSCRAMBLER_MODULE, ref_count) {
|
||||
if (bs->cfg.dir == BITSCRAMBLER_DIR_RX || bs->loopback) {
|
||||
bitscrambler_ll_set_bus_clock_rx_enable(0);
|
||||
}
|
||||
if (bs->cfg.dir == BITSCRAMBLER_DIR_TX || bs->loopback) {
|
||||
bitscrambler_ll_set_bus_clock_tx_enable(0);
|
||||
}
|
||||
if (ref_count == 0) { //we're the last to disable the BitScrambler module
|
||||
bitscrambler_ll_set_bus_clock_sys_enable(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Private function: init an existing BitScrambler object as a loopback BitScrambler.
|
||||
esp_err_t bitscrambler_init_loopback(bitscrambler_handle_t handle, const bitscrambler_config_t *config)
|
||||
{
|
||||
if (!claim_channel_loopback()) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
assert(config->dir == BITSCRAMBLER_DIR_TX);
|
||||
handle->loopback = true;
|
||||
enable_clocks(handle);
|
||||
esp_err_t r = init_from_config(handle, config);
|
||||
//Loopback mode also needs RX channel set to the selected peripheral, even if it's not used.
|
||||
bitscrambler_ll_select_peripheral(handle->hw, BITSCRAMBLER_DIR_RX, config->attach_to);
|
||||
return r;
|
||||
}
|
||||
|
||||
esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_handle_t *handle)
|
||||
{
|
||||
if (!config) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!handle) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
// Allocate memory for private data
|
||||
bitscrambler_t *bs = calloc(1, sizeof(bitscrambler_t));
|
||||
if (!bs) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Claim channel
|
||||
if (!claim_channel(config->dir)) {
|
||||
free(bs);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
enable_clocks(bs);
|
||||
// Do initialization of BS object.
|
||||
esp_err_t r = init_from_config(bs, config);
|
||||
if (r != ESP_OK) {
|
||||
bitscrambler_free(bs);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Done.
|
||||
*handle = bs;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bitscrambler_load_program(bitscrambler_handle_t bs, const void *program_bin)
|
||||
{
|
||||
if (!bs || !program_bin) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
bitscrambler_program_hdr_t hdr;
|
||||
|
||||
//Parse the program header. There are two versions, V0 is generated by the C assembler while
|
||||
//v1 is generated by the Python assembler.
|
||||
int inst_len_bytes = INST_LEN_WORDS * sizeof(uint32_t); //note this is different for v1 and v0
|
||||
memcpy(&hdr, program_bin, sizeof(bitscrambler_program_hdr_t));
|
||||
if (hdr.version != BITSCRAMBLER_BINARY_VER) {
|
||||
ESP_LOGE(TAG, "Bitscrambler binary version %d not supported!", hdr.version);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (hdr.hw_rev != BITSCRAMBLER_HW_REV) {
|
||||
ESP_LOGE(TAG, "Bitscrambler hardware rev %d not supported!", hdr.hw_rev);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
bitscrambler_ll_set_state(bs->hw, bs->cfg.dir, BITSCRAMBLER_SET_STATE_HALT);
|
||||
|
||||
//Load the program
|
||||
const uint8_t *p = (const uint8_t*)program_bin;
|
||||
p += hdr.hdr_len * sizeof(uint32_t); //skip header
|
||||
uint32_t instr[INST_LEN_WORDS];
|
||||
for (int inst = 0; inst < hdr.inst_ct; inst++) {
|
||||
//v0 doesn't have the words 32-bit aligned, so memcpy to work around that
|
||||
memcpy(instr, p, INST_LEN_WORDS * sizeof(uint32_t));
|
||||
p += inst_len_bytes;
|
||||
for (int w = 0; w < INST_LEN_WORDS; w++) {
|
||||
bitscrambler_ll_instmem_write(bs->hw, bs->cfg.dir, inst, w, instr[w]);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Loaded %d instructions", hdr.inst_ct);
|
||||
//Load the LUT.
|
||||
bitscrambler_ll_set_lut_width(bs->hw, bs->cfg.dir, BITSCRAMBLER_LUT_WIDTH_32BIT);
|
||||
uint32_t *lut = (uint32_t*)p;
|
||||
for (int w = 0; w < hdr.lut_word_ct; w++) {
|
||||
bitscrambler_ll_lutmem_write(bs->hw, bs->cfg.dir, w, lut[w]);
|
||||
}
|
||||
|
||||
//Set options from header
|
||||
bitscrambler_ll_set_lut_width(bs->hw, bs->cfg.dir, hdr.lut_width);
|
||||
bitscrambler_ll_set_prefetch_mode(bs->hw, bs->cfg.dir, hdr.prefetch ? BITSCRAMBLER_PREFETCH_ENABLED : BITSCRAMBLER_PREFETCH_DISABLED);
|
||||
bitscrambler_ll_set_eof_mode(bs->hw, bs->cfg.dir, hdr.eof_on);
|
||||
bitscrambler_ll_set_tailing_bits(bs->hw, bs->cfg.dir, hdr.trailing_bits);
|
||||
//fixed options
|
||||
bitscrambler_ll_set_dummy_mode(bs->hw, bs->cfg.dir, BITSCRAMBLER_DUMMY_MODE_DUMMY);
|
||||
bitscrambler_ll_set_halt_mode(bs->hw, bs->cfg.dir, BITSCRAMBLER_HALT_IGNORE_WRITES);
|
||||
//enable loopback mode if requested
|
||||
bitscrambler_ll_enable_loopback(bs->hw, bs->loopback);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t size_bytes)
|
||||
{
|
||||
if (!handle || !lut) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
uint32_t *lut_words = (uint32_t*)lut;
|
||||
bitscrambler_lut_width_t lut_width = bitscrambler_ll_get_lut_width(handle->hw, handle->cfg.dir);
|
||||
bitscrambler_ll_set_lut_width(handle->hw, handle->cfg.dir, BITSCRAMBLER_LUT_WIDTH_32BIT);
|
||||
size_t size_words = (size_bytes + 3) / 4;
|
||||
for (int w = 0; w < size_words; w++) {
|
||||
bitscrambler_ll_lutmem_write(handle->hw, handle->cfg.dir, w, lut_words[w]);
|
||||
}
|
||||
bitscrambler_ll_set_lut_width(handle->hw, handle->cfg.dir, lut_width);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void bitscrambler_free(bitscrambler_handle_t handle)
|
||||
{
|
||||
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);
|
||||
}
|
||||
free(handle);
|
||||
}
|
||||
|
||||
esp_err_t bitscrambler_start(bitscrambler_handle_t handle)
|
||||
{
|
||||
if (!handle) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
bitscrambler_ll_set_state(handle->hw, handle->cfg.dir, BITSCRAMBLER_SET_STATE_RUN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bitscrambler_reset(bitscrambler_handle_t handle)
|
||||
{
|
||||
if (!handle) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_err_t ret = ESP_OK;
|
||||
bitscrambler_ll_set_state(handle->hw, handle->cfg.dir, BITSCRAMBLER_SET_STATE_HALT);
|
||||
//If the halt bit is set, the Bitscrambler should (eventually) go to idle state. If it
|
||||
//does not, something got stuck.
|
||||
int timeout = BITSCRAMBLER_RESET_ITERATIONS;
|
||||
while ((bitscrambler_ll_current_state(handle->hw, handle->cfg.dir) != BITSCRAMBLER_STATE_IDLE) && timeout != 0) {
|
||||
timeout--;
|
||||
}
|
||||
if (timeout == 0) {
|
||||
ESP_LOGE(TAG, "bitscrambler_reset: Timeout waiting for idle!");
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
}
|
||||
//Reset the fifos & eof trace ctrs
|
||||
bitscrambler_ll_reset_fifo(handle->hw, handle->cfg.dir);
|
||||
bitscrambler_ll_clear_eof_trace(handle->hw, handle->cfg.dir);
|
||||
|
||||
return ret;
|
||||
}
|
27
components/esp_driver_bitscrambler/bitscrambler_esp32p4.c
Normal file
27
components/esp_driver_bitscrambler/bitscrambler_esp32p4.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "soc/gdma_channel.h"
|
||||
#include "bitscrambler_soc_specific.h"
|
||||
|
||||
// Note: these are indexed by the values of the SOC_BITSCRAMBLER_ATTACH_ defines
|
||||
// in soc/bitscrambler_peri_select.h
|
||||
// This map is used by the bitscrambler loopback driver only.
|
||||
|
||||
const bitscrambler_periph_desc_t g_bitscrambler_periph_desc[] = {
|
||||
[SOC_BITSCRAMBLER_ATTACH_LCD_CAM] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0), SOC_GDMA_TRIG_PERIPH_LCD0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_GPSPI2] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 2), SOC_GDMA_TRIG_PERIPH_SPI2_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_GPSPI3] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SPI, 3), SOC_GDMA_TRIG_PERIPH_SPI3_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_PARL_IO] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0), SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_AES] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_AES, 0), SOC_GDMA_TRIG_PERIPH_AES0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_SHA] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SHA, 0), SOC_GDMA_TRIG_PERIPH_SHA0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_ADC] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_ADC, 0), SOC_GDMA_TRIG_PERIPH_ADC0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_I2S0] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I2S, 0), SOC_GDMA_TRIG_PERIPH_I2S0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_I2S1] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I2S, 1), SOC_GDMA_TRIG_PERIPH_I2S1_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_I2S2] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I2S, 2), SOC_GDMA_TRIG_PERIPH_I2S2_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_I3C_MST] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_I3C, 0), SOC_GDMA_TRIG_PERIPH_I3C0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_UHCI] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UHCI, 0), SOC_GDMA_TRIG_PERIPH_UHCI0_BUS},
|
||||
[SOC_BITSCRAMBLER_ATTACH_RMT] = {GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_RMT, 0), SOC_GDMA_TRIG_PERIPH_RMT0_BUS},
|
||||
};
|
273
components/esp_driver_bitscrambler/bitscrambler_loopback.c
Normal file
273
components/esp_driver_bitscrambler/bitscrambler_loopback.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 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 "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
|
||||
SemaphoreHandle_t sema_done;
|
||||
size_t max_transfer_sz_bytes;
|
||||
} bitscrambler_loopback_t;
|
||||
|
||||
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;
|
||||
//Note that there are chips that do not have SOC_GDMA_BUS_* defined, but those chips also do
|
||||
//not have a BitScrambler.
|
||||
#ifdef SOC_GDMA_BUS_AHB
|
||||
if (bus == SOC_GDMA_BUS_AHB || bus == SOC_GDMA_BUS_ANY) {
|
||||
ESP_RETURN_ON_ERROR(gdma_new_ahb_channel(cfg, handle), TAG, "alloc AHB DMA channel failed");
|
||||
}
|
||||
#endif
|
||||
#ifdef SOC_GDMA_BUS_AXI
|
||||
if (bus == SOC_GDMA_BUS_AXI) {
|
||||
ESP_RETURN_ON_ERROR(gdma_new_axi_channel(cfg, handle), TAG, "alloc AXI DMA channel failed");
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IRAM_ATTR bool trans_done_cb(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
BaseType_t higher_prio_task_awoken = pdFALSE;
|
||||
bitscrambler_loopback_t *bs = (bitscrambler_loopback_t*)user_data;
|
||||
xSemaphoreGiveFromISR(bs->sema_done, &higher_prio_task_awoken);
|
||||
return higher_prio_task_awoken;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (attach_to < 0 || attach_to > SOC_BITSCRAMBLER_ATTACH_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
bitscrambler_loopback_t *bs = calloc(1, sizeof(bitscrambler_loopback_t));
|
||||
if (!bs) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
//Create the underlying BitScrambler object
|
||||
bitscrambler_config_t cfg = {
|
||||
.dir = BITSCRAMBLER_DIR_TX,
|
||||
.attach_to = attach_to
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_init_loopback(&bs->bs, &cfg), err, TAG, "failed bitscrambler init for loopback");
|
||||
|
||||
bs->sema_done = xSemaphoreCreateBinary();
|
||||
if (!bs->sema_done) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
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 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,
|
||||
};
|
||||
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 = {
|
||||
.direction = GDMA_CHANNEL_DIRECTION_RX,
|
||||
.sibling_chan = bs->tx_channel,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(new_dma_channel(&rx_alloc_config, &bs->rx_channel, bus), err, TAG, "failed to create GDMA RX channel");
|
||||
|
||||
gdma_connect(bs->rx_channel, g_bitscrambler_periph_desc[attach_to].dma_trigger);
|
||||
gdma_connect(bs->tx_channel, g_bitscrambler_periph_desc[attach_to].dma_trigger);
|
||||
gdma_strategy_config_t gdma_strategy_conf = {
|
||||
.auto_update_desc = true,
|
||||
.owner_check = false,
|
||||
};
|
||||
gdma_apply_strategy(bs->rx_channel, &gdma_strategy_conf);
|
||||
gdma_apply_strategy(bs->tx_channel, &gdma_strategy_conf);
|
||||
|
||||
gdma_rx_event_callbacks_t rx_cbs = {
|
||||
.on_recv_eof = trans_done_cb,
|
||||
};
|
||||
gdma_register_rx_event_callbacks(bs->rx_channel, &rx_cbs, bs);
|
||||
|
||||
*handle = (bitscrambler_handle_t)bs;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
bitscrambler_loopback_free(&bs->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)
|
||||
{
|
||||
bitscrambler_loopback_t *bsl = (bitscrambler_loopback_t*)bs;
|
||||
if (bsl->rx_channel) {
|
||||
gdma_disconnect(bsl->rx_channel);
|
||||
gdma_del_channel(bsl->rx_channel);
|
||||
}
|
||||
if (bsl->tx_channel) {
|
||||
gdma_disconnect(bsl->tx_channel);
|
||||
gdma_del_channel(bsl->tx_channel);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
A BitScrambler program could theoretically take a bunch of time to run, e.g. when transferring from PSRAM to PSRAM.
|
||||
However, given that this is a memory copy, it feels stupid to have a 'soft' parameter as a timeout; we do need a timeout,
|
||||
however, as the BitScrambler program may be buggy and e.g. never read or write anything.
|
||||
|
||||
As an upper limit for a timeout, we can assume the backing memory is quad psram @ 20MHz, meaning we have a throughput
|
||||
of around 10MByte/second. Any BitScrambler program is going to be lots faster than that, simply because it doesn't
|
||||
have the instructions to delay writing by much. Just for safety, we can add an extra factor of 10 to that, making
|
||||
us assume a minimum throughput of 1MByte/sec.
|
||||
*/
|
||||
#define BS_MIN_BYTES_PER_SEC 1000000
|
||||
|
||||
/*
|
||||
We'll also add a few FreeRTOS ticks to the delay, so tiny data transfers won't have an impossibly short timeout.
|
||||
*/
|
||||
#define BS_TIMEOUT_BASE_TICKS 3
|
||||
|
||||
esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, size_t length_bytes_in, void *buffer_out, size_t length_bytes_out, size_t *bytes_written)
|
||||
{
|
||||
//Note that buffer_in and buffer_out are from the perspective of the BitScrambler,
|
||||
//however tx/rx are from the perspective of the memory. So buffer_in=tx, buffer_out=rx.
|
||||
esp_err_t ret = ESP_OK;
|
||||
bitscrambler_loopback_t *bsl = (bitscrambler_loopback_t*)bs;
|
||||
if (length_bytes_in > bsl->max_transfer_sz_bytes) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
if (length_bytes_out > bsl->max_transfer_sz_bytes) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
//Casual check to see if the buffer is aligned to cache requirements.
|
||||
esp_dma_mem_info_t dma_mem_info = {
|
||||
.dma_alignment_bytes = 4
|
||||
};
|
||||
//Note: we know the size of the data, but not of the buffer that contains it, so we set length=0.
|
||||
if (!esp_dma_is_buffer_alignment_satisfied(buffer_in, 0, dma_mem_info)) {
|
||||
ESP_LOGE(TAG, "buffer_in not aligned to DMA requirements");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!esp_dma_is_buffer_alignment_satisfied(buffer_out, 0, dma_mem_info)) {
|
||||
ESP_LOGE(TAG, "buffer_out not aligned to DMA requirements");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
gdma_reset(bsl->rx_channel);
|
||||
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);
|
||||
|
||||
//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);
|
||||
bitscrambler_start(bs);
|
||||
|
||||
int timeout_ms = (length_bytes_out + length_bytes_in) / (BS_MIN_BYTES_PER_SEC / 1000);
|
||||
int timeout = pdMS_TO_TICKS(timeout_ms) + BS_TIMEOUT_BASE_TICKS;
|
||||
if (!xSemaphoreTake(bsl->sema_done, timeout)) {
|
||||
gdma_reset(bsl->rx_channel);
|
||||
gdma_reset(bsl->tx_channel);
|
||||
bitscrambler_reset(bs);
|
||||
ESP_LOGE(TAG, "bitscrambler_loopback_run: timed out waiting for BitScrambler program to complete!");
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
23
components/esp_driver_bitscrambler/bitscrambler_private.h
Normal file
23
components/esp_driver_bitscrambler/bitscrambler_private.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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);
|
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "hal/bitscrambler_types.h"
|
||||
#include "soc/bitscrambler_peri_select.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BITSCRAMBLER_PROGRAM(VAR, NAME) extern const uint8_t VAR[] asm("_binary_bitscrambler_program_" NAME "_start")
|
||||
|
||||
typedef struct bitscrambler_t *bitscrambler_handle_t;
|
||||
|
||||
/**
|
||||
* @brief BitScrambler configuration
|
||||
*/
|
||||
typedef struct {
|
||||
bitscrambler_direction_t dir; /*!< Direction (tx or rx) */
|
||||
int attach_to; /*!< Peripheral to attach to. One of SOC_BITSCRAMBLER_ATTACH_. */
|
||||
} bitscrambler_config_t;
|
||||
|
||||
/**
|
||||
* @brief Allocate BitScrambler handle for a hardware channel
|
||||
*
|
||||
* @param config Configuration for requested BitScrambler
|
||||
* @param[out] handle BitScrambler controller handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_NO_MEM: No memory available
|
||||
* - ESP_ERR_NOT_FOUND: No free hardware channel available
|
||||
*/
|
||||
esp_err_t bitscrambler_new(const bitscrambler_config_t *config, bitscrambler_handle_t *handle);
|
||||
|
||||
/**
|
||||
* @brief Free previously allocated BitScrambler handle
|
||||
*
|
||||
* @param handle Previously allocated handle
|
||||
*/
|
||||
void bitscrambler_free(bitscrambler_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Load a BitScrambler binary program into BitScrambler memory
|
||||
*
|
||||
* @param handle BitScrambler handle
|
||||
* @param program Binary program to load
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_ARG: Not a valid or recognized BitScrambler binary, or invalid handle
|
||||
*/
|
||||
esp_err_t bitscrambler_load_program(bitscrambler_handle_t handle, const void *program);
|
||||
|
||||
/**
|
||||
* @brief Load data into the Look-Up Table
|
||||
*
|
||||
* @param handle BitScrambler handle
|
||||
* @param lut Data to load
|
||||
* @param size_bytes Size of the data, in bytes
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_ARG: Invalid handle or lut pointer
|
||||
*/
|
||||
esp_err_t bitscrambler_load_lut(bitscrambler_handle_t handle, void *lut, size_t size_bytes);
|
||||
|
||||
/**
|
||||
* @brief Start executing BitScrambler program
|
||||
*
|
||||
* @param handle BitScrambler handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_ARG: Invalid handle
|
||||
*/
|
||||
esp_err_t bitscrambler_start(bitscrambler_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Reset BitScrambler program and FIFOs for a new transaction. Note that this does not
|
||||
* affect the loaded program itself.
|
||||
*
|
||||
* @param handle BitScrambler handle
|
||||
* @return
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_ARG: Invalid handle
|
||||
*/
|
||||
esp_err_t bitscrambler_reset(bitscrambler_handle_t handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "driver/bitscrambler.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create handle for BitScrambler in loopback mode
|
||||
*
|
||||
* @note Use bitscrambler_free to free created handle.
|
||||
*
|
||||
* @param[out] handle BitScrambler handle
|
||||
* @param attach_to Peripheral to attach to. One of SOC_BITSCRAMBLER_ATTACH_. The BitScrambler
|
||||
* must be attached to some peripheral, even in loopback mode. This peripheral does not
|
||||
* need to be initialized for the BitScrambler to work. However, it can be initialized
|
||||
* and will work as normal, with the exception that DMA functionality for this peripheral
|
||||
* cannot be used.
|
||||
* @param max_transfer_sz_bytes Maximum transfer size, in bytes, of either the incoming or outgoing data
|
||||
* fed to bitscrambler_loopback_run.
|
||||
*
|
||||
* @returns
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_NO_MEM: No memory available
|
||||
* - ESP_ERR_NOT_FOUND: No free hardware channel available
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument passed to function
|
||||
* - ESP_FAIL: Bitscrambler object creation failed because of some other error
|
||||
*/
|
||||
esp_err_t bitscrambler_loopback_create(bitscrambler_handle_t *handle, int attach_to, size_t max_transfer_sz_bytes);
|
||||
|
||||
/**
|
||||
* @brief Run Bitscrambler program on a data buffer
|
||||
*
|
||||
* @param bs BitScrambler handle. This BitScrambler should have a program loaded using
|
||||
* bitscrambler_load_program()
|
||||
* @param buffer_in Data to feed into the BitScrambler
|
||||
* @param length_bytes_in Size of the data in buffer_in, in bytes
|
||||
* @param buffer_out Buffer for BitScrambler to write processed data to
|
||||
* @param length_bytes_out Size of output buffer
|
||||
* @param[out] bytes_written Pointer to variable to store the size of actual data written
|
||||
* to output buffer. Can be NULL if not needed.
|
||||
*
|
||||
* @returns
|
||||
* - ESP_OK
|
||||
* - ESP_ERR_INVALID_SIZE if a buffer size exceeds max_transfer_sz_bytes
|
||||
* - ESP_ERR_TIMEOUT if BitScrambler program does not complete
|
||||
*/
|
||||
esp_err_t bitscrambler_loopback_run(bitscrambler_handle_t bs, void *buffer_in, size_t length_bytes_in, void *buffer_out, size_t length_bytes_out, size_t *bytes_written);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_private/gdma.h"
|
||||
#include "soc/bitscrambler_peri_select.h"
|
||||
|
||||
typedef struct {
|
||||
gdma_trigger_t dma_trigger;
|
||||
int bus;
|
||||
} bitscrambler_periph_desc_t;
|
||||
|
||||
extern const bitscrambler_periph_desc_t g_bitscrambler_periph_desc[];
|
18
components/esp_driver_bitscrambler/project_include.cmake
Normal file
18
components/esp_driver_bitscrambler/project_include.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
# target_bitscrambler_add_src
|
||||
#
|
||||
# Assemble BitScrambler sources and embed into the application.
|
||||
function(target_bitscrambler_add_src s_sources)
|
||||
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
spaces2list(s_sources)
|
||||
foreach(source ${s_sources})
|
||||
get_filename_component(source ${source} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
get_filename_component(basename ${source} NAME_WE)
|
||||
set(ps_output ${CMAKE_CURRENT_BINARY_DIR}/${basename}.bsbin)
|
||||
idf_build_get_property(python PYTHON)
|
||||
idf_build_get_property(idf_path IDF_PATH)
|
||||
add_custom_command(OUTPUT ${ps_output} DEPENDS ${source}
|
||||
COMMAND ${python} ${idf_path}/tools/bsasm.py ${source} ${ps_output})
|
||||
target_add_binary_data(${COMPONENT_LIB} ${ps_output} BINARY RENAME_TO bitscrambler_program_${basename})
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
@@ -0,0 +1,5 @@
|
||||
components/esp_driver_bitscrambler/test_apps/bitscrambler:
|
||||
disable:
|
||||
- if: SOC_BITSCRAMBLER_SUPPORTED != 1
|
||||
depends_components:
|
||||
- esp_driver_bitscrambler
|
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(test_bitscrambler)
|
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
@@ -0,0 +1,18 @@
|
||||
set(srcs "test_app_main.c")
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
list(APPEND srcs "test_bitscrambler.c")
|
||||
endif()
|
||||
|
||||
set(priv_requires
|
||||
unity
|
||||
esp_driver_bitscrambler
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES ${priv_requires}
|
||||
WHOLE_ARCHIVE TRUE)
|
||||
|
||||
target_bitscrambler_add_src("timeout.bsasm")
|
||||
target_bitscrambler_add_src("trivial.bsasm")
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (400)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "driver/bitscrambler_loopback.h"
|
||||
#include "esp_dma_utils.h"
|
||||
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_trivial, "trivial");
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_timeout, "timeout");
|
||||
|
||||
TEST_CASE("Basic BitScrambler I/O", "[bs]")
|
||||
{
|
||||
int len = 0x4010;
|
||||
uint8_t *data_in = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
uint8_t *data_out = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
TEST_ASSERT_NOT_NULL(data_in);
|
||||
TEST_ASSERT_NOT_NULL(data_out);
|
||||
for (int i = 0; i < len; i++) {
|
||||
data_in[i] = (uint8_t)rand();
|
||||
data_out[i] = 0xFF;
|
||||
}
|
||||
bitscrambler_handle_t bs;
|
||||
TEST_ESP_OK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_I2S0, len));
|
||||
TEST_ESP_OK(bitscrambler_load_program(bs, bitscrambler_program_trivial));
|
||||
size_t res_len;
|
||||
TEST_ESP_OK(bitscrambler_loopback_run(bs, data_in, len, data_out, len, &res_len));
|
||||
bitscrambler_free(bs);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(data_in, data_out, len);
|
||||
|
||||
free(data_in);
|
||||
free(data_out);
|
||||
}
|
||||
|
||||
TEST_CASE("Timeout on stuck program", "[bs]")
|
||||
{
|
||||
int len = 4096 * 10;
|
||||
uint8_t *data_in = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
uint8_t *data_out = heap_caps_aligned_calloc(8, 1, len, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
bitscrambler_handle_t bs;
|
||||
|
||||
TEST_ESP_OK(bitscrambler_loopback_create(&bs, SOC_BITSCRAMBLER_ATTACH_I2S0, len));
|
||||
TEST_ESP_OK(bitscrambler_load_program(bs, bitscrambler_program_timeout));
|
||||
esp_err_t err = bitscrambler_loopback_run(bs, data_in, len, data_out, len, NULL);
|
||||
TEST_ASSERT(err == ESP_ERR_TIMEOUT);
|
||||
bitscrambler_free(bs);
|
||||
free(data_in);
|
||||
free(data_out);
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
cfg trailing_bytes 0 #End program as soon as the input EOFs.
|
||||
cfg prefetch true #We expect M0/M1 to be filled
|
||||
cfg lut_width_bits 8 #Not really applicable here
|
||||
|
||||
#Does nothing, so host logic will go into timeout
|
||||
|
||||
loop:
|
||||
read 0,
|
||||
write 0,
|
||||
jmp loop
|
@@ -0,0 +1,11 @@
|
||||
# Example bitscrambler program. Does nothing but forward all bytes.
|
||||
|
||||
cfg trailing_bytes 12 # Let M0/M1 empty when EOF on input is found
|
||||
cfg prefetch true # We expect M0/M1 to be filled
|
||||
cfg lut_width_bits 8 # Not really applicable here
|
||||
|
||||
loop:
|
||||
set 0..31 0..31,
|
||||
write 32,
|
||||
read 32,
|
||||
jmp loop
|
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2024 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(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
@@ -0,0 +1 @@
|
||||
# CONFIG_ESP_TASK_WDT_EN is not set
|
Reference in New Issue
Block a user