Merge branch 'fix/parlio_add_gdma_fifo_reset_v5.3' into 'release/v5.3'

fix(parlio_tx): add clock and fifo reset in disable function (v5.3)

See merge request espressif/esp-idf!36274
This commit is contained in:
morris
2025-04-15 10:47:25 +08:00
7 changed files with 185 additions and 42 deletions

View File

@@ -59,6 +59,7 @@ typedef struct parlio_tx_unit_t {
#endif #endif
portMUX_TYPE spinlock; // prevent resource accessing by user and interrupt concurrently portMUX_TYPE spinlock; // prevent resource accessing by user and interrupt concurrently
uint32_t out_clk_freq_hz; // output clock frequency 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 max_transfer_bits; // maximum transfer size in bits
size_t queue_depth; // size of transaction queue 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 size_t num_trans_inflight; // indicates the number of transactions that are undergoing but not recycled to ready_queue
@@ -267,6 +268,7 @@ 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) { 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); ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32, tx_unit->out_clk_freq_hz);
} }
tx_unit->clk_src = clk_src;
return ESP_OK; return ESP_OK;
} }
@@ -427,6 +429,18 @@ static void IRAM_ATTR parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio
tx_unit->cur_trans = t; 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.
// And then switched back to the actual clock after the reset is completed.
bool switch_clk = tx_unit->clk_src == PARLIO_CLK_SRC_EXTERNAL ? true : false;
if (switch_clk) {
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_set_clock_source(hal->regs, PARLIO_CLK_SRC_XTAL);
}
}
PARLIO_RCC_ATOMIC() {
parlio_ll_tx_reset_clock(hal->regs);
}
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up // DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
gdma_buffer_mount_config_t mount_config = { gdma_buffer_mount_config_t mount_config = {
.buffer = (void *)t->payload, .buffer = (void *)t->payload,
@@ -436,12 +450,21 @@ static void IRAM_ATTR parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio
.mark_final = true, // singly link list, mark final descriptor .mark_final = true, // singly link list, mark final descriptor
} }
}; };
// Since the threshold of the clock divider counter is not updated simultaneously with the clock source switching.
// The update of the threshold relies on the moment when the counter reaches the threshold each time.
// We place gdma_link_mount_buffers between reset clock and disable clock to ensure enough time for updating the threshold of the clock divider counter.
gdma_link_mount_buffers(tx_unit->dma_link, 0, &mount_config, 1, NULL); gdma_link_mount_buffers(tx_unit->dma_link, 0, &mount_config, 1, NULL);
parlio_ll_tx_reset_fifo(hal->regs); if (switch_clk) {
PARLIO_RCC_ATOMIC() { PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_reset_clock(hal->regs); parlio_ll_tx_set_clock_source(hal->regs, PARLIO_CLK_SRC_EXTERNAL);
} }
}
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_enable_clock(hal->regs, false);
}
// 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_idle_data_value(hal->regs, t->idle_value);
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits); parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
@@ -457,6 +480,7 @@ static void IRAM_ATTR parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio
esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit) 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"); ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_INIT; 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_ENABLE_WAIT)) {
@@ -464,13 +488,16 @@ esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit)
if (tx_unit->pm_lock) { if (tx_unit->pm_lock) {
esp_pm_lock_acquire(tx_unit->pm_lock); esp_pm_lock_acquire(tx_unit->pm_lock);
} }
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_EOF, true); parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_EOF, true);
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE); atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE);
} else { } else {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "unit not in init state"); ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
} }
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_enable_clock(hal->regs, true);
}
// check if we need to start one pending transaction // check if we need to start one pending transaction
parlio_tx_trans_desc_t *t = NULL; parlio_tx_trans_desc_t *t = NULL;
expected_fsm = PARLIO_TX_FSM_ENABLE; expected_fsm = PARLIO_TX_FSM_ENABLE;
@@ -511,9 +538,18 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit)
} }
ESP_RETURN_ON_FALSE(valid_state, ESP_ERR_INVALID_STATE, TAG, "unit can't be disabled in state %d", expected_fsm); ESP_RETURN_ON_FALSE(valid_state, ESP_ERR_INVALID_STATE, TAG, "unit can't be disabled in state %d", expected_fsm);
// stop the TX engine // stop the DMA engine, reset the peripheral state
parlio_hal_context_t *hal = &tx_unit->base.group->hal; parlio_hal_context_t *hal = &tx_unit->base.group->hal;
// to stop the undergoing transaction, disable and reset clock
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_enable_clock(hal->regs, false);
}
PARLIO_RCC_ATOMIC() {
parlio_ll_tx_reset_clock(hal->regs);
}
gdma_stop(tx_unit->dma_chan); gdma_stop(tx_unit->dma_chan);
gdma_reset(tx_unit->dma_chan);
parlio_ll_tx_reset_fifo(hal->regs);
parlio_ll_tx_start(hal->regs, false); parlio_ll_tx_start(hal->regs, false);
parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_EOF, false); parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_EOF, false);
@@ -603,9 +639,6 @@ static void IRAM_ATTR parlio_tx_default_isr(void *args)
if (status & PARLIO_LL_EVENT_TX_EOF) { if (status & PARLIO_LL_EVENT_TX_EOF) {
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_EOF); parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_EOF);
PARLIO_CLOCK_SRC_ATOMIC() {
parlio_ll_tx_enable_clock(hal->regs, false);
}
parlio_ll_tx_start(hal->regs, false); parlio_ll_tx_start(hal->regs, false);
parlio_tx_trans_desc_t *trans_desc = NULL; parlio_tx_trans_desc_t *trans_desc = NULL;

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -22,6 +22,7 @@ extern "C" {
#if CONFIG_IDF_TARGET_ESP32C6 #if CONFIG_IDF_TARGET_ESP32C6
#define TEST_CLK_GPIO 10 #define TEST_CLK_GPIO 10
#define TEST_EXT_CLK_GPIO 12
#define TEST_VALID_GPIO 11 #define TEST_VALID_GPIO 11
#define TEST_DATA0_GPIO 0 #define TEST_DATA0_GPIO 0
#define TEST_DATA1_GPIO 1 #define TEST_DATA1_GPIO 1
@@ -32,27 +33,29 @@ extern "C" {
#define TEST_DATA6_GPIO 6 #define TEST_DATA6_GPIO 6
#define TEST_DATA7_GPIO 7 #define TEST_DATA7_GPIO 7
#elif CONFIG_IDF_TARGET_ESP32H2 #elif CONFIG_IDF_TARGET_ESP32H2
#define TEST_CLK_GPIO 10 #define TEST_VALID_GPIO 2
#define TEST_VALID_GPIO 11 #define TEST_EXT_CLK_GPIO 4
#define TEST_DATA0_GPIO 0 #define TEST_CLK_GPIO 3
#define TEST_DATA1_GPIO 1 #define TEST_DATA0_GPIO 8
#define TEST_DATA2_GPIO 2 #define TEST_DATA1_GPIO 5
#define TEST_DATA3_GPIO 3 #define TEST_DATA2_GPIO 9
#define TEST_DATA4_GPIO 4 #define TEST_DATA3_GPIO 10
#define TEST_DATA5_GPIO 5 #define TEST_DATA4_GPIO 27
#define TEST_DATA6_GPIO 8 #define TEST_DATA5_GPIO 11
#define TEST_DATA7_GPIO 9 #define TEST_DATA6_GPIO 26
#define TEST_DATA7_GPIO 12
#elif CONFIG_IDF_TARGET_ESP32P4 #elif CONFIG_IDF_TARGET_ESP32P4
#define TEST_CLK_GPIO 32 #define TEST_CLK_GPIO 33
#define TEST_VALID_GPIO 36 #define TEST_EXT_CLK_GPIO 34
#define TEST_DATA0_GPIO 20 #define TEST_VALID_GPIO 32
#define TEST_DATA1_GPIO 21 #define TEST_DATA0_GPIO 24
#define TEST_DATA2_GPIO 22 #define TEST_DATA1_GPIO 25
#define TEST_DATA3_GPIO 23 #define TEST_DATA2_GPIO 26
#define TEST_DATA4_GPIO 45 #define TEST_DATA3_GPIO 27
#define TEST_DATA5_GPIO 46 #define TEST_DATA4_GPIO 28
#define TEST_DATA6_GPIO 47 #define TEST_DATA5_GPIO 29
#define TEST_DATA7_GPIO 48 #define TEST_DATA6_GPIO 30
#define TEST_DATA7_GPIO 31
#else #else
#error "Unsupported target" #error "Unsupported target"
#endif #endif

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -11,9 +11,11 @@
#include "unity.h" #include "unity.h"
#include "driver/parlio_tx.h" #include "driver/parlio_tx.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "hal/parlio_ll.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "test_board.h" #include "test_board.h"
#include "soc/parl_io_struct.h"
TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]") TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]")
{ {
@@ -143,7 +145,7 @@ TEST_CASE("parallel_tx_unit_enable_disable", "[parlio_tx]")
TEST_DATA7_GPIO, TEST_DATA7_GPIO,
}, },
.output_clk_freq_hz = 1 * 1000 * 1000, .output_clk_freq_hz = 1 * 1000 * 1000,
.trans_queue_depth = 64, .trans_queue_depth = 4,
.max_transfer_size = 256, .max_transfer_size = 256,
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB, .bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
.sample_edge = PARLIO_SAMPLE_EDGE_POS, .sample_edge = PARLIO_SAMPLE_EDGE_POS,
@@ -155,19 +157,19 @@ TEST_CASE("parallel_tx_unit_enable_disable", "[parlio_tx]")
parlio_transmit_config_t transmit_config = { parlio_transmit_config_t transmit_config = {
.idle_value = 0x00, .idle_value = 0x00,
}; };
__attribute__((aligned(64))) uint8_t payload[128] = {0}; __attribute__((aligned(64))) uint8_t payload[256] = {0};
for (int i = 0; i < 128; i++) { for (int i = 0; i < 256; i++) {
payload[i] = i; payload[i] = i;
} }
for (int j = 0; j < 64; j++) { for (int j = 0; j < 3; j++) {
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 128 * sizeof(uint8_t) * 8, &transmit_config)); TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 256 * sizeof(uint8_t) * 8, &transmit_config));
} }
printf("disable the transaction in the middle\r\n"); printf("disable the transaction in the middle\r\n");
while (parlio_tx_unit_disable(tx_unit) != ESP_OK) { while (parlio_tx_unit_disable(tx_unit) != ESP_OK) {
esp_rom_delay_us(1000); esp_rom_delay_us(1000);
} }
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(10));
printf("resume the transaction and pending packets should continue\r\n"); printf("resume the transaction and pending packets should continue\r\n");
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit)); TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
@@ -274,3 +276,95 @@ TEST_CASE("parallel_tx_clock_gating", "[paralio_tx]")
TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
} }
#endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING #endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING
static void test_gpio_simulate_rising_edge(int gpio_sig, size_t times)
{
while (times--) {
gpio_set_level(gpio_sig, 0);
gpio_set_level(gpio_sig, 1);
gpio_set_level(gpio_sig, 0);
}
}
static uint8_t test_gpio_get_output_data(gpio_num_t* gpio, size_t gpio_num)
{
uint8_t result = 0;
for (size_t i = 0; i < gpio_num; i++) {
int level = gpio_get_level(gpio[i]);
result |= level << i;
}
return result;
}
static void test_use_external_non_free_running_clock(parlio_tx_unit_handle_t tx_unit, parlio_tx_unit_config_t config, int test_round)
{
uint32_t clock_div = config.input_clk_src_freq_hz / config.output_clk_freq_hz;
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
// let core clock running for a while to update the clock divider threshold, this is a hardware limitation
// the following rising edge count is not a magic value, we just need it to be larger than the clock divider value in the previous test case
test_gpio_simulate_rising_edge(TEST_EXT_CLK_GPIO, 100);
esp_rom_delay_us(1000);
parlio_transmit_config_t transmit_config = {
.idle_value = 0xAA,
};
__attribute__((aligned(64))) uint8_t payload[256] = {0};
for (int i = 0; i < 256; i++) {
payload[i] = i;
}
for (int round = 0; round < test_round; round++) {
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 256 * sizeof(uint8_t) * 8, &transmit_config));
for (int i = 0; i < 256; i++) {
// After "clock_div" times external pulses pass through the internal frequency divider, the parlio core clock generates a single pulse.
test_gpio_simulate_rising_edge(TEST_EXT_CLK_GPIO, clock_div);
TEST_ASSERT_EQUAL(i, test_gpio_get_output_data(config.data_gpio_nums, config.data_width));
}
// In order to update the idle value, an additional rising edge is required
test_gpio_simulate_rising_edge(TEST_EXT_CLK_GPIO, clock_div);
TEST_ASSERT_EQUAL(transmit_config.idle_value, test_gpio_get_output_data(config.data_gpio_nums, config.data_width));
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, 100));
}
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
}
TEST_CASE("parallel tx unit use external non-free running clock", "[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 = 8,
.clk_in_gpio_num = TEST_EXT_CLK_GPIO,
.input_clk_src_freq_hz = 80 * 1000 * 1000, // Note that this is not the real input frequency, we just use it to calculate the clock divider
.valid_gpio_num = -1, // don't generate valid signal
.clk_out_gpio_num = TEST_CLK_GPIO,
.data_gpio_nums = {
TEST_DATA0_GPIO,
TEST_DATA1_GPIO,
TEST_DATA2_GPIO,
TEST_DATA3_GPIO,
TEST_DATA4_GPIO,
TEST_DATA5_GPIO,
TEST_DATA6_GPIO,
TEST_DATA7_GPIO,
},
.output_clk_freq_hz = 1 * 1000 * 1000, // For the same reason, this is not the real output frequency
.trans_queue_depth = 8,
.max_transfer_size = 256,
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
.flags.io_loop_back = true,
};
uint8_t test_round = 50;
printf("test input clk freq is greater than output clk freq\r\n");
test_use_external_non_free_running_clock(tx_unit, config, test_round);
// changes input clk freq
config.input_clk_src_freq_hz = 1 * 1000 * 1000;
printf("test special condition, input clk freq equals to output clk freq\r\n");
test_use_external_non_free_running_clock(tx_unit, config, test_round);
};

View File

@@ -1,5 +1,6 @@
CONFIG_PM_ENABLE=y CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -30,7 +30,7 @@
#define PARLIO_LL_EVENT_TX_FIFO_EMPTY (1 << 0) #define PARLIO_LL_EVENT_TX_FIFO_EMPTY (1 << 0)
#define PARLIO_LL_EVENT_RX_FIFO_FULL (1 << 1) #define PARLIO_LL_EVENT_RX_FIFO_FULL (1 << 1)
#define PARLIO_LL_EVENT_TX_EOF (1 << 2) #define PARLIO_LL_EVENT_TX_EOF (1 << 2)
#define PARLIO_LL_EVENT_TX_MASK (PARLIO_LL_EVENT_TX_FIFO_EMPTY | PARLIO_LL_EVENT_TX_EOF) #define PARLIO_LL_EVENT_TX_MASK (PARLIO_LL_EVENT_TX_EOF) // On C6, TX FIFO EMPTY event always comes with TX EOF event. We don't enable it
#define PARLIO_LL_EVENT_RX_MASK (PARLIO_LL_EVENT_RX_FIFO_FULL) #define PARLIO_LL_EVENT_RX_MASK (PARLIO_LL_EVENT_RX_FIFO_FULL)
#define PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG 15 // TXD[15] can be used a valid signal #define PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG 15 // TXD[15] can be used a valid signal
@@ -373,6 +373,7 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
* @param dev Parallel IO register base address * @param dev Parallel IO register base address
* @param src Clock source * @param src Clock source
*/ */
__attribute__((always_inline))
static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src) static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
{ {
(void)dev; (void)dev;
@@ -450,7 +451,7 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
} }
/** /**
* @brief Wether to enable the TX clock gating * @brief Whether to enable the TX clock gating
* *
* @note The TXD[7] will be taken as the gating enable signal * @note The TXD[7] will be taken as the gating enable signal
* *
@@ -465,8 +466,11 @@ static inline void parlio_ll_tx_enable_clock_gating(parl_io_dev_t *dev, bool en)
/** /**
* @brief Start TX unit to transmit data * @brief Start TX unit to transmit data
* *
* @note The hardware monitors the rising edge of tx_start as the trigger signal.
* Once the transmission starts, it cannot be stopped by clearing tx_start.
*
* @param dev Parallel IO register base address * @param dev Parallel IO register base address
* @param en True to start, False to stop * @param en True to start, False to reset the reg state (not meaning the TX unit will be stopped)
*/ */
__attribute__((always_inline)) __attribute__((always_inline))
static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en)

View File

@@ -380,6 +380,7 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
* @param dev Parallel IO register base address * @param dev Parallel IO register base address
* @param src Clock source * @param src Clock source
*/ */
__attribute__((always_inline))
static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src) static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
{ {
(void)dev; (void)dev;
@@ -472,8 +473,11 @@ static inline void parlio_ll_tx_enable_clock_gating(parl_io_dev_t *dev, bool en)
/** /**
* @brief Start TX unit to transmit data * @brief Start TX unit to transmit data
* *
* @note The hardware monitors the rising edge of tx_start as the trigger signal.
* Once the transmission starts, it cannot be stopped by clearing tx_start.
*
* @param dev Parallel IO register base address * @param dev Parallel IO register base address
* @param en True to start, False to stop * @param en True to start, False to reset the reg state (not meaning the TX unit will be stopped)
*/ */
__attribute__((always_inline)) __attribute__((always_inline))
static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en)

View File

@@ -413,6 +413,7 @@ static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev)
* @param dev Parallel IO register base address * @param dev Parallel IO register base address
* @param src Clock source * @param src Clock source
*/ */
__attribute__((always_inline))
static inline void _parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src) static inline void _parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_clock_source_t src)
{ {
(void)dev; (void)dev;
@@ -534,8 +535,11 @@ static inline void parlio_ll_tx_enable_clock_gating(parl_io_dev_t *dev, bool en)
/** /**
* @brief Start TX unit to transmit data * @brief Start TX unit to transmit data
* *
* @note The hardware monitors the rising edge of tx_start as the trigger signal.
* Once the transmission starts, it cannot be stopped by clearing tx_start.
*
* @param dev Parallel IO register base address * @param dev Parallel IO register base address
* @param en True to start, False to stop * @param en True to start, False to reset the reg state (not meaning the TX unit will be stopped)
*/ */
__attribute__((always_inline)) __attribute__((always_inline))
static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en)