diff --git a/core/esp_iomux.c b/core/esp_iomux.c index fb4ec67..414da19 100644 --- a/core/esp_iomux.c +++ b/core/esp_iomux.c @@ -7,14 +7,15 @@ #include "esp/iomux.h" #include "common_macros.h" -/* These are non-static versions of the GPIO mapping tables in - iomux.h, so if they need to be linked only one copy is linked for - the entire program. +const static IRAM_DATA uint32_t IOMUX_TO_GPIO[] = { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 }; +const static IRAM_DATA uint32_t GPIO_TO_IOMUX[] = { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 }; - These are only ever linked in if the arguments to gpio_to_ionum - or ionum_to_gpio are not known at compile time. +uint8_t IRAM gpio_to_iomux(const uint8_t gpio_number) +{ + return GPIO_TO_IOMUX[gpio_number]; +} - Arrays are declared as 32-bit integers in IROM to save RAM. -*/ -const IROM uint32_t GPIO_TO_IOMUX_MAP[] = _GPIO_TO_IOMUX; -const IROM uint32_t IOMUX_TO_GPIO_MAP[] = _IOMUX_TO_GPIO; +uint8_t IRAM iomux_to_gpio(const uint8_t iomux_number) +{ + return IOMUX_TO_GPIO[iomux_number]; +} diff --git a/core/include/common_macros.h b/core/include/common_macros.h index 355248b..7c6dd56 100644 --- a/core/include/common_macros.h +++ b/core/include/common_macros.h @@ -53,8 +53,30 @@ #define IROM __attribute__((section(".irom0.literal"))) const #endif -#define INLINED inline static __attribute__((always_inline)) __attribute__((unused)) +/* Use this macro to place functions into Instruction RAM (IRAM) + instead of flash memory (IROM). + This is useful for functions which are called when the flash may + not be available (for example during NMI exceptions), or for + functions which are called very frequently and need high + performance. + + Bear in mind IRAM is limited (32KB), compared to up to 1MB of flash. +*/ #define IRAM __attribute__((section(".iram1.text"))) +/* Use this macro to place read-only data into Instruction RAM (IRAM) + instead of loaded into rodata which resides in DRAM. + + This may be useful to free up data RAM. However all data read from + the instruction space must be 32-bit aligned word reads + (non-aligned reads will use an interrupt routine to "fix" them and + still work, but are very slow.. +*/ +#ifdef __cplusplus + #define IRAM_DATA __attribute__((section(".iram1.rodata"))) +#else + #define IRAM_DATA __attribute__((section(".iram1.rodata"))) const +#endif + #endif diff --git a/core/include/esp/gpio.h b/core/include/esp/gpio.h index da7bcfa..b5efcaf 100644 --- a/core/include/esp/gpio.h +++ b/core/include/esp/gpio.h @@ -23,41 +23,14 @@ typedef enum { /* Enable GPIO on the specified pin, and set it to input/output/ with * pullup as needed */ -INLINED void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction) -{ - uint32_t iomux_flags; - - switch(direction) { - case GPIO_INPUT: - iomux_flags = 0; - break; - case GPIO_OUTPUT: - iomux_flags = IOMUX_PIN_OUTPUT_ENABLE; - break; - case GPIO_OUT_OPEN_DRAIN: - iomux_flags = IOMUX_PIN_OUTPUT_ENABLE; - break; - case GPIO_INPUT_PULLUP: - iomux_flags = IOMUX_PIN_PULLUP; - break; - } - iomux_set_gpio_function(gpio_num, iomux_flags); - if(direction == GPIO_OUT_OPEN_DRAIN) - GPIO.CONF[gpio_num] |= GPIO_CONF_OPEN_DRAIN; - else - GPIO.CONF[gpio_num] &= ~GPIO_CONF_OPEN_DRAIN; - if (iomux_flags & IOMUX_PIN_OUTPUT_ENABLE) - GPIO.ENABLE_OUT_SET = BIT(gpio_num); - else - GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num); -} +void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction); /* Disable GPIO on the specified pin, and set it Hi-Z. * * If later muxing this pin to a different function, make sure to set * IOMUX_PIN_OUTPUT_ENABLE if necessary to enable the output buffer. */ -INLINED void gpio_disable(const uint8_t gpio_num) +static inline void gpio_disable(const uint8_t gpio_num) { GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num); *gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE; @@ -67,7 +40,7 @@ INLINED void gpio_disable(const uint8_t gpio_num) * * Only works if pin has been set to GPIO_OUTPUT via gpio_enable() */ -INLINED void gpio_write(const uint8_t gpio_num, const bool set) +static inline void gpio_write(const uint8_t gpio_num, const bool set) { if(set) GPIO.OUT_SET = BIT(gpio_num); @@ -79,7 +52,7 @@ INLINED void gpio_write(const uint8_t gpio_num, const bool set) * * Only works if pin has been set to GPIO_OUTPUT via gpio_enable() */ -INLINED void gpio_toggle(const uint8_t gpio_num) +static inline void gpio_toggle(const uint8_t gpio_num) { /* Why implement like this instead of GPIO_OUT_REG ^= xxx? Concurrency. If an interrupt or higher priority task writes to @@ -98,7 +71,7 @@ INLINED void gpio_toggle(const uint8_t gpio_num) * If pin is set as an input, this reads the value on the pin. * If pin is set as an output, this reads the last value written to the pin. */ -INLINED bool gpio_read(const uint8_t gpio_num) +static inline bool gpio_read(const uint8_t gpio_num) { return GPIO.IN & BIT(gpio_num); } @@ -109,7 +82,7 @@ extern void gpio_interrupt_handler(void); * * If int_type is not GPIO_INTTYPE_NONE, the gpio_interrupt_handler will be attached and unmasked. */ -INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int_type) +static inline void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int_type) { GPIO.CONF[gpio_num] = SET_FIELD(GPIO.CONF[gpio_num], GPIO_CONF_INTTYPE, int_type); if(int_type != GPIO_INTTYPE_NONE) { @@ -119,7 +92,7 @@ INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int } /* Return the interrupt type set for a pin */ -INLINED gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num) +static inline gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num) { return (gpio_inttype_t)FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_num]); } diff --git a/core/include/esp/interrupts.h b/core/include/esp/interrupts.h index 55a115c..a6e779a 100644 --- a/core/include/esp/interrupts.h +++ b/core/include/esp/interrupts.h @@ -39,7 +39,7 @@ void sdk__xt_tick_timer_init (void); void sdk__xt_timer_int(void); void sdk__xt_timer_int1(void); -INLINED uint32_t _xt_get_intlevel(void) +static inline uint32_t _xt_get_intlevel(void) { uint32_t level; __asm__ volatile("rsr %0, intlevel" : "=a"(level)); @@ -53,7 +53,7 @@ INLINED uint32_t _xt_get_intlevel(void) portDISABLE_INTERRUPTS/portENABLE_INTERRUPTS for non-FreeRTOS & non-portable code. */ -INLINED uint32_t _xt_disable_interrupts(void) +static inline uint32_t _xt_disable_interrupts(void) { uint32_t old_level; __asm__ volatile ("rsil %0, " XTSTR(XCHAL_EXCM_LEVEL) : "=a" (old_level)); @@ -61,14 +61,14 @@ INLINED uint32_t _xt_disable_interrupts(void) } /* Restore PS level. Intended to be used with _xt_disable_interrupts */ -INLINED void _xt_restore_interrupts(uint32_t new_ps) +static inline void _xt_restore_interrupts(uint32_t new_ps) { __asm__ volatile ("wsr %0, ps; rsync" :: "a" (new_ps)); } /* ESPTODO: the mask/unmask functions aren't thread safe */ -INLINED void _xt_isr_unmask(uint32_t unmask) +static inline void _xt_isr_unmask(uint32_t unmask) { uint32_t intenable; asm volatile ("rsr %0, intenable" : "=a" (intenable)); @@ -76,7 +76,7 @@ INLINED void _xt_isr_unmask(uint32_t unmask) asm volatile ("wsr %0, intenable; esync" :: "a" (intenable)); } -INLINED void _xt_isr_mask (uint32_t mask) +static inline void _xt_isr_mask (uint32_t mask) { uint32_t intenable; asm volatile ("rsr %0, intenable" : "=a" (intenable)); @@ -84,14 +84,14 @@ INLINED void _xt_isr_mask (uint32_t mask) asm volatile ("wsr %0, intenable; esync" :: "a" (intenable)); } -INLINED uint32_t _xt_read_ints (void) +static inline uint32_t _xt_read_ints (void) { uint32_t interrupt; asm volatile ("rsr %0, interrupt" : "=a" (interrupt)); return interrupt; } -INLINED void _xt_clear_ints(uint32_t mask) +static inline void _xt_clear_ints(uint32_t mask) { asm volatile ("wsr %0, intclear; esync" :: "a" (mask)); } diff --git a/core/include/esp/iomux.h b/core/include/esp/iomux.h index a893f4e..214c9e8 100644 --- a/core/include/esp/iomux.h +++ b/core/include/esp/iomux.h @@ -22,7 +22,7 @@ extern "C" { * known at compile time, or return the result from a lookup table if not. * */ -inline static uint8_t gpio_to_iomux(const uint8_t gpio_number); +uint8_t IRAM gpio_to_iomux(const uint8_t gpio_number); /** * Convert an iomux register index to a GPIO pin number. @@ -31,7 +31,7 @@ inline static uint8_t gpio_to_iomux(const uint8_t gpio_number); * known at compile time, or return the result from a lookup table if not. * */ -inline static uint8_t iomux_to_gpio(const uint8_t iomux_num); +uint8_t IRAM iomux_to_gpio(const uint8_t iomux_num); /** * Directly get the IOMUX register for a particular gpio number @@ -64,10 +64,6 @@ inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint IOMUX.PIN[reg_idx] = func | flags; } -/* esp_iomux_private contains implementation parts of the inline functions - declared above */ -#include "esp/iomux_private.h" - #ifdef __cplusplus } #endif diff --git a/core/include/esp/iomux_private.h b/core/include/esp/iomux_private.h deleted file mode 100644 index d3cb780..0000000 --- a/core/include/esp/iomux_private.h +++ /dev/null @@ -1,46 +0,0 @@ -/** esp/iomux_private.h - * - * Private implementation parts of iomux registers. In headers to - * allow compile-time optimisations. - * - * Part of esp-open-rtos - * Copyright (C) 2015 Superhouse Automation Pty Ltd - * BSD Licensed as described in the file LICENSE - */ - -/* Mapping from register index to GPIO and from GPIO index to register - number. DO NOT USE THESE IN YOUR CODE, call gpio_to_iomux(xxx) or - iomux_to_gpio(xxx) instead. -*/ -#ifndef _IOMUX_PRIVATE -#define _IOMUX_PRIVATE - -#include "common_macros.h" - -#define _IOMUX_TO_GPIO { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 } -#define _GPIO_TO_IOMUX { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 } - -extern const IROM uint32_t GPIO_TO_IOMUX_MAP[]; -extern const IROM uint32_t IOMUX_TO_GPIO_MAP[]; - -INLINED uint8_t gpio_to_iomux(const uint8_t gpio_number) -{ - if(__builtin_constant_p(gpio_number)) { - static const uint8_t _regs[] = _GPIO_TO_IOMUX; - return _regs[gpio_number]; - } else { - return GPIO_TO_IOMUX_MAP[gpio_number]; - } -} - -INLINED uint8_t iomux_to_gpio(const uint8_t iomux_number) -{ - if(__builtin_constant_p(iomux_number)) { - static const uint8_t _regs[] = _IOMUX_TO_GPIO; - return _regs[iomux_number]; - } else { - return IOMUX_TO_GPIO_MAP[iomux_number]; - } -} - -#endif diff --git a/examples/blink/blink.c b/examples/blink/blink.c index 00388b5..92fe700 100644 --- a/examples/blink/blink.c +++ b/examples/blink/blink.c @@ -12,9 +12,6 @@ const int gpio = 14; /* This task uses the high level GPIO API (esp_gpio.h) to blink an LED. * - * Even though it reads better than the register-level version in blinkenRegisterTask, - * they compile to the exact same instructions (except gpio_enable also set the output type in - * the GPIO control register). */ void blinkenTask(void *pvParameters) { @@ -28,10 +25,12 @@ void blinkenTask(void *pvParameters) } -/* This task uses all raw register operations to set the pins. +/* This task demonstrates an alternative way to use raw register + operations to blink an LED. - It's not fully parameterised, as the IOMUX_GPIO# macros involve a non-linear - mapping from GPIO to IOMUX ports. + The step that sets the iomux register can't be automatically + updated from the 'gpio' constant variable, so you need to change + the line that sets IOMUX_GPIO14 if you change 'gpio'. There is no significant performance benefit to this way over the blinkenTask version, so it's probably better to use the blinkenTask diff --git a/examples/experiments/unaligned_load/unaligned_load.c b/examples/experiments/unaligned_load/unaligned_load.c index 48016af..258b4d0 100644 --- a/examples/experiments/unaligned_load/unaligned_load.c +++ b/examples/experiments/unaligned_load/unaligned_load.c @@ -17,7 +17,7 @@ const char *dramtest = TESTSTRING; const __attribute__((section(".iram1.notrodata"))) char iramtest[] = TESTSTRING; const __attribute__((section(".text.notrodata"))) char iromtest[] = TESTSTRING; -INLINED uint32_t get_ccount (void) +static inline uint32_t get_ccount (void) { uint32_t ccount; asm volatile ("rsr.ccount %0" : "=a" (ccount));