diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 28b1efee72..093e321823 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -67,6 +67,7 @@ if(IDF_TARGET STREQUAL "esp32c3") list(APPEND srcs "gdma.c" "spi_slave_hd.c" "adc_common.c" + "dedic_gpio.c" "usb_serial_jtag.c" "esp32c3/adc.c" "esp32c3/adc2_init_cal.c" @@ -77,6 +78,7 @@ if(IDF_TARGET STREQUAL "esp32h2") list(APPEND srcs "gdma.c" "spi_slave_hd.c" "adc_common.c" + "dedic_gpio.c" "esp32h2/adc.c" "esp32h2/adc2_init_cal.c" "esp32h2/rtc_tempsensor.c") diff --git a/components/driver/dedic_gpio.c b/components/driver/dedic_gpio.c index 98109f51bd..46daf61ae6 100644 --- a/components/driver/dedic_gpio.c +++ b/components/driver/dedic_gpio.c @@ -63,8 +63,8 @@ struct dedic_gpio_bundle_t { uint32_t in_mask; // mask of input channels in the bank uint32_t out_offset; // offset in the bank (seen from output channel) uint32_t in_offset; // offset in the bank (seen from input channel) - size_t nr_gpio; // number of GPIOs in the gpio_array - int gpio_array[]; // array of GPIO numbers (configured by user) + size_t nr_gpio; // number of GPIOs in the gpio_array + int gpio_array[]; // array of GPIO numbers (configured by user) }; static esp_err_t dedic_gpio_build_platform(uint32_t core_id) @@ -80,8 +80,10 @@ static esp_err_t dedic_gpio_build_platform(uint32_t core_id) s_platform[core_id]->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; #if SOC_DEDIC_GPIO_ALLOW_REG_ACCESS s_platform[core_id]->dev = &DEDIC_GPIO; -#endif +#endif // SOC_DEDIC_GPIO_ALLOW_REG_ACCESS +#if !SOC_DEDIC_PERIPH_AUTO_ENABLE periph_module_enable(dedic_gpio_periph_signals.module); // enable APB clock to peripheral +#endif // !SOC_DEDIC_PERIPH_AUTO_ENABLE } } _lock_release(&s_platform_mutexlock[core_id]); @@ -102,7 +104,9 @@ static void dedic_gpio_break_platform(uint32_t core_id) if (s_platform[core_id]) { free(s_platform[core_id]); s_platform[core_id] = NULL; +#if !SOC_DEDIC_PERIPH_AUTO_ENABLE periph_module_disable(dedic_gpio_periph_signals.module); // disable module if no GPIO channel is being used +#endif // !SOC_DEDIC_PERIPH_AUTO_ENABLE } _lock_release(&s_platform_mutexlock[core_id]); } @@ -263,6 +267,9 @@ esp_err_t dedic_gpio_new_bundle(const dedic_gpio_bundle_config_t *config, dedic_ gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[config->gpio_array[i]], PIN_FUNC_GPIO); esp_rom_gpio_connect_out_signal(config->gpio_array[i], dedic_gpio_periph_signals.cores[core_id].out_sig_per_channel[out_offset + i], config->flags.out_invert, false); } +#if !SOC_DEDIC_GPIO_OUT_AUTO_ENABLE + cpu_ll_enable_dedic_gpio_output(s_platform[core_id]->out_occupied_mask); +#endif // !SOC_DEDIC_GPIO_OUT_AUTO_ENABLE } // it's safe to initialize bundle members without locks here @@ -340,14 +347,14 @@ err: void dedic_gpio_bundle_write(dedic_gpio_bundle_handle_t bundle, uint32_t mask, uint32_t value) { - // For performace reasons, we don't want to check the validation of parameters here + // For performance reasons, we don't want to check the validation of parameters here // Even didn't check if we're working on the correct CPU core (i.e. bundle->core_id == current core_id) cpu_ll_write_dedic_gpio_mask(bundle->out_mask & (mask << bundle->out_offset), value << bundle->out_offset); } uint32_t dedic_gpio_bundle_read_out(dedic_gpio_bundle_handle_t bundle) { - // For performace reasons, we don't want to check the validation of parameters here + // For performance reasons, we don't want to check the validation of parameters here // Even didn't check if we're working on the correct CPU core (i.e. bundle->core_id == current core_id) uint32_t value = cpu_ll_read_dedic_gpio_out(); return (value & bundle->out_mask) >> (bundle->out_offset); @@ -355,7 +362,7 @@ uint32_t dedic_gpio_bundle_read_out(dedic_gpio_bundle_handle_t bundle) uint32_t dedic_gpio_bundle_read_in(dedic_gpio_bundle_handle_t bundle) { - // For performace reasons, we don't want to check the validation of parameters here + // For performance reasons, we don't want to check the validation of parameters here // Even didn't check if we're working on the correct CPU core (i.e. bundle->core_id == current core_id) uint32_t value = cpu_ll_read_dedic_gpio_in(); return (value & bundle->in_mask) >> (bundle->in_offset); diff --git a/components/hal/esp32c3/include/hal/cpu_ll.h b/components/hal/esp32c3/include/hal/cpu_ll.h index 5c9e56dfc9..cfb52410c8 100644 --- a/components/hal/esp32c3/include/hal/cpu_ll.h +++ b/components/hal/esp32c3/include/hal/cpu_ll.h @@ -28,6 +28,11 @@ #define CSR_PCMR_MACHINE 0x7e1 #define CSR_PCCR_MACHINE 0x7e2 +/*fast gpio*/ +#define CSR_GPIO_OEN_USER 0x803 +#define CSR_GPIO_IN_USER 0x804 +#define CSR_GPIO_OUT_USER 0x805 + #ifdef __cplusplus extern "C" { #endif @@ -163,6 +168,34 @@ static inline void cpu_ll_waiti(void) asm volatile ("wfi\n"); } +static inline void cpu_ll_enable_dedic_gpio_output(uint32_t mask) +{ + RV_WRITE_CSR(CSR_GPIO_OEN_USER, mask); +} + +static inline void cpu_ll_write_dedic_gpio_all(uint32_t value) +{ + RV_WRITE_CSR(CSR_GPIO_OUT_USER, value); +} + +static inline uint32_t cpu_ll_read_dedic_gpio_in(void) +{ + uint32_t value = RV_READ_CSR(CSR_GPIO_IN_USER); + return value; +} + +static inline uint32_t cpu_ll_read_dedic_gpio_out(void) +{ + uint32_t value = RV_READ_CSR(CSR_GPIO_OUT_USER); + return value; +} + +static inline void cpu_ll_write_dedic_gpio_mask(uint32_t mask, uint32_t value) +{ + RV_SET_CSR(CSR_GPIO_OUT_USER, mask & value); + RV_CLEAR_CSR(CSR_GPIO_OUT_USER, mask & ~(value)); +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/cpu_ll.h b/components/hal/esp32h2/include/hal/cpu_ll.h index 0b05675d32..02c4ff2f63 100644 --- a/components/hal/esp32h2/include/hal/cpu_ll.h +++ b/components/hal/esp32h2/include/hal/cpu_ll.h @@ -26,6 +26,11 @@ #define CSR_PCMR_MACHINE 0x7e1 #define CSR_PCCR_MACHINE 0x7e2 +/*fast gpio*/ +#define CSR_GPIO_OEN_USER 0x803 +#define CSR_GPIO_IN_USER 0x804 +#define CSR_GPIO_OUT_USER 0x805 + #ifdef __cplusplus extern "C" { #endif @@ -156,6 +161,34 @@ static inline void cpu_ll_waiti(void) asm volatile ("wfi\n"); } +static inline void cpu_ll_enable_dedic_gpio_output(uint32_t mask) +{ + RV_WRITE_CSR(CSR_GPIO_OEN_USER, mask); +} + +static inline void cpu_ll_write_dedic_gpio_all(uint32_t value) +{ + RV_WRITE_CSR(CSR_GPIO_OUT_USER, value); +} + +static inline uint32_t cpu_ll_read_dedic_gpio_in(void) +{ + uint32_t value = RV_READ_CSR(CSR_GPIO_IN_USER); + return value; +} + +static inline uint32_t cpu_ll_read_dedic_gpio_out(void) +{ + uint32_t value = RV_READ_CSR(CSR_GPIO_OUT_USER); + return value; +} + +static inline void cpu_ll_write_dedic_gpio_mask(uint32_t mask, uint32_t value) +{ + RV_SET_CSR(CSR_GPIO_OUT_USER, mask & value); + RV_CLEAR_CSR(CSR_GPIO_OUT_USER, mask & ~(value)); +} + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32c3/CMakeLists.txt b/components/soc/esp32c3/CMakeLists.txt index a8eac92c55..a59b5173c1 100644 --- a/components/soc/esp32c3/CMakeLists.txt +++ b/components/soc/esp32c3/CMakeLists.txt @@ -1,5 +1,6 @@ set(srcs "adc_periph.c" + "dedic_gpio_periph.c" "gdma_periph.c" "gpio_periph.c" "interrupts.c" diff --git a/components/soc/esp32c3/dedic_gpio_periph.c b/components/soc/esp32c3/dedic_gpio_periph.c new file mode 100644 index 0000000000..711274f893 --- /dev/null +++ b/components/soc/esp32c3/dedic_gpio_periph.c @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/gpio_sig_map.h" +#include "soc/dedic_gpio_periph.h" + +const dedic_gpio_signal_conn_t dedic_gpio_periph_signals = { + .module = -1, + .irq = -1, + .cores = { + [0] = { + .in_sig_per_channel = { + [0] = CPU_GPIO_IN0_IDX, + [1] = CPU_GPIO_IN1_IDX, + [2] = CPU_GPIO_IN2_IDX, + [3] = CPU_GPIO_IN3_IDX, + [4] = CPU_GPIO_IN4_IDX, + [5] = CPU_GPIO_IN5_IDX, + [6] = CPU_GPIO_IN6_IDX, + [7] = CPU_GPIO_IN7_IDX, + }, + .out_sig_per_channel = { + [0] = CPU_GPIO_OUT0_IDX, + [1] = CPU_GPIO_OUT1_IDX, + [2] = CPU_GPIO_OUT2_IDX, + [3] = CPU_GPIO_OUT3_IDX, + [4] = CPU_GPIO_OUT4_IDX, + [5] = CPU_GPIO_OUT5_IDX, + [6] = CPU_GPIO_OUT6_IDX, + [7] = CPU_GPIO_OUT7_IDX, + } + }, + }, +}; diff --git a/components/soc/esp32c3/include/soc/gpio_sig_map.h b/components/soc/esp32c3/include/soc/gpio_sig_map.h index 45be68b3e5..61aa95a3c4 100644 --- a/components/soc/esp32c3/include/soc/gpio_sig_map.h +++ b/components/soc/esp32c3/include/soc/gpio_sig_map.h @@ -53,26 +53,26 @@ #define GPIO_WLAN_PRIO_IDX 18 #define GPIO_BT_ACTIVE_IDX 19 #define GPIO_WLAN_ACTIVE_IDX 19 -#define BB_DIAG0_IDX 20 -#define BB_DIAG1_IDX 21 -#define BB_DIAG2_IDX 22 -#define BB_DIAG3_IDX 23 -#define BB_DIAG4_IDX 24 -#define BB_DIAG5_IDX 25 -#define BB_DIAG6_IDX 26 -#define BB_DIAG7_IDX 27 -#define BB_DIAG8_IDX 28 -#define BB_DIAG9_IDX 29 -#define BB_DIAG10_IDX 30 -#define BB_DIAG11_IDX 31 -#define BB_DIAG12_IDX 32 -#define BB_DIAG13_IDX 33 -#define BB_DIAG14_IDX 34 -#define BB_DIAG15_IDX 35 -#define BB_DIAG16_IDX 36 -#define BB_DIAG17_IDX 37 -#define BB_DIAG18_IDX 38 -#define BB_DIAG19_IDX 39 +#define CPU_GPIO_IN0_IDX 28 +#define CPU_GPIO_OUT0_IDX 28 +#define CPU_GPIO_IN1_IDX 29 +#define CPU_GPIO_OUT1_IDX 29 +#define CPU_GPIO_IN2_IDX 30 +#define CPU_GPIO_OUT2_IDX 30 +#define CPU_GPIO_IN3_IDX 31 +#define CPU_GPIO_OUT3_IDX 31 +#define CPU_GPIO_IN4_IDX 32 +#define CPU_GPIO_OUT4_IDX 32 +#define CPU_GPIO_IN5_IDX 33 +#define CPU_GPIO_OUT5_IDX 33 +#define CPU_GPIO_IN6_IDX 34 +#define CPU_GPIO_OUT6_IDX 34 +#define CPU_GPIO_IN7_IDX 35 +#define CPU_GPIO_OUT7_IDX 35 +#define USB_JTAG_TCK_OUT_IDX 36 +#define USB_JTAG_TMS_OUT_IDX 37 +#define USB_JTAG_TDI_OUT_IDX 38 +#define USB_JTAG_TDO_OUT_IDX 39 #define USB_EXTPHY_VP_IDX 40 #define USB_EXTPHY_OEN_IDX 40 #define USB_EXTPHY_VM_IDX 41 @@ -179,6 +179,7 @@ #define CLK_OUT_OUT2_IDX 124 #define CLK_OUT_OUT3_IDX 125 #define SPICS1_OUT_IDX 126 +#define USB_JTAG_TRST_OUT_IDX 127 #define SIG_GPIO_OUT_IDX 128 #define GPIO_MAP_DATE_IDX 0x2006130 #endif /* _SOC_GPIO_SIG_MAP_H_ */ diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index 5ad734e6d0..611ee03450 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -6,6 +6,7 @@ #pragma once #define SOC_CPU_CORES_NUM 1 +#define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_GDMA_SUPPORTED 1 #define SOC_TWAI_SUPPORTED 1 #define SOC_BT_SUPPORTED 1 @@ -97,6 +98,11 @@ // Support to configure sleep status #define SOC_GPIO_SUPPORT_SLP_SWITCH (1) +/*-------------------------- Dedicated GPIO CAPS -----------------------------*/ +#define SOC_DEDIC_GPIO_OUT_CHANNELS_NUM (8) /*!< 8 outward channels on each CPU core */ +#define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ +#define SOC_DEDIC_PERIPH_AUTO_ENABLE (1) /*!< The dedicated GPIO peripheral is enabled automatically */ + /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-C3 have 2 I2C. #define SOC_I2C_NUM (1) diff --git a/components/soc/esp32h2/CMakeLists.txt b/components/soc/esp32h2/CMakeLists.txt index a8eac92c55..a59b5173c1 100644 --- a/components/soc/esp32h2/CMakeLists.txt +++ b/components/soc/esp32h2/CMakeLists.txt @@ -1,5 +1,6 @@ set(srcs "adc_periph.c" + "dedic_gpio_periph.c" "gdma_periph.c" "gpio_periph.c" "interrupts.c" diff --git a/components/soc/esp32h2/dedic_gpio_periph.c b/components/soc/esp32h2/dedic_gpio_periph.c new file mode 100644 index 0000000000..711274f893 --- /dev/null +++ b/components/soc/esp32h2/dedic_gpio_periph.c @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/gpio_sig_map.h" +#include "soc/dedic_gpio_periph.h" + +const dedic_gpio_signal_conn_t dedic_gpio_periph_signals = { + .module = -1, + .irq = -1, + .cores = { + [0] = { + .in_sig_per_channel = { + [0] = CPU_GPIO_IN0_IDX, + [1] = CPU_GPIO_IN1_IDX, + [2] = CPU_GPIO_IN2_IDX, + [3] = CPU_GPIO_IN3_IDX, + [4] = CPU_GPIO_IN4_IDX, + [5] = CPU_GPIO_IN5_IDX, + [6] = CPU_GPIO_IN6_IDX, + [7] = CPU_GPIO_IN7_IDX, + }, + .out_sig_per_channel = { + [0] = CPU_GPIO_OUT0_IDX, + [1] = CPU_GPIO_OUT1_IDX, + [2] = CPU_GPIO_OUT2_IDX, + [3] = CPU_GPIO_OUT3_IDX, + [4] = CPU_GPIO_OUT4_IDX, + [5] = CPU_GPIO_OUT5_IDX, + [6] = CPU_GPIO_OUT6_IDX, + [7] = CPU_GPIO_OUT7_IDX, + } + }, + }, +}; diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 42e3393923..ed33d28ef6 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -6,6 +6,7 @@ #pragma once #define SOC_CPU_CORES_NUM 1 +#define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_GDMA_SUPPORTED 1 #define SOC_TWAI_SUPPORTED 1 #define SOC_BT_SUPPORTED 1 @@ -92,6 +93,11 @@ // Support to configure sleep status #define SOC_GPIO_SUPPORT_SLP_SWITCH (1) +/*-------------------------- Dedicated GPIO CAPS -----------------------------*/ +#define SOC_DEDIC_GPIO_OUT_CHANNELS_NUM (8) /*!< 8 outward channels on each CPU core */ +#define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ +#define SOC_DEDIC_PERIPH_AUTO_ENABLE (1) /*!< The dedicated GPIO peripheral is enabled automatically */ + /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-C3 have 2 I2C. #define SOC_I2C_NUM (1) diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index aa5705d86e..37182e64df 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -120,6 +120,7 @@ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ #define SOC_DEDIC_GPIO_ALLOW_REG_ACCESS (1) /*!< Allow access dedicated GPIO channel by register */ #define SOC_DEDIC_GPIO_HAS_INTERRUPT (1) /*!< Dedicated GPIO has its own interrupt source */ +#define SOC_DEDIC_GPIO_OUT_AUTO_ENABLE (1) /*!< Dedicated GPIO output attribution is enabled automatically */ /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-S2 have 2 I2C. diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 339c34124d..81ce978f5e 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -74,6 +74,7 @@ /*-------------------------- Dedicated GPIO CAPS -----------------------------*/ #define SOC_DEDIC_GPIO_OUT_CHANNELS_NUM (8) /*!< 8 outward channels on each CPU core */ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ +#define SOC_DEDIC_GPIO_OUT_AUTO_ENABLE (1) /*!< Dedicated GPIO output attribution is enabled automatically */ /*-------------------------- I2C CAPS ----------------------------------------*/ #include "i2c_caps.h" diff --git a/docs/en/api-reference/peripherals/dedic_gpio.rst b/docs/en/api-reference/peripherals/dedic_gpio.rst index 9f9c675957..89b83e948b 100644 --- a/docs/en/api-reference/peripherals/dedic_gpio.rst +++ b/docs/en/api-reference/peripherals/dedic_gpio.rst @@ -4,7 +4,7 @@ Dedicated GPIO Overview -------- -The dedicated GPIO is designed for CPU interaction with GPIO matrix and IO MUX. Any GPIO that is configured as "dedicated" can be access by CPU instructions directly, which makes it easy to achieve a high GPIO flip speed, and simulate serial/parallel interface in a bit-banging way. +The dedicated GPIO is designed for CPU interaction with GPIO matrix and IO MUX. Any GPIO that is configured as "dedicated" can be access by CPU instructions directly, which makes it easy to achieve a high GPIO flip speed, and simulate serial/parallel interface in a bit-banging way. As toggling a GPIO in this "CPU Dedicated" way costs few overhead, it would be great for cases like performance measurement using an oscilloscope. Create/Destroy GPIO Bundle @@ -75,7 +75,7 @@ GPIO Bundle Operations .. note:: The functions above just wrap the customized instructions defined for {IDF_TARGET_NAME}, for the details of those instructions, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__]. -.. only:: esp32s2 +.. only:: SOC_DEDIC_GPIO_HAS_INTERRUPT Interrupt Handling ------------------ @@ -112,11 +112,21 @@ For advanced users, they can always manipulate the GPIOs by writing assembly cod 1. Allocate a GPIO bundle: :cpp:func:`dedic_gpio_new_bundle` 2. Query the mask occupied by that bundle: :cpp:func:`dedic_gpio_get_out_mask` or/and :cpp:func:`dedic_gpio_get_in_mask` 3. Call CPU LL apis (e.g. `cpu_ll_write_dedic_gpio_mask`) or write assembly code with that mask +4. The fasted way of toggling IO is to use the dedicated "set/clear" instructions: -For details of supported dedicated GPIO instructions, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__]. ++----------+---------------------------+---------------------------+------------------------------------------------------------------------+ +| CPU Arch | Set bits of GPIO | Clear bits of GPIO | Remarks | ++==========+===========================+===========================+========================================================================+ +| Xtensa | set_bit_gpio_out imm[7:0] | clr_bit_gpio_out imm[7:0] | immediate value width depends on the number of dedicated GPIO channels | ++----------+---------------------------+---------------------------+------------------------------------------------------------------------+ +| RISC-V | csrrsi rd, csr, imm[4:0] | csrrci rd, csr, imm[4:0] | can only control the lowest 4 GPIO channels | ++----------+---------------------------+---------------------------+------------------------------------------------------------------------+ + + +For details of supported dedicated GPIO instructions, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__]. The supported dedicated CPU instructions are also wrapped inside `soc/cpu_ll.h` as helper inline functions. .. note:: - Writing assembly code in application could make your code hard to port between targets, because those customized instructions are not guaranteed to remain the same format in different targets. + Writing assembly code in application could make your code hard to port between targets, because those customized instructions are not guaranteed to remain the same format on different targets. Application Example