diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/configure.tgt mb-gdb/gdb/configure.tgt --- gdb/gdb/configure.tgt 2009-08-06 03:28:38.000000000 -0700 +++ mb-gdb/gdb/configure.tgt 2009-09-21 12:12:32.000000000 -0700 @@ -313,6 +313,19 @@ mep-*-*) # No sim needed. Target uses SID. ;; +microblaze*-linux-*) + # Target: Xilinx MicroBlaze running Linux + gdb_target_obs="microblaze-tdep.o microblaze-linux-tdep.o microblaze-rom.o \ + monitor.o dsrec.o solib.o solib-svr4.o corelow.o \ + symfile-mem.o" + gdb_sim=../sim/microblaze/libsim.a + ;; +microblaze*-xilinx-*) + # Target: Xilinx MicroBlaze running standalone + gdb_target_obs="microblaze-tdep.o microblaze-rom.o monitor.o dsrec.o" + gdb_sim=../sim/microblaze/libsim.a + ;; + mips*-sgi-irix5*) # Target: MIPS SGI running Irix 5 gdb_target_obs="mips-tdep.o mips-irix-tdep.o solib.o solib-irix.o" diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/doc/gdb.texinfo mb-gdb/gdb/doc/gdb.texinfo --- gdb/gdb/doc/gdb.texinfo 2009-09-06 10:14:43.000000000 -0700 +++ mb-gdb/gdb/doc/gdb.texinfo 2009-09-23 11:02:38.000000000 -0700 @@ -517,6 +517,9 @@ Tensilica, Inc.@: contributed support fo who have worked on the Xtensa port of @value{GDBN} in the past include Steve Tjiang, John Newlin, and Scott Foehner. +Michael Eager and staff of Xilinx, Inc., contributed support for the +Xilinx MicroBlaze architecture. + @node Sample Session @chapter A Sample @value{GDBN} Session @@ -16427,6 +16430,7 @@ acceptable commands. * ARM:: ARM RDI * M32R/D:: Renesas M32R/D * M68K:: Motorola M68K +* MicroBlaze:: Xilinx MicroBlaze * MIPS Embedded:: MIPS Embedded * OpenRISC 1000:: OpenRisc 1000 * PA:: HP PA Embedded @@ -16678,6 +16682,38 @@ dBUG ROM monitor for Motorola ColdFire. @end table +@node MicroBlaze +@subsection MicroBlaze +@cindex Xilinx MicroBlaze +@cindex XMD, Xilinx Microprocessor Debugger + +The MicroBlaze is a soft-core processor supported on various Xilinx +FPGAs, such as Spartan or Virtex series. Boards with these processors +usually have JTAG ports which connect to a host system running the Xilinx +Embedded Development Kit (EDK) or Software Development Kit (SDK). +This host system is used to download the configuration bitstream to +the target FPGA. The Xilinx Microprocessor Debugger (XMD) program +communicates with the target board using the JTAG interface and +presents a @code{gdbserver} interface to the board. By default +@code{xmd} uses port @code{1234}. (While it is possible to change +this default port, it requires the use of undocumented @code{xmd} +commands. Contact Xilinx support if you need to do this.) + +Use these GDB commands to connect to the MicroBlaze target processor. + +@table @code +@item target remote :1234 +Use this command to connect to the target if you are running @value{GDBN} +on the same system as @code{xmd}. + +@item target remote XMD-HOST:1234 +Use this command to connect to the target if it is connected to @code{xmd} +running on a different system named @code{XMD-HOST}. + +@item load +Use this command to download a program to the MicroBlaze target. +@end table + @node MIPS Embedded @subsection MIPS Embedded diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/MAINTAINERS mb-gdb/gdb/MAINTAINERS --- gdb/gdb/MAINTAINERS 2009-08-06 03:28:37.000000000 -0700 +++ mb-gdb/gdb/MAINTAINERS 2009-09-06 12:26:05.000000000 -0700 @@ -290,6 +290,10 @@ the native maintainer when resolving ABI mep --target=mep-elf ,-Werror Kevin Buettner kevinb@redhat.com + microblaze --target=microblaze-xilinx-elf ,-Werror + --target=microblaze-linux-gnu ,-Werror + Michael Eager eager@eagercon.com + mips --target=mips-elf ,-Werror mn10300 --target=mn10300-elf broken diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/Makefile.in mb-gdb/gdb/Makefile.in --- gdb/gdb/Makefile.in 2009-08-27 09:08:34.000000000 -0700 +++ mb-gdb/gdb/Makefile.in 2009-08-27 12:20:01.000000000 -0700 @@ -498,6 +498,7 @@ ALL_TARGET_OBS = \ m68kbsd-tdep.o m68klinux-tdep.o m68k-tdep.o \ m88k-tdep.o \ mep-tdep.o \ + microblaze-tdep.o microblaze-linux-tdep.o \ mips-irix-tdep.o mips-linux-tdep.o \ mipsnbsd-tdep.o mips-tdep.o \ mn10300-linux-tdep.o mn10300-tdep.o \ @@ -1390,6 +1391,7 @@ ALLDEPFILES = \ m68kbsd-nat.c m68kbsd-tdep.c \ m68klinux-nat.c m68klinux-tdep.c \ m88k-tdep.c m88kbsd-nat.c \ + microblaze-tdep.c microblaze-linux-tdep.c \ mingw-hdep.c \ mips-linux-nat.c mips-linux-tdep.c \ mips-irix-tdep.c \ diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/microblaze-linux-tdep.c mb-gdb/gdb/microblaze-linux-tdep.c --- gdb/gdb/microblaze-linux-tdep.c 1969-12-31 16:00:00.000000000 -0800 +++ mb-gdb/gdb/microblaze-linux-tdep.c 2009-09-21 12:23:33.000000000 -0700 @@ -0,0 +1,139 @@ +/* Target-dependent code for Xilinx MicroBlaze. + Copyright 2009 Free Software Foundation, Inc. + + 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; either version 2 of the License, or + (at your option) any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "symtab.h" +#include "target.h" +#include "gdbcore.h" +#include "gdbcmd.h" +#include "symfile.h" +#include "objfiles.h" +#include "regcache.h" +#include "value.h" +#include "osabi.h" +#include "regset.h" +#include "solib-svr4.h" +#include "microblaze-tdep.h" +#include "trad-frame.h" +#include "frame-unwind.h" +#include "tramp-frame.h" + + +static int +microblaze_linux_memory_remove_breakpoint (struct bp_target_info *bp_tgt) +{ + CORE_ADDR addr = bp_tgt->placed_address; + const gdb_byte *bp; + int val; + int bplen; + gdb_byte old_contents[BREAKPOINT_MAX]; + + /* Determine appropriate breakpoint contents and size for this address. */ + bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen); + if (bp == NULL) + error (_("Software breakpoints not implemented for this target.")); + + val = target_read_memory (addr, old_contents, bplen); + + /* If our breakpoint is no longer at the address, this means that the + program modified the code on us, so it is wrong to put back the + old value. */ + if ((val == 0) && (memcmp (bp, old_contents, bplen) == 0)) + val = target_write_memory (addr, bp_tgt->shadow_contents, bplen); + + return val; +} + +static void +microblaze_linux_sigtramp_cache (struct frame_info *next_frame, + struct trad_frame_cache *this_cache, + CORE_ADDR func, LONGEST offset, + int bias) +{ + CORE_ADDR base; + CORE_ADDR gpregs; + int regnum; + struct gdbarch *gdbarch = get_frame_arch (next_frame); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + base = frame_unwind_register_unsigned (next_frame, SP_REGNUM); + if ((bias > 0) && (frame_pc_unwind (next_frame) != func)) + /* See below, some signal trampolines increment the stack as their + first instruction, need to compensate for that. */ + base -= bias; + + /* Find the address of the register buffer. */ + gpregs = base + offset; + + /* Registers saved on stack. */ + for (regnum = 0; regnum < MICROBLAZE_BTR_REGNUM; regnum++) + trad_frame_set_reg_addr (this_cache, regnum, gpregs + regnum * REGISTER_SIZE); + trad_frame_set_id (this_cache, frame_id_build (base, func)); +} + + +static void +microblaze_linux_sighandler_cache_init (const struct tramp_frame *self, + struct frame_info *next_frame, + struct trad_frame_cache *this_cache, + CORE_ADDR func) +{ + microblaze_linux_sigtramp_cache (next_frame, this_cache, func, + 0 /* Offset to ucontext_t. */ + + 24 /* Offset to .reg. */, + 0); +} + +static struct tramp_frame microblaze_linux_sighandler_tramp_frame = { + SIGTRAMP_FRAME, + 4, + { + { 0x31800077, -1 }, /* addik R12,R0,119. */ + { 0xb9cc0008, -1 }, /* brki R14,8. */ + { TRAMP_SENTINEL_INSN }, + }, + microblaze_linux_sighandler_cache_init +}; + + +static void +microblaze_linux_init_abi (struct gdbarch_info info, + struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + set_gdbarch_memory_remove_breakpoint (gdbarch, + microblaze_linux_memory_remove_breakpoint); + + /* Shared library handling. */ + set_solib_svr4_fetch_link_map_offsets (gdbarch, + svr4_ilp32_fetch_link_map_offsets); + + /* Trampolines. */ + tramp_frame_prepend_unwinder (gdbarch, + µblaze_linux_sighandler_tramp_frame); +} + +void +_initialize_microblaze_linux_tdep (void) +{ + gdbarch_register_osabi (bfd_arch_microblaze, 0, + GDB_OSABI_LINUX, microblaze_linux_init_abi); +} diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/microblaze-rom.c mb-gdb/gdb/microblaze-rom.c --- gdb/gdb/microblaze-rom.c 1969-12-31 16:00:00.000000000 -0800 +++ mb-gdb/gdb/microblaze-rom.c 2009-09-21 12:23:30.000000000 -0700 @@ -0,0 +1,190 @@ +/* Remote debugging interface to Xilinx MicroBlaze. + Copyright 2009 Free Software Foundation, Inc. + + 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; either version 2 of the License, or + (at your option) any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "defs.h" +#include "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "gdb_string.h" +#include "serial.h" +#include "regcache.h" + +static char *picobug_inits[] = +{"\r", NULL}; + +static struct target_ops picobug_ops; +static struct monitor_ops picobug_cmds; + +/* Picobug only supports a subset of registers from MCore. In reality, + it doesn't support ss1, either. */ +static char *picobug_regnames[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + "psr", "vbr", "epsr", "fpsr", "epc", "fpc", 0, "ss1", + "ss2", "ss3", "ss4", 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + "pc" }; + + + +void +picobug_open (char *args, int from_tty) +{ + monitor_open (args, &picobug_cmds, from_tty); +} +/* We choose to write our own dumpregs routine, since the output of + the register dumping is rather difficult to encapsulate in a + regexp: + +picobug> rd + pc 2f00031e epc 2f00031e fpc 00000000 + psr 80000101 epsr 80000101 fpsr 00000000 +ss0-ss4 bad0beef 00000000 00000000 00000000 00000000 vbr 30005c00 + r0-r7 2f0fff4c 00000090 00000001 00000002 00000003 00000004 00000005 00000006 + r8-r15 2f0fff64 00000000 00000000 00000000 00000000 00000000 00000000 2f00031e +*/ + +static int +picobug_dumpregs (struct regcache *regcache) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + char buf[1024]; + int resp_len; + char *p; + + /* Send the dump register command to the monitor and + get the reply. */ + monitor_printf (picobug_cmds.dump_registers); + resp_len = monitor_expect_prompt (buf, sizeof (buf)); + + p = strtok (buf, " \t\r\n"); + while (p) + { + if (strchr (p, '-')) + { + /* got a range. either r0-r7, r8-r15 or ss0-ss4. */ + if (strncmp (p, "r0", 2) == 0 || strncmp (p, "r8", 2) == 0) + { + int rn = (p[1] == '0' ? 0 : 8); + int i = 0; + + /* Get the next 8 values and record them. */ + while (i < 8) + { + p = strtok (NULL, " \t\r\n"); + if (p) + monitor_supply_register (regcache, rn + i, p); + i++; + } + } + else if (strncmp (p, "ss", 2) == 0) + { + /* get the next five values, ignoring the first. */ + int rn; + p = strtok (NULL, " \t\r\n"); + for (rn = 39; rn < 43; rn++) + { + p = strtok (NULL, " \t\r\n"); + if (p) + monitor_supply_register (regcache, rn, p); + } + } + else + { + break; + } + } + else + { + /* Simple register type, paired. */ + char *name = p; + int i; + + /* Get and record value. */ + p = strtok (NULL, " \t\r\n"); + if (p) + { + for (i = 0; i < gdbarch_num_regs (gdbarch); i++) + { + if (picobug_regnames[i] + && (strcmp (picobug_regnames[i], name) == 0)) + break; + } + + if (i <= gdbarch_num_regs (gdbarch)) + monitor_supply_register (regcache, i, p); + } + } + p = strtok (NULL, " \t\r\n"); + } + + return 0; +} + +static void +init_picobug_cmds (void) +{ + picobug_cmds.flags = MO_GETMEM_NEEDS_RANGE | MO_CLR_BREAK_USES_ADDR + | MO_PRINT_PROGRAM_OUTPUT; + + picobug_cmds.init = picobug_inits; /* Init strings */ + picobug_cmds.cont = "g\n"; /* continue command */ + picobug_cmds.step = "s\n"; /* single step */ + picobug_cmds.set_break = "br %x\n"; /* set a breakpoint */ + picobug_cmds.clr_break = "nobr %x\n"; /* clear a breakpoint */ + picobug_cmds.clr_all_break = "nobr\n"; /* clear all breakpoints */ + picobug_cmds.setmem.cmdb = "mm %x %x ;b\n"; /* setmem.cmdb (addr, value) */ + picobug_cmds.setmem.cmdw = "mm %x %x ;h\n"; /* setmem.cmdw (addr, value) */ + picobug_cmds.setmem.cmdl = "mm %x %x ;w\n"; /* setmem.cmdl (addr, value) */ + picobug_cmds.getmem.cmdb = "md %x %x\n"; /* getmem.cmdb (start addr, + end addr) */ + picobug_cmds.getmem.resp_delim = ":"; /* getmem.resp_delim */ + picobug_cmds.setreg.cmd = "rm %s %x\n"; /* setreg.cmd (name, value) */ + picobug_cmds.getreg.cmd = "rd %s\n"; /* getreg.cmd (name) */ + picobug_cmds.getreg.resp_delim = ":"; /* getreg.resp_delim */ + picobug_cmds.dump_registers = "rd\n"; /* dump_registers */ + picobug_cmds.dumpregs = picobug_dumpregs; /* dump registers parser */ + picobug_cmds.load = "lo\n"; /* download command */ + picobug_cmds.prompt = "picobug> "; /* monitor command prompt */ + picobug_cmds.line_term = "\n"; /* end-of-line terminator */ + picobug_cmds.target = &picobug_ops; /* target operations */ + picobug_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + picobug_cmds.regnames = picobug_regnames; /* registers names */ + picobug_cmds.num_breakpoints = 20; /* number of breakpoints */ + picobug_cmds.magic = MONITOR_OPS_MAGIC; /* magic */ +} + +void +_initialize_picobug_rom () +{ + int i; + + /* Initialize m32r RevC monitor target. */ + init_picobug_cmds (); + init_monitor_ops (&picobug_ops); + picobug_ops.to_shortname = "picobug"; + picobug_ops.to_longname = "picobug monitor"; + picobug_ops.to_doc = "Debug via the picobug monitor.\n\ +Specify the serial device it is connected to (e.g. /dev/ttya)."; + picobug_ops.to_open = picobug_open; + + add_target (&picobug_ops); +} diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/microblaze-tdep.c mb-gdb/gdb/microblaze-tdep.c --- gdb/gdb/microblaze-tdep.c 1969-12-31 16:00:00.000000000 -0800 +++ mb-gdb/gdb/microblaze-tdep.c 2009-09-23 10:27:08.000000000 -0700 @@ -0,0 +1,895 @@ +/* Target-dependent code for Xilinx MicroBlaze. + Copyright 2009 Free Software Foundation, Inc. + + 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; either version 2 of the License, or + (at your option) any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "defs.h" +#include "arch-utils.h" +#include "dis-asm.h" +#include "frame.h" +#include "trad-frame.h" +#include "symtab.h" +#include "value.h" +#include "gdbcmd.h" +#include "breakpoint.h" +#include "inferior.h" +#include "regcache.h" +#include "target.h" +#include "frame.h" +#include "frame-base.h" +#include "frame-unwind.h" +#include "dwarf2-frame.h" +#include "osabi.h" + +#include "gdb_assert.h" +#include "gdb_string.h" +#include "target-descriptions.h" + + +/* FIXME - this shouldn't be here */ +#include "../opcodes/microblaze-opcm.h" + +#include "microblaze-tdep.h" + +extern enum microblaze_instr get_insn_microblaze (long, bfd_boolean *, + enum microblaze_instr_type *, short *); +extern unsigned long microblaze_get_target_address (long, bfd_boolean, int, long, long, + long, bfd_boolean *, bfd_boolean *); + + +/* The registers of the Xilinx microblaze processor */ + +static const char *microblaze_register_names[] = +{ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "rpc", "rmsr", "rear", "resr", "rfsr", "rbtr", + "rpvr0", "rpvr1", "rpvr2", "rpvr3", "rpvr4", "rpvr5", "rpvr6", + "rpvr7", "rpvr8", "rpvr9", "rpvr10", "rpvr11", + "redr", "rpid", "rzpr", "rtlbx", "rtlbsx", "rtlblo", "rtlbhi" +}; + +#define MICROBLAZE_NUM_REGS ARRAY_SIZE (microblaze_register_names) + +#define mb_error(msg) internal_error (__FILE__, __LINE__, _(msg)) + +extern enum microblaze_instr microblaze_decode_insn (long, int *, int *, + int *, int *); + +static int microblaze_debug = 0; + +static void +microblaze_dump_insn (char *commnt, CORE_ADDR pc, int insn) +{ + if (microblaze_debug) + { + printf_filtered ("MICROBLAZE: %s %08x %08x ", + commnt, (unsigned int) pc, (unsigned int) insn); + /* print_insn_microblaze (pc, &tm_print_insn_info); */ + printf_filtered ("\n"); + } +} +#define microblaze_insn_debug(args) \ + { if (microblaze_debug) printf_filtered args; } +#define mb_warn(msg) \ + if (microblaze_debug) printf ("mb_warning: %s:%d %s\n", __FILE__, __LINE__, _(msg)) + + +/* Return the name of register REGNUM. */ + +static const char * +microblaze_register_name (struct gdbarch *gdbarch, int regnum) +{ + if ((regnum >= 0) && (regnum < MICROBLAZE_NUM_REGS)) + return microblaze_register_names[regnum]; + return NULL; +} + +static struct type * +microblaze_register_type (struct gdbarch *gdbarch, int regnum) +{ + if (regnum == MICROBLAZE_SP_REGNUM) + return builtin_type (gdbarch)->builtin_data_ptr; + + if (regnum == MICROBLAZE_PC_REGNUM) + return builtin_type (gdbarch)->builtin_func_ptr; + + return builtin_type (gdbarch)->builtin_int; +} + + +/* Fetch the instruction at PC. */ + +unsigned long +microblaze_fetch_instruction (CORE_ADDR pc) +{ + gdb_byte buf[4]; + unsigned long insn; + int i; + + /* If we can't read the instruction at PC, return zero. */ + if (target_read_memory (pc, buf, sizeof (buf))) + return 0; + + insn = 0; + for (i = 0; i < sizeof (buf); i++) + insn = (insn << 8) | buf[i]; + return insn; +} + + +static CORE_ADDR +microblaze_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, + CORE_ADDR funcaddr, + struct value **args, int nargs, + struct type *value_type, + CORE_ADDR *real_pc, CORE_ADDR *bp_addr, + struct regcache *regcache) +{ + mb_warn ("push_dummy_code not implemented"); + return sp; +} + + +static CORE_ADDR +microblaze_push_dummy_call (struct gdbarch *gdbarch, struct value *function, + struct regcache *regcache, CORE_ADDR bp_addr, + int nargs, struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + mb_warn ("store_arguments not implemented"); + return sp; +} + +/* Return a pointer to a string of bytes that encode a breakpoint + instruction, store the length of the string in *LEN. */ + +static const gdb_byte * +microblaze_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pc, int *len) +{ + static gdb_byte break_insn[] = BREAKPOINT; + + *len = sizeof (break_insn); + return break_insn; +} + +/* Allocate and initialize a frame cache. */ + +static struct microblaze_frame_cache * +microblaze_alloc_frame_cache (void) +{ + struct microblaze_frame_cache *cache; + int i; + + cache = FRAME_OBSTACK_ZALLOC (struct microblaze_frame_cache); + + /* Base address. */ + cache->base = 0; + cache->pc = 0; + + /* Frameless until proven otherwise. */ + cache->frameless_p = 1; + + return cache; +} + +/* Function prologues on the Xilinx microblaze processors consist of: + + - adjustments to the stack pointer (r1) (addi r1, r1, imm) + - making a copy of r1 into another register (a "frame" pointer) + (add r?, r1, r0) + - store word/multiples that use r1 or the frame pointer as the + base address (swi r?, r1, imm OR swi r?, fp, imm) + + Note that microblaze really doesn't have a real frame pointer. + Instead, the compiler may copy the SP into a register (usually + r19) to act as an arg pointer. For our target-dependent purposes, + the frame info's "frame" member will be the beginning of the + frame. The SP could, in fact, point below this. + + The prologue ends when an instruction fails to meet either of + these criteria. */ + +/* Analyze the prologue to determine where registers are saved, + the end of the prologue, etc. Return the address of the first line + of "real" code (i.e., the end of the prologue). */ + +CORE_ADDR +microblaze_analyze_prologue (CORE_ADDR pc, CORE_ADDR current_pc, + struct microblaze_frame_cache *cache) +{ + char *name; + CORE_ADDR func_addr, func_end, addr, stop, prologue_end_addr = 0; + unsigned long insn; + int rn, rd, ra, rb, imm; + enum microblaze_instr op; + int flags = 0; + bfd_boolean save_hidden_pointer_found = FALSE; + bfd_boolean non_stack_instruction_found = FALSE; + + /* printf("microblaze_analyze_prologue (pc = 0x%8.8x, " + "current_pc = 0x%8.8x, cache = 0x%8.8x)\n", + (int) pc, (int) current_pc, (int) cache); */ + + /* Find the start of this function. */ + find_pc_partial_function (pc, &name, &func_addr, &func_end); + if (func_addr < pc) + pc = func_addr; + + if (current_pc < pc) + return current_pc; + + /* Initialize info about frame */ + cache->framesize = 0; + cache->fp_regnum = MICROBLAZE_SP_REGNUM; + cache->frameless_p = 1; + + /* Start decoding the prologue. We start by checking two special cases: + + 1. We're about to return + 2. We're at the first insn of the prologue. + + If we're about to return, our frame has already been deallocated. + If we are stopped at the first instruction of a prologue, + then our frame has not yet been set up. */ + + /* Get the first insn from memory */ + + insn = microblaze_fetch_instruction (pc); + op = microblaze_decode_insn (insn, &rd, &ra, &rb, &imm); + + if (IS_RETURN(op)) + return pc; + + /* Start at beginning of function and analyze until we get to the + current pc, or the end of the function, whichever is first. */ + stop = (current_pc < func_end ? current_pc : func_end); + + microblaze_insn_debug(("MICROBLAZE: Scanning prologue: name=%s, " + "func_addr=0x%x, stop=0x%x\n", + name, (unsigned int) func_addr, + (unsigned int) stop)); + + for (addr = func_addr; addr < stop; addr += INST_WORD_SIZE) + { + insn = microblaze_fetch_instruction (addr); + op = microblaze_decode_insn (insn, &rd, &ra, &rb, &imm); + microblaze_dump_insn ("got 2: ", addr, insn); + + /* This code is very sensitive to what functions are present in the + prologue. It assumes that the (addi, addik, swi, sw) can be the + only instructions in the prologue. */ + if (IS_UPDATE_SP(op, rd, ra)) + { + microblaze_insn_debug ( + ("MICROBLAZE: got addi r1,r1,%d; contnuing\n", imm)); + if (cache->framesize) + break; /* break if framesize already computed */ + cache->framesize = -1 * imm; /* stack grows towards low memory */ + cache->frameless_p = 0; /* Frame found. */ + save_hidden_pointer_found = FALSE; + non_stack_instruction_found = FALSE; + continue; + } + else if (IS_SPILL_SP(op, rd, ra)) + { + /* Spill stack pointer */ + cache->register_offsets[rd] = imm; /* SP spilled before updating */ + + microblaze_insn_debug ( + ("MICROBLAZE: swi r1 r1 %d, continuing\n", imm)); + save_hidden_pointer_found = FALSE; + if (!cache->framesize) + non_stack_instruction_found = FALSE; + continue; + } + else if (IS_SPILL_REG(op, rd, ra)) + { + /* Spill register */ + cache->register_offsets[rd] = imm - cache->framesize; + /* reg spilled after updating */ + + microblaze_insn_debug ( + ("MICROBLAZE: swi %d r1 %d, continuing\n", rd, imm)); + save_hidden_pointer_found = FALSE; + if (!cache->framesize) + non_stack_instruction_found = FALSE; + continue; + } + else if (IS_ALSO_SPILL_REG(op, rd, ra, rb)) + { + /* Spill register */ + cache->register_offsets[rd] = 0 - cache->framesize; + /* reg spilled after updating */ + + microblaze_insn_debug ( + ("MICROBLAZE: sw %d r0 r1, continuing\n", rd)); + save_hidden_pointer_found = FALSE; + if (!cache->framesize) + non_stack_instruction_found = FALSE; + continue; + } + else if (IS_SETUP_FP(op, ra, rb)) + { + /* We have a frame pointer. Note + the register which is acting as the frame pointer. */ + flags |= MY_FRAME_IN_FP; + flags &= ~MY_FRAME_IN_SP; + cache->fp_regnum = rd; + microblaze_insn_debug ( + ("MICROBLAZE: Found a frame pointer: r%d\n", + cache->fp_regnum)); + save_hidden_pointer_found = FALSE; + if (!cache->framesize) + non_stack_instruction_found = FALSE; + continue; + } + else if (IS_SPILL_REG_FP(op, rd, ra, cache->fp_regnum)) + { + cache->register_offsets[rd] = imm - cache->framesize; + /* reg spilled after updating */ + + microblaze_insn_debug ( + ("MICROBLAZE: swi %d %d %d, continuing\n", rd, ra, imm)); + save_hidden_pointer_found = FALSE; + if (!cache->framesize) + non_stack_instruction_found = FALSE; + continue; + } + else if (IS_SAVE_HIDDEN_PTR(op, rd, ra, rb)) + { + /* If the first argument is a hidden pointer to the area where the + return structure is to be saved, then it is saved as part of the + prologue */ + + microblaze_insn_debug ( + ("MICROBLAZE: add %d %d %d, continuing\n", rd, ra, rb)); + save_hidden_pointer_found = TRUE; + if (!cache->framesize) + non_stack_instruction_found = FALSE; + continue; + } + + /* As a result of the modification in the next step where we continue + to analyze the prologue till we reach a control flow instruction, + we need another variable to store when exactly a non-stack + instruction was encountered, which is the current definition + of a prologue. */ + if (!non_stack_instruction_found) + prologue_end_addr = addr; + non_stack_instruction_found = TRUE; + + /* When optimizations are enabled, it is not guaranteed that prologue + instructions are not mixed in with other instructions from the + program. Some programs show this behavior at -O2. This can be + avoided by adding -fno-schedule-insns2 switch as of now (edk 8.1) + In such cases, we scan the function until we see the first control + instruction. */ + + { + unsigned op = (unsigned)insn >> 26; + + if (!(op == 0x26 || op == 0x27 || op == 0x2d || op == 0x2e + || op == 0x2f)) + /* continue if not control flow (branch, return) */ + continue; + else if (op == 0x2c) + continue; /* continue if imm */ + } + + /* This is not a prologue insn, so stop here. */ + microblaze_insn_debug ( + ("microblaze: insn is not a prologue insn -- ending scan\n")); + break; + } + + microblaze_insn_debug (("microblaze: done analyzing prologue\n")); + microblaze_insn_debug (("microblaze: prologue end = 0x%x\n", (int) addr)); + + /* If the last instruction was an add rd, r5, r0 then don't count it as + part of the prologue */ + if (save_hidden_pointer_found) + prologue_end_addr -= INST_WORD_SIZE; + + return prologue_end_addr; +} + +static CORE_ADDR +microblaze_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + gdb_byte buf[4]; + CORE_ADDR pc; + + frame_unwind_register (next_frame, MICROBLAZE_PC_REGNUM, buf); + pc = extract_typed_address (buf, builtin_type (gdbarch)->builtin_func_ptr); + /* For sentinel frame, return address is actual PC. For other frames, + return address is pc+8. This is a workaround because gcc does not + generate correct return address in CIE. */ + if (frame_relative_level (next_frame) >= 0) + pc += 8; + return pc; +} + +/* Return PC of first real instruction of the function starting at + START_PC. */ + +CORE_ADDR +microblaze_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc) +{ + struct symtab_and_line sal; + CORE_ADDR func_start, func_end, ostart_pc; + struct microblaze_frame_cache cache; + + /* This is the preferred method, find the end of the prologue by + using the debugging information. Debugging info does not always + give the right answer since parameters are stored on stack after this. + Always analyze the prologue. */ + if (find_pc_partial_function (start_pc, NULL, &func_start, &func_end)) + { + sal = find_pc_line (func_start, 0); + + if (sal.end < func_end + && start_pc <= sal.end) + start_pc = sal.end; + } + + ostart_pc = microblaze_analyze_prologue (func_start, 0xffffffffUL, &cache); + + if (ostart_pc > start_pc) + return ostart_pc; + return start_pc; +} + +/* Normal frames. */ + +struct microblaze_frame_cache * +microblaze_frame_cache (struct frame_info *next_frame, void **this_cache) +{ + struct microblaze_frame_cache *cache; + struct gdbarch *gdbarch = get_frame_arch (next_frame); + CORE_ADDR func, pc, fp; + int rn; + + if (*this_cache) + return *this_cache; + + cache = microblaze_alloc_frame_cache (); + *this_cache = cache; + cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); + + /* Clear offsets to saved regs in frame. */ + for (rn = 0; rn < gdbarch_num_regs (gdbarch); rn++) + cache->register_offsets[rn] = -1; + + func = get_frame_func (next_frame); + + cache->pc = get_frame_address_in_block (next_frame); + + return cache; +} + +static void +microblaze_frame_this_id (struct frame_info *next_frame, void **this_cache, + struct frame_id *this_id) +{ + struct microblaze_frame_cache *cache = + microblaze_frame_cache (next_frame, this_cache); + + /* This marks the outermost frame. */ + if (cache->base == 0) + return; + + (*this_id) = frame_id_build (cache->base, cache->pc); +} + +static struct value * +microblaze_frame_prev_register (struct frame_info *this_frame, + void **this_cache, int regnum) +{ + struct microblaze_frame_cache *cache = + microblaze_frame_cache (this_frame, this_cache); + + /* FIXME: Rework this code and add comments. */ + if (cache->frameless_p) + { + if (regnum == MICROBLAZE_PC_REGNUM) + regnum = 15; + if (regnum == MICROBLAZE_SP_REGNUM) + regnum = 1; + return trad_frame_get_prev_register (this_frame, cache->saved_regs, regnum); + } + else + return trad_frame_get_prev_register (this_frame, cache->saved_regs, + regnum); + +} + +static const struct frame_unwind microblaze_frame_unwind = +{ + NORMAL_FRAME, + microblaze_frame_this_id, + microblaze_frame_prev_register, + NULL, + default_frame_sniffer +}; + +static CORE_ADDR +microblaze_frame_base_address (struct frame_info *next_frame, void **this_cache) +{ + struct microblaze_frame_cache *cache = + microblaze_frame_cache (next_frame, this_cache); + + return cache->base; +} + +static const struct frame_base microblaze_frame_base = +{ + µblaze_frame_unwind, + microblaze_frame_base_address, + microblaze_frame_base_address, + microblaze_frame_base_address +}; + +/* Extract from an array REGBUF containing the (raw) register state, a + function return value of TYPE, and copy that into VALBUF. */ +static void +microblaze_extract_return_value (struct type *type, struct regcache *regcache, + gdb_byte *valbuf) +{ + gdb_byte buf[8]; + + /* Copy the return value (starting) in RETVAL_REGNUM to VALBUF. */ + switch (TYPE_LENGTH(type)) + { + case 1: /* return last byte in the register */ + regcache_cooked_read (regcache, MICROBLAZE_RETVAL_REGNUM, buf); + memcpy(valbuf, buf + REGISTER_SIZE - 1, 1); + return; + case 2: /* return last 2 bytes in register */ + memcpy(valbuf, buf + REGISTER_SIZE - 2, 2); + return; + case 4: /* for sizes 4 or 8, copy the required length */ + case 8: + regcache_cooked_read (regcache, MICROBLAZE_RETVAL_REGNUM, buf); + regcache_cooked_read (regcache, MICROBLAZE_RETVAL_REGNUM+1, buf+4); + memcpy (valbuf, buf, TYPE_LENGTH (type)); + return; + default: + printf_filtered("Fatal error: unsupported return value size " + "requested (%s @ %d)\n", __FILE__, __LINE__); + } +} + +/* Store the return value in VALBUF (of type TYPE) where the caller + expects to see it. + + Integers are stored in r3. + + Values less than 32 bits (short, boolean) are stored in r2, right + justified and sign or zero extended. FIXME + + Longs are stored in r3 (most significant word) and r4 (least + significant word). + + Small structures are always returned on stack. +*/ + +static void +microblaze_store_return_value (struct type *type, struct regcache *regcache, + const gdb_byte *valbuf) +{ + int len = TYPE_LENGTH (type); + gdb_byte buf[8]; + + memset (buf, 0, sizeof(buf)); + + /* Integral and pointer return values. */ + + if (len > 4) + { + gdb_assert (len == 8); + memcpy (buf, valbuf, 8); + regcache_cooked_write (regcache, MICROBLAZE_RETVAL_REGNUM+1, buf + 4); + } + else + { + /* ??? Do we need to do any sign-extension here? */ + memcpy (buf + 4 - len, valbuf, len); + } + regcache_cooked_write (regcache, MICROBLAZE_RETVAL_REGNUM, buf); +} + +static enum return_value_convention +microblaze_return_value (struct gdbarch *gdbarch, struct type *func_type, + struct type *type, struct regcache *regcache, + gdb_byte *readbuf, const gdb_byte *writebuf) +{ + if (readbuf) + microblaze_extract_return_value (type, regcache, readbuf); + if (writebuf) + microblaze_store_return_value (type, regcache, writebuf); + + return RETURN_VALUE_REGISTER_CONVENTION; +} + +static int +microblaze_stabs_argument_has_addr (struct gdbarch *gdbarch, struct type *type) +{ + return (TYPE_LENGTH (type) == 16); /* FIXME */ +} + +int +microblaze_can_use_hardware_watchpoints (enum bptype type, int len, int ot) +{ + int count = 0; + + switch (type) + { + case bp_hardware_breakpoint: + count = 2; + break; + case bp_read_watchpoint: + count = 1; + break; + case bp_hardware_watchpoint: + count = 1; + break; + default: + count = 0; + } + return count; +} + +/* Simulate single-step calls, since microblaze does not support + single-stepping */ + +/* single_step() is called just before we want to resume the inferior, + if we want to single-step it but there is no hardware or kernel + single-step support. We find all the possible targets of the + coming instruction and breakpoint them. + + single_step is also called just after the inferior stops. If we had + set up a simulated single-step, we undo our damage. */ + +int +microblaze_software_single_step (struct frame_info *frame) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + struct regcache *regcache = get_current_regcache (); + + /* Save the address and the values of the next_pc and the target */ + static struct sstep_breaks + { + CORE_ADDR address; + bfd_boolean valid; + } stepbreaks[2]; + + CORE_ADDR pc; + long insn; + enum microblaze_instr minstr; + bfd_boolean isunsignednum; + enum microblaze_instr_type insn_type; + short delay_slots; + int imm; + bfd_boolean immfound = FALSE; + + /* Set a breakpoint at the next instruction */ + /* If the current instruction is an imm, set it at the inst after */ + /* If the instruction has a delay slot, skip the delay slot */ + pc = get_frame_register_unsigned (frame, MICROBLAZE_PC_REGNUM); + insn = microblaze_fetch_instruction (pc); + minstr = get_insn_microblaze (insn, &isunsignednum, &insn_type, + &delay_slots); + if (insn_type == immediate_inst) + { + int rd, ra, rb; + immfound = TRUE; + minstr = microblaze_decode_insn (insn, &rd, &ra, &rb, &imm); + pc = pc + INST_WORD_SIZE; + insn = microblaze_fetch_instruction (pc); + minstr = get_insn_microblaze (insn, &isunsignednum, &insn_type, + &delay_slots); + } + stepbreaks[0].address = pc + (delay_slots * INST_WORD_SIZE) + INST_WORD_SIZE; + if (insn_type != return_inst) + { + stepbreaks[0].valid = TRUE; + } + else + { + stepbreaks[0].valid = FALSE; + } + + /* Now check for branch or return instructions */ + if (insn_type == branch_inst || insn_type == return_inst) + { + int limm; + int lrd, lra, lrb; + ULONGEST ra, rb; + bfd_boolean targetvalid; + bfd_boolean unconditionalbranch; + microblaze_decode_insn(insn, &lrd, &lra, &lrb, &limm); + if (lra >= 0 && lra < gdbarch_num_regs (gdbarch)) + regcache_cooked_read_unsigned (regcache, lra, &ra); + else + ra = 0; + if (lrb >= 0 && lrb < gdbarch_num_regs (gdbarch)) + regcache_cooked_read_unsigned (regcache, lrb, &rb); + else + rb = 0; + stepbreaks[1].address = + microblaze_get_target_address (insn, immfound, imm, pc, + ra, rb, &targetvalid, + &unconditionalbranch); + if (unconditionalbranch) + /* This is a unconditional branch: will not come to the next address */ + stepbreaks[0].valid = FALSE; + if (targetvalid + && (stepbreaks[0].valid == FALSE + || (stepbreaks[0].address != stepbreaks[1].address)) + && (stepbreaks[1].address != pc)) + { + stepbreaks[1].valid = TRUE; + } + else + { + stepbreaks[1].valid = FALSE; + } + } + else + { + stepbreaks[1].valid = FALSE; + } + + /* Insert the breakpoints */ + if (stepbreaks[0].valid) + insert_single_step_breakpoint (gdbarch, stepbreaks[0].address); + if (stepbreaks[1].valid) + insert_single_step_breakpoint (gdbarch, stepbreaks[1].address); + + return 1; +} + +static void +microblaze_write_pc (struct regcache *regcache, CORE_ADDR pc) +{ + regcache_cooked_write_unsigned (regcache, MICROBLAZE_PC_REGNUM, pc); +} + +static int dwarf2_to_reg_map[78] = +{ 0 /* r0 */, 1 /* r1 */, 2 /* r2 */, 3 /* r3 */, /* 0- 3 */ + 4 /* r4 */, 5 /* r5 */, 6 /* r6 */, 7 /* r7 */, /* 4- 7 */ + 8 /* r8 */, 9 /* r9 */, 10 /* r10 */, 11 /* r11 */, /* 8-11 */ + 12 /* r12 */, 13 /* r13 */, 14 /* r14 */, 15 /* r15 */, /* 12-15 */ + 16 /* r16 */, 17 /* r17 */, 18 /* r18 */, 19 /* r19 */, /* 16-19 */ + 20 /* r20 */, 21 /* r21 */, 22 /* r22 */, 23 /* r23 */, /* 20-23 */ + 24 /* r24 */, 25 /* r25 */, 26 /* r26 */, 27 /* r27 */, /* 24-25 */ + 28 /* r28 */, 29 /* r29 */, 30 /* r30 */, 31 /* r31 */, /* 28-31 */ + -1 /* $f0 */, -1 /* $f1 */, -1 /* $f2 */, -1 /* $f3 */, /* 32-35 */ + -1 /* $f4 */, -1 /* $f5 */, -1 /* $f6 */, -1 /* $f7 */, /* 36-39 */ + -1 /* $f8 */, -1 /* $f9 */, -1 /* $f10 */, -1 /* $f11 */, /* 40-43 */ + -1 /* $f12 */, -1 /* $f13 */, -1 /* $f14 */, -1 /* $f15 */, /* 44-47 */ + -1 /* $f16 */, -1 /* $f17 */, -1 /* $f18 */, -1 /* $f19 */, /* 48-51 */ + -1 /* $f20 */, -1 /* $f21 */, -1 /* $f22 */, -1 /* $f23 */, /* 52-55 */ + -1 /* $f24 */, -1 /* $f25 */, -1 /* $f26 */, -1 /* $f27 */, /* 56-59 */ + -1 /* $f28 */, -1 /* $f29 */, -1 /* $f30 */, -1 /* $f31 */, /* 60-63 */ + -1 /* hi */, -1 /* lo */, -1 /* accum*/, 33 /* rmsr */, /* 64-67 */ + -1 /* $fcc1*/, -1 /* $fcc2*/, -1 /* $fcc3*/, -1 /* $fcc4*/, /* 68-71 */ + -1 /* $fcc5*/, -1 /* $fcc6*/, -1 /* $fcc7*/, -1 /* $ap */, /* 72-75 */ + -1 /* $rap */, -1 /* $frp */ /* 76-77 */ +}; + +static int +microblaze_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int reg) +{ + return dwarf2_to_reg_map[reg]; +} + +static struct gdbarch * +microblaze_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch_tdep *tdep; + struct gdbarch *gdbarch; + + /* If there is already a candidate, use it. */ + arches = gdbarch_list_lookup_by_info (arches, &info); + if (arches != NULL) + return arches->gdbarch; + + /* Allocate space for the new architecture. */ + tdep = XMALLOC (struct gdbarch_tdep); + gdbarch = gdbarch_alloc (&info, tdep); + + set_gdbarch_long_double_bit (gdbarch, 128); + /* set_gdbarch_long_double_format (gdbarch, &floatformat_microblaze_quad); */ + + set_gdbarch_num_regs (gdbarch, MICROBLAZE_NUM_REGS); + set_gdbarch_register_name (gdbarch, microblaze_register_name); + set_gdbarch_register_type (gdbarch, microblaze_register_type); + + /* Register numbers of various important registers. */ + set_gdbarch_sp_regnum (gdbarch, MICROBLAZE_SP_REGNUM); /* $sp */ + set_gdbarch_pc_regnum (gdbarch, MICROBLAZE_PC_REGNUM); /* $pc */ + + /* Map Dwarf2 registers to GDB registers. */ + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, microblaze_dwarf2_reg_to_regnum); + + /* Call dummy code. */ + set_gdbarch_call_dummy_location (gdbarch, ON_STACK); + set_gdbarch_push_dummy_code (gdbarch, microblaze_push_dummy_code); + set_gdbarch_push_dummy_call (gdbarch, microblaze_push_dummy_call); + + set_gdbarch_return_value (gdbarch, microblaze_return_value); + set_gdbarch_stabs_argument_has_addr + (gdbarch, microblaze_stabs_argument_has_addr); + + set_gdbarch_skip_prologue (gdbarch, microblaze_skip_prologue); + + /* Stack grows downward. */ + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + + set_gdbarch_breakpoint_from_pc (gdbarch, microblaze_breakpoint_from_pc); + + set_gdbarch_frame_args_skip (gdbarch, 8); + + set_gdbarch_print_insn (gdbarch, print_insn_microblaze); + + set_gdbarch_software_single_step (gdbarch, microblaze_software_single_step); + set_gdbarch_write_pc (gdbarch, microblaze_write_pc); + + set_gdbarch_unwind_pc (gdbarch, microblaze_unwind_pc); + + frame_base_set_default (gdbarch, µblaze_frame_base); + + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch); + + /* Unwind the frame. */ + dwarf2_append_unwinders (gdbarch); + frame_unwind_append_unwinder (gdbarch, µblaze_frame_unwind); + frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer); + + return gdbarch; +} + +int +microblaze_frame_num_args_p (struct gdbarch *gdbarch) +{ + return 0; +} + +/* Provide a prototype to silence -Wmissing-prototypes. */ +void _initialize_microblaze_tdep (void); + +void +_initialize_microblaze_tdep (void) +{ + register_gdbarch_init (bfd_arch_microblaze, microblaze_gdbarch_init); + + /* Debug this files internals. */ + add_setshow_zinteger_cmd ("microblaze", class_maintenance, + µblaze_debug, _("\ +Set microblaze debugging."), _("\ +Show microblaze debugging."), _("\ +When non-zero, microblaze specific debugging is enabled."), + NULL, + NULL, + &setdebuglist, &showdebuglist); + +} diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/microblaze-tdep.h mb-gdb/gdb/microblaze-tdep.h --- gdb/gdb/microblaze-tdep.h 1969-12-31 16:00:00.000000000 -0800 +++ mb-gdb/gdb/microblaze-tdep.h 2009-09-21 14:05:50.000000000 -0700 @@ -0,0 +1,197 @@ +/* Target-dependent code for Xilinx MicroBlaze. + Copyright 2009 Free Software Foundation, Inc. + + This file is part of GDB. + + 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; either version 2 of the License, or + (at your option) any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef MICROBLAZE_TDEP_H +#define MICROBLAZE_TDEP_H 1 + +#undef MICROBLAZE_DEBUG +#ifdef MICROBLAZE_DEBUG +int microblaze_debug = 1; +#endif + +/* Microblaze architecture-specific information. */ + +struct gdbarch_tdep +{ +}; + +struct microblaze_frame_cache +{ + /* Base address. */ + CORE_ADDR base; + CORE_ADDR pc; + + /* Do we have a frame? */ + int frameless_p; + + /* Frame size. */ + int framesize; + + /* Frame register. */ + int fp_regnum; + + /* Offsets to saved registers. */ + int register_offsets[57]; /* Must match MICROBLAZE_NUM_REGS. */ + + /* Table of saved registers. */ + struct trad_frame_saved_reg *saved_regs; +}; + +/* Register numbers. */ +enum microblaze_regnum +{ + MICROBLAZE_R0_REGNUM, + MICROBLAZE_R1_REGNUM, MICROBLAZE_SP_REGNUM = MICROBLAZE_R1_REGNUM, + MICROBLAZE_R2_REGNUM, + MICROBLAZE_R3_REGNUM, MICROBLAZE_RETVAL_REGNUM = MICROBLAZE_R3_REGNUM, + MICROBLAZE_R4_REGNUM, + MICROBLAZE_R5_REGNUM, + MICROBLAZE_R6_REGNUM, + MICROBLAZE_R7_REGNUM, + MICROBLAZE_R8_REGNUM, + MICROBLAZE_R9_REGNUM, + MICROBLAZE_R10_REGNUM, + MICROBLAZE_R11_REGNUM, + MICROBLAZE_R12_REGNUM, + MICROBLAZE_R13_REGNUM, + MICROBLAZE_R14_REGNUM, + MICROBLAZE_R15_REGNUM, + MICROBLAZE_R16_REGNUM, + MICROBLAZE_R17_REGNUM, + MICROBLAZE_R18_REGNUM, + MICROBLAZE_R19_REGNUM, + MICROBLAZE_R20_REGNUM, + MICROBLAZE_R21_REGNUM, + MICROBLAZE_R22_REGNUM, + MICROBLAZE_R23_REGNUM, + MICROBLAZE_R24_REGNUM, + MICROBLAZE_R25_REGNUM, + MICROBLAZE_R26_REGNUM, + MICROBLAZE_R27_REGNUM, + MICROBLAZE_R28_REGNUM, + MICROBLAZE_R29_REGNUM, + MICROBLAZE_R30_REGNUM, + MICROBLAZE_R31_REGNUM, + MICROBLAZE_PC_REGNUM, + MICROBLAZE_MSR_REGNUM, + MICROBLAZE_EAR_REGNUM, + MICROBLAZE_ESR_REGNUM, + MICROBLAZE_FSR_REGNUM, + MICROBLAZE_BTR_REGNUM, + MICROBLAZE_PVR0_REGNUM, + MICROBLAZE_PVR1_REGNUM, + MICROBLAZE_PVR2_REGNUM, + MICROBLAZE_PVR3_REGNUM, + MICROBLAZE_PVR4_REGNUM, + MICROBLAZE_PVR5_REGNUM, + MICROBLAZE_PVR6_REGNUM, + MICROBLAZE_PVR7_REGNUM, + MICROBLAZE_PVR8_REGNUM, + MICROBLAZE_PVR9_REGNUM, + MICROBLAZE_PVR10_REGNUM, + MICROBLAZE_PVR11_REGNUM, + MICROBLAZE_REDR_REGNUM, + MICROBLAZE_RPID_REGNUM, + MICROBLAZE_RZPR_REGNUM, + MICROBLAZE_RTLBX_REGNUM, + MICROBLAZE_RTLBSX_REGNUM, + MICROBLAZE_RTLBLO_REGNUM, + MICROBLAZE_RTLBHI_REGNUM +}; + +#define RWSDP_REGNUM 13 +#define LINK_REGNUM 15 +#define PR_REGNUM 15 +#define FIRST_ARGREG 5 +#define LAST_ARGREG 10 +#define RETVAL_REGNUM 3 + +/* All registers are 32 bits. */ +#define REGISTER_SIZE 4 + +/* The base of the current frame is actually in the stack pointer. + This happens when there is no frame pointer (microblaze ABI does not + require a frame pointer) or when we're stopped in the prologue or + epilogue itself. In these cases, microblaze_analyze_prologue will need + to update fi->frame before returning or analyzing the register + save instructions. */ +#define MY_FRAME_IN_SP 0x1 + +/* The base of the current frame is in a frame pointer register. + This register is noted in frame_extra_info->fp_regnum. + + Note that the existance of an FP might also indicate that the + function has called alloca. */ +#define MY_FRAME_IN_FP 0x2 + +/* This flag is set to indicate that this frame is the top-most + frame. This tells frame chain not to bother trying to unwind + beyond this frame. */ +#define NO_MORE_FRAMES 0x4 + + +/* Instruction macros used for analyzing the prologue. */ +/* This set of instruction macros need to be changed whenever the + prologue generated by the compiler could have more instructions or + different type of instructions. + This set also needs to be verified if it is complete. */ +#define IS_RETURN(op) (op == rtsd || op == rtid) +#define IS_UPDATE_SP(op, rd, ra) \ + ((op == addik || op == addi) && rd == REG_SP && ra == REG_SP) +#define IS_SPILL_SP(op, rd, ra) \ + ((op == swi || op == sw) && rd == REG_SP && ra == REG_SP) +#define IS_SPILL_REG(op, rd, ra) \ + ((op == swi || op == sw) && rd != REG_SP && ra == REG_SP) +/* The base of the current frame is actually in the stack pointer. + This happens when there is no frame pointer (microblaze ABI does not + require a frame pointer) or when we're stopped in the prologue or + epilogue itself. In these cases, microblaze_analyze_prologue will need + to update fi->frame before returning or analyzing the register + save instructions. */ +#define MY_FRAME_IN_SP 0x1 + +/* The base of the current frame is in a frame pointer register. + This register is noted in frame_extra_info->fp_regnum. + + Note that the existance of an FP might also indicate that the + function has called alloca. */ +#define MY_FRAME_IN_FP 0x2 + +/* This flag is set to indicate that this frame is the top-most + frame. This tells frame chain not to bother trying to unwind + beyond this frame. */ +#define NO_MORE_FRAMES 0x4 +#define IS_ALSO_SPILL_REG(op, rd, ra, rb) \ + ((op == swi || op == sw) && rd != REG_SP && ra == 0 && rb == REG_SP) +#define IS_SETUP_FP(op, ra, rb) \ + ((op == add || op == addik || op == addk) && ra == REG_SP && rb == 0) +#define IS_SPILL_REG_FP(op, rd, ra, fpregnum) \ + ((op == swi || op == sw) && rd != REG_SP && ra == fpregnum && ra != 0) +#define IS_SAVE_HIDDEN_PTR(op, rd, ra, rb) \ + ((op == add || op == addik) && ra == FIRST_ARGREG && rb == 0) + +/* BREAKPOINT defines the breakpoint that should be used. */ +#ifdef __MICROBLAZE_UCLINUX__ +#define BREAKPOINT {0xb9, 0xcc, 0x00, 0x60} +#else +#define BREAKPOINT {0x98, 0x0c, 0x00, 0x00} +#endif + +#endif /* microblaze-tdep.h */ diff -urNp --exclude '*.swp' --exclude DEV-PHASE --exclude .svn --exclude CVS gdb/gdb/NEWS mb-gdb/gdb/NEWS --- gdb/gdb/NEWS 2009-09-06 10:14:42.000000000 -0700 +++ mb-gdb/gdb/NEWS 2009-09-23 10:41:55.000000000 -0700 @@ -442,6 +442,11 @@ Lattice Mico32 lm32-* x86 DICOS i[34567]86-*-dicos* x86_64 DICOS x86_64-*-dicos* S+core 3 score-*-* +Xilinx MicroBlaze microblaze-*-* + +* New Simulators + +Xilinx MicroBlaze microblaze * The GDB remote stub, gdbserver, now supports x86 Windows CE (mingw32ce) debugging.