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, &reg) > 0) {
+        mem2hex((char *)&registers[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, &reg) > 0 && *ptr++ == '=') {
+        hex2mem(ptr, (char *)&registers[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

Reply via email to