mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-31 13:09:38 +00:00 
			
		
		
		
	driver: add parallel IO TX driver
This commit is contained in:
		| @@ -64,6 +64,10 @@ components/driver/test_apps/mcpwm: | ||||
|   disable: | ||||
|     - if: SOC_MCPWM_SUPPORTED != 1 | ||||
|  | ||||
| components/driver/test_apps/parlio: | ||||
|   disable: | ||||
|     - if: SOC_PARLIO_SUPPORTED != 1 | ||||
|  | ||||
| components/driver/test_apps/pulse_cnt: | ||||
|   disable: | ||||
|     - if: SOC_PCNT_SUPPORTED != 1 | ||||
|   | ||||
| @@ -17,6 +17,7 @@ set(includes "include" | ||||
|              "i2s/include" | ||||
|              "ledc/include" | ||||
|              "mcpwm/include" | ||||
|              "parlio/include" | ||||
|              "pcnt/include" | ||||
|              "rmt/include" | ||||
|              "sdio_slave/include" | ||||
| @@ -49,6 +50,11 @@ if(CONFIG_SOC_DAC_SUPPORTED) | ||||
|                      "deprecated/${target}/dac_legacy.c") | ||||
| endif() | ||||
|  | ||||
| # Parallel IO related source files | ||||
| if(CONFIG_SOC_PARLIO_SUPPORTED) | ||||
|     list(APPEND srcs "parlio/parlio_common.c" "parlio/parlio_tx.c") | ||||
| endif() | ||||
|  | ||||
| # GPIO related source files | ||||
| if(CONFIG_SOC_DEDICATED_GPIO_SUPPORTED) | ||||
|     list(APPEND srcs "gpio/dedic_gpio.c") | ||||
|   | ||||
| @@ -478,4 +478,24 @@ menu "Driver Configurations" | ||||
|                 USB Serial/JTAG is in use. | ||||
|     endmenu # USB Serial/JTAG Configuration | ||||
|  | ||||
|     menu "Parallel IO Configuration" | ||||
|         depends on SOC_PARLIO_SUPPORTED | ||||
|  | ||||
|         config PARLIO_ENABLE_DEBUG_LOG | ||||
|             bool "Enable debug log" | ||||
|             default n | ||||
|             help | ||||
|                 Wether to enable the debug log message for parallel IO driver. | ||||
|                 Note that, this option only controls the parallel IO driver log, won't affect other drivers. | ||||
|  | ||||
|         config PARLIO_ISR_IRAM_SAFE | ||||
|             bool "Parallel IO ISR IRAM-Safe" | ||||
|             default n | ||||
|             select GDMA_CTRL_FUNC_IN_IRAM # the driver needs to start the GDMA in the interrupt | ||||
|             help | ||||
|                 Ensure the Parallel IO interrupt is IRAM-Safe by allowing the interrupt handler to be | ||||
|                 executable when the cache is disabled (e.g. SPI Flash write). | ||||
|  | ||||
|     endmenu # Parallel IO Configuration | ||||
|  | ||||
| endmenu  # Driver configurations | ||||
|   | ||||
							
								
								
									
										187
									
								
								components/driver/parlio/include/driver/parlio_tx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								components/driver/parlio/include/driver/parlio_tx.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include "esp_err.h" | ||||
| #include "driver/parlio_types.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief Parallel IO TX unit configuration | ||||
|  */ | ||||
| typedef struct { | ||||
|     parlio_clock_source_t clk_src;  /*!< Parallel IO internal clock source */ | ||||
|     gpio_num_t clk_in_gpio_num;     /*!< If the clock source is input from external, set the corresponding GPIO number. | ||||
|                                          Otherwise, set to `-1` and the driver will use the internal `clk_src` as clock source. | ||||
|                                          This option has higher priority than `clk_src` */ | ||||
|     uint32_t input_clk_src_freq_hz; /*!< Frequency of the input clock source, valid only if `clk_in_gpio_num` is not `-1` */ | ||||
|     uint32_t output_clk_freq_hz;    /*!< Frequency of the output clock. It's divided from either internal `clk_src` or external clock source */ | ||||
|     size_t data_width;              /*!< Parallel IO data width, can set to 1/2/4/8/..., but can't bigger than PARLIO_TX_UNIT_MAX_DATA_WIDTH */ | ||||
|     gpio_num_t data_gpio_nums[PARLIO_TX_UNIT_MAX_DATA_WIDTH]; /*!< Parallel IO data GPIO numbers, if any GPIO is not used, you can set it to `-1` */ | ||||
|     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 */ | ||||
|     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 */ | ||||
|     parlio_sample_edge_t sample_edge;       /*!< Parallel IO sample edge */ | ||||
|     parlio_bit_pack_order_t bit_pack_order; /*!< Set the order of packing the bits into bytes (only works when `data_width` < 8) */ | ||||
|     struct { | ||||
|         uint32_t clk_gate_en: 1;  /*!< Enable TX clock gating, | ||||
|                                        the output clock will be controlled by the MSB bit of the data bus, | ||||
|                                        i.e. by data_gpio_nums[PARLIO_TX_UNIT_MAX_DATA_WIDTH-1]. High level to enable the clock output, low to disable */ | ||||
|         uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */ | ||||
|     } flags;                      /*!< Extra configuration flags */ | ||||
| } parlio_tx_unit_config_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Create a Parallel IO TX unit | ||||
|  * | ||||
|  * @param[in] config Parallel IO TX unit configuration | ||||
|  * @param[out] ret_unit Returned Parallel IO TX unit handle | ||||
|  * @return | ||||
|  *      - ESP_OK: Create Parallel IO TX unit successfully | ||||
|  *      - ESP_ERR_INVALID_ARG: Create Parallel IO TX unit failed because of invalid argument | ||||
|  *      - ESP_ERR_NO_MEM: Create Parallel IO TX unit failed because of out of memory | ||||
|  *      - ESP_ERR_NOT_FOUND: Create Parallel IO TX unit failed because all TX units are used up and no more free one | ||||
|  *      - ESP_ERR_NOT_SUPPORTED: Create Parallel IO TX unit failed because some feature is not supported by hardware, e.g. clock gating | ||||
|  *      - ESP_FAIL: Create Parallel IO TX unit failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_unit_handle_t *ret_unit); | ||||
|  | ||||
| /** | ||||
|  * @brief Delete a Parallel IO TX unit | ||||
|  * | ||||
|  * @param[in] unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @return | ||||
|  *      - ESP_OK: Delete Parallel IO TX unit successfully | ||||
|  *      - ESP_ERR_INVALID_ARG: Delete Parallel IO TX unit failed because of invalid argument | ||||
|  *      - ESP_ERR_INVALID_STATE: Delete Parallel IO TX unit failed because it is still in working | ||||
|  *      - ESP_FAIL: Delete Parallel IO TX unit failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_del_tx_unit(parlio_tx_unit_handle_t unit); | ||||
|  | ||||
| /** | ||||
|  * @brief Enable the Parallel IO TX unit | ||||
|  * | ||||
|  * @note This function will transit the driver state from init to enable | ||||
|  * @note This function will acquire a PM lock that might be installed during channel allocation | ||||
|  * @note If there're transaction pending in the queue, this function will pick up the first one and start the transfer | ||||
|  * | ||||
|  * @param[in] unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @return | ||||
|  *      - ESP_OK: Enable Parallel IO TX unit successfully | ||||
|  *      - ESP_ERR_INVALID_ARG: Enable Parallel IO TX unit failed because of invalid argument | ||||
|  *      - ESP_ERR_INVALID_STATE: Enable Parallel IO TX unit failed because it is already enabled | ||||
|  *      - ESP_FAIL: Enable Parallel IO TX unit failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t unit); | ||||
|  | ||||
| /** | ||||
|  * @brief Disable the Parallel IO TX unit | ||||
|  * | ||||
|  * @note This function will transit the driver state from enable to init | ||||
|  * @note This function will release the PM lock that might be installed during channel allocation | ||||
|  * @note If one transaction is undergoing, this function will terminate it immediately | ||||
|  * | ||||
|  * @param[in] unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @return | ||||
|  *      - ESP_OK: Disable Parallel IO TX unit successfully | ||||
|  *      - ESP_ERR_INVALID_ARG: Disable Parallel IO TX unit failed because of invalid argument | ||||
|  *      - ESP_ERR_INVALID_STATE: Disable Parallel IO TX unit failed because it's not enabled yet | ||||
|  *      - ESP_FAIL: Disable Parallel IO TX unit failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t unit); | ||||
|  | ||||
| /** | ||||
|  * @brief Type of Parallel IO TX done event data | ||||
|  */ | ||||
| typedef struct { | ||||
| } parlio_tx_done_event_data_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Prototype of parlio tx event callback | ||||
|  * @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @param[in] edata Point to Parallel IO TX event data. The lifecycle of this pointer memory is inside this function, | ||||
|  *                  user should copy it into static memory if used outside this function. | ||||
|  * @param[in] user_ctx User registered context, passed from `parlio_tx_unit_register_event_callbacks` | ||||
|  * | ||||
|  * @return Whether a high priority task has been waken up by this callback function | ||||
|  */ | ||||
| typedef bool (*parlio_tx_done_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx); | ||||
|  | ||||
| /** | ||||
|  * @brief Group of Parallel IO TX callbacks | ||||
|  * @note The callbacks are all running under ISR environment | ||||
|  * @note When CONFIG_PARLIO_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. | ||||
|  *       The variables used in the function should be in the SRAM as well. | ||||
|  */ | ||||
| typedef struct { | ||||
|     parlio_tx_done_callback_t on_trans_done; /*!< Event callback, invoked when one transmission is finished */ | ||||
| } parlio_tx_event_callbacks_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Set event callbacks for Parallel IO TX unit | ||||
|  * | ||||
|  * @note User can deregister a previously registered callback by calling this function and setting the callback member in the `cbs` structure to NULL. | ||||
|  * @note When CONFIG_PARLIO_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. | ||||
|  *       The variables used in the function should be in the SRAM as well. The `user_data` should also reside in SRAM. | ||||
|  * | ||||
|  * @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @param[in] cbs Group of callback functions | ||||
|  * @param[in] user_data User data, which will be passed to callback functions directly | ||||
|  * @return | ||||
|  *      - ESP_OK: Set event callbacks successfully | ||||
|  *      - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument | ||||
|  *      - ESP_FAIL: Set event callbacks failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_unit, const parlio_tx_event_callbacks_t *cbs, void *user_data); | ||||
|  | ||||
| /** | ||||
|  * @brief Parallel IO transmit configuration | ||||
|  */ | ||||
| typedef struct { | ||||
|     uint32_t idle_value; /*!< The value on the data line when the parallel IO is in idle state */ | ||||
| } parlio_transmit_config_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Transmit data on by Parallel IO TX unit | ||||
|  * | ||||
|  * @note After the function returns, it doesn't mean the transaction is finished. This function only constructs a transcation structure and push into a queue. | ||||
|  * | ||||
|  * @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @param[in] payload Pointer to the data to be transmitted | ||||
|  * @param[in] payload_bits Length of the data to be transmitted, in bits | ||||
|  * @param[in] config Transmit configuration | ||||
|  * @return | ||||
|  *      - ESP_OK: Transmit data successfully | ||||
|  *      - ESP_ERR_INVALID_ARG: Transmit data failed because of invalid argument | ||||
|  *      - ESP_ERR_INVALID_STATE: Transmit data failed because the Parallel IO TX unit is not enabled | ||||
|  *      - ESP_FAIL: Transmit data failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *payload, size_t payload_bits, const parlio_transmit_config_t *config); | ||||
|  | ||||
| /** | ||||
|  * @brief Wait for all pending TX transactions done | ||||
|  * | ||||
|  * @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit` | ||||
|  * @param[in] timeout_ms Timeout in milliseconds, `-1` means to wait forever | ||||
|  * @return | ||||
|  *      - ESP_OK: All pending TX transactions is finished and recycled | ||||
|  *      - ESP_ERR_INVALID_ARG: Wait for all pending TX transactions done failed because of invalid argument | ||||
|  *      - ESP_ERR_TIMEOUT: Wait for all pending TX transactions done timeout | ||||
|  *      - ESP_FAIL: Wait for all pending TX transactions done failed because of other error | ||||
|  */ | ||||
| esp_err_t parlio_tx_unit_wait_all_done(parlio_tx_unit_handle_t tx_unit, int timeout_ms); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										26
									
								
								components/driver/parlio/include/driver/parlio_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								components/driver/parlio/include/driver/parlio_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include "esp_err.h" | ||||
| #include "hal/parlio_types.h" | ||||
| #include "hal/gpio_types.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief Type of Parallel IO TX unit handle | ||||
|  */ | ||||
| typedef struct parlio_tx_unit_t *parlio_tx_unit_handle_t; | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										89
									
								
								components/driver/parlio/parlio_common.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								components/driver/parlio/parlio_common.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include <sys/lock.h> | ||||
| #include "sdkconfig.h" | ||||
| #if CONFIG_PARLIO_ENABLE_DEBUG_LOG | ||||
| // The local log level must be defined before including esp_log.h | ||||
| // Set the maximum log level for this source file | ||||
| #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG | ||||
| #endif | ||||
| #include "esp_log.h" | ||||
| #include "esp_check.h" | ||||
| #include "clk_ctrl_os.h" | ||||
| #include "soc/rtc.h" | ||||
| #include "soc/parlio_periph.h" | ||||
| #include "hal/parlio_ll.h" | ||||
| #include "esp_private/esp_clk.h" | ||||
| #include "esp_private/periph_ctrl.h" | ||||
| #include "parlio_private.h" | ||||
|  | ||||
| static const char *TAG = "parlio"; | ||||
|  | ||||
| typedef struct parlio_platform_t { | ||||
|     _lock_t mutex;                             // platform level mutex lock | ||||
|     parlio_group_t *groups[SOC_PARLIO_GROUPS]; // array of parallel IO group instances | ||||
|     int group_ref_counts[SOC_PARLIO_GROUPS];   // reference count used to protect group install/uninstall | ||||
| } parlio_platform_t; | ||||
|  | ||||
| static parlio_platform_t s_platform; // singleton platform | ||||
|  | ||||
| parlio_group_t *parlio_acquire_group_handle(int group_id) | ||||
| { | ||||
|     bool new_group = false; | ||||
|     parlio_group_t *group = NULL; | ||||
|  | ||||
|     // prevent install parlio group concurrently | ||||
|     _lock_acquire(&s_platform.mutex); | ||||
|     if (!s_platform.groups[group_id]) { | ||||
|         group = heap_caps_calloc(1, sizeof(parlio_group_t), PARLIO_MEM_ALLOC_CAPS); | ||||
|         if (group) { | ||||
|             new_group = true; | ||||
|             s_platform.groups[group_id] = group; | ||||
|             group->group_id = group_id; | ||||
|             group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; | ||||
|             // enable APB access PARLIO registers | ||||
|             periph_module_enable(parlio_periph_signals.groups[group_id].module); | ||||
|             periph_module_reset(parlio_periph_signals.groups[group_id].module); | ||||
|             // hal layer initialize | ||||
|             parlio_hal_init(&group->hal); | ||||
|         } | ||||
|     } else { // group already install | ||||
|         group = s_platform.groups[group_id]; | ||||
|     } | ||||
|     if (group) { | ||||
|         // someone acquired the group handle means we have a new object that refer to this group | ||||
|         s_platform.group_ref_counts[group_id]++; | ||||
|     } | ||||
|     _lock_release(&s_platform.mutex); | ||||
|  | ||||
|     if (new_group) { | ||||
|         ESP_LOGD(TAG, "new group(%d) at %p", group_id, group); | ||||
|     } | ||||
|     return group; | ||||
| } | ||||
|  | ||||
| void parlio_release_group_handle(parlio_group_t *group) | ||||
| { | ||||
|     int group_id = group->group_id; | ||||
|     bool do_deinitialize = false; | ||||
|  | ||||
|     _lock_acquire(&s_platform.mutex); | ||||
|     s_platform.group_ref_counts[group_id]--; | ||||
|     if (s_platform.group_ref_counts[group_id] == 0) { | ||||
|         do_deinitialize = true; | ||||
|         s_platform.groups[group_id] = NULL; | ||||
|         // hal layer deinitialize | ||||
|         parlio_hal_deinit(&group->hal); | ||||
|         periph_module_disable(parlio_periph_signals.groups[group_id].module); | ||||
|         free(group); | ||||
|     } | ||||
|     _lock_release(&s_platform.mutex); | ||||
|  | ||||
|     if (do_deinitialize) { | ||||
|         ESP_LOGD(TAG, "del group(%d)", group_id); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										70
									
								
								components/driver/parlio/parlio_private.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								components/driver/parlio/parlio_private.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "sdkconfig.h" | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "soc/soc_caps.h" | ||||
| #include "hal/parlio_types.h" | ||||
| #include "hal/parlio_hal.h" | ||||
| #include "esp_heap_caps.h" | ||||
| #include "driver/parlio_types.h" | ||||
|  | ||||
| #if CONFIG_PARLIO_ISR_IRAM_SAFE | ||||
| #define PARLIO_MEM_ALLOC_CAPS    (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) | ||||
| #else | ||||
| #define PARLIO_MEM_ALLOC_CAPS    MALLOC_CAP_DEFAULT | ||||
| #endif | ||||
|  | ||||
| #if SOC_PARLIO_TX_RX_SHARE_INTERRUPT | ||||
| #define PARLIO_INTR_ALLOC_FLAG_SHARED ESP_INTR_FLAG_SHARED | ||||
| #else | ||||
| #define PARLIO_INTR_ALLOC_FLAG_SHARED 0 | ||||
| #endif | ||||
|  | ||||
| #if CONFIG_PARLIO_ISR_IRAM_SAFE | ||||
| #define PARLIO_INTR_ALLOC_FLAG   (ESP_INTR_FLAG_LOWMED | PARLIO_INTR_ALLOC_FLAG_SHARED | ESP_INTR_FLAG_IRAM) | ||||
| #else | ||||
| #define PARLIO_INTR_ALLOC_FLAG   (ESP_INTR_FLAG_LOWMED | PARLIO_INTR_ALLOC_FLAG_SHARED) | ||||
| #endif | ||||
|  | ||||
| #define PARLIO_PM_LOCK_NAME_LEN_MAX 16 | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| enum { | ||||
|     PARLIO_TX_QUEUE_READY, | ||||
|     PARLIO_TX_QUEUE_PROGRESS, | ||||
|     PARLIO_TX_QUEUE_COMPLETE, | ||||
|     PARLIO_TX_QUEUE_MAX, | ||||
| }; | ||||
|  | ||||
| typedef enum { | ||||
|     PARLIO_TX_FSM_INIT_WAIT, | ||||
|     PARLIO_TX_FSM_INIT, | ||||
|     PARLIO_TX_FSM_ENABLE_WAIT, | ||||
|     PARLIO_TX_FSM_ENABLE, | ||||
|     PARLIO_TX_FSM_RUN_WAIT, | ||||
|     PARLIO_TX_FSM_RUN, | ||||
| } parlio_tx_fsm_t; | ||||
|  | ||||
| typedef struct parlio_group_t { | ||||
|     int group_id;             // group ID, index from 0 | ||||
|     portMUX_TYPE spinlock;    // to protect per-group register level concurrent access | ||||
|     parlio_hal_context_t hal; // hal layer for each group | ||||
|     parlio_tx_unit_handle_t tx_units[SOC_PARLIO_TX_UNITS_PER_GROUP]; // tx unit handles | ||||
| } parlio_group_t; | ||||
|  | ||||
| parlio_group_t *parlio_acquire_group_handle(int group_id); | ||||
|  | ||||
| void parlio_release_group_handle(parlio_group_t *group); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										628
									
								
								components/driver/parlio/parlio_tx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										628
									
								
								components/driver/parlio/parlio_tx.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,628 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <stdatomic.h> | ||||
| #include <sys/cdefs.h> | ||||
| #include <sys/param.h> | ||||
| #include "sdkconfig.h" | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "freertos/task.h" | ||||
| #include "freertos/queue.h" | ||||
| #if CONFIG_PARLIO_ENABLE_DEBUG_LOG | ||||
| // The local log level must be defined before including esp_log.h | ||||
| // Set the maximum log level for this source file | ||||
| #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG | ||||
| #endif | ||||
| #include "esp_log.h" | ||||
| #include "esp_check.h" | ||||
| #include "esp_attr.h" | ||||
| #include "esp_err.h" | ||||
| #include "esp_rom_gpio.h" | ||||
| #include "esp_intr_alloc.h" | ||||
| #include "esp_pm.h" | ||||
| #include "soc/parlio_periph.h" | ||||
| #include "hal/parlio_ll.h" | ||||
| #include "hal/gpio_hal.h" | ||||
| #include "hal/dma_types.h" | ||||
| #include "driver/gpio.h" | ||||
| #include "driver/parlio_tx.h" | ||||
| #include "parlio_private.h" | ||||
| #include "esp_memory_utils.h" | ||||
| #include "clk_tree.h" | ||||
| #include "esp_private/gdma.h" | ||||
|  | ||||
| static const char *TAG = "parlio-tx"; | ||||
|  | ||||
| typedef struct { | ||||
|     uint32_t idle_value; // Parallel IO bus idle value | ||||
|     const void *payload; // payload to be transmitted | ||||
|     size_t payload_bits; // payload size in bits | ||||
| } parlio_tx_trans_desc_t; | ||||
|  | ||||
| typedef struct parlio_tx_unit_t { | ||||
|     int unit_id;           // unit id | ||||
|     size_t data_width;     // data width | ||||
|     parlio_group_t *group; // group handle | ||||
|     intr_handle_t intr;    // allocated interrupt handle | ||||
|     esp_pm_lock_handle_t pm_lock;   // power management lock | ||||
|     gdma_channel_handle_t dma_chan; // DMA channel | ||||
| #if CONFIG_PM_ENABLE | ||||
|     char pm_lock_name[PARLIO_PM_LOCK_NAME_LEN_MAX]; // pm lock name | ||||
| #endif | ||||
|     portMUX_TYPE spinlock;     // prevent resource accessing by user and interrupt concurrently | ||||
|     uint32_t out_clk_freq_hz;  // output clock frequency | ||||
|     size_t max_transfer_bits;  // maximum transfer size in bits | ||||
|     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 | ||||
|     void *queues_storage;      // storage of transaction queues | ||||
|     QueueHandle_t trans_queues[PARLIO_TX_QUEUE_MAX]; // transaction queues | ||||
|     StaticQueue_t trans_queue_structs[PARLIO_TX_QUEUE_MAX]; // memory to store the static structure for trans_queues | ||||
|     parlio_tx_trans_desc_t *cur_trans; // points to current transaction | ||||
|     uint32_t idle_value_mask;          // mask of idle value | ||||
|     _Atomic parlio_tx_fsm_t fsm;       // Driver FSM state | ||||
|     parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done | ||||
|     void *user_data;                   // user data passed to the callback function | ||||
|     dma_descriptor_t *dma_nodes; // DMA descriptor nodes | ||||
|     parlio_tx_trans_desc_t trans_desc_pool[];   // transaction descriptor pool | ||||
| } parlio_tx_unit_t; | ||||
|  | ||||
| static void parlio_tx_default_isr(void *args); | ||||
|  | ||||
| static esp_err_t parlio_tx_register_to_group(parlio_tx_unit_t *unit) | ||||
| { | ||||
|     parlio_group_t *group = NULL; | ||||
|     int unit_id = -1; | ||||
|     for (int i = 0; i < SOC_PARLIO_GROUPS; i++) { | ||||
|         group = parlio_acquire_group_handle(i); | ||||
|         ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no memory for group (%d)", i); | ||||
|         portENTER_CRITICAL(&group->spinlock); | ||||
|         for (int j = 0; j < SOC_PARLIO_TX_UNITS_PER_GROUP; j++) { | ||||
|             if (group->tx_units[j] == NULL) { | ||||
|                 group->tx_units[j] = unit; | ||||
|                 unit_id = j; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         portEXIT_CRITICAL(&group->spinlock); | ||||
|         if (unit_id < 0) { | ||||
|             // didn't find a free unit slot in the group | ||||
|             parlio_release_group_handle(group); | ||||
|             group = NULL; | ||||
|         } else { | ||||
|             unit->unit_id = unit_id; | ||||
|             unit->group = group; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free tx unit"); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static void parlio_tx_unregister_to_group(parlio_tx_unit_t *unit, parlio_group_t *group) | ||||
| { | ||||
|     portENTER_CRITICAL(&group->spinlock); | ||||
|     group->tx_units[unit->unit_id] = NULL; | ||||
|     portEXIT_CRITICAL(&group->spinlock); | ||||
|     // the tx unit has a reference of the group, release it now | ||||
|     parlio_release_group_handle(group); | ||||
| } | ||||
|  | ||||
| static esp_err_t parlio_tx_create_trans_queue(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config) | ||||
| { | ||||
|     tx_unit->queue_depth = config->trans_queue_depth; | ||||
|     // the queue only saves transaction description pointers | ||||
|     tx_unit->queues_storage = heap_caps_calloc(config->trans_queue_depth * PARLIO_TX_QUEUE_MAX, sizeof(parlio_tx_trans_desc_t *), PARLIO_MEM_ALLOC_CAPS); | ||||
|     ESP_RETURN_ON_FALSE(tx_unit->queues_storage, ESP_ERR_NO_MEM, TAG, "no mem for queue storage"); | ||||
|     parlio_tx_trans_desc_t **pp_trans_desc = (parlio_tx_trans_desc_t **)tx_unit->queues_storage; | ||||
|     for (int i = 0; i < PARLIO_TX_QUEUE_MAX; i++) { | ||||
|         tx_unit->trans_queues[i] = xQueueCreateStatic(config->trans_queue_depth, sizeof(parlio_tx_trans_desc_t *), | ||||
|                                    (uint8_t *)pp_trans_desc, &tx_unit->trans_queue_structs[i]); | ||||
|         pp_trans_desc += config->trans_queue_depth; | ||||
|         // because trans_queue_structs is guaranteed to be non-NULL, so the trans_queues will also not be NULL | ||||
|         assert(tx_unit->trans_queues[i]); | ||||
|     } | ||||
|     // initialize the ready queue | ||||
|     parlio_tx_trans_desc_t *p_trans_desc = NULL; | ||||
|     for (int i = 0; i < config->trans_queue_depth; i++) { | ||||
|         p_trans_desc = &tx_unit->trans_desc_pool[i]; | ||||
|         ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &p_trans_desc, 0) == pdTRUE, | ||||
|                             ESP_ERR_INVALID_STATE, TAG, "ready queue full"); | ||||
|     } | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit) | ||||
| { | ||||
|     if (tx_unit->intr) { | ||||
|         ESP_RETURN_ON_ERROR(esp_intr_free(tx_unit->intr), TAG, "delete interrupt service failed"); | ||||
|     } | ||||
|     if (tx_unit->pm_lock) { | ||||
|         ESP_RETURN_ON_ERROR(esp_pm_lock_delete(tx_unit->pm_lock), TAG, "delete pm lock failed"); | ||||
|     } | ||||
|     if (tx_unit->dma_chan) { | ||||
|         ESP_RETURN_ON_ERROR(gdma_disconnect(tx_unit->dma_chan), TAG, "disconnect dma channel failed"); | ||||
|         ESP_RETURN_ON_ERROR(gdma_del_channel(tx_unit->dma_chan), TAG, "delete dma channel failed"); | ||||
|     } | ||||
|     for (int i = 0; i < PARLIO_TX_QUEUE_MAX; i++) { | ||||
|         if (tx_unit->trans_queues[i]) { | ||||
|             vQueueDelete(tx_unit->trans_queues[i]); | ||||
|         } | ||||
|     } | ||||
|     if (tx_unit->group) { | ||||
|         // de-register from group | ||||
|         parlio_tx_unregister_to_group(tx_unit, tx_unit->group); | ||||
|     } | ||||
|     free(tx_unit->queues_storage); | ||||
|     free(tx_unit->dma_nodes); | ||||
|     free(tx_unit); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t parlio_tx_unit_configure_gpio(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config) | ||||
| { | ||||
|     int group_id = tx_unit->group->group_id; | ||||
|     int unit_id = tx_unit->unit_id; | ||||
|     gpio_config_t gpio_conf = { | ||||
|         .intr_type = GPIO_INTR_DISABLE, | ||||
|         .mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_OUTPUT, | ||||
|         .pull_down_en = false, | ||||
|         .pull_up_en = true, | ||||
|     }; | ||||
|  | ||||
|     // connect peripheral signals via GPIO matrix | ||||
|     for (size_t i = 0; i < config->data_width; i++) { | ||||
|         if (config->data_gpio_nums[i] >= 0) { | ||||
|             gpio_conf.pin_bit_mask = BIT64(config->data_gpio_nums[i]); | ||||
|             ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config data GPIO failed"); | ||||
|             esp_rom_gpio_connect_out_signal(config->data_gpio_nums[i], | ||||
|                                             parlio_periph_signals.groups[group_id].tx_units[unit_id].data_sigs[i], false, false); | ||||
|             gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->data_gpio_nums[i]], PIN_FUNC_GPIO); | ||||
|         } | ||||
|     } | ||||
|     // Note: the valid signal will override TXD[PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG] | ||||
|     if (config->valid_gpio_num >= 0) { | ||||
|         gpio_conf.pin_bit_mask = BIT64(config->valid_gpio_num); | ||||
|         ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config valid GPIO failed"); | ||||
|         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], | ||||
|                                         false, false); | ||||
|         gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->valid_gpio_num], PIN_FUNC_GPIO); | ||||
|     } | ||||
|     if (config->clk_out_gpio_num >= 0) { | ||||
|         gpio_conf.pin_bit_mask = BIT64(config->clk_out_gpio_num); | ||||
|         ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk out GPIO failed"); | ||||
|         esp_rom_gpio_connect_out_signal(config->clk_out_gpio_num, | ||||
|                                         parlio_periph_signals.groups[group_id].tx_units[unit_id].clk_out_sig, false, false); | ||||
|         gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_out_gpio_num], PIN_FUNC_GPIO); | ||||
|     } | ||||
|     if (config->clk_in_gpio_num >= 0) { | ||||
|         gpio_conf.mode = config->flags.io_loop_back ? GPIO_MODE_INPUT_OUTPUT : GPIO_MODE_INPUT; | ||||
|         gpio_conf.pin_bit_mask = BIT64(config->clk_in_gpio_num); | ||||
|         ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config clk in GPIO failed"); | ||||
|         esp_rom_gpio_connect_in_signal(config->clk_in_gpio_num, | ||||
|                                        parlio_periph_signals.groups[group_id].tx_units[unit_id].clk_in_sig, false); | ||||
|         gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->clk_in_gpio_num], PIN_FUNC_GPIO); | ||||
|     } | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit) | ||||
| { | ||||
|     gdma_channel_alloc_config_t dma_chan_config = { | ||||
|         .direction = GDMA_CHANNEL_DIRECTION_TX, | ||||
|     }; | ||||
|     ESP_RETURN_ON_ERROR(gdma_new_channel(&dma_chan_config, &tx_unit->dma_chan), TAG, "allocate TX DMA channel failed"); | ||||
|     gdma_connect(tx_unit->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0)); | ||||
|     gdma_strategy_config_t gdma_strategy_conf = { | ||||
|         .auto_update_desc = true, | ||||
|         .owner_check = true, | ||||
|     }; | ||||
|     gdma_apply_strategy(tx_unit->dma_chan, &gdma_strategy_conf); | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config) | ||||
| { | ||||
|     parlio_hal_context_t *hal = &tx_unit->group->hal; | ||||
|     // parlio_ll_clock_source_t and parlio_clock_source_t are binary compatible if the clock source is from internal | ||||
|     parlio_ll_clock_source_t clk_src = (parlio_ll_clock_source_t)(config->clk_src); | ||||
|     uint32_t periph_src_clk_hz = 0; | ||||
|     // if the source clock is input from the GPIO, then we're in the slave mode | ||||
|     if (config->clk_in_gpio_num >= 0) { | ||||
|         clk_src = PARLIO_LL_CLK_SRC_PAD; | ||||
|         periph_src_clk_hz = config->input_clk_src_freq_hz; | ||||
|     } else { | ||||
|         // get the internal clock source frequency | ||||
|         clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz); | ||||
|     } | ||||
|     ESP_RETURN_ON_FALSE(periph_src_clk_hz, ESP_ERR_INVALID_ARG, TAG, "invalid clock source frequency"); | ||||
|  | ||||
| #if CONFIG_PM_ENABLE | ||||
|     if (clk_src != PARLIO_LL_CLK_SRC_PAD) { | ||||
|         // XTAL and PLL clock source will be turned off in light sleep, so we need to create a NO_LIGHT_SLEEP lock | ||||
|         sprintf(tx_unit->pm_lock_name, "parlio_tx_%d_%d", tx_unit->group->group_id, tx_unit->unit_id); // e.g. parlio_tx_0_0 | ||||
|         esp_err_t ret  = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, tx_unit->pm_lock_name, &tx_unit->pm_lock); | ||||
|         ESP_RETURN_ON_ERROR(ret, TAG, "create NO_LIGHT_SLEEP lock failed"); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     parlio_ll_tx_set_clock_source(hal->regs, clk_src); | ||||
|     // set clock division, round up | ||||
|     uint32_t div = (periph_src_clk_hz + config->output_clk_freq_hz - 1) / config->output_clk_freq_hz; | ||||
|     parlio_ll_tx_set_clock_div(hal->regs, div); | ||||
|     // precision lost due to division, calculate the real frequency | ||||
|     tx_unit->out_clk_freq_hz = periph_src_clk_hz / div; | ||||
|     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); | ||||
|     } | ||||
|  | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_unit_handle_t *ret_unit) | ||||
| { | ||||
| #if CONFIG_PARLIO_ENABLE_DEBUG_LOG | ||||
|     esp_log_level_set(TAG, ESP_LOG_DEBUG); | ||||
| #endif | ||||
|     esp_err_t ret = ESP_OK; | ||||
|     parlio_tx_unit_t *unit = NULL; | ||||
|     ESP_GOTO_ON_FALSE(config && ret_unit, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); | ||||
|     size_t data_width = config->data_width; | ||||
|     // data_width must be power of 2 and less than or equal to SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH | ||||
|     ESP_GOTO_ON_FALSE(data_width && (data_width <= SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH) && ((data_width & (data_width - 1)) == 0), | ||||
|                       ESP_ERR_INVALID_ARG, err, TAG, "invalid data width"); | ||||
|     // data_width must not conflict with the valid signal | ||||
|     ESP_GOTO_ON_FALSE(!(config->valid_gpio_num >= 0 && data_width > PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG), | ||||
|                       ESP_ERR_INVALID_ARG, err, TAG, "valid signal conflicts with data signal"); | ||||
|     ESP_GOTO_ON_FALSE(config->max_transfer_size && config->max_transfer_size <= PARLIO_LL_TX_MAX_BITS_PER_FRAME / 8, | ||||
|                       ESP_ERR_INVALID_ARG, err, TAG, "invalid max transfer size"); | ||||
| #if SOC_PARLIO_TX_CLK_SUPPORT_GATING | ||||
|     // clock gating is controlled by either the MSB bit of data bus or the valid signal | ||||
|     ESP_GOTO_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, err, TAG, "no gpio can control the clock gating"); | ||||
| #else | ||||
|     ESP_GOTO_ON_FALSE(config->flags.clk_gate_en == 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "clock gating is not supported"); | ||||
| #endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING | ||||
|  | ||||
|     // malloc unit memory | ||||
|     unit = heap_caps_calloc(1, sizeof(parlio_tx_unit_t) + sizeof(parlio_tx_trans_desc_t) * config->trans_queue_depth, PARLIO_MEM_ALLOC_CAPS); | ||||
|     ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no memory for tx unit"); | ||||
|     size_t dma_nodes_num = config->max_transfer_size / DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1; | ||||
|     // DMA descriptors must be placed in internal SRAM | ||||
|     unit->dma_nodes = heap_caps_calloc(dma_nodes_num, sizeof(dma_descriptor_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); | ||||
|     ESP_GOTO_ON_FALSE(unit->dma_nodes, ESP_ERR_NO_MEM, err, TAG, "no memory for DMA nodes"); | ||||
|     unit->max_transfer_bits = config->max_transfer_size * 8; | ||||
|  | ||||
|     unit->data_width = data_width; | ||||
|     //create transaction queue | ||||
|     ESP_GOTO_ON_ERROR(parlio_tx_create_trans_queue(unit, config), err, TAG, "create transaction queue failed"); | ||||
|  | ||||
|     // register the unit to a group | ||||
|     ESP_GOTO_ON_ERROR(parlio_tx_register_to_group(unit), err, TAG, "register unit to group failed"); | ||||
|     parlio_group_t *group = unit->group; | ||||
|     parlio_hal_context_t *hal = &group->hal; | ||||
|     // select the clock source | ||||
|     ESP_GOTO_ON_ERROR(parlio_select_periph_clock(unit, config), err, TAG, "set clock source failed"); | ||||
|  | ||||
|     // install interrupt service | ||||
|     int isr_flags = PARLIO_INTR_ALLOC_FLAG; | ||||
|     ret = esp_intr_alloc_intrstatus(parlio_periph_signals.groups[group->group_id].tx_irq_id, isr_flags, | ||||
|                                     (uint32_t)parlio_ll_get_interrupt_status_reg(hal->regs), | ||||
|                                     PARLIO_LL_EVENT_TX_EOF, parlio_tx_default_isr, unit, &unit->intr); | ||||
|     ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed"); | ||||
|  | ||||
|     // install DMA service | ||||
|     ESP_GOTO_ON_ERROR(parlio_tx_unit_init_dma(unit), err, TAG, "install tx DMA failed"); | ||||
|  | ||||
|     // reset fifo and core clock domain | ||||
|     parlio_ll_tx_reset_clock(hal->regs); | ||||
|     parlio_ll_tx_reset_fifo(hal->regs); | ||||
|     // stop output clock | ||||
|     parlio_ll_tx_enable_clock(hal->regs, false); | ||||
|     // clock gating | ||||
|     parlio_ll_tx_enable_clock_gating(hal->regs, config->flags.clk_gate_en); | ||||
|     // set data width | ||||
|     parlio_ll_tx_set_bus_width(hal->regs, data_width); | ||||
|     unit->idle_value_mask = (1 << data_width) - 1; | ||||
|     // whether to use the valid signal | ||||
|     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); | ||||
|     } | ||||
|     // set data byte packing order | ||||
|     if (data_width < 8) { | ||||
|         parlio_ll_tx_set_bit_pack_order(hal->regs, config->bit_pack_order); | ||||
|     } | ||||
|     // set sample clock edge | ||||
|     parlio_ll_tx_set_sample_clock_edge(hal->regs, config->sample_edge); | ||||
|  | ||||
|     // clear any pending interrupt | ||||
|     parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK); | ||||
|  | ||||
|     // GPIO Matrix/MUX configuration | ||||
|     ESP_GOTO_ON_ERROR(parlio_tx_unit_configure_gpio(unit, config), err, TAG, "configure gpio failed"); | ||||
|  | ||||
|     portMUX_INITIALIZE(&unit->spinlock); | ||||
|     atomic_init(&unit->fsm, PARLIO_TX_FSM_INIT); | ||||
|     // return TX unit handle | ||||
|     *ret_unit = unit; | ||||
|     ESP_LOGD(TAG, "new tx unit(%d,%d) at %p, out clk=%"PRIu32"Hz, queue_depth=%zu, idle_mask=%"PRIx32, | ||||
|              group->group_id, unit->unit_id, unit, unit->out_clk_freq_hz, unit->queue_depth, unit->idle_value_mask); | ||||
|     return ESP_OK; | ||||
|  | ||||
| err: | ||||
|     if (unit) { | ||||
|         parlio_destroy_tx_unit(unit); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_del_tx_unit(parlio_tx_unit_handle_t unit) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     ESP_RETURN_ON_FALSE(atomic_load(&unit->fsm) == PARLIO_TX_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state"); | ||||
|     ESP_LOGD(TAG, "del tx unit(%d,%d)", unit->group->group_id, unit->unit_id); | ||||
|     return parlio_destroy_tx_unit(unit); | ||||
| } | ||||
|  | ||||
| static void IRAM_ATTR parlio_tx_mount_dma_data(dma_descriptor_t *desc_head, const void *buffer, size_t len) | ||||
| { | ||||
|     size_t prepared_length = 0; | ||||
|     uint8_t *data = (uint8_t *)buffer; | ||||
|     dma_descriptor_t *desc = desc_head; | ||||
|     while (len > DMA_DESCRIPTOR_BUFFER_MAX_SIZE) { | ||||
|         desc->dw0.suc_eof = 0; // not the end of the transaction | ||||
|         desc->dw0.size = DMA_DESCRIPTOR_BUFFER_MAX_SIZE; | ||||
|         desc->dw0.length = DMA_DESCRIPTOR_BUFFER_MAX_SIZE; | ||||
|         desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; | ||||
|         desc->buffer = &data[prepared_length]; | ||||
|         desc = desc->next; // move to next descriptor | ||||
|         prepared_length += DMA_DESCRIPTOR_BUFFER_MAX_SIZE; | ||||
|         len -= DMA_DESCRIPTOR_BUFFER_MAX_SIZE; | ||||
|     } | ||||
|     if (len) { | ||||
|         desc->dw0.suc_eof = 1; // end of the transaction | ||||
|         desc->dw0.size = len; | ||||
|         desc->dw0.length = len; | ||||
|         desc->dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA; | ||||
|         desc->buffer = &data[prepared_length]; | ||||
|         desc = desc->next; // move to next descriptor | ||||
|         prepared_length += len; | ||||
|     } | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_tx_unit_wait_all_done(parlio_tx_unit_handle_t tx_unit, int timeout_ms) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     TickType_t wait_ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); | ||||
|     // recycle all pending transactions | ||||
|     parlio_tx_trans_desc_t *t = NULL; | ||||
|     size_t num_trans_inflight = tx_unit->num_trans_inflight; | ||||
|     for (size_t i = 0; i < num_trans_inflight; i++) { | ||||
|         ESP_RETURN_ON_FALSE(xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &t, wait_ticks) == pdTRUE, | ||||
|                             ESP_ERR_TIMEOUT, TAG, "flush timeout"); | ||||
|         ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &t, 0) == pdTRUE, | ||||
|                             ESP_ERR_INVALID_STATE, TAG, "ready queue full"); | ||||
|         tx_unit->num_trans_inflight--; | ||||
|     } | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_unit, const parlio_tx_event_callbacks_t *cbs, void *user_data) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(tx_unit && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|  | ||||
| #if CONFIG_PARLIO_ISR_IRAM_SAFE | ||||
|     if (cbs->on_trans_done) { | ||||
|         ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_trans_done callback not in IRAM"); | ||||
|     } | ||||
|     if (user_data) { | ||||
|         ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM"); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     tx_unit->on_trans_done = cbs->on_trans_done; | ||||
|     tx_unit->user_data = user_data; | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static void IRAM_ATTR parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t) | ||||
| { | ||||
|     parlio_hal_context_t *hal = &tx_unit->group->hal; | ||||
|  | ||||
|     tx_unit->cur_trans = t; | ||||
|  | ||||
|     // DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up | ||||
|     parlio_tx_mount_dma_data(tx_unit->dma_nodes, t->payload, (t->payload_bits + 7) / 8); | ||||
|  | ||||
|     parlio_ll_tx_reset_fifo(hal->regs); | ||||
|     parlio_ll_tx_reset_clock(hal->regs); | ||||
|     parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value); | ||||
|     parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits); | ||||
|  | ||||
|     gdma_start(tx_unit->dma_chan, (intptr_t)tx_unit->dma_nodes); | ||||
|     // wait until the data goes from the DMA to TX unit's FIFO | ||||
|     while (parlio_ll_tx_is_ready(hal->regs) == false); | ||||
|     // turn on the core clock after we start the TX unit | ||||
|     parlio_ll_tx_start(hal->regs, true); | ||||
|     parlio_ll_tx_enable_clock(hal->regs, true); | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t tx_unit) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     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)) { | ||||
|         // acquire power management lock | ||||
|         if (tx_unit->pm_lock) { | ||||
|             esp_pm_lock_acquire(tx_unit->pm_lock); | ||||
|         } | ||||
|         parlio_hal_context_t *hal = &tx_unit->group->hal; | ||||
|         parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_EOF, true); | ||||
|         atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE); | ||||
|     } else { | ||||
|         ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "unit not in init state"); | ||||
|     } | ||||
|  | ||||
|     // check if we need to start one pending transaction | ||||
|     parlio_tx_trans_desc_t *t = NULL; | ||||
|     expected_fsm = PARLIO_TX_FSM_ENABLE; | ||||
|     if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) { | ||||
|         // check if we need to start one transaction | ||||
|         if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) { | ||||
|             atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN); | ||||
|             parlio_tx_do_transaction(tx_unit, t); | ||||
|         } else { | ||||
|             atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(tx_unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     bool valid_state = false; | ||||
|     // check the supported states, and switch to intermediate state: INIT_WAIT | ||||
|     parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE; | ||||
|     if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_INIT_WAIT)) { | ||||
|         valid_state = true; | ||||
|     } | ||||
|     expected_fsm = PARLIO_TX_FSM_RUN; | ||||
|     if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_INIT_WAIT)) { | ||||
|         valid_state = true; | ||||
|         assert(tx_unit->cur_trans); | ||||
|         // recycle the interrupted transaction | ||||
|         if (xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &tx_unit->cur_trans, 0) == pdFALSE) { | ||||
|             // this should never happen | ||||
|             valid_state = false; | ||||
|         } | ||||
|         tx_unit->cur_trans = NULL; | ||||
|     } | ||||
|     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 | ||||
|     parlio_hal_context_t *hal = &tx_unit->group->hal; | ||||
|     gdma_stop(tx_unit->dma_chan); | ||||
|     parlio_ll_tx_start(hal->regs, false); | ||||
|     parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_EOF, false); | ||||
|  | ||||
|     // release power management lock | ||||
|     if (tx_unit->pm_lock) { | ||||
|         esp_pm_lock_release(tx_unit->pm_lock); | ||||
|     } | ||||
|  | ||||
|     // finally we switch to the INIT state | ||||
|     atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_INIT); | ||||
|  | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *payload, size_t payload_bits, const parlio_transmit_config_t *config) | ||||
| { | ||||
|     ESP_RETURN_ON_FALSE(tx_unit && payload && payload_bits, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); | ||||
|     ESP_RETURN_ON_FALSE((payload_bits % tx_unit->data_width) == 0, ESP_ERR_INVALID_ARG, TAG, "payload bit length must align to bus width"); | ||||
|     ESP_RETURN_ON_FALSE(payload_bits <= tx_unit->max_transfer_bits, ESP_ERR_INVALID_ARG, TAG, "payload bit length too large"); | ||||
| #if !SOC_PARLIO_TRANS_BIT_ALIGN | ||||
|     ESP_RETURN_ON_FALSE((payload_bits % 8) == 0, ESP_ERR_INVALID_ARG, TAG, "payload bit length must be multiple of 8"); | ||||
| #endif // !SOC_PARLIO_TRANS_BIT_ALIGN | ||||
|  | ||||
|     // acquire one transaction description from ready queue or complete queue | ||||
|     parlio_tx_trans_desc_t *t = NULL; | ||||
|     if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &t, 0) != pdTRUE) { | ||||
|         if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &t, 0) == pdTRUE) { | ||||
|             tx_unit->num_trans_inflight--; | ||||
|         } | ||||
|     } | ||||
|     ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth"); | ||||
|  | ||||
|     // fill in the transaction descriptor | ||||
|     memset(t, 0, sizeof(parlio_tx_trans_desc_t)); | ||||
|     t->payload = payload; | ||||
|     t->payload_bits = payload_bits; | ||||
|     t->idle_value = config->idle_value & tx_unit->idle_value_mask; | ||||
|  | ||||
|     // send the transaction descriptor to progress queue | ||||
|     ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE, | ||||
|                         ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue"); | ||||
|     tx_unit->num_trans_inflight++; | ||||
|  | ||||
|     // check if we need to start one pending transaction | ||||
|     parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE; | ||||
|     if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) { | ||||
|         // check if we need to start one transaction | ||||
|         if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) { | ||||
|             atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN); | ||||
|             parlio_tx_do_transaction(tx_unit, t); | ||||
|         } else { | ||||
|             atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return ESP_OK; | ||||
| } | ||||
|  | ||||
| static void IRAM_ATTR parlio_tx_default_isr(void *args) | ||||
| { | ||||
|     parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *)args; | ||||
|     parlio_group_t *group = tx_unit->group; | ||||
|     parlio_hal_context_t *hal = &group->hal; | ||||
|     BaseType_t high_task_woken = pdFALSE; | ||||
|     bool need_yield = false; | ||||
|  | ||||
|     uint32_t status = parlio_ll_tx_get_interrupt_status(hal->regs); | ||||
|  | ||||
|     if (status & PARLIO_LL_EVENT_TX_EOF) { | ||||
|         parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_EOF); | ||||
|         parlio_ll_tx_enable_clock(hal->regs, false); | ||||
|         parlio_ll_tx_start(hal->regs, false); | ||||
|  | ||||
|         parlio_tx_trans_desc_t *trans_desc = NULL; | ||||
|  | ||||
|         parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_RUN; | ||||
|         if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_ENABLE_WAIT)) { | ||||
|             trans_desc = tx_unit->cur_trans; | ||||
|             // move current finished transaction to the complete queue | ||||
|             xQueueSendFromISR(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &trans_desc, &high_task_woken); | ||||
|             if (high_task_woken == pdTRUE) { | ||||
|                 need_yield = true; | ||||
|             } | ||||
|             tx_unit->cur_trans = NULL; | ||||
|             atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE); | ||||
|         } | ||||
|  | ||||
|         // invoke callback | ||||
|         parlio_tx_done_callback_t done_cb = tx_unit->on_trans_done; | ||||
|         if (done_cb) { | ||||
|             if (done_cb(tx_unit, NULL, tx_unit->user_data)) { | ||||
|                 need_yield = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // if the tx unit is till in enable state (i.e. not disabled by user), let's try start the next pending transaction | ||||
|         expected_fsm = PARLIO_TX_FSM_ENABLE; | ||||
|         if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) { | ||||
|             if (xQueueReceiveFromISR(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &trans_desc, &high_task_woken) == pdTRUE) { | ||||
|                 atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN); | ||||
|                 parlio_tx_do_transaction(tx_unit, trans_desc); | ||||
|                 if (high_task_woken == pdTRUE) { | ||||
|                     need_yield = true; | ||||
|                 } | ||||
|             } else { | ||||
|                 atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (need_yield) { | ||||
|             portYIELD_FROM_ISR(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -42,7 +42,7 @@ typedef struct { | ||||
|  * @brief Prototype of RMT event callback | ||||
|  * @param[in] tx_chan RMT channel handle, created from `rmt_new_tx_channel()` | ||||
|  * @param[in] edata Point to RMT event data. The lifecycle of this pointer memory is inside this function, | ||||
|  *                  user should copy it into static memory if used outside this funcion. | ||||
|  *                  user should copy it into static memory if used outside this function. | ||||
|  * @param[in] user_ctx User registered context, passed from `rmt_tx_register_event_callbacks()` | ||||
|  * | ||||
|  * @return Whether a high priority task has been waken up by this callback function | ||||
| @@ -62,7 +62,7 @@ typedef struct { | ||||
|  * | ||||
|  * @param[in] rx_chan RMT channel handle, created from `rmt_new_rx_channel()` | ||||
|  * @param[in] edata Point to RMT event data. The lifecycle of this pointer memory is inside this function, | ||||
|  *                  user should copy it into static memory if used outside this funcion. | ||||
|  *                  user should copy it into static memory if used outside this function. | ||||
|  * @param[in] user_ctx User registered context, passed from `rmt_rx_register_event_callbacks()` | ||||
|  * @return Whether a high priority task has been waken up by this function | ||||
|  */ | ||||
|   | ||||
							
								
								
									
										18
									
								
								components/driver/test_apps/parlio/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								components/driver/test_apps/parlio/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| # This is the project CMakeLists.txt file for the test subproject | ||||
| cmake_minimum_required(VERSION 3.16) | ||||
|  | ||||
| include($ENV{IDF_PATH}/tools/cmake/project.cmake) | ||||
| project(parlio_test) | ||||
|  | ||||
| if(CONFIG_COMPILER_DUMP_RTL_FILES) | ||||
|     add_custom_target(check_test_app_sections ALL | ||||
|                       COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py | ||||
|                       --rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/ | ||||
|                       --elf-file ${CMAKE_BINARY_DIR}/parlio_test.elf | ||||
|                       find-refs | ||||
|                       --from-sections=.iram0.text | ||||
|                       --to-sections=.flash.text,.flash.rodata | ||||
|                       --exit-code | ||||
|                       DEPENDS ${elf} | ||||
|                       ) | ||||
| endif() | ||||
							
								
								
									
										2
									
								
								components/driver/test_apps/parlio/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								components/driver/test_apps/parlio/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| | Supported Targets | ESP32-C6 | ESP32-H2 | | ||||
| | ----------------- | -------- | -------- | | ||||
							
								
								
									
										7
									
								
								components/driver/test_apps/parlio/main/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								components/driver/test_apps/parlio/main/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| set(srcs "test_app_main.c" | ||||
|          "test_parlio_tx.c") | ||||
|  | ||||
| # In order for the cases defined by `TEST_CASE` to be linked into the final elf, | ||||
| # the component can be registered as WHOLE_ARCHIVE | ||||
| idf_component_register(SRCS ${srcs} | ||||
|                        WHOLE_ARCHIVE) | ||||
							
								
								
									
										51
									
								
								components/driver/test_apps/parlio/main/test_app_main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								components/driver/test_apps/parlio/main/test_app_main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include "unity.h" | ||||
| #include "unity_test_runner.h" | ||||
| #include "esp_heap_caps.h" | ||||
|  | ||||
| // Some resources are lazy allocated in pulse_cnt driver, the threshold is left for that case | ||||
| #define TEST_MEMORY_LEAK_THRESHOLD (-300) | ||||
|  | ||||
| static size_t before_free_8bit; | ||||
| static size_t before_free_32bit; | ||||
|  | ||||
| static void check_leak(size_t before_free, size_t after_free, const char *type) | ||||
| { | ||||
|     ssize_t delta = after_free - before_free; | ||||
|     printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); | ||||
|     TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); | ||||
| } | ||||
|  | ||||
| void setUp(void) | ||||
| { | ||||
|     before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); | ||||
|     before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); | ||||
| } | ||||
|  | ||||
| void tearDown(void) | ||||
| { | ||||
|     size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); | ||||
|     size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); | ||||
|     check_leak(before_free_8bit, after_free_8bit, "8BIT"); | ||||
|     check_leak(before_free_32bit, after_free_32bit, "32BIT"); | ||||
| } | ||||
|  | ||||
| void app_main(void) | ||||
| { | ||||
|     //  ____                 _ _      _   ___ ___    _____         _ | ||||
|     // |  _ \ __ _ _ __ __ _| | | ___| | |_ _/ _ \  |_   _|__  ___| |_ | ||||
|     // | |_) / _` | '__/ _` | | |/ _ \ |  | | | | |   | |/ _ \/ __| __| | ||||
|     // |  __/ (_| | | | (_| | | |  __/ |  | | |_| |   | |  __/\__ \ |_ | ||||
|     // |_|   \__,_|_|  \__,_|_|_|\___|_| |___\___/    |_|\___||___/\__| | ||||
|     printf(" ____                 _ _      _   ___ ___    _____         _\r\n"); | ||||
|     printf("|  _ \\ __ _ _ __ __ _| | | ___| | |_ _/ _ \\  |_   _|__  ___| |_\r\n"); | ||||
|     printf("| |_) / _` | '__/ _` | | |/ _ \\ |  | | | | |   | |/ _ \\/ __| __|\r\n"); | ||||
|     printf("|  __/ (_| | | | (_| | | |  __/ |  | | |_| |   | |  __/\\__ \\ |_\r\n"); | ||||
|     printf("|_|   \\__,_|_|  \\__,_|_|_|\\___|_| |___\\___/    |_|\\___||___/\\__|\r\n"); | ||||
|     unity_run_menu(); | ||||
| } | ||||
							
								
								
									
										40
									
								
								components/driver/test_apps/parlio/main/test_board.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								components/driver/test_apps/parlio/main/test_board.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "sdkconfig.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| #if CONFIG_IDF_TARGET_ESP32C6 | ||||
| #define TEST_CLK_GPIO       10 | ||||
| #define TEST_DATA0_GPIO     0 | ||||
| #define TEST_DATA1_GPIO     1 | ||||
| #define TEST_DATA2_GPIO     2 | ||||
| #define TEST_DATA3_GPIO     3 | ||||
| #define TEST_DATA4_GPIO     4 | ||||
| #define TEST_DATA5_GPIO     5 | ||||
| #define TEST_DATA6_GPIO     6 | ||||
| #define TEST_DATA7_GPIO     7 | ||||
| #elif CONFIG_IDF_TARGET_ESP32H2 | ||||
| #define TEST_CLK_GPIO       10 | ||||
| #define TEST_DATA0_GPIO     0 | ||||
| #define TEST_DATA1_GPIO     1 | ||||
| #define TEST_DATA2_GPIO     2 | ||||
| #define TEST_DATA3_GPIO     3 | ||||
| #define TEST_DATA4_GPIO     4 | ||||
| #define TEST_DATA5_GPIO     5 | ||||
| #define TEST_DATA6_GPIO     8 | ||||
| #define TEST_DATA7_GPIO     9 | ||||
| #else | ||||
| #error "Unsupported target" | ||||
| #endif | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										280
									
								
								components/driver/test_apps/parlio/main/test_parlio_tx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								components/driver/test_apps/parlio/main/test_parlio_tx.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include "sdkconfig.h" | ||||
| #include "freertos/FreeRTOS.h" | ||||
| #include "freertos/task.h" | ||||
| #include "unity.h" | ||||
| #include "driver/parlio_tx.h" | ||||
| #include "driver/gpio.h" | ||||
| #include "soc/soc_caps.h" | ||||
| #include "esp_attr.h" | ||||
| #include "test_board.h" | ||||
|  | ||||
| #if CONFIG_PARLIO_ISR_IRAM_SAFE | ||||
| #define TEST_PARLIO_CALLBACK_ATTR IRAM_ATTR | ||||
| #else | ||||
| #define TEST_PARLIO_CALLBACK_ATTR | ||||
| #endif | ||||
|  | ||||
| TEST_CASE("parallel_tx_unit_install_uninstall", "[parlio_tx]") | ||||
| { | ||||
|     printf("install tx units exhaustively\r\n"); | ||||
|     parlio_tx_unit_handle_t units[SOC_PARLIO_GROUPS * SOC_PARLIO_TX_UNITS_PER_GROUP]; | ||||
|     int k = 0; | ||||
|     parlio_tx_unit_config_t config = { | ||||
|         .clk_src = PARLIO_CLK_SRC_DEFAULT, | ||||
|         .data_width = SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH, | ||||
|         .clk_in_gpio_num = -1, // clock source from internal | ||||
|         .clk_out_gpio_num = 0, | ||||
|         .output_clk_freq_hz = 1 * 1000 * 1000, | ||||
|         .trans_queue_depth = 4, | ||||
|         .max_transfer_size = 64, | ||||
|         .valid_gpio_num = -1, | ||||
|     }; | ||||
|     for (int i = 0; i < SOC_PARLIO_GROUPS; i++) { | ||||
|         for (int j = 0; j < SOC_PARLIO_TX_UNITS_PER_GROUP; j++) { | ||||
|             TEST_ESP_OK(parlio_new_tx_unit(&config, &units[k++])); | ||||
|         } | ||||
|     } | ||||
|     TEST_ESP_ERR(ESP_ERR_NOT_FOUND, parlio_new_tx_unit(&config, &units[0])); | ||||
|  | ||||
|     for (int i = 0; i < k; i++) { | ||||
|         TEST_ESP_OK(parlio_del_tx_unit(units[i])); | ||||
|     } | ||||
|  | ||||
|     printf("install tx unit with valid signal and external core clock\r\n"); | ||||
|     // clock from external | ||||
|     config.clk_in_gpio_num = 2; | ||||
|     // failed because of invalid clock source frequency | ||||
|     TEST_ESP_ERR(ESP_ERR_INVALID_ARG, parlio_new_tx_unit(&config, &units[0])); | ||||
|     config.input_clk_src_freq_hz = 1000000; | ||||
|  | ||||
|     config.valid_gpio_num = 0; | ||||
|     // failed because of data line conflict with valid signal | ||||
|     TEST_ESP_ERR(ESP_ERR_INVALID_ARG, parlio_new_tx_unit(&config, &units[0])); | ||||
|  | ||||
|     config.data_width = 4; | ||||
|     TEST_ESP_OK(parlio_new_tx_unit(&config, &units[0])); | ||||
|     TEST_ESP_OK(parlio_tx_unit_enable(units[0])); | ||||
|     // delete unit before it's disabled is not allowed | ||||
|     TEST_ESP_ERR(ESP_ERR_INVALID_STATE, parlio_del_tx_unit(units[0])); | ||||
|     TEST_ESP_OK(parlio_tx_unit_disable(units[0])); | ||||
|     TEST_ESP_OK(parlio_del_tx_unit(units[0])); | ||||
| } | ||||
|  | ||||
| TEST_PARLIO_CALLBACK_ATTR | ||||
| static bool test_parlio_tx_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx) | ||||
| { | ||||
|     BaseType_t high_task_wakeup = pdFALSE; | ||||
|     TaskHandle_t task = (TaskHandle_t)user_ctx; | ||||
|     vTaskNotifyGiveFromISR(task, &high_task_wakeup); | ||||
|     return high_task_wakeup == pdTRUE; | ||||
| } | ||||
|  | ||||
| TEST_CASE("parallel_tx_unit_trans_done_event", "[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 = -1,  // use internal clock source | ||||
|         .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, | ||||
|         .trans_queue_depth = 8, | ||||
|         .max_transfer_size = 128, | ||||
|         .bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB, | ||||
|         .sample_edge = PARLIO_SAMPLE_EDGE_POS, | ||||
|     }; | ||||
|     TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit)); | ||||
|     TEST_ESP_OK(parlio_tx_unit_enable(tx_unit)); | ||||
|  | ||||
|     printf("register trans_done event callback\r\n"); | ||||
|     parlio_tx_event_callbacks_t cbs = { | ||||
|         .on_trans_done = test_parlio_tx_done_callback, | ||||
|     }; | ||||
|     TEST_ESP_OK(parlio_tx_unit_register_event_callbacks(tx_unit, &cbs, xTaskGetCurrentTaskHandle())); | ||||
|  | ||||
|     printf("send packets and check event is fired\r\n"); | ||||
|     parlio_transmit_config_t transmit_config = { | ||||
|         .idle_value = 0x00, | ||||
|     }; | ||||
|     uint8_t payload[64] = {0}; | ||||
|     for (int i = 0; i < 64; i++) { | ||||
|         payload[i] = i; | ||||
|     } | ||||
|     TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 64 * sizeof(uint8_t) * 8, &transmit_config)); | ||||
|     TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, portMAX_DELAY)); | ||||
|     TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 64 * sizeof(uint8_t) * 8, &transmit_config)); | ||||
|     TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdTRUE, portMAX_DELAY)); | ||||
|  | ||||
|     TEST_ESP_OK(parlio_tx_unit_disable(tx_unit)); | ||||
|     TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); | ||||
| }; | ||||
|  | ||||
| TEST_CASE("parallel_tx_unit_enable_disable", "[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 = -1,  // use internal clock source | ||||
|         .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, | ||||
|         .trans_queue_depth = 64, | ||||
|         .max_transfer_size = 256, | ||||
|         .bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB, | ||||
|         .sample_edge = PARLIO_SAMPLE_EDGE_POS, | ||||
|     }; | ||||
|     TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit)); | ||||
|     TEST_ESP_OK(parlio_tx_unit_enable(tx_unit)); | ||||
|  | ||||
|     printf("send packets for multiple times\r\n"); | ||||
|     parlio_transmit_config_t transmit_config = { | ||||
|         .idle_value = 0x00, | ||||
|     }; | ||||
|     uint8_t payload[128] = {0}; | ||||
|     for (int i = 0; i < 128; i++) { | ||||
|         payload[i] = i; | ||||
|     } | ||||
|     for (int j = 0; j < 64; j++) { | ||||
|         TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 128 * sizeof(uint8_t) * 8, &transmit_config)); | ||||
|     } | ||||
|  | ||||
|     printf("disable the transaction in the middle\r\n"); | ||||
|     while (parlio_tx_unit_disable(tx_unit) != ESP_OK) { | ||||
|         esp_rom_delay_us(1000); | ||||
|     } | ||||
|     vTaskDelay(pdMS_TO_TICKS(100)); | ||||
|  | ||||
|     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_wait_all_done(tx_unit, -1)); | ||||
|     TEST_ESP_OK(parlio_tx_unit_disable(tx_unit)); | ||||
|     TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("parallel_tx_unit_idle_value", "[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 = -1,  // use internal clock source | ||||
|         .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, | ||||
|         .trans_queue_depth = 4, | ||||
|         .max_transfer_size = 64, | ||||
|         .bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB, | ||||
|         .sample_edge = PARLIO_SAMPLE_EDGE_POS, | ||||
|         .flags.io_loop_back = 1,   // enable loop back by GPIO matrix, so that we can read the level of the data line by gpio driver | ||||
|     }; | ||||
|     TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit)); | ||||
|     TEST_ESP_OK(parlio_tx_unit_enable(tx_unit)); | ||||
|  | ||||
|     printf("send packet with different idle_value\r\n"); | ||||
|     parlio_transmit_config_t transmit_config = { | ||||
|         .idle_value = 0x00, | ||||
|     }; | ||||
|     uint8_t payload[8] = {0}; | ||||
|     for (int i = 0; i < 8; i++) { | ||||
|         payload[i] = i; | ||||
|     } | ||||
|     for (int j = 0; j < 16; j++) { | ||||
|         transmit_config.idle_value = j; | ||||
|         TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, sizeof(payload) * 8, &transmit_config)); | ||||
|         TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, 100)); | ||||
|         TEST_ASSERT_EQUAL(j & 0x01, gpio_get_level(TEST_DATA0_GPIO)); | ||||
|     } | ||||
|  | ||||
|     TEST_ESP_OK(parlio_tx_unit_disable(tx_unit)); | ||||
|     TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); | ||||
| } | ||||
|  | ||||
| #if SOC_PARLIO_TX_CLK_SUPPORT_GATING | ||||
| TEST_CASE("parallel_tx_clock_gating", "[paralio_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 = 2, | ||||
|         .clk_in_gpio_num = -1,  // use internal clock source | ||||
|         .valid_gpio_num = TEST_DATA7_GPIO, // generate the valid signal | ||||
|         .clk_out_gpio_num = TEST_CLK_GPIO, | ||||
|         .data_gpio_nums = { | ||||
|             TEST_DATA0_GPIO, | ||||
|             TEST_DATA1_GPIO, | ||||
|         }, | ||||
|         .output_clk_freq_hz = 1 * 1000 * 1000, | ||||
|         .trans_queue_depth = 4, | ||||
|         .max_transfer_size = 64, | ||||
|         .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, | ||||
|         .sample_edge = PARLIO_SAMPLE_EDGE_POS, | ||||
|         .flags.clk_gate_en = true, // enable clock gating, controlled by the level of TEST_DATA7_GPIO | ||||
|         .flags.io_loop_back = true, // for reading the level of the clock line in IDLE state | ||||
|     }; | ||||
|     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 = { | ||||
|         .idle_value = 0x00, | ||||
|     }; | ||||
|     uint8_t payload[8] = {0}; | ||||
|     for (int i = 0; i < 8; i++) { | ||||
|         payload[i] = 0x1B; // 8'b00011011, in PARLIO_BIT_PACK_ORDER_MSB, you should see 2'b00, 2'b01, 2'b10, 2'b11 on the data line | ||||
|     } | ||||
|     TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 8 * 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_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload, 8 * 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_ESP_OK(parlio_tx_unit_disable(tx_unit)); | ||||
|     TEST_ESP_OK(parlio_del_tx_unit(tx_unit)); | ||||
| } | ||||
| #endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING | ||||
							
								
								
									
										22
									
								
								components/driver/test_apps/parlio/pytest_parlio_unity.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								components/driver/test_apps/parlio/pytest_parlio_unity.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| # SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
| # SPDX-License-Identifier: CC0-1.0 | ||||
|  | ||||
| import pytest | ||||
| from pytest_embedded import Dut | ||||
|  | ||||
|  | ||||
| @pytest.mark.esp32c6 | ||||
| @pytest.mark.esp32h2 | ||||
| @pytest.mark.generic | ||||
| @pytest.mark.parametrize( | ||||
|     'config', | ||||
|     [ | ||||
|         'iram_safe', | ||||
|         'release', | ||||
|     ], | ||||
|     indirect=True, | ||||
| ) | ||||
| def test_parlio(dut: Dut) -> None: | ||||
|     dut.expect_exact('Press ENTER to see the list of tests') | ||||
|     dut.write('*') | ||||
|     dut.expect_unity_test_output() | ||||
| @@ -0,0 +1,7 @@ | ||||
| CONFIG_COMPILER_DUMP_RTL_FILES=y | ||||
| CONFIG_COMPILER_OPTIMIZATION_NONE=y | ||||
| CONFIG_PARLIO_ISR_IRAM_SAFE=y | ||||
| # place non-ISR FreeRTOS functions in Flash | ||||
| CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y | ||||
| # silent the error check, as the error string are stored in rodata, causing RTL check failure | ||||
| CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y | ||||
							
								
								
									
										5
									
								
								components/driver/test_apps/parlio/sdkconfig.ci.release
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								components/driver/test_apps/parlio/sdkconfig.ci.release
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| CONFIG_PM_ENABLE=y | ||||
| CONFIG_FREERTOS_USE_TICKLESS_IDLE=y | ||||
| CONFIG_COMPILER_OPTIMIZATION_SIZE=y | ||||
| CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y | ||||
| CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y | ||||
							
								
								
									
										5
									
								
								components/driver/test_apps/parlio/sdkconfig.defaults
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								components/driver/test_apps/parlio/sdkconfig.defaults
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| # This file was generated using idf.py save-defconfig. It can be edited manually. | ||||
| # Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration | ||||
| # | ||||
| CONFIG_ESP_TASK_WDT_INIT=n | ||||
| CONFIG_FREERTOS_HZ=1000 | ||||
| @@ -105,6 +105,10 @@ if(NOT BOOTLOADER_BUILD) | ||||
|         list(APPEND srcs "etm_hal.c") | ||||
|     endif() | ||||
|  | ||||
|     if(CONFIG_SOC_PARLIO_SUPPORTED) | ||||
|         list(APPEND srcs "parlio_hal.c") | ||||
|     endif() | ||||
|  | ||||
|     if(CONFIG_SOC_ADC_DMA_SUPPORTED) | ||||
|         list(APPEND srcs "adc_hal.c") | ||||
|     endif() | ||||
|   | ||||
| @@ -58,6 +58,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) | ||||
|             return PCR_PWM_CLK_EN; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_CLK_EN; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_CLK_EN; | ||||
|         case PERIPH_AES_MODULE: | ||||
|             return PCR_AES_CLK_EN; | ||||
|         case PERIPH_SHA_MODULE: | ||||
| @@ -136,6 +138,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en | ||||
|             return PCR_PWM_RST_EN; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_RST_EN; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_RST_EN; | ||||
|         case PERIPH_ECC_MODULE: | ||||
|             return PCR_ECC_RST_EN; | ||||
|         case PERIPH_TEMPSENSOR_MODULE: | ||||
| @@ -233,6 +237,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) | ||||
|             return PCR_PWM_CONF_REG; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_CONF_REG; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_IO_CONF_REG; | ||||
|         case PERIPH_AES_MODULE: | ||||
|             return PCR_AES_CONF_REG; | ||||
|         case PERIPH_SHA_MODULE: | ||||
| @@ -297,6 +303,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) | ||||
|             return PCR_PWM_CONF_REG; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_CONF_REG; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_IO_CONF_REG; | ||||
|         case PERIPH_AES_MODULE: | ||||
|             return PCR_AES_CONF_REG; | ||||
|         case PERIPH_SHA_MODULE: | ||||
|   | ||||
							
								
								
									
										612
									
								
								components/hal/esp32c6/include/hal/parlio_ll.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										612
									
								
								components/hal/esp32c6/include/hal/parlio_ll.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,612 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| // Note that most of the register operations in this layer are non-atomic operations. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include "hal/assert.h" | ||||
| #include "hal/misc.h" | ||||
| #include "soc/pcr_struct.h" | ||||
| #include "soc/parl_io_struct.h" | ||||
| #include "hal/parlio_types.h" | ||||
|  | ||||
| #define PARLIO_LL_RX_MAX_BYTES_PER_FRAME 0xFFFF | ||||
| #define PARLIO_LL_RX_MAX_CLOCK_DIV       0x10000 | ||||
| #define PARLIO_LL_RX_MAX_TIMEOUT         0xFFFF | ||||
|  | ||||
| #define PARLIO_LL_TX_MAX_BYTES_PER_FRAME 0xFFFF | ||||
| #define PARLIO_LL_TX_MAX_BITS_PER_FRAME  (PARLIO_LL_TX_MAX_BYTES_PER_FRAME * 8) | ||||
| #define PARLIO_LL_TX_MAX_CLOCK_DIV       0x10000 | ||||
|  | ||||
| #define PARLIO_LL_EVENT_TX_FIFO_EMPTY    (1 << 0) | ||||
| #define PARLIO_LL_EVENT_RX_FIFO_FULL     (1 << 1) | ||||
| #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_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 | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| typedef enum { | ||||
|     PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL, | ||||
|     PARLIO_LL_CLK_SRC_PLL_F240M = PARLIO_CLK_SRC_PLL_F240M, | ||||
|     PARLIO_LL_CLK_SRC_PAD, // clock source from GPIO pad | ||||
| } parlio_ll_clock_source_t; | ||||
|  | ||||
| typedef enum { | ||||
|     PARLIO_LL_RX_EOF_COND_RX_FULL,     /*!< RX unit generates EOF event when it receives enough data */ | ||||
|     PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */ | ||||
| } parlio_ll_rx_eof_cond_t; | ||||
|  | ||||
| ///////////////////////////////////////RX Unit/////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock source for the RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param src Clock source | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src) | ||||
| { | ||||
|     (void)dev; | ||||
|     uint32_t clk_sel = 0; | ||||
|     switch (src) { | ||||
|     case PARLIO_LL_CLK_SRC_XTAL: | ||||
|         clk_sel = 0; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PLL_F240M: | ||||
|         clk_sel = 1; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PAD: | ||||
|         clk_sel = 3; | ||||
|         break; | ||||
|  | ||||
|     default: // unsupported clock source | ||||
|         HAL_ASSERT(false); | ||||
|         break; | ||||
|     } | ||||
|     PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock divider for the RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param div Clock divider | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_clock_div(parl_io_dev_t *dev, uint32_t div) | ||||
| { | ||||
|     (void)dev; | ||||
|     HAL_ASSERT(div > 0 && div <= PARLIO_LL_RX_MAX_CLOCK_DIV); | ||||
|     PCR.parl_clk_rx_conf.parl_clk_rx_div_num = div - 1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset the RX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_rx_conf.parl_rx_rst_en = 1; | ||||
|     PCR.parl_clk_rx_conf.parl_rx_rst_en = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Enable the RX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_rx_conf.parl_clk_rx_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the condition to generate the RX EOF event | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param cond RX EOF condition | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_rx_eof_cond_t cond) | ||||
| { | ||||
|     dev->rx_cfg0.rx_eof_gen_sel = cond; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Start RX unit to sample the input data | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to start, False to stop | ||||
|  */ | ||||
| static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_cfg0.rx_start = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the receive length | ||||
|  * | ||||
|  * @note The receive length can be used to generate DMA EOF signal, or to work as a frame end delimiter | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param bitlen Number of bits to receive in the next transaction, bitlen must be a multiple of 8 | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bitlen) | ||||
| { | ||||
|     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_cfg0, rx_data_bytelen, bitlen / 8); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sub mode of the level controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param active_level Level of the external enable signal, true for active high, false for active low | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool active_level) | ||||
| { | ||||
|     dev->rx_cfg0.rx_smp_mode_sel = 0; | ||||
|     dev->rx_cfg0.rx_level_submode_sel = !active_level; // 0: active low, 1: active high | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sub mode of the pulse controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param start_inc Whether the start pulse is counted | ||||
|  * @param end_inc Whether the end pulse is counted | ||||
|  * @param end_by_len Whether to use the frame length to determine the end of the frame | ||||
|  * @param pulse_inv Whether the pulse is inverted | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool start_inc, bool end_inc, bool end_by_len, bool pulse_inv) | ||||
| { | ||||
|     uint32_t submode = 0; | ||||
|     uint32_t step = 1; | ||||
|     if (end_by_len) { | ||||
|         submode += 4; | ||||
|     } else { | ||||
|         step = 2; | ||||
|         if (!end_inc) { | ||||
|             submode += 1; | ||||
|         } | ||||
|     } | ||||
|     if (!start_inc) { | ||||
|         submode += step; | ||||
|     } | ||||
|     if (pulse_inv) { | ||||
|         submode += 6; | ||||
|     } | ||||
|     dev->rx_cfg0.rx_smp_mode_sel = 1; | ||||
|     dev->rx_cfg0.rx_pulse_submode_sel = submode; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the receive mode to software controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_soft_recv_mode(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->rx_cfg0.rx_smp_mode_sel = 2; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Whether to start the software controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_cfg0.rx_sw_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sample clock edge | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param edge Sample clock edge | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge) | ||||
| { | ||||
|     dev->rx_cfg0.rx_clk_edge_sel = edge; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the order to pack bits into one byte | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param order Packing order | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order) | ||||
| { | ||||
|     dev->rx_cfg0.rx_bit_pack_order = order; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the bus width of the RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param width Bus width | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_bus_width(parl_io_dev_t *dev, uint32_t width) | ||||
| { | ||||
|     uint32_t width_sel = 0; | ||||
|     switch (width) { | ||||
|     case 16: | ||||
|         width_sel = 0; | ||||
|         break; | ||||
|     case 8: | ||||
|         width_sel = 1; | ||||
|         break; | ||||
|     case 4: | ||||
|         width_sel = 2; | ||||
|         break; | ||||
|     case 2: | ||||
|         width_sel = 3; | ||||
|         break; | ||||
|     case 1: | ||||
|         width_sel = 4; | ||||
|         break; | ||||
|     default: | ||||
|         HAL_ASSERT(false); | ||||
|     } | ||||
|     dev->rx_cfg0.rx_bus_wid_sel = width_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset RX Async FIFO | ||||
|  * | ||||
|  * @note During the reset of the asynchronous FIFO, it takes two clock cycles to synchronize within AHB clock domain (GDMA) and Core clock domain. | ||||
|  *       The reset synchronization must be performed two clock cycles in advance. | ||||
|  * @note If the next frame transfer needs to be reset, you need to first switch to the internal free-running clock, | ||||
|  *       and then switch to the actual clock after the reset is completed. | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->rx_cfg0.rx_fifo_srst = 1; | ||||
|     dev->rx_cfg0.rx_fifo_srst = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set which data line as the enable signal | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param line_num Data line number (0-15) | ||||
|  */ | ||||
| static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32_t line_num) | ||||
| { | ||||
|     dev->rx_cfg1.rx_ext_en_sel = line_num; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Enable RX timeout feature | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_cfg1.rx_timeout_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the threshold of RX timeout | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param thres Threshold of RX timeout | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t thres) | ||||
| { | ||||
|     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_cfg1, rx_timeout_threshold, thres); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Update the RX configuration, to make the new configuration take effect | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->rx_cfg1.rx_reg_update = 1; | ||||
|     while (dev->rx_cfg1.rx_reg_update); | ||||
| } | ||||
|  | ||||
| ///////////////////////////////////TX Unit/////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock source for the TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param src Clock source | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src) | ||||
| { | ||||
|     (void)dev; | ||||
|     uint32_t clk_sel = 0; | ||||
|     switch (src) { | ||||
|     case PARLIO_LL_CLK_SRC_XTAL: | ||||
|         clk_sel = 0; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PLL_F240M: | ||||
|         clk_sel = 1; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PAD: | ||||
|         clk_sel = 3; | ||||
|         break; | ||||
|  | ||||
|     default: // unsupported clock source | ||||
|         HAL_ASSERT(false); | ||||
|         break; | ||||
|     } | ||||
|     PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock divider for the TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param div Clock divider | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_clock_div(parl_io_dev_t *dev, uint32_t div) | ||||
| { | ||||
|     (void)dev; | ||||
|     HAL_ASSERT(div > 0 && div <= PARLIO_LL_TX_MAX_CLOCK_DIV); | ||||
|     PCR.parl_clk_tx_conf.parl_clk_tx_div_num = div - 1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset the TX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_reset_clock(parl_io_dev_t *dev) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_tx_conf.parl_tx_rst_en = 1; | ||||
|     PCR.parl_clk_tx_conf.parl_tx_rst_en = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Enable the TX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_enable_clock(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_tx_conf.parl_clk_tx_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the data length to be transmitted | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param bitlen Data length in bits, must be a multiple of 8 | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t bitlen) | ||||
| { | ||||
|     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->tx_cfg0, tx_bytelen, bitlen / 8); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Wether to enable the TX clock gating | ||||
|  * | ||||
|  * @note The TXD[7] will be taken as the gating enable signal | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_tx_enable_clock_gating(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->tx_cfg0.tx_gating_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Start TX unit to transmit data | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to start, False to stop | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->tx_cfg0.tx_start = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Whether to treat the MSB of TXD as the valid signal | ||||
|  * | ||||
|  * @note If enabled, TXD[15] will work as valid signal, which stay high during data transmission. | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_tx_treat_msb_as_valid(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->tx_cfg0.tx_hw_valid_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sample clock edge | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param edge Sample clock edge | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge) | ||||
| { | ||||
|     dev->tx_cfg0.tx_smp_edge_sel = edge; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the order to unpack bits from a byte | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param order Packing order | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order) | ||||
| { | ||||
|     dev->tx_cfg0.tx_bit_unpack_order = order; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the bus width of the TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param width Bus width | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_bus_width(parl_io_dev_t *dev, uint32_t width) | ||||
| { | ||||
|     uint32_t width_sel = 0; | ||||
|     switch (width) { | ||||
|     case 16: | ||||
|         width_sel = 0; | ||||
|         break; | ||||
|     case 8: | ||||
|         width_sel = 1; | ||||
|         break; | ||||
|     case 4: | ||||
|         width_sel = 2; | ||||
|         break; | ||||
|     case 2: | ||||
|         width_sel = 3; | ||||
|         break; | ||||
|     case 1: | ||||
|         width_sel = 4; | ||||
|         break; | ||||
|     default: | ||||
|         HAL_ASSERT(false); | ||||
|     } | ||||
|     dev->tx_cfg0.tx_bus_wid_sel = width_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset TX Async FIFO | ||||
|  * | ||||
|  * @note During the reset of the asynchronous FIFO, it takes two clock cycles to synchronize within AHB clock domain (GDMA) and Core clock domain. | ||||
|  *       The reset synchronization must be performed two clock cycles in advance. | ||||
|  * @note If the next frame transfer needs to be reset, you need to first switch to the internal free-running clock, | ||||
|  *       and then switch to the actual clock after the reset is completed. | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_reset_fifo(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->tx_cfg0.tx_fifo_srst = 1; | ||||
|     dev->tx_cfg0.tx_fifo_srst = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the value to output on the TXD when the TX unit is in IDLE state | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param value Value to output | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_set_idle_data_value(parl_io_dev_t *dev, uint32_t value) | ||||
| { | ||||
|     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->tx_cfg1, tx_idle_value, value); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Check whether the TX unit is ready | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return true: ready, false: busy | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline bool parlio_ll_tx_is_ready(parl_io_dev_t *dev) | ||||
| { | ||||
|     return dev->st.tx_ready; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////Interrupt//////////////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Enable Parallel IO interrupt for specific event mask | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param mask Event mask | ||||
|  * @param enable True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_enable_interrupt(parl_io_dev_t *dev, uint32_t mask, bool enable) | ||||
| { | ||||
|     if (enable) { | ||||
|         dev->int_ena.val |= mask; | ||||
|     } else { | ||||
|         dev->int_ena.val &= ~mask; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Get interrupt status for TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return Interrupt status | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline uint32_t parlio_ll_tx_get_interrupt_status(parl_io_dev_t *dev) | ||||
| { | ||||
|     return dev->int_st.val & PARLIO_LL_EVENT_TX_MASK; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Get interrupt status for RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return Interrupt status | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline uint32_t parlio_ll_rx_get_interrupt_status(parl_io_dev_t *dev) | ||||
| { | ||||
|     return dev->int_st.val & PARLIO_LL_EVENT_RX_MASK; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Clear Parallel IO interrupt status by mask | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param mask Interrupt status mask | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_clear_interrupt_status(parl_io_dev_t *dev, uint32_t mask) | ||||
| { | ||||
|     dev->int_clr.val = mask; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Get interrupt status register address | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return Register address | ||||
|  */ | ||||
| static inline volatile void *parlio_ll_get_interrupt_status_reg(parl_io_dev_t *dev) | ||||
| { | ||||
|     return &dev->int_st; | ||||
| } | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @@ -57,6 +57,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) | ||||
|             return PCR_PWM_CLK_EN; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_CLK_EN; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_CLK_EN; | ||||
|         case PERIPH_AES_MODULE: | ||||
|             return PCR_AES_CLK_EN; | ||||
|         case PERIPH_SHA_MODULE: | ||||
| @@ -130,6 +132,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en | ||||
|             return PCR_PWM_RST_EN; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_RST_EN; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_RST_EN; | ||||
|         case PERIPH_TEMPSENSOR_MODULE: | ||||
|             return PCR_TSENS_RST_EN; | ||||
|         case PERIPH_AES_MODULE: | ||||
| @@ -221,6 +225,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) | ||||
|             return PCR_PWM_CONF_REG; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_CONF_REG; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_IO_CONF_REG; | ||||
|         case PERIPH_AES_MODULE: | ||||
|             return PCR_AES_CONF_REG; | ||||
|         case PERIPH_SHA_MODULE: | ||||
| @@ -280,6 +286,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) | ||||
|             return PCR_PWM_CONF_REG; | ||||
|         case PERIPH_ETM_MODULE: | ||||
|             return PCR_ETM_CONF_REG; | ||||
|         case PERIPH_PARLIO_MODULE: | ||||
|             return PCR_PARL_IO_CONF_REG; | ||||
|         case PERIPH_AES_MODULE: | ||||
|             return PCR_AES_CONF_REG; | ||||
|         case PERIPH_SHA_MODULE: | ||||
|   | ||||
							
								
								
									
										614
									
								
								components/hal/esp32h2/include/hal/parlio_ll.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										614
									
								
								components/hal/esp32h2/include/hal/parlio_ll.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,614 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| // Note that most of the register operations in this layer are non-atomic operations. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include "hal/assert.h" | ||||
| #include "hal/misc.h" | ||||
| #include "soc/pcr_struct.h" | ||||
| #include "soc/parl_io_struct.h" | ||||
| #include "hal/parlio_types.h" | ||||
|  | ||||
| #define PARLIO_LL_RX_MAX_BYTES_PER_FRAME 0xFFFF | ||||
| #define PARLIO_LL_RX_MAX_CLOCK_DIV       0x10000 | ||||
| #define PARLIO_LL_RX_MAX_TIMEOUT         0xFFFF | ||||
|  | ||||
| #define PARLIO_LL_TX_MAX_BITS_PER_FRAME  0x7FFFF | ||||
| #define PARLIO_LL_TX_MAX_CLOCK_DIV       0x10000 | ||||
|  | ||||
| #define PARLIO_LL_EVENT_TX_FIFO_EMPTY    (1 << 0) | ||||
| #define PARLIO_LL_EVENT_RX_FIFO_FULL     (1 << 1) | ||||
| #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_RX_MASK          (PARLIO_LL_EVENT_RX_FIFO_FULL) | ||||
|  | ||||
| #define PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG 7 // TXD[7] can be used a valid signal | ||||
| #define PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE  7 // TXD[7] can be used as clock gate signal | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| typedef enum { | ||||
|     PARLIO_LL_CLK_SRC_XTAL = PARLIO_CLK_SRC_XTAL, | ||||
|     PARLIO_LL_CLK_SRC_PLL_F96M = PARLIO_CLK_SRC_PLL_F96M, | ||||
|     PARLIO_LL_CLK_SRC_PAD, // clock source from GPIO pad | ||||
| } parlio_ll_clock_source_t; | ||||
|  | ||||
| typedef enum { | ||||
|     PARLIO_LL_RX_EOF_COND_RX_FULL,     /*!< RX unit generates EOF event when it receives enough data */ | ||||
|     PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */ | ||||
| } parlio_ll_rx_eof_cond_t; | ||||
|  | ||||
| ///////////////////////////////////////RX Unit/////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock source for the RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param src Clock source | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src) | ||||
| { | ||||
|     (void)dev; | ||||
|     uint32_t clk_sel = 0; | ||||
|     switch (src) { | ||||
|     case PARLIO_LL_CLK_SRC_XTAL: | ||||
|         clk_sel = 0; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PLL_F96M: | ||||
|         clk_sel = 1; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PAD: | ||||
|         clk_sel = 3; | ||||
|         break; | ||||
|  | ||||
|     default: // unsupported clock source | ||||
|         HAL_ASSERT(false); | ||||
|         break; | ||||
|     } | ||||
|     PCR.parl_clk_rx_conf.parl_clk_rx_sel = clk_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock divider for the RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param div Clock divider | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_clock_div(parl_io_dev_t *dev, uint32_t div) | ||||
| { | ||||
|     (void)dev; | ||||
|     HAL_ASSERT(div > 0 && div <= PARLIO_LL_RX_MAX_CLOCK_DIV); | ||||
|     PCR.parl_clk_rx_conf.parl_clk_rx_div_num = div - 1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset the RX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| static inline void parlio_ll_rx_reset_clock(parl_io_dev_t *dev) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_rx_conf.parl_rx_rst_en = 1; | ||||
|     PCR.parl_clk_rx_conf.parl_rx_rst_en = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Enable the RX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_rx_enable_clock(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_rx_conf.parl_clk_rx_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the condition to generate the RX EOF event | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param cond RX EOF condition | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_rx_eof_cond_t cond) | ||||
| { | ||||
|     dev->rx_genrl_cfg.rx_eof_gen_sel = cond; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Start RX unit to sample the input data | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to start, False to stop | ||||
|  */ | ||||
| static inline void parlio_ll_rx_start(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_start_cfg.rx_start = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the receive length | ||||
|  * | ||||
|  * @note The receive length can be used to generate DMA EOF signal, or to work as a frame end delimiter | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param bitlen Number of bits to receive in the next transaction, bitlen must be a multiple of 8 | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_recv_bit_len(parl_io_dev_t *dev, uint32_t bitlen) | ||||
| { | ||||
|     dev->rx_data_cfg.rx_bitlen = bitlen; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sub mode of the level controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param active_level Level of the external enable signal, true for active high, false for active low | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_level_recv_mode(parl_io_dev_t *dev, bool active_level) | ||||
| { | ||||
|     dev->rx_mode_cfg.rx_smp_mode_sel = 0; | ||||
|     dev->rx_mode_cfg.rx_ext_en_inv = !active_level; // 0: active low, 1: active high | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sub mode of the pulse controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param start_inc Whether the start pulse is counted | ||||
|  * @param end_inc Whether the end pulse is counted | ||||
|  * @param end_by_len Whether to use the frame length to determine the end of the frame | ||||
|  * @param pulse_inv Whether the pulse is inverted | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_pulse_recv_mode(parl_io_dev_t *dev, bool start_inc, bool end_inc, bool end_by_len, bool pulse_inv) | ||||
| { | ||||
|     uint32_t submode = 0; | ||||
|     uint32_t step = 1; | ||||
|     if (end_by_len) { | ||||
|         submode += 4; | ||||
|     } else { // end by pulse | ||||
|         step = 2; | ||||
|         if (!end_inc) { | ||||
|             submode += 1; | ||||
|         } | ||||
|     } | ||||
|     if (!start_inc) { | ||||
|         submode += step; | ||||
|     } | ||||
|     dev->rx_mode_cfg.rx_smp_mode_sel = 1; | ||||
|     dev->rx_mode_cfg.rx_pulse_submode_sel = submode; | ||||
|     dev->rx_mode_cfg.rx_ext_en_inv = pulse_inv; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the receive mode to software controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_soft_recv_mode(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->rx_mode_cfg.rx_smp_mode_sel = 2; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Whether to start the software controlled receive mode | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_rx_start_soft_recv(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_mode_cfg.rx_sw_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sample clock edge | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param edge Sample clock edge | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge) | ||||
| { | ||||
|     dev->rx_clk_cfg.rx_clk_i_inv = edge; | ||||
|     dev->rx_clk_cfg.rx_clk_o_inv = edge; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the order to pack bits into one byte | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param order Packing order | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order) | ||||
| { | ||||
|     dev->rx_data_cfg.rx_data_order_inv = order; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the bus width of the RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param width Bus width | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_bus_width(parl_io_dev_t *dev, uint32_t width) | ||||
| { | ||||
|     uint32_t width_sel = 0; | ||||
|     switch (width) { | ||||
|     case 8: | ||||
|         width_sel = 3; | ||||
|         break; | ||||
|     case 4: | ||||
|         width_sel = 2; | ||||
|         break; | ||||
|     case 2: | ||||
|         width_sel = 1; | ||||
|         break; | ||||
|     case 1: | ||||
|         width_sel = 0; | ||||
|         break; | ||||
|     default: | ||||
|         HAL_ASSERT(false); | ||||
|     } | ||||
|     dev->rx_data_cfg.rx_bus_wid_sel = width_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset RX Async FIFO | ||||
|  * | ||||
|  * @note During the reset of the asynchronous FIFO, it takes two clock cycles to synchronize within AHB clock domain (GDMA) and Core clock domain. | ||||
|  *       The reset synchronization must be performed two clock cycles in advance. | ||||
|  * @note If the next frame transfer needs to be reset, you need to first switch to the internal free-running clock, | ||||
|  *       and then switch to the actual clock after the reset is completed. | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| static inline void parlio_ll_rx_reset_fifo(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->fifo_cfg.rx_fifo_srst = 1; | ||||
|     dev->fifo_cfg.rx_fifo_srst = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set which data line as the enable signal | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param line_num Data line number (0-15) | ||||
|  */ | ||||
| static inline void parlio_ll_rx_treat_data_line_as_en(parl_io_dev_t *dev, uint32_t line_num) | ||||
| { | ||||
|     dev->rx_mode_cfg.rx_ext_en_sel = line_num; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Wether to enable the RX clock gating | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_rx_enable_clock_gating(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_genrl_cfg.rx_gating_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Enable RX timeout feature | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_rx_enable_timeout(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->rx_genrl_cfg.rx_timeout_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the threshold of RX timeout | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param thres Threshold of RX timeout | ||||
|  */ | ||||
| static inline void parlio_ll_rx_set_timeout_thres(parl_io_dev_t *dev, uint32_t thres) | ||||
| { | ||||
|     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rx_genrl_cfg, rx_timeout_thres, thres); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Update the RX configuration, to make the new configuration take effect | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| static inline void parlio_ll_rx_update_config(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->reg_update.rx_reg_update = 1; | ||||
|     while (dev->reg_update.rx_reg_update); | ||||
| } | ||||
|  | ||||
| ///////////////////////////////////TX Unit/////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock source for the TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param src Clock source | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_clock_source(parl_io_dev_t *dev, parlio_ll_clock_source_t src) | ||||
| { | ||||
|     (void)dev; | ||||
|     uint32_t clk_sel = 0; | ||||
|     switch (src) { | ||||
|     case PARLIO_LL_CLK_SRC_XTAL: | ||||
|         clk_sel = 0; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PLL_F96M: | ||||
|         clk_sel = 1; | ||||
|         break; | ||||
|     case PARLIO_LL_CLK_SRC_PAD: | ||||
|         clk_sel = 3; | ||||
|         break; | ||||
|  | ||||
|     default: // unsupported clock source | ||||
|         HAL_ASSERT(false); | ||||
|         break; | ||||
|     } | ||||
|     PCR.parl_clk_tx_conf.parl_clk_tx_sel = clk_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the clock divider for the TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param div Clock divider | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_clock_div(parl_io_dev_t *dev, uint32_t div) | ||||
| { | ||||
|     (void)dev; | ||||
|     HAL_ASSERT(div > 0 && div <= PARLIO_LL_TX_MAX_CLOCK_DIV); | ||||
|     PCR.parl_clk_tx_conf.parl_clk_tx_div_num = div - 1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset the TX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_reset_clock(parl_io_dev_t *dev) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_tx_conf.parl_tx_rst_en = 1; | ||||
|     PCR.parl_clk_tx_conf.parl_tx_rst_en = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Enable the TX unit Core clock domain | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_enable_clock(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     (void)dev; | ||||
|     PCR.parl_clk_tx_conf.parl_clk_tx_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the data length to be transmitted | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param bitlen Data length in bits, must be a multiple of 8 | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t bitlen) | ||||
| { | ||||
|     dev->tx_data_cfg.tx_bitlen = bitlen; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Wether to enable the TX clock gating | ||||
|  * | ||||
|  * @note The MSB of TXD will be taken as the gating enable signal | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_tx_enable_clock_gating(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->tx_genrl_cfg.tx_gating_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Start TX unit to transmit data | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to start, False to stop | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_start(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->tx_start_cfg.tx_start = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Whether to treat the MSB of TXD as the valid signal | ||||
|  * | ||||
|  * @note If enabled, TXD[15] will work as valid signal, which stay high during data transmission. | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param en True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_tx_treat_msb_as_valid(parl_io_dev_t *dev, bool en) | ||||
| { | ||||
|     dev->tx_genrl_cfg.tx_valid_output_en = en; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the sample clock edge | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param edge Sample clock edge | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_sample_clock_edge(parl_io_dev_t *dev, parlio_sample_edge_t edge) | ||||
| { | ||||
|     dev->tx_clk_cfg.tx_clk_i_inv = edge; | ||||
|     dev->tx_clk_cfg.tx_clk_o_inv = edge; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the order to unpack bits from a byte | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param order Packing order | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_bit_pack_order(parl_io_dev_t *dev, parlio_bit_pack_order_t order) | ||||
| { | ||||
|     dev->tx_data_cfg.tx_data_order_inv = order; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the bus width of the TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param width Bus width | ||||
|  */ | ||||
| static inline void parlio_ll_tx_set_bus_width(parl_io_dev_t *dev, uint32_t width) | ||||
| { | ||||
|     uint32_t width_sel = 0; | ||||
|     switch (width) { | ||||
|     case 8: | ||||
|         width_sel = 3; | ||||
|         break; | ||||
|     case 4: | ||||
|         width_sel = 2; | ||||
|         break; | ||||
|     case 2: | ||||
|         width_sel = 1; | ||||
|         break; | ||||
|     case 1: | ||||
|         width_sel = 0; | ||||
|         break; | ||||
|     default: | ||||
|         HAL_ASSERT(false); | ||||
|     } | ||||
|     dev->tx_data_cfg.tx_bus_wid_sel = width_sel; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Reset TX Async FIFO | ||||
|  * | ||||
|  * @note During the reset of the asynchronous FIFO, it takes two clock cycles to synchronize within AHB clock domain (GDMA) and Core clock domain. | ||||
|  *       The reset synchronization must be performed two clock cycles in advance. | ||||
|  * @note If the next frame transfer needs to be reset, you need to first switch to the internal free-running clock, | ||||
|  *       and then switch to the actual clock after the reset is completed. | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_reset_fifo(parl_io_dev_t *dev) | ||||
| { | ||||
|     dev->fifo_cfg.tx_fifo_srst = 1; | ||||
|     dev->fifo_cfg.tx_fifo_srst = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Set the value to output on the TXD when the TX unit is in IDLE state | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param value Value to output | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_tx_set_idle_data_value(parl_io_dev_t *dev, uint32_t value) | ||||
| { | ||||
|     dev->tx_genrl_cfg.tx_idle_value = value; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Check whether the TX unit is ready | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return true: ready, false: busy | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline bool parlio_ll_tx_is_ready(parl_io_dev_t *dev) | ||||
| { | ||||
|     return dev->st.tx_ready; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////Interrupt//////////////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Enable Parallel IO interrupt for specific event mask | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param mask Event mask | ||||
|  * @param enable True to enable, False to disable | ||||
|  */ | ||||
| static inline void parlio_ll_enable_interrupt(parl_io_dev_t *dev, uint32_t mask, bool enable) | ||||
| { | ||||
|     if (enable) { | ||||
|         dev->int_ena.val |= mask; | ||||
|     } else { | ||||
|         dev->int_ena.val &= ~mask; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Get interrupt status for TX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return Interrupt status | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline uint32_t parlio_ll_tx_get_interrupt_status(parl_io_dev_t *dev) | ||||
| { | ||||
|     return dev->int_st.val & PARLIO_LL_EVENT_TX_MASK; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Get interrupt status for RX unit | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return Interrupt status | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline uint32_t parlio_ll_rx_get_interrupt_status(parl_io_dev_t *dev) | ||||
| { | ||||
|     return dev->int_st.val & PARLIO_LL_EVENT_RX_MASK; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Clear Parallel IO interrupt status by mask | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @param mask Interrupt status mask | ||||
|  */ | ||||
| __attribute__((always_inline)) | ||||
| static inline void parlio_ll_clear_interrupt_status(parl_io_dev_t *dev, uint32_t mask) | ||||
| { | ||||
|     dev->int_clr.val = mask; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Get interrupt status register address | ||||
|  * | ||||
|  * @param dev Parallel IO register base address | ||||
|  * @return Register address | ||||
|  */ | ||||
| static inline volatile void *parlio_ll_get_interrupt_status_reg(parl_io_dev_t *dev) | ||||
| { | ||||
|     return &dev->int_st; | ||||
| } | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @@ -27,6 +27,7 @@ typedef enum { | ||||
|     GDMA_TRIG_PERIPH_LCD,  /*!< GDMA trigger peripheral: LCD */ | ||||
|     GDMA_TRIG_PERIPH_CAM,  /*!< GDMA trigger peripheral: CAM */ | ||||
|     GDMA_TRIG_PERIPH_RMT,  /*!< GDMA trigger peripheral: RMT */ | ||||
|     GDMA_TRIG_PERIPH_PARLIO, /*!< GDMA trigger peripheral: PARLIO */ | ||||
| } gdma_trigger_peripheral_t; | ||||
|  | ||||
| /** | ||||
|   | ||||
							
								
								
									
										46
									
								
								components/hal/include/hal/parlio_hal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								components/hal/include/hal/parlio_hal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| /******************************************************************************* | ||||
|  * NOTICE | ||||
|  * The hal is not public api, don't use in application code. | ||||
|  * See readme.md in hal/include/hal/readme.md | ||||
|  ******************************************************************************/ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| typedef struct parl_io_dev_t *parlio_soc_handle_t; // Parallel IO SOC layer handle | ||||
|  | ||||
| /** | ||||
|  * @brief HAL context type of Parallel IO driver | ||||
|  */ | ||||
| typedef struct { | ||||
|     parlio_soc_handle_t regs; /*!< Parallel IO Register base address */ | ||||
| } parlio_hal_context_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Initialize the Parallel IO HAL driver | ||||
|  * | ||||
|  * @param hal: Parallel IO HAL context | ||||
|  */ | ||||
| void parlio_hal_init(parlio_hal_context_t *hal); | ||||
|  | ||||
| /** | ||||
|  * @brief Deinitialize the Parallel IO HAL driver | ||||
|  * | ||||
|  * @param hal: Parallel IO HAL context | ||||
|  */ | ||||
| void parlio_hal_deinit(parlio_hal_context_t *hal); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										68
									
								
								components/hal/include/hal/parlio_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								components/hal/include/hal/parlio_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include "soc/soc_caps.h" | ||||
| #include "soc/clk_tree_defs.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief Parallel IO sample edge | ||||
|  */ | ||||
| typedef enum { | ||||
|     PARLIO_SAMPLE_EDGE_NEG, /*!< Sample data on falling edge of clock */ | ||||
|     PARLIO_SAMPLE_EDGE_POS, /*!< Sample data on rising edge of clock */ | ||||
| } parlio_sample_edge_t; | ||||
|  | ||||
| /** | ||||
|  * @brief Parallel IO bit packing order | ||||
|  * | ||||
|  * Data in memory: | ||||
|  * Byte 0: MSB < B0.7 B0.6 B0.5 B0.4 B0.3 B0.2 B0.1 B0.0 > LSB | ||||
|  * Byte 1: MSB < B1.7 B1.6 B1.5 B1.4 B1.3 B1.2 B1.1 B1.0 > LSB | ||||
|  * | ||||
|  * Output on line (PARLIO_BIT_PACK_ORDER_LSB): | ||||
|  *          Cycle 0   Cycle 1   Cycle 2  ---> time | ||||
|  * GPIO 0:   B0.0      B0.4      B1.0 | ||||
|  * GPIO 1:   B0.1      B0.5      B1.1 | ||||
|  * GPIO 2:   B0.2      B0.6      B1.2 | ||||
|  * GPIO 3:   B0.3      B0.7      B1.3 | ||||
|  * | ||||
|  * Output on line (PARLIO_BIT_PACK_ORDER_MSB): | ||||
|  *          Cycle 0   Cycle 1   Cycle 2  ---> time | ||||
|  * GPIO 0:   B0.4      B0.0      B1.4 | ||||
|  * GPIO 1:   B0.5      B0.1      B1.5 | ||||
|  * GPIO 2:   B0.6      B0.2      B1.6 | ||||
|  * GPIO 3:   B0.7      B0.3      B1.7 | ||||
|  */ | ||||
| typedef enum { | ||||
|     PARLIO_BIT_PACK_ORDER_LSB, /*!< Bit pack order: LSB */ | ||||
|     PARLIO_BIT_PACK_ORDER_MSB, /*!< Bit pack order: MSB */ | ||||
| } parlio_bit_pack_order_t; | ||||
|  | ||||
| #if SOC_PARLIO_SUPPORTED | ||||
| /** | ||||
|  * @brief Parallel IO clock source | ||||
|  * @note User should select the clock source based on the power and resolution requirement | ||||
|  */ | ||||
| typedef soc_periph_parlio_clk_src_t parlio_clock_source_t; | ||||
|  | ||||
| /// Maximum data width of TX unit | ||||
| #define PARLIO_TX_UNIT_MAX_DATA_WIDTH SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH | ||||
| #else | ||||
| typedef int parlio_clock_source_t; | ||||
| #define PARLIO_TX_UNIT_MAX_DATA_WIDTH 0 | ||||
| #endif // SOC_PARLIO_SUPPORTED | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										19
									
								
								components/hal/parlio_hal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								components/hal/parlio_hal.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include "hal/parlio_hal.h" | ||||
| #include "hal/parlio_ll.h" | ||||
|  | ||||
| void parlio_hal_init(parlio_hal_context_t *hal) | ||||
| { | ||||
|     hal->regs = &PARL_IO; | ||||
| } | ||||
|  | ||||
| void parlio_hal_deinit(parlio_hal_context_t *hal) | ||||
| { | ||||
|     hal->regs = NULL; | ||||
| } | ||||
| @@ -78,6 +78,10 @@ if(CONFIG_SOC_LCDCAM_SUPPORTED OR CONFIG_SOC_LCD_I80_SUPPORTED) | ||||
|     list(APPEND srcs "${target}/lcd_periph.c") | ||||
| endif() | ||||
|  | ||||
| if(CONFIG_SOC_PARLIO_SUPPORTED) | ||||
|     list(APPEND srcs "${target}/parlio_periph.c") | ||||
| endif() | ||||
|  | ||||
| if(CONFIG_SOC_MCPWM_SUPPORTED) | ||||
|     list(APPEND srcs "${target}/mcpwm_periph.c") | ||||
| endif() | ||||
|   | ||||
| @@ -39,6 +39,10 @@ config SOC_ETM_SUPPORTED | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_PARLIO_SUPPORTED | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_BT_SUPPORTED | ||||
|     bool | ||||
|     default y | ||||
| @@ -667,6 +671,30 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_PARLIO_GROUPS | ||||
|     int | ||||
|     default 1 | ||||
|  | ||||
| config SOC_PARLIO_TX_UNITS_PER_GROUP | ||||
|     int | ||||
|     default 1 | ||||
|  | ||||
| config SOC_PARLIO_RX_UNITS_PER_GROUP | ||||
|     int | ||||
|     default 1 | ||||
|  | ||||
| config SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH | ||||
|     int | ||||
|     default 16 | ||||
|  | ||||
| config SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH | ||||
|     int | ||||
|     default 16 | ||||
|  | ||||
| config SOC_PARLIO_TX_RX_SHARE_INTERRUPT | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_RSA_MAX_BIT_LEN | ||||
|     int | ||||
|     default 3072 | ||||
|   | ||||
| @@ -405,6 +405,22 @@ typedef enum { | ||||
|     LEDC_USE_RTC8M_CLK __attribute__((deprecated("please use 'LEDC_USE_RC_FAST_CLK' instead"))) = LEDC_USE_RC_FAST_CLK,   /*!< Alias of 'LEDC_USE_RC_FAST_CLK' */ | ||||
| } soc_periph_ledc_clk_src_legacy_t; | ||||
|  | ||||
| //////////////////////////////////////////////////PARLIO//////////////////////////////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Array initializer for all supported clock sources of PARLIO | ||||
|  */ | ||||
| #define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F240M} | ||||
|  | ||||
| /** | ||||
|  * @brief PARLIO clock source | ||||
|  */ | ||||
| typedef enum { | ||||
|     PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL,           /*!< Select XTAL as the source clock */ | ||||
|     PARLIO_CLK_SRC_PLL_F240M = SOC_MOD_CLK_PLL_F240M, /*!< Select PLL_F240M as the source clock */ | ||||
|     PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F240M,   /*!< Select PLL_F240M as the default clock choice */ | ||||
| } soc_periph_parlio_clk_src_t; | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -37,6 +37,7 @@ typedef enum { | ||||
|     PERIPH_GDMA_MODULE, | ||||
|     PERIPH_MCPWM0_MODULE, | ||||
|     PERIPH_ETM_MODULE, | ||||
|     PERIPH_PARLIO_MODULE, | ||||
|     PERIPH_SYSTIMER_MODULE, | ||||
|     PERIPH_SARADC_MODULE, | ||||
|     PERIPH_TEMPSENSOR_MODULE, | ||||
|   | ||||
| @@ -34,6 +34,7 @@ | ||||
| #define SOC_MCPWM_SUPPORTED             1 | ||||
| #define SOC_TWAI_SUPPORTED              1 | ||||
| #define SOC_ETM_SUPPORTED               1 | ||||
| #define SOC_PARLIO_SUPPORTED            1 | ||||
| #define SOC_BT_SUPPORTED                1 | ||||
| #define SOC_IEEE802154_SUPPORTED        1 | ||||
| #define SOC_ASYNC_MEMCPY_SUPPORTED      1 | ||||
| @@ -279,6 +280,14 @@ | ||||
| /*------------------------ USB SERIAL JTAG CAPS ------------------------------*/ | ||||
| // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP     (1)     /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 | ||||
|  | ||||
| /*-------------------------- PARLIO CAPS --------------------------------------*/ | ||||
| #define SOC_PARLIO_GROUPS                    1U  /*!< Number of parallel IO peripherals */ | ||||
| #define SOC_PARLIO_TX_UNITS_PER_GROUP        1U  /*!< number of TX units in each group */ | ||||
| #define SOC_PARLIO_RX_UNITS_PER_GROUP        1U  /*!< number of RX units in each group */ | ||||
| #define SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH    16  /*!< Number of data lines of the TX unit */ | ||||
| #define SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH    16  /*!< Number of data lines of the RX unit */ | ||||
| #define SOC_PARLIO_TX_RX_SHARE_INTERRUPT     1   /*!< TX and RX unit share the same interrupt source number */ | ||||
|  | ||||
| /*--------------------------- RSA CAPS ---------------------------------------*/ | ||||
| #define SOC_RSA_MAX_BIT_LEN    (3072) | ||||
|  | ||||
|   | ||||
							
								
								
									
										66
									
								
								components/soc/esp32c6/parlio_periph.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								components/soc/esp32c6/parlio_periph.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include "soc/parlio_periph.h" | ||||
| #include "soc/gpio_sig_map.h" | ||||
|  | ||||
| const parlio_signal_conn_t parlio_periph_signals = { | ||||
|     .groups = { | ||||
|         [0] = { | ||||
|             .module = PERIPH_PARLIO_MODULE, | ||||
|             .tx_irq_id = ETS_PARL_IO_INTR_SOURCE, | ||||
|             .rx_irq_id = ETS_PARL_IO_INTR_SOURCE, | ||||
|             .tx_units = { | ||||
|                 [0] = { | ||||
|                     .data_sigs = { | ||||
|                         PARL_TX_DATA0_IDX, | ||||
|                         PARL_TX_DATA1_IDX, | ||||
|                         PARL_TX_DATA2_IDX, | ||||
|                         PARL_TX_DATA3_IDX, | ||||
|                         PARL_TX_DATA4_IDX, | ||||
|                         PARL_TX_DATA5_IDX, | ||||
|                         PARL_TX_DATA6_IDX, | ||||
|                         PARL_TX_DATA7_IDX, | ||||
|                         PARL_TX_DATA8_IDX, | ||||
|                         PARL_TX_DATA9_IDX, | ||||
|                         PARL_TX_DATA10_IDX, | ||||
|                         PARL_TX_DATA11_IDX, | ||||
|                         PARL_TX_DATA12_IDX, | ||||
|                         PARL_TX_DATA13_IDX, | ||||
|                         PARL_TX_DATA14_IDX, | ||||
|                         PARL_TX_DATA15_IDX, | ||||
|                     }, | ||||
|                     .clk_out_sig = PARL_TX_CLK_OUT_IDX, | ||||
|                     .clk_in_sig = PARL_TX_CLK_IN_IDX, | ||||
|                 } | ||||
|             }, | ||||
|             .rx_units = { | ||||
|                 [0] = { | ||||
|                     .data_sigs = { | ||||
|                         PARL_RX_DATA0_IDX, | ||||
|                         PARL_RX_DATA1_IDX, | ||||
|                         PARL_RX_DATA2_IDX, | ||||
|                         PARL_RX_DATA3_IDX, | ||||
|                         PARL_RX_DATA4_IDX, | ||||
|                         PARL_RX_DATA5_IDX, | ||||
|                         PARL_RX_DATA6_IDX, | ||||
|                         PARL_RX_DATA7_IDX, | ||||
|                         PARL_RX_DATA8_IDX, | ||||
|                         PARL_RX_DATA9_IDX, | ||||
|                         PARL_RX_DATA10_IDX, | ||||
|                         PARL_RX_DATA11_IDX, | ||||
|                         PARL_RX_DATA12_IDX, | ||||
|                         PARL_RX_DATA13_IDX, | ||||
|                         PARL_RX_DATA14_IDX, | ||||
|                         PARL_RX_DATA15_IDX, | ||||
|                     }, | ||||
|                     .clk_out_sig = -1, | ||||
|                     .clk_in_sig = PARL_RX_CLK_IN_IDX, | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| @@ -79,6 +79,10 @@ config SOC_RMT_SUPPORTED | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_PARLIO_SUPPORTED | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_GPSPI_SUPPORTED | ||||
|     bool | ||||
|     default y | ||||
| @@ -615,6 +619,34 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_PARLIO_GROUPS | ||||
|     int | ||||
|     default 1 | ||||
|  | ||||
| config SOC_PARLIO_TX_UNITS_PER_GROUP | ||||
|     int | ||||
|     default 1 | ||||
|  | ||||
| config SOC_PARLIO_RX_UNITS_PER_GROUP | ||||
|     int | ||||
|     default 1 | ||||
|  | ||||
| config SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH | ||||
|     int | ||||
|     default 8 | ||||
|  | ||||
| config SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH | ||||
|     int | ||||
|     default 8 | ||||
|  | ||||
| config SOC_PARLIO_TX_CLK_SUPPORT_GATING | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_PARLIO_TRANS_BIT_ALIGN | ||||
|     bool | ||||
|     default y | ||||
|  | ||||
| config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH | ||||
|     int | ||||
|     default 128 | ||||
|   | ||||
| @@ -415,6 +415,22 @@ typedef enum { | ||||
|     LEDC_USE_RTC8M_CLK __attribute__((deprecated("please use 'LEDC_USE_RC_FAST_CLK' instead"))) = LEDC_USE_RC_FAST_CLK,   /*!< Alias of 'LEDC_USE_RC_FAST_CLK' */ | ||||
| } soc_periph_ledc_clk_src_legacy_t; | ||||
|  | ||||
| //////////////////////////////////////////////////PARLIO//////////////////////////////////////////////////////////////// | ||||
|  | ||||
| /** | ||||
|  * @brief Array initializer for all supported clock sources of PARLIO | ||||
|  */ | ||||
| #define SOC_PARLIO_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_PLL_F96M} | ||||
|  | ||||
| /** | ||||
|  * @brief PARLIO clock source | ||||
|  */ | ||||
| typedef enum { | ||||
|     PARLIO_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL,          /*!< Select XTAL as the source clock */ | ||||
|     PARLIO_CLK_SRC_PLL_F96M = SOC_MOD_CLK_PLL_F96M,  /*!< Select PLL_F96M as the source clock */ | ||||
|     PARLIO_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F96M,   /*!< Select PLL_F96M as the default clock choice */ | ||||
| } soc_periph_parlio_clk_src_t; | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  *  SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
| @@ -467,7 +467,7 @@ typedef union { | ||||
| } parl_io_version_reg_t; | ||||
|  | ||||
|  | ||||
| typedef struct { | ||||
| typedef struct parl_io_dev_t { | ||||
|     volatile parl_io_rx_mode_cfg_reg_t rx_mode_cfg; | ||||
|     volatile parl_io_rx_data_cfg_reg_t rx_data_cfg; | ||||
|     volatile parl_io_rx_genrl_cfg_reg_t rx_genrl_cfg; | ||||
|   | ||||
| @@ -40,6 +40,7 @@ typedef enum { | ||||
|     PERIPH_GDMA_MODULE, | ||||
|     PERIPH_MCPWM0_MODULE, | ||||
|     PERIPH_ETM_MODULE, | ||||
|     PERIPH_PARLIO_MODULE, | ||||
|     PERIPH_SYSTIMER_MODULE, | ||||
|     PERIPH_SARADC_MODULE, | ||||
|     PERIPH_TEMPSENSOR_MODULE, | ||||
|   | ||||
| @@ -48,6 +48,7 @@ | ||||
| #define SOC_SDM_SUPPORTED               1 | ||||
| #define SOC_ETM_SUPPORTED               1 | ||||
| #define SOC_RMT_SUPPORTED               1 | ||||
| #define SOC_PARLIO_SUPPORTED            1 | ||||
| #define SOC_GPSPI_SUPPORTED             1 | ||||
| #define SOC_LEDC_SUPPORTED              1 | ||||
| #define SOC_I2C_SUPPORTED               1 | ||||
| @@ -268,6 +269,15 @@ | ||||
| /*------------------------ USB SERIAL JTAG CAPS ------------------------------*/ | ||||
| // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP     (1)     /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 | ||||
|  | ||||
| /*-------------------------- PARLIO CAPS --------------------------------------*/ | ||||
| #define SOC_PARLIO_GROUPS                    1U /*!< Number of parallel IO peripherals */ | ||||
| #define SOC_PARLIO_TX_UNITS_PER_GROUP        1U /*!< number of TX units in each group */ | ||||
| #define SOC_PARLIO_RX_UNITS_PER_GROUP        1U /*!< number of RX units in each group */ | ||||
| #define SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH    8  /*!< Number of data lines of the TX unit */ | ||||
| #define SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH    8  /*!< Number of data lines of the RX unit */ | ||||
| #define SOC_PARLIO_TX_CLK_SUPPORT_GATING     1  /*!< Support gating TX clock */ | ||||
| #define SOC_PARLIO_TRANS_BIT_ALIGN           1  /*!< Support bit alignment in transaction */ | ||||
|  | ||||
| // TODO: IDF-6267 (Copy from esp32c6, need check) | ||||
| /*-------------------------- RTC CAPS --------------------------------------*/ | ||||
| #define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH       (128) | ||||
|   | ||||
							
								
								
									
										50
									
								
								components/soc/esp32h2/parlio_periph.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								components/soc/esp32h2/parlio_periph.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #include "soc/parlio_periph.h" | ||||
| #include "soc/gpio_sig_map.h" | ||||
|  | ||||
| const parlio_signal_conn_t parlio_periph_signals = { | ||||
|     .groups = { | ||||
|         [0] = { | ||||
|             .module = PERIPH_PARLIO_MODULE, | ||||
|             .tx_irq_id = ETS_PARL_IO_TX_INTR_SOURCE, | ||||
|             .rx_irq_id = ETS_PARL_IO_RX_INTR_SOURCE, | ||||
|             .tx_units = { | ||||
|                 [0] = { | ||||
|                     .data_sigs = { | ||||
|                         PARL_TX_DATA0_IDX, | ||||
|                         PARL_TX_DATA1_IDX, | ||||
|                         PARL_TX_DATA2_IDX, | ||||
|                         PARL_TX_DATA3_IDX, | ||||
|                         PARL_TX_DATA4_IDX, | ||||
|                         PARL_TX_DATA5_IDX, | ||||
|                         PARL_TX_DATA6_IDX, | ||||
|                         PARL_TX_DATA7_IDX, | ||||
|                     }, | ||||
|                     .clk_out_sig = PARL_TX_CLK_OUT_IDX, | ||||
|                     .clk_in_sig = PARL_TX_CLK_IN_IDX, | ||||
|                 } | ||||
|             }, | ||||
|             .rx_units = { | ||||
|                 [0] = { | ||||
|                     .data_sigs = { | ||||
|                         PARL_RX_DATA0_IDX, | ||||
|                         PARL_RX_DATA1_IDX, | ||||
|                         PARL_RX_DATA2_IDX, | ||||
|                         PARL_RX_DATA3_IDX, | ||||
|                         PARL_RX_DATA4_IDX, | ||||
|                         PARL_RX_DATA5_IDX, | ||||
|                         PARL_RX_DATA6_IDX, | ||||
|                         PARL_RX_DATA7_IDX, | ||||
|                     }, | ||||
|                     .clk_out_sig = PARL_RX_CLK_OUT_IDX, | ||||
|                     .clk_in_sig = PARL_RX_CLK_IN_IDX, | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
							
								
								
									
										41
									
								
								components/soc/include/soc/parlio_periph.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								components/soc/include/soc/parlio_periph.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD | ||||
|  * | ||||
|  * SPDX-License-Identifier: Apache-2.0 | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include "soc/soc_caps.h" | ||||
| #include "soc/periph_defs.h" | ||||
| #include "soc/parl_io_reg.h" | ||||
| #include "soc/parl_io_struct.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| typedef struct { | ||||
|     struct { | ||||
|         struct { | ||||
|             const int data_sigs[SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH]; | ||||
|             const int clk_out_sig; | ||||
|             const int clk_in_sig; | ||||
|         } tx_units[SOC_PARLIO_TX_UNITS_PER_GROUP]; | ||||
|         struct { | ||||
|             const int data_sigs[SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH]; | ||||
|             const int clk_out_sig; | ||||
|             const int clk_in_sig; | ||||
|         } rx_units[SOC_PARLIO_RX_UNITS_PER_GROUP]; | ||||
|         const int tx_irq_id; | ||||
|         const int rx_irq_id; | ||||
|         const periph_module_t module; | ||||
|     } groups[SOC_PARLIO_GROUPS]; | ||||
| } parlio_signal_conn_t; | ||||
|  | ||||
| extern const parlio_signal_conn_t parlio_periph_signals; | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user
	 morris
					morris