OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [final_interface/] [insight/] [gdb/] [remote-or1k.c] - Rev 1765

Compare with Previous | Blame | View Log

/* Remote debugging interface for various or1k debugging protocols.
   Copyright 1993-1995, 2000 Free Software Foundation, Inc.
   Contributed by Cygnus Support.  Written by Marko Mlinar
   <markom@opencores.org>
 
   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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */
 
#include "defs.h"
#include "inferior.h"
#include "bfd.h"
#include "symfile.h"
#include "gdb_wait.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "target.h"
#include "remote-utils.h"
#include "gdb_string.h"
#include "tm.h"
 
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
 
extern void jtag_init PARAMS ((char * args));
extern unsigned long long int jtag_read_reg PARAMS ((unsigned int regno));
extern void jtag_write_reg PARAMS ((unsigned int regno, unsigned long long int data));
extern void jtag_done PARAMS ((void));
extern void jtag_set_chain PARAMS ((int chain));
struct target_ops or1k_jtag_ops;
static struct or1k_target_ops or1k_target_jtag =
  {
    "jtag",
    jtag_init,
    jtag_done,
    jtag_read_reg,
    jtag_write_reg,
    jtag_set_chain,
    NULL,
    &or1k_jtag_ops,
    OPS_MAGIC
  };
 
struct target_ops or1k_sim_ops;
static struct or1k_target_ops or1k_target_sim =
  {
    "simulator",
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    &or1k_sim_ops,
    OPS_MAGIC
  };
 
struct target_ops or1k_dummy_ops;
static struct or1k_target_ops or1k_target_dummy =
  {
    "dummy",
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    &or1k_dummy_ops,
    OPS_MAGIC
  };
 
const char *str_err[] =
  {
    "None", "CRC error"
  };
 
const char *status_name[] =
  {
    "UNDEFINED", "CONNECTING", "DISCONNECTING", "RUNNING", "STOPPED"
  };
 
/* Names for matchpoint related stuff.  */
static char *ct_names[] =
  {
    "DIS", "IFEA", "LEA", "SEA", "AEA", "LDATA", "SDATA", "ADATA"
  };
 
static char *cc_names[] =
  {
    "&", "==", "<", "<=", ">", ">=", "!=", "ERR"
  };
 
static char *ch_names[] =
  {
    "ERR", "&", "|", "ERR" 
  };
 
/* Implementation specific information.  Set by or1k_initialize.  */
struct struct_or1k_implementation or1k_implementation;
 
/* Current target status.  */
static enum target_status or1k_status = TARGET_UNDEFINED;
 
/* The target vector.  */
struct target_ops or1k_dummy_ops, or1k_jtag_ops, or1k_sim_ops;
 
/* Currently active target description (if or1k_is_open == 1) */
static struct target_ops *current_ops;
 
/* Currently active or1k target operations.  */
static struct or1k_target_ops *current_or1k_target = NULL;
 
/* Set to 1 if the target is open.  */
static int or1k_is_open = 0;
 
/* Error last occured, zero = ok.  */
static int err = 0;
 
/* Number of interrupts while waiting for process.  */
static int interrupt_count = 0;
 
/* Reason of last stop.  */
static int hit_watchpoint = 0;
 
/* Current register values.  */
static unsigned int dmr1 = 0;
static unsigned int dmr2 = 0;
static unsigned int dsr = 0; 
static unsigned int drr = 0;
 
/* Current watchpoints.  */
static int dvr[8];
static struct dcr_struct dcr[8];
 
/* Handle low-level error that we can't recover from.  Note that just
   error()ing out from target_wait or some such low-level place will cause
   all hell to break loose--the rest of GDB will tend to get left in an
   inconsistent state.  */
 
static NORETURN void
or1k_error (char *string,...)
{
  va_list args;
 
  va_start (args, string);
 
  target_terminal_ours ();
  wrap_here ("");		/* Force out any buffered output */
  gdb_flush (gdb_stdout);
  if (error_pre_print)
    fprintf_filtered (gdb_stderr, error_pre_print);
  vfprintf_filtered (gdb_stderr, string, args);
  fprintf_filtered (gdb_stderr, "\n");
  va_end (args);
  gdb_flush (gdb_stderr);
 
  /* Clean up in such a way that or1k_close won't try to talk to the
     board (it almost surely won't work since we weren't able to talk to
     it).  */
  or1k_is_open = 0;
 
  printf_unfiltered ("Ending remote or1k debugging.\n");
  target_mourn_inferior ();
 
  return_to_top_level (RETURN_ERROR);
}
 
const char *
or1k_err_name (e)
     int e;
{
  return str_err[e];
}
 
/* putc_readable - print a character, displaying non-printable chars in
   ^x notation or in hex.  */
 
static void
fputc_readable (ch, file)
     int ch;
     struct ui_file *file;
{
  if (ch == '\n')
    fputc_unfiltered ('\n', file);
  else if (ch == '\r')
    fprintf_unfiltered (file, "\\r");
  else if (ch < 0x20)		/* ASCII control character */
    fprintf_unfiltered (file, "^%c", ch + '@');
  else if (ch >= 0x7f)		/* non-ASCII characters (rubout or greater) */
    fprintf_unfiltered (file, "[%02x]", ch & 0xff);
  else
    fputc_unfiltered (ch, file);
}
 
 
/* puts_readable - print a string, displaying non-printable chars in
   ^x notation or in hex.  */
 
static void
fputs_readable (string, file)
     char *string;
     struct ui_file *file;
{
  int c;
 
  while ((c = *string++) != '\0')
    fputc_readable (c, file);
}
 
/* Sets scan chain.  */
 
static void
or1k_set_chain (chain)
     int chain;
{
  if (current_or1k_target != NULL && current_or1k_target->to_set_chain != NULL)
    current_or1k_target->to_set_chain (chain); 
}
 
/* Sets register/memory regno to data.  */
 
static void
or1k_write_reg (regno, data)
     unsigned int regno;
     unsigned long long int data;
{
  if (current_or1k_target != NULL && current_or1k_target->to_write_reg != NULL)
    current_or1k_target->to_write_reg (regno, data);
}
 
/* Reads register/memory from regno.  */
 
static unsigned long long int
or1k_read_reg (regno)
     unsigned int regno;
{ 
  if (current_or1k_target != NULL && current_or1k_target->to_read_reg != NULL)
    return current_or1k_target->to_read_reg (regno);
  else
    return 0x1234;
}
 
/* Sets SPR register regno to data.  */
 
void
or1k_write_spr_reg (regno, data)
     unsigned int regno;
     unsigned int data;
{
  or1k_set_chain (SC_RISC_DEBUG); 
  or1k_write_reg (regno + REG_SPACE, data);
}
 
/* Reads register SPR from regno.  */
 
unsigned int
or1k_read_spr_reg (regno)
     unsigned int regno;
{
  or1k_set_chain (SC_RISC_DEBUG); 
  return or1k_read_reg (regno + REG_SPACE);
}
 
/* Stalls the CPU.  */
 
static void
or1k_stall ()
{
  int val;
  or1k_set_chain (SC_REGISTER);  
  val = or1k_read_reg (JTAG_RISCOP);
  or1k_write_reg (JTAG_RISCOP, val | 1);
  or1k_flush_pipeline ();
}
 
/* Unstalls the CPU.  */
 
static void
or1k_unstall ()
{
  unsigned int val;
  or1k_set_chain (SC_REGISTER);
  val = or1k_read_reg (JTAG_RISCOP);
  or1k_write_reg (JTAG_RISCOP, val & ~1);
}
 
/* Resets the CPU and stalls it.  */
static void
or1k_reset ()
{
  unsigned int val;
  or1k_set_chain (SC_REGISTER);
  val = or1k_read_reg (JTAG_RISCOP);
  val &= ~3;
  or1k_write_reg (JTAG_RISCOP, val | 3); /* Assert reset signal.  */
  usleep (1000);  /* give it some time */
  or1k_flush_pipeline ();
  or1k_write_reg (JTAG_RISCOP, val | 1); /* Release reset signal, but keep in stall state.  */
  or1k_flush_pipeline ();
}
 
static void
or1k_set_undefined_cleanups (arg)
     PTR arg;
{
  or1k_status = TARGET_UNDEFINED;
}
 
/* Initialize a new connection to the or1k board, and make sure we are
   really connected.  */
 
static void
or1k_init (args)
     char *args;
{
  struct cleanup *old_cleanups = make_cleanup (or1k_set_undefined_cleanups, NULL);
  int i;
 
  /* What is this code doing here?  I don't see any way it can happen, and
     it might mean or1k_initializing didn't get cleared properly.
     So I'll make it a warning.  */
 
  if (or1k_status == TARGET_CONNECTING)
    {
      warning ("internal error: or1k_initialize called twice");
      return;
    }
 
  or1k_status = TARGET_CONNECTING;
  or1k_reset ();
  if (current_or1k_target != NULL && current_or1k_target->to_init != NULL)
    current_or1k_target->to_init (args);
 
  /* Determine implementation configuration.  */
  or1k_implementation.VR = or1k_read_spr_reg (VR_SPRNUM);
  or1k_implementation.UPR = or1k_read_spr_reg (UPR_SPRNUM);
 
  /* Determine max number of supported matchpoints.  */ 
  or1k_implementation.num_matchpoints = 2;
  or1k_implementation.num_used_matchpoints = 0;
  or1k_implementation.num_gpr_regs = 32;
  /*!!! FINISH */
 
 
  /* Is implementation supported? */
 
  /* First we should have system and debug groups implemented. */
  if (or1k_implementation.VR & (1 << SPR_SYSTEM_GROUP) == 0)
    error ("System group should be available in the or1k implementation.");
  if (or1k_implementation.VR & (1 << SPR_DEBUG_GROUP) == 0)
    error ("Debug group should be available in the or1k implementation.");
 
 
 
  /* Delete break, watch, catch points.  */
  for(i = 0; i < NUM_MATCHPOINTS; i++)
    or1k_write_spr_reg (DCR0_SPRNUM + i, 0);
 
  dmr1 = 0;
  or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
  dmr2 = 0;
  or1k_write_spr_reg (DMR2_SPRNUM, dmr2);
  if (err != 0)
    error ("Cannot connect.");
 
  /* Stop when breakpoint occurs.  */
  or1k_write_spr_reg (DSR_SPRNUM, 0x1000);
 
  do_cleanups (old_cleanups);
 
  /* This should cause an error if not connected.  */
  or1k_fetch_registers (-1);
 
  set_current_frame (create_new_frame (read_fp (), read_pc ()));
  select_frame (get_current_frame (), 0);
}
 
/* Kill the process running on the board.  */
 
void
or1k_kill ()
{
  if (or1k_status != TARGET_RUNNING)
    return;
  or1k_status = TARGET_UNDEFINED;
  or1k_reset();
  or1k_status = TARGET_STOPPED;
 
  inferior_pid = 0;
}
 
/* Open a connection to the remote board.  */
 
static void
or1k_open (name, from_tty)
     char *name;
     int from_tty;
{
  or1k_init (name);
 
  /* Switch to using remote target now.  */
  current_ops = current_or1k_target->gdb_ops;
  or1k_is_open = 1;
  push_target (current_ops);
 
  /* FIXME: Should we call start_remote here?  */
 
/* This is really the job of start_remote however, that makes an assumption
   that the target is about to print out a status message of some sort.  That
   doesn't happen here (in fact, it may not be possible to get the monitor to
   send the appropriate packet).  */
 
  flush_cached_frames ();
  registers_changed ();
  stop_pc = read_pc ();
  set_current_frame (create_new_frame (read_fp (), stop_pc));
  select_frame (get_current_frame (), 0);
  print_stack_frame (selected_frame, -1, 1);
}
 
/* Close a connection to the remote board.  */
 
static void
or1k_close (quitting)
     int quitting;
{
  if (or1k_is_open)
    {
      or1k_kill ();
      if (current_or1k_target != NULL && current_or1k_target->to_done != NULL)
	current_or1k_target->to_done ();
      current_or1k_target = NULL;
    }
  generic_mourn_inferior ();
}
 
/* Detach from the remote board.  */
 
static void
or1k_detach (args, from_tty)
     char *args;
     int from_tty;
{
  if (args)
    error ("Argument given to \"detach\" when remotely debugging.");
 
  pop_target ();
 
  or1k_close (1);
 
  if (from_tty)
    printf_unfiltered ("Ending remote or1k debugging.\n");
}
 
/* Resume execution of the target process.  STEP says whether to single-step
   or to run free; SIGGNAL is the signal value (e.g. SIGINT) to be given
   to the target, or zero for no signal.  */
 
static void
or1k_resume (pid, step, siggnal)
     int pid, step;
     enum target_signal siggnal;
{  
  if (or1k_status != TARGET_STOPPED)
    if (or1k_status == TARGET_RUNNING)
      error ("Program is already running.");
    else
      error ("The program is not being run.");
 
  /* Clear reason register for later.  */
  or1k_write_spr_reg (DRR_SPRNUM, 0);
 
  if (step)
    {
      /* HW STEP.  Set DMR1_ST.  */
      dmr1 |= DMR1_ST;
      or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
      dmr1 &= ~DMR1_ST;
    }
 
  /* Run the target. */
  or1k_unstall ();
  or1k_status = TARGET_RUNNING;
}
 
/* Wait until the remote stops, and return a wait status.  */
 
static int
or1k_wait (pid, status)
     int pid;
     struct target_waitstatus *status;
{
  interrupt_count = 0;
 
  /* If we have not sent a single step or continue command, then the
     board is waiting for us to do something.  Return a status
     indicating that it is stopped.  */
  if (or1k_status != TARGET_RUNNING)
    {
      if (or1k_status != TARGET_STOPPED)
	error("Target in invalid state."); 
      status->kind = TARGET_WAITKIND_STOPPED;
      status->value.sig = TARGET_SIGNAL_TRAP;
      return 0;
    }
 
  if (err)
    or1k_error ("Remote failure: %s", or1k_err_name (err));
 
  /* Wait for or1k DRR register to be nonzero.  */
  do
    {
      drr = or1k_read_spr_reg (DRR_SPRNUM);
      usleep (10);
    }
  while (drr == 0);
 
  status->kind = TARGET_WAITKIND_STOPPED;
 
  or1k_flush_pipeline ();
 
  if (drr & DRR_RSTE)
    status->value.sig = TARGET_SIGNAL_REALTIME_33;
  else if (drr & DRR_BUSEE)
    status->value.sig = TARGET_SIGNAL_BUS;
  else if (drr & DRR_DPFE)
    status->value.sig = TARGET_SIGNAL_REALTIME_34;
  else if (drr & DRR_IPFE)
    status->value.sig = TARGET_SIGNAL_REALTIME_35;
  else if (drr & DRR_LPINTE)
    status->value.sig = TARGET_SIGNAL_INT;
  else if (drr & DRR_AE)
    status->value.sig = TARGET_SIGNAL_REALTIME_36;
  else if (drr & DRR_IIE)
    status->value.sig = TARGET_SIGNAL_ILL;
  else if (drr & DRR_HPINTE)
    status->value.sig = TARGET_SIGNAL_INT;
  else if (drr & DRR_DME)
    status->value.sig = TARGET_SIGNAL_REALTIME_37;
  else if (drr & DRR_IME)
    status->value.sig = TARGET_SIGNAL_REALTIME_38;
  else if (drr & DRR_RE)
    status->value.sig = TARGET_SIGNAL_REALTIME_39;
  else if (drr & DRR_SCE)
    status->value.sig = TARGET_SIGNAL_REALTIME_40;
  else if (drr & DRR_BE)
    status->value.sig = TARGET_SIGNAL_TRAP;
  else
    {
      status->value.sig = TARGET_SIGNAL_UNKNOWN;
      warning ("Invalid exception occured.");
    }
 
  /* Log remote stop.  */
  or1k_status = TARGET_STOPPED;
 
  /* Determine what caused trap - breakpoint or watchpoint.  */
  if (status->value.sig == TARGET_SIGNAL_TRAP)
    {
      /* Search all active breakpoints for a match.  */
      CORE_ADDR pc = read_pc ();
      int breakpoint = 0;
      int i;
      for (i = 0; i < or1k_implementation.num_used_matchpoints; i++)
	if (dvr[i] == pc && dcr[i].dp && dcr[i].cc == CC_EQUAL && !dcr[i].sc && dcr[i].ct == CT_FETCH)
	  {
	    breakpoint = 1;
	    break;
	  }
      hit_watchpoint = !breakpoint;
    }
  else
    hit_watchpoint = 0;
 
  /* If the stop PC is in the _exit function, assume
     we hit the 'break 0x3ff' instruction in _exit, so this
     is not a normal breakpoint.  */
  {
    char *func_name;
    CORE_ADDR func_start;
    CORE_ADDR pc = read_pc ();
 
    find_pc_partial_function (pc, &func_name, &func_start, NULL);
    if (func_name != NULL && strcmp (func_name, "_exit") == 0
	&& func_start == pc)
      status->kind = TARGET_WAITKIND_EXITED;
  }
  return 0;
}
 
/* Fetch a word from the target board.  All memory accesses to the
   remote board are word aligned.  */
 
static unsigned int
or1k_fetch_word (addr)
     CORE_ADDR addr;
{
  if (addr & 3)
    {
      int subaddr = addr & 3;
      unsigned char buf[8];
      unsigned int low, high;
      addr >>= 2;
      low = or1k_read_reg (addr + MEM_SPACE);
      high = or1k_read_reg (addr + 1 + MEM_SPACE);
      memcpy (&buf[0], &low, 4);
      memcpy (&buf[4], &high, 4);
      memcpy (&low, &buf[subaddr], 4);
      return low;
    }
  else
    {
      addr >>= 2;
      return or1k_read_reg (addr + MEM_SPACE);
    }
}
 
/* Store a word to the target board.  Returns errno code or zero for
   success.  All memory accesses to the remote board are word aligned.  */
static int
or1k_store_word (addr, val)
     CORE_ADDR addr;
     unsigned int val;
{
  if (addr & 3)
    {
      int subaddr = addr & 3;
      unsigned char buf[8];
      unsigned int low, high;
      addr >>= 2;
      low = or1k_read_reg (addr + MEM_SPACE);
      high = or1k_read_reg (addr + 1 + MEM_SPACE);
      memcpy (&buf[0], &low, 4);
      memcpy (&buf[4], &high, 4);
      memcpy (&buf[subaddr], &val, 4);
      memcpy (&low, &buf[0], 4);
      memcpy (&high, &buf[4], 4);
      or1k_write_reg (addr + MEM_SPACE, low);
      or1k_write_reg (addr + 1 + MEM_SPACE, high);
    }
  else
    {
      addr >>= 2;
      or1k_write_reg (addr + MEM_SPACE, val);
    }
  return err;
}
 
/* Fetch the remote registers.  */
 
void
or1k_fetch_registers (regno)
     int regno;
{
  unsigned int val;
 
  if (regno == -1)
    {
      for (regno = 0; regno < NUM_REGS; regno++)
	or1k_fetch_registers (regno);
      return;
    }
 
  if (regno >= NUM_REGS)
    error("Invalid register number!");
 
  /* Convert to SPRNUM and read.  */
  val = or1k_read_spr_reg (REGNUM_TO_SPRNUM(regno));
 
  {
    char buf[MAX_REGISTER_RAW_SIZE];
 
    /* We got the number the register holds, but gdb expects to see a
       value in the target byte ordering.  */
    store_unsigned_integer (buf, REGISTER_RAW_SIZE (regno), val);
    supply_register (regno, buf);
  }
  if (err)
    or1k_error ("Can't read register %d(%i): %s", regno, REGNUM_TO_SPRNUM(regno) + REG_SPACE, or1k_err_name (err));
}
 
/* Fetch and return instruction from the specified location.  */
 
unsigned int
or1k_fetch_instruction (addr)
     CORE_ADDR addr;
{
  char buf[OR1K_INSTLEN];
  int status;
 
  status = read_memory_nobpt (addr, buf, OR1K_INSTLEN);
  if (status)
    memory_error (status, addr);
  return extract_unsigned_integer (buf, OR1K_INSTLEN);
}
 
/* Currently not needed.  */
 
static void
or1k_prepare_to_store ()
{
}
 
/* Store remote register(s).  */
 
static void
or1k_store_registers (regno)
     int regno;
{
  if (regno == -1)
    {
      for (regno = 0; regno < NUM_REGS; regno++)
	or1k_store_registers (regno);
      return;
    }
 
  if (regno >= NUM_REGS)
    error("Invalid register number!");
 
  or1k_write_spr_reg (REGNUM_TO_SPRNUM(regno), or1k_read_spr_reg (REGNUM_TO_SPRNUM(regno)));
  if (err)
    or1k_error ("Can't write register %d(%i): %s", regno, REGNUM_TO_SPRNUM(regno), or1k_err_name (err));
}
 
/* Read or write LEN bytes from inferior memory at MEMADDR,
   transferring to or from debugger address MYADDR.  Write to inferior
   if SHOULD_WRITE is nonzero.  Returns length of data written or
   read; 0 for error.  Note that protocol gives us the correct value
   for a longword, since it transfers values in ASCII.  We want the
   byte values, so we have to swap the longword values.  */
 
static int
or1k_xfer_memory (memaddr, myaddr, len, write, ignore)
     CORE_ADDR memaddr;
     char *myaddr;
     int len;
     int write;
     struct target_ops *ignore;
{
  register int i;
  /* Round starting address down to longword boundary.  */
  register CORE_ADDR addr = memaddr & ~3;
  /* Round ending address up; get number of longwords that makes.  */
  register int count = (((memaddr + len) - addr) + 3) / 4;
  /* Allocate buffer of that many longwords.  */
  register char *buffer = alloca (count * 4);
 
  int status;
 
  if (memaddr >= MEM_SPACE)
    error("Invalid address");
 
  if (write)
    {
      /* Fill start and end extra bytes of buffer with existing data.  */
      if (addr != memaddr || len < 4)
	{
	  /* Need part of initial word -- fetch it.  */
	  store_unsigned_integer (&buffer[0], 4, or1k_fetch_word (addr));
	}
 
      if (count > 1)
	{
	  /* Need part of last word -- fetch it.  FIXME: we do this even
	     if we don't need it.  */
	  store_unsigned_integer (&buffer[(count - 1) * 4], 4,
				  or1k_fetch_word (addr + (count - 1) * 4));
	}
 
      /* Copy data to be written over corresponding part of buffer */
 
      memcpy ((char *) buffer + (memaddr & 3), myaddr, len);
 
      /* Write the entire buffer.  */
 
      for (i = 0; i < count; i++, addr += 4)
	{
	  status = or1k_store_word (addr,
			       extract_unsigned_integer (&buffer[i * 4], 4));
	  /* Report each kilobyte (we download 32-bit words at a time) */
	  if (i % 256 == 255)
	    {
	      printf_unfiltered ("*");
	      gdb_flush (gdb_stdout);
	    }
	  if (status)
	    {
	      errno = status;
	      return 0;
	    }
	  /* FIXME: Do we want a QUIT here?  */
	}
      if (count >= 256)
	printf_unfiltered ("\n");
    }
  else
    {
      /* Read all the longwords */
      for (i = 0; i < count; i++, addr += 4)
	{
	  store_unsigned_integer (&buffer[i * 4], 4, or1k_fetch_word (addr));
	  QUIT;
	}
 
      /* Copy appropriate bytes out of the buffer.  */
      memcpy (myaddr, buffer + (memaddr & 3), len);
    }
  return len;
}
 
/* Flushes pipeline. May not be needed by all implementations, but
   it doen't hurt to do it. This function should be called every time
   or1k stops.
   When or1k stops it still has instructions in pipeline.
   We do this by inserting nop instructions.
   IF cycle remains unaffacted by writing to DIR, and it still holds
   instruction, that caused break or watchpoint. */ 
 
void
or1k_flush_pipeline ()
{
  or1k_write_spr_reg (DIR_SPRNUM, NOP_INSTR);
  or1k_write_spr_reg (DIR_SPRNUM, NOP_INSTR);
  or1k_write_spr_reg (DIR_SPRNUM, NOP_INSTR);
}
 
/* Print info on this target.  */
 
static void
or1k_files_info (ignore)
     struct target_ops *ignore;
{ 
  char *file = "nothing";
 
  if (exec_bfd)
    file = bfd_get_filename (exec_bfd);
 
  printf_filtered ("or1k_files_info: file \"%s\"\n", file);
 
  if (exec_bfd)
    {
      printf_filtered ("\tAttached to %s running program %s\n",
		       target_shortname, file);
    }
  /* Print target info. */
  printf_filtered ("Status: %s\n", status_name[or1k_status]);
}
 
/* Tell whether we can support a hardware breakpoint.  */
static int
or1k_can_use_hardware_breakpoint ()
{
  int i;
  /* Search for unused breakpoint.  */
  return or1k_implementation.num_used_matchpoints < or1k_implementation.num_matchpoints;
}
 
/* Insert a breakpoint.  On targets that don't have built-in breakpoint
   support, we read the contents of the target location and stash it,
   then overwrite it with a breakpoint instruction.  ADDR is the target
   location in the target machine.  CONTENTS_CACHE is a pointer to 
   memory allocated for saving the target contents.  It is guaranteed
   by the caller to be long enough to save sizeof BREAKPOINT bytes (this
   is accomplished via BREAKPOINT_MAX).  */
 
int
or1k_insert_breakpoint (addr, contents_cache)
     CORE_ADDR addr;
     char *contents_cache;
{
  if (or1k_can_use_hardware_breakpoint())
    return set_breakpoint (addr);
  else
    return memory_insert_breakpoint (addr, contents_cache);
}
 
int
or1k_remove_breakpoint (addr, contents_cache)
     CORE_ADDR addr;
     char *contents_cache;
{
  /* First try to remove HW breakpoint at address */
  if (clear_breakpoint (addr))
    return memory_remove_breakpoint (addr, contents_cache);
  else
    return 0;
}
 
/* Tell whether this target can support a hardware breakpoint.  CNT
   is the number of hardware breakpoints already installed.  This
   implements the TARGET_CAN_USE_HARDWARE_WATCHPOINT macro.
   Lower bound is estimated. !!! Can we estimate better? */
 
int
or1k_can_use_hardware_watchpoint (bp_type, cnt)
     enum bptype bp_type;
     int cnt;
{
  /* Are there at least two matchpoints left for watch? - estimate lower bound  */
  return cnt + ((bp_type == bp_hardware_watchpoint)?(1):(0))
    <= or1k_implementation.num_matchpoints;
}
 
/* Sifts unused matchpoints to higher indexses.  */
 
static void
sift_matchpoints ()
{
  int i, first_free = 0;
  unsigned int u;
  for (i = 0; i < or1k_implementation.num_matchpoints; i++)
    if (dcr[i].dp)
      {
	int chaining;
	dvr[first_free] = dvr[i];
	dcr[first_free] = dcr[i];
 
	/* Copy chaining bits.  */ 
	chaining = dmr1 & (3 << (2 * i));
	dmr1 &= ~(3 << (2 * first_free));
	dmr1 |= chaining << (2 * first_free);
	/* Copy watchpoint bits */
	chaining = dmr2 & (1 << i);
	dmr2 &= 1 << first_free;
	dmr2 |= chaining << first_free;
	/* !!! move references also */
 
	or1k_write_spr_reg (DVR0_SPRNUM + first_free, dvr[first_free]);
	memcpy (&u, &dcr[first_free], sizeof(dcr[first_free]));
	or1k_write_spr_reg (DCR0_SPRNUM + first_free, u);
	first_free++;
      }
  /* Disable unused.  */
  for (i = first_free; i < or1k_implementation.num_matchpoints; i++)
    {
      dmr2 &= ~(1 << i);
      dcr[i].dp = 0;
    }
  or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
  or1k_write_spr_reg (DMR2_SPRNUM, dmr2);
}
 
/* Translates gdb watchpoint type into one in DCR register.  */
 
static int
translate_type (gdb_type)
     int gdb_type;
{
  switch (gdb_type)
    {
    case 0:
      return CT_SDATA;
    case 1:
      return CT_LDATA;
    case 2:
      return CT_ADATA;
    default:
      error ("Invalid type.");
    }
}
 
/* Set a data watchpoint.  ADDR and LEN should be obvious.  TYPE is 0
   for a write watchpoint, 1 for a read watchpoint, or 2 for a read/write
   watchpoint. */
 
int
or1k_insert_watchpoint (addr, len, type)
     CORE_ADDR addr;
     int len;
     int type;
{
  int i;
  unsigned int u;
  if (len < 1)
    return -1;
 
  type = translate_type (type);
 
  /* Moves unused watchpoints to the top.  */ 
  sift_matchpoints ();
  /* Place at first free matchpoint.  */
  i = or1k_implementation.num_used_matchpoints;
  dvr[i] = addr;
  dcr[i].dp = 1;
  dcr[i].cc = CC_GREATE;
  dcr[i].sc = 0;
  dcr[i].ct = type;
  or1k_write_spr_reg (DVR0_SPRNUM + i, dvr[i]);
  memcpy (&u, &dcr[i], sizeof(dcr[i]));
  or1k_write_spr_reg (DCR0_SPRNUM + i, u);
 
  /* Set && chaining here.  */ 
  dmr1 &= ~(3 << (2 * i));
  dmr1 |= 2 << (2 * i);
  or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
 
  /* Set upper watchpoint bound.  */
  i++;
  dvr[i] = addr + len - 1;
  dcr[i].dp = 1;
  dcr[i].cc = CC_LESSE;
  dcr[i].sc = 0;
  dcr[i].ct = type;
  or1k_write_spr_reg (DVR0_SPRNUM + i, dvr[i]);
  memcpy (&u, &dcr[i], sizeof(dcr[i]));
  or1k_write_spr_reg (DCR0_SPRNUM + i, u);
 
  /* Matchpoints will cause breakpoints */
  or1k_write_spr_reg (DMR2_SPRNUM, dmr2 |= (1 << i));
  or1k_implementation.num_used_matchpoints += 2;
  return 0;
}
 
/* Removes a data watchpoint.  ADDR and LEN should be obvious.  TYPE is 0
   for a write watchpoint, 1 for a read watchpoint, or 2 for a read/write
   watchpoint. */
int
or1k_remove_watchpoint (addr, len, type)
     CORE_ADDR addr;
     int len;
     int type;
{
  int i, found = -1;
  unsigned int u;
  if (len < 1)
    return -1;
 
  type = translate_type (type);
 
  /* Find the right one.  */
  for (i = 0; i < or1k_implementation.num_used_matchpoints; i++)
    if (dvr[i] == addr && dcr[i].dp && dcr[i].cc == CC_GREATE && !dcr[i].sc && dcr[i].ct == type
	&& dvr[i + 1] == addr + len - 1 && dcr[i + 1].dp && dcr[i + 1].cc == CC_LESSE
	&& !dcr[i + 1].sc && dcr[i + 1].ct == type)
      {
	found = i;
	break;
      }
 
  if (found < 0)
    return -1;
 
  dcr[found].dp = 0;
  memcpy (&u, &dcr[found], sizeof(dcr[found]));
  or1k_write_spr_reg (DCR0_SPRNUM + found, u);
  dcr[found + 1].dp = 0;
  memcpy (&u, &dcr[found + 1], sizeof(dcr[found + 1]));
  or1k_write_spr_reg (DCR0_SPRNUM + found + 1, u);
 
  /* Matchpoints will not cause breakpoints anymore. */
  or1k_write_spr_reg (DMR2_SPRNUM, dmr2 &= ~(1 << i));
  or1k_implementation.num_used_matchpoints -= 2;
  return 0;
}
 
int
or1k_stopped_by_watchpoint (void)
{
  return hit_watchpoint;
}
 
/* Insert a breakpoint.  */
 
int
set_breakpoint (addr)
     CORE_ADDR addr;
{
  int i;
  unsigned int u;
  /* Search for unused breakpoint.  */
  for (i = 0; i < NUM_MATCHPOINTS; i++)
    if (dcr[i].dp == 0) break;
  if (i >= NUM_MATCHPOINTS) return 1;
  dvr[i] = addr;
  dcr[i].dp = 1;
  dcr[i].cc = CC_EQUAL;
  dcr[i].sc = 0;
  dcr[i].ct = CT_FETCH;
  or1k_write_spr_reg (DVR0_SPRNUM + i, dvr[i]);
  memcpy (&u, &dcr[i], sizeof(dcr[i]));
  or1k_write_spr_reg (DCR0_SPRNUM + i, u);
  or1k_implementation.num_used_matchpoints++;
 
  /* No chaining here.  */ 
  dmr1 &= ~(3 << (2*i));
  or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
  /* Matchpoints will cause breakpoints */
  or1k_write_spr_reg (DMR2_SPRNUM, dmr2 |= (1 << i));
  return 0;
}
 
/* Clear a breakpoint.  */
 
int
clear_breakpoint (addr)
     CORE_ADDR addr;
{
  int i;
  unsigned int u;
  /* Search for matching breakpoint.  */
  for (i = 0; i < NUM_MATCHPOINTS; i++)
    if ((dcr[i].dp == 1) && (dvr[i] == addr) && (dcr[i].cc == CC_EQUAL)
	&& (dcr[i].sc == 0) && (dcr[i].ct == CT_FETCH)) break;
 
  if (i >= NUM_MATCHPOINTS) return 1;
  dcr[i].dp = 0;
  memcpy (&u, &dcr[i], sizeof(dcr[i]));
  or1k_write_spr_reg (DCR0_SPRNUM + i, u);
  /* Matchpoints will cause breakpoints */
  or1k_write_spr_reg (DMR2_SPRNUM, dmr2 &= ~(1 << i));
  or1k_implementation.num_used_matchpoints--;
  return 0;
}
 
/* Start running on the target board.  */
 
static void
or1k_create_inferior (execfile, args, env)
     char *execfile;
     char *args;
     char **env;
{
  CORE_ADDR entry_pt;
 
  if (args && *args)
    {
      warning ("\
Can't pass arguments to remote OR1K board; arguments ignored.");
      /* And don't try to use them on the next "run" command.  */
      execute_command ("set args", 0);
    }
 
  if (execfile == 0 || exec_bfd == 0)
    error ("No executable file specified");
 
  or1k_kill ();
  remove_breakpoints ();
 
  entry_pt = (CORE_ADDR) bfd_get_start_address (exec_bfd);
  init_wait_for_inferior ();
 
  /* FIXME: Should we set inferior_pid here?  */
 
  insert_breakpoints ();	/* Needed to get correct instruction in cache */
  clear_proceed_status ();
  or1k_status = TARGET_STOPPED;
  proceed (entry_pt, TARGET_SIGNAL_DEFAULT, 0);
}
 
/* Clean up after a process.  Actually nothing to do.  */
 
static void
or1k_mourn_inferior ()
{
  generic_mourn_inferior ();
}
 
static void
or1k_dummy_open (name, from_tty)
     char *name;
     int from_tty;
{
  target_preopen (from_tty);
  if (or1k_is_open)
    unpush_target (current_ops);
  current_or1k_target = &or1k_target_dummy;
  or1k_open (name, from_tty);
}
 
static void
or1k_jtag_open (name, from_tty)
     char *name;
     int from_tty;
{
  target_preopen (from_tty);
  if (or1k_is_open)
    unpush_target (current_ops);
  current_or1k_target = &or1k_target_jtag;
  or1k_open (name, from_tty);
}
 
static void
or1k_sim_open (name, from_tty)
     char *name;
     int from_tty;
{
  /* target_preopen (from_tty); - do we need this ? */
  if (or1k_is_open)
    unpush_target (current_ops);
  current_or1k_target = &or1k_target_sim;
  or1k_open (name, from_tty);
}
 
/* Executes command on the target.  */
 
void
or1k_sim_cmd (char *args, int from_tty)
{
  if (current_or1k_target != NULL && current_or1k_target->to_exec_command != NULL)
    current_or1k_target->to_exec_command (args, from_tty);
  else
    error ("Command not supported on this target. ");
}
 
/* Displays matchpoints usage.  */
 
void
info_matchpoints_command (char *args, int from_tty)
{
  int i;
  for (i = 0; i < or1k_implementation.num_matchpoints; i++)
    {
      printf_filtered ("WP%i ", i);
      if (dcr[i].dp)
	{
	  int chaining = (dmr1 << 2*i) & 3; 
	  printf_filtered ("= %s ", ct_names[dcr[i].ct]);
	  if (dcr[i]. sc)
	    printf_filtered ("s%s %i", cc_names[dcr[i].cc], (int)dvr[i]);
          else
	    printf_filtered ("%s %u", cc_names[dcr[i].cc], (unsigned int)dvr[i]);
	  if (chaining)
	    printf_filtered ("%s WP%i", ch_names[chaining], i - 1);
	}
      else
	printf_filtered ("NOT USED");
      if ((dmr2 >> i) & 1)
	printf_filtered (", causes breakpoint");
      if ((dmr2 >> (i + 11)) & 1)
	printf_filtered (", increments counter");
      printf_filtered ("\n");
    }
}
 
void
_initialize_remote_or1k ()
{
  /* Initialize the fields in or1k_ops that are common to all targets.  */
  or1k_dummy_ops.to_close = or1k_close;
  or1k_dummy_ops.to_detach = or1k_detach;
  or1k_dummy_ops.to_resume = or1k_resume;
  or1k_dummy_ops.to_wait = or1k_wait;
  or1k_dummy_ops.to_fetch_registers = or1k_fetch_registers;
  or1k_dummy_ops.to_store_registers = or1k_store_registers;
  or1k_dummy_ops.to_prepare_to_store = or1k_prepare_to_store;
  or1k_dummy_ops.to_xfer_memory = or1k_xfer_memory;
  or1k_dummy_ops.to_files_info = or1k_files_info;
  or1k_dummy_ops.to_insert_breakpoint = or1k_insert_breakpoint;
  or1k_dummy_ops.to_remove_breakpoint = or1k_remove_breakpoint;
  or1k_dummy_ops.to_kill = or1k_kill;
  or1k_dummy_ops.to_load = generic_load;
  or1k_dummy_ops.to_create_inferior = or1k_create_inferior;
  or1k_dummy_ops.to_mourn_inferior = or1k_mourn_inferior;
  or1k_dummy_ops.to_stratum = process_stratum;
  or1k_dummy_ops.to_has_all_memory = 0; /* We can access memory while program is running.  */
  or1k_dummy_ops.to_has_memory = 1;
  or1k_dummy_ops.to_has_stack = 1;
  or1k_dummy_ops.to_has_registers = 1;
  or1k_dummy_ops.to_has_execution = 1;
  or1k_dummy_ops.to_magic = OPS_MAGIC;
 
  /* Copy the common fields to all target vectors.  */
  or1k_jtag_ops = or1k_sim_ops = or1k_dummy_ops;
 
  /* Initialize target-specific fields in the target vectors adn add targets.  */
  or1k_jtag_ops.to_shortname = "jtag";
  or1k_jtag_ops.to_longname = "Remote or1k debugging over JTAG port";
  or1k_jtag_ops.to_doc = "\
Debug a board using the OR1K remote debugging protocol over a parallel line.\n\
The argument is the device it is connected to or, if it contains a colon,\n";
  or1k_jtag_ops.to_open = or1k_jtag_open;
  add_target (&or1k_jtag_ops);
 
  or1k_dummy_ops.to_shortname = "dummy";
  or1k_dummy_ops.to_longname = "Dummy target";
  or1k_dummy_ops.to_doc = "Actually no real target attached - more like /dev/null.\n";
  or1k_dummy_ops.to_open = or1k_dummy_open;
  add_target (&or1k_dummy_ops);
 
  or1k_sim_ops.to_shortname = "sim";
  or1k_sim_ops.to_longname = "Remote or1k debugging using architecture simulator";
  or1k_sim_ops.to_doc = "Debug using an architecture simulator.\n";
  or1k_sim_ops.to_open = or1k_sim_open;
  add_target (&or1k_sim_ops);
  add_info ("matchpoints", info_matchpoints_command, "Show current matchpoints allocation status.");
}
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.