From 073299b3f4a171953126bc6b4bb5e5f9ec01f215 Mon Sep 17 00:00:00 2001 From: Kyle Fleming Date: Mon, 12 Mar 2018 22:20:34 -0700 Subject: [PATCH 01/11] Add full gdb support with uart/Serial integration --- cores/esp8266/gdb_hooks.c | 14 +- cores/esp8266/gdb_hooks.h | 65 + cores/esp8266/uart.c | 208 ++-- libraries/GDBStub/src/internal/gdbstub-cfg.h | 36 +- .../GDBStub/src/internal/gdbstub-entry.S | 154 ++- libraries/GDBStub/src/internal/gdbstub.c | 1073 +++++++++-------- libraries/GDBStub/src/internal/gdbstub.h | 26 + 7 files changed, 954 insertions(+), 622 deletions(-) diff --git a/cores/esp8266/gdb_hooks.c b/cores/esp8266/gdb_hooks.c index 9d5eb3c3ab..26aa6b0dfb 100644 --- a/cores/esp8266/gdb_hooks.c +++ b/cores/esp8266/gdb_hooks.c @@ -25,12 +25,18 @@ value is in register, it doesn't hurt to return a bool, so that the same stub can be used for gdb_present. */ -bool ICACHE_RAM_ATTR __gdb_no_op() +static bool ICACHE_RAM_ATTR __gdb_no_op() { return false; } -extern void gdb_init(void) __attribute__ ((weak, alias("__gdb_no_op"))); -extern void gdb_do_break(void) __attribute__ ((weak, alias("__gdb_no_op"))); -extern bool gdb_present(void) __attribute__ ((weak, alias("__gdb_no_op"))); +void gdb_init(void) __attribute__ ((weak, alias("__gdb_no_op"))); +void gdb_do_break(void) __attribute__ ((weak, alias("__gdb_no_op"))); +bool gdb_present(void) __attribute__ ((weak, alias("__gdb_no_op"))); +bool gdbstub_has_putc1_control(void) __attribute__ ((weak, alias("__gdb_no_op"))); +void gdbstub_set_putc1_callback(void (*func)(char)) __attribute__ ((weak, alias("__gdb_no_op"))); +bool gdbstub_has_uart_isr_control(void) __attribute__ ((weak, alias("__gdb_no_op"))); +void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg) __attribute__ ((weak, alias("__gdb_no_op"))); +void gdbstub_write_char(char c) __attribute__ ((weak, alias("__gdb_no_op"))); +void gdbstub_write(const char* buf, size_t size) __attribute__ ((weak, alias("__gdb_no_op"))); diff --git a/cores/esp8266/gdb_hooks.h b/cores/esp8266/gdb_hooks.h index 1d7a375d6c..4953216a1a 100644 --- a/cores/esp8266/gdb_hooks.h +++ b/cores/esp8266/gdb_hooks.h @@ -52,6 +52,71 @@ void gdb_do_break(void); */ bool gdb_present(void); +// If gdbstub has these set true, then we will disable our own +// usage of them, but use gdbstub's callbacks for them instead +/** + * @brief Check if GDB is installing a putc1 callback. + * + * By default, this function returns false. When GDBStub library is linked, + * this function is overriden and returns true. + * + * @return true if GDB is installing a putc1 callback + */ +bool gdbstub_has_putc1_control(void); + +/** + * @brief Register a putc1 callback with GDB. + * @param func function GDB will proxy putc1 data to + * + * By default, this function is a no-op. When GDBStub library is linked, + * this function is overriden and sets GDB stub's secondary putc1 callback to + * func. When GDB stub is linked, but a GDB session is not current attached, + * then GDB stub will pass putc1 chars directly to this function. + */ +void gdbstub_set_putc1_callback(void (*func)(char)); + +/** + * @brief Check if GDB is installing a uart0 isr callback. + * + * By default, this function returns false. When GDBStub library is linked, + * this function is overriden and returns true. + * + * @return true if GDB is installing a uart0 isr callback + */ +bool gdbstub_has_uart_isr_control(void); + +/** + * @brief Register a uart0 isr callback with GDB. + * @param func function GDB will proxy uart0 isr data to + * + * By default, this function is a no-op. When GDBStub library is linked, + * this function is overriden and sets GDB stub's secondary uart0 isr callback + * to func. When GDB stub is linked, but a GDB session is not current attached, + * then GDB stub will pass uart0 isr data back to this function. + */ +void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg); + +/** + * @brief Write a character for output to a GDB session on uart0. + * @param c character to write + * + * By default, this function is a no-op. When GDBStub library is linked, + * this function is overriden and writes a char to either the GDB session on + * uart0 or directly to uart0 if not GDB session is attached. + */ +void gdbstub_write_char(char c); + +/** + * @brief Write a char buffer for output to a GDB session on uart0. + * @param buf buffer of data to write + * @param size length of buffer + * + * By default, this function is a no-op. When GDBStub library is linked, + * this function is overriden and writes a buffer to either the GDB session on + * uart0 or directly to uart0 if not GDB session is attached. + */ +void gdbstub_write(const char* buf, size_t size); + #ifdef __cplusplus } #endif diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index cad8e77a79..750afb0de5 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -171,8 +171,20 @@ size_t uart_rx_available(uart_t* uart) return uart_rx_buffer_available(uart) + uart_rx_fifo_available(uart); } +static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) +{ + uart_t* uart = (uart_t*)arg; + if(uart == NULL || !uart->rx_enabled) { + return; + } + size_t nextPos = (uart->rx_buffer->wpos + 1) % uart->rx_buffer->size; + if(nextPos != uart->rx_buffer->rpos) { + uart->rx_buffer->buffer[uart->rx_buffer->wpos] = data; + uart->rx_buffer->wpos = nextPos; + } +} -void ICACHE_RAM_ATTR uart_isr(void * arg) +static void ICACHE_RAM_ATTR uart_isr(void* arg) { uart_t* uart = (uart_t*)arg; if(uart == NULL || !uart->rx_enabled) { @@ -180,33 +192,41 @@ void ICACHE_RAM_ATTR uart_isr(void * arg) ETS_UART_INTR_DISABLE(); return; } - if(USIS(uart->uart_nr) & ((1 << UIFF) | (1 << UITO))){ + if(USIS(uart->uart_nr) & ((1 << UIFF) | (1 << UITO))) { uart_rx_copy_fifo_to_buffer(uart); } USIC(uart->uart_nr) = USIS(uart->uart_nr); } -void uart_start_isr(uart_t* uart) +static void uart_start_isr(uart_t* uart) { if(uart == NULL || !uart->rx_enabled) { return; } + if(gdbstub_has_uart_isr_control()) { + gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart); + return; + } // UCFFT value is when the RX fifo full interrupt triggers. A value of 1 // triggers the IRS very often. A value of 127 would not leave much time // for ISR to clear fifo before the next byte is dropped. So pick a value // in the middle. - USC1(uart->uart_nr) = (100 << UCFFT) | (0x02 << UCTOT) | (1 <uart_nr) = (100 << UCFFT) | (0x02 << UCTOT) | (1 <uart_nr) = 0xffff; USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO); ETS_UART_INTR_ATTACH(uart_isr, (void *)uart); ETS_UART_INTR_ENABLE(); } -void uart_stop_isr(uart_t* uart) +static void uart_stop_isr(uart_t* uart) { if(uart == NULL || !uart->rx_enabled) { return; } + if(gdbstub_has_uart_isr_control()) { + gdbstub_set_uart_isr_callback(NULL, NULL); + return; + } ETS_UART_INTR_DISABLE(); USC1(uart->uart_nr) = 0; USIC(uart->uart_nr) = 0xffff; @@ -214,14 +234,22 @@ void uart_stop_isr(uart_t* uart) ETS_UART_INTR_ATTACH(NULL, NULL); } +static void uart_do_write_char(int uart_nr, char c) +{ + while((USS(uart_nr) >> USTXC) >= 0x7f) ; + USF(uart_nr) = c; +} void uart_write_char(uart_t* uart, char c) { if(uart == NULL || !uart->tx_enabled) { return; } - while((USS(uart->uart_nr) >> USTXC) >= 0x7f); - USF(uart->uart_nr) = c; + if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { + gdbstub_write_char(c); + return; + } + uart_do_write_char(uart->uart_nr, c); } void uart_write(uart_t* uart, const char* buf, size_t size) @@ -229,8 +257,12 @@ void uart_write(uart_t* uart, const char* buf, size_t size) if(uart == NULL || !uart->tx_enabled) { return; } + if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { + gdbstub_write(buf, size); + return; + } while(size--) { - uart_write_char(uart, *buf++); + uart_do_write_char(uart->uart_nr, *buf++); } } @@ -271,8 +303,10 @@ void uart_flush(uart_t* uart) tmp |= (1 << UCTXRST); } - USC0(uart->uart_nr) |= (tmp); - USC0(uart->uart_nr) &= ~(tmp); + if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) { + USC0(uart->uart_nr) |= (tmp); + USC0(uart->uart_nr) &= ~(tmp); + } } void uart_set_baudrate(uart_t* uart, int baud_rate) @@ -292,6 +326,43 @@ int uart_get_baudrate(uart_t* uart) return uart->baud_rate; } +void uart0_enable_tx_pin(uint8_t pin) +{ + switch(pin) { + case 1: + pinMode(pin, SPECIAL); + break; + case 2: + case 15: + pinMode(pin, FUNCTION_4); + break; + } +} + +void uart0_enable_rx_pin(uint8_t pin) +{ + switch(pin) { + case 3: + pinMode(pin, SPECIAL); + break; + case 13: + pinMode(pin, FUNCTION_4); + break; + } +} + +void uart1_enable_tx_pin(uint8_t pin) +{ + if(pin == 2) { + pinMode(pin, SPECIAL); + } +} + +void uart_disable_pin(uint8_t pin) +{ + pinMode(pin, INPUT); +} + uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size) { uart_t* uart = (uart_t*) malloc(sizeof(uart_t)); @@ -305,7 +376,9 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, s switch(uart->uart_nr) { case UART0: ETS_UART_INTR_DISABLE(); - ETS_UART_INTR_ATTACH(NULL, NULL); + if(!gdbstub_has_uart_isr_control()) { + ETS_UART_INTR_ATTACH(NULL, NULL); + } uart->rx_enabled = (mode != UART_TX_ONLY); uart->tx_enabled = (mode != UART_RX_ONLY); uart->rx_pin = (uart->rx_enabled)?3:255; @@ -325,16 +398,15 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, s return NULL; } uart->rx_buffer = rx_buffer; - pinMode(uart->rx_pin, SPECIAL); + uart0_enable_rx_pin(uart->rx_pin); } if(uart->tx_enabled) { if (tx_pin == 2) { uart->tx_pin = 2; - pinMode(uart->tx_pin, FUNCTION_4); } else { uart->tx_pin = 1; - pinMode(uart->tx_pin, FUNCTION_0); } + uart0_enable_tx_pin(uart->tx_pin); } else { uart->tx_pin = 255; } @@ -347,7 +419,7 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, s uart->rx_pin = 255; uart->tx_pin = (uart->tx_enabled)?2:255; // GPIO7 as TX not possible! See GPIO pins used by UART if(uart->tx_enabled) { - pinMode(uart->tx_pin, SPECIAL); + uart1_enable_tx_pin(uart->tx_pin); } break; case UART_NO: @@ -359,12 +431,19 @@ uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, s uart_set_baudrate(uart, baudrate); USC0(uart->uart_nr) = config; - uart_flush(uart); - USC1(uart->uart_nr) = 0; - USIC(uart->uart_nr) = 0xffff; - USIE(uart->uart_nr) = 0; - if(uart->uart_nr == UART0 && uart->rx_enabled) { - uart_start_isr(uart); + if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) { + uart_flush(uart); + USC1(uart->uart_nr) = 0; + USIC(uart->uart_nr) = 0xffff; + USIE(uart->uart_nr) = 0; + } + if(uart->uart_nr == UART0) { + if(uart->rx_enabled && !gdbstub_has_uart_isr_control()) { + uart_start_isr(uart); + } + if(gdbstub_has_uart_isr_control()) { + ETS_UART_INTR_ENABLE(); + } } return uart; @@ -376,31 +455,17 @@ void uart_uninit(uart_t* uart) return; } - switch(uart->rx_pin) { - case 3: - pinMode(3, INPUT); - break; - case 13: - pinMode(13, INPUT); - break; + if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) { + uart_disable_pin(uart->tx_pin); } - switch(uart->tx_pin) { - case 1: - pinMode(1, INPUT); - break; - case 2: - pinMode(2, INPUT); - break; - case 15: - pinMode(15, INPUT); - break; - } - - if(uart->rx_enabled){ + if(uart->rx_enabled) { free(uart->rx_buffer->buffer); free(uart->rx_buffer); - uart_stop_isr(uart); + if(!gdbstub_has_uart_isr_control()) { + uart_disable_pin(uart->rx_pin); + uart_stop_isr(uart); + } } free(uart); } @@ -412,40 +477,39 @@ void uart_swap(uart_t* uart, int tx_pin) } switch(uart->uart_nr) { case UART0: - if(((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) || (uart->rx_pin == 3 && uart->rx_enabled)) { + if(uart->tx_enabled) { //TX + uart_disable_pin(uart->tx_pin); + } + if(uart->rx_enabled) { //RX + uart_disable_pin(uart->rx_pin); + } + + if(((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) + || (uart->rx_pin == 3 && uart->rx_enabled)) { if(uart->tx_enabled) { //TX - pinMode(uart->tx_pin, INPUT); uart->tx_pin = 15; } if(uart->rx_enabled) { //RX - pinMode(uart->rx_pin, INPUT); uart->rx_pin = 13; } - if(uart->tx_enabled) { - pinMode(uart->tx_pin, FUNCTION_4); //TX - } - if(uart->rx_enabled) { - pinMode(uart->rx_pin, FUNCTION_4); //RX - } IOSWAP |= (1 << IOSWAPU0); } else { if(uart->tx_enabled) { //TX - pinMode(uart->tx_pin, INPUT); uart->tx_pin = (tx_pin == 2)?2:1; } if(uart->rx_enabled) { //RX - pinMode(uart->rx_pin, INPUT); uart->rx_pin = 3; } - if(uart->tx_enabled) { - pinMode(uart->tx_pin, (tx_pin == 2)?FUNCTION_4:SPECIAL); //TX - } - if(uart->rx_enabled) { - pinMode(3, SPECIAL); //RX - } IOSWAP &= ~(1 << IOSWAPU0); } + if(uart->tx_enabled) { //TX + uart0_enable_tx_pin(uart->tx_pin); + } + if(uart->rx_enabled) { //RX + uart0_enable_rx_pin(uart->rx_pin); + } + break; case UART1: // Currently no swap possible! See GPIO pins used by UART @@ -464,13 +528,13 @@ void uart_set_tx(uart_t* uart, int tx_pin) case UART0: if(uart->tx_enabled) { if (uart->tx_pin == 1 && tx_pin == 2) { - pinMode(uart->tx_pin, INPUT); + uart_disable_pin(uart->tx_pin); uart->tx_pin = 2; - pinMode(uart->tx_pin, FUNCTION_4); + uart0_enable_tx_pin(uart->tx_pin); } else if (uart->tx_pin == 2 && tx_pin != 2) { - pinMode(uart->tx_pin, INPUT); + uart_disable_pin(uart->tx_pin); uart->tx_pin = 1; - pinMode(uart->tx_pin, SPECIAL); + uart0_enable_tx_pin(uart->tx_pin); } } @@ -491,7 +555,7 @@ void uart_set_pins(uart_t* uart, int tx, int rx) if(uart->uart_nr == UART0) { // Only UART0 allows pin changes if(uart->tx_enabled && uart->tx_pin != tx) { - if( rx == 13 && tx == 15) { + if(rx == 13 && tx == 15) { uart_swap(uart, 15); } else if (rx == 3 && (tx == 1 || tx == 2)) { if (uart->rx_pin != rx) { @@ -558,21 +622,25 @@ static void uart1_write_char(char c) void uart_set_debug(int uart_nr) { s_uart_debug_nr = uart_nr; + void (*func)(char) = NULL; switch(s_uart_debug_nr) { case UART0: - system_set_os_print(1); - ets_install_putc1((void *) &uart0_write_char); + func = &uart0_write_char; break; case UART1: - system_set_os_print(1); - ets_install_putc1((void *) &uart1_write_char); + func = &uart1_write_char; break; case UART_NO: default: - system_set_os_print(0); - ets_install_putc1((void *) &uart_ignore_char); + func = &uart_ignore_char; break; } + if(!gdbstub_has_putc1_control()) { + system_set_os_print((uint8)((uart_nr == UART0 || uart_nr == UART1)?1:0)); + ets_install_putc1((void *) func); + } else { + gdbstub_set_putc1_callback(func); + } } int uart_get_debug() diff --git a/libraries/GDBStub/src/internal/gdbstub-cfg.h b/libraries/GDBStub/src/internal/gdbstub-cfg.h index 361de6c7f7..be40ab98f6 100644 --- a/libraries/GDBStub/src/internal/gdbstub-cfg.h +++ b/libraries/GDBStub/src/internal/gdbstub-cfg.h @@ -1,5 +1,5 @@ #ifndef GDBSTUB_CFG_H -#define GDBSTUB_CFG_H +#define GDBSTUB_CFG_H /* Enable this define if you're using the RTOS SDK. It will use a custom exception handler instead of the HAL @@ -19,6 +19,14 @@ stops when you run into an error in your code, try enabling this. #define GDBSTUB_USE_OWN_STACK 0 #endif +/* +Enable this to cause the program to pause and wait for gdb to be connected when an exception is +encountered. +*/ +#ifndef GDBSTUB_BREAK_ON_EXCEPTION +#define GDBSTUB_BREAK_ON_EXCEPTION 1 +#endif + /* If this is defined, gdbstub will break the program when you press Ctrl-C in gdb. it does this by hooking the UART interrupt. Unfortunately, this means receiving stuff over the serial port won't @@ -26,7 +34,7 @@ work for your program anymore. This will fail if your program sets an UART inter the gdbstub_init call. */ #ifndef GDBSTUB_CTRLC_BREAK -#define GDBSTUB_CTRLC_BREAK 0 +#define GDBSTUB_CTRLC_BREAK 1 #endif /* @@ -35,7 +43,7 @@ will show up in your gdb session, which is useful if you use gdb to do stuff. It you use a normal terminal, you can't read the printfs anymore. */ #ifndef GDBSTUB_REDIRECT_CONSOLE_OUTPUT -#define GDBSTUB_REDIRECT_CONSOLE_OUTPUT 0 +#define GDBSTUB_REDIRECT_CONSOLE_OUTPUT 1 #endif /* @@ -55,7 +63,25 @@ flash somehow is disabled (eg during SPI operations or flash write/erase operati are called when the flash is disabled (eg due to a Ctrl-C at the wrong time), the ESP8266 will most likely crash. */ -#define ATTR_GDBINIT ICACHE_FLASH_ATTR -#define ATTR_GDBFN ICACHE_RAM_ATTR +#ifndef ATTR_GDBINIT +#define ATTR_GDBINIT ICACHE_FLASH_ATTR +#endif +#ifndef ATTR_GDBFN +#define ATTR_GDBFN ICACHE_RAM_ATTR +#endif +#ifndef ATTR_GDBEXTERNFN +#define ATTR_GDBEXTERNFN ICACHE_FLASH_ATTR +#endif + +#ifndef ASATTR_GDBINIT +#define ASATTR_GDBINIT .section .irom0.text +#endif +#ifndef ASATTR_GDBFN +#define ASATTR_GDBFN .section .iram.text +#endif +#ifndef ASATTR_GDBEXTERNFN +#define ASATTR_GDBEXTERNFN .section .irom0.text +#endif + #endif diff --git a/libraries/GDBStub/src/internal/gdbstub-entry.S b/libraries/GDBStub/src/internal/gdbstub-entry.S index 8ff28da66e..7ea3a40522 100644 --- a/libraries/GDBStub/src/internal/gdbstub-entry.S +++ b/libraries/GDBStub/src/internal/gdbstub-entry.S @@ -24,10 +24,13 @@ .global gdbstub_exceptionStack #endif - .text + ASATTR_GDBFN .literal_position - .text + ASATTR_GDBINIT +.literal_position + + ASATTR_GDBFN .align 4 /* @@ -51,6 +54,7 @@ This is the debugging exception routine; it's called by the debugging vector We arrive here with all regs intact except for a2. The old contents of A2 are saved into the DEBUG_EXCSAVE special function register. EPC is the original PC. */ + .type gdbstub_debug_exception_entry, @function gdbstub_debug_exception_entry: /* //Minimum no-op debug exception handler, for debug @@ -64,32 +68,32 @@ gdbstub_debug_exception_entry: //Save all regs to structure movi a2, gdbstub_savedRegs s32i a0, a2, 0x10 - s32i a1, a2, 0x58 + s32i a1, a2, 0x14 rsr a0, DEBUG_PS s32i a0, a2, 0x04 rsr a0, DEBUG_EXCSAVE //was R2 - s32i a0, a2, 0x14 - s32i a3, a2, 0x18 - s32i a4, a2, 0x1c - s32i a5, a2, 0x20 - s32i a6, a2, 0x24 - s32i a7, a2, 0x28 - s32i a8, a2, 0x2c - s32i a9, a2, 0x30 - s32i a10, a2, 0x34 - s32i a11, a2, 0x38 - s32i a12, a2, 0x3c - s32i a13, a2, 0x40 - s32i a14, a2, 0x44 - s32i a15, a2, 0x48 + s32i a0, a2, 0x18 + s32i a3, a2, 0x1c + s32i a4, a2, 0x20 + s32i a5, a2, 0x24 + s32i a6, a2, 0x28 + s32i a7, a2, 0x2c + s32i a8, a2, 0x30 + s32i a9, a2, 0x34 + s32i a10, a2, 0x38 + s32i a11, a2, 0x3c + s32i a12, a2, 0x40 + s32i a13, a2, 0x44 + s32i a14, a2, 0x48 + s32i a15, a2, 0x4c rsr a0, SAR s32i a0, a2, 0x08 rsr a0, LITBASE - s32i a0, a2, 0x4C - rsr a0, 176 s32i a0, a2, 0x50 - rsr a0, 208 + rsr a0, 176 s32i a0, a2, 0x54 + rsr a0, 208 + s32i a0, a2, 0x58 rsr a0, DEBUGCAUSE s32i a0, a2, 0x5C rsr a4, DEBUG_PC @@ -127,33 +131,33 @@ DebugExceptionExit: movi a2, gdbstub_savedRegs l32i a0, a2, 0x00 wsr a0, DEBUG_PC -// l32i a0, a2, 0x54 +// l32i a0, a2, 0x58 // wsr a0, 208 - l32i a0, a2, 0x50 + l32i a0, a2, 0x54 //wsr a0, 176 //Some versions of gcc do not understand this... .byte 0x00, 176, 0x13 //so we hand-assemble the instruction. - l32i a0, a2, 0x4C + l32i a0, a2, 0x50 wsr a0, LITBASE l32i a0, a2, 0x08 wsr a0, SAR - l32i a15, a2, 0x48 - l32i a14, a2, 0x44 - l32i a13, a2, 0x40 - l32i a12, a2, 0x3c - l32i a11, a2, 0x38 - l32i a10, a2, 0x34 - l32i a9, a2, 0x30 - l32i a8, a2, 0x2c - l32i a7, a2, 0x28 - l32i a6, a2, 0x24 - l32i a5, a2, 0x20 - l32i a4, a2, 0x1c - l32i a3, a2, 0x18 - l32i a0, a2, 0x14 + l32i a15, a2, 0x4c + l32i a14, a2, 0x48 + l32i a13, a2, 0x44 + l32i a12, a2, 0x40 + l32i a11, a2, 0x3c + l32i a10, a2, 0x38 + l32i a9, a2, 0x34 + l32i a8, a2, 0x30 + l32i a7, a2, 0x2c + l32i a6, a2, 0x28 + l32i a5, a2, 0x24 + l32i a4, a2, 0x20 + l32i a3, a2, 0x1c + l32i a0, a2, 0x18 wsr a0, DEBUG_EXCSAVE //was R2 l32i a0, a2, 0x04 wsr a0, DEBUG_PS - l32i a1, a2, 0x58 + l32i a1, a2, 0x14 l32i a0, a2, 0x10 //Read back vector-saved a2 value, put back address of this routine. @@ -162,8 +166,10 @@ DebugExceptionExit: //All done. Return to where we came from. rfi XCHAL_DEBUGLEVEL + .size gdbstub_debug_exception_entry, .-gdbstub_debug_exception_entry +#if GDBSTUB_BREAK_ON_EXCEPTION #if GDBSTUB_FREERTOS /* @@ -184,32 +190,34 @@ the user exception handler vector: */ .global gdbstub_handle_user_exception .global gdbstub_user_exception_entry + .type gdbstub_user_exception_entry, @function + ASATTR_GDBFN .align 4 gdbstub_user_exception_entry: //Save all regs to structure movi a0, gdbstub_savedRegs - s32i a1, a0, 0x14 //was a2 - s32i a3, a0, 0x18 - s32i a4, a0, 0x1c - s32i a5, a0, 0x20 - s32i a6, a0, 0x24 - s32i a7, a0, 0x28 - s32i a8, a0, 0x2c - s32i a9, a0, 0x30 - s32i a10, a0, 0x34 - s32i a11, a0, 0x38 - s32i a12, a0, 0x3c - s32i a13, a0, 0x40 - s32i a14, a0, 0x44 - s32i a15, a0, 0x48 + s32i a1, a0, 0x18 //was a2 + s32i a3, a0, 0x1c + s32i a4, a0, 0x20 + s32i a5, a0, 0x24 + s32i a6, a0, 0x28 + s32i a7, a0, 0x2c + s32i a8, a0, 0x30 + s32i a9, a0, 0x34 + s32i a10, a0, 0x38 + s32i a11, a0, 0x3c + s32i a12, a0, 0x40 + s32i a13, a0, 0x44 + s32i a14, a0, 0x48 + s32i a15, a0, 0x4c rsr a2, SAR s32i a2, a0, 0x08 rsr a2, LITBASE - s32i a2, a0, 0x4C - rsr a2, 176 s32i a2, a0, 0x50 - rsr a2, 208 + rsr a2, 176 s32i a2, a0, 0x54 + rsr a2, 208 + s32i a2, a0, 0x58 rsr a2, EXCCAUSE s32i a2, a0, 0x5C @@ -243,10 +251,13 @@ is still something we need to implement later, if there's any demand for it, or FreeRTOS to allow this in the future. (Which will then kill backwards compatibility... hmmm.) */ j UserExceptionExit + .size gdbstub_user_exception_entry, .-gdbstub_user_exception_entry .global gdbstub_handle_uart_int .global gdbstub_uart_entry + .type gdbstub_uart_entry, @function + ASATTR_GDBFN .align 4 gdbstub_uart_entry: //On entry, the stack frame is at SP+16. @@ -255,29 +266,37 @@ gdbstub_uart_entry: add a2, a2, a1 movi a3, gdbstub_handle_uart_int jx a3 + .size gdbstub_uart_entry, .-gdbstub_uart_entry + +#endif #endif .global gdbstub_save_extra_sfrs_for_exception + .type gdbstub_save_extra_sfrs_for_exception, @function + ASATTR_GDBFN .align 4 //The Xtensa OS HAL does not save all the special function register things. This bit of assembly //fills the gdbstub_savedRegs struct with them. gdbstub_save_extra_sfrs_for_exception: movi a2, gdbstub_savedRegs rsr a3, LITBASE - s32i a3, a2, 0x4C - rsr a3, 176 s32i a3, a2, 0x50 - rsr a3, 208 + rsr a3, 176 s32i a3, a2, 0x54 + rsr a3, 208 + s32i a3, a2, 0x58 rsr a3, EXCCAUSE s32i a3, a2, 0x5C ret + .size gdbstub_save_extra_sfrs_for_exception, .-gdbstub_save_extra_sfrs_for_exception .global gdbstub_init_debug_entry .global _DebugExceptionVector + .type gdbstub_init_debug_entry, @function + ASATTR_GDBINIT .align 4 gdbstub_init_debug_entry: //This puts the following 2 instructions into the debug exception vector: @@ -294,10 +313,13 @@ gdbstub_init_debug_entry: wsr a2, DEBUG_EXCSAVE ret + .size gdbstub_init_debug_entry, .-gdbstub_init_debug_entry //Set up ICOUNT register to step one single instruction .global gdbstub_icount_ena_single_step + .type gdbstub_icount_ena_single_step, @function + ASATTR_GDBFN .align 4 gdbstub_icount_ena_single_step: movi a3, XCHAL_DEBUGLEVEL //Only count steps in non-debug mode @@ -306,6 +328,7 @@ gdbstub_icount_ena_single_step: wsr a2, ICOUNT isync ret + .size gdbstub_icount_ena_single_step, .-gdbstub_icount_ena_single_step //These routines all assume only one breakpoint and watchpoint is available, which @@ -313,6 +336,8 @@ gdbstub_icount_ena_single_step: .global gdbstub_set_hw_breakpoint + .type gdbstub_set_hw_breakpoint, @function + ASATTR_GDBFN gdbstub_set_hw_breakpoint: //a2 - addr, a3 - len (unused here) rsr a4, IBREAKENABLE @@ -323,8 +348,11 @@ gdbstub_set_hw_breakpoint: isync movi a2, 1 ret + .size gdbstub_set_hw_breakpoint, .-gdbstub_set_hw_breakpoint .global gdbstub_del_hw_breakpoint + .type gdbstub_del_hw_breakpoint, @function + ASATTR_GDBFN gdbstub_del_hw_breakpoint: //a2 - addr rsr a5, IBREAKENABLE @@ -336,8 +364,11 @@ gdbstub_del_hw_breakpoint: isync movi a2, 1 ret + .size gdbstub_del_hw_breakpoint, .-gdbstub_del_hw_breakpoint .global gdbstub_set_hw_watchpoint + .type gdbstub_set_hw_watchpoint, @function + ASATTR_GDBFN //a2 - addr, a3 - mask, a4 - type (1=read, 2=write, 3=access) gdbstub_set_hw_watchpoint: //Check if any of the masked address bits are set. If so, that is an error. @@ -362,9 +393,12 @@ gdbstub_set_hw_watchpoint: mov a2, a3 isync ret + .size gdbstub_set_hw_watchpoint, .-gdbstub_set_hw_watchpoint .global gdbstub_del_hw_watchpoint + .type gdbstub_del_hw_watchpoint, @function + ASATTR_GDBFN //a2 - addr gdbstub_del_hw_watchpoint: //See if the address matches @@ -384,11 +418,14 @@ gdbstub_del_hw_watchpoint: return_w_error: movi a2, 0 ret + .size gdbstub_del_hw_watchpoint, .-gdbstub_del_hw_watchpoint //Breakpoint, with an attempt at a functional function prologue and epilogue... .global gdbstub_do_break_breakpoint_addr .global gdbstub_do_break + .type gdbstub_do_break, @function + ASATTR_GDBFN .align 4 gdbstub_do_break: addi a1, a1, -16 @@ -402,3 +439,4 @@ gdbstub_do_break_breakpoint_addr: l32i a15, a1, 12 addi a1, a1, 16 ret + .size gdbstub_do_break, .-gdbstub_do_break diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index 08bfc5925d..50cda02fcf 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -14,8 +14,8 @@ #include "c_types.h" #include "gpio.h" #include "xtensa/corebits.h" +#include "uart_register.h" -#include "gdbstub.h" #include "gdbstub-entry.h" #include "gdbstub-cfg.h" @@ -26,18 +26,17 @@ struct XTensa_exception_frame_s { uint32_t ps; uint32_t sar; uint32_t vpri; - uint32_t a0; - uint32_t a[14]; //a2..a15 + uint32_t a[16]; //a0..a15 //These are added manually by the exception code; the HAL doesn't set these on an exception. uint32_t litbase; uint32_t sr176; uint32_t sr208; - uint32_t a1; //'reason' is abused for both the debug and the exception vector: if bit 7 is set, //this contains an exception reason, otherwise it contains a debug vector bitmap. uint32_t reason; }; +#if GDBSTUB_FREERTOS struct XTensa_rtos_int_frame_s { uint32_t exitPtr; @@ -47,21 +46,19 @@ struct XTensa_rtos_int_frame_s { uint32_t sar; }; -#if GDBSTUB_FREERTOS /* Definitions for FreeRTOS. This redefines some os_* functions to use their non-os* counterparts. It also sets up some function pointers for ROM functions that aren't in the FreeRTOS ld files. */ #include #include -void _xt_isr_attach(int inum, void *fn); -void _xt_isr_unmask(int inum); +void os_isr_attach(int inum, void *fn); void os_install_putc1(void (*p)(char c)); #define os_printf(...) printf(__VA_ARGS__) -#define os_memcpy(a,b,c) memcpy(a,b,c) +#define os_strncmp(...) strncmp(__VA_ARGS__) typedef void wdtfntype(); -static wdtfntype *ets_wdt_disable=(wdtfntype *)0x400030f0; -static wdtfntype *ets_wdt_enable=(wdtfntype *)0x40002fa0; +static wdtfntype *ets_wdt_disable = (wdtfntype *)0x400030f0; +static wdtfntype *ets_wdt_enable = (wdtfntype *)0x40002fa0; #else /* @@ -72,37 +69,15 @@ the xthal stack frame struct. #include "user_interface.h" void _xtos_set_exception_handler(int cause, void (exhandler)(struct XTensa_exception_frame_s *frame)); -int os_printf_plus(const char *format, ...) __attribute__ ((format (printf, 1, 2))); #endif #define EXCEPTION_GDB_SP_OFFSET 0x100 -//We need some UART register defines. -#define ETS_UART_INUM 5 -#define REG_UART_BASE( i ) (0x60000000+(i)*0xf00) -#define UART_STATUS( i ) (REG_UART_BASE( i ) + 0x1C) -#define UART_RXFIFO_CNT 0x000000FF -#define UART_RXFIFO_CNT_S 0 -#define UART_TXFIFO_CNT 0x000000FF -#define UART_TXFIFO_CNT_S 16 -#define UART_FIFO( i ) (REG_UART_BASE( i ) + 0x0) -#define UART_INT_ENA(i) (REG_UART_BASE(i) + 0xC) -#define UART_INT_CLR(i) (REG_UART_BASE(i) + 0x10) -#define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) -#define UART_RXFIFO_FULL_INT_ENA (BIT(0)) -#define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) -#define UART_RXFIFO_FULL_INT_CLR (BIT(0)) - - - - //Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which //implies a minimum size of about 190 bytes. #define PBUFLEN 256 -//Length of gdb stdout buffer, for console redirection -#define OBUFLEN 32 //The asm stub saves the Xtensa registers here when a debugging exception happens. struct XTensa_exception_frame_s gdbstub_savedRegs; @@ -111,86 +86,53 @@ struct XTensa_exception_frame_s gdbstub_savedRegs; int exceptionStack[256]; #endif +static bool gdb_attached = false; static unsigned char cmd[PBUFLEN]; //GDB command input buffer static char chsum; //Running checksum of the output packet +#if GDBSTUB_CTRLC_BREAK +static void (*uart_isr_callback)(void*, uint8_t) = NULL; +static void* uart_isr_arg = NULL; +#endif #if GDBSTUB_REDIRECT_CONSOLE_OUTPUT -static unsigned char obuf[OBUFLEN]; //GDB stdout buffer -static int obufpos=0; //Current position in the buffer +static void (*uart_putc1_callback)(char) = NULL; #endif -static int32_t singleStepPs=-1; //Stores ps when single-stepping instruction. -1 when not in use. - -//Small function to feed the hardware watchdog. Needed to stop the ESP from resetting -//due to a watchdog timeout while reading a command. -static void ATTR_GDBFN keepWDTalive() { - uint64_t *wdtval=(uint64_t*)0x3ff21048; - uint64_t *wdtovf=(uint64_t*)0x3ff210cc; - int *wdtctl=(int*)0x3ff210c8; - *wdtovf=*wdtval+1600000; - *wdtctl|=(1<<31); -} - -//Receive a char from the uart. Uses polling and feeds the watchdog. -static int ATTR_GDBFN gdbRecvChar() { - int i; - while (((READ_PERI_REG(UART_STATUS(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) { - keepWDTalive(); - } - i=READ_PERI_REG(UART_FIFO(0)); - return i; -} - -//Send a char to the uart. -static void ATTR_GDBFN gdbSendChar(char c) { - while (((READ_PERI_REG(UART_STATUS(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; - WRITE_PERI_REG(UART_FIFO(0), c); -} +//Stores ps when single-stepping instruction. -1 when not in use. +static int32_t singleStepPs = -1; -//Send the start of a packet; reset checksum calculation. -static void ATTR_GDBFN gdbPacketStart() { - chsum=0; - gdbSendChar('$'); +//Uart libs can reference these to see if gdb is attaching to them +bool gdbstub_has_putc1_control() { +#if GDBSTUB_REDIRECT_CONSOLE_OUTPUT + return true; +#else + return false; +#endif } - -//Send a char as part of a packet -static void ATTR_GDBFN gdbPacketChar(char c) { - if (c=='#' || c=='$' || c=='}' || c=='*') { - gdbSendChar('}'); - gdbSendChar(c^0x20); - chsum+=(c^0x20)+'}'; - } else { - gdbSendChar(c); - chsum+=c; - } +bool gdbstub_has_uart_isr_control() { +#if GDBSTUB_CTRLC_BREAK + return true; +#else + return false; +#endif } -//Send a string as part of a packet -static void ATTR_GDBFN gdbPacketStr(char *c) { - while (*c!=0) { - gdbPacketChar(*c); - c++; - } -} -//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void ATTR_GDBFN gdbPacketHex(int val, int bits) { - char hexChars[]="0123456789abcdef"; - int i; - for (i=bits; i>0; i-=4) { - gdbPacketChar(hexChars[(val>>(i-4))&0xf]); - } +//Small function to feed the hardware watchdog. Needed to stop the ESP from resetting +//due to a watchdog timeout while reading a command. +static void ATTR_GDBFN keepWDTalive() { + uint64_t *wdtval = (uint64_t*)0x3ff21048; + uint64_t *wdtovf = (uint64_t*)0x3ff210cc; + int *wdtctl = (int*)0x3ff210c8; + *wdtovf = *wdtval + 1600000; + *wdtctl |= 1 << 31; } -//Finish sending a packet. -static void ATTR_GDBFN gdbPacketEnd() { - gdbSendChar('#'); - gdbPacketHex(chsum, 8); -} //Error states used by the routines that grab stuff from the incoming gdb packet #define ST_ENDPACKET -1 #define ST_ERR -2 #define ST_OK -3 #define ST_CONT -4 +#define ST_DETACH -5 //Grab a hex value from the gdb packet. Ptr will get positioned on the end //of the hex string, as far as the routine has read into it. Bits/4 indicates @@ -199,30 +141,31 @@ static void ATTR_GDBFN gdbPacketEnd() { static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) { int i; int no; - unsigned int v=0; + unsigned int v = 0; char c; - no=bits/4; - if (bits==-1) no=64; - for (i=0; i='0' && c<='9') { - v<<=4; - v|=(c-'0'); - } else if (c>='A' && c<='F') { - v<<=4; - v|=(c-'A')+10; - } else if (c>='a' && c<='f') { - v<<=4; - v|=(c-'a')+10; - } else if (c=='#') { - if (bits==-1) { + if (c >= '0' && c <= '9') { + v <<= 4; + v |= (c-'0'); + } else if (c >= 'A' && c <= 'F') { + v <<= 4; + v |= (c-'A') + 10; + } else if (c >= 'a' && c <= 'f') { + v <<= 4; + v |= (c-'a') + 10; + } else if (c == '#') { + if (bits == -1) { (*ptr)--; return v; } return ST_ENDPACKET; } else { - if (bits==-1) { + if (bits == -1) { (*ptr)--; return v; } @@ -234,72 +177,152 @@ static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) { //Swap an int into the form gdb wants it static int ATTR_GDBFN iswap(int i) { - int r; - r=((i>>24)&0xff); - r|=((i>>16)&0xff)<<8; - r|=((i>>8)&0xff)<<16; - r|=((i>>0)&0xff)<<24; - return r; + return ((i >> 24) & 0xff) + | (((i >> 16) & 0xff) << 8) + | (((i >> 8) & 0xff) << 16) + | (((i >> 0) & 0xff) << 24); } //Read a byte from the ESP8266 memory. static unsigned char ATTR_GDBFN readbyte(unsigned int p) { - int *i=(int*)(p&(~3)); - if (p<0x20000000 || p>=0x60000000) return -1; - return *i>>((p&3)*8); + if (p < 0x20000000 || p >= 0x60000000) return -1; + int *i = (int*)(p & ~3); + return *i >> ((p & 3) * 8); } //Write a byte to the ESP8266 memory. static void ATTR_GDBFN writeByte(unsigned int p, unsigned char d) { - int *i=(int*)(p&(~3)); - if (p<0x20000000 || p>=0x60000000) return; - if ((p&3)==0) *i=(*i&0xffffff00)|(d<<0); - if ((p&3)==1) *i=(*i&0xffff00ff)|(d<<8); - if ((p&3)==2) *i=(*i&0xff00ffff)|(d<<16); - if ((p&3)==3) *i=(*i&0x00ffffff)|(d<<24); + if (p < 0x20000000 || p >= 0x60000000) return; + int *i = (int*)(p & ~3); + if ((p & 3) == 0) *i = (*i & 0xffffff00) | (d << 0); + else if ((p & 3) == 1) *i = (*i & 0xffff00ff) | (d << 8); + else if ((p & 3) == 2) *i = (*i & 0xff00ffff) | (d << 16); + else if ((p & 3) == 3) *i = (*i & 0x00ffffff) | (d << 24); } //Returns 1 if it makes sense to write to addr p static int ATTR_GDBFN validWrAddr(int p) { - if (p>=0x3ff00000 && p<0x40000000) return 1; - if (p>=0x40100000 && p<0x40140000) return 1; - if (p>=0x60000000 && p<0x60002000) return 1; - return 0; + return (p >= 0x3ff00000 && p < 0x40000000) + || (p >= 0x40100000 && p < 0x40140000) + || (p >= 0x60000000 && p < 0x60002000); } -/* -Register file in the format lx106 gdb port expects it. -Inspired by gdb/regformats/reg-xtensa.dat from -https://github.com/jcmvbkbc/crosstool-NG/blob/lx106-g%2B%2B/overlays/xtensa_lx106.tar -As decoded by Cesanta. -*/ -struct regfile { - uint32_t a[16]; - uint32_t pc; - uint32_t sar; - uint32_t litbase; - uint32_t sr176; - uint32_t sr208; - uint32_t ps; -}; + +static inline bool ATTR_GDBFN gdbRxFifoIsEmpty() { + return ((READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT) == 0; +} + +static inline bool ATTR_GDBFN gdbTxFifoIsFull() { + return ((READ_PERI_REG(UART_STATUS(0)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) >= 126; +} + +//Receive a char from the uart. Uses polling and feeds the watchdog. +static inline int ATTR_GDBFN gdbRecvChar() { + while (gdbRxFifoIsEmpty()) { + keepWDTalive(); + } + return READ_PERI_REG(UART_FIFO(0)); +} + +//Send a char to the uart. +static void ATTR_GDBFN gdbSendChar(char c) { + while (gdbTxFifoIsFull()) + ; + WRITE_PERI_REG(UART_FIFO(0), c); +} + + +//Send the start of a packet; reset checksum calculation. +static void ATTR_GDBFN gdbPacketStart() { + chsum = 0; + gdbSendChar('$'); +} + +//Send a char as part of a packet +static void ATTR_GDBFN gdbPacketChar(char c) { + if (c == '#' || c == '$' || c == '}' || c == '*') { + gdbSendChar('}'); + chsum += '}'; + c ^= 0x20; + } + gdbSendChar(c); + chsum += c; +} + +//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. +static void ATTR_GDBFN gdbPacketHex(int val, int bits) { + static const char hexChars[] = "0123456789abcdef"; + int i; + for (i = bits; i > 0; i -= 4) { + gdbPacketChar(hexChars[(val >> (i - 4)) & 0xf]); + } +} + +//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. +static void ATTR_GDBFN gdbPacketSwappedHexInt(int val) { + gdbPacketHex(iswap(val), 32); +} + +//Finish sending a packet. +static void ATTR_GDBFN gdbPacketEnd() { + gdbSendChar('#'); + //Ok to use packet version here since hex char can never be an + //excape-requiring character + gdbPacketHex(chsum, 8); +} + +// Send a complete packet containing str +static void ATTR_GDBFN gdbSendPacketStr(const char *c) { + gdbPacketStart(); + while (*c != 0) { + gdbPacketChar(*c); + c++; + } + gdbPacketEnd(); +} + +// Send a complete packet containing str as an output message +static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketStr(const unsigned char* buf, size_t size) { + size_t i; + gdbPacketStart(); + gdbPacketChar('O'); + for (i = 0; i < size; i++) + gdbPacketHex(buf[i], 8); + gdbPacketEnd(); +} + +// Send a complete packet containing c as an output message +static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketChar(unsigned char c) { + gdbPacketStart(); + gdbPacketChar('O'); + gdbPacketHex(c, 8); + gdbPacketEnd(); +} + +static long ATTR_GDBFN gdbGetSwappedHexInt(unsigned char **ptr) { + return iswap(gdbGetHexVal(ptr, 32)); +} //Send the reason execution is stopped to GDB. static void ATTR_GDBFN sendReason() { + static const char exceptionSignal[] = {4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; #if 0 char *reason=""; //default #endif //exception-to-signal mapping - char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; - unsigned int i=0; + size_t i; gdbPacketStart(); gdbPacketChar('T'); - if (gdbstub_savedRegs.reason==0xff) { + if (gdbstub_savedRegs.reason == 0xff) { gdbPacketHex(2, 8); //sigint - } else if (gdbstub_savedRegs.reason&0x80) { + } else if (gdbstub_savedRegs.reason & 0x80) { //We stopped because of an exception. Convert exception code to a signal number and send it. - i=gdbstub_savedRegs.reason&0x7f; - if (i= '1' && cmd[2] <= '4') { //hardware break/watchpoint + int result; + data += 2; //skip 'x,' + i = gdbGetHexVal(&data, -1); data++; //skip ',' - j=gdbGetHexVal(&data, -1); - gdbPacketStart(); - if (cmd[1]=='1') { //Set breakpoint - if (gdbstub_set_hw_breakpoint(i, j)) { - gdbPacketStr("OK"); - } else { - gdbPacketStr("E01"); + j = gdbGetHexVal(&data, -1); + if (cmd[0] == 'Z') { //Set hardware break/watchpoint + if (cmd[1] == '1') { //Set breakpoint + result = gdbstub_set_hw_breakpoint(i, j); + } else { //Set watchpoint + int access; + unsigned int mask = 0; + if (cmd[1] == '2') access = 2; //write + if (cmd[1] == '3') access = 1; //read + if (cmd[1] == '4') access = 3; //access + if (j == 1) mask = 0x3F; + if (j == 2) mask = 0x3E; + if (j == 4) mask = 0x3C; + if (j == 8) mask = 0x38; + if (j == 16) mask = 0x30; + if (j == 32) mask = 0x20; + result = mask != 0 && gdbstub_set_hw_watchpoint(i, mask, access); } - } else if (cmd[1]=='2' || cmd[1]=='3' || cmd[1]=='4') { //Set watchpoint - int access=0; - int mask=0; - if (cmd[1]=='2') access=2; //write - if (cmd[1]=='3') access=1; //read - if (cmd[1]=='4') access=3; //access - if (j==1) mask=0x3F; - if (j==2) mask=0x3E; - if (j==4) mask=0x3C; - if (j==8) mask=0x38; - if (j==16) mask=0x30; - if (j==32) mask=0x20; - if (j==64) mask=0x00; - if (mask!=0 && gdbstub_set_hw_watchpoint(i,mask, access)) { - gdbPacketStr("OK"); - } else { - gdbPacketStr("E01"); + } else { //Clear hardware break/watchpoint + if (cmd[1] == '1') { //hardware breakpoint + result = gdbstub_del_hw_breakpoint(i); + } else { //hardware watchpoint + result = gdbstub_del_hw_watchpoint(i); } } - gdbPacketEnd(); - } else if (cmd[0]=='z') { //Clear hardware break/watchpoint - data+=2; //skip 'x,' - i=gdbGetHexVal(&data, -1); - data++; //skip ',' - j=gdbGetHexVal(&data, -1); - gdbPacketStart(); - if (cmd[1]=='1') { //hardware breakpoint - if (gdbstub_del_hw_breakpoint(i)) { - gdbPacketStr("OK"); - } else { - gdbPacketStr("E01"); - } - } else if (cmd[1]=='2' || cmd[1]=='3' || cmd[1]=='4') { //hardware watchpoint - if (gdbstub_del_hw_watchpoint(i)) { - gdbPacketStr("OK"); - } else { - gdbPacketStr("E01"); - } + if (result) { + gdbSendPacketOK(); + } else { + gdbSendPacketE01(); } - gdbPacketEnd(); } else { //We don't recognize or support whatever GDB just sent us. - gdbPacketStart(); - gdbPacketEnd(); - return ST_ERR; + gdbSendEmptyPacket(); } return ST_OK; } - //Lower layer: grab a command packet and check the checksum //Calls gdbHandleCommand on the packet if the checksum is OK -//Returns ST_OK on success, ST_ERR when checksum fails, a -//character if it is received instead of the GDB packet -//start char. +//Returns only if execution of the user program should continue +//Otherwise keeps reading uart data and executing commands +//Flags that gdb has been attached whenever a gdb formatted +// packet is received +//While gdb is attached, checks for ctl-c (\x03) if it's not +// already paused +//Keeps reading commands if it is paused, until either a +// continue, detach, or kill command is received +//It is not necessary for gdb to be attached for it to be paused +//For example, during an exception break, the program is +// paused but gdb might not be attached yet static int ATTR_GDBFN gdbReadCommand() { - unsigned char c; - unsigned char chsum=0, rchsum; + unsigned char chsum; unsigned char sentchs[2]; - int p=0; + size_t p; + unsigned char c; unsigned char *ptr; - c=gdbRecvChar(); - if (c!='$') return c; - while(1) { - c=gdbRecvChar(); - if (c=='#') { //end of packet, checksum follows - cmd[p]=0; - break; - } - chsum+=c; - if (c=='$') { - //Wut, restart packet? - chsum=0; - p=0; - continue; + int result; + ETS_UART_INTR_DISABLE(); + ets_wdt_disable(); + sendReason(); + while (true) { +gdbReadCommand_start: + while (gdbRecvChar() != '$') + ; +gdbReadCommand_packetBegin: + chsum = 0; + p = 0; + while ((c = gdbRecvChar()) != '#') { //end of packet, checksum follows + if (c == '$') { + //Wut, restart packet? + goto gdbReadCommand_packetBegin; + } + if (c == '}') { //escape the next char + c = gdbRecvChar() ^ 0x20; + } + chsum += c; + cmd[p++] = c; + if (p >= PBUFLEN) { + //Received more than the size of the command buffer + goto gdbReadCommand_start; + } } - if (c=='}') { //escape the next char - c=gdbRecvChar(); - chsum+=c; - c^=0x20; + cmd[p] = 0; + sentchs[0] = gdbRecvChar(); + sentchs[1] = gdbRecvChar(); + ptr = &sentchs[0]; + if (gdbGetHexVal(&ptr, 8) == chsum) { + gdb_attached = true; + gdbSendChar('+'); + result = gdbHandleCommand(); + if (result != ST_OK) { + break; + } + } else { + gdbSendChar('-'); } - cmd[p++]=c; - if (p>=PBUFLEN) return ST_ERR; } - //A # has been received. Get and check the received chsum. - sentchs[0]=gdbRecvChar(); - sentchs[1]=gdbRecvChar(); - ptr=&sentchs[0]; - rchsum=gdbGetHexVal(&ptr, 8); -// os_printf("c %x r %x\n", chsum, rchsum); - if (rchsum!=chsum) { - gdbSendChar('-'); - return ST_ERR; - } else { - gdbSendChar('+'); - return gdbHandleCommand(cmd, p); + if (result == ST_DETACH) { + gdb_attached = false; } + ets_wdt_enable(); + ETS_UART_INTR_ENABLE(); + return result; } + + //Get the value of one of the A registers static unsigned int ATTR_GDBFN getaregval(int reg) { - if (reg==0) return gdbstub_savedRegs.a0; - if (reg==1) return gdbstub_savedRegs.a1; - return gdbstub_savedRegs.a[reg-2]; + return gdbstub_savedRegs.a[reg]; } //Set the value of one of the A registers -static void ATTR_GDBFN setaregval(int reg, unsigned int val) { - os_printf("%x -> %x\n", val, reg); - if (reg==0) gdbstub_savedRegs.a0=val; - if (reg==1) gdbstub_savedRegs.a1=val; - gdbstub_savedRegs.a[reg-2]=val; +static inline void ATTR_GDBFN setaregval(int reg, unsigned int val) { + // os_printf("%x -> %x\n", val, reg); + gdbstub_savedRegs.a[reg] = val; } //Emulate the l32i/s32i instruction we're stopped at. -static void ATTR_GDBFN emulLdSt() { - unsigned char i0=readbyte(gdbstub_savedRegs.pc); - unsigned char i1=readbyte(gdbstub_savedRegs.pc+1); - unsigned char i2=readbyte(gdbstub_savedRegs.pc+2); +static inline void ATTR_GDBFN emulLdSt() { + unsigned char i0 = readbyte(gdbstub_savedRegs.pc); + unsigned char i1 = readbyte(gdbstub_savedRegs.pc + 1); + unsigned char i2; int *p; - if ((i0&0xf)==2 && (i1&0xf0)==0x20) { - //l32i - p=(int*)getaregval(i1&0xf)+(i2*4); - setaregval(i0>>4, *p); - gdbstub_savedRegs.pc+=3; - } else if ((i0&0xf)==0x8) { - //l32i.n - p=(int*)getaregval(i1&0xf)+((i1>>4)*4); - setaregval(i0>>4, *p); - gdbstub_savedRegs.pc+=2; - } else if ((i0&0xf)==2 && (i1&0xf0)==0x60) { - //s32i - p=(int*)getaregval(i1&0xf)+(i2*4); - *p=getaregval(i0>>4); - gdbstub_savedRegs.pc+=3; - } else if ((i0&0xf)==0x9) { - //s32i.n - p=(int*)getaregval(i1&0xf)+((i1>>4)*4); - *p=getaregval(i0>>4); - gdbstub_savedRegs.pc+=2; - } else { - os_printf("GDBSTUB: No l32i/s32i instruction: %x %x %x. Huh?", i2, i1, i0); + + if ((i0 & 0xf) == 2 && (i1 & 0xb0) == 0x20) { + //l32i or s32i + i2 = readbyte(gdbstub_savedRegs.pc + 2); + p = (int*)getaregval(i1 & 0xf) + (i2 * 4); + i0 >>= 4; + if ((i1 & 0xf0) == 0x20) { //l32i + setaregval(i0, *p); + } else { //s32i + *p = getaregval(i0); + } + gdbstub_savedRegs.pc += 3; + } else if ((i0 & 0xe) == 0x8) { + //l32i.n or s32i.n + p = (int*)getaregval(i1 & 0xf) + ((i1 >> 4) * 4); + if ((i0 & 0xf) == 0x8) { //l32i.n + setaregval(i0 >> 4, *p); + } else { + *p = getaregval(i0 >> 4); + } + gdbstub_savedRegs.pc += 2; + // } else { + // os_printf("GDBSTUB: No l32i/s32i instruction: %x %x. Huh?", i1, i0); } } //We just caught a debug exception and need to handle it. This is called from an assembly //routine in gdbstub-entry.S void ATTR_GDBFN gdbstub_handle_debug_exception() { - ets_wdt_disable(); - - if (singleStepPs!=-1) { + if (singleStepPs != -1) { //We come here after single-stepping an instruction. Interrupts are disabled //for the single step. Re-enable them here. - gdbstub_savedRegs.ps=(gdbstub_savedRegs.ps&~0xf)|(singleStepPs&0xf); - singleStepPs=-1; + gdbstub_savedRegs.ps = (gdbstub_savedRegs.ps & ~0xf) | (singleStepPs & 0xf); + singleStepPs =- 1; } - sendReason(); - while(gdbReadCommand()!=ST_CONT); - if ((gdbstub_savedRegs.reason&0x84)==0x4) { - //We stopped due to a watchpoint. We can't re-execute the current instruction - //because it will happily re-trigger the same watchpoint, so we emulate it - //while we're still in debugger space. - emulLdSt(); - } else if ((gdbstub_savedRegs.reason&0x88)==0x8) { - //We stopped due to a BREAK instruction. Skip over it. - //Check the instruction first; gdb may have replaced it with the original instruction - //if it's one of the breakpoints it set. - if (readbyte(gdbstub_savedRegs.pc+2)==0 && - (readbyte(gdbstub_savedRegs.pc+1)&0xf0)==0x40 && - (readbyte(gdbstub_savedRegs.pc)&0x0f)==0x00) { - gdbstub_savedRegs.pc+=3; - } - } else if ((gdbstub_savedRegs.reason&0x90)==0x10) { - //We stopped due to a BREAK.N instruction. Skip over it, after making sure the instruction - //actually is a BREAK.N - if ((readbyte(gdbstub_savedRegs.pc+1)&0xf0)==0xf0 && - readbyte(gdbstub_savedRegs.pc)==0x2d) { - gdbstub_savedRegs.pc+=3; + gdbReadCommand(); + if ((gdbstub_savedRegs.reason & 0x80) == 0) { //Watchpoint/BREAK/BREAK.N + if ((gdbstub_savedRegs.reason & 0x4) != 0) { + //We stopped due to a watchpoint. We can't re-execute the current instruction + //because it will happily re-trigger the same watchpoint, so we emulate it + //while we're still in debugger space. + emulLdSt(); + } else if ((((gdbstub_savedRegs.reason & 0x8) != 0) + //We stopped due to a BREAK instruction. Skip over it. + //Check the instruction first; gdb may have replaced it with the original instruction + //if it's one of the breakpoints it set. + && readbyte(gdbstub_savedRegs.pc + 2) == 0 + && (readbyte(gdbstub_savedRegs.pc + 1) & 0xf0) == 0x40 + && (readbyte(gdbstub_savedRegs.pc) & 0x0f) == 0x00) + || (((gdbstub_savedRegs.reason & 0x10) != 0) + //We stopped due to a BREAK.N instruction. Skip over it, after making sure the instruction + //actually is a BREAK.N + && (readbyte(gdbstub_savedRegs.pc + 1) & 0xf0) == 0xf0 + && readbyte(gdbstub_savedRegs.pc) == 0x2d)) { + gdbstub_savedRegs.pc += 3; } } - ets_wdt_enable(); } -#if GDBSTUB_FREERTOS -//Freetos exception. This routine is called by an assembly routine in gdbstub-entry.S -void ATTR_GDBFN gdbstub_handle_user_exception() { - ets_wdt_disable(); - gdbstub_savedRegs.reason|=0x80; //mark as an exception reason - sendReason(); - while(gdbReadCommand()!=ST_CONT); - ets_wdt_enable(); -} -#else -//Non-OS exception handler. Gets called by the Xtensa HAL. -static void ATTR_GDBFN gdb_exception_handler(struct XTensa_exception_frame_s *frame) { - //Save the extra registers the Xtensa HAL doesn't save - gdbstub_save_extra_sfrs_for_exception(); +#if GDBSTUB_BREAK_ON_EXCEPTION || GDBSTUB_CTRLC_BREAK + +#if !GDBSTUB_FREERTOS +static inline int ATTR_GDBFN gdbReadCommandWithFrame(void* frame) { //Copy registers the Xtensa HAL did save to gdbstub_savedRegs - os_memcpy(&gdbstub_savedRegs, frame, 19*4); + os_memcpy(&gdbstub_savedRegs, frame, 5 * 4); + os_memcpy(&gdbstub_savedRegs.a[2], ((uint32_t*)frame) + 5, 14 * 4); //Credits go to Cesanta for this trick. A1 seems to be destroyed, but because it //has a fixed offset from the address of the passed frame, we can recover it. - gdbstub_savedRegs.a1=(uint32_t)frame+EXCEPTION_GDB_SP_OFFSET; + gdbstub_savedRegs.a[1] = (uint32_t)frame + EXCEPTION_GDB_SP_OFFSET; - gdbstub_savedRegs.reason|=0x80; //mark as an exception reason - - ets_wdt_disable(); - *((uint32_t*)UART_INT_ENA(0)) = 0; - sendReason(); - while(gdbReadCommand()!=ST_CONT); - ets_wdt_enable(); + int result = gdbReadCommand(); //Copy any changed registers back to the frame the Xtensa HAL uses. - os_memcpy(frame, &gdbstub_savedRegs, 19*4); + os_memcpy(frame, &gdbstub_savedRegs, 5 * 4); + os_memcpy(((uint32_t*)frame) + 5, &gdbstub_savedRegs.a[2], 14 * 4); + + return result; } #endif -#if GDBSTUB_REDIRECT_CONSOLE_OUTPUT -//Replacement putchar1 routine. Instead of spitting out the character directly, it will buffer up to -//OBUFLEN characters (or up to a \n, whichever comes earlier) and send it out as a gdb stdout packet. -static void ATTR_GDBFN gdb_semihost_putchar1(char c) { - int i; - obuf[obufpos++]=c; - if (c=='\n' || obufpos==OBUFLEN) { - gdbPacketStart(); - gdbPacketChar('O'); - for (i=0; i>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; - while (fifolen!=0) { - if ((READ_PERI_REG(UART_FIFO(0)) & 0xFF)==0x3) doDebug=1; //Check if any of the chars is control-C. Throw away rest. + fifolen = (READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; + while (fifolen != 0) { + //Check if any of the chars is control-C. Throw away rest. + if ((READ_PERI_REG(UART_FIFO(0)) & 0xFF) == 0x3) + doDebug = 1; fifolen--; } - WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR|UART_RXFIFO_TOUT_INT_CLR); + WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); if (doDebug) { //Copy registers the Xtensa HAL did save to gdbstub_savedRegs - os_memcpy(&gdbstub_savedRegs, frame, 19*4); - gdbstub_savedRegs.a1=(uint32_t)frame+EXCEPTION_GDB_SP_OFFSET; - - gdbstub_savedRegs.reason=0xff; //mark as user break reason + gdbstub_savedRegs.pc = frame->pc; + gdbstub_savedRegs.ps = frame->ps; + gdbstub_savedRegs.sar = frame->sar; + for (x = 0; x < 16; x++) + gdbstub_savedRegs.a[x] = frame->a[x]; +// gdbstub_savedRegs.a1=(uint32_t)frame+EXCEPTION_GDB_SP_OFFSET; + gdbstub_savedRegs.reason = 0xff; //mark as user break reason - ets_wdt_disable(); - sendReason(); - while(gdbReadCommand()!=ST_CONT); - ets_wdt_enable(); + gdbReadCommand(); //Copy any changed registers back to the frame the Xtensa HAL uses. - os_memcpy(frame, &gdbstub_savedRegs, 19*4); + frame->pc = gdbstub_savedRegs.pc; + frame->ps = gdbstub_savedRegs.ps; + frame->sar = gdbstub_savedRegs.sar; + for (x = 0; x < 16; x++) + frame->a[x] = gdbstub_savedRegs.a[x]; } } static void ATTR_GDBINIT install_uart_hdlr() { - ets_isr_attach(ETS_UART_INUM, uart_hdlr, NULL); - SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - ets_isr_unmask((1<>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; - while (fifolen!=0) { - if ((READ_PERI_REG(UART_FIFO(0)) & 0xFF)==0x3) doDebug=1; //Check if any of the chars is control-C. Throw away rest. + ETS_UART_INTR_DISABLE(); + WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR); + + int fifolen = (READ_PERI_REG(UART_STATUS(0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT; + while (true) { + if (fifolen == 0) { + ETS_UART_INTR_ENABLE(); + return; + } + c = READ_PERI_REG(UART_FIFO(0)) & 0xFF; + //Check if any of the chars is control-C + if (c == 0x3) { + break; + } +#if GDBSTUB_CTRLC_BREAK + if (!gdb_attached && uart_isr_callback != NULL) { + uart_isr_callback(uart_isr_arg, c); + } +#endif fifolen--; } - WRITE_PERI_REG(UART_INT_CLR(0), UART_RXFIFO_FULL_INT_CLR|UART_RXFIFO_TOUT_INT_CLR); - if (doDebug) { - //Copy registers the Xtensa HAL did save to gdbstub_savedRegs - gdbstub_savedRegs.pc=frame->pc; - gdbstub_savedRegs.ps=frame->ps; - gdbstub_savedRegs.sar=frame->sar; - gdbstub_savedRegs.a0=frame->a[0]; - gdbstub_savedRegs.a1=frame->a[1]; - for (x=2; x<16; x++) gdbstub_savedRegs.a[x-2]=frame->a[x]; + gdbstub_savedRegs.reason = 0xff; //mark as user break reason + gdbReadCommandWithFrame(frame); +} -// gdbstub_savedRegs.a1=(uint32_t)frame+EXCEPTION_GDB_SP_OFFSET; +static void ATTR_GDBINIT install_uart_hdlr() { + ETS_UART_INTR_DISABLE(); + ETS_UART_INTR_ATTACH(gdbstub_uart_hdlr, NULL); - gdbstub_savedRegs.reason=0xff; //mark as user break reason + configure_uart(); -// ets_wdt_disable(); - sendReason(); - while(gdbReadCommand()!=ST_CONT); -// ets_wdt_enable(); - //Copy any changed registers back to the frame the Xtensa HAL uses. - frame->pc=gdbstub_savedRegs.pc; - frame->ps=gdbstub_savedRegs.ps; - frame->sar=gdbstub_savedRegs.sar; - frame->a[0]=gdbstub_savedRegs.a0; - frame->a[1]=gdbstub_savedRegs.a1; - for (x=2; x<16; x++) frame->a[x]=gdbstub_savedRegs.a[x-2]; - } + WRITE_PERI_REG(UART_CONF1(0), + ((100 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | + ((0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S) | + UART_RX_TOUT_EN); + + WRITE_PERI_REG(UART_INT_CLR(0), 0xffff); + SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); + ETS_UART_INTR_ENABLE(); } -static void ATTR_GDBINIT install_uart_hdlr() { - _xt_isr_attach(ETS_UART_INUM, gdbstub_uart_entry); - SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - _xt_isr_unmask((1< +#include +#include + +#include "gdbstub-cfg.h" + #ifdef __cplusplus extern "C" { #endif void gdbstub_init(); +//Indicates whether gdbstub will attach to these or not +//Useful for other uart libs to avoid conflicts +bool gdbstub_has_putc1_control(); +bool gdbstub_has_uart_isr_control(); + +#if GDBSTUB_REDIRECT_CONSOLE_OUTPUT +void gdbstub_set_putc1_callback(void (*callback)(char)); +#endif + +void gdbstub_write_char(char c); +void gdbstub_write(const char* buf, size_t size); + +#if GDBSTUB_CTRLC_BREAK && !GDBSTUB_FREERTOS +void gdbstub_set_uart_isr_callback(void (*callback)(void*, uint8_t), void* arg); + +//Override points for enabling tx and rx pins for uart0 +void gdbstub_hook_enable_tx_pin_uart0(uint8_t pin); +void gdbstub_hook_enable_rx_pin_uart0(uint8_t pin); +#endif + #ifdef __cplusplus } #endif From c3890bd16f4f8261f8a4f7b64a414d2a1d0fe3b3 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 26 Dec 2018 12:15:08 -0800 Subject: [PATCH 02/11] Fix GDB merge errors --- cores/esp8266/uart.c | 7 ------- libraries/GDBStub/src/internal/gdbstub.c | 2 +- tools/sdk/lwip2/builder | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index 724d825f8d..2a64dc870c 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -636,13 +636,6 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx } } - uart_flush(uart); - USC1(uart->uart_nr) = 0; - USIC(uart->uart_nr) = 0xffff; - USIE(uart->uart_nr) = 0; - if(uart->uart_nr == UART0 && uart->rx_enabled) - uart_start_isr(uart); - return uart; } diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index 50cda02fcf..8f6a864ff6 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -369,7 +369,7 @@ void ATTR_GDBEXTERNFN gdbstub_write(const char* buf, size_t size) { size_t i; if (gdb_attached) { ETS_UART_INTR_DISABLE(); - gdbSendOutputPacketStr(buf, size); + gdbSendOutputPacketStr((const unsigned char *)buf, size); ETS_UART_INTR_ENABLE(); } else { for (i = 0; i < size; i++) { diff --git a/tools/sdk/lwip2/builder b/tools/sdk/lwip2/builder index 51b2d6ceec..c434c6fab1 160000 --- a/tools/sdk/lwip2/builder +++ b/tools/sdk/lwip2/builder @@ -1 +1 @@ -Subproject commit 51b2d6ceec717e06dc6ef0c839bb1e23809193be +Subproject commit c434c6fab1cb7c670cd9a223e6667ec80a618d29 From 6a714f3bf8dcaa4fe0068dc1d0faa867c0ca5f82 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 26 Dec 2018 22:37:57 -0800 Subject: [PATCH 03/11] Update to unpatched GDB protocol specification It appears that Espressif patched the open source xtensa GDB port in order to build their old GDB executable and their old gdbstub (basically removing any register in a generic xtensa and only leaving those present in the chip they synthesized). Their GDBStub also assumed this behavior. Unpatched upstream GNU GDB now expects all the registers in xtensa-config.c to be sent/read on a 'g' command. Change the GDB stub to send "xxxxxxxx"s (legal per the spec) for unimplemented registers. This makes the 'g' response much longer, but it's results are cached and in an interactive debugger it isn't noticeable. --- libraries/GDBStub/src/internal/gdbstub.c | 48 ++++++++++++++---------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index 8f6a864ff6..8b3a2fac77 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -263,6 +263,10 @@ static void ATTR_GDBFN gdbPacketSwappedHexInt(int val) { gdbPacketHex(iswap(val), 32); } +static void ATTR_GDBFN gdbPacketXXXXInt() { + for (int i=0; i<8; i++) gdbPacketChar('x'); +} + //Finish sending a packet. static void ATTR_GDBFN gdbPacketEnd() { gdbSendChar('#'); @@ -400,26 +404,30 @@ static inline int ATTR_GDBFN gdbHandleCommand() { //Handle a command int i, j, k; unsigned char *data = cmd + 1; - if (cmd[0] == 'g') { //send all registers to gdb - gdbPacketStart(); - for (i = 0; i < 16; i++) - gdbPacketSwappedHexInt(gdbstub_savedRegs.a[i]); - gdbPacketSwappedHexInt(gdbstub_savedRegs.pc); - gdbPacketSwappedHexInt(gdbstub_savedRegs.sar); - gdbPacketSwappedHexInt(gdbstub_savedRegs.litbase); - gdbPacketSwappedHexInt(gdbstub_savedRegs.sr176); - gdbPacketHex(0, 32); - gdbPacketSwappedHexInt(gdbstub_savedRegs.ps); - gdbPacketEnd(); - } else if (cmd[0] == 'G') { //receive content for all registers from gdb - for (i = 0; i < 16; i++) - gdbstub_savedRegs.a[i] = gdbGetSwappedHexInt(&data); - gdbstub_savedRegs.pc = gdbGetSwappedHexInt(&data); - gdbstub_savedRegs.sar = gdbGetSwappedHexInt(&data); - gdbstub_savedRegs.litbase = gdbGetSwappedHexInt(&data); - gdbstub_savedRegs.sr176 = gdbGetSwappedHexInt(&data); - gdbGetHexVal(&data, 32); - gdbstub_savedRegs.ps = gdbGetSwappedHexInt(&data); + if (cmd[0]=='g') { //send all registers to gdb + gdbPacketStart(); + gdbPacketSwappedHexInt(gdbstub_savedRegs.pc); + for (int i=1; i<=35; i++) gdbPacketXXXXInt(); + gdbPacketSwappedHexInt(gdbstub_savedRegs.sar); + gdbPacketSwappedHexInt(gdbstub_savedRegs.litbase); + for (int i=38; i<=39; i++) gdbPacketXXXXInt(); + gdbPacketSwappedHexInt(gdbstub_savedRegs.sr176); + for (int i=41; i<=41; i++) gdbPacketXXXXInt(); + gdbPacketSwappedHexInt(gdbstub_savedRegs.ps); + for (int i=43; i<=96; i++) gdbPacketXXXXInt(); + for (i=0; i<16; i++) gdbPacketSwappedHexInt(gdbstub_savedRegs.a[i]); + gdbPacketEnd(); + } else if (cmd[0]=='G') { //receive content for all registers from gdb + gdbstub_savedRegs.pc=gdbGetSwappedHexInt(&data); + for (int i=1; i<=35; i++) gdbGetHexVal(&data, 32); + gdbstub_savedRegs.sar=gdbGetSwappedHexInt(&data); + gdbstub_savedRegs.litbase=gdbGetSwappedHexInt(&data); + for (int i=38; i<=39; i++) gdbGetHexVal(&data, 32); + gdbstub_savedRegs.sr176=gdbGetSwappedHexInt(&data); + for (int i=41; i<=41; i++) gdbGetHexVal(&data, 32); + gdbstub_savedRegs.ps=gdbGetSwappedHexInt(&data); + for (int i=43; i<=96; i++) gdbGetHexVal(&data, 32); + for (i=0; i<16; i++) gdbstub_savedRegs.a[i]=gdbGetSwappedHexInt(&data); gdbSendPacketOK(); } else if ((cmd[0] | 0x20) == 'm') { //read/write memory to gdb i = gdbGetHexVal(&data, -1); //addr From a4c8ea48bbb9c74f9c7963608a20a70442a2a0bb Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 26 Dec 2018 22:49:37 -0800 Subject: [PATCH 04/11] Fix .iram.literal to come before .iram.text for GDB --- tools/sdk/ld/eagle.app.v6.common.ld.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index 4237bcd9c8..d72a95d308 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -192,7 +192,7 @@ SECTIONS *(.entry.text) *(.init.literal) *(.init) - *(.literal .text .iram.text .iram.text.* .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.literal .text .iram.literal .iram.text .iram.text.* .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) #ifdef VTABLES_IN_IRAM *(.rodata._ZTV*) /* C++ vtables */ #endif From b1d47e6bc9f91e45e30ac6d9d61e1caa4e13563d Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 27 Dec 2018 09:20:57 -0800 Subject: [PATCH 05/11] Move functions to flash, call using wrappers All functions which are not interrupt or exception called are now in flash. A small IRAM wrapper enables flash when processing main GDB ops by calling Cache_Read_Enable_New() and then jumping to the main flash code. This seems to work for catching exceptions, data and code breaks, and Ctrl-C. The UART ISR handler and exception handler register-saving bits of code in ASM are still in IRAM. GDB IRAM usage is now about 670 bytes. --- libraries/GDBStub/src/internal/gdbstub.c | 53 ++++++++++++++---------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index 8b3a2fac77..ec051ebe31 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -115,7 +115,6 @@ bool gdbstub_has_uart_isr_control() { #endif } - //Small function to feed the hardware watchdog. Needed to stop the ESP from resetting //due to a watchdog timeout while reading a command. static void ATTR_GDBFN keepWDTalive() { @@ -138,7 +137,7 @@ static void ATTR_GDBFN keepWDTalive() { //of the hex string, as far as the routine has read into it. Bits/4 indicates //the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much //hex chars as possible. -static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) { +static long /*ATTR_GDBFN*/ gdbGetHexVal(unsigned char **ptr, int bits) { int i; int no; unsigned int v = 0; @@ -176,7 +175,7 @@ static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) { } //Swap an int into the form gdb wants it -static int ATTR_GDBFN iswap(int i) { +static int /*ATTR_GDBFN*/ iswap(int i) { return ((i >> 24) & 0xff) | (((i >> 16) & 0xff) << 8) | (((i >> 8) & 0xff) << 16) @@ -184,14 +183,14 @@ static int ATTR_GDBFN iswap(int i) { } //Read a byte from the ESP8266 memory. -static unsigned char ATTR_GDBFN readbyte(unsigned int p) { +static unsigned char /*ATTR_GDBFN*/ readbyte(unsigned int p) { if (p < 0x20000000 || p >= 0x60000000) return -1; int *i = (int*)(p & ~3); return *i >> ((p & 3) * 8); } //Write a byte to the ESP8266 memory. -static void ATTR_GDBFN writeByte(unsigned int p, unsigned char d) { +static void /*ATTR_GDBFN*/ writeByte(unsigned int p, unsigned char d) { if (p < 0x20000000 || p >= 0x60000000) return; int *i = (int*)(p & ~3); if ((p & 3) == 0) *i = (*i & 0xffffff00) | (d << 0); @@ -201,7 +200,7 @@ static void ATTR_GDBFN writeByte(unsigned int p, unsigned char d) { } //Returns 1 if it makes sense to write to addr p -static int ATTR_GDBFN validWrAddr(int p) { +static int /*ATTR_GDBFN*/ validWrAddr(int p) { return (p >= 0x3ff00000 && p < 0x40000000) || (p >= 0x40100000 && p < 0x40140000) || (p >= 0x60000000 && p < 0x60002000); @@ -217,7 +216,7 @@ static inline bool ATTR_GDBFN gdbTxFifoIsFull() { } //Receive a char from the uart. Uses polling and feeds the watchdog. -static inline int ATTR_GDBFN gdbRecvChar() { +static inline int /*ATTR_GDBFN*/ gdbRecvChar() { while (gdbRxFifoIsEmpty()) { keepWDTalive(); } @@ -225,7 +224,7 @@ static inline int ATTR_GDBFN gdbRecvChar() { } //Send a char to the uart. -static void ATTR_GDBFN gdbSendChar(char c) { +static void /*ATTR_GDBFN*/ gdbSendChar(char c) { while (gdbTxFifoIsFull()) ; WRITE_PERI_REG(UART_FIFO(0), c); @@ -233,13 +232,13 @@ static void ATTR_GDBFN gdbSendChar(char c) { //Send the start of a packet; reset checksum calculation. -static void ATTR_GDBFN gdbPacketStart() { +static void /*ATTR_GDBFN*/ gdbPacketStart() { chsum = 0; gdbSendChar('$'); } //Send a char as part of a packet -static void ATTR_GDBFN gdbPacketChar(char c) { +static void /*ATTR_GDBFN*/ gdbPacketChar(char c) { if (c == '#' || c == '$' || c == '}' || c == '*') { gdbSendChar('}'); chsum += '}'; @@ -250,7 +249,7 @@ static void ATTR_GDBFN gdbPacketChar(char c) { } //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void ATTR_GDBFN gdbPacketHex(int val, int bits) { +static void /*ATTR_GDBFN*/ gdbPacketHex(int val, int bits) { static const char hexChars[] = "0123456789abcdef"; int i; for (i = bits; i > 0; i -= 4) { @@ -259,16 +258,16 @@ static void ATTR_GDBFN gdbPacketHex(int val, int bits) { } //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void ATTR_GDBFN gdbPacketSwappedHexInt(int val) { +static void /*ATTR_GDBFN*/ gdbPacketSwappedHexInt(int val) { gdbPacketHex(iswap(val), 32); } -static void ATTR_GDBFN gdbPacketXXXXInt() { +static void /*ATTR_GDBFN*/ gdbPacketXXXXInt() { for (int i=0; i<8; i++) gdbPacketChar('x'); } //Finish sending a packet. -static void ATTR_GDBFN gdbPacketEnd() { +static void /*ATTR_GDBFN*/ gdbPacketEnd() { gdbSendChar('#'); //Ok to use packet version here since hex char can never be an //excape-requiring character @@ -276,7 +275,7 @@ static void ATTR_GDBFN gdbPacketEnd() { } // Send a complete packet containing str -static void ATTR_GDBFN gdbSendPacketStr(const char *c) { +static void /*ATTR_GDBFN*/ gdbSendPacketStr(const char *c) { gdbPacketStart(); while (*c != 0) { gdbPacketChar(*c); @@ -303,13 +302,13 @@ static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketChar(unsigned char c) { gdbPacketEnd(); } -static long ATTR_GDBFN gdbGetSwappedHexInt(unsigned char **ptr) { +static long /*ATTR_GDBFN*/ gdbGetSwappedHexInt(unsigned char **ptr) { return iswap(gdbGetHexVal(ptr, 32)); } //Send the reason execution is stopped to GDB. -static void ATTR_GDBFN sendReason() { +static void /*ATTR_GDBFN*/ sendReason() { static const char exceptionSignal[] = {4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; #if 0 char *reason=""; //default @@ -400,7 +399,7 @@ struct regfile { */ //Handle a command as received from GDB. -static inline int ATTR_GDBFN gdbHandleCommand() { +static inline int /*ATTR_GDBFN*/ gdbHandleCommand() { //Handle a command int i, j, k; unsigned char *data = cmd + 1; @@ -537,7 +536,7 @@ static inline int ATTR_GDBFN gdbHandleCommand() { //It is not necessary for gdb to be attached for it to be paused //For example, during an exception break, the program is // paused but gdb might not be attached yet -static int ATTR_GDBFN gdbReadCommand() { +static int /*ATTR_GDBFN*/ gdbReadCommand() { unsigned char chsum; unsigned char sentchs[2]; size_t p; @@ -606,7 +605,7 @@ static inline void ATTR_GDBFN setaregval(int reg, unsigned int val) { } //Emulate the l32i/s32i instruction we're stopped at. -static inline void ATTR_GDBFN emulLdSt() { +static inline void /*ATTR_GDBFN*/ emulLdSt() { unsigned char i0 = readbyte(gdbstub_savedRegs.pc); unsigned char i1 = readbyte(gdbstub_savedRegs.pc + 1); unsigned char i2; @@ -639,7 +638,13 @@ static inline void ATTR_GDBFN emulLdSt() { //We just caught a debug exception and need to handle it. This is called from an assembly //routine in gdbstub-entry.S +static void gdbstub_handle_debug_exception_flash(); void ATTR_GDBFN gdbstub_handle_debug_exception() { + Cache_Read_Enable_New(); + gdbstub_handle_debug_exception_flash(); +} + +static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash() { if (singleStepPs != -1) { //We come here after single-stepping an instruction. Interrupts are disabled //for the single step. Re-enable them here. @@ -676,7 +681,7 @@ void ATTR_GDBFN gdbstub_handle_debug_exception() { #if GDBSTUB_BREAK_ON_EXCEPTION || GDBSTUB_CTRLC_BREAK #if !GDBSTUB_FREERTOS -static inline int ATTR_GDBFN gdbReadCommandWithFrame(void* frame) { +static inline int /*ATTR_GDBFN*/ gdbReadCommandWithFrame(void* frame) { //Copy registers the Xtensa HAL did save to gdbstub_savedRegs os_memcpy(&gdbstub_savedRegs, frame, 5 * 4); os_memcpy(&gdbstub_savedRegs.a[2], ((uint32_t*)frame) + 5, 14 * 4); @@ -720,9 +725,15 @@ static void ATTR_GDBINIT install_exceptions() { } #else //Non-OS exception handler. Gets called by the Xtensa HAL. +static void gdbstub_exception_handler_flash(struct XTensa_exception_frame_s *frame); static void ATTR_GDBFN gdbstub_exception_handler(struct XTensa_exception_frame_s *frame) { //Save the extra registers the Xtensa HAL doesn't save gdbstub_save_extra_sfrs_for_exception(); + Cache_Read_Enable_New(); + gdbstub_exception_handler_flash(frame); +} + +static void __attribute__((noinline)) gdbstub_exception_handler_flash(struct XTensa_exception_frame_s *frame) { gdbstub_savedRegs.reason |= 0x80; //mark as an exception reason while (gdbReadCommandWithFrame((void*)frame) != ST_CONT) ; From b341c07f6c346d5f56e05aa0703c5927795d13b6 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 27 Dec 2018 10:50:24 -0800 Subject: [PATCH 06/11] Remove LWIP2 builder commit --- tools/sdk/lwip2/builder | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sdk/lwip2/builder b/tools/sdk/lwip2/builder index c434c6fab1..4670dfc00f 160000 --- a/tools/sdk/lwip2/builder +++ b/tools/sdk/lwip2/builder @@ -1 +1 @@ -Subproject commit c434c6fab1cb7c670cd9a223e6667ec80a618d29 +Subproject commit 4670dfc00f0ce2caf194aca2f497d89d7b758ac7 From cc03543e7b9a6468dc2dc80a12b2c7130192fbd0 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 27 Dec 2018 11:59:02 -0800 Subject: [PATCH 07/11] Add documentation and gdbstub_init header Add some simple GDB documentation to the main tree showing a worked example. Adds the definition of `void gdbstub_init()` to --- doc/gdb.rst | 365 ++++++++++++++++++++++++++++++++ doc/index.rst | 1 + libraries/GDBStub/src/GDBStub.h | 2 +- 3 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 doc/gdb.rst diff --git a/doc/gdb.rst b/doc/gdb.rst new file mode 100644 index 0000000000..0e1e838071 --- /dev/null +++ b/doc/gdb.rst @@ -0,0 +1,365 @@ +Using GDB to Debug Applications +=============================== + +ESP applications can be debugged using GDB, the GNU debugger, which is +included with the standard IDE installation. This note will only discuss +the ESP specific steps, so please refer to the +`main GNU GDB documentation +`__. + +Note that as of 2.5.0, the toolchain moved from the ESPRESSIF patched, +closed-source version of GDB to the main GNU version. The debugging +formats are different, so please be sure to use only the latest Arduino +toolchain GDB executable. + +CLI and IDE Note +---------------- + +Because the Arduino IDE doesn't support interactive debugging, the following +sections describe debugging using the command line. Other IDEs which use +GDB in their debug backends should work identically, but you may need to +edit their configuration files or options to enable the remote serial +debugging required and to set the standard options. PRs are happily +accepted for updates to this document with additional IDEs! + + +Preparing your application for GDB +---------------------------------- + +Applications need to be changed to enable GDB debugging support. This +change will add 2-3KB of flash and around 700 bytes of IRAM usage, but +should not affect operation of the application. + +In your main ``sketch.ino`` file, add the following line to the top of +the application: + +.. code:: cpp + + #include + +And in the ``void setup()`` function ensure the serial port is initialized +and call ``gdbstub_init()``: + +.. code:: cpp + + Serial.begin(115200); + gdbstub_init(); + +Rebuild and reupload your application and it should run exactly as before. + + +Starting a Debug Session +------------------------ + +Once your application is running, the process to attach a debugger is +quite simple: +. Close the Arduino Serial Monitor +. Locate Application.ino.elf File +. Open a Command Prompt and Start GDB +. Apply the GDB configurations +. Attach the Debugger +. Debug Away! + + +Close the Arduino Serial Monitor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because GDB needs full control of the serial port, you will need to close +any Arduino Serial Monitor windows you may have open. Otherwise GDB will +report an error while attempting to debug. + +Locate Application.ino.elf File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order for GDB to debug your application, you need to locate the compiled +ELF format version of it (which includes needed debug symbols). Under Linux +these files are stored in ``/tmp/arduino_build_*`` and the following command +will help locate the right file for your app + +.. code:: cpp + + find /tmp -name "*.elf" -print + +Note the full path of ELF file that corresponds to your sketch name, it will +be needed later once GDB is started. + + +Open a Command Prompt and Start GDB +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Open a terminal or ``CMD`` prompt and navigate to the proper ESP8266 toolchain +directory. + +.. code:: cpp + + ~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb + +.. code:: cpp + + cd TODO WINDOWS + xtensa-lx106-elf-gdb.exe + +Please note the proper GDB name is "xtensa-lx106-elf-gdb". If you accidentally +run "gdb" you may start your own operating system's GDB, which will not know how +to talk to the ESP8266. + +Apply the GDB Configurations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At the ``(gdb)`` prompt, enter the following options to configure GDB for the +ESP8266 memory map and configuration: + +.. code:: cpp + + set remote hardware-breakpoint-limit 1 + set remote hardware-watchpoint-limit 1 + set remote interrupt-on-connect on + set remote kill-packet off + set remote symbol-lookup-packet off + set remote verbose-resume-packet off + mem 0x20000000 0x3fefffff ro cache + mem 0x3ff00000 0x3fffffff rw + mem 0x40000000 0x400fffff ro cache + mem 0x40100000 0x4013ffff rw cache + mem 0x40140000 0x5fffffff ro cache + mem 0x60000000 0x60001fff rw + set serial baud 115200 + +Now tell GDB where your compiled ELF file is located: + +.. code:: cpp + + file /tmp/arduino_build_257110/sketch_dec26a.ino.elf + +Attach the Debugger +~~~~~~~~~~~~~~~~~~~ + +Once GDB has been configured properly and loaded your debugging symbols, connect +it to the ESP with the command (replace the ttyUSB0 or COM9 with your ESP's serial +port): + +.. code:: cpp + + target remote /dev/ttyUSB0 + +or + +.. code:: cpp + + target remote \\.\COM9 + +At this point GDB will send a stop the application on the ESP8266 and you can +begin setting a breakpoint (``break loop``) or any other debugging operation. + + +Example Debugging Session +------------------------- + +Create a new sketch and paste the following code into it: + +.. code:: cpp + + #include + + void setup() { + Serial.begin(115200); + gdbstub_init(); + Serial.printf("Starting...\n"); + } + + void loop() { + static uint32_t cnt = 0; + Serial.printf("%d\n", cnt++); + delay(100); + } + +Save it and then build and upload to your ESP8266. On the Serial monitor you +should see something like + +.. code:: cpp + + 1 + 2 + 3 + .... + + +Now close the Serial Monitor. + +Open a command prompt and find the ELF file: + +.. code:: cpp + + earle@server:~$ find /tmp -name "*.elf" -print + /tmp/arduino_build_257110/testgdb.ino.elf + /tmp/arduino_build_531411/listfiles.ino.elf + /tmp/arduino_build_156712/SDWebServer.ino.elf + +In this example there are multiple ``elf`` files found, but we only care about +the one we just built, ``testgdb.ino.elf``. + +Open up the proper ESP8266-specific GDB + +.. code:: cpp + + earle@server:~$ ~/.arduino15/packages/esp8266/hardware/xtensa-lx106-elf/bin/xtensa-lx106-elf-gdb + GNU gdb (GDB) 8.2.50.20180723-git + Copyright (C) 2018 Free Software Foundation, Inc. + License GPLv3+: GNU GPL version 3 or later + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + Type "show copying" and "show warranty" for details. + This GDB was configured as "--host=x86_64-linux-gnu --target=xtensa-lx106-elf". + Type "show configuration" for configuration details. + For bug reporting instructions, please see: + . + Find the GDB manual and other documentation resources online at: + . + + For help, type "help". + Type "apropos word" to search for commands related to "word". + (gdb) + +We're now at the GDB prompt, but nothing has been set up for the ESP8266 +and no debug information has been loaded. Cut-and-paste the setup options: + +.. code:: cpp + (gdb) set remote hardware-breakpoint-limit 1 + (gdb) set remote hardware-watchpoint-limit 1 + (gdb) set remote interrupt-on-connect on + (gdb) set remote kill-packet off + (gdb) set remote symbol-lookup-packet off + (gdb) set remote verbose-resume-packet off + (gdb) mem 0x20000000 0x3fefffff ro cache + (gdb) mem 0x3ff00000 0x3fffffff rw + (gdb) mem 0x40000000 0x400fffff ro cache + (gdb) mem 0x40100000 0x4013ffff rw cache + (gdb) mem 0x40140000 0x5fffffff ro cache + (gdb) mem 0x60000000 0x60001fff rw + (gdb) set serial baud 115200 + (gdb) + +And tell GDB where the debugging info ELF file is located: + +.. code:: cpp + + (gdb) file /tmp/arduino_build_257110/testgdb.ino.elf + Reading symbols from /tmp/arduino_build_257110/testgdb.ino.elf...done. + +Now, connect to the running ESP8266: + +.. code:: cpp + + (gdb) target remote /dev/ttyUSB0 + Remote debugging using /dev/ttyUSB0 + 0x40000f68 in ?? () + (gdb) + +Don't worry that GDB doesn't know what is at our present address, we broke +into the code at a random spot and we could be in an interrupt, in the +ROM, or elsewhere. The important bit is that we're not connected and +two things will now happen: we can debug, and the app's regular serial +output will be displayed. + +Continue the running app to see the serial output: + +.. code:: cpp + + (gdb) cont + Continuing. + 74 + 75 + 76 + 77 + ... + +The app is back running and we can stop it at any time using ``Ctrl-C``: + +.. code:: cpp + 113 + ^C + Program received signal SIGINT, Interrupt. + 0x40000f68 in ?? () + (gdb) + +At this point we can set a breakpoint on the main ``loop()`` and restart +to get into our own code: + +.. code:: cpp + + (gdb) break loop + Breakpoint 1 at 0x40202e33: file /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino, line 10. + (gdb) cont + Continuing. + Note: automatically using hardware breakpoints for read-only addresses. + bcn_timout,ap_probe_send_start + + Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10 + 10 void loop() + (gdb) + +Let's examine the local variable: + +.. code:: cpp + (gdb) next + loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:13 + 13 Serial.printf("%d\n", cnt++); + (gdb) print cnt + $1 = 114 + (gdb) + +And change it: + +.. code:: cpp + + $2 = 114 + (gdb) set cnt = 2000 + (gdb) print cnt + $3 = 2000 + (gdb) + +And restart the app and see our changes take effect: + +.. code:: cpp + + (gdb) cont + Continuing. + 2000 + Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10 + 10 void loop() { + (gdb) cont + Continuing. + 2001 + Breakpoint 1, loop () at /home/earle/Arduino/sketch_dec26a/sketch_dec26a.ino:10 + 10 void loop() { + (gdb) + +Looks like we left the breakpoint on loop(), let's get rid of it and try again: + +.. code:: cpp + + (gdb) delete + Delete all breakpoints? (y or n) y + (gdb) cont + Continuing. + 2002 + 2003 + 2004 + 2005 + 2006 + .... + +At this point we can exit GDB with ``quit`` or do further debugging. + + +ESP8266 Hardware Debugging Limitations +-------------------------------------- + +The ESP8266 only supports a single hardware breakpoint and a single +hardware data watchpoint. This means only one breakpoint in user code +is allowed at any time. Consider using the ``thb`` (temporary hardware +breakpoint) command in GDB while debugging instead of the more common +``break`` command, since ``thb`` will remove the breakpoint once it is +reached automatically and save you some trouble. + + diff --git a/doc/index.rst b/doc/index.rst index c8be3ac6ab..9fdba0f1de 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -12,6 +12,7 @@ Welcome to ESP8266 Arduino Core's documentation! ESP8266WiFi OTA Updates PROGMEM + Using GDB to debug Boards FAQ diff --git a/libraries/GDBStub/src/GDBStub.h b/libraries/GDBStub/src/GDBStub.h index c9145016d0..bc809ff692 100644 --- a/libraries/GDBStub/src/GDBStub.h +++ b/libraries/GDBStub/src/GDBStub.h @@ -1,6 +1,6 @@ #ifndef GDBSTUB_H #define GDBSTUB_H -// this header is intentionally left blank +extern "C" void gdbstub_init(); #endif //GDBSTUB_H From 0e2adcb8c27095ced05b4a7d3760ac5cff26a6ba Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sun, 30 Dec 2018 09:11:29 -0800 Subject: [PATCH 08/11] Clean up GDB include and library dir Replace GDBstub.h with the version in the internal/ directory, and adjust stub code accordingly. This way, only one copy of a file called "GDBstub.h" will exist. Update the gdbcommands and replace the obsolete ESPRESSIF readme with @kylefleming's version since we're mainly doing serial, not TCP, connected debugging. Bump the library rev. number since this is a pretty big functionality change. Minor documentation tweak. --- doc/gdb.rst | 4 +- .../GDBStub/{README-lib.md => README.md} | 0 libraries/GDBStub/README.rst | 49 ------------------- libraries/GDBStub/gdbcmds | 19 ++++++- libraries/GDBStub/library.json | 2 +- libraries/GDBStub/library.properties | 2 +- libraries/GDBStub/src/GDBStub.h | 38 +++++++++++++- libraries/GDBStub/src/internal/gdbstub.c | 2 +- libraries/GDBStub/src/internal/gdbstub.h | 40 --------------- 9 files changed, 59 insertions(+), 97 deletions(-) rename libraries/GDBStub/{README-lib.md => README.md} (100%) delete mode 100644 libraries/GDBStub/README.rst delete mode 100644 libraries/GDBStub/src/internal/gdbstub.h diff --git a/doc/gdb.rst b/doc/gdb.rst index 0e1e838071..619f394e66 100644 --- a/doc/gdb.rst +++ b/doc/gdb.rst @@ -257,9 +257,9 @@ Now, connect to the running ESP8266: Don't worry that GDB doesn't know what is at our present address, we broke into the code at a random spot and we could be in an interrupt, in the -ROM, or elsewhere. The important bit is that we're not connected and +ROM, or elsewhere. The important bit is that we're now connected and two things will now happen: we can debug, and the app's regular serial -output will be displayed. +output will be displayed on the GDB console.. Continue the running app to see the serial output: diff --git a/libraries/GDBStub/README-lib.md b/libraries/GDBStub/README.md similarity index 100% rename from libraries/GDBStub/README-lib.md rename to libraries/GDBStub/README.md diff --git a/libraries/GDBStub/README.rst b/libraries/GDBStub/README.rst deleted file mode 100644 index 312af4befe..0000000000 --- a/libraries/GDBStub/README.rst +++ /dev/null @@ -1,49 +0,0 @@ -Using GDB stub --------------- - -- Add ``#include `` to the sketch -- Upload the sketch -- Redirect serial port to TCP port: - - :: - - tcp_serial_redirect.py -p /dev/tty.SLAB_USBtoUART -b 115200 --spy -P 9980 --rts=0 --dtr=0 - - Change port and baud rate as necessary. This command requires python - and pyserial. -- Observe serial output: - - :: - - nc localhost 9980 - -- Once crash happens, close nc and start gdb: - - :: - - xtensa-lx106-elf-gdb /path/to/Sketch.cpp.elf -ex "target remote :9980" - - Or, using the provided gdbcmds file: - - :: - - xtensa-lx106-elf-gdb /path/to/Sketch.cpp.elf -x gdbcmds - -- Use gdb to inspect program state at the point of an exception. - -Tips and tricks ---------------- - -- Upon including GDBStub.h in the sketch, the target is automatically halted when software WDT fires, with - - :: - - *((int*)0) = 0; - - at the top of ``__wrap_system_restart_local`` in - core\_esp8266\_postmortem.c. - -License -------- - -Espressif MIT License. See License file. diff --git a/libraries/GDBStub/gdbcmds b/libraries/GDBStub/gdbcmds index 8962413e77..b5f7def68b 100644 --- a/libraries/GDBStub/gdbcmds +++ b/libraries/GDBStub/gdbcmds @@ -1,3 +1,20 @@ +# ESP8266 HW limits the number of breakpoints/watchpoints set remote hardware-breakpoint-limit 1 set remote hardware-watchpoint-limit 1 -target remote :9980 +# Some GDBstub settings +set remote interrupt-on-connect on +set remote kill-packet off +set remote symbol-lookup-packet off +set remote verbose-resume-packet off +# The memory map, so GDB knows where it can install SW breakpoints +mem 0x20000000 0x3fefffff ro cache +mem 0x3ff00000 0x3fffffff rw +mem 0x40000000 0x400fffff ro cache +mem 0x40100000 0x4013ffff rw cache +mem 0x40140000 0x5fffffff ro cache +mem 0x60000000 0x60001fff rw +# Change the following to your sketch's ELF file +file /path/to/sketch.ino.elf +# Change the following to your serial port and baud +set serial baud 115200 +target remote /dev/ttyUSB0 diff --git a/libraries/GDBStub/library.json b/libraries/GDBStub/library.json index 6b38685eb8..9d8c692e0c 100644 --- a/libraries/GDBStub/library.json +++ b/libraries/GDBStub/library.json @@ -1,6 +1,6 @@ { "name": "GDBStub", - "version": "0.2", + "version": "0.3", "keywords": "gdb, debug", "description": "GDB server stub helps debug crashes when JTAG isn't an option.", "repository": diff --git a/libraries/GDBStub/library.properties b/libraries/GDBStub/library.properties index 28a69deef6..8dd2bd97b4 100644 --- a/libraries/GDBStub/library.properties +++ b/libraries/GDBStub/library.properties @@ -1,5 +1,5 @@ name=GDBStub -version=0.2 +version=0.3 author=Jeroen Domburg maintainer=Ivan Grokhotkov sentence=GDB server stub by Espressif diff --git a/libraries/GDBStub/src/GDBStub.h b/libraries/GDBStub/src/GDBStub.h index bc809ff692..2a6560a5b6 100644 --- a/libraries/GDBStub/src/GDBStub.h +++ b/libraries/GDBStub/src/GDBStub.h @@ -1,6 +1,40 @@ #ifndef GDBSTUB_H #define GDBSTUB_H -extern "C" void gdbstub_init(); +#include +#include +#include -#endif //GDBSTUB_H +#include "internal/gdbstub-cfg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void gdbstub_init(); + +//Indicates whether gdbstub will attach to these or not +//Useful for other uart libs to avoid conflicts +bool gdbstub_has_putc1_control(); +bool gdbstub_has_uart_isr_control(); + +#if GDBSTUB_REDIRECT_CONSOLE_OUTPUT +void gdbstub_set_putc1_callback(void (*callback)(char)); +#endif + +void gdbstub_write_char(char c); +void gdbstub_write(const char* buf, size_t size); + +#if GDBSTUB_CTRLC_BREAK && !GDBSTUB_FREERTOS +void gdbstub_set_uart_isr_callback(void (*callback)(void*, uint8_t), void* arg); + +//Override points for enabling tx and rx pins for uart0 +void gdbstub_hook_enable_tx_pin_uart0(uint8_t pin); +void gdbstub_hook_enable_rx_pin_uart0(uint8_t pin); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index ec051ebe31..c41e53a962 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -7,7 +7,7 @@ * License: ESPRESSIF MIT License *******************************************************************************/ -#include "gdbstub.h" +#include #include #include "ets_sys.h" #include "eagle_soc.h" diff --git a/libraries/GDBStub/src/internal/gdbstub.h b/libraries/GDBStub/src/internal/gdbstub.h deleted file mode 100644 index b87eb1e365..0000000000 --- a/libraries/GDBStub/src/internal/gdbstub.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef GDBSTUB_H -#define GDBSTUB_H - -#include -#include -#include - -#include "gdbstub-cfg.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void gdbstub_init(); - -//Indicates whether gdbstub will attach to these or not -//Useful for other uart libs to avoid conflicts -bool gdbstub_has_putc1_control(); -bool gdbstub_has_uart_isr_control(); - -#if GDBSTUB_REDIRECT_CONSOLE_OUTPUT -void gdbstub_set_putc1_callback(void (*callback)(char)); -#endif - -void gdbstub_write_char(char c); -void gdbstub_write(const char* buf, size_t size); - -#if GDBSTUB_CTRLC_BREAK && !GDBSTUB_FREERTOS -void gdbstub_set_uart_isr_callback(void (*callback)(void*, uint8_t), void* arg); - -//Override points for enabling tx and rx pins for uart0 -void gdbstub_hook_enable_tx_pin_uart0(uint8_t pin); -void gdbstub_hook_enable_rx_pin_uart0(uint8_t pin); -#endif - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file From fc7012f27cdb265dca802bf01b6808a1ca250d93 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Mon, 31 Dec 2018 11:46:37 -0800 Subject: [PATCH 09/11] Undo much of UART refactoring, set fifo IRQ to 16 Remove the refactoring of pin control and other little things not directly related to GDB processing. Should greatly reduce the diff size in uart.c. Should also remove any register value changes (intended or otherwise) introduced in the original PR from @kylefleming. Set the FIFO interrupt to 16 chars when in GDB mode, matching the latest UART configuration for highest speed. --- cores/esp8266/uart.c | 153 +++++++++++------------ libraries/GDBStub/src/internal/gdbstub.c | 8 +- 2 files changed, 80 insertions(+), 81 deletions(-) diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index 2a64dc870c..58f5a6f230 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -300,7 +300,7 @@ uart_get_rx_buffer_size(uart_t* uart) } -static void ICACHE_RAM_ATTR +void ICACHE_RAM_ATTR uart_isr(void * arg) { uart_t* uart = (uart_t*)arg; @@ -411,9 +411,9 @@ uart_do_write_char(const int uart_nr, char c) size_t uart_write_char(uart_t* uart, char c) { - if(uart == NULL || !uart->tx_enabled) { + if(uart == NULL || !uart->tx_enabled) return 0; - } + if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) { gdbstub_write_char(c); return 1; @@ -506,43 +506,6 @@ uart_get_baudrate(uart_t* uart) return uart->baud_rate; } -void uart0_enable_tx_pin(uint8_t pin) -{ - switch(pin) { - case 1: - pinMode(pin, SPECIAL); - break; - case 2: - case 15: - pinMode(pin, FUNCTION_4); - break; - } -} - -void uart0_enable_rx_pin(uint8_t pin) -{ - switch(pin) { - case 3: - pinMode(pin, SPECIAL); - break; - case 13: - pinMode(pin, FUNCTION_4); - break; - } -} - -void uart1_enable_tx_pin(uint8_t pin) -{ - if(pin == 2) { - pinMode(pin, SPECIAL); - } -} - -void uart_disable_pin(uint8_t pin) -{ - pinMode(pin, INPUT); -} - uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size) { @@ -583,16 +546,20 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx return NULL; } uart->rx_buffer = rx_buffer; - uart0_enable_rx_pin(uart->rx_pin); + pinMode(uart->rx_pin, SPECIAL); } if(uart->tx_enabled) { - if (tx_pin == 2) { - uart->tx_pin = 2; - } else { - uart->tx_pin = 1; - } - uart0_enable_tx_pin(uart->tx_pin); + if (tx_pin == 2) + { + uart->tx_pin = 2; + pinMode(uart->tx_pin, FUNCTION_4); + } + else + { + uart->tx_pin = 1; + pinMode(uart->tx_pin, FUNCTION_0); + } } else { @@ -646,14 +613,33 @@ uart_uninit(uart_t* uart) return; if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) { - uart_disable_pin(uart->tx_pin); + switch(uart->tx_pin) + { + case 1: + pinMode(1, INPUT); + break; + case 2: + pinMode(2, INPUT); + break; + case 15: + pinMode(15, INPUT); + break; + } } if(uart->rx_enabled) { free(uart->rx_buffer->buffer); free(uart->rx_buffer); if(!gdbstub_has_uart_isr_control()) { - uart_disable_pin(uart->rx_pin); + switch(uart->rx_pin) + { + case 3: + pinMode(3, INPUT); + break; + case 13: + pinMode(13, INPUT); + break; + } uart_stop_isr(uart); } } @@ -669,39 +655,46 @@ uart_swap(uart_t* uart, int tx_pin) switch(uart->uart_nr) { case UART0: - if(uart->tx_enabled) { //TX - uart_disable_pin(uart->tx_pin); - } - if(uart->rx_enabled) { //RX - uart_disable_pin(uart->rx_pin); - } - - if(((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) - || (uart->rx_pin == 3 && uart->rx_enabled)) { - if(uart->tx_enabled) { //TX + if(((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) || (uart->rx_pin == 3 && uart->rx_enabled)) + { + if(uart->tx_enabled) //TX + { + pinMode(uart->tx_pin, INPUT); uart->tx_pin = 15; } - if(uart->rx_enabled) { //RX + if(uart->rx_enabled) //RX + { + pinMode(uart->rx_pin, INPUT); uart->rx_pin = 13; } + if(uart->tx_enabled) + pinMode(uart->tx_pin, FUNCTION_4); //TX + + if(uart->rx_enabled) + pinMode(uart->rx_pin, FUNCTION_4); //RX + IOSWAP |= (1 << IOSWAPU0); - } else { - if(uart->tx_enabled) { //TX + } + else + { + if(uart->tx_enabled) //TX + { + pinMode(uart->tx_pin, INPUT); uart->tx_pin = (tx_pin == 2)?2:1; } - if(uart->rx_enabled) { //RX + if(uart->rx_enabled) //RX + { + pinMode(uart->rx_pin, INPUT); uart->rx_pin = 3; } - IOSWAP &= ~(1 << IOSWAPU0); - } + if(uart->tx_enabled) + pinMode(uart->tx_pin, (tx_pin == 2)?FUNCTION_4:SPECIAL); //TX - if(uart->tx_enabled) { //TX - uart0_enable_tx_pin(uart->tx_pin); - } - if(uart->rx_enabled) { //RX - uart0_enable_rx_pin(uart->rx_pin); - } + if(uart->rx_enabled) + pinMode(3, SPECIAL); //RX + IOSWAP &= ~(1 << IOSWAPU0); + } break; case UART1: // Currently no swap possible! See GPIO pins used by UART @@ -720,15 +713,19 @@ uart_set_tx(uart_t* uart, int tx_pin) switch(uart->uart_nr) { case UART0: - if(uart->tx_enabled) { - if (uart->tx_pin == 1 && tx_pin == 2) { - uart_disable_pin(uart->tx_pin); + if(uart->tx_enabled) + { + if (uart->tx_pin == 1 && tx_pin == 2) + { + pinMode(uart->tx_pin, INPUT); uart->tx_pin = 2; - uart0_enable_tx_pin(uart->tx_pin); - } else if (uart->tx_pin == 2 && tx_pin != 2) { - uart_disable_pin(uart->tx_pin); + pinMode(uart->tx_pin, FUNCTION_4); + } + else if (uart->tx_pin == 2 && tx_pin != 2) + { + pinMode(uart->tx_pin, INPUT); uart->tx_pin = 1; - uart0_enable_tx_pin(uart->tx_pin); + pinMode(uart->tx_pin, SPECIAL); } } diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index c41e53a962..5ae16406d2 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -9,6 +9,7 @@ #include #include +#include #include "ets_sys.h" #include "eagle_soc.h" #include "c_types.h" @@ -781,8 +782,9 @@ static void ATTR_GDBINIT configure_uart() {} static void ATTR_GDBINIT configure_uart() { #ifdef ARDUINO - uart0_enable_tx_pin(1); - uart0_enable_rx_pin(3); + // Set the UART input/output pins to TX=1, RX=3 + pinMode(3, SPECIAL); + pinMode(1, FUNCTION_0); #endif WRITE_PERI_REG(UART_CONF0(0), 0b00011100); //8N1 @@ -875,7 +877,7 @@ static void ATTR_GDBINIT install_uart_hdlr() { configure_uart(); WRITE_PERI_REG(UART_CONF1(0), - ((100 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | + ((16 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) | ((0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S) | UART_RX_TOUT_EN); From 33a21cdb7cf09eb3267b7f5b56dbf6fe359378da Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Tue, 1 Jan 2019 15:18:21 -0800 Subject: [PATCH 10/11] Add architecture comments, cleanup uart.c code Comments added to UART.c trying to explain (as best as I understand it) the changes done to support GDB and how they interact with standard operation. Fix the uart_uninit to stop the ISR and then free appropriately. Fix uart_isr_handle_data (GDB's shim for sending chars to the 8266 app) to do the exact same thing as the standard UART handler including set the overflow properly and either discard or overwrite in that case. Fix serial reception when GDB enabled by enabling the user recv ISR. Remove commented attributes from gdbstub, leftover from the move to flash. General logic cleanup per comments in the PR. --- cores/esp8266/uart.c | 78 ++++++++++++++++++++---- libraries/GDBStub/src/internal/gdbstub.c | 40 ++++++------ 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index 58f5a6f230..1e5eac7211 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -47,7 +47,29 @@ #include "user_interface.h" #include "uart_register.h" -//const char overrun_str [] PROGMEM STORE_ATTR = "uart input full!\r\n"; +/* + Some general architecture for GDB integration with the UART to enable + serial debugging. + + UART1 is transmit only and can never be used by GDB. + + When gdbstub_has_uart_isr_control() (only true in the case GDB is enabled), + UART0 needs to be under the control of the GDB stub for enable/disable/irq + (but speed, parity, etc. still alllowable). Basically, GDB needs to make + sure that UART0 is never really disabled. + + GDB sets up UART0 with a fifo and a 2-character timeout during init. This + is required to ensure that GDBStub can check every character coming in, even + if it is not read by the user app or if the commands don't hit the FIFO + interrupt level. It checks every character that comes in, and if GDB isn't + active just passes them to the user RAM FIFO unless it's a Ctrl-C (0x03). + + GDBStub doesn't care about the struct uart_*, and allocating it or freeing + it has no effect (so you can do Serial.end() and free the uart...but as + mentioned above even if you Serial.end, the actual UART0 HW will still be + kept running to enable GDB to do commands. +*/ + static int s_uart_debug_nr = UART0; @@ -250,17 +272,41 @@ uart_read(uart_t* uart, char* userbuffer, size_t usersize) return ret; } +// When GDB is running, this is called one byte at a time to stuff the user FIFO +// instead of the uart_isr...uart_rx_copy_fifo_to_buffer_unsafe() +// Since we've already read the bytes from the FIFO, can't use that +// function directly and need to implement it bytewise here static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) { uart_t* uart = (uart_t*)arg; if(uart == NULL || !uart->rx_enabled) { return; } - size_t nextPos = (uart->rx_buffer->wpos + 1) % uart->rx_buffer->size; - if(nextPos != uart->rx_buffer->rpos) { - uart->rx_buffer->buffer[uart->rx_buffer->wpos] = data; - uart->rx_buffer->wpos = nextPos; + +// Copy all the rx fifo bytes that fit into the rx buffer +// called by ISR + struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; + + size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; + if(nextPos == rx_buffer->rpos) + { + uart->rx_overrun = true; + //os_printf_plus(overrun_str); + + // a choice has to be made here, + // do we discard newest or oldest data? +#ifdef UART_DISCARD_NEWEST + // discard newest data + // Stop copying if rx buffer is full + return; +#else + // discard oldest data + if (++rx_buffer->rpos == rx_buffer->size) + rx_buffer->rpos = 0; +#endif } + rx_buffer->buffer[rx_buffer->wpos] = data; + rx_buffer->wpos = nextPos; } size_t @@ -299,7 +345,7 @@ uart_get_rx_buffer_size(uart_t* uart) return uart && uart->rx_enabled? uart->rx_buffer->size: 0; } - +// The default ISR handler called when GDB is not enabled void ICACHE_RAM_ATTR uart_isr(void * arg) { @@ -576,6 +622,7 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx uart->tx_pin = (uart->tx_enabled)?2:255; // GPIO7 as TX not possible! See GPIO pins used by UART if(uart->tx_enabled) pinMode(uart->tx_pin, SPECIAL); + break; case UART_NO: @@ -595,11 +642,11 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx USIE(uart->uart_nr) = 0; } if(uart->uart_nr == UART0) { - if(uart->rx_enabled && !gdbstub_has_uart_isr_control()) { + if(uart->rx_enabled) { uart_start_isr(uart); } if(gdbstub_has_uart_isr_control()) { - ETS_UART_INTR_ENABLE(); + ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above } } @@ -612,6 +659,8 @@ uart_uninit(uart_t* uart) if(uart == NULL) return; + uart_stop_isr(uart); + if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) { switch(uart->tx_pin) { @@ -640,7 +689,6 @@ uart_uninit(uart_t* uart) pinMode(13, INPUT); break; } - uart_stop_isr(uart); } } free(uart); @@ -852,11 +900,15 @@ uart_set_debug(int uart_nr) func = &uart_ignore_char; break; } - if(!gdbstub_has_putc1_control()) { - system_set_os_print((uint8)((uart_nr == UART0 || uart_nr == UART1)?1:0)); - ets_install_putc1((void *) func); - } else { + if(gdbstub_has_putc1_control()) { gdbstub_set_putc1_callback(func); + } else { + if (uart_nr == UART0 || uart_nr == UART1) { + system_set_os_print(1); + } else { + system_set_os_print(0); + } + ets_install_putc1((void *) func); } } diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index 5ae16406d2..cd707d278c 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -138,7 +138,7 @@ static void ATTR_GDBFN keepWDTalive() { //of the hex string, as far as the routine has read into it. Bits/4 indicates //the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much //hex chars as possible. -static long /*ATTR_GDBFN*/ gdbGetHexVal(unsigned char **ptr, int bits) { +static long gdbGetHexVal(unsigned char **ptr, int bits) { int i; int no; unsigned int v = 0; @@ -176,7 +176,7 @@ static long /*ATTR_GDBFN*/ gdbGetHexVal(unsigned char **ptr, int bits) { } //Swap an int into the form gdb wants it -static int /*ATTR_GDBFN*/ iswap(int i) { +static int iswap(int i) { return ((i >> 24) & 0xff) | (((i >> 16) & 0xff) << 8) | (((i >> 8) & 0xff) << 16) @@ -184,14 +184,14 @@ static int /*ATTR_GDBFN*/ iswap(int i) { } //Read a byte from the ESP8266 memory. -static unsigned char /*ATTR_GDBFN*/ readbyte(unsigned int p) { +static unsigned char readbyte(unsigned int p) { if (p < 0x20000000 || p >= 0x60000000) return -1; int *i = (int*)(p & ~3); return *i >> ((p & 3) * 8); } //Write a byte to the ESP8266 memory. -static void /*ATTR_GDBFN*/ writeByte(unsigned int p, unsigned char d) { +static void writeByte(unsigned int p, unsigned char d) { if (p < 0x20000000 || p >= 0x60000000) return; int *i = (int*)(p & ~3); if ((p & 3) == 0) *i = (*i & 0xffffff00) | (d << 0); @@ -201,7 +201,7 @@ static void /*ATTR_GDBFN*/ writeByte(unsigned int p, unsigned char d) { } //Returns 1 if it makes sense to write to addr p -static int /*ATTR_GDBFN*/ validWrAddr(int p) { +static int validWrAddr(int p) { return (p >= 0x3ff00000 && p < 0x40000000) || (p >= 0x40100000 && p < 0x40140000) || (p >= 0x60000000 && p < 0x60002000); @@ -217,7 +217,7 @@ static inline bool ATTR_GDBFN gdbTxFifoIsFull() { } //Receive a char from the uart. Uses polling and feeds the watchdog. -static inline int /*ATTR_GDBFN*/ gdbRecvChar() { +static inline int gdbRecvChar() { while (gdbRxFifoIsEmpty()) { keepWDTalive(); } @@ -225,7 +225,7 @@ static inline int /*ATTR_GDBFN*/ gdbRecvChar() { } //Send a char to the uart. -static void /*ATTR_GDBFN*/ gdbSendChar(char c) { +static void gdbSendChar(char c) { while (gdbTxFifoIsFull()) ; WRITE_PERI_REG(UART_FIFO(0), c); @@ -233,13 +233,13 @@ static void /*ATTR_GDBFN*/ gdbSendChar(char c) { //Send the start of a packet; reset checksum calculation. -static void /*ATTR_GDBFN*/ gdbPacketStart() { +static void gdbPacketStart() { chsum = 0; gdbSendChar('$'); } //Send a char as part of a packet -static void /*ATTR_GDBFN*/ gdbPacketChar(char c) { +static void gdbPacketChar(char c) { if (c == '#' || c == '$' || c == '}' || c == '*') { gdbSendChar('}'); chsum += '}'; @@ -250,7 +250,7 @@ static void /*ATTR_GDBFN*/ gdbPacketChar(char c) { } //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void /*ATTR_GDBFN*/ gdbPacketHex(int val, int bits) { +static void gdbPacketHex(int val, int bits) { static const char hexChars[] = "0123456789abcdef"; int i; for (i = bits; i > 0; i -= 4) { @@ -259,16 +259,16 @@ static void /*ATTR_GDBFN*/ gdbPacketHex(int val, int bits) { } //Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. -static void /*ATTR_GDBFN*/ gdbPacketSwappedHexInt(int val) { +static void gdbPacketSwappedHexInt(int val) { gdbPacketHex(iswap(val), 32); } -static void /*ATTR_GDBFN*/ gdbPacketXXXXInt() { +static void gdbPacketXXXXInt() { for (int i=0; i<8; i++) gdbPacketChar('x'); } //Finish sending a packet. -static void /*ATTR_GDBFN*/ gdbPacketEnd() { +static void gdbPacketEnd() { gdbSendChar('#'); //Ok to use packet version here since hex char can never be an //excape-requiring character @@ -276,7 +276,7 @@ static void /*ATTR_GDBFN*/ gdbPacketEnd() { } // Send a complete packet containing str -static void /*ATTR_GDBFN*/ gdbSendPacketStr(const char *c) { +static void gdbSendPacketStr(const char *c) { gdbPacketStart(); while (*c != 0) { gdbPacketChar(*c); @@ -303,13 +303,13 @@ static inline void ATTR_GDBEXTERNFN gdbSendOutputPacketChar(unsigned char c) { gdbPacketEnd(); } -static long /*ATTR_GDBFN*/ gdbGetSwappedHexInt(unsigned char **ptr) { +static long gdbGetSwappedHexInt(unsigned char **ptr) { return iswap(gdbGetHexVal(ptr, 32)); } //Send the reason execution is stopped to GDB. -static void /*ATTR_GDBFN*/ sendReason() { +static void sendReason() { static const char exceptionSignal[] = {4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; #if 0 char *reason=""; //default @@ -400,7 +400,7 @@ struct regfile { */ //Handle a command as received from GDB. -static inline int /*ATTR_GDBFN*/ gdbHandleCommand() { +static inline int gdbHandleCommand() { //Handle a command int i, j, k; unsigned char *data = cmd + 1; @@ -537,7 +537,7 @@ static inline int /*ATTR_GDBFN*/ gdbHandleCommand() { //It is not necessary for gdb to be attached for it to be paused //For example, during an exception break, the program is // paused but gdb might not be attached yet -static int /*ATTR_GDBFN*/ gdbReadCommand() { +static int gdbReadCommand() { unsigned char chsum; unsigned char sentchs[2]; size_t p; @@ -606,7 +606,7 @@ static inline void ATTR_GDBFN setaregval(int reg, unsigned int val) { } //Emulate the l32i/s32i instruction we're stopped at. -static inline void /*ATTR_GDBFN*/ emulLdSt() { +static inline void emulLdSt() { unsigned char i0 = readbyte(gdbstub_savedRegs.pc); unsigned char i1 = readbyte(gdbstub_savedRegs.pc + 1); unsigned char i2; @@ -682,7 +682,7 @@ static void __attribute__((noinline)) gdbstub_handle_debug_exception_flash() { #if GDBSTUB_BREAK_ON_EXCEPTION || GDBSTUB_CTRLC_BREAK #if !GDBSTUB_FREERTOS -static inline int /*ATTR_GDBFN*/ gdbReadCommandWithFrame(void* frame) { +static inline int gdbReadCommandWithFrame(void* frame) { //Copy registers the Xtensa HAL did save to gdbstub_savedRegs os_memcpy(&gdbstub_savedRegs, frame, 5 * 4); os_memcpy(&gdbstub_savedRegs.a[2], ((uint32_t*)frame) + 5, 14 * 4); From e72291a68d05286020d9c7cd5c66880f964ba872 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Wed, 2 Jan 2019 12:21:38 -0800 Subject: [PATCH 11/11] Also set the UART flags for HW error in GDB Ensure we also check the UART flags and set the uart status appropriately when in GDB mode. --- cores/esp8266/uart.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index 1e5eac7211..88048cd363 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -307,6 +307,17 @@ static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) } rx_buffer->buffer[rx_buffer->wpos] = data; rx_buffer->wpos = nextPos; + + // Check the UART flags and note hardware overflow/etc. + uint32_t usis = USIS(uart->uart_nr); + + if(usis & (1 << UIOF)) + uart->rx_overrun = true; + + if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO))) + uart->rx_error = true; + + USIC(uart->uart_nr) = usis; } size_t @@ -362,7 +373,7 @@ uart_isr(void * arg) if(usis & (1 << UIFF)) uart_rx_copy_fifo_to_buffer_unsafe(uart); - if((usis & (1 << UIOF)) && !uart->rx_overrun) + if(usis & (1 << UIOF)) { uart->rx_overrun = true; //os_printf_plus(overrun_str);