mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
Chip/support esp32c61 v5.5
This commit is contained in:
@@ -116,6 +116,7 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t unit);
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_tx_done_callback_t on_trans_done; /*!< Event callback, invoked when one transmission is finished */
|
||||
parlio_tx_buffer_switched_callback_t on_buffer_switched; /*!< Event callback, invoked when the buffer is switched in loop transmission */
|
||||
} parlio_tx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,14 @@ typedef struct parlio_rx_delimiter_t *parlio_rx_delimiter_handle_t;
|
||||
typedef struct {
|
||||
} parlio_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Type of Parallel IO TX buffer switched event data
|
||||
*/
|
||||
typedef struct {
|
||||
void *old_buffer_addr; /*!< Address of the previous buffer used before switching */
|
||||
void *new_buffer_addr; /*!< Address of the new buffer switched to */
|
||||
} parlio_tx_buffer_switched_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`
|
||||
@@ -48,6 +56,17 @@ typedef struct {
|
||||
*/
|
||||
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 Prototype of parlio tx buffer switched 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_buffer_switched_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_buffer_switched_event_data_t *edata, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,10 @@ entries:
|
||||
parlio_tx: parlio_tx_default_isr (noflash)
|
||||
parlio_tx: parlio_tx_do_transaction (noflash)
|
||||
parlio_tx: parlio_mount_buffer (noflash)
|
||||
|
||||
if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y:
|
||||
parlio_tx: parlio_tx_gdma_eof_callback (noflash)
|
||||
|
||||
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:
|
||||
parlio_rx: parlio_rx_default_eof_callback (noflash)
|
||||
parlio_rx: parlio_rx_default_desc_done_callback (noflash)
|
||||
@@ -18,3 +22,6 @@ entries:
|
||||
gdma_link: gdma_link_mount_buffers (noflash)
|
||||
gdma_link: gdma_link_concat (noflash)
|
||||
gdma_link: gdma_link_get_head_addr (noflash)
|
||||
|
||||
if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y:
|
||||
gdma_link: gdma_link_get_buffer (noflash)
|
||||
|
||||
@@ -168,6 +168,10 @@ 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
|
||||
gpio_num_t data_gpio_nums[SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH]; // data GPIO numbers
|
||||
gpio_num_t valid_gpio_num; // valid signal GPIO number
|
||||
gpio_num_t clk_out_gpio_num; // output clock GPIO number
|
||||
gpio_num_t clk_in_gpio_num; // input clock GPIO number
|
||||
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
|
||||
@@ -187,7 +191,9 @@ typedef struct parlio_tx_unit_t {
|
||||
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
|
||||
_Atomic bool buffer_need_switch; // whether the buffer need to be switched
|
||||
parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done
|
||||
parlio_tx_buffer_switched_callback_t on_buffer_switched; // callback function when the buffer is switched in loop transmission
|
||||
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
|
||||
|
||||
@@ -491,6 +491,7 @@ static esp_err_t parlio_select_periph_clock(parlio_rx_unit_handle_t rx_unit, con
|
||||
{
|
||||
parlio_hal_context_t *hal = &rx_unit->base.group->hal;
|
||||
parlio_clock_source_t clk_src = config->clk_src;
|
||||
rx_unit->clk_src = clk_src;
|
||||
uint32_t src_freq_hz = 0;
|
||||
uint32_t exp_freq_hz = 0;
|
||||
hal_utils_clk_div_t clk_div = {
|
||||
@@ -536,14 +537,12 @@ static esp_err_t parlio_select_periph_clock(parlio_rx_unit_handle_t rx_unit, con
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true);
|
||||
/* Set clock configuration */
|
||||
PARLIO_CLOCK_SRC_ATOMIC() {
|
||||
parlio_ll_rx_set_clock_source(hal->regs, clk_src);
|
||||
parlio_ll_rx_set_clock_div(hal->regs, &clk_div);
|
||||
}
|
||||
|
||||
rx_unit->clk_src = clk_src;
|
||||
/* warning if precision lost due to division */
|
||||
if ((clk_src != PARLIO_CLK_SRC_EXTERNAL) &&
|
||||
(config->exp_clk_freq_hz != rx_unit->cfg.exp_clk_freq_hz)) {
|
||||
@@ -595,6 +594,9 @@ static esp_err_t parlio_destroy_rx_unit(parlio_rx_unit_handle_t rx_unit)
|
||||
if (rx_unit->base.group) {
|
||||
parlio_unregister_unit_from_group(&rx_unit->base);
|
||||
}
|
||||
if (rx_unit->clk_src) {
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)rx_unit->clk_src, false), TAG, "clock source disable failed");
|
||||
}
|
||||
/* Free the RX unit */
|
||||
free(rx_unit);
|
||||
return ESP_OK;
|
||||
@@ -661,6 +663,7 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un
|
||||
}
|
||||
parlio_ll_rx_start(hal->regs, false);
|
||||
/* parlio_ll_clock_source_t and parlio_clock_source_t are binary compatible if the clock source is from internal */
|
||||
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)(config->clk_src), true), err, TAG, "clock source enable failed");
|
||||
ESP_GOTO_ON_ERROR(parlio_select_periph_clock(unit, config), err, TAG, "set clock source failed");
|
||||
/* Set the data width */
|
||||
parlio_ll_rx_set_bus_width(hal->regs, config->data_width);
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "parlio_priv.h"
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||
#endif
|
||||
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)
|
||||
@@ -72,6 +75,9 @@ static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
|
||||
ESP_RETURN_ON_ERROR(gdma_del_link_list(tx_unit->dma_link[i]), TAG, "delete dma link list failed");
|
||||
}
|
||||
}
|
||||
if (tx_unit->clk_src) {
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)tx_unit->clk_src, false), TAG, "clock source disable failed");
|
||||
}
|
||||
free(tx_unit);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -94,6 +100,7 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
|
||||
// connect the signal to the GPIO by matrix, it will also enable the output path properly
|
||||
esp_rom_gpio_connect_out_signal(config->data_gpio_nums[i],
|
||||
parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[i], false, false);
|
||||
tx_unit->data_gpio_nums[i] = config->data_gpio_nums[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +125,7 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
|
||||
parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG],
|
||||
config->flags.invert_valid_out, false);
|
||||
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
|
||||
tx_unit->valid_gpio_num = config->valid_gpio_num;
|
||||
}
|
||||
if (config->clk_out_gpio_num >= 0) {
|
||||
gpio_func_sel(config->clk_out_gpio_num, PIN_FUNC_GPIO);
|
||||
@@ -130,6 +138,7 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
|
||||
// connect the signal to the GPIO by matrix, it will also enable the output path properly
|
||||
esp_rom_gpio_connect_out_signal(config->clk_out_gpio_num,
|
||||
parlio_periph_signals.groups[group_id].tx_units[unit_id].clk_out_sig, false, false);
|
||||
tx_unit->clk_out_gpio_num = config->clk_out_gpio_num;
|
||||
}
|
||||
if (config->clk_in_gpio_num >= 0) {
|
||||
gpio_input_enable(config->clk_in_gpio_num);
|
||||
@@ -141,6 +150,7 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
|
||||
|
||||
esp_rom_gpio_connect_in_signal(config->clk_in_gpio_num,
|
||||
parlio_periph_signals.groups[group_id].tx_units[unit_id].clk_in_sig, false);
|
||||
tx_unit->clk_in_gpio_num = config->clk_in_gpio_num;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -202,6 +212,7 @@ static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const par
|
||||
{
|
||||
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
|
||||
parlio_clock_source_t clk_src = config->clk_src;
|
||||
tx_unit->clk_src = clk_src;
|
||||
if (config->clk_in_gpio_num >= 0 && clk_src != PARLIO_CLK_SRC_EXTERNAL) {
|
||||
ESP_LOGW(TAG, "input clock GPIO is set, use external clk src");
|
||||
clk_src = PARLIO_CLK_SRC_EXTERNAL;
|
||||
@@ -244,7 +255,6 @@ static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const par
|
||||
#else
|
||||
tx_unit->out_clk_freq_hz = hal_utils_calc_clk_div_integer(&clk_info, &clk_div.integer);
|
||||
#endif
|
||||
esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true);
|
||||
PARLIO_CLOCK_SRC_ATOMIC() {
|
||||
// turn on the tx module clock to sync the clock divider configuration because of the CDC (Cross Domain Crossing)
|
||||
parlio_ll_tx_enable_clock(hal->regs, true);
|
||||
@@ -256,8 +266,6 @@ static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const par
|
||||
if (tx_unit->out_clk_freq_hz != config->output_clk_freq_hz) {
|
||||
ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32, tx_unit->out_clk_freq_hz);
|
||||
}
|
||||
tx_unit->clk_src = clk_src;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -305,6 +313,7 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
parlio_group_t *group = unit->base.group;
|
||||
parlio_hal_context_t *hal = &group->hal;
|
||||
// select the clock source
|
||||
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)(config->clk_src), true), err, TAG, "clock source enable failed");
|
||||
ESP_GOTO_ON_ERROR(parlio_select_periph_clock(unit, config), err, TAG, "set clock source failed");
|
||||
|
||||
// install interrupt service
|
||||
@@ -357,9 +366,6 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
// set sample clock edge
|
||||
parlio_ll_tx_set_sample_clock_edge(hal->regs, config->sample_edge);
|
||||
|
||||
// In default, use DATA LEN EOF as the Parlio TX EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
|
||||
// clear any pending interrupt
|
||||
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK);
|
||||
|
||||
@@ -374,6 +380,7 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
|
||||
portMUX_INITIALIZE(&unit->spinlock);
|
||||
atomic_init(&unit->fsm, PARLIO_TX_FSM_INIT);
|
||||
atomic_init(&unit->buffer_need_switch, false);
|
||||
// return TX unit handle
|
||||
*ret_unit = unit;
|
||||
ESP_LOGD(TAG, "new tx unit(%d,%d) at %p, out clk=%"PRIu32"Hz, queue_depth=%zu, idle_mask=%"PRIx32,
|
||||
@@ -392,6 +399,22 @@ esp_err_t parlio_del_tx_unit(parlio_tx_unit_handle_t unit)
|
||||
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&unit->fsm) == PARLIO_TX_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
|
||||
ESP_LOGD(TAG, "del tx unit(%d,%d)", unit->base.group->group_id, unit->base.unit_id);
|
||||
for (size_t i = 0; i < unit->data_width; i++) {
|
||||
if (unit->data_gpio_nums[i] >= 0) {
|
||||
gpio_output_disable(unit->data_gpio_nums[i]);
|
||||
}
|
||||
}
|
||||
if (unit->valid_gpio_num >= 0) {
|
||||
gpio_output_disable(unit->valid_gpio_num);
|
||||
}
|
||||
if (unit->clk_out_gpio_num >= 0) {
|
||||
gpio_output_disable(unit->clk_out_gpio_num);
|
||||
}
|
||||
if (unit->clk_in_gpio_num >= 0) {
|
||||
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT,
|
||||
parlio_periph_signals.groups[unit->base.group->group_id].tx_units[unit->base.unit_id].clk_in_sig,
|
||||
false);
|
||||
}
|
||||
return parlio_destroy_tx_unit(unit);
|
||||
}
|
||||
|
||||
@@ -420,12 +443,30 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
|
||||
if (cbs->on_trans_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_trans_done callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_buffer_switched) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_buffer_switched), ESP_ERR_INVALID_ARG, TAG, "on_buffer_switched callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cbs->on_buffer_switched) {
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
// workaround for DIG-559
|
||||
ESP_RETURN_ON_FALSE(tx_unit->data_width > 1, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported for 1-bit data width");
|
||||
|
||||
gdma_tx_event_callbacks_t gdma_cbs = {
|
||||
.on_trans_eof = parlio_tx_gdma_eof_callback,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(tx_unit->dma_chan, &gdma_cbs, tx_unit), TAG, "install DMA callback failed");
|
||||
#else
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
tx_unit->on_trans_done = cbs->on_trans_done;
|
||||
tx_unit->on_buffer_switched = cbs->on_buffer_switched;
|
||||
tx_unit->user_data = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -437,8 +478,8 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_
|
||||
.buffer = (void *)t->payload,
|
||||
.length = (t->payload_bits + 7) / 8,
|
||||
.flags = {
|
||||
// if transmission is loop, we don't need to generate the EOF, as well as the final mark
|
||||
.mark_eof = !t->flags.loop_transmission,
|
||||
// if transmission is loop, we don't need to generate the EOF for 1-bit data width, DIG-559
|
||||
.mark_eof = tx_unit->data_width == 1 ? !t->flags.loop_transmission : true,
|
||||
.mark_final = !t->flags.loop_transmission,
|
||||
}
|
||||
};
|
||||
@@ -456,12 +497,6 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_
|
||||
static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t)
|
||||
{
|
||||
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
|
||||
|
||||
if (t->flags.loop_transmission) {
|
||||
// Once a loop transmission is started, it cannot be stopped until it is disabled
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
}
|
||||
|
||||
tx_unit->cur_trans = t;
|
||||
|
||||
// If the external clock is a non-free-running clock, it needs to be switched to the internal free-running clock first.
|
||||
@@ -490,7 +525,30 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
|
||||
// reset tx fifo after disabling tx core clk to avoid unexpected rempty interrupt
|
||||
parlio_ll_tx_reset_fifo(hal->regs);
|
||||
parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
|
||||
// set EOF condition
|
||||
if (t->flags.loop_transmission) {
|
||||
if (tx_unit->data_width == 1) {
|
||||
// for 1-bit data width, we need to set the EOF condition to DMA EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
} else {
|
||||
// for other data widths, we still use the data length EOF condition,
|
||||
// but let the `bit counter` + `data width` for each cycle is never equal to the configured bit lens.
|
||||
// Thus, we can skip the exact match, prevents EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, 0x01);
|
||||
}
|
||||
} else {
|
||||
// non-loop transmission
|
||||
#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
// for DMA EOF supported target, we need to set the EOF condition to DMA EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
#else
|
||||
// for DMA EOF not supported target, we need to set the bit length to the configured bit lens
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
}
|
||||
|
||||
if (tx_unit->bs_handle) {
|
||||
// load the bitscrambler program and start it
|
||||
@@ -583,10 +641,6 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit)
|
||||
parlio_ll_tx_start(hal->regs, false);
|
||||
parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_MASK, false);
|
||||
|
||||
// Once a loop teansmission transaction is started, it can only be stopped in disable function
|
||||
// change the EOF condition to be the data length, so the EOF will be triggered normally
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
|
||||
// release power management lock
|
||||
if (tx_unit->pm_lock) {
|
||||
esp_pm_lock_release(tx_unit->pm_lock);
|
||||
@@ -609,18 +663,20 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
if (config->flags.loop_transmission) {
|
||||
ESP_RETURN_ON_FALSE(parlio_ll_tx_support_dma_eof(NULL), ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported by this chip revision");
|
||||
ESP_RETURN_ON_FALSE(parlio_ll_tx_support_dma_eof(NULL) || tx_unit->data_width > 1, ESP_ERR_NOT_SUPPORTED, TAG,
|
||||
"1-bit data width loop transmission is not supported by this chip revision");
|
||||
}
|
||||
#else
|
||||
ESP_RETURN_ON_FALSE(config->flags.loop_transmission == false, ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported on this chip");
|
||||
#endif
|
||||
|
||||
// check the max payload size if it's not a loop transmission
|
||||
// workaround for EOF limitation, when DMA EOF issue is fixed, we can remove this check
|
||||
#if !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
// check the max payload size if it's not a loop transmission and the DMA EOF is not supported
|
||||
if (!config->flags.loop_transmission) {
|
||||
ESP_RETURN_ON_FALSE(tx_unit->max_transfer_bits <= PARLIO_LL_TX_MAX_BITS_PER_FRAME,
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid transfer size");
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid transfer size, max transfer size should be less than %d", PARLIO_LL_TX_MAX_BITS_PER_FRAME / 8);
|
||||
}
|
||||
#endif // !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
|
||||
size_t cache_line_size = 0;
|
||||
size_t alignment = 0;
|
||||
@@ -643,6 +699,7 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
tx_unit->cur_trans->payload = payload;
|
||||
tx_unit->cur_trans->payload_bits = payload_bits;
|
||||
parlio_mount_buffer(tx_unit, tx_unit->cur_trans);
|
||||
atomic_store(&tx_unit->buffer_need_switch, true);
|
||||
} else {
|
||||
TickType_t queue_wait_ticks = portMAX_DELAY;
|
||||
if (config->flags.queue_nonblocking) {
|
||||
@@ -691,6 +748,31 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *) user_data;
|
||||
bool need_yield = false;
|
||||
bool expected_state = true;
|
||||
// invoke callback to notify the application
|
||||
parlio_tx_buffer_switched_callback_t on_buffer_switched = tx_unit->on_buffer_switched;
|
||||
if (on_buffer_switched) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->buffer_need_switch, &expected_state, false)) {
|
||||
parlio_tx_buffer_switched_event_data_t edata = {
|
||||
// we use 2 dma links to do the buffer switch in loop transmission
|
||||
.old_buffer_addr = gdma_link_get_buffer(tx_unit->dma_link[1 - tx_unit->cur_trans->dma_link_idx], 0),
|
||||
.new_buffer_addr = gdma_link_get_buffer(tx_unit->dma_link[tx_unit->cur_trans->dma_link_idx], 0),
|
||||
};
|
||||
if (on_buffer_switched(tx_unit, &edata, tx_unit->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
|
||||
static void parlio_tx_default_isr(void *args)
|
||||
{
|
||||
parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *)args;
|
||||
|
||||
@@ -10,6 +10,10 @@ if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
|
||||
list(APPEND srcs "test_parlio_bitscrambler.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_PARLIO_TX_ISR_CACHE_SAFE)
|
||||
list(APPEND srcs "test_parlio_tx_cache_safe.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}
|
||||
@@ -21,4 +25,6 @@ idf_component_register(SRCS ${srcs}
|
||||
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")
|
||||
target_bitscrambler_add_src("test_parlio_tx_in8_out32.bsasm")
|
||||
target_bitscrambler_add_src("test_parlio_tx_in32_out8.bsasm")
|
||||
endif()
|
||||
|
||||
@@ -21,13 +21,21 @@
|
||||
|
||||
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");
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx_in8_out32, "test_parlio_tx_in8_out32");
|
||||
BITSCRAMBLER_PROGRAM(bitscrambler_program_test_tx_in32_out8, "test_parlio_tx_in32_out8");
|
||||
|
||||
typedef struct {
|
||||
TaskHandle_t task;
|
||||
size_t recv_bytes;
|
||||
} test_parlio_bitscrambler_rx_ctx_t;
|
||||
|
||||
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);
|
||||
test_parlio_bitscrambler_rx_ctx_t *ctx = (test_parlio_bitscrambler_rx_ctx_t *)user_ctx;
|
||||
ctx->recv_bytes = edata->recv_bytes;
|
||||
vTaskNotifyGiveFromISR(ctx->task, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
@@ -105,10 +113,13 @@ static void test_parlio_bitscrambler(void)
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_rx_level_delimiter(&lvl_deli_cfg, &deli));
|
||||
printf("register receive_done event callback\r\n");
|
||||
test_parlio_bitscrambler_rx_ctx_t rx_ctx = {
|
||||
.task = xTaskGetCurrentTaskHandle(),
|
||||
};
|
||||
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()));
|
||||
TEST_ESP_OK(parlio_rx_unit_register_event_callbacks(rx_unit, &rx_cbs, &rx_ctx));
|
||||
|
||||
parlio_receive_config_t recv_config = {
|
||||
.delimiter = deli,
|
||||
@@ -121,11 +132,12 @@ static void test_parlio_bitscrambler(void)
|
||||
// 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_enable(rx_unit, true));
|
||||
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)));
|
||||
TEST_ASSERT_EQUAL(TEST_PAYLOAD_SIZE, rx_ctx.recv_bytes);
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
@@ -148,6 +160,7 @@ static void test_parlio_bitscrambler(void)
|
||||
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)));
|
||||
TEST_ASSERT_EQUAL(TEST_PAYLOAD_SIZE, rx_ctx.recv_bytes);
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
@@ -166,6 +179,7 @@ static void test_parlio_bitscrambler(void)
|
||||
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)));
|
||||
TEST_ASSERT_EQUAL(TEST_PAYLOAD_SIZE, rx_ctx.recv_bytes);
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
@@ -187,3 +201,156 @@ TEST_CASE("parlio_tx_bitscrambler_test", "[parlio_bitscrambler]")
|
||||
{
|
||||
test_parlio_bitscrambler();
|
||||
}
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
static void test_parlio_bitscrambler_different_input_output_sizes(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 = 256,
|
||||
.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_in32_out8,
|
||||
};
|
||||
|
||||
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_LSB,
|
||||
.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");
|
||||
test_parlio_bitscrambler_rx_ctx_t rx_ctx = {
|
||||
.task = xTaskGetCurrentTaskHandle(),
|
||||
};
|
||||
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, &rx_ctx));
|
||||
|
||||
parlio_receive_config_t recv_config = {
|
||||
.delimiter = deli,
|
||||
.flags.partial_rx_en = false,
|
||||
};
|
||||
|
||||
uint8_t tx_payload[TEST_PAYLOAD_SIZE * 4] = {0};
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE * 4; i++) {
|
||||
tx_payload[i] = i;
|
||||
}
|
||||
|
||||
__attribute__((aligned(TEST_PAYLOAD_SIZE))) uint8_t rx_payload[TEST_PAYLOAD_SIZE] = {0};
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
TEST_ESP_OK(parlio_rx_unit_enable(rx_unit, true));
|
||||
|
||||
// test input size is larger than output size
|
||||
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, 4 * TEST_PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_EQUAL(TEST_PAYLOAD_SIZE, rx_ctx.recv_bytes);
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE; i++) {
|
||||
printf("%.3d ", (rx_payload[i]));
|
||||
TEST_ASSERT_EQUAL(tx_payload[i * 4 + 3], rx_payload[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// test input size is smaller than output size
|
||||
transmit_config.bitscrambler_program = bitscrambler_program_test_tx_in8_out32;
|
||||
|
||||
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 / 4) * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_EQUAL(TEST_PAYLOAD_SIZE, rx_ctx.recv_bytes);
|
||||
|
||||
for (int i = 0; i < TEST_PAYLOAD_SIZE / 4; i++) {
|
||||
uint32_t *test_value = (uint32_t *)&rx_payload[i * 4];
|
||||
printf("0x%lx ", *test_value);
|
||||
switch (i % 4) {
|
||||
case 0:
|
||||
TEST_ASSERT_EQUAL(0x11111, *test_value);
|
||||
break;
|
||||
case 1:
|
||||
TEST_ASSERT_EQUAL(0x22222, *test_value);
|
||||
break;
|
||||
case 2:
|
||||
TEST_ASSERT_EQUAL(0x33333, *test_value);
|
||||
break;
|
||||
case 3:
|
||||
TEST_ASSERT_EQUAL(0x00000, *test_value);
|
||||
break;
|
||||
}
|
||||
if ((i + 1) % 4 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_disable(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_different_input_output_sizes_test", "[parlio_bitscrambler]")
|
||||
{
|
||||
test_parlio_bitscrambler_different_input_output_sizes();
|
||||
}
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
|
||||
@@ -518,6 +518,22 @@ TEST_CASE("parallel tx unit use external non-free running clock", "[parlio_tx]")
|
||||
};
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
typedef struct {
|
||||
uint32_t switch_count;
|
||||
void *old_buffer_addr[5];
|
||||
void *new_buffer_addr[5];
|
||||
} test_parlio_tx_buffer_switched_context_t;
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_tx_buffer_switched_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_buffer_switched_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
test_parlio_tx_buffer_switched_context_t *context = (test_parlio_tx_buffer_switched_context_t *)user_ctx;
|
||||
context->old_buffer_addr[context->switch_count] = edata->old_buffer_addr;
|
||||
context->new_buffer_addr[context->switch_count] = edata->new_buffer_addr;
|
||||
context->switch_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
{
|
||||
printf("install parlio tx unit\r\n");
|
||||
@@ -545,6 +561,16 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
|
||||
printf("register trans_done event callback\r\n");
|
||||
parlio_tx_event_callbacks_t cbs = {
|
||||
.on_buffer_switched = test_parlio_tx_buffer_switched_callback,
|
||||
};
|
||||
test_parlio_tx_buffer_switched_context_t context = {
|
||||
.switch_count = 0,
|
||||
};
|
||||
TEST_ESP_OK(parlio_tx_unit_register_event_callbacks(tx_unit, &cbs, &context));
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
printf("send packets and check event is fired\r\n");
|
||||
@@ -560,37 +586,48 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
payload_loop2[i] = 255 - i;
|
||||
payload_oneshot[i] = i * 2 + 1;
|
||||
}
|
||||
if (parlio_ll_tx_support_dma_eof(NULL)) { // for some chips, only support in particular ECO version
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
int lopp_count = 3;
|
||||
while (lopp_count--) {
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// Should be sent after the previous frame has been completely sent
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop2, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
transmit_config.flags.loop_transmission = false;
|
||||
// should be pending in queue
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_oneshot, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
// there is a oneshot trans in queue, should also be pending in queue
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
int lopp_count = 3;
|
||||
while (lopp_count--) {
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, parlio_tx_unit_wait_all_done(tx_unit, 50));
|
||||
|
||||
// stop infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
// We should see 1 oneshot frame and 1 loop transmission (both pending in queue)
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// stop the second infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
} else {
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
// Should be sent after the previous frame has been completely sent
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop2, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
transmit_config.flags.loop_transmission = false;
|
||||
// should be pending in queue
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_oneshot, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
// there is a oneshot trans in queue, should also be pending in queue
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, parlio_tx_unit_wait_all_done(tx_unit, 50));
|
||||
|
||||
// stop infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
// We should see 1 oneshot frame and 1 loop transmission (both pending in queue)
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// stop the second infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
|
||||
// total 5 switch events
|
||||
TEST_ASSERT_EQUAL(5, context.switch_count);
|
||||
for (int i = 0; i < context.switch_count; i++) {
|
||||
void *old_buffer_addr = context.old_buffer_addr[i];
|
||||
void *new_buffer_addr = context.new_buffer_addr[i];
|
||||
if (i % 2 == 0) {
|
||||
TEST_ASSERT_EQUAL(payload_loop1, old_buffer_addr);
|
||||
TEST_ASSERT_EQUAL(payload_loop2, new_buffer_addr);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL(payload_loop2, old_buffer_addr);
|
||||
TEST_ASSERT_EQUAL(payload_loop1, new_buffer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
|
||||
@@ -598,3 +635,51 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
|
||||
}
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
TEST_CASE("parlio_tx can transmit buffer larger than max_size decided by datalen_eof", "[parlio_tx]")
|
||||
{
|
||||
printf("install parlio tx unit\r\n");
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t 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, // generate the valid signal
|
||||
.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 = 10 * 1000 * 1000,
|
||||
.trans_queue_depth = 1,
|
||||
.max_transfer_size = 100 * 1024,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
.flags.clk_gate_en = true,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
const size_t buffer_size = 100 * 1024; // 100KB, larger than the 65535 bytes limit
|
||||
uint8_t *buffer = heap_caps_malloc(buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(buffer);
|
||||
for (int i = 0; i < buffer_size; i++) {
|
||||
buffer[i] = i;
|
||||
}
|
||||
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, buffer, buffer_size * 8, &transmit_config));
|
||||
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
|
||||
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
|
||||
free(buffer);
|
||||
}
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "unity_test_utils.h"
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/parlio_ll.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
#include "test_board.h"
|
||||
|
||||
static void IRAM_ATTR test_delay_post_cache_disable(void *args)
|
||||
{
|
||||
esp_rom_delay_us(100000);
|
||||
}
|
||||
|
||||
static void test_parlio_tx_cache_safe(void)
|
||||
{
|
||||
printf("install parlio tx unit\r\n");
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t config = {
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT,
|
||||
.data_width = 1,
|
||||
.clk_in_gpio_num = -1, // use internal clock source
|
||||
.valid_gpio_num = TEST_VALID_GPIO, // generate the valid signal
|
||||
.clk_out_gpio_num = TEST_CLK_GPIO,
|
||||
.data_gpio_nums = {
|
||||
TEST_DATA0_GPIO,
|
||||
},
|
||||
.output_clk_freq_hz = 10 * 1000 * 1000,
|
||||
.trans_queue_depth = 4,
|
||||
.max_transfer_size = 65535,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
const size_t buffer_size = 160 * 1000;
|
||||
const size_t chunk_size = buffer_size / 4; // 40KB per trunk
|
||||
uint8_t *buffer = heap_caps_malloc(buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
|
||||
TEST_ASSERT_NOT_NULL(buffer);
|
||||
for (int i = 0; i < buffer_size; i++) {
|
||||
buffer[i] = i;
|
||||
}
|
||||
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00,
|
||||
};
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, buffer + (i % 4) * chunk_size, chunk_size * 8, &transmit_config));
|
||||
}
|
||||
// during the cache disabled period, the parlio tx unit can be scheduled as well
|
||||
unity_utils_run_cache_disable_stub(test_delay_post_cache_disable, NULL);
|
||||
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
|
||||
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
|
||||
free(buffer);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("parlio tx works with cache disabled", "[parlio]")
|
||||
{
|
||||
test_parlio_tx_cache_safe();
|
||||
}
|
||||
@@ -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..7 24..31,
|
||||
|
||||
write 8,
|
||||
read 32,
|
||||
jmp loop
|
||||
@@ -0,0 +1,21 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
|
||||
cfg prefetch false # disable data prefetch
|
||||
cfg eof_on upstream # set EOF on upstream
|
||||
cfg trailing_bytes 9
|
||||
cfg lut_width_bits 32
|
||||
|
||||
# Define contents that stored in the lookup table
|
||||
lut 0x00011111 # index 0
|
||||
lut 0x00022222 # index 1
|
||||
lut 0x00033333 # index 2
|
||||
lut 0x00000000 # index 3
|
||||
|
||||
set 16..18 L # init the LUT index: 0 (0b000)
|
||||
|
||||
loop:
|
||||
read 8,
|
||||
set 31..0 L31..L0,
|
||||
write 32,
|
||||
jmp loop
|
||||
Reference in New Issue
Block a user