feat(parlio_tx): support cs signal on esp32c5 v1.0

This commit is contained in:
Chen Jichang
2025-04-25 14:25:01 +08:00
parent 182b33efb2
commit 6edf48d253
19 changed files with 221 additions and 32 deletions
@@ -30,6 +30,8 @@ typedef struct {
gpio_num_t clk_out_gpio_num; /*!< GPIO number of the output clock signal, the clock is synced with TX data */
gpio_num_t valid_gpio_num; /*!< GPIO number of the valid signal, which stays high when transferring data.
Note that, the valid signal will always occupy the MSB data bit */
uint16_t valid_start_delay; /*!< The clock cycles that the valid signal becomes active before data start */
uint16_t valid_stop_delay; /*!< The clock cycles that the valid signal keeps active after data end */
size_t trans_queue_depth; /*!< Depth of internal transaction queue */
size_t max_transfer_size; /*!< Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction */
size_t dma_burst_size; /*!< DMA burst size, in bytes */
+29 -3
View File
@@ -130,7 +130,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[i], false, false);
}
}
// Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG]
if (config->valid_gpio_num >= 0) {
gpio_func_sel(config->valid_gpio_num, PIN_FUNC_GPIO);
@@ -138,11 +138,20 @@ static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const
if (config->flags.io_loop_back) {
gpio_input_enable(config->valid_gpio_num);
}
#if !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
// Configure CS signal if supported
// Note: the default value of CS signal is low, so we need to invert the CS to keep compatible with the default value
// connect the signal to the GPIO by matrix, it will also enable the output path properly
esp_rom_gpio_connect_out_signal(config->valid_gpio_num,
parlio_periph_signals.groups[group_id].tx_units[unit_id].cs_sig,
!config->flags.invert_valid_out, false);
#else
// connect the signal to the GPIO by matrix, it will also enable the output path properly
// Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG]
esp_rom_gpio_connect_out_signal(config->valid_gpio_num,
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
}
if (config->clk_out_gpio_num >= 0) {
gpio_func_sel(config->clk_out_gpio_num, PIN_FUNC_GPIO);
@@ -295,11 +304,16 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
// data_width must be power of 2 and less than or equal to SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH
ESP_RETURN_ON_FALSE(data_width && (data_width <= SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH) && ((data_width & (data_width - 1)) == 0),
ESP_ERR_INVALID_ARG, TAG, "invalid data width");
// No need to check data width conflict with valid signal when CS is supported
#if PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
// data_width must not conflict with the valid signal
ESP_RETURN_ON_FALSE(!(config->valid_gpio_num >= 0 && data_width > PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG),
ESP_ERR_INVALID_ARG, TAG, "valid signal conflicts with data signal");
#endif // PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
#if SOC_PARLIO_TX_CLK_SUPPORT_GATING
// clock gating is controlled by either the MSB bit of data bus or the valid signal
// clock gating is controlled by either the MSB bit of data bus or the valid signal(or CS signal when supported)
ESP_RETURN_ON_FALSE(!(config->flags.clk_gate_en && config->valid_gpio_num < 0 && config->data_width <= PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE),
ESP_ERR_INVALID_ARG, TAG, "no gpio can control the clock gating");
#else
@@ -351,13 +365,25 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
// set data width
parlio_ll_tx_set_bus_width(hal->regs, data_width);
unit->idle_value_mask = (1 << data_width) - 1;
// set valid delay
ESP_GOTO_ON_FALSE(parlio_ll_tx_set_valid_delay(hal->regs, config->valid_start_delay, config->valid_stop_delay), ESP_ERR_INVALID_ARG, err, TAG, "invalid valid delay");
// whether to use the valid signal
#if !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
// the clock gating source is actual selectable, it doesn't rely on the available of valid GPIO.
// but there is no use case that valid signal is used, but clocking gating is still controlled by data.
if (config->valid_gpio_num >= 0) {
parlio_ll_tx_clock_gating_from_valid(hal->regs, true);
} else {
parlio_ll_tx_clock_gating_from_valid(hal->regs, false);
}
#else
if (config->valid_gpio_num >= 0) {
parlio_ll_tx_treat_msb_as_valid(hal->regs, true);
unit->idle_value_mask &= ~(1 << PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG);
} else {
parlio_ll_tx_treat_msb_as_valid(hal->regs, false);
}
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
// set data byte packing order
if (data_width < 8) {
parlio_ll_tx_set_bit_pack_order(hal->regs, config->bit_pack_order);
@@ -50,8 +50,10 @@ TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]")
config.input_clk_src_freq_hz = 1000000;
config.valid_gpio_num = 0;
#if PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
// failed because of data line conflict with valid signal
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, parlio_new_tx_unit(&config, &units[0]));
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
config.data_width = 4;
TEST_ESP_OK(parlio_new_tx_unit(&config, &units[0]));
@@ -288,6 +290,73 @@ TEST_CASE("parallel_tx_clock_gating", "[paralio_tx]")
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
TEST_ESP_OK(gpio_reset_pin(TEST_CLK_GPIO));
}
#if !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
TEST_CASE("parallel_tx_clock_gating_and_msb_coexist", "[paralio_tx]")
{
printf("init a gpio to read parlio_tx clk output\r\n");
gpio_config_t test_gpio_conf = {
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = BIT64(TEST_CLK_GPIO) | BIT64(TEST_DATA7_GPIO),
};
TEST_ESP_OK(gpio_config(&test_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 = -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,
TEST_DATA4_GPIO,
TEST_DATA5_GPIO,
TEST_DATA6_GPIO,
TEST_DATA7_GPIO,
},
.output_clk_freq_hz = 1 * 1000 * 1000,
.trans_queue_depth = 4,
.max_transfer_size = 256,
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB,
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
.valid_start_delay = 5,
.valid_stop_delay = 5,
.flags.clk_gate_en = true, // enable clock gating, controlled by the CS signal
};
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
printf("send packets and see if the clock is gated when there's no transaction on line\r\n");
parlio_transmit_config_t transmit_config = {
// set the idle value to 0x80, so that the MSB is high when there's no transaction
.idle_value = 0x80,
};
uint32_t size = 256;
__attribute__((aligned(64))) uint8_t payload[size];
for (int i = 0; i < size; i++) {
payload[i] = i;
}
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, size * sizeof(uint8_t) * 8, &transmit_config));
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
// check if the level on the clock line is low
TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_CLK_GPIO));
TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_DATA7_GPIO));
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, size * sizeof(uint8_t) * 8, &transmit_config));
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_CLK_GPIO));
TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_CLK_GPIO));
TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_DATA7_GPIO));
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
TEST_ESP_OK(gpio_reset_pin(TEST_CLK_GPIO));
}
#endif // !PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG
#endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING
#if SOC_PSRAM_DMA_CAPABLE