From 1f56d9a6aafdcbbd644f4fea73e0d5596ce64aac Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Thu, 22 Nov 2018 15:39:00 +0800 Subject: [PATCH] feat(esp8266): Add new "ets_vprintf" to save stack It is better for interrupt and NMI functions to output information. --- components/esp8266/Kconfig | 20 ++ components/esp8266/ld/esp8266.common.ld | 4 + components/esp8266/source/ets_printf.c | 236 +++++++++++++++++++++++- 3 files changed, 255 insertions(+), 5 deletions(-) diff --git a/components/esp8266/Kconfig b/components/esp8266/Kconfig index 286205c5..674dc357 100644 --- a/components/esp8266/Kconfig +++ b/components/esp8266/Kconfig @@ -49,6 +49,26 @@ config ESP_FILENAME_MACRO_NULL bool "null" endchoice +config USING_NEW_ETS_VPRINTF + bool "Using new ets_vprintf instead of rom code" + default y + help + Enable this option, SDK will use new "ets_vprintf" function instead of old code "ets_vprintf" which is depend on ROM + code "ets_io_vprintf". + + Note: Bootloader can't use this function. + +config LINK_ETS_PRINTF_TO_IRAM + bool "Link ets_printf to IRAM" + default n + help + Enable this option, SDK will link the old/new "ets_printf" and "ets_vprintf" to IRAM instead of flash. + So although flash can't be access for SoC when reading to writing, the "ets_printf" and "ets_vprintf" also can be used by user. + + Using new "ets_vprintf" should cost more 1.6KB IRAM. + + Note: Bootloader can't use this function. + config SOC_FULL_ICACHE bool "Enable full cache mode" default n diff --git a/components/esp8266/ld/esp8266.common.ld b/components/esp8266/ld/esp8266.common.ld index 514c6def..f35ca08a 100644 --- a/components/esp8266/ld/esp8266.common.ld +++ b/components/esp8266/ld/esp8266.common.ld @@ -129,6 +129,10 @@ SECTIONS *libfreertos.a:freertos_hooks.o(.bss .data .bss.* .data.* COMMON) #endif +#ifdef CONFIG_LINK_ETS_PRINTF_TO_IRAM + *libesp8266.a:ets_printf.o(.literal .text .literal.* .text.* .rodata.* .rodata) +#endif + _text_end = ABSOLUTE(.); _etext = .; } >iram1_0_seg :iram1_0_phdr diff --git a/components/esp8266/source/ets_printf.c b/components/esp8266/source/ets_printf.c index 06e90c46..bc0e316e 100644 --- a/components/esp8266/source/ets_printf.c +++ b/components/esp8266/source/ets_printf.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include "sdkconfig.h" @@ -22,7 +23,7 @@ #include "esp8266/uart_register.h" #include "esp8266/rom_functions.h" -int IRAM_ATTR ets_putc(int c) +int ets_putc(int c) { while (1) { uint32_t fifo_cnt = READ_PERI_REG(UART_STATUS(CONFIG_CONSOLE_UART_NUM)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S); @@ -36,15 +37,240 @@ int IRAM_ATTR ets_putc(int c) return c; } -int IRAM_ATTR ets_vprintf(const char *fmt, va_list ap) +#if defined(CONFIG_USING_NEW_ETS_VPRINTF) && !defined(BOOTLOADER_BUILD) + +#define FILL_0 0x01 +#define FILL_LEFT 0x02 +#define POINTOR 0x04 +#define ALTERNATE 0x08 +#define OUPUT_INT 0x10 +#define START 0x20 + +#define VINT_STR_MAX 10 + +typedef union _val_cache { + uint8_t val8; + int32_t val32; + uint32_t val32u; + const char *valcp; +} val_cache_t; + +typedef struct _val_attr { + int8_t type; + uint8_t state; + uint8_t fillbytes; + uint8_t precision; + + val_cache_t value; +} val_attr_t; + +#define isdigit(_c) ((_c <= '9') && (_c >= '0')) +#define fill_num(_attr) ((attr)->fillbytes) +#define isfill_0(_attr) (fill_num(_attr) && ((_attr)->state & FILL_0)) +#define isfill_left(_attr) (fill_num(_attr) && ((_attr)->state & FILL_LEFT)) +#define isstart(_attr) ((_attr)->state & START) + +static inline void ets_printf_ch_mutlti(uint32_t c, uint32_t len) +{ + while (len--) + ets_putc(c); +} + +static inline void ets_printf_buf(const char *s, uint32_t len) +{ + while (len--) + ets_putc(*s++); +} + +static int ets_printf_str(const val_attr_t * const attr) +{ + const char *s = attr->value.valcp; + s = s == NULL ? "" : s; + + if (fill_num(attr)) { + unsigned char left; + unsigned char len; + + while (*s != '\0') + s++; + len = s - attr->value.valcp; + left = fill_num(attr) > len ? fill_num(attr) - len : 0; + + if (!isfill_left(attr)) { + ets_printf_ch_mutlti(' ', left); + } + + ets_printf_buf(attr->value.valcp, len); + + if (isfill_left(attr)) { + ets_printf_ch_mutlti(' ', left); + } + } else { + while (*s != '\0') + ets_putc(*s++); + } + + return 0; +} + +static int ets_printf_int(val_attr_t * const attr, uint8_t hex) +{ + char buf[VINT_STR_MAX]; + unsigned char offset = VINT_STR_MAX; + + if (attr->value.val32u != 0) { + for (; attr->value.val32u > 0; attr->value.val32u /= hex) { + unsigned char c = attr->value.val32u % hex; + if (c < 10) + buf[--offset] = c + '0'; + else + buf[--offset] = c + 'a' - 10; + } + } else + buf[--offset] = '0'; + + if (fill_num(attr)) { + char fill_data = isfill_0(attr) ? '0' : ' '; + unsigned char len = fill_num(attr) - (VINT_STR_MAX - offset); + unsigned char left = fill_num(attr) > (VINT_STR_MAX - offset) ? len : 0; + + if (!isfill_left(attr)) { + ets_printf_ch_mutlti(fill_data, left); + } + + ets_printf_buf(&buf[offset], VINT_STR_MAX - offset); + + if (isfill_left(attr)) { + ets_printf_ch_mutlti(fill_data, left); + } + } else { + ets_printf_buf(&buf[offset], VINT_STR_MAX - offset); + } + + return 0; +} + +int ets_vprintf(const char *fmt, va_list va) +{ + for (; ;) { + const char *ps = fmt; + val_attr_t attr; + + while (*ps != '\0' && *ps != '%') + ets_putc(*ps++); + + if (*ps == '\0') + break; + + fmt = ps; + + attr.state = 0; + attr.type = -1; + attr.fillbytes = 0; + attr.precision = 0; + + for (; ;) { + switch (*++ps) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'c': + case 's': + case 'p': + case '\0': + attr.type = *ps++; + break; + case '#': + attr.state |= ALTERNATE; + ps++; + break; + case '0'...'9': + if (!isstart(&attr) && *ps == '0') { + attr.state |= FILL_0; + } else { + if (attr.state & POINTOR) + attr.precision = attr.precision * 10 + *ps - '0'; + else + attr.fillbytes = attr.fillbytes * 10 + *ps - '0'; + } + break; + case '.': + attr.state |= POINTOR; + break; + case '-': + attr.state |= FILL_LEFT; + break; + default: + attr.type = -2; + break; + } + + attr.state |= START; + + if (attr.type != -1) + break; + } + + switch (attr.type) { + case 'c': + attr.value.val8 = (char)va_arg(va, int); + ets_putc(attr.value.val8); + break; + case 's': + attr.value.valcp = va_arg(va, const char *); + ets_printf_str(&attr); + break; + case 'i': + case 'd': + attr.value.val32 = va_arg(va, int); + if (attr.value.val32 < 0) { + ets_putc('-'); + attr.value.val32 = -attr.value.val32; + } + ets_printf_int(&attr, 10); + break; + case 'u': + attr.value.val32u = va_arg(va, unsigned int); + ets_printf_int(&attr, 10); + break; + case 'x': + attr.value.val32u = va_arg(va, unsigned int); + ets_printf_int(&attr, 16); + break; + case 'p': + attr.value.valcp = va_arg(va, const char *); + ets_printf_int(&attr, 16); + default: + break; + } + + fmt = ps; + } + + return 0; +} + +#else /* defined(CONFIG_USING_NEW_ETS_VPRINTF) && !defined(BOOTLOADER_BUILD) */ + +int ets_vprintf(const char *fmt, va_list ap) { return ets_io_vprintf(ets_putc, fmt, ap); } -/* Re-write ets_printf in SDK side, since ets_printf in ROM will use a global +#endif /* defined(CONFIG_USING_NEW_ETS_VPRINTF) && !defined(BOOTLOADER_BUILD) */ + +/** + * Re-write ets_printf in SDK side, since ets_printf in ROM will use a global * variable which address is in heap region of SDK side. If use ets_printf in ROM, - * this variable maybe re-write when heap alloc and modification.*/ -int IRAM_ATTR ets_printf(const char* fmt, ...) + * this variable maybe re-write when heap alloc and modification. + * + * Using new "ets_vprintf" costs stack without alignment and accuracy: + * just "fmt": 136 Bytes + * "%s": 172 Bytes + * "%p", "%d, "%i, "%u", "%x": 215 Bytes + */ +int ets_printf(const char *fmt, ...) { va_list ap; int ret;