forked from espressif/esp-idf
Merge branch 'fix/fix_parlio_tx_rempty_interrupt' into 'master'
fix(parlio_tx): fix rempty interrupt during resetting fifo See merge request espressif/esp-idf!36111
This commit is contained in:
@@ -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
|
||||||
*/
|
*/
|
||||||
@@ -62,6 +62,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
|
||||||
@@ -290,6 +291,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;
|
||||||
}
|
}
|
||||||
@@ -465,6 +467,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,
|
||||||
@@ -474,14 +488,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);
|
|
||||||
PARLIO_RCC_ATOMIC() {
|
if (switch_clk) {
|
||||||
parlio_ll_tx_reset_clock(hal->regs);
|
PARLIO_CLOCK_SRC_ATOMIC() {
|
||||||
|
parlio_ll_tx_set_clock_source(hal->regs, PARLIO_CLK_SRC_EXTERNAL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PARLIO_CLOCK_SRC_ATOMIC() {
|
PARLIO_CLOCK_SRC_ATOMIC() {
|
||||||
parlio_ll_tx_enable_clock(hal->regs, false);
|
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);
|
||||||
|
|
||||||
|
@@ -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
|
||||||
@@ -33,6 +34,7 @@ extern "C" {
|
|||||||
#define TEST_DATA7_GPIO 7
|
#define TEST_DATA7_GPIO 7
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||||
#define TEST_CLK_GPIO 25
|
#define TEST_CLK_GPIO 25
|
||||||
|
#define TEST_EXT_CLK_GPIO 10
|
||||||
#define TEST_VALID_GPIO 26
|
#define TEST_VALID_GPIO 26
|
||||||
#define TEST_DATA0_GPIO 0
|
#define TEST_DATA0_GPIO 0
|
||||||
#define TEST_DATA1_GPIO 1
|
#define TEST_DATA1_GPIO 1
|
||||||
@@ -44,6 +46,7 @@ extern "C" {
|
|||||||
#define TEST_DATA7_GPIO 7
|
#define TEST_DATA7_GPIO 7
|
||||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||||
#define TEST_VALID_GPIO 2
|
#define TEST_VALID_GPIO 2
|
||||||
|
#define TEST_EXT_CLK_GPIO 4
|
||||||
#define TEST_CLK_GPIO 3
|
#define TEST_CLK_GPIO 3
|
||||||
#define TEST_DATA0_GPIO 8
|
#define TEST_DATA0_GPIO 8
|
||||||
#define TEST_DATA1_GPIO 5
|
#define TEST_DATA1_GPIO 5
|
||||||
@@ -55,6 +58,7 @@ extern "C" {
|
|||||||
#define TEST_DATA7_GPIO 12
|
#define TEST_DATA7_GPIO 12
|
||||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||||
#define TEST_CLK_GPIO 33
|
#define TEST_CLK_GPIO 33
|
||||||
|
#define TEST_EXT_CLK_GPIO 34
|
||||||
#define TEST_VALID_GPIO 32
|
#define TEST_VALID_GPIO 32
|
||||||
#define TEST_DATA0_GPIO 24
|
#define TEST_DATA0_GPIO 24
|
||||||
#define TEST_DATA1_GPIO 25
|
#define TEST_DATA1_GPIO 25
|
||||||
|
@@ -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]")
|
||||||
{
|
{
|
||||||
@@ -338,3 +340,109 @@ TEST_CASE("parlio can transmit PSRAM buffer", "[parlio_tx]")
|
|||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
#endif // SOC_PSRAM_DMA_CAPABLE
|
#endif // SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|
||||||
|
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
|
||||||
|
esp_rom_delay_us(100);
|
||||||
|
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("use gpio as external clock source\r\n");
|
||||||
|
// configure the data gpio for loopback test
|
||||||
|
gpio_config_t gpio_conf = {
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pin_bit_mask = BIT64(TEST_DATA0_GPIO) | BIT64(TEST_DATA1_GPIO) | BIT64(TEST_DATA2_GPIO) | BIT64(TEST_DATA3_GPIO) |
|
||||||
|
BIT64(TEST_DATA4_GPIO) | BIT64(TEST_DATA5_GPIO) | BIT64(TEST_DATA6_GPIO) | BIT64(TEST_DATA7_GPIO),
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(gpio_config(&gpio_conf));
|
||||||
|
// configure the external clock output gpio
|
||||||
|
gpio_conf.mode = GPIO_MODE_OUTPUT;
|
||||||
|
gpio_conf.pin_bit_mask = BIT64(TEST_EXT_CLK_GPIO);
|
||||||
|
TEST_ESP_OK(gpio_config(&gpio_conf));
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
TEST_ESP_OK(gpio_reset_pin(TEST_EXT_CLK_GPIO));
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
TEST_ESP_OK(gpio_reset_pin(config.data_gpio_nums[i]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@@ -397,6 +397,7 @@ static inline uint32_t parlio_ll_rx_get_fifo_cycle_cnt(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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -398,6 +398,7 @@ static inline uint32_t parlio_ll_rx_get_fifo_cycle_cnt(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;
|
||||||
|
@@ -427,6 +427,7 @@ static inline uint32_t parlio_ll_rx_get_fifo_cycle_cnt(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;
|
||||||
|
Reference in New Issue
Block a user