From 0c924e56c640a08ec8db039350c53ada33416550 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Mon, 22 Jun 2015 18:43:01 +1000 Subject: [PATCH 1/2] Support for loading sub-32 wide data from irom. --- app/include/rom.h | 54 +++++++++++++++++++++ app/user/user_exceptions.c | 97 ++++++++++++++++++++++++++++++++++++++ app/user/user_exceptions.h | 5 ++ app/user/user_main.c | 4 ++ include/c_types.h | 2 + 5 files changed, 162 insertions(+) create mode 100644 app/user/user_exceptions.c create mode 100644 app/user/user_exceptions.h diff --git a/app/include/rom.h b/app/include/rom.h index 88d7c81d..51d3fbe1 100644 --- a/app/include/rom.h +++ b/app/include/rom.h @@ -37,3 +37,57 @@ extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_ // initialized because it uses mem_malloc extern void mem_init(void * start_addr); + + +// Hardware exception handling +struct exception_frame +{ + uint32_t epc; + uint32_t ps; + uint32_t sar; + uint32_t unused; + union { + struct { + uint32_t a0; + // note: no a1 here! + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + uint32_t a8; + uint32_t a9; + uint32_t a10; + uint32_t a11; + uint32_t a12; + uint32_t a13; + uint32_t a14; + uint32_t a15; + }; + uint32_t a_reg[15]; + }; + uint32_t cause; +}; + +/** + * C-level exception handler callback prototype. + * + * Does not need an RFE instruction - it is called through a wrapper which + * performs state capture & restore, as well as the actual RFE. + * + * @param ef An exception frame containing the relevant state from the + * exception triggering. This state may be manipulated and will + * be applied on return. + * @param cause The exception cause number. + */ +typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause); + +/** + * Sets the exception handler function for a particular exception cause. + * @param handler The exception handler to install. + * If NULL, reverts to the XTOS default handler. + * @returns The previous exception handler, or NULL if none existed prior. + */ +exception_handler_fn _xtos_set_exception_handler (uint32_t cause, exception_handler_fn handler); + diff --git a/app/user/user_exceptions.c b/app/user/user_exceptions.c new file mode 100644 index 00000000..d20f0622 --- /dev/null +++ b/app/user/user_exceptions.c @@ -0,0 +1,97 @@ +/* + * Copyright 2015 Dius Computing Pty Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the copyright holders nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Johny Mattsson + */ + +#include "user_exceptions.h" + +#define LOAD_MASK 0x00f00fu +#define L8UI_MATCH 0x000002u +#define L16UI_MATCH 0x001002u +#define L16SI_MATCH 0x009002u + + +void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) +{ + /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */ + (void)cause; + + uint32_t epc1 = ef->epc; + uint32_t excvaddr; + uint32_t insn; + asm ( + "rsr %0, EXCVADDR;" /* read out the faulting address */ + "movi a4, ~3;" /* prepare a mask for the EPC */ + "and a4, a4, %2;" /* apply mask for 32bit aligned base */ + "l32i a5, a4, 0;" /* load part 1 */ + "l32i a6, a4, 4;" /* load part 2 */ + "ssa8l %2;" /* set up shift register for src op */ + "src %1, a6, a5;" /* right shift to get faulting instruction */ + :"=r"(excvaddr), "=r"(insn) + :"r"(epc1) + :"a4", "a5", "a6" + ); + + uint32_t valmask = 0; + uint32_t what = insn & LOAD_MASK; + + if (what == L8UI_MATCH) + valmask = 0xffu; + else if (what == L16UI_MATCH || what == L16SI_MATCH) + valmask = 0xffffu; + else + { +die: + /* Turns out we couldn't fix this, trigger a system break instead + * and hang if the break doesn't get handled. This is effectively + * what would happen if the default handler was installed. */ + asm ("break 1, 1"); + while (1) {} + } + + /* Load, shift and mask down to correct size */ + uint32_t val = (*(uint32_t *)(excvaddr & ~0x3)); + val >>= (excvaddr & 0x3) * 8; + val &= valmask; + + /* Sign-extend for L16SI, if applicable */ + if (what == L16SI_MATCH && (val & 0x8000)) + val |= 0xffff0000; + + int regno = (insn & 0x0000f0u) >> 4; + if (regno == 1) + goto die; /* we can't support loading into a1, just die */ + else if (regno != 0) + --regno; /* account for skipped a1 in exception_frame */ + + ef->a_reg[regno] = val; /* carry out the load */ + ef->epc += 3; /* resume at following instruction */ +} diff --git a/app/user/user_exceptions.h b/app/user/user_exceptions.h new file mode 100644 index 00000000..d2cf5166 --- /dev/null +++ b/app/user/user_exceptions.h @@ -0,0 +1,5 @@ +#include "c_types.h" +#include "rom.h" +#include + +void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) TEXT_SECTION_ATTR; diff --git a/app/user/user_main.c b/app/user/user_main.c index 88938da8..6064937e 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -16,6 +16,7 @@ #include "flash_fs.h" #include "user_interface.h" +#include "user_exceptions.h" #include "ets_sys.h" #include "driver/uart.h" @@ -125,6 +126,9 @@ void nodemcu_init(void) *******************************************************************************/ void user_init(void) { + _xtos_set_exception_handler ( + EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler); + // NODE_DBG("SDK version:%s\n", system_get_sdk_version()); // system_print_meminfo(); // os_printf("Heap size::%d.\n",system_get_free_heap_size()); diff --git a/include/c_types.h b/include/c_types.h index 55bffd46..34da0503 100644 --- a/include/c_types.h +++ b/include/c_types.h @@ -84,6 +84,8 @@ typedef enum { #define ICACHE_FLASH_ATTR #endif /* ICACHE_FLASH */ +#define TEXT_SECTION_ATTR __attribute__((section(".text"))) + #ifndef __cplusplus typedef unsigned char bool; #define BOOL bool From b0f9788a6d02609dc31a1a3360ad79210e3443e2 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Tue, 23 Jun 2015 15:38:52 +1000 Subject: [PATCH 2/2] Move constants to ROM. Frees up 16k+ of RAM. Accessing 8bit and 16bit constants from ROM rather than RAM comes with a performance cost, as these loads go through the load/store exception vector. Any performance critical constants can be forced back into RAM as RAM_CONST_ATTR. The entry point has changed from call_user_start() to user_start_trampoline() in order for the exception handler to be installed early enough. --- app/Makefile | 2 +- app/user/user_exceptions.c | 15 +++++++++++++++ app/user/user_main.c | 18 +++++++++++++++--- include/c_types.h | 1 + ld/eagle.app.v6.ld | 9 +++++---- tools/esptool.py | 16 +++++++++++++++- 6 files changed, 52 insertions(+), 9 deletions(-) diff --git a/app/Makefile b/app/Makefile index 87331c24..8275a264 100644 --- a/app/Makefile +++ b/app/Makefile @@ -99,7 +99,7 @@ LINKFLAGS_eagle.app.v6 = \ -nostdlib \ -T$(LD_FILE) \ -Wl,--no-check-sections \ - -u call_user_start \ + -Wl,--wrap=_xtos_set_exception_handler \ -Wl,-static \ -Wl,--start-group \ -lc \ diff --git a/app/user/user_exceptions.c b/app/user/user_exceptions.c index d20f0622..fb26e76a 100644 --- a/app/user/user_exceptions.c +++ b/app/user/user_exceptions.c @@ -95,3 +95,18 @@ die: ef->a_reg[regno] = val; /* carry out the load */ ef->epc += 3; /* resume at following instruction */ } + + +/** + * The SDK's user_main function installs a debugging handler regardless + * of whether there's a proper handler installed for EXCCAUSE_LOAD_STORE_ERROR, + * which of course breaks everything if we allow that to go through. As such, + * we use the linker to wrap that call and stop the SDK from shooting itself in + * its proverbial foot. + */ +exception_handler_fn +__wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) +{ + if (cause != EXCCAUSE_LOAD_STORE_ERROR) + __real__xtos_set_exception_handler (cause, fn); +} diff --git a/app/user/user_main.c b/app/user/user_main.c index 6064937e..c24e0f00 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -26,6 +26,21 @@ #define TASK_QUEUE_LEN 4 os_event_t *taskQueue; + +/* Note: the trampoline *must* be explicitly put into the .text segment, since + * by the time it is invoked the irom has not yet been mapped. This naturally + * also goes for anything the trampoline itself calls. + */ +void user_start_trampoline (void) TEXT_SECTION_ATTR; +void user_start_trampoline (void) +{ + __real__xtos_set_exception_handler ( + EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler); + + call_user_start (); +} + + void task_lua(os_event_t *e){ char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; NODE_DBG("Task task_lua started.\n"); @@ -126,9 +141,6 @@ void nodemcu_init(void) *******************************************************************************/ void user_init(void) { - _xtos_set_exception_handler ( - EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler); - // NODE_DBG("SDK version:%s\n", system_get_sdk_version()); // system_print_meminfo(); // os_printf("Heap size::%d.\n",system_get_free_heap_size()); diff --git a/include/c_types.h b/include/c_types.h index 34da0503..2cd6332a 100644 --- a/include/c_types.h +++ b/include/c_types.h @@ -85,6 +85,7 @@ typedef enum { #endif /* ICACHE_FLASH */ #define TEXT_SECTION_ATTR __attribute__((section(".text"))) +#define RAM_CONST_ATTR __attribute__((section(".text"))) #ifndef __cplusplus typedef unsigned char bool; diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld index a36cb31e..c0c7c7c5 100644 --- a/ld/eagle.app.v6.ld +++ b/ld/eagle.app.v6.ld @@ -19,7 +19,7 @@ PHDRS /* Default entry point: */ -ENTRY(call_user_start) +ENTRY(user_start_trampoline) PROVIDE(_memmap_vecbase_reset = 0x40000000); /* Various memory-map dependent cache attribute settings: */ _memmap_cacheattr_wb_base = 0x00000110; @@ -75,6 +75,10 @@ SECTIONS /* put font and progmem data into irom0 */ *(.u8g_progmem.*) + /* Trade some performance for lots of ram. At the time of writing the + * available Lua heap went from 18248 to 34704. */ + *(.rodata*) + _irom0_text_end = ABSOLUTE(.); _flash_used_end = ABSOLUTE(.); } >irom0_0_seg :irom0_0_phdr @@ -124,10 +128,7 @@ SECTIONS .rodata : ALIGN(4) { _rodata_start = ABSOLUTE(.); - *(.rodata) - *(.rodata.*) *(.gnu.linkonce.r.*) - *(.rodata1) __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); *(.xt_except_table) *(.gcc_except_table) diff --git a/tools/esptool.py b/tools/esptool.py index 88a4b4ed..53e10041 100755 --- a/tools/esptool.py +++ b/tools/esptool.py @@ -360,6 +360,20 @@ class ELFFile: self._fetch_symbols() return self.symbols[sym] + def get_entry_point(self): + tool_readelf = "xtensa-lx106-elf-readelf" + if os.getenv('XTENSA_CORE')=='lx106': + tool_objcopy = "xt-readelf" + try: + proc = subprocess.Popen([tool_readelf, "-h", self.name], stdout=subprocess.PIPE) + except OSError: + print "Error calling "+tool_nm+", do you have Xtensa toolchain in PATH?" + sys.exit(1) + for l in proc.stdout: + fields = l.strip().split() + if fields[0] == "Entry": + return int(fields[3], 0); + def load_section(self, section): tool_objcopy = "xtensa-lx106-elf-objcopy" if os.getenv('XTENSA_CORE')=='lx106': @@ -586,7 +600,7 @@ if __name__ == '__main__': args.output = args.input + '-' e = ELFFile(args.input) image = ESPFirmwareImage() - image.entrypoint = e.get_symbol_addr("call_user_start") + image.entrypoint = e.get_entry_point() for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): data = e.load_section(section) image.add_segment(e.get_symbol_addr(start), data)