forked from espressif/esp-idf
feat(parlio_tx): support to mount bitscrambler
This commit is contained in:
@ -8,10 +8,14 @@ if(CONFIG_SOC_PARLIO_SUPPORTED)
|
||||
"src/parlio_rx.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
list(APPEND srcs "src/parlio_bitscrambler.c")
|
||||
endif()
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
set(priv_requires "")
|
||||
else()
|
||||
set(priv_requires esp_pm esp_driver_gpio esp_mm)
|
||||
set(priv_requires esp_pm esp_driver_gpio esp_mm esp_driver_bitscrambler)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/parlio_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Decorate Parlio TX units with BitScrambler
|
||||
*
|
||||
* @note This function creates a BitScrambler instance and associates it with the Parlio TX unit.
|
||||
* The decorated TX unit will be able to do some pre-process to the user data with the help of BitScrambler
|
||||
* Only can be called before enable the TX unit
|
||||
*
|
||||
* @param[in] tx_unit Parlio TX unit handle
|
||||
* @return
|
||||
* - ESP_OK: Decorate Parlio TX units with BitScrambler success
|
||||
* - ESP_ERR_INVALID_ARG: Failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Failed because the TX unit is already decorated with BitScrambler
|
||||
* - ESP_FAIL: Failed because of other error
|
||||
*/
|
||||
esp_err_t parlio_tx_unit_decorate_bitscrambler(parlio_tx_unit_handle_t tx_unit);
|
||||
|
||||
/**
|
||||
* @brief Remove the BitScrambler decoration from the Parlio TX unit
|
||||
*
|
||||
* @note This function removes the BitScrambler decoration from the Parlio TX unit, restoring the original functionality.
|
||||
* Only can be called before enable the TX unit
|
||||
*
|
||||
* @param[in] tx_unit Parlio TX unit handle
|
||||
* @return
|
||||
* - ESP_OK: Remove the BitScrambler decoration from the Parlio TX unit success
|
||||
* - ESP_ERR_INVALID_ARG: Failed because of invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Failed because the TX unit is not decorated with BitScrambler
|
||||
* - ESP_FAIL: Failed because of other error
|
||||
*/
|
||||
esp_err_t parlio_tx_unit_undecorate_bitscrambler(parlio_tx_unit_handle_t tx_unit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -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
|
||||
*/
|
||||
@ -108,23 +108,6 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t unit);
|
||||
*/
|
||||
esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t unit);
|
||||
|
||||
/**
|
||||
* @brief Type of Parallel IO TX done event data
|
||||
*/
|
||||
typedef struct {
|
||||
} parlio_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of parlio tx event callback
|
||||
* @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit`
|
||||
* @param[in] edata Point to Parallel IO TX event data. The lifecycle of this pointer memory is inside this function,
|
||||
* user should copy it into static memory if used outside this function.
|
||||
* @param[in] user_ctx User registered context, passed from `parlio_tx_unit_register_event_callbacks`
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*parlio_tx_done_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief Group of Parallel IO TX callbacks
|
||||
* @note The callbacks are all running under ISR environment
|
||||
@ -157,6 +140,7 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t idle_value; /*!< The value on the data line when the parallel IO is in idle state */
|
||||
const void *bitscrambler_program; /*!< BitScrambler program binary, NULL if not use BitScrambler */
|
||||
struct {
|
||||
uint32_t queue_nonblocking : 1; /*!< If set, when the transaction queue is full, driver will not block the thread but return directly */
|
||||
uint32_t loop_transmission : 1; /*!< If set, the transmission will be repeated continuously, until the tx_unit is disabled by `parlio_tx_unit_disable` */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -31,6 +31,23 @@ typedef struct parlio_rx_unit_t *parlio_rx_unit_handle_t;
|
||||
*/
|
||||
typedef struct parlio_rx_delimiter_t *parlio_rx_delimiter_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of Parallel IO TX done event data
|
||||
*/
|
||||
typedef struct {
|
||||
} parlio_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of parlio tx event callback
|
||||
* @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit`
|
||||
* @param[in] edata Point to Parallel IO TX event data. The lifecycle of this pointer memory is inside this function,
|
||||
* user should copy it into static memory if used outside this function.
|
||||
* @param[in] user_ctx User registered context, passed from `parlio_tx_unit_register_event_callbacks`
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*parlio_tx_done_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
83
components/esp_driver_parlio/src/parlio_bitscrambler.c
Normal file
83
components/esp_driver_parlio/src/parlio_bitscrambler.c
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "parlio_priv.h"
|
||||
|
||||
static esp_err_t parlio_tx_unit_bs_enable(parlio_tx_unit_handle_t tx_unit, parlio_tx_trans_desc_t *t)
|
||||
{
|
||||
if (t->bitscrambler_program) {
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_enable(tx_unit->bs_handle), TAG, "enable bitscrambler failed");
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_load_program(tx_unit->bs_handle, t->bitscrambler_program), TAG, "load bitscrambler program failed");
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_reset(tx_unit->bs_handle), TAG, "reset bitscrambler failed");
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_start(tx_unit->bs_handle), TAG, "start bitscrambler failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t parlio_tx_unit_bs_disable(parlio_tx_unit_handle_t tx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_disable(tx_unit->bs_handle), TAG, "disable bitscrambler failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_tx_unit_decorate_bitscrambler(parlio_tx_unit_handle_t tx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid TX unit");
|
||||
|
||||
// bitscrambler function is used under Parlio TX ISR context
|
||||
// if the cache is disabled, all functions called by ISR must be in IRAM
|
||||
#if CONFIG_PARLIO_TX_ISR_CACHE_SAFE && !CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM must be enabled");
|
||||
#endif
|
||||
|
||||
// check if already decorated
|
||||
if (tx_unit->bs_handle) {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit already decorated with BitScrambler");
|
||||
}
|
||||
|
||||
// create BitScrambler instance
|
||||
bitscrambler_config_t bs_config = {
|
||||
.dir = BITSCRAMBLER_DIR_TX,
|
||||
.attach_to = SOC_BITSCRAMBLER_ATTACH_PARL_IO,
|
||||
};
|
||||
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_new(&bs_config, &tx_unit->bs_handle), TAG, "create bitscrambler failed");
|
||||
tx_unit->bs_enable_fn = parlio_tx_unit_bs_enable;
|
||||
tx_unit->bs_disable_fn = parlio_tx_unit_bs_disable;
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_INIT);
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit is not in init state");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t parlio_tx_unit_undecorate_bitscrambler(parlio_tx_unit_handle_t tx_unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(tx_unit != NULL, ESP_ERR_INVALID_ARG, TAG, "invalid TX unit");
|
||||
|
||||
// check if already decorated
|
||||
if (!tx_unit->bs_handle) {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit not decorated with BitScrambler yet");
|
||||
}
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
bitscrambler_free(tx_unit->bs_handle);
|
||||
tx_unit->bs_handle = NULL;
|
||||
tx_unit->bs_enable_fn = NULL;
|
||||
tx_unit->bs_disable_fn = NULL;
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_INIT);
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit is not in init state");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
#include "rom/cache.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "driver/parlio_types.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "esp_cache.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_pm.h"
|
||||
@ -122,12 +123,10 @@ typedef enum {
|
||||
} parlio_dir_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_TX_FSM_INIT_WAIT,
|
||||
PARLIO_TX_FSM_INIT,
|
||||
PARLIO_TX_FSM_ENABLE_WAIT,
|
||||
PARLIO_TX_FSM_ENABLE,
|
||||
PARLIO_TX_FSM_RUN_WAIT,
|
||||
PARLIO_TX_FSM_RUN,
|
||||
PARLIO_TX_FSM_WAIT,
|
||||
} parlio_tx_fsm_t;
|
||||
|
||||
typedef struct parlio_unit_t *parlio_unit_base_handle_t;
|
||||
@ -150,6 +149,52 @@ struct parlio_unit_t {
|
||||
parlio_group_t *group; // group handle
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t idle_value; // Parallel IO bus idle value
|
||||
const void *payload; // payload to be transmitted
|
||||
size_t payload_bits; // payload size in bits
|
||||
int dma_link_idx; // index of DMA link list
|
||||
const void *bitscrambler_program; // bitscrambler program binary
|
||||
struct {
|
||||
uint32_t loop_transmission : 1; // whether the transmission is in loop mode
|
||||
} flags; // Extra configuration flags
|
||||
} parlio_tx_trans_desc_t;
|
||||
|
||||
// original function pointer type definition
|
||||
|
||||
typedef esp_err_t (*parlio_tx_bs_enable_fn_t)(parlio_tx_unit_handle_t tx_unit, parlio_tx_trans_desc_t *t);
|
||||
typedef esp_err_t (*parlio_tx_bs_disable_fn_t)(parlio_tx_unit_handle_t tx_unit);
|
||||
|
||||
typedef struct parlio_tx_unit_t {
|
||||
struct parlio_unit_t base; // base unit
|
||||
size_t data_width; // data width
|
||||
intr_handle_t intr; // allocated interrupt handle
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
gdma_channel_handle_t dma_chan; // DMA channel
|
||||
gdma_link_list_handle_t dma_link[PARLIO_DMA_LINK_NUM]; // DMA link list handle
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
portMUX_TYPE spinlock; // prevent resource accessing by user and interrupt concurrently
|
||||
uint32_t out_clk_freq_hz; // output clock frequency
|
||||
parlio_clock_source_t clk_src; // Parallel IO internal clock source
|
||||
size_t max_transfer_bits; // maximum transfer size in bits
|
||||
size_t queue_depth; // size of transaction queue
|
||||
size_t num_trans_inflight; // indicates the number of transactions that are undergoing but not recycled to ready_queue
|
||||
QueueHandle_t trans_queues[PARLIO_TX_QUEUE_MAX]; // transaction queues
|
||||
parlio_tx_trans_desc_t *cur_trans; // points to current transaction
|
||||
uint32_t idle_value_mask; // mask of idle value
|
||||
_Atomic parlio_tx_fsm_t fsm; // Driver FSM state
|
||||
parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done
|
||||
void *user_data; // user data passed to the callback function
|
||||
bitscrambler_handle_t bs_handle; // bitscrambler handle
|
||||
parlio_tx_bs_enable_fn_t bs_enable_fn; // bitscrambler enable function
|
||||
parlio_tx_bs_disable_fn_t bs_disable_fn; // bitscrambler disable function
|
||||
parlio_tx_trans_desc_t trans_desc_pool[]; // transaction descriptor pool
|
||||
} parlio_tx_unit_t;
|
||||
|
||||
/**
|
||||
* @brief Register the rx or tx unit to the parlio group
|
||||
*
|
||||
|
@ -10,43 +10,6 @@
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "parlio_priv.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t idle_value; // Parallel IO bus idle value
|
||||
const void *payload; // payload to be transmitted
|
||||
size_t payload_bits; // payload size in bits
|
||||
int dma_link_idx; // index of DMA link list
|
||||
struct {
|
||||
uint32_t loop_transmission : 1; // whether the transmission is in loop mode
|
||||
} flags; // Extra configuration flags
|
||||
} parlio_tx_trans_desc_t;
|
||||
|
||||
typedef struct parlio_tx_unit_t {
|
||||
struct parlio_unit_t base; // base unit
|
||||
size_t data_width; // data width
|
||||
intr_handle_t intr; // allocated interrupt handle
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
gdma_channel_handle_t dma_chan; // DMA channel
|
||||
gdma_link_list_handle_t dma_link[PARLIO_DMA_LINK_NUM]; // DMA link list handle
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
#if CONFIG_PM_ENABLE
|
||||
char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; // pm lock name
|
||||
#endif
|
||||
portMUX_TYPE spinlock; // prevent resource accessing by user and interrupt concurrently
|
||||
uint32_t out_clk_freq_hz; // output clock frequency
|
||||
parlio_clock_source_t clk_src; // Parallel IO internal clock source
|
||||
size_t max_transfer_bits; // maximum transfer size in bits
|
||||
size_t queue_depth; // size of transaction queue
|
||||
size_t num_trans_inflight; // indicates the number of transactions that are undergoing but not recycled to ready_queue
|
||||
QueueHandle_t trans_queues[PARLIO_TX_QUEUE_MAX]; // transaction queues
|
||||
parlio_tx_trans_desc_t *cur_trans; // points to current transaction
|
||||
uint32_t idle_value_mask; // mask of idle value
|
||||
_Atomic parlio_tx_fsm_t fsm; // Driver FSM state
|
||||
parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done
|
||||
void *user_data; // user data passed to the callback function
|
||||
parlio_tx_trans_desc_t trans_desc_pool[]; // transaction descriptor pool
|
||||
} parlio_tx_unit_t;
|
||||
|
||||
static void parlio_tx_default_isr(void *args);
|
||||
|
||||
static esp_err_t parlio_tx_create_trans_queue(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
|
||||
@ -82,6 +45,9 @@ exit:
|
||||
|
||||
static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
|
||||
{
|
||||
if (tx_unit->bs_handle) {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "please call parlio_tx_unit_undecorate_bitscrambler() before delete the tx unit");
|
||||
}
|
||||
if (tx_unit->intr) {
|
||||
ESP_RETURN_ON_ERROR(esp_intr_free(tx_unit->intr), TAG, "delete interrupt service failed");
|
||||
}
|
||||
@ -526,6 +492,11 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
|
||||
parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
|
||||
if (tx_unit->bs_handle) {
|
||||
// load the bitscrambler program and start it
|
||||
tx_unit->bs_enable_fn(tx_unit, t);
|
||||
}
|
||||
|
||||
gdma_start(tx_unit->dma_chan, gdma_link_get_head_addr(tx_unit->dma_link[t->dma_link_idx]));
|
||||
// wait until the data goes from the DMA to TX unit's FIFO
|
||||
while (parlio_ll_tx_is_ready(hal->regs) == false);
|
||||
@ -541,7 +512,7 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit)
|
||||
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
|
||||
ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_ENABLE_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
// acquire power management lock
|
||||
if (tx_unit->pm_lock) {
|
||||
esp_pm_lock_acquire(tx_unit->pm_lock);
|
||||
@ -560,7 +531,7 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit)
|
||||
// check if we need to start one pending transaction
|
||||
parlio_tx_trans_desc_t *t = NULL;
|
||||
expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
// check if we need to start one transaction
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||
// sanity check
|
||||
@ -581,11 +552,11 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit)
|
||||
bool valid_state = false;
|
||||
// check the supported states, and switch to intermediate state: INIT_WAIT
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_INIT_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
valid_state = true;
|
||||
}
|
||||
expected_fsm = PARLIO_TX_FSM_RUN;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_INIT_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
valid_state = true;
|
||||
assert(tx_unit->cur_trans);
|
||||
// recycle the interrupted transaction
|
||||
@ -693,6 +664,12 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
t->idle_value = config->idle_value & tx_unit->idle_value_mask;
|
||||
t->flags.loop_transmission = config->flags.loop_transmission;
|
||||
|
||||
if (tx_unit->bs_handle) {
|
||||
t->bitscrambler_program = config->bitscrambler_program;
|
||||
} else if (config->bitscrambler_program) {
|
||||
ESP_RETURN_ON_ERROR(ESP_ERR_INVALID_STATE, TAG, "TX unit is not decorated with bitscrambler");
|
||||
}
|
||||
|
||||
// send the transaction descriptor to progress queue
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue");
|
||||
@ -700,7 +677,7 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
|
||||
// check if we need to start one pending transaction
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
// check if we need to start one transaction
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN);
|
||||
@ -733,6 +710,10 @@ static void parlio_tx_default_isr(void *args)
|
||||
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_EOF);
|
||||
parlio_ll_tx_start(hal->regs, false);
|
||||
|
||||
if (tx_unit->bs_handle && tx_unit->cur_trans->bitscrambler_program) {
|
||||
tx_unit->bs_disable_fn(tx_unit);
|
||||
}
|
||||
|
||||
// invoke callback before sending the transaction to complete queue
|
||||
parlio_tx_done_callback_t done_cb = tx_unit->on_trans_done;
|
||||
if (done_cb) {
|
||||
@ -743,7 +724,7 @@ static void parlio_tx_default_isr(void *args)
|
||||
|
||||
parlio_tx_trans_desc_t *trans_desc = NULL;
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_RUN;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_ENABLE_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
trans_desc = tx_unit->cur_trans;
|
||||
// move current finished transaction to the complete queue
|
||||
xQueueSendFromISR(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &trans_desc, &high_task_woken);
|
||||
@ -756,7 +737,7 @@ static void parlio_tx_default_isr(void *args)
|
||||
|
||||
// if the tx unit is till in enable state (i.e. not disabled by user), let's try start the next pending transaction
|
||||
expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_WAIT)) {
|
||||
if (xQueueReceiveFromISR(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &trans_desc, &high_task_woken) == pdTRUE) {
|
||||
// sanity check
|
||||
assert(trans_desc);
|
||||
|
@ -6,9 +6,19 @@ if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED AND CONFIG_PM_ENABLE)
|
||||
list(APPEND srcs "test_parlio_sleep.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
list(APPEND srcs "test_parlio_bitscrambler.c")
|
||||
endif()
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity esp_driver_parlio esp_driver_gpio
|
||||
esp_driver_i2s esp_driver_spi esp_psram
|
||||
esp_driver_bitscrambler
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
target_bitscrambler_add_src("test_parlio_tx_LSB_to_MSB.bsasm")
|
||||
target_bitscrambler_add_src("test_parlio_tx_multiply.bsasm")
|
||||
endif()
|
||||
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "driver/parlio_rx.h"
|
||||
#include "driver/parlio_bitscrambler.h"
|
||||
#include "driver/bitscrambler.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/parlio_ll.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
#include "test_board.h"
|
||||
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx_LSB_to_MSB, "test_parlio_tx_LSB_to_MSB");
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx_multiply, "test_parlio_tx_multiply");
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_rx_done_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
TaskHandle_t task = (TaskHandle_t)user_ctx;
|
||||
vTaskNotifyGiveFromISR(task, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
#define TEST_PAYLOAD_SIZE 64
|
||||
static void test_parlio_bitscrambler(void)
|
||||
{
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t tx_config = {
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT,
|
||||
.data_width = 4,
|
||||
.clk_in_gpio_num = -1, // use internal clock source
|
||||
.valid_gpio_num = TEST_VALID_GPIO,
|
||||
.clk_out_gpio_num = TEST_CLK_GPIO,
|
||||
.data_gpio_nums = {
|
||||
TEST_DATA0_GPIO,
|
||||
TEST_DATA1_GPIO,
|
||||
TEST_DATA2_GPIO,
|
||||
TEST_DATA3_GPIO,
|
||||
},
|
||||
.output_clk_freq_hz = 1 * 1000 * 1000,
|
||||
.trans_queue_depth = 8,
|
||||
.max_transfer_size = 128,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
};
|
||||
|
||||
parlio_rx_unit_handle_t rx_unit = NULL;
|
||||
parlio_rx_unit_config_t rx_config = {
|
||||
.trans_queue_depth = 10,
|
||||
.max_recv_size = 1024,
|
||||
.data_width = 4,
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT,
|
||||
.ext_clk_freq_hz = 0,
|
||||
.clk_in_gpio_num = -1,
|
||||
.exp_clk_freq_hz = 1 * 1000 * 1000,
|
||||
.clk_out_gpio_num = -1,
|
||||
.valid_gpio_num = TEST_VALID_GPIO,
|
||||
.data_gpio_nums = {
|
||||
TEST_DATA0_GPIO,
|
||||
TEST_DATA1_GPIO,
|
||||
TEST_DATA2_GPIO,
|
||||
TEST_DATA3_GPIO,
|
||||
},
|
||||
.flags = {
|
||||
.clk_gate_en = false,
|
||||
}
|
||||
};
|
||||
|
||||
printf("install parlio unit\r\n");
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&tx_config, &tx_unit));
|
||||
TEST_ESP_OK(parlio_new_rx_unit(&rx_config, &rx_unit));
|
||||
|
||||
printf("decorate tx unit with bitscrambler\r\n");
|
||||
TEST_ESP_OK(parlio_tx_unit_decorate_bitscrambler(tx_unit));
|
||||
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00,
|
||||
.bitscrambler_program = bitscrambler_program_test_tx_LSB_to_MSB,
|
||||
};
|
||||
uint8_t tx_payload[TEST_PAYLOAD_SIZE] = {0};
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
tx_payload[i] = i;
|
||||
}
|
||||
|
||||
parlio_rx_delimiter_handle_t deli = NULL;
|
||||
parlio_rx_level_delimiter_config_t lvl_deli_cfg = {
|
||||
.valid_sig_line_id = PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // data in LSB mode is adjusted by bitscrambler, so we use MSB mode for receiving
|
||||
.eof_data_len = TEST_PAYLOAD_SIZE,
|
||||
.timeout_ticks = 0,
|
||||
.flags = {
|
||||
.active_low_en = 0,
|
||||
},
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli));
|
||||
printf("register receive_done event callback\r\n");
|
||||
parlio_rx_event_callbacks_t rx_cbs = {
|
||||
.on_receive_done = test_parlio_rx_done_callback,
|
||||
};
|
||||
TEST_ESP_OK(parlio_rx_unit_register_event_callbacks(rx_unit, &rx_cbs, xTaskGetCurrentTaskHandle()));
|
||||
|
||||
parlio_receive_config_t recv_config = {
|
||||
.delimiter = deli,
|
||||
.flags.partial_rx_en = false,
|
||||
};
|
||||
__attribute__((aligned(TEST_PAYLOAD_SIZE))) uint8_t rx_payload[TEST_PAYLOAD_SIZE] = {0};
|
||||
|
||||
// Tx in LSB mode
|
||||
// Tx Bitscrambler turn LSB to MSB
|
||||
// Rx in MSB mode
|
||||
printf("enable parlio and transmit\r\n");
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, 1));
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, rx_payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, tx_payload, TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i], rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
// we don't use bitscrambler to adjust the bit order in the following tests, so we keep LSB order to receive the original data
|
||||
lvl_deli_cfg.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB;
|
||||
TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli));
|
||||
|
||||
// not use bitscrambler
|
||||
transmit_config.bitscrambler_program = NULL;
|
||||
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, rx_payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, tx_payload, TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i], rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// use another bitscrambler program
|
||||
// Tx Bitscrambler multiply the tx payload by 2
|
||||
transmit_config.bitscrambler_program = bitscrambler_program_test_tx_multiply;
|
||||
|
||||
TEST_ESP_OK(parlio_rx_unit_receive(rx_unit, rx_payload, TEST_PAYLOAD_SIZE, &recv_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, tx_payload, TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i] * 2, rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, parlio_del_tx_unit(tx_unit));
|
||||
TEST_ESP_OK(parlio_tx_unit_undecorate_bitscrambler(tx_unit));
|
||||
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
|
||||
TEST_ESP_OK(parlio_rx_unit_disable(rx_unit));
|
||||
TEST_ESP_OK(parlio_del_rx_delimiter(deli));
|
||||
TEST_ESP_OK(parlio_del_rx_unit(rx_unit));
|
||||
}
|
||||
|
||||
TEST_CASE("parlio_tx_bitscrambler_test", "[parlio_bitscrambler]")
|
||||
{
|
||||
test_parlio_bitscrambler();
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
|
||||
cfg prefetch true # enable data prefetch
|
||||
cfg eof_on upstream # set EOF on upstream
|
||||
cfg trailing_bytes 9 # due to prefetch is enable, upstream is 8 bytes ahead of downstream
|
||||
|
||||
loop:
|
||||
set 0..3 4..7,
|
||||
set 4..7 0..3,
|
||||
write 8,
|
||||
read 8,
|
||||
jmp loop
|
@ -0,0 +1,14 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
|
||||
cfg prefetch true # enable data prefetch
|
||||
cfg eof_on upstream # set EOF on upstream
|
||||
cfg trailing_bytes 9 # due to prefetch is enable, upstream is 8 bytes ahead of downstream
|
||||
|
||||
loop:
|
||||
set 1..7 0..6,
|
||||
set 0 L,
|
||||
write 8,
|
||||
read 8,
|
||||
jmp loop
|
@ -2,6 +2,7 @@ CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||
CONFIG_PARLIO_TX_ISR_CACHE_SAFE=y
|
||||
CONFIG_PARLIO_RX_ISR_CACHE_SAFE=y
|
||||
CONFIG_BITSCRAMBLER_CTRL_FUNC_IN_IRAM=y
|
||||
# place non-ISR FreeRTOS functions in Flash
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
|
@ -106,6 +106,7 @@ static size_t rmt_encode_bs(rmt_encoder_t *encoder, rmt_channel_handle_t channel
|
||||
static esp_err_t rmt_del_bs_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_bs_encoder_t *bs_encoder = __containerof(encoder, rmt_bs_encoder_t, base);
|
||||
ESP_RETURN_ON_ERROR(bitscrambler_disable(bs_encoder->bs), TAG, "disable bitscrambler failed");
|
||||
bitscrambler_free(bs_encoder->bs);
|
||||
free(bs_encoder);
|
||||
return ESP_OK;
|
||||
@ -129,7 +130,7 @@ esp_err_t rmt_new_bitscrambler_encoder(const rmt_bs_encoder_config_t *config, rm
|
||||
.attach_to = SOC_BITSCRAMBLER_ATTACH_RMT,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_new(&bs_config, &encoder->bs), err, TAG, "create bitscrambler failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_enable(encoder->bs), err, TAG, "enable bitscrambler failed");
|
||||
// load the bitscrambler program
|
||||
ESP_GOTO_ON_ERROR(bitscrambler_load_program(encoder->bs, config->program_bin), err, TAG, "load bitscrambler program failed");
|
||||
|
||||
|
Reference in New Issue
Block a user