This patch adds a gdb stub which replaces the tiny LM32 debug monitor. It needs some more resources but has the advantage that you can directly connect with GDB. --- software/gdbstub/Makefile | 33 ++ software/gdbstub/crt0.S | 246 ++++++++++++++ software/gdbstub/gdbstub.c | 767 ++++++++++++++++++++++++++++++++++++++++++++ software/gdbstub/linker.ld | 45 +++ 4 files changed, 1091 insertions(+), 0 deletions(-) create mode 100644 software/gdbstub/Makefile create mode 100644 software/gdbstub/crt0.S create mode 100644 software/gdbstub/gdbstub.c create mode 100644 software/gdbstub/linker.ld
diff --git a/software/gdbstub/Makefile b/software/gdbstub/Makefile new file mode 100644 index 0000000..bd5c314 --- /dev/null +++ b/software/gdbstub/Makefile @@ -0,0 +1,33 @@ +MMDIR=../.. +include $(MMDIR)/software/include.mak + +OBJECTS=crt0.o gdbstub.o +SEGMENTS=-j .text -j .data -j .rodata + +# override optimization flags +CFLAGS+=-Os + +all: gdbstub.elf gdbstub.bin gdbstub.rom + +%.bin: %.elf + $(MAKE) -C $(MMDIR)/tools + $(OBJCOPY) $(SEGMENTS) -O binary $< $@ + chmod -x $@ + $(MMDIR)/tools/mkmmimg $@ write + +%.rom: %.bin + $(MAKE) -C $(MMDIR)/tools + $(MMDIR)/tools/bin2hex $< $@ 2048 + +gdbstub.elf: linker.ld $(OBJECTS) + $(LD) $(LDFLAGS) -T linker.ld -N -o $@ $(OBJECTS) + chmod -x $@ + +.PHONY: clean depend + +depend: + makedepend -Y -- $(CFLAGS) -- *.c + +clean: + rm -f *.o gdbstub.elf gdbstub.bin gdbstub.rom .*~ *~ Makefile.bak + diff --git a/software/gdbstub/crt0.S b/software/gdbstub/crt0.S new file mode 100644 index 0000000..372fb23 --- /dev/null +++ b/software/gdbstub/crt0.S @@ -0,0 +1,246 @@ +/* + * Milkymist SoC + * Copyright (c) 2010 Michael Walle + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +.section .text, "ax", @progbits +.global _start +.global clear_bss + +_start: +_reset_handler: + xor r0, r0, r0 /* clear r0 */ + wcsr IE, r0 /* disable interrupts */ + mvi ba, 0 + bi _breakpoint_handler /* just call our bp handler */ + nop + nop + nop + nop + +_breakpoint_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ba /* save pc */ + calli handle_exception + bi b_restore_and_return + +_instruction_bus_error_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ea /* save pc */ + calli handle_exception + bi e_restore_and_return + +_watchpoint_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ba /* save pc */ + calli handle_exception + bi b_restore_and_return + +_data_bus_error_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ea /* save pc */ + calli handle_exception + bi e_restore_and_return + +_divide_by_zero_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ea /* save pc */ + calli handle_exception + bi e_restore_and_return + +_interrupt_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ea /* save pc */ + calli handle_exception + bi e_restore_and_return + +_system_call_handler: + xor r0, r0, r0 /* clear r0, in case its corrupted */ + mvhi r0, hi(_debug_stack) /* temporary stack pointer */ + ori r0, r0, lo(_debug_stack) + sw (r0+0), ra /* save ra, calli overwrites it */ + calli save_all /* after this, sp is our debug stack */ + sw (sp+128), ea /* save pc */ + calli handle_exception + bi e_restore_and_return + +/* save all registers onto the stack */ +save_all: + /* save origin sp */ + addi r0, r0, -144 + /* save registers */ + /* 0 - R0 - saved below */ + sw (r0+4), r1 + sw (r0+8), r2 + sw (r0+12), r3 + sw (r0+16), r4 + sw (r0+20), r5 + sw (r0+24), r6 + sw (r0+28), r7 + sw (r0+32), r8 + sw (r0+36), r9 + sw (r0+40), r10 + sw (r0+44), r11 + sw (r0+48), r12 + sw (r0+52), r13 + sw (r0+56), r14 + sw (r0+60), r15 + sw (r0+64), r16 + sw (r0+68), r17 + sw (r0+72), r18 + sw (r0+76), r19 + sw (r0+80), r20 + sw (r0+84), r21 + sw (r0+88), r22 + sw (r0+92), r23 + sw (r0+96), r24 + sw (r0+100), r25 + sw (r0+104), r26 + sw (r0+108), r27 + sw (r0+112), sp + /* 116 - RA - saved below */ + sw (r0+120), ea + sw (r0+124), ba + /* 128 - PC - saved in handler code above */ + /* 132 - EID - saved below */ + rcsr r1, EBA + sw (r0+136), r1 + rcsr r1, DEBA + sw (r0+140), r1 + + /* work out EID from exception entry point address */ + andi r1, ra, 0xff + srui r1, r1, 5 + sw (r0+132), r1 + + /* switch sp to debug stack. We can't use mv yet, as r0 + * is not 0. */ + sw (r0+116), r0 + lw sp, (r0+116) + + /* restore r0 to 0 */ + xor r0, r0, r0 + + /* fix ra */ + lw r1, (sp+144) + sw (sp+116), r1 + + /* save r0 (hardcoded to 0) */ + sw (sp+0), r0 + + /* save pointer to registers, this is the first argument for + * handle_exception(), so save it in r1 */ + mv r1, sp + ret + +/* Restore gp registers */ +restore_gp: + lw r1, (sp+4) + lw r2, (sp+8) + lw r3, (sp+12) + lw r4, (sp+16) + lw r5, (sp+20) + lw r6, (sp+24) + lw r7, (sp+28) + lw r8, (sp+32) + lw r9, (sp+36) + lw r10, (sp+40) + lw r11, (sp+44) + lw r12, (sp+48) + lw r13, (sp+52) + lw r14, (sp+56) + lw r15, (sp+60) + lw r16, (sp+64) + lw r17, (sp+68) + lw r18, (sp+72) + lw r19, (sp+76) + lw r20, (sp+80) + lw r21, (sp+84) + lw r22, (sp+88) + lw r23, (sp+92) + lw r24, (sp+96) + lw r25, (sp+100) + lw r26, (sp+104) + lw r27, (sp+108) + ret + +/* restore registers and return from exception */ +e_restore_and_return: + /* first restore gp registers */ + calli restore_gp + lw ra, (sp+116) + lw ba, (sp+124) + lw ea, (sp+136) + wcsr EBA, ea + lw ea, (sp+140) + wcsr DEBA, ea + /* restore EA from PC */ + lw ea, (sp+128) + /* stack pointer must be restored last, in case it has been updated */ + lw sp, (sp+112) + eret + +/* restore registers and return from breakpoint */ +b_restore_and_return: + /* first restore gp registers */ + calli restore_gp + lw ra, (sp+116) + lw ea, (sp+120) + lw ba, (sp+136) + wcsr EBA, ba + lw ba, (sp+140) + wcsr DEBA, ba + /* restore BA from PC */ + lw ba, (sp+128) + /* stack pointer must be restored last, in case it has been updated */ + lw sp, (sp+112) + bret + +/* clear BSS, this is called from handle_exception() */ +clear_bss: + mvhi r1, hi(_fbss) + ori r1, r1, lo(_fbss) + mvhi r2, hi(_ebss) + ori r2, r2, lo(_ebss) +1: + be r1, r2, 2f + sw (r1+0), r0 + addi r1, r1, 4 + bi 1b +2: + ret diff --git a/software/gdbstub/gdbstub.c b/software/gdbstub/gdbstub.c new file mode 100644 index 0000000..692ff3a --- /dev/null +++ b/software/gdbstub/gdbstub.c @@ -0,0 +1,767 @@ +/* + * Milkymist SoC + * Copyright (c) 2011 Michael Walle + * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq + * Copyright (C) Linus Torvalds and Linux kernel developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#include <base/irq.h> +#include <hw/uart.h> +#include <hw/interrupts.h> +#include <hw/gpio.h> +#include <hw/sysctl.h> + +#define SUPPORT_P_CMD 1 +#define SUPPORT_X_CMD 1 +#define SUPPORT_Z_CMD 1 +#define SUPPORT_Q_CMD 1 + +/* see crt0.S */ +extern void clear_bss(void); + +/* Convert the exception identifier to a signal number. */ +static const char signals[] = { + 0, /* reset */ + 5 /* SIGTRAP */, /* breakpoint */ + 11 /* SIGSEGV */, /* instruction bus error */ + 5 /* SIGTRAP */, /* watchpoint */ + 11 /* SIGSEGV */, /* data bus error */ + 8 /* SIGFPE */, /* divide by zero */ + 2 /* SIGINT */, /* interrupt */ + 1 /* SIGHUP */, /* system call */ +}; + +/* Stringification macro */ +#define STRINGY_(x) #x +#define STRINGY(x) STRINGY_(x) + +enum lm32_regnames { + LM32_REG_R0, LM32_REG_R1, LM32_REG_R2, LM32_REG_R3, LM32_REG_R4, LM32_REG_R5, + LM32_REG_R6, LM32_REG_R7, LM32_REG_R8, LM32_REG_R9, LM32_REG_R10, + LM32_REG_R11, LM32_REG_R12, LM32_REG_R13, LM32_REG_R14, LM32_REG_R15, + LM32_REG_R16, LM32_REG_R17, LM32_REG_R18, LM32_REG_R19, LM32_REG_R20, + LM32_REG_R21, LM32_REG_R22, LM32_REG_R23, LM32_REG_R24, LM32_REG_R25, + LM32_REG_GP, LM32_REG_FP, LM32_REG_SP, LM32_REG_RA, LM32_REG_EA, LM32_REG_BA, + LM32_REG_PC, LM32_REG_EID, LM32_REG_EBA, LM32_REG_DEBA, LM32_REG_IE, NUM_REGS +}; + +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers */ +#define BUFMAX 800 + +/* I/O packet buffers */ +static char remcom_in_buffer[BUFMAX]; +static char remcom_out_buffer[BUFMAX]; + +/* Remember breakpoint and watchpoint addresses */ +#ifdef SUPPORT_Z_CMD +#define BPSMAX 4 +static unsigned int bp_address[BPSMAX]; +#define WPSMAX 4 +static unsigned int wp_address[WPSMAX]; + +/* Track breakpoints and watchpoints which are in use */ +static unsigned int bpwp_enabled; +#endif + +/* Remember if GDB was connected before */ +static char gdb_connected; + +/* Track DC register content, because register is write only */ +static unsigned int dc; + +/* + * Common helper functions + * + */ +static char *strcpy(char *dst, char *src) +{ + char *tmp = dst; + while ((*dst++ = *src++) != '\0'); + return tmp; +} + +#ifdef SUPPORT_Q_CMD +static int memcmp(const void *cs, const void *ct, size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} +#endif + +static char get_debug_char(void) +{ + while (!(irq_pending() & IRQ_UARTRX)); + irq_ack(IRQ_UARTRX); + return (char)CSR_UART_RXTX; +} + +static void put_debug_char(char c) +{ + CSR_UART_RXTX = c; + /* Blocking on UART pending bit is intended here! Have a + * look at the end of handle_exception() too. */ + while (CSR_UART_BREAK & UART_TX_PENDING); +} + +/* + * Conversion helper functions + */ + +/* For integer to ASCII conversion */ +static const char hexchars[]="0123456789abcdef"; +#define highhex(x) hexchars[(x >> 4) & 0xf] +#define lowhex(x) hexchars[x & 0xf] + +/* Convert ch from a hex digit to an int */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') { + return ch-'a'+10; + } + if (ch >= '0' && ch <= '9') { + return ch-'0'; + } + if (ch >= 'A' && ch <= 'F') { + return ch-'A'+10; + } + return -1; +} + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf ('\0'), in case of mem fault, + * return NULL. + */ +static char *mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + while (count-- > 0) + { + ch = *mem++; + *buf++ = highhex(ch); + *buf++ = lowhex(ch); + } + + *buf = '\0'; + return buf; +} + +/* + * Convert the hex array pointed to by buf into binary to be placed in mem. + * Return a pointer to the character AFTER the last byte written. + */ +static char *hex2mem(char *buf, char *mem, int count) +{ + int i; + char ch; + + for (i = 0; i < count; i++) + { + /* convert hex data to 8-bit value */ + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + /* attempt to write data to memory */ + *mem++ = ch; + } + + return mem; +} + +/* + * Copy the binary data pointed to by buf to mem and return a pointer to the + * character AFTER the last byte written $, # and 0x7d are escaped with 0x7d. + */ +#ifdef SUPPORT_X_CMD +static char *bin2mem(char *buf, char *mem, int count) +{ + int i; + char c; + + for (i = 0; i < count; i++) + { + /* Convert binary data to unsigned byte */ + c = *buf++; + if (c == 0x7d) { + c = *buf++ ^ 0x20; + } + /* Attempt to write value to memory */ + *mem++ = c; + } + + return mem; +} +#endif + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hex2int(char **ptr, unsigned int *value) +{ + int num_chars = 0; + int hex_value; + + *value = 0; + + while(**ptr) + { + hex_value = hex(**ptr); + if (hex_value < 0) { + break; + } + + *value = (*value << 4) | hex_value; + num_chars++; + + (*ptr)++; + } + + return num_chars; +} + +/* Scan for the sequence $<data>#<checksum> */ +static char *get_packet(void) +{ + char *buffer = remcom_in_buffer; + unsigned char checksum; + unsigned char xmitcsum; + int count; + char ch; + + while (1) { + /* wait around for the start character, ignore all other characters */ + while ((ch = get_debug_char()) != '$'); + + retry: + checksum = 0; + xmitcsum = -1; + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX - 1) { + ch = get_debug_char(); + if (ch == '$') { + goto retry; + } + if (ch == '#') { + break; + } + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + ch = get_debug_char(); + xmitcsum = hex(ch) << 4; + ch = get_debug_char(); + xmitcsum += hex(ch); + + if (checksum != xmitcsum) { + /* failed checksum */ + put_debug_char('-'); + } else { + /* successful transfer */ + put_debug_char('+'); + + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + put_debug_char(buffer[0]); + put_debug_char(buffer[1]); + + return &buffer[3]; + } + return &buffer[0]; + } + } + } +} + +/* Send the packet in buffer. */ +static void put_packet(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* $<packet info>#<checksum> */ + do { + put_debug_char('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + put_debug_char(ch); + checksum += ch; + count += 1; + } + + put_debug_char('#'); + put_debug_char(highhex(checksum)); + put_debug_char(lowhex(checksum)); + } while (get_debug_char() != '+'); +} + +static void flush_cache(void) +{ + __asm__ __volatile__("wcsr ICC, r0"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("wcsr DCC, r0"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); + __asm__ __volatile__("nop"); +} + +#ifdef SUPPORT_Z_CMD +static int set_hw_breakpoint(unsigned int address, int kind) +{ + int cfg; + char num_bps; + + __asm__ __volatile__("rcsr %0, CFG" : "=r" (cfg)); + num_bps = (cfg >> 18) & 0xf; + + /* find a free breakpoint register */ + if (num_bps > 0 && (bpwp_enabled & 0x01) == 0) { + __asm__ __volatile__("wcsr BP0, %0" : : "r" (address | 1)); + bpwp_enabled |= 0x01; + bp_address[0] = address; + return 1; + } + if (num_bps > 1 && (bpwp_enabled & 0x02) == 0) { + __asm__ __volatile__("wcsr BP1, %0" : : "r" (address | 1)); + bpwp_enabled |= 0x02; + bp_address[1] = address; + return 1; + } + if (num_bps > 2 && (bpwp_enabled & 0x04) == 0) { + __asm__ __volatile__("wcsr BP2, %0" : : "r" (address | 1)); + bpwp_enabled |= 0x04; + bp_address[2] = address; + return 1; + } + if (num_bps > 3 && (bpwp_enabled & 0x08) == 0) { + __asm__ __volatile__("wcsr BP3, %0" : : "r" (address | 1)); + bpwp_enabled |= 0x08; + bp_address[3] = address; + return 1; + } + + return 0; +} + +static int set_hw_watchpoint(unsigned int address, int kind, char mode) +{ + int i; + int bit = 0x10; + int dc_mode = mode; + int dc_mask = 0x0c; + int cfg; + char num_wps; + + __asm__ __volatile__("rcsr %0, CFG" : "=r" (cfg)); + num_wps = (cfg >> 22) & 0xf; + + /* find a free watchpoint register */ + for (i = 0; i < num_wps; i++) { + if (!(bpwp_enabled & bit)) break; + bit <<= 1; + dc_mode <<= 2; + dc_mask <<= 2; + } + + if (i == num_wps) { + return 0; + } + + bpwp_enabled |= bit; + wp_address[i] = address; + dc &= ~dc_mask; + dc |= dc_mode; + __asm__ __volatile__("wcsr WP0, %0" : : "r" (address)); + __asm__ __volatile__("wcsr DC, %0" : : "r" (dc)); + + return 1; +} + +static int disable_hw_breakpoint(unsigned int address, int kind) +{ + int cfg; + + __asm__ __volatile__("rcsr %0, CFG" : "=r" (cfg)); + + if ((bpwp_enabled & 0x01) && bp_address[0] == address) { + __asm__ __volatile__("wcsr BP0, r0"); + bpwp_enabled &= ~0x01; + return 1; + } + if ((bpwp_enabled & 0x02) && bp_address[1] == address) { + __asm__ __volatile__("wcsr BP1, r0"); + bpwp_enabled &= ~0x02; + return 1; + } + if ((bpwp_enabled & 0x04) && bp_address[2] == address) { + __asm__ __volatile__("wcsr BP2, r0"); + bpwp_enabled &= ~0x04; + return 1; + } + if ((bpwp_enabled & 0x08) && bp_address[3] == address) { + __asm__ __volatile__("wcsr BP3, r0"); + bpwp_enabled &= ~0x08; + return 1; + } + + return 0; +} + +static int disable_hw_watchpoint(unsigned int address, int kind, char mode) +{ + int i; + int bit = 0x10; + int dc_mode = mode; + int dc_mask = 0x0c; + + for (i = 0; i < 4; i++) { + if (bpwp_enabled & bit && wp_address[i] == address + && (dc & dc_mask) == dc_mode) { + bpwp_enabled &= ~bit; + dc &= ~dc_mask; + __asm__ __volatile__("wcsr DC, %0" : : "r" (dc)); + return 1; + } + bit <<= 1; + dc_mode <<= 2; + dc_mask <<= 2; + } + + return 0; +} +#endif + +static void cmd_status(unsigned int *registers) +{ + char *ptr = remcom_out_buffer; + int sigval; + + /* convert an exception ID to a signal number */ + sigval = signals[registers[LM32_REG_EID] & 0x7]; + + *ptr++ = 'T'; + *ptr++ = highhex(sigval); + *ptr++ = lowhex(sigval); + *ptr++ = highhex(LM32_REG_PC); + *ptr++ = lowhex(LM32_REG_PC); + *ptr++ = ':'; + ptr = mem2hex((char *)&(registers[LM32_REG_PC]), ptr, 4); + *ptr++ = ';'; + *ptr++ = highhex(LM32_REG_SP); + *ptr++ = lowhex(LM32_REG_SP); + *ptr++ = ':'; + ptr = mem2hex((char *)&(registers[LM32_REG_SP]), ptr, 4); + *ptr++ = ';'; + *ptr++ = '\0'; +} + +static void cmd_getregs(unsigned int *registers) +{ + mem2hex((char *)registers, remcom_out_buffer, NUM_REGS * 4); +} + +static void cmd_setregs(unsigned int *registers) +{ + hex2mem(&remcom_in_buffer[1], (char *)registers, NUM_REGS * 4); + strcpy(remcom_out_buffer, "OK"); +} + +static void cmd_mem_read(void) +{ + char *ptr = &remcom_in_buffer[1]; + unsigned int length; + unsigned int addr; + + /* try to read %x,%x */ + if (hex2int(&ptr, &addr) > 0 && *ptr++ == ',' && hex2int(&ptr, &length) > 0 + && length < (sizeof(remcom_out_buffer) / 2)) { + if (mem2hex((char *)addr, remcom_out_buffer, length) == NULL) { + strcpy(remcom_out_buffer, "E14"); + } + } else { + strcpy(remcom_out_buffer, "E22"); + } +} + +static void cmd_mem_write(int binary) +{ + char *ptr = &remcom_in_buffer[1]; + unsigned int length; + unsigned int addr; + + /* try to read '%x,%x:' */ + if (hex2int(&ptr, &addr) > 0 && *ptr++ == ',' + && hex2int(&ptr, &length) > 0 && *ptr++ == ':') { +#ifdef SUPPORT_X_CMD + if (binary) { + bin2mem(ptr, (char *)addr, length); + } else { + hex2mem(ptr, (char *)addr, length); + } +#else + hex2mem(ptr, (char *)addr, length); + (void)binary; +#endif + strcpy(remcom_out_buffer, "OK"); + } else { + strcpy(remcom_out_buffer, "E22"); + } +} + +static void remove_all_break(void) +{ + __asm__ __volatile__("wcsr DC, r0"); + __asm__ __volatile__("wcsr BP0, r0"); + __asm__ __volatile__("wcsr BP1, r0"); + __asm__ __volatile__("wcsr BP2, r0"); + __asm__ __volatile__("wcsr BP3, r0"); +} + +static void cmd_detachkill(void) +{ + remove_all_break(); + gdb_connected = 0; + + if (remcom_in_buffer[0] == 'D') { + strcpy(remcom_out_buffer, "OK"); + put_packet(remcom_out_buffer); + } +} + +static void cmd_continuestep(unsigned int *registers) +{ + char *ptr = &remcom_in_buffer[1]; + unsigned int addr; + + if (hex2int(&ptr, &addr) > 0) { + registers[LM32_REG_PC] = addr; + } + + if (remcom_in_buffer[0] == 's') { + /* singlestepping */ + __asm__ __volatile__("wcsr DC, %0" : : "r" (dc | 1)); + } +} + +#ifdef SUPPORT_Z_CMD +static void cmd_break(void) +{ + char *ptr = &remcom_in_buffer[2]; + unsigned int addr; + unsigned int kind; + int err; + char mode; + + if (*ptr++ == ',' && hex2int(&ptr, &addr) > 0 + && *ptr++ == ',' && hex2int(&ptr, &kind) > 0) + { + switch (remcom_in_buffer[1]) { + case '1': /* h/w breakpoint */ + if (remcom_in_buffer[0] == 'Z') { + err = set_hw_breakpoint(addr, kind); + } else { + err = disable_hw_breakpoint(addr, kind); + } + break; + case '2': /* write watchpoint */ + mode = 2 << 2; + goto do_set_clear_hw_watchpoint; + case '3': /* read watchpoint */ + mode = 1 << 2; + goto do_set_clear_hw_watchpoint; + case '4': /* access watchpoint */ + mode = 3 << 2; +do_set_clear_hw_watchpoint: + if (remcom_in_buffer[0] == 'Z') { + err = set_hw_watchpoint(addr, kind, mode); + } else { + err = disable_hw_watchpoint(addr, kind, mode); + } + break; + default: + return; + } + if (err > 0) { + strcpy(remcom_out_buffer, "OK"); + } else { + strcpy(remcom_out_buffer, "E28"); + } + } else { + strcpy(remcom_out_buffer, "E22"); + } +} +#endif + +static void cmd_reset(void) +{ + CSR_SYSTEM_ID = 1; +} + +#ifdef SUPPORT_P_CMD +static void cmd_reg_get(unsigned int *registers) +{ + unsigned int reg; + char *ptr = &remcom_in_buffer[1]; + + if (hex2int(&ptr, ®) > 0) { + mem2hex((char *)®isters[reg], remcom_out_buffer, 4); + } else { + strcpy(remcom_out_buffer, "E22"); + } +} + +static void cmd_reg_set(unsigned int *registers) +{ + unsigned int reg; + char *ptr = &remcom_in_buffer[1]; + + if (hex2int(&ptr, ®) > 0 && *ptr++ == '=') { + hex2mem(ptr, (char *)®isters[reg], 4); + strcpy(remcom_out_buffer, "OK"); + } else { + strcpy(remcom_out_buffer, "E22"); + } +} +#endif + +#ifdef SUPPORT_Q_CMD +static void cmd_query(void) +{ + if (memcmp(&remcom_in_buffer[1], "Supported", 9) == 0) { + strcpy(remcom_out_buffer, "PacketSize=" STRINGY(BUFMAX)); + } +} +#endif + +/* + * This function does all command procesing for interfacing to gdb. The error + * codes we return are errno numbers. + */ +void handle_exception(unsigned int *registers) +{ + int irq; + + /* clear BSS there was a board reset */ + if (!CSR_DBG_SCRATCHPAD) { + CSR_DBG_SCRATCHPAD = 1; + clear_bss(); + } + + /* wait until TX transaction is finished */ + while (CSR_UART_BREAK & UART_TX_PENDING); + + /* remember if irq was set */ + irq = irq_pending() & IRQ_UARTTX; + + /* reply to host that an exception has occured */ + if (gdb_connected) { + cmd_status(registers); + put_packet(remcom_out_buffer); + } + + while (1) { + remcom_out_buffer[0] = '\0'; + get_packet(); + gdb_connected = 1; + + switch (remcom_in_buffer[0]) + { + case '?': /* return last signal */ + cmd_status(registers); + break; + case 'g': /* return the value of the CPU registers */ + cmd_getregs(registers); + break; + case 'G': /* set the value of the CPU registers - return OK */ + cmd_setregs(registers); + break; + case 'm': /* read memory */ + cmd_mem_read(); + break; + case 'M': /* write memory */ + cmd_mem_write(0); + break; +#ifdef SUPPORT_X_CMD + case 'X': /* write memory (binary) */ + cmd_mem_write(1); + break; +#endif +#ifdef SUPPORT_P_CMD + case 'p': /* return the value of the specified register */ + cmd_reg_get(registers); + break; + case 'P': /* set the specified register to the given value */ + cmd_reg_set(registers); + break; +#endif + case 'D': /* detach */ + case 'k': /* kill */ + cmd_detachkill(); + goto out; + case 'c': /* continue */ + case 's': /* single step */ + cmd_continuestep(registers); + goto out; +#ifdef SUPPORT_Z_CMD + case 'z': /* remove breakpoint/watchpoint */ + case 'Z': /* insert breakpoint/watchpoint */ + cmd_break(); + break; +#endif + case 'r': /* reset */ + case 'R': /* restart */ + cmd_reset(); + break; +#ifdef SUPPORT_Q_CMD + case 'q': /* general query */ + cmd_query(); + break; + } +#endif + + /* reply to the request */ + put_packet(remcom_out_buffer); + } + +out: + flush_cache(); + + /* ack TX IRQ only if it wasn't set before */ + if (!irq) { + irq_ack(IRQ_UARTTX); + } + + /* reenable break */ + CSR_UART_BREAK = UART_BREAK_EN; +} diff --git a/software/gdbstub/linker.ld b/software/gdbstub/linker.ld new file mode 100644 index 0000000..b05f882 --- /dev/null +++ b/software/gdbstub/linker.ld @@ -0,0 +1,45 @@ +OUTPUT_FORMAT("elf32-lm32") +ENTRY(_start) + +__DYNAMIC = 0; + +MEMORY { + rom : ORIGIN = 0x10000000, LENGTH = 0x00001800 /* 6k */ + ram : ORIGIN = 0x10001800, LENGTH = 0x00000800 /* 2k */ +} + +SECTIONS +{ + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > rom + + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > rom + + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + _end = .; + } > ram +} + +PROVIDE(_debug_stack = ORIGIN(ram) + LENGTH(ram) - 4); -- 1.7.2.3 _______________________________________________ http://lists.milkymist.org/listinfo.cgi/devel-milkymist.org IRC: #milkymist@Freenode Twitter: www.twitter.com/milkymistvj Ideas? http://milkymist.uservoice.com
