mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(parlio_tx): support cs signal on esp32c5 v1.0
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user