mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-17 12:02:15 +03:00
GDB support w/new toolchain and UART driver (#5559)
* Add full gdb support with uart/Serial integration * Fix GDB merge errors * 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. * Fix .iram.literal to come before .iram.text for GDB * 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. * Remove LWIP2 builder commit * 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 <GDBStub.h> * 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. * 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. * 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. * 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.
This commit is contained in:
committed by
Develo
parent
4657666319
commit
bff3a6d963
@ -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")));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,6 +272,54 @@ 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
uart_resize_rx_buffer(uart_t* uart, size_t new_size)
|
||||
{
|
||||
@ -286,7 +356,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)
|
||||
{
|
||||
@ -303,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);
|
||||
@ -321,6 +391,11 @@ 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
|
||||
@ -351,6 +426,11 @@ 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;
|
||||
@ -358,8 +438,6 @@ uart_stop_isr(uart_t* uart)
|
||||
ETS_UART_INTR_ATTACH(NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Reference for uart_tx_fifo_available() and uart_tx_fifo_full():
|
||||
-Espressif Techinical Reference doc, chapter 11.3.7
|
||||
@ -393,6 +471,10 @@ uart_write_char(uart_t* uart, char c)
|
||||
if(uart == NULL || !uart->tx_enabled)
|
||||
return 0;
|
||||
|
||||
if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) {
|
||||
gdbstub_write_char(c);
|
||||
return 1;
|
||||
}
|
||||
uart_do_write_char(uart->uart_nr, c);
|
||||
return 1;
|
||||
}
|
||||
@ -403,6 +485,11 @@ uart_write(uart_t* uart, const char* buf, size_t size)
|
||||
if(uart == NULL || !uart->tx_enabled)
|
||||
return 0;
|
||||
|
||||
if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) {
|
||||
gdbstub_write(buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ret = size;
|
||||
const int uart_nr = uart->uart_nr;
|
||||
while (size--)
|
||||
@ -412,7 +499,6 @@ uart_write(uart_t* uart, const char* buf, size_t size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t
|
||||
uart_tx_free(uart_t* uart)
|
||||
{
|
||||
@ -452,8 +538,10 @@ uart_flush(uart_t* uart)
|
||||
if(uart->tx_enabled)
|
||||
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
|
||||
@ -490,7 +578,9 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx
|
||||
{
|
||||
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;
|
||||
@ -555,12 +645,21 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx
|
||||
|
||||
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) {
|
||||
uart_start_isr(uart);
|
||||
}
|
||||
if(gdbstub_has_uart_isr_control()) {
|
||||
ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above
|
||||
}
|
||||
}
|
||||
|
||||
return uart;
|
||||
}
|
||||
@ -573,34 +672,35 @@ uart_uninit(uart_t* uart)
|
||||
|
||||
uart_stop_isr(uart);
|
||||
|
||||
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)) {
|
||||
switch(uart->tx_pin)
|
||||
{
|
||||
case 1:
|
||||
pinMode(1, INPUT);
|
||||
break;
|
||||
case 2:
|
||||
pinMode(2, INPUT);
|
||||
break;
|
||||
case 15:
|
||||
pinMode(15, INPUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uart_stop_isr(uart);
|
||||
if(uart->rx_enabled) {
|
||||
free(uart->rx_buffer->buffer);
|
||||
free(uart->rx_buffer);
|
||||
if(!gdbstub_has_uart_isr_control()) {
|
||||
switch(uart->rx_pin)
|
||||
{
|
||||
case 3:
|
||||
pinMode(3, INPUT);
|
||||
break;
|
||||
case 13:
|
||||
pinMode(13, INPUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(uart);
|
||||
}
|
||||
@ -654,7 +754,6 @@ uart_swap(uart_t* uart, int tx_pin)
|
||||
|
||||
IOSWAP &= ~(1 << IOSWAPU0);
|
||||
}
|
||||
|
||||
break;
|
||||
case UART1:
|
||||
// Currently no swap possible! See GPIO pins used by UART
|
||||
@ -798,22 +897,30 @@ 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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
Reference in New Issue
Block a user