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/app_main.c b/core/app_main.c index 88ac743..a710c77 100644 --- a/core/app_main.c +++ b/core/app_main.c @@ -16,8 +16,8 @@ #include "common_macros.h" #include "xtensa_ops.h" #include "esp/rom.h" +#include "esp/uart.h" #include "esp/iomux_regs.h" -#include "esp/uart_regs.h" #include "esp/spi_regs.h" #include "esp/dport_regs.h" #include "esp/wdev_regs.h" @@ -30,7 +30,6 @@ void user_init(void); -#define UART_DIVISOR 1068 // 74906 bps (yeah, I don't understand it either) #define BOOT_INFO_SIZE 28 //TODO: phy_info should probably be a struct (no idea about its organization, though) #define PHY_INFO_SIZE 128 @@ -82,8 +81,6 @@ xTaskHandle sdk_xWatchDogTaskHandle; static void IRAM get_otp_mac_address(uint8_t *buf); static void IRAM set_spi0_divisor(uint32_t divisor); -static int IRAM default_putc(char c); -static void IRAM default_putc1(char c); static void zero_bss(void); static void init_networking(uint8_t *phy_info, uint8_t *mac_addr); static void init_g_ic(void); @@ -160,27 +157,15 @@ void IRAM sdk_user_fatal_exception_handler(void) { Cache_Read_Disable(); Cache_Read_Enable(0, 0, 1); dump_excinfo(); - while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(0).STATUS)) {} - while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(1).STATUS)) {} + uart_flush_txfifo(0); + uart_flush_txfifo(1); sdk_system_restart_in_nmi(); halt(); } -// .Lfunc003 -- .text+0x1d0 -static int IRAM default_putc(char c) { - while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(0).STATUS) > 125) {} - UART(0).FIFO = c; - return 0; -} -// .Lfunc004 -- .text+0x1f4 -static void IRAM default_putc1(char c) { - if (c == '\n') { - default_putc('\r'); - } else if (c == '\r') { - return; - } - default_putc(c); +static void IRAM default_putc(char c) { + uart_putc(0, c); } // .text+0x258 @@ -247,7 +232,7 @@ void IRAM sdk_user_start(void) { sdk_SPIRead(ic_flash_addr, buf32, sizeof(struct sdk_g_ic_saved_st)); Cache_Read_Enable(0, 0, 1); zero_bss(); - sdk_os_install_putc1(default_putc1); + sdk_os_install_putc1(default_putc); if (cksum_magic == 0xffffffff) { // No checksum required } else if ((cksum_magic == 0x55aa55aa) && @@ -293,8 +278,8 @@ static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) { printf("FATAL: sdk_register_chipv6_phy failed"); halt(); } - sdk_uart_div_modify(0, UART_DIVISOR); - sdk_uart_div_modify(1, UART_DIVISOR); + uart_set_baud(0, 74906); + uart_set_baud(1, 74906); sdk_phy_disable_agc(); sdk_ieee80211_phy_init(sdk_g_ic.s.phy_mode); sdk_lmacInit(); @@ -378,15 +363,6 @@ static void dump_excinfo(void) { sdk_system_rtc_mem_write(0, excinfo, 32); } -// .irom0.text+0x368 -int sdk_uart_rx_one_char(char *buf) { - if (FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(0).STATUS)) { - *buf = UART(0).FIFO; - return 0; - } - return 1; -} - // .irom0.text+0x398 void sdk_wdt_init(void) { WDT.CTRL &= ~WDT_CTRL_ENABLE; @@ -455,9 +431,11 @@ static void user_start_phase2(void) { phy_info = malloc(PHY_INFO_SIZE); sdk_spi_flash_read(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)phy_info, PHY_INFO_SIZE); + // Disable default buffering on stdout + setbuf(stdout, NULL); // Wait for UARTs to finish sending anything in their queues. - while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(0).STATUS) > 0) {} - while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(1).STATUS) > 0) {} + uart_flush_txfifo(0); + uart_flush_txfifo(1); if (phy_info[0] != 5) { // Bad version byte. Discard what we read and use default values 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/esp/uart.h b/core/include/esp/uart.h new file mode 100644 index 0000000..9d72dc3 --- /dev/null +++ b/core/include/esp/uart.h @@ -0,0 +1,127 @@ +/** esp/uart.h + * + * Utility routines for working with UARTs + * + * Part of esp-open-rtos + * Copyright (C) 2015 Superhouse Automation Pty Ltd + * BSD Licensed as described in the file LICENSE + */ +#ifndef _ESP_UART_H +#define _ESP_UART_H + +#include "esp/types.h" +#include "esp/uart_regs.h" +#include "esp/clocks.h" + +#define UART_FIFO_MAX 127 + +/* Wait for at least `min_count` bytes of data to be available in the UART's + * receive FIFO + * + * Returns the number of bytes actually available for reading. + */ +static inline int uart_rxfifo_wait(int uart_num, int min_count) { + int count; + do { + count = FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(uart_num).STATUS); + } while (count < min_count); + return count; +} + +/* Wait for at least `min_count` bytes of space to be available in the UART's + * transmit FIFO + * + * Returns the number of bytes actually available in the write buffer. + */ +static inline int uart_txfifo_wait(int uart_num, int min_count) { + int count; + do { + count = UART_FIFO_MAX - FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(uart_num).STATUS); + } while (count < min_count); + return count; +} + +/* Read a character from the UART. Block until a character is available for + * reading. + * + * Returns the character read. + */ +static inline int uart_getc(int uart_num) { + uart_rxfifo_wait(uart_num, 1); + return UART(uart_num).FIFO; +} + +/* Read a character from the UART. Does not block. + * + * Returns the read character on success. If the RX FIFO is currently empty + * (nothing to read), returns -1. + */ +static inline int uart_getc_nowait(int uart_num) { + if (FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(uart_num).STATUS)) { + return UART(uart_num).FIFO; + } + return -1; +} + +/* Write a character to the UART. Blocks if necessary until there is space in + * the TX FIFO. + */ +static inline void uart_putc(int uart_num, char c) { + uart_txfifo_wait(uart_num, 1); + UART(uart_num).FIFO = c; +} + +/* Write a character to the UART. Does not block. + * + * Returns 0 on success. If there is no space currently in the TX FIFO, + * returns -1. + */ +static inline int uart_putc_nowait(int uart_num, char c) { + if (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(uart_num).STATUS) < UART_FIFO_MAX) { + UART(uart_num).FIFO = c; + return 0; + } + return -1; +} + +/* Clear (discard) all pending write data in the TX FIFO */ +static inline void uart_clear_txfifo(int uart_num) { + uint32_t conf = UART(uart_num).CONF0; + UART(uart_num).CONF0 = conf | UART_CONF0_TXFIFO_RESET; + UART(uart_num).CONF0 = conf & ~UART_CONF0_TXFIFO_RESET; +} + +/* Clear (discard) all pending read data in the RX FIFO */ +static inline void uart_clear_rxfifo(int uart_num) { + uint32_t conf = UART(uart_num).CONF0; + UART(uart_num).CONF0 = conf | UART_CONF0_RXFIFO_RESET; + UART(uart_num).CONF0 = conf & ~UART_CONF0_RXFIFO_RESET; +} + +/* Wait until all pending output in the UART's TX FIFO has been sent out the + * serial port. */ +static inline void uart_flush_txfifo(int uart_num) { + while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(uart_num).STATUS) != 0) {} +} + +/* Flush all pending input in the UART's RX FIFO + * (this is just another name for uart_clear_rxfifo) + */ +static inline void uart_flush_rxfifo(int uart_num) { + uart_clear_rxfifo(uart_num); +} + +/* Set uart baud rate to the desired value */ +static inline void uart_set_baud(int uart_num, int bps) +{ + uint32_t divider = APB_CLK_FREQ / bps; + UART(uart_num).CLOCK_DIVIDER = divider; +} + +/* Returns the current baud rate for the UART */ +static inline int uart_get_baud(int uart_num) +{ + return APB_CLK_FREQ / FIELD2VAL(UART_CLOCK_DIVIDER_VALUE, UART(uart_num).CLOCK_DIVIDER); +} + +#endif /* _ESP_UART_H */ 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" diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c index cce4f49..ae15aff 100644 --- a/core/newlib_syscalls.c +++ b/core/newlib_syscalls.c @@ -9,6 +9,7 @@ #include #include #include +#include #include IRAM caddr_t _sbrk_r (struct _reent *r, int incr) @@ -32,38 +33,40 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr) return (caddr_t) prev_heap_end; } -/* syscall implementation for stdio write to UART - - at the moment UART functionality is all still in the binary SDK - */ +/* syscall implementation for stdio write to UART */ long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { r->_errno = EBADF; return -1; } - for(int i = 0; i < len; i++) - sdk_os_putc(ptr[i]); + for(int i = 0; i < len; i++) { + /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ + if(ptr[i] == '\r') + continue; + if(ptr[i] == '\n') + uart_putc(0, '\r'); + uart_putc(0, ptr[i]); + } return len; } -/* syscall implementation for stdio read from UART - - at the moment UART functionality is all still in the binary SDK - but there is support for IRQ driven UART0 RX in extras/serial-driver - */ -long __attribute__((weak)) _read_r( struct _reent *r, int fd, char *ptr, int len ) +/* syscall implementation for stdio read from UART */ +long _read_r( struct _reent *r, int fd, char *ptr, int len ) { + int ch, i; + if(fd != r->_stdin->_file) { r->_errno = EBADF; return -1; } - for(int i = 0; i < len; i++) { - char ch; - while (sdk_uart_rx_one_char(&ch)) ; + uart_rxfifo_wait(0, 1); + for(i = 0; i < len; i++) { + ch = uart_getc_nowait(0); + if (ch < 0) break; ptr[i] = ch; } - return len; + return i; } /* Stub syscall implementations follow, to allow compiling newlib functions that diff --git a/core/sdk_compat.c b/core/sdk_compat.c index b6bb6b3..b0d6789 100644 --- a/core/sdk_compat.c +++ b/core/sdk_compat.c @@ -1,5 +1,5 @@ /* - * Wrappers for functions called by binary espressif libraries + * Compatibility routines for the Espressif SDK and its API * * Part of esp-open-rtos * Copyright (C) 2015 Superhouse Automation Pty Ltd @@ -10,6 +10,8 @@ #include #include +#include + void IRAM *zalloc(size_t nbytes) { return calloc(1, nbytes); @@ -18,9 +20,8 @@ void IRAM *zalloc(size_t nbytes) extern void (*__init_array_start)(void); extern void (*__init_array_end)(void); -/* Do things which should be done as part of the startup code, but aren't. - - Can be replaced with _start() once we have open source startup code. +/* Do things which should be done as part of the SDK startup code, but aren't. + TODO: Move into app_main.c */ void sdk_compat_initialise() { @@ -29,3 +30,15 @@ void sdk_compat_initialise() for ( p = &__init_array_start; p != &__init_array_end; ++p) (*p)(); } + +/* UART RX function from Espressif SDK internals. + * + * Not part of published API. + */ +int sdk_uart_rx_one_char(char *buf) { + int c = uart_getc_nowait(0); + if(c < 0) + return 1; + *buf = (char)c; + return 0; +} diff --git a/examples/blink/blink.c b/examples/blink/blink.c index 52d69a5..00388b5 100644 --- a/examples/blink/blink.c +++ b/examples/blink/blink.c @@ -3,7 +3,7 @@ * This sample code is in the public domain. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "esp8266.h" @@ -53,7 +53,7 @@ void blinkenRegisterTask(void *pvParameters) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); xTaskCreate(blinkenTask, (signed char *)"blinkenTask", 256, NULL, 2, NULL); //xTaskCreate(blinkenRegisterTask, (signed char *)"blinkenRegisterTask", 256, NULL, 2, NULL); } diff --git a/examples/blink_timers/blink_timers.c b/examples/blink_timers/blink_timers.c index 5b6b7a2..177cff3 100644 --- a/examples/blink_timers/blink_timers.c +++ b/examples/blink_timers/blink_timers.c @@ -3,7 +3,7 @@ * This sample code is in the public domain. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "esp8266.h" @@ -33,7 +33,7 @@ void frc2_interrupt_handler(void) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); /* configure GPIOs */ gpio_enable(gpio_frc1, GPIO_OUTPUT); diff --git a/examples/bmp180_i2c/bmp180_i2c.c b/examples/bmp180_i2c/bmp180_i2c.c index 578db67..64ee961 100644 --- a/examples/bmp180_i2c/bmp180_i2c.c +++ b/examples/bmp180_i2c/bmp180_i2c.c @@ -3,7 +3,7 @@ * This sample code is in the public domain. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" @@ -87,7 +87,7 @@ void bmp180_task(void *pvParameters) void user_setup(void) { // Set UART Parameter - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); // Give the UART some time to settle sdk_os_delay_us(500); diff --git a/examples/button/button.c b/examples/button/button.c index e197ffc..0b0784d 100644 --- a/examples/button/button.c +++ b/examples/button/button.c @@ -6,7 +6,7 @@ * This sample code is in the public domain. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" @@ -75,7 +75,7 @@ void GPIO_HANDLER(void) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); gpio_enable(gpio, GPIO_INPUT); tsqueue = xQueueCreate(2, sizeof(uint32_t)); diff --git a/examples/cpp_01_tasks/cpp_tasks.cpp b/examples/cpp_01_tasks/cpp_tasks.cpp index 9e381b9..1a54129 100644 --- a/examples/cpp_01_tasks/cpp_tasks.cpp +++ b/examples/cpp_01_tasks/cpp_tasks.cpp @@ -31,6 +31,8 @@ #include "espressif/esp_common.h" +#include "esp/uart.h" + /****************************************************************************************************************** * task_1_t * @@ -94,8 +96,8 @@ esp_open_rtos::thread::queue_t MyQueue; */ extern "C" void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); - + uart_set_baud(0, 115200); + MyQueue.queue_create(10); task_1.queue = MyQueue; diff --git a/examples/experiments/timers/timers.c b/examples/experiments/timers/timers.c index 2a11bec..d5a2f50 100644 --- a/examples/experiments/timers/timers.c +++ b/examples/experiments/timers/timers.c @@ -7,7 +7,7 @@ * This experimental reverse engineering code is in the public domain. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "esp8266.h" @@ -118,7 +118,7 @@ void frc2_handler(void) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); xTaskCreate(timerRegTask, (signed char *)"timerRegTask", 1024, NULL, 2, NULL); TIMER(0).CTRL = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_256) | TIMER_CTRL_RELOAD; diff --git a/examples/experiments/unaligned_load/unaligned_load.c b/examples/experiments/unaligned_load/unaligned_load.c index 1574172..48016af 100644 --- a/examples/experiments/unaligned_load/unaligned_load.c +++ b/examples/experiments/unaligned_load/unaligned_load.c @@ -3,7 +3,7 @@ #include "esp/rom.h" #include "esp/timer.h" #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" @@ -214,7 +214,7 @@ void sanity_tests(void); void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); gpio_enable(2, GPIO_OUTPUT); /* used for LED debug */ gpio_write(2, 1); /* active low */ diff --git a/examples/http_get/http_get.c b/examples/http_get/http_get.c index 408b462..b90abdf 100644 --- a/examples/http_get/http_get.c +++ b/examples/http_get/http_get.c @@ -5,7 +5,7 @@ * This sample code is in the public domain., */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include @@ -114,7 +114,7 @@ void http_get_task(void *pvParameters) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); printf("SDK version:%s\n", sdk_system_get_sdk_version()); struct sdk_station_config config = { diff --git a/examples/http_get_mbedtls/http_get_mbedtls.c b/examples/http_get_mbedtls/http_get_mbedtls.c index d1fbf0d..f45fd54 100644 --- a/examples/http_get_mbedtls/http_get_mbedtls.c +++ b/examples/http_get_mbedtls/http_get_mbedtls.c @@ -10,7 +10,7 @@ * Additions Copyright (C) 2015 Angus Gratton, Apache 2.0 License. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include @@ -80,7 +80,6 @@ static void my_debug(void *ctx, int level, file = file_sep+1; printf("%s:%04d: %s", file, line, str); - fflush(stdout); } #endif @@ -107,7 +106,6 @@ void http_get_task(void *pvParameters) mbedtls_x509_crt_init(&cacert); mbedtls_ctr_drbg_init(&ctr_drbg); printf("\n . Seeding the random number generator..."); - fflush(stdout); mbedtls_ssl_config_init(&conf); @@ -126,7 +124,6 @@ void http_get_task(void *pvParameters) * 0. Initialize certificates */ printf(" . Loading the CA root certificate ..."); - fflush(stdout); ret = mbedtls_x509_crt_parse(&cacert, (uint8_t*)server_root_cert, strlen(server_root_cert)+1); if(ret < 0) @@ -148,7 +145,6 @@ void http_get_task(void *pvParameters) * 2. Setup stuff */ printf(" . Setting up the SSL/TLS structure..."); - fflush(stdout); if((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, @@ -182,7 +178,6 @@ void http_get_task(void *pvParameters) our network is probably working... */ printf("Waiting for server DNS to resolve... "); - fflush(stdout); err_t dns_err; ip_addr_t host_ip; do { @@ -198,7 +193,6 @@ void http_get_task(void *pvParameters) * 1. Start the connection */ printf(" . Connecting to %s:%s...", WEB_SERVER, WEB_PORT); - fflush(stdout); if((ret = mbedtls_net_connect(&server_fd, WEB_SERVER, WEB_PORT, MBEDTLS_NET_PROTO_TCP)) != 0) @@ -215,7 +209,6 @@ void http_get_task(void *pvParameters) * 4. Handshake */ printf(" . Performing the SSL/TLS handshake..."); - fflush(stdout); while((ret = mbedtls_ssl_handshake(&ssl)) != 0) { @@ -251,7 +244,6 @@ void http_get_task(void *pvParameters) * 3. Write the GET request */ printf(" > Write to server:"); - fflush(stdout); int len = sprintf((char *) buf, GET_REQUEST); @@ -271,7 +263,6 @@ void http_get_task(void *pvParameters) * 7. Read the HTTP response */ printf(" < Read from server:"); - fflush(stdout); do { @@ -322,7 +313,6 @@ void http_get_task(void *pvParameters) printf("\n\nsuccesses = %d failures = %d\n", successes, failures); for(int countdown = successes ? 10 : 5; countdown >= 0; countdown--) { printf("%d... ", countdown); - fflush(stdout); vTaskDelay(1000 / portTICK_RATE_MS); } printf("\nStarting again!\n"); @@ -331,7 +321,7 @@ void http_get_task(void *pvParameters) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); printf("SDK version:%s\n", sdk_system_get_sdk_version()); struct sdk_station_config config = { diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c index 69751d0..a6d354d 100644 --- a/examples/ota_basic/ota_basic.c +++ b/examples/ota_basic/ota_basic.c @@ -7,7 +7,7 @@ * NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!! */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "esp8266.h" @@ -18,7 +18,7 @@ void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); rboot_config_t conf = rboot_get_config(); printf("\r\n\r\nOTA Basic demo.\r\nCurrently running on flash slot %d / %d.\r\n\r\n", diff --git a/examples/serial_echo/Makefile b/examples/serial_echo/Makefile new file mode 100644 index 0000000..2b34c0e --- /dev/null +++ b/examples/serial_echo/Makefile @@ -0,0 +1,3 @@ +# Simple makefile for serial_echo example +PROGRAM=serial_echo +include ../../common.mk diff --git a/examples/serial_echo/serial_echo.c b/examples/serial_echo/serial_echo.c new file mode 100644 index 0000000..5be9ff4 --- /dev/null +++ b/examples/serial_echo/serial_echo.c @@ -0,0 +1,42 @@ +/* Extremely simple example that just reads from stdin and echoes back on stdout + * + * Has an easter egg, which is if you type "QUACK" it'll quack 3 times back at you. + * + * This example code is in the public domain + */ +#include "espressif/esp_common.h" +#include +#include + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + printf("Going into echo mode...\n"); + + /* By default stdout is line-buffered, so you only see output + after a newline is sent. This is helpful in a multithreaded + environment so output doesn't get chopped up within a line. + + Here we want to see the echo immediately, so disable buffering. + */ + setbuf(stdout, NULL); + + while(1) { + int c = getchar(); + if(c != EOF) + putchar(c); + + /* Easter egg: check for a quack! */ + static int quack; + if(c == "QUACK"[quack]) { + quack++; + if(quack == strlen("QUACK")) { + printf("\nQUACK\nQUACK\n"); + quack = 0; + } + } else { + quack = 0; + } + } +} diff --git a/examples/simple/simple.c b/examples/simple/simple.c index 98e4ada..d95a040 100644 --- a/examples/simple/simple.c +++ b/examples/simple/simple.c @@ -1,7 +1,7 @@ /* Very basic example that just demonstrates we can run at all! */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" @@ -36,7 +36,7 @@ static xQueueHandle mainqueue; void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); printf("SDK version:%s\n", sdk_system_get_sdk_version()); mainqueue = xQueueCreate(10, sizeof(uint32_t)); xTaskCreate(task1, (signed char *)"tsk1", 256, &mainqueue, 2, NULL); diff --git a/examples/simple_cplusplus/simple.cpp b/examples/simple_cplusplus/simple.cpp index 1826499..dec286d 100644 --- a/examples/simple_cplusplus/simple.cpp +++ b/examples/simple_cplusplus/simple.cpp @@ -3,11 +3,12 @@ This sample code is in the public domain. */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" +#include + class Counter { private: @@ -58,7 +59,7 @@ void task1(void *pvParameters) extern "C" void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); printf("SDK version:%s\n", sdk_system_get_sdk_version()); xTaskCreate(task1, (signed char *)"tsk1", 256, NULL, 2, NULL); } diff --git a/examples/tests/hmac_test_vectors/hmac_test_vectors.c b/examples/tests/hmac_test_vectors/hmac_test_vectors.c index a6a20f9..d0ed1de 100644 --- a/examples/tests/hmac_test_vectors/hmac_test_vectors.c +++ b/examples/tests/hmac_test_vectors/hmac_test_vectors.c @@ -8,7 +8,7 @@ * This sample code is in the public domain., */ #include "espressif/esp_common.h" -#include "espressif/sdk_private.h" +#include "esp/uart.h" #include "FreeRTOS.h" #include "mbedtls/md.h" @@ -130,7 +130,7 @@ static void test_md5(void) void user_init(void) { - sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); + uart_set_baud(0, 115200); test_md5(); diff --git a/libc/xtensa-lx106-elf/include/newlib.h b/libc/xtensa-lx106-elf/include/newlib.h index 7ea84d4..e9bf566 100644 --- a/libc/xtensa-lx106-elf/include/newlib.h +++ b/libc/xtensa-lx106-elf/include/newlib.h @@ -31,7 +31,7 @@ /* Optional reentrant struct support. Used mostly on platforms with very restricted storage. */ -/* #undef _WANT_REENT_SMALL */ +#define _WANT_REENT_SMALL 1 /* Multibyte supported */ /* #undef _MB_CAPABLE */ diff --git a/libc/xtensa-lx106-elf/include/sys/config.h b/libc/xtensa-lx106-elf/include/sys/config.h index ee9d3b7..f6f923e 100644 --- a/libc/xtensa-lx106-elf/include/sys/config.h +++ b/libc/xtensa-lx106-elf/include/sys/config.h @@ -188,6 +188,8 @@ #ifdef __XTENSA__ #include #define MALLOC_ALIGNMENT ((XCHAL_DATA_WIDTH) < 16 ? 16 : (XCHAL_DATA_WIDTH)) +/* esp8266-specific: shrink the default fd buffer size */ +#define __BUFSIZ__ 128 #endif /* This block should be kept in sync with GCC's limits.h. The point diff --git a/libc/xtensa-lx106-elf/lib/libc.a b/libc/xtensa-lx106-elf/lib/libc.a index 33cfced..a166b9e 100644 Binary files a/libc/xtensa-lx106-elf/lib/libc.a and b/libc/xtensa-lx106-elf/lib/libc.a differ diff --git a/libc/xtensa-lx106-elf/lib/libg.a b/libc/xtensa-lx106-elf/lib/libg.a index 33cfced..a166b9e 100644 Binary files a/libc/xtensa-lx106-elf/lib/libg.a and b/libc/xtensa-lx106-elf/lib/libg.a differ diff --git a/libc/xtensa-lx106-elf/lib/libm.a b/libc/xtensa-lx106-elf/lib/libm.a index 7a2455b..be41436 100644 Binary files a/libc/xtensa-lx106-elf/lib/libm.a and b/libc/xtensa-lx106-elf/lib/libm.a differ