diff --git a/.dir-locals.el b/.dir-locals.el index d14fc88..6eccfa9 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -6,4 +6,20 @@ (c-file-style . "bsd") (c-basic-offset . 4) ) + (asm-mode + (indent-tabs-mode . nil) + ; this is basically a hack so asm-mode indents with spaces not tabs + ; taken from http://stackoverflow.com/questions/2668563/emacs-indentation-in-asm-mode + ; (moving to gas-mode may be a better choice) + (tab-stop-list (quote (4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 112 116 120))) + (asm-comment-char . "#") + ) ) + +; IMPORTANT: If you want to write assembly and have indenting to not be infuriating, +; you probably also want this in your .emacs file: +; +; (add-hook 'asm-mode-hook '(lambda () (setq indent-line-function 'indent-relative))) +; +; This is not safe to set as a local variable. + diff --git a/FreeRTOS/Source/portable/esp8266/port.c b/FreeRTOS/Source/portable/esp8266/port.c index 71c6658..fc452f0 100644 --- a/FreeRTOS/Source/portable/esp8266/port.c +++ b/FreeRTOS/Source/portable/esp8266/port.c @@ -198,6 +198,8 @@ portBASE_TYPE xPortStartScheduler( void ) } /* Initialize system tick timer interrupt and schedule the first tick. */ + _xt_isr_attach(INUM_TICK, sdk__xt_timer_int); + _xt_isr_unmask(BIT(INUM_TICK)); sdk__xt_tick_timer_init(); vTaskSwitchContext(); @@ -265,60 +267,3 @@ void IRAM vPortExitCritical( void ) portENABLE_INTERRUPTS(); } -/*-----------------------------------------------------------*/ - -/* Main ISR handler for FreeRTOS side of the ESP libs? - - As far as I can tell, the "real" Xtensa ISRs ("Exceptions") are - handled in libmain.a (xtensa_vectors.o) which then can call into here - passing an interrupt mask. -*/ - -_xt_isr isr[16]; - -void IRAM _xt_isr_attach(uint8_t i, _xt_isr func) -{ - isr[i] = func; -} - -uint16_t IRAM _xt_isr_handler(uint16_t i) -{ - uint8_t index; - - /* I think this is implementing some kind of interrupt priority or - short-circuiting an expensive ffs for most common interrupts - ie - WDT And GPIO are common or high priority, then remaining flags. - */ - if (i & (1 << INUM_WDT)) { - index = INUM_WDT; - } - else if (i & (1 << INUM_GPIO)) { - index = INUM_GPIO; - }else { - index = __builtin_ffs(i) - 1; - - if (index == INUM_MAX) { - /* I don't understand what happens here. INUM_MAX is not - the highest interrupt number listed (and the isr array - has 16 entries). - - Clearing that flag and then setting index to - __builtin_ffs(i)-1 may result in index == 255 if no - higher flags are set, unless this is guarded against - somehow by the caller? - - I also don't understand why the code is written like - this in esp_iot_rtos_sdk instead of just putting the i - &= line near the top... Probably no good reason? - */ - i &= ~(1 << INUM_MAX); - index = __builtin_ffs(i) - 1; - } - } - - _xt_clear_ints(1< -#include "xtensa_rtos.h" -#include "xtensa_interrupts.h" +#include "xtensa_rtos.h" +#include /*----------------------------------------------------------- * Port specific definitions for ESP8266 diff --git a/README.md b/README.md index 144ea33..fc2c70b 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,17 @@ The best way to write suitable code is to first add documentation somewhere like ## Coding Style -For new contributions, please use BSD style and indent using 4 spaces. If you're an emacs user then there is a .dir-locals.el file in the root which configures cc-mode. +For new contributions in C, please use BSD style and indent using 4 spaces. + +For assembly, please use the following: +* Instructions indented using 8 spaces. +* Inline comments use `#` as a comment delimiter. +* Comments on their own line(s) use `/*`..`*/`. +* First operand of each instruction should be vertically aligned where possible. +* For xtensa special registers, prefer `wsr aX, SR` over `wsr.SR aX` + +If you're an emacs user then there is a .dir-locals.el file in the root which configures cc-mode and asm-mode (you will need to approve some variable values as safe). See also +the additional comments in .dir-locals.el, if you're editing assembly code. Upstream code is left with the indentation and style of the upstream project. diff --git a/core/esp_interrupts.c b/core/esp_interrupts.c new file mode 100644 index 0000000..4fd68b7 --- /dev/null +++ b/core/esp_interrupts.c @@ -0,0 +1,39 @@ +/* ESP8266 Xtensa interrupt management functions + * + * + * Part of esp-open-rtos + * Copyright (C) 2015 Angus Gratton + * BSD Licensed as described in the file LICENSE + */ +#include + +_xt_isr isr[16]; + +void IRAM _xt_isr_attach(uint8_t i, _xt_isr func) +{ + isr[i] = func; +} + +/* Generic ISR handler. + + Handles all flags set for interrupts in 'intset'. +*/ +uint16_t IRAM _xt_isr_handler(uint16_t intset) +{ + /* WDT has highest priority (occasional WDT resets otherwise) */ + if(intset & BIT(INUM_WDT)) { + _xt_clear_ints(BIT(INUM_WDT)); + isr[INUM_WDT](); + intset -= BIT(INUM_WDT); + } + + while(intset) { + uint8_t index = __builtin_ffs(intset) - 1; + uint16_t mask = BIT(index); + _xt_clear_ints(mask); + isr[index](); + intset -= mask; + } + + return 0; +} diff --git a/core/exception_vectors.S b/core/exception_vectors.S index 5e7f20e..c1f1761 100644 --- a/core/exception_vectors.S +++ b/core/exception_vectors.S @@ -25,12 +25,19 @@ .section .bss -NMIHandlerStack: # stack space for NMI handler - .skip 4*0x100 -.LNMIHandlerStackTop: -NMIRegisterSaved: # register space for saving NMI registers - .skip 4*(16 + 6) +/* Stack space for NMI handler + NMI handler stack high water mark measured at 0x134 bytes. Any use + of the NMI timer callback will add stack overhead as well. + + The NMI handler does a basic check for stack overflow +*/ + .balign 16 +NMIHandlerStack: + .skip 0x200 +.NMIHandlerStackTop: + + .balign 16 LoadStoreErrorHandlerStack: .word 0 # a0 .word 0 # (unused) @@ -67,10 +74,7 @@ DebugExceptionVector: .org VecBase + 0x20 NMIExceptionVector: .type NMIExceptionVector, @function - - wsr a0, excsave3 - call0 CallNMIExceptionHandler - rfi 3 # Should never be reached + j NMIExceptionHandler .org VecBase + 0x30 KernelExceptionVector: @@ -371,96 +375,103 @@ call_user_start: /*************************** NMI Exception Handler ***************************/ +#define NMI_STACK_CANARY 0xABBABABA + .section .vecbase.text, "x" -/* Save register relative to a0 */ -.macro SAVE_REG register, regnum - s32i \register, a0, (4 * (\regnum + 6)) -.endm - -/* Load register relative to sp */ -.macro LOAD_REG register, regnum - l32i \register, sp, (4 * (\regnum + 6)) -.endm - .literal_position - .balign 16 -CallNMIExceptionHandler: - .type CallNMIExceptionHandler, @function +NMIExceptionHandler: + .type NMIExceptionHandler, @function - movi a0, NMIRegisterSaved - SAVE_REG a2, 2 - SAVE_REG sp, 1 - SAVE_REG a3, 3 - rsr a2, excsave3 # a2 is now former a0 - SAVE_REG a4, 4 - SAVE_REG a2, 0 - rsr a3, epc1 - rsr a4, exccause - SAVE_REG a3, -5 - SAVE_REG a4, -4 - rsr a3, excvaddr - SAVE_REG a3, -3 - rsr a3, excsave1 - SAVE_REG a3, -2 - SAVE_REG a5, 5 - SAVE_REG a6, 6 - SAVE_REG a7, 7 - SAVE_REG a8, 8 - SAVE_REG a9, 9 - SAVE_REG a10, 10 - SAVE_REG a11, 11 - SAVE_REG a12, 12 - SAVE_REG a13, 13 - SAVE_REG a14, 14 - SAVE_REG a15, 15 - movi sp, .LNMIHandlerStackTop - movi a0, 0 - movi a2, 0x23 # argument for handler - wsr a2, ps + wsr sp, excsave3 # excsave3 holds user stack + movi sp, .NMIHandlerStackTop - 0x40 + s32i a0, sp, 0x00 + s32i a2, sp, 0x04 + s32i a3, sp, 0x08 + s32i a4, sp, 0x0c + s32i a5, sp, 0x10 + s32i a6, sp, 0x14 + s32i a7, sp, 0x18 + s32i a8, sp, 0x1c + s32i a9, sp, 0x20 + s32i a10, sp, 0x24 + s32i a11, sp, 0x28 + rsr a0, epc1 + s32i a0, sp, 0x2c + rsr a0, exccause + s32i a0, sp, 0x30 + rsr a0, excsave1 + s32i a0, sp, 0x34 + rsr a0, excvaddr + s32i a0, sp, 0x38 + rsr a0, sar + s32i a0, sp, 0x3c + movi a0, 0x23 # Override PS for NMI handler + wsr a0, ps rsync - rsr a14, sar - s32i a14, sp, 0 # this is also NMIRegisterSaved+0 + + /* mark the stack overflow point before we call the actual NMI handler */ + movi a0, NMIHandlerStack + movi a2, NMI_STACK_CANARY + s32i a2, a0, 0x00 + call0 sdk_wDev_ProcessFiq - l32i a15, sp, 0 - wsr a15, sar - movi a2, 0x33 - wsr a2, ps + + /* verify we didn't overflow */ + movi a0, NMIHandlerStack + l32i a3, a0, 0 + movi a2, NMI_STACK_CANARY + bne a3, a2, .NMIFatalStackOverflow + + l32i a0, sp, 0x3c + wsr a0, sar + l32i a0, sp, 0x38 + wsr a0, excvaddr + l32i a0, sp, 0x34 + wsr a0, excsave1 + l32i a0, sp, 0x30 + wsr a0, exccause + l32i a0, sp, 0x2c + wsr a0, epc1 + l32i a11, sp, 0x28 + l32i a10, sp, 0x24 + l32i a9, sp, 0x20 + l32i a8, sp, 0x1c + l32i a7, sp, 0x18 + l32i a6, sp, 0x14 + l32i a5, sp, 0x10 + l32i a4, sp, 0x0c + l32i a3, sp, 0x08 + movi a0, 0x33 # Reset PS + wsr a0, ps rsync - LOAD_REG a4, 4 - LOAD_REG a5, 5 - LOAD_REG a6, 6 - LOAD_REG a7, 7 - LOAD_REG a8, 8 - LOAD_REG a9, 9 - LOAD_REG a10, 10 - LOAD_REG a11, 11 - LOAD_REG a12, 12 - LOAD_REG a13, 13 - LOAD_REG a14, 14 - LOAD_REG a15, 15 - LOAD_REG a2, -5 - LOAD_REG a3, -4 - wsr a2, epc1 - wsr a3, exccause - LOAD_REG a2, -3 - LOAD_REG a3, -2 - wsr a2, excvaddr - wsr a3, excsave1 - LOAD_REG a0, 0 - /* set dport nmi status bit 0 (wDev_ProcessFiq clears & verifies this - * bit stays cleared, see + /* set dport nmi status to 1 (wDev_ProcessFiq clears bit 0 and verifies it + * stays cleared, see * http://esp8266-re.foogod.com/wiki/WDev_ProcessFiq_%28IoT_RTOS_SDK_0.9.9%29) */ - movi a2, 0x3ff00000 - movi a3, 0x1 - s32i a3, a2, 0 - LOAD_REG a2, 2 - LOAD_REG a3, 3 - LOAD_REG a1, 1 + movi a0, 0x3ff00000 + movi a2, 0x1 + s32i a2, a0, 0 + l32i a2, sp, 0x04 + l32i a0, sp, 0x00 + movi a1, 0x0 + xsr a1, excsave3 # Load stack back from excsave3, clear excsave3 rfi 3 + .section .rodata + +.NMIStackOverflowErrorMsg: + .string "\nFATAL: NMI Stack Overflow\n" + + .section .vecbase.text, "x" + +.NMIFatalStackOverflow: + movi a2, .NMIStackOverflowErrorMsg + call0 printf +.NMIInfiniteLoop: + j .NMIInfiniteLoop /* TODO: replace with call to abort() */ + /*********************** General UserException Handler ***********************/ .section .vecbase.text, "x" @@ -469,7 +480,6 @@ CallNMIExceptionHandler: * LoadStoreCause. */ .literal_position - .balign 4 UserExceptionHandler: .type UserExceptionHandler, @function @@ -490,46 +500,28 @@ UserExceptionHandler: wsr a0, ps rsync rsr a2, exccause - beqi a2, CAUSE_LVL1INT, UserHandleInterrupt - /* Any UserException cause other than level 1 interrupt should panic */ -UserFailOtherExceptionCause: - break 1, 1 - call0 sdk_user_fatal_exception_handler -UserHandleInterrupt: + /* Any UserException cause other than a level 1 interrupt is fatal */ + bnei a2, CAUSE_LVL1INT, .LUserFailOtherExceptionCause +.LUserHandleInterrupt: rsil a0, 1 rsr a2, intenable rsr a3, interrupt movi a4, 0x3fff and a2, a2, a3 - and a2, a2, a4 # a2 = 0x3FFF & INTENABLE & INTERRUPT -UserHandleTimer: - movi a3, 0xffbf - and a3, a2, a3 # a3 = a2 with bit 6 cleared - bnez a3, UserTimerDone # If any non-timer interrupt bits set - movi a3, 0x40 - sub a12, a2, a3 # a12 = a2 - 0x40 -- Will be zero if bit 6 set - call0 sdk__xt_timer_int # tick timer interrupt - mov a2, a12 # restore a2 from a12, ie zero - beqz a2, UserIntDone -UserTimerDone: + and a2, a2, a4 # a2 = 0x3FFF & INTENABLE & INTERRUPT call0 _xt_isr_handler - bnez a2, UserHandleTimer -UserIntDone: - beqz a2, UserIntExit - /* FIXME: this code will never be reached */ + call0 sdk__xt_int_exit # once finished, jumps to _xt_user_exit via stack + + .literal_position +.LUserFailOtherExceptionCause: break 1, 1 call0 sdk_user_fatal_exception_handler -UserIntExit: - call0 sdk__xt_int_exit # jumps to _xt_user_exit. Never returns here - .section .text - -/* _xt_user_exit is used to exit interrupt context. */ -/* TODO: Find a better place for this to live. */ +/* _xt_user_exit is pushed onto the stack as part of the user exception handler, + restores same set registers which were saved there and returns from exception */ _xt_user_exit: .global _xt_user_exit .type _xt_user_exit, @function - l32i a0, sp, 0x8 wsr a0, ps l32i a0, sp, 0x4 @@ -538,4 +530,3 @@ _xt_user_exit: l32i sp, sp, 0x10 rsync rfe - diff --git a/core/include/esp/cpu.h b/core/include/esp/cpu.h deleted file mode 100644 index 923e142..0000000 --- a/core/include/esp/cpu.h +++ /dev/null @@ -1,33 +0,0 @@ -/* esp/cpu.h - * - * Details relating to the ESP8266 Xtensa core. - * - */ -#ifndef _ESP_CPU_H -#define _ESP_CPU_H -#include - -/* Interrupt numbers for level 1 exception handler. - * - * Currently the UserExceptionVector calls down to _xt_isr_handler, - * defined in port.c, for at least some of these interrupts. Some are handled - * on the SDK side, though. - */ -typedef enum { - INUM_SPI = 2, - INUM_GPIO = 4, - INUM_UART = 5, - INUM_MAX = 6, /* in some places this is documented as timer0 CCOMPARE0 interrupt */ - INUM_SOFT = 7, - INUM_WDT = 8, - INUM_TIMER_FRC1 = 9, - - /* FRC2 default handler. Configured by sdk_ets_timer_init, which - runs as part of default libmain.a startup code, assigns - interrupt handler to sdk_vApplicationTickHook+0x68 - */ - INUM_TIMER_FRC2 = 10, -} xt_isr_num_t; - -#endif - diff --git a/core/include/esp/gpio.h b/core/include/esp/gpio.h index 4f431f2..da7bcfa 100644 --- a/core/include/esp/gpio.h +++ b/core/include/esp/gpio.h @@ -11,8 +11,7 @@ #include #include "esp/gpio_regs.h" #include "esp/iomux.h" -#include "esp/cpu.h" -#include "xtensa_interrupts.h" +#include "esp/interrupts.h" typedef enum { GPIO_INPUT, diff --git a/include/xtensa_interrupts.h b/core/include/esp/interrupts.h similarity index 79% rename from include/xtensa_interrupts.h rename to core/include/esp/interrupts.h index d06e2b9..18d6303 100644 --- a/include/xtensa_interrupts.h +++ b/core/include/esp/interrupts.h @@ -1,4 +1,4 @@ -/* Xtensa interrupt management functions +/* ESP8266 Xtensa interrupt management functions * * Some (w/ sdk_ prefix) are implemented in binary libs, rest are * inlines replacing functions in the binary libraries. @@ -15,9 +15,27 @@ #include #include +/* Interrupt numbers for level 1 exception handler. */ +typedef enum { + INUM_SPI = 2, + INUM_GPIO = 4, + INUM_UART = 5, + INUM_TICK = 6, /* RTOS timer tick, possibly xtensa CPU CCOMPARE0(?) */ + INUM_SOFT = 7, + INUM_WDT = 8, + INUM_TIMER_FRC1 = 9, + + /* FRC2 default handler. Configured by sdk_ets_timer_init, which + runs as part of default libmain.a startup code, assigns + interrupt handler to sdk_vApplicationTickHook+0x68 + */ + INUM_TIMER_FRC2 = 10, +} xt_isr_num_t; + void sdk__xt_int_exit (void); void _xt_user_exit (void); 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) diff --git a/core/include/esp/timer.h b/core/include/esp/timer.h index 0fb27ef..49559e4 100644 --- a/core/include/esp/timer.h +++ b/core/include/esp/timer.h @@ -10,9 +10,8 @@ #define _ESP_TIMER_H #include -#include #include "esp/timer_regs.h" -#include "esp/cpu.h" +#include "esp/interrupts.h" #ifdef __cplusplus extern "C" { diff --git a/core/include/esp8266.h b/core/include/esp8266.h index 968a522..d30ec0e 100644 --- a/core/include/esp8266.h +++ b/core/include/esp8266.h @@ -13,7 +13,7 @@ #include "common_macros.h" #include "esp/registers.h" -#include "esp/cpu.h" +#include "esp/interrupts.h" #include "esp/iomux.h" #include "esp/gpio.h" #include "esp/timer.h"