|
1 | 1 | /* |
2 | | - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD |
| 2 | + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD |
3 | 3 | * |
4 | 4 | * SPDX-License-Identifier: Apache-2.0 |
5 | 5 | */ |
|
11 | 11 | #include "unity.h" |
12 | 12 | #include "driver/parlio_tx.h" |
13 | 13 | #include "driver/gpio.h" |
| 14 | +#include "hal/parlio_ll.h" |
14 | 15 | #include "soc/soc_caps.h" |
15 | 16 | #include "esp_attr.h" |
16 | 17 | #include "test_board.h" |
| 18 | +#include "soc/parl_io_struct.h" |
17 | 19 |
|
18 | 20 | TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]") |
19 | 21 | { |
@@ -338,3 +340,109 @@ TEST_CASE("parlio can transmit PSRAM buffer", "[parlio_tx]") |
338 | 340 | free(buffer); |
339 | 341 | } |
340 | 342 | #endif // SOC_PSRAM_DMA_CAPABLE |
| 343 | + |
| 344 | +static void test_gpio_simulate_rising_edge(int gpio_sig, size_t times) |
| 345 | +{ |
| 346 | + while (times--) { |
| 347 | + gpio_set_level(gpio_sig, 0); |
| 348 | + gpio_set_level(gpio_sig, 1); |
| 349 | + gpio_set_level(gpio_sig, 0); |
| 350 | + } |
| 351 | +} |
| 352 | + |
| 353 | +static uint8_t test_gpio_get_output_data(gpio_num_t* gpio, size_t gpio_num) |
| 354 | +{ |
| 355 | + uint8_t result = 0; |
| 356 | + for (size_t i = 0; i < gpio_num; i++) { |
| 357 | + int level = gpio_get_level(gpio[i]); |
| 358 | + result |= level << i; |
| 359 | + } |
| 360 | + return result; |
| 361 | +} |
| 362 | + |
| 363 | +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) |
| 364 | +{ |
| 365 | + uint32_t clock_div = config.input_clk_src_freq_hz / config.output_clk_freq_hz; |
| 366 | + TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit)); |
| 367 | + TEST_ESP_OK(parlio_tx_unit_enable(tx_unit)); |
| 368 | + // let core clock running for a while to update the clock divider threshold |
| 369 | + esp_rom_delay_us(100); |
| 370 | + parlio_transmit_config_t transmit_config = { |
| 371 | + .idle_value = 0xAA, |
| 372 | + }; |
| 373 | + __attribute__((aligned(64))) uint8_t payload[256] = {0}; |
| 374 | + for (int i = 0; i < 256; i++) { |
| 375 | + payload[i] = i; |
| 376 | + } |
| 377 | + |
| 378 | + for (int round = 0; round < test_round; round++) { |
| 379 | + TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 256 * sizeof(uint8_t) * 8, &transmit_config)); |
| 380 | + for (int i = 0; i < 256; i++) { |
| 381 | + // After "clock_div" times external pulses pass through the internal frequency divider, the parlio core clock generates a single pulse. |
| 382 | + test_gpio_simulate_rising_edge(TEST_EXT_CLK_GPIO, clock_div); |
| 383 | + TEST_ASSERT_EQUAL(i, test_gpio_get_output_data(config.data_gpio_nums, config.data_width)); |
| 384 | + } |
| 385 | + // In order to update the idle value, an additional rising edge is required |
| 386 | + test_gpio_simulate_rising_edge(TEST_EXT_CLK_GPIO, clock_div); |
| 387 | + TEST_ASSERT_EQUAL(transmit_config.idle_value, test_gpio_get_output_data(config.data_gpio_nums, config.data_width)); |
| 388 | + TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, 100)); |
| 389 | + } |
| 390 | + TEST_ESP_OK(parlio_tx_unit_disable(tx_unit)); |
| 391 | + TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); |
| 392 | +} |
| 393 | + |
| 394 | +TEST_CASE("parallel tx unit use external non-free running clock", "[parlio_tx]") |
| 395 | +{ |
| 396 | + printf("use gpio as external clock source\r\n"); |
| 397 | + // configure the data gpio for loopback test |
| 398 | + gpio_config_t gpio_conf = { |
| 399 | + .mode = GPIO_MODE_INPUT, |
| 400 | + .pin_bit_mask = BIT64(TEST_DATA0_GPIO) | BIT64(TEST_DATA1_GPIO) | BIT64(TEST_DATA2_GPIO) | BIT64(TEST_DATA3_GPIO) | |
| 401 | + BIT64(TEST_DATA4_GPIO) | BIT64(TEST_DATA5_GPIO) | BIT64(TEST_DATA6_GPIO) | BIT64(TEST_DATA7_GPIO), |
| 402 | + }; |
| 403 | + TEST_ESP_OK(gpio_config(&gpio_conf)); |
| 404 | + // configure the external clock output gpio |
| 405 | + gpio_conf.mode = GPIO_MODE_OUTPUT; |
| 406 | + gpio_conf.pin_bit_mask = BIT64(TEST_EXT_CLK_GPIO); |
| 407 | + TEST_ESP_OK(gpio_config(&gpio_conf)); |
| 408 | + |
| 409 | + printf("install parlio tx unit\r\n"); |
| 410 | + parlio_tx_unit_handle_t tx_unit = NULL; |
| 411 | + parlio_tx_unit_config_t config = { |
| 412 | + .clk_src = PARLIO_CLK_SRC_DEFAULT, |
| 413 | + .data_width = 8, |
| 414 | + .clk_in_gpio_num = TEST_EXT_CLK_GPIO, |
| 415 | + .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 |
| 416 | + .valid_gpio_num = -1, // don't generate valid signal |
| 417 | + .clk_out_gpio_num = TEST_CLK_GPIO, |
| 418 | + .data_gpio_nums = { |
| 419 | + TEST_DATA0_GPIO, |
| 420 | + TEST_DATA1_GPIO, |
| 421 | + TEST_DATA2_GPIO, |
| 422 | + TEST_DATA3_GPIO, |
| 423 | + TEST_DATA4_GPIO, |
| 424 | + TEST_DATA5_GPIO, |
| 425 | + TEST_DATA6_GPIO, |
| 426 | + TEST_DATA7_GPIO, |
| 427 | + }, |
| 428 | + .output_clk_freq_hz = 1 * 1000 * 1000, // For the same reason, this is not the real output frequency |
| 429 | + .trans_queue_depth = 8, |
| 430 | + .max_transfer_size = 256, |
| 431 | + .bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB, |
| 432 | + .sample_edge = PARLIO_SAMPLE_EDGE_POS, |
| 433 | + }; |
| 434 | + |
| 435 | + uint8_t test_round = 50; |
| 436 | + printf("test input clk freq is greater than output clk freq\r\n"); |
| 437 | + test_use_external_non_free_running_clock(tx_unit, config, test_round); |
| 438 | + |
| 439 | + // changes input clk freq |
| 440 | + config.input_clk_src_freq_hz = 1 * 1000 * 1000; |
| 441 | + printf("test special condition, input clk freq equals to output clk freq\r\n"); |
| 442 | + test_use_external_non_free_running_clock(tx_unit, config, test_round); |
| 443 | + |
| 444 | + TEST_ESP_OK(gpio_reset_pin(TEST_EXT_CLK_GPIO)); |
| 445 | + for (int i = 0; i < 8; i++) { |
| 446 | + TEST_ESP_OK(gpio_reset_pin(config.data_gpio_nums[i])); |
| 447 | + } |
| 448 | +}; |
0 commit comments