/* Remote debugging interface for various or1k debugging protocols.
|
/* Remote debugging interface for various or1k debugging protocols.
|
Currently supported or1k targets are: simulator, jtag, dummy.
|
Currently supported or1k targets are: simulator, jtag, dummy.
|
|
|
Copyright 1993-1995, 2000 Free Software Foundation, Inc.
|
Copyright 1993-1995, 2000 Free Software Foundation, Inc.
|
Contributed by Cygnus Support. Written by Marko Mlinar
|
Contributed by Cygnus Support. Written by Marko Mlinar
|
<markom@opencores.org>
|
<markom@opencores.org>
|
|
|
This file is part of GDB.
|
This file is part of GDB.
|
|
|
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
(at your option) any later version.
|
(at your option) any later version.
|
|
|
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU General Public License for more details.
|
GNU General Public License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
Foundation, Inc., 59 Temple Place - Suite 330,
|
Foundation, Inc., 59 Temple Place - Suite 330,
|
Boston, MA 02111-1307, USA. */
|
Boston, MA 02111-1307, USA. */
|
|
|
#include "defs.h"
|
#include "defs.h"
|
#include "inferior.h"
|
#include "inferior.h"
|
#include "bfd.h"
|
#include "bfd.h"
|
#include "symfile.h"
|
#include "symfile.h"
|
#include "gdb_wait.h"
|
#include "gdb_wait.h"
|
#include "gdbcmd.h"
|
#include "gdbcmd.h"
|
#include "gdbcore.h"
|
#include "gdbcore.h"
|
#include "target.h"
|
#include "target.h"
|
#include "remote-utils.h"
|
#include "remote-utils.h"
|
#include "gdb_string.h"
|
#include "gdb_string.h"
|
#include "tm.h"
|
#include "tm.h"
|
|
|
#include <signal.h>
|
#include <signal.h>
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <sys/stat.h>
|
#include <sys/stat.h>
|
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
#include <fcntl.h>
|
#include <fcntl.h>
|
|
|
#define debug if (remote_debug) printf_unfiltered
|
#define debug if (remote_debug) printf_unfiltered
|
|
|
/* The following prototype is necessary or the compiler will not
|
/* The following prototype is necessary or the compiler will not
|
correctly promote the data argument to ULONGEST */
|
correctly promote the data argument to ULONGEST */
|
static void or1k_write_reg (unsigned int, ULONGEST);
|
static void or1k_write_reg (unsigned int, ULONGEST);
|
|
static int insn_has_delay_slot (unsigned int);
|
|
|
/* JTAG or1k target ops. */
|
/* JTAG or1k target ops. */
|
extern void jtag_init PARAMS ((char * args));
|
extern void jtag_init PARAMS ((char * args));
|
extern ULONGEST jtag_read_reg PARAMS ((unsigned int regno));
|
extern ULONGEST jtag_read_reg PARAMS ((unsigned int regno));
|
extern void jtag_write_reg PARAMS ((unsigned int regno, ULONGEST data));
|
extern void jtag_write_reg PARAMS ((unsigned int regno, ULONGEST data));
|
extern void jtag_done PARAMS ((void));
|
extern void jtag_done PARAMS ((void));
|
extern int jtag_read_block PARAMS ((unsigned int regno, void* block, int nRegisters));
|
extern int jtag_read_block PARAMS ((unsigned int regno, void* block, int nRegisters));
|
extern int jtag_write_block PARAMS ((unsigned int regno, void* block, int nRegisters));
|
extern int jtag_write_block PARAMS ((unsigned int regno, void* block, int nRegisters));
|
extern void jtag_set_chain PARAMS ((int chain));
|
extern void jtag_set_chain PARAMS ((int chain));
|
struct target_ops or1k_jtag_ops;
|
struct target_ops or1k_jtag_ops;
|
static struct or1k_target_ops or1k_target_jtag =
|
static struct or1k_target_ops or1k_target_jtag =
|
{
|
{
|
"jtag",
|
"jtag",
|
jtag_init,
|
jtag_init,
|
jtag_done,
|
jtag_done,
|
jtag_read_reg,
|
jtag_read_reg,
|
jtag_write_reg,
|
jtag_write_reg,
|
jtag_read_block,
|
jtag_read_block,
|
jtag_write_block,
|
jtag_write_block,
|
jtag_set_chain,
|
jtag_set_chain,
|
NULL,
|
NULL,
|
&or1k_jtag_ops,
|
&or1k_jtag_ops,
|
OPS_MAGIC
|
OPS_MAGIC
|
};
|
};
|
|
|
/* simulator or1k target ops. */
|
/* simulator or1k target ops. */
|
struct target_ops or1k_sim_ops;
|
struct target_ops or1k_sim_ops;
|
static struct or1k_target_ops or1k_target_sim =
|
static struct or1k_target_ops or1k_target_sim =
|
{
|
{
|
"simulator",
|
"simulator",
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
&or1k_sim_ops,
|
&or1k_sim_ops,
|
OPS_MAGIC
|
OPS_MAGIC
|
};
|
};
|
|
|
/* dummy or1k target ops. */
|
/* dummy or1k target ops. */
|
struct target_ops or1k_dummy_ops;
|
struct target_ops or1k_dummy_ops;
|
static struct or1k_target_ops or1k_target_dummy =
|
static struct or1k_target_ops or1k_target_dummy =
|
{
|
{
|
"dummy",
|
"dummy",
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
NULL,
|
&or1k_dummy_ops,
|
&or1k_dummy_ops,
|
OPS_MAGIC
|
OPS_MAGIC
|
};
|
};
|
|
|
const char *str_err[] =
|
const char *str_err[] =
|
{
|
{
|
"None", "CRC error"
|
"None", "CRC error"
|
};
|
};
|
|
|
const char *status_name[] =
|
const char *status_name[] =
|
{
|
{
|
"UNDEFINED", "CONNECTING", "DISCONNECTING", "RUNNING", "STOPPED"
|
"UNDEFINED", "CONNECTING", "DISCONNECTING", "RUNNING", "STOPPED"
|
};
|
};
|
|
|
/* Names for matchpoint related stuff. */
|
/* Names for matchpoint related stuff. */
|
static char *ct_names[] =
|
static char *ct_names[] =
|
{
|
{
|
"DIS", "IFEA", "LEA", "SEA", "AEA", "LDATA", "SDATA", "ADATA"
|
"DIS", "IFEA", "LEA", "SEA", "AEA", "LDATA", "SDATA", "ADATA"
|
};
|
};
|
|
|
static char *cc_names[] =
|
static char *cc_names[] =
|
{
|
{
|
"&", "==", "<", "<=", ">", ">=", "!=", "ERR"
|
"&", "==", "<", "<=", ">", ">=", "!=", "ERR"
|
};
|
};
|
|
|
static char *ch_names[] =
|
static char *ch_names[] =
|
{
|
{
|
"ERR", "&", "|", "ERR"
|
"ERR", "&", "|", "ERR"
|
};
|
};
|
|
|
/* Implementation specific information. Set by or1k_initialize. */
|
/* Implementation specific information. Set by or1k_initialize. */
|
struct struct_or1k_implementation or1k_implementation;
|
struct struct_or1k_implementation or1k_implementation;
|
|
|
/* Current target status. */
|
/* Current target status. */
|
static enum target_status or1k_status = TARGET_UNDEFINED;
|
static enum target_status or1k_status = TARGET_UNDEFINED;
|
|
|
/* The target vector. */
|
/* The target vector. */
|
struct target_ops or1k_dummy_ops, or1k_jtag_ops, or1k_sim_ops;
|
struct target_ops or1k_dummy_ops, or1k_jtag_ops, or1k_sim_ops;
|
|
|
/* Currently active target description (if or1k_is_open == 1) */
|
/* Currently active target description (if or1k_is_open == 1) */
|
static struct target_ops *current_ops;
|
static struct target_ops *current_ops;
|
|
|
/* Currently active or1k target operations. */
|
/* Currently active or1k target operations. */
|
static struct or1k_target_ops *current_or1k_target = NULL;
|
static struct or1k_target_ops *current_or1k_target = NULL;
|
|
|
/* Set to 1 if the target is open. */
|
/* Set to 1 if the target is open. */
|
static int or1k_is_open = 0;
|
static int or1k_is_open = 0;
|
|
|
/* Error last occured, zero = ok. */
|
/* Error last occured, zero = ok. */
|
int err = 0;
|
int err = 0;
|
|
|
/* Nonzero, if we changed something (except DMR1 which is updated on every run anyway). */
|
/* Nonzero, if we changed something (except DMR1 which is updated on every run anyway). */
|
int debug_regs_changed;
|
int debug_regs_changed;
|
|
|
/* Number of interrupts while waiting for process. */
|
/* Number of interrupts while waiting for process. */
|
static int interrupt_count = 0;
|
static int interrupt_count = 0;
|
|
|
/* Reason of last stop. */
|
/* Reason of last stop. */
|
static int hit_watchpoint = 0;
|
static int hit_watchpoint = 0;
|
|
|
/* Current register values. */
|
/* Current register values. */
|
unsigned int dmr1 = 0;
|
unsigned int dmr1 = 0;
|
unsigned int dmr2 = 0;
|
unsigned int dmr2 = 0;
|
unsigned int dsr = 0;
|
unsigned int dsr = 0;
|
unsigned int drr = 0;
|
unsigned int drr = 0;
|
|
|
/* Current matchpoints. */
|
/* Current matchpoints. */
|
unsigned int dvr[MAX_MATCHPOINTS];
|
unsigned int dvr[MAX_MATCHPOINTS];
|
struct dcr_struct dcr[MAX_MATCHPOINTS];
|
struct dcr_struct dcr[MAX_MATCHPOINTS];
|
|
|
/* Number of matchpoint users */
|
/* Number of matchpoint users */
|
int matchpoint_user_count[MAX_MATCHPOINTS] = {0};
|
int matchpoint_user_count[MAX_MATCHPOINTS] = {0};
|
|
|
/* Old SIGINT handler. */
|
/* Old SIGINT handler. */
|
static void (*ofunc) PARAMS ((int));
|
static void (*ofunc) PARAMS ((int));
|
|
|
|
|
/* Handle low-level error that we can't recover from. Note that just
|
/* 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
|
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
|
all hell to break loose--the rest of GDB will tend to get left in an
|
inconsistent state. */
|
inconsistent state. */
|
|
|
static NORETURN void
|
static NORETURN void
|
or1k_error (char *string,...)
|
or1k_error (char *string,...)
|
{
|
{
|
va_list args;
|
va_list args;
|
|
|
va_start (args, string);
|
va_start (args, string);
|
|
|
target_terminal_ours ();
|
target_terminal_ours ();
|
wrap_here (""); /* Force out any buffered output */
|
wrap_here (""); /* Force out any buffered output */
|
gdb_flush (gdb_stdout);
|
gdb_flush (gdb_stdout);
|
if (error_pre_print)
|
if (error_pre_print)
|
fprintf_filtered (gdb_stderr, error_pre_print);
|
fprintf_filtered (gdb_stderr, error_pre_print);
|
vfprintf_filtered (gdb_stderr, string, args);
|
vfprintf_filtered (gdb_stderr, string, args);
|
fprintf_filtered (gdb_stderr, "\n");
|
fprintf_filtered (gdb_stderr, "\n");
|
va_end (args);
|
va_end (args);
|
gdb_flush (gdb_stderr);
|
gdb_flush (gdb_stderr);
|
|
|
/* Clean up in such a way that or1k_close won't try to talk to the
|
/* 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
|
board (it almost surely won't work since we weren't able to talk to
|
it). */
|
it). */
|
or1k_is_open = 0;
|
or1k_is_open = 0;
|
|
|
printf_unfiltered ("Ending remote or1k debugging.\n");
|
printf_unfiltered ("Ending remote or1k debugging.\n");
|
target_mourn_inferior ();
|
target_mourn_inferior ();
|
|
|
return_to_top_level (RETURN_ERROR);
|
return_to_top_level (RETURN_ERROR);
|
}
|
}
|
|
|
const char *
|
const char *
|
or1k_err_name (e)
|
or1k_err_name (e)
|
int e;
|
int e;
|
{
|
{
|
return str_err[e];
|
return str_err[e];
|
}
|
}
|
|
|
/* putc_readable - print a character, displaying non-printable chars in
|
/* putc_readable - print a character, displaying non-printable chars in
|
^x notation or in hex. */
|
^x notation or in hex. */
|
|
|
static void
|
static void
|
fputc_readable (ch, file)
|
fputc_readable (ch, file)
|
int ch;
|
int ch;
|
struct ui_file *file;
|
struct ui_file *file;
|
{
|
{
|
if (ch == '\n')
|
if (ch == '\n')
|
fputc_unfiltered ('\n', file);
|
fputc_unfiltered ('\n', file);
|
else if (ch == '\r')
|
else if (ch == '\r')
|
fprintf_unfiltered (file, "\\r");
|
fprintf_unfiltered (file, "\\r");
|
else if (ch < 0x20) /* ASCII control character */
|
else if (ch < 0x20) /* ASCII control character */
|
fprintf_unfiltered (file, "^%c", ch + '@');
|
fprintf_unfiltered (file, "^%c", ch + '@');
|
else if (ch >= 0x7f) /* non-ASCII characters (rubout or greater) */
|
else if (ch >= 0x7f) /* non-ASCII characters (rubout or greater) */
|
fprintf_unfiltered (file, "[%02x]", ch & 0xff);
|
fprintf_unfiltered (file, "[%02x]", ch & 0xff);
|
else
|
else
|
fputc_unfiltered (ch, file);
|
fputc_unfiltered (ch, file);
|
}
|
}
|
|
|
|
|
/* puts_readable - print a string, displaying non-printable chars in
|
/* puts_readable - print a string, displaying non-printable chars in
|
^x notation or in hex. */
|
^x notation or in hex. */
|
|
|
static void
|
static void
|
fputs_readable (string, file)
|
fputs_readable (string, file)
|
char *string;
|
char *string;
|
struct ui_file *file;
|
struct ui_file *file;
|
{
|
{
|
int c;
|
int c;
|
|
|
while ((c = *string++) != '\0')
|
while ((c = *string++) != '\0')
|
fputc_readable (c, file);
|
fputc_readable (c, file);
|
}
|
}
|
|
|
/* Sets scan chain. */
|
/* Sets scan chain. */
|
|
|
static void
|
static void
|
or1k_set_chain (chain)
|
or1k_set_chain (chain)
|
int chain;
|
int chain;
|
{
|
{
|
if (current_or1k_target != NULL && current_or1k_target->to_set_chain != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_set_chain != NULL)
|
current_or1k_target->to_set_chain (chain);
|
current_or1k_target->to_set_chain (chain);
|
}
|
}
|
|
|
/* Sets register/memory regno to data. */
|
/* Sets register/memory regno to data. */
|
|
|
static void
|
static void
|
or1k_write_reg (regno, data)
|
or1k_write_reg (regno, data)
|
unsigned int regno;
|
unsigned int regno;
|
ULONGEST data;
|
ULONGEST data;
|
{
|
{
|
if (current_or1k_target != NULL && current_or1k_target->to_write_reg != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_write_reg != NULL)
|
current_or1k_target->to_write_reg (regno, data);
|
current_or1k_target->to_write_reg (regno, data);
|
}
|
}
|
|
|
/* Reads register/memory from regno. */
|
/* Reads register/memory from regno. */
|
|
|
static ULONGEST
|
static ULONGEST
|
or1k_read_reg (regno)
|
or1k_read_reg (regno)
|
unsigned int regno;
|
unsigned int regno;
|
{
|
{
|
if (current_or1k_target != NULL && current_or1k_target->to_read_reg != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_read_reg != NULL)
|
return current_or1k_target->to_read_reg (regno);
|
return current_or1k_target->to_read_reg (regno);
|
else
|
else
|
return 0x1234;
|
return 0x1234;
|
}
|
}
|
|
|
/* Sets SPR register regno to data. */
|
/* Sets SPR register regno to data. */
|
|
|
void
|
void
|
or1k_write_spr_reg (regno, data)
|
or1k_write_spr_reg (regno, data)
|
unsigned int regno;
|
unsigned int regno;
|
unsigned int data;
|
unsigned int data;
|
{
|
{
|
or1k_set_chain (SC_RISC_DEBUG);
|
or1k_set_chain (SC_RISC_DEBUG);
|
or1k_write_reg (regno, (ULONGEST)data);
|
or1k_write_reg (regno, (ULONGEST)data);
|
}
|
}
|
|
|
/* Reads register SPR from regno. */
|
/* Reads register SPR from regno. */
|
|
|
unsigned int
|
unsigned int
|
or1k_read_spr_reg (regno)
|
or1k_read_spr_reg (regno)
|
unsigned int regno;
|
unsigned int regno;
|
{
|
{
|
or1k_set_chain (SC_RISC_DEBUG);
|
or1k_set_chain (SC_RISC_DEBUG);
|
return or1k_read_reg (regno);
|
return or1k_read_reg (regno);
|
}
|
}
|
|
|
/* Sets mem to data. */
|
/* Sets mem to data. */
|
|
|
void
|
void
|
or1k_write_mem (addr, data)
|
or1k_write_mem (addr, data)
|
unsigned int addr;
|
unsigned int addr;
|
unsigned int data;
|
unsigned int data;
|
{
|
{
|
or1k_set_chain (SC_WISHBONE);
|
or1k_set_chain (SC_WISHBONE);
|
or1k_write_reg (addr, (ULONGEST)data);
|
or1k_write_reg (addr, (ULONGEST)data);
|
}
|
}
|
|
|
/* Reads register SPR from regno. */
|
/* Reads register SPR from regno. */
|
|
|
unsigned int
|
unsigned int
|
or1k_read_mem (addr)
|
or1k_read_mem (addr)
|
unsigned int addr;
|
unsigned int addr;
|
{
|
{
|
or1k_set_chain (SC_WISHBONE);
|
or1k_set_chain (SC_WISHBONE);
|
return or1k_read_reg (addr);
|
return or1k_read_reg (addr);
|
}
|
}
|
|
|
/* Stalls the CPU. */
|
/* Stalls the CPU. */
|
|
|
static void
|
static void
|
or1k_stall ()
|
or1k_stall ()
|
{
|
{
|
int val;
|
int val;
|
or1k_set_chain (SC_REGISTER);
|
or1k_set_chain (SC_REGISTER);
|
val = or1k_read_reg (JTAG_RISCOP);
|
val = or1k_read_reg (JTAG_RISCOP);
|
or1k_write_reg (JTAG_RISCOP, val | 1);
|
or1k_write_reg (JTAG_RISCOP, val | 1);
|
or1k_read_reg (JTAG_RISCOP);
|
or1k_read_reg (JTAG_RISCOP);
|
|
|
/* Be cautious - disable trace. */
|
/* Be cautious - disable trace. */
|
val = or1k_read_reg (JTAG_MODER);
|
val = or1k_read_reg (JTAG_MODER);
|
or1k_write_reg (JTAG_MODER, val & ~2);
|
or1k_write_reg (JTAG_MODER, val & ~2);
|
}
|
}
|
|
|
/* Unstalls the CPU. */
|
/* Unstalls the CPU. */
|
|
|
static void
|
static void
|
or1k_unstall ()
|
or1k_unstall ()
|
{
|
{
|
unsigned int val;
|
unsigned int val;
|
|
|
|
|
or1k_set_chain (SC_REGISTER);
|
or1k_set_chain (SC_REGISTER);
|
val = or1k_read_reg (JTAG_RISCOP);
|
val = or1k_read_reg (JTAG_RISCOP);
|
or1k_write_reg (JTAG_RISCOP, val & ~1);
|
or1k_write_reg (JTAG_RISCOP, val & ~1);
|
or1k_read_reg (JTAG_RISCOP);
|
or1k_read_reg (JTAG_RISCOP);
|
}
|
}
|
|
|
/* Resets the CPU and stalls it. */
|
/* Resets the CPU and stalls it. */
|
|
|
static void
|
static void
|
or1k_reset ()
|
or1k_reset ()
|
{
|
{
|
unsigned int val;
|
unsigned int val;
|
int i;
|
int i;
|
debug ("%08x\n", or1k_read_reg (JTAG_RISCOP));
|
debug ("%08x\n", or1k_read_reg (JTAG_RISCOP));
|
or1k_set_chain (SC_REGISTER);
|
or1k_set_chain (SC_REGISTER);
|
|
|
/* Be cautious - disable trace. */
|
/* Be cautious - disable trace. */
|
val = or1k_read_reg (JTAG_MODER);
|
val = or1k_read_reg (JTAG_MODER);
|
or1k_write_reg (JTAG_MODER, val & ~2);
|
or1k_write_reg (JTAG_MODER, val & ~2);
|
|
|
val = or1k_read_reg (JTAG_RISCOP);
|
val = or1k_read_reg (JTAG_RISCOP);
|
val &= ~3;
|
val &= ~3;
|
/* Assert reset signal. */
|
/* Assert reset signal. */
|
or1k_write_reg (JTAG_RISCOP, val | 3);
|
or1k_write_reg (JTAG_RISCOP, val | 3);
|
|
|
/* Just do something */
|
/* Just do something */
|
for (i = 0; i < 100; i++)
|
for (i = 0; i < 100; i++)
|
or1k_read_reg (JTAG_RISCOP);
|
or1k_read_reg (JTAG_RISCOP);
|
|
|
/* give it some time */
|
/* give it some time */
|
usleep (1000);
|
usleep (1000);
|
|
|
or1k_set_chain (SC_REGISTER);
|
or1k_set_chain (SC_REGISTER);
|
/* Release reset signal, but keep in stall state. */
|
/* Release reset signal, but keep in stall state. */
|
or1k_write_reg (JTAG_RISCOP, val | 1);
|
or1k_write_reg (JTAG_RISCOP, val | 1);
|
or1k_read_reg (JTAG_RISCOP);
|
or1k_read_reg (JTAG_RISCOP);
|
}
|
}
|
|
|
/* Synchronizes debug registers in memory with those on target,
|
/* Synchronizes debug registers in memory with those on target,
|
if there is any change. */
|
if there is any change. */
|
|
|
static void
|
static void
|
or1k_commit_debug_registers ()
|
or1k_commit_debug_registers ()
|
{
|
{
|
int i;
|
int i;
|
unsigned int u;
|
unsigned int u;
|
if (!debug_regs_changed)
|
if (!debug_regs_changed)
|
return;
|
return;
|
|
|
/* Matchpoints (breakpoints, watchpoints). */
|
/* Matchpoints (breakpoints, watchpoints). */
|
for (i = 0; i < NUM_MATCHPOINTS; i++)
|
for (i = 0; i < NUM_MATCHPOINTS; i++)
|
{
|
{
|
unsigned int u;
|
unsigned int u;
|
or1k_write_spr_reg (DVR0_SPRNUM + i, dvr[i]);
|
or1k_write_spr_reg (DVR0_SPRNUM + i, dvr[i]);
|
memcpy (&u, &dcr[i], sizeof(dcr[i]));
|
memcpy (&u, &dcr[i], sizeof(dcr[i]));
|
or1k_write_spr_reg (DCR0_SPRNUM + i, u);
|
or1k_write_spr_reg (DCR0_SPRNUM + i, u);
|
}
|
}
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
or1k_write_spr_reg (DMR2_SPRNUM, dmr2);
|
or1k_write_spr_reg (DMR2_SPRNUM, dmr2);
|
|
|
/* Trace dependent. */
|
/* Trace dependent. */
|
or1k_set_chain (SC_REGISTER);
|
or1k_set_chain (SC_REGISTER);
|
memcpy (&u, &or1k_htrace.trig, sizeof(or1k_htrace.trig));
|
memcpy (&u, &or1k_htrace.trig, sizeof(or1k_htrace.trig));
|
or1k_write_reg (JTAG_TSEL, u);
|
or1k_write_reg (JTAG_TSEL, u);
|
memcpy (&u, &or1k_htrace.qual, sizeof(or1k_htrace.qual));
|
memcpy (&u, &or1k_htrace.qual, sizeof(or1k_htrace.qual));
|
or1k_write_reg (JTAG_QSEL, u);
|
or1k_write_reg (JTAG_QSEL, u);
|
memcpy (&u, &or1k_htrace.stop, sizeof(or1k_htrace.stop));
|
memcpy (&u, &or1k_htrace.stop, sizeof(or1k_htrace.stop));
|
or1k_write_reg (JTAG_SSEL, u);
|
or1k_write_reg (JTAG_SSEL, u);
|
debug_regs_changed = 0;
|
debug_regs_changed = 0;
|
for (i = 0; i < NUM_RECORDS; i++)
|
for (i = 0; i < NUM_RECORDS; i++)
|
{
|
{
|
memcpy (&u, &or1k_htrace.recwp[i], sizeof(or1k_htrace.recwp[i]));
|
memcpy (&u, &or1k_htrace.recwp[i], sizeof(or1k_htrace.recwp[i]));
|
or1k_write_reg (JTAG_RECWP0 + i, u);
|
or1k_write_reg (JTAG_RECWP0 + i, u);
|
}
|
}
|
memcpy (&u, &or1k_htrace.recbp, sizeof(or1k_htrace.recbp));
|
memcpy (&u, &or1k_htrace.recbp, sizeof(or1k_htrace.recbp));
|
or1k_write_reg (JTAG_RECBP0, u);
|
or1k_write_reg (JTAG_RECBP0, u);
|
memcpy (&u, &or1k_htrace.moder, sizeof(or1k_htrace.moder));
|
memcpy (&u, &or1k_htrace.moder, sizeof(or1k_htrace.moder));
|
or1k_write_reg (JTAG_MODER, u);
|
or1k_write_reg (JTAG_MODER, u);
|
}
|
}
|
|
|
static void
|
static void
|
or1k_set_undefined_cleanups (arg)
|
or1k_set_undefined_cleanups (arg)
|
PTR arg;
|
PTR arg;
|
{
|
{
|
or1k_status = TARGET_UNDEFINED;
|
or1k_status = TARGET_UNDEFINED;
|
}
|
}
|
|
|
/* Initialize a new connection to the or1k board, and make sure we are
|
/* Initialize a new connection to the or1k board, and make sure we are
|
really connected. */
|
really connected. */
|
|
|
static void
|
static void
|
or1k_init (args)
|
or1k_init (args)
|
char *args;
|
char *args;
|
{
|
{
|
struct cleanup *old_cleanups = make_cleanup (or1k_set_undefined_cleanups, NULL);
|
struct cleanup *old_cleanups = make_cleanup (or1k_set_undefined_cleanups, NULL);
|
int i;
|
int i;
|
unsigned int tmp;
|
unsigned int tmp;
|
FILE *f;
|
FILE *f;
|
|
|
/* What is this code doing here? I don't see any way it can happen, and
|
/* 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.
|
it might mean or1k_initializing didn't get cleared properly.
|
So I'll make it a warning. */
|
So I'll make it a warning. */
|
|
|
if (or1k_status == TARGET_CONNECTING)
|
if (or1k_status == TARGET_CONNECTING)
|
{
|
{
|
warning ("internal error: or1k_initialize called twice");
|
warning ("internal error: or1k_initialize called twice");
|
return;
|
return;
|
}
|
}
|
|
|
or1k_status = TARGET_CONNECTING;
|
or1k_status = TARGET_CONNECTING;
|
if (current_or1k_target != NULL && current_or1k_target->to_init != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_init != NULL)
|
current_or1k_target->to_init (args);
|
current_or1k_target->to_init (args);
|
|
|
debug("%08x\n", read_pc ());
|
debug("%08x\n", read_pc ());
|
or1k_stall ();
|
or1k_stall ();
|
debug("%08x\n", read_pc ());
|
debug("%08x\n", read_pc ());
|
usleep (1000);
|
usleep (1000);
|
|
|
/* Determine implementation configuration. */
|
/* Determine implementation configuration. */
|
or1k_implementation.VR = or1k_read_spr_reg (VR_SPRNUM);
|
or1k_implementation.VR = or1k_read_spr_reg (VR_SPRNUM);
|
or1k_implementation.UPR = or1k_read_spr_reg (UPR_SPRNUM);
|
or1k_implementation.UPR = or1k_read_spr_reg (UPR_SPRNUM);
|
|
|
/* Determine number of gpr_regs. */
|
/* Determine number of gpr_regs. */
|
tmp = or1k_read_spr_reg (CPUCFGR_SPRNUM);
|
tmp = or1k_read_spr_reg (CPUCFGR_SPRNUM);
|
or1k_implementation.num_gpr_regs = ((tmp >> 4) & 1)?(16):(32);
|
or1k_implementation.num_gpr_regs = ((tmp >> 4) & 1)?(16):(32);
|
|
|
/* Is any vector or floating point support present? */
|
/* Is any vector or floating point support present? */
|
or1k_implementation.vf_present = ((tmp >> 7) & 7) != 0;
|
or1k_implementation.vf_present = ((tmp >> 7) & 7) != 0;
|
or1k_implementation.num_vfpr_regs = (or1k_implementation.vf_present)?(32):(0);
|
or1k_implementation.num_vfpr_regs = (or1k_implementation.vf_present)?(32):(0);
|
|
|
/* Determine max number of supported matchpoints. */
|
/* Determine max number of supported matchpoints. */
|
tmp = or1k_read_spr_reg (DCFGR_SPRNUM);
|
tmp = or1k_read_spr_reg (DCFGR_SPRNUM);
|
or1k_implementation.num_matchpoints = tmp & 7;
|
or1k_implementation.num_matchpoints = tmp & 7;
|
or1k_implementation.num_used_matchpoints = 0;
|
or1k_implementation.num_used_matchpoints = 0;
|
or1k_implementation.has_counters = tmp & 4 == 1;
|
or1k_implementation.has_counters = tmp & 4 == 1;
|
|
|
/* Is implementation supported? */
|
/* Is implementation supported? */
|
|
|
/* First we should have system and debug groups implemented. */
|
/* First we should have system and debug groups implemented. */
|
if (or1k_implementation.VR & (1 << SPR_SYSTEM_GROUP) == 0)
|
if (or1k_implementation.VR & (1 << SPR_SYSTEM_GROUP) == 0)
|
error ("System group should be available in the or1k implementation.");
|
error ("System group should be available in the or1k implementation.");
|
if (or1k_implementation.VR & (1 << SPR_DEBUG_GROUP) == 0)
|
if (or1k_implementation.VR & (1 << SPR_DEBUG_GROUP) == 0)
|
error ("Debug group should be available in the or1k implementation.");
|
error ("Debug group should be available in the or1k implementation.");
|
if (or1k_implementation.has_counters)
|
if (or1k_implementation.has_counters)
|
warning ("Counters not supported.");
|
warning ("Counters not supported.");
|
|
|
/* Delete break, watch, catch points. */
|
/* Delete break, watch, catch points. */
|
for(i = 0; i < NUM_MATCHPOINTS; i++)
|
for(i = 0; i < NUM_MATCHPOINTS; i++)
|
{
|
{
|
memset (&dcr[i], 0, sizeof (dcr[i]));
|
memset (&dcr[i], 0, sizeof (dcr[i]));
|
matchpoint_user_count[i] = 0;
|
matchpoint_user_count[i] = 0;
|
}
|
}
|
|
|
dmr1 = 0;
|
dmr1 = 0;
|
dmr2 = 0;
|
dmr2 = 0;
|
memset (&or1k_htrace, 0, sizeof (or1k_htrace));
|
memset (&or1k_htrace, 0, sizeof (or1k_htrace));
|
|
|
/* RECSELDEPEND = 0 does not match our trace scheme. */
|
/* RECSELDEPEND = 0 does not match our trace scheme. */
|
or1k_htrace.moder.rec_sel_dep = 1;
|
or1k_htrace.moder.rec_sel_dep = 1;
|
|
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
or1k_commit_debug_registers ();
|
or1k_commit_debug_registers ();
|
|
|
if (err != 0)
|
if (err != 0)
|
error ("Cannot connect.");
|
error ("Cannot connect.");
|
|
|
/* Enable exceptions */
|
/* Enable exceptions */
|
or1k_write_spr_reg (SR_SPRNUM, or1k_read_spr_reg(SR_SPRNUM) | 0x2);
|
or1k_write_spr_reg (SR_SPRNUM, or1k_read_spr_reg(SR_SPRNUM) | 0x2);
|
|
|
/* Stop when breakpoint occurs. */
|
/* Stop when breakpoint occurs. */
|
or1k_write_spr_reg (DSR_SPRNUM, 0x2000);
|
or1k_write_spr_reg (DSR_SPRNUM, dsr = 0x2000);
|
|
|
do_cleanups (old_cleanups);
|
do_cleanups (old_cleanups);
|
|
|
/* This should cause an error if not connected. */
|
/* This should cause an error if not connected. */
|
or1k_fetch_registers (-1);
|
or1k_fetch_registers (-1);
|
|
|
set_current_frame (create_new_frame (read_fp (), read_pc ()));
|
set_current_frame (create_new_frame (read_fp (), read_pc ()));
|
select_frame (get_current_frame (), 0);
|
select_frame (get_current_frame (), 0);
|
|
|
/* Just empty it. */
|
/* Just empty it. */
|
if ((f = fopen (TRACE_FILENAME, "wb+")) == NULL)
|
if ((f = fopen (TRACE_FILENAME, "wb+")) == NULL)
|
error ("Cannot open trace file.");
|
error ("Cannot open trace file.");
|
fclose (f);
|
fclose (f);
|
trace_size = 0;
|
trace_size = 0;
|
or1k_status = TARGET_STOPPED;
|
or1k_status = TARGET_STOPPED;
|
}
|
}
|
|
|
/* Kill the process running on the board. */
|
/* Kill the process running on the board. */
|
|
|
void
|
void
|
or1k_kill ()
|
or1k_kill ()
|
{
|
{
|
if (or1k_status != TARGET_RUNNING)
|
if (or1k_status != TARGET_RUNNING)
|
return;
|
return;
|
or1k_status = TARGET_UNDEFINED;
|
or1k_status = TARGET_UNDEFINED;
|
or1k_reset();
|
or1k_reset();
|
or1k_status = TARGET_STOPPED;
|
or1k_status = TARGET_STOPPED;
|
|
|
inferior_pid = 0;
|
inferior_pid = 0;
|
}
|
}
|
|
|
/* Open a connection to the remote board. */
|
/* Open a connection to the remote board. */
|
|
|
static void
|
static void
|
or1k_open (name, from_tty)
|
or1k_open (name, from_tty)
|
char *name;
|
char *name;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
or1k_init (name);
|
or1k_init (name);
|
|
|
/* Switch to using remote target now. */
|
/* Switch to using remote target now. */
|
current_ops = current_or1k_target->gdb_ops;
|
current_ops = current_or1k_target->gdb_ops;
|
or1k_is_open = 1;
|
or1k_is_open = 1;
|
push_target (current_ops);
|
push_target (current_ops);
|
|
|
/* FIXME: Should we call start_remote here? */
|
/* FIXME: Should we call start_remote here? */
|
|
|
/* This is really the job of start_remote however, that makes an assumption
|
/* 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
|
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
|
doesn't happen here (in fact, it may not be possible to get the monitor to
|
send the appropriate packet). */
|
send the appropriate packet). */
|
|
|
flush_cached_frames ();
|
flush_cached_frames ();
|
registers_changed ();
|
registers_changed ();
|
stop_pc = read_pc ();
|
stop_pc = read_pc ();
|
set_current_frame (create_new_frame (read_fp (), stop_pc));
|
set_current_frame (create_new_frame (read_fp (), stop_pc));
|
select_frame (get_current_frame (), 0);
|
select_frame (get_current_frame (), 0);
|
print_stack_frame (selected_frame, -1, 1);
|
print_stack_frame (selected_frame, -1, 1);
|
}
|
}
|
|
|
/* This is the generic stop called via the target vector. When a target
|
/* This is the generic stop called via the target vector. When a target
|
interrupt is requested, either by the command line or the GUI, we
|
interrupt is requested, either by the command line or the GUI, we
|
will eventually end up here. */
|
will eventually end up here. */
|
static void
|
static void
|
or1k_stop ()
|
or1k_stop ()
|
{
|
{
|
/* Send a break or a ^C, depending on user preference. */
|
/* Send a break or a ^C, depending on user preference. */
|
debug ("remote_stop called\n");
|
debug ("remote_stop called\n");
|
|
|
/* We should not stop the target immediately, since it can be in an
|
/* We should not stop the target immediately, since it can be in an
|
unfinished state. So we do a single step. This should not affect
|
unfinished state. So we do a single step. This should not affect
|
on anything. */
|
on anything. */
|
or1k_stall ();
|
or1k_stall ();
|
/* HW STEP. Set DMR1_ST. */
|
/* HW STEP. Set DMR1_ST. */
|
dmr1 |= DMR1_ST;
|
dmr1 |= DMR1_ST;
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
dmr1 &= ~DMR1_ST;
|
dmr1 &= ~DMR1_ST;
|
or1k_unstall ();
|
or1k_unstall ();
|
}
|
}
|
|
|
/* Close a connection to the remote board. */
|
/* Close a connection to the remote board. */
|
|
|
static void
|
static void
|
or1k_close (quitting)
|
or1k_close (quitting)
|
int quitting;
|
int quitting;
|
{
|
{
|
if (or1k_is_open)
|
if (or1k_is_open)
|
{
|
{
|
or1k_kill ();
|
or1k_kill ();
|
if (current_or1k_target != NULL && current_or1k_target->to_done != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_done != NULL)
|
current_or1k_target->to_done ();
|
current_or1k_target->to_done ();
|
current_or1k_target = NULL;
|
current_or1k_target = NULL;
|
}
|
}
|
generic_mourn_inferior ();
|
generic_mourn_inferior ();
|
}
|
}
|
|
|
/* Detach from the remote board. */
|
/* Detach from the remote board. */
|
|
|
static void
|
static void
|
or1k_detach (args, from_tty)
|
or1k_detach (args, from_tty)
|
char *args;
|
char *args;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
if (args)
|
if (args)
|
error ("Argument given to \"detach\" when remotely debugging.");
|
error ("Argument given to \"detach\" when remotely debugging.");
|
|
|
pop_target ();
|
pop_target ();
|
|
|
or1k_close (1);
|
or1k_close (1);
|
|
|
if (from_tty)
|
if (from_tty)
|
printf_unfiltered ("Ending remote or1k debugging.\n");
|
printf_unfiltered ("Ending remote or1k debugging.\n");
|
}
|
}
|
|
|
/* Appends trace data to the trace file. */
|
/* Appends trace data to the trace file. */
|
|
|
static void
|
static void
|
or1k_read_trace ()
|
or1k_read_trace ()
|
{
|
{
|
struct htrace_data_struct data;
|
struct htrace_data_struct data;
|
ULONGEST tmp;
|
ULONGEST tmp;
|
int first = 1;
|
int first = 1;
|
FILE *fd;
|
FILE *fd;
|
if ((fd = fopen (TRACE_FILENAME, "ab")) == NULL)
|
if ((fd = fopen (TRACE_FILENAME, "ab")) == NULL)
|
{
|
{
|
warning ("Cannot append to trace file.");
|
warning ("Cannot append to trace file.");
|
return;
|
return;
|
}
|
}
|
|
|
or1k_set_chain (SC_TRACE);
|
or1k_set_chain (SC_TRACE);
|
while (1)
|
while (1)
|
{
|
{
|
tmp = or1k_read_reg (0);
|
tmp = or1k_read_reg (0);
|
memcpy (&data, &tmp, sizeof (data));
|
memcpy (&data, &tmp, sizeof (data));
|
|
|
/* Last record reached. */
|
/* Last record reached. */
|
if (!data.valid)
|
if (!data.valid)
|
break;
|
break;
|
data.valid = first;
|
data.valid = first;
|
first = 0;
|
first = 0;
|
if (!fwrite (&data, sizeof (data), 1, fd))
|
if (!fwrite (&data, sizeof (data), 1, fd))
|
{
|
{
|
warning ("Cannot write trace data");
|
warning ("Cannot write trace data");
|
break;
|
break;
|
}
|
}
|
}
|
}
|
fclose (fd);
|
fclose (fd);
|
}
|
}
|
|
|
/* ^C Interrupt handling */
|
/* ^C Interrupt handling */
|
|
|
/* Ask the user what to do when an interrupt is received. */
|
/* Ask the user what to do when an interrupt is received. */
|
static void
|
static void
|
interrupt_query ()
|
interrupt_query ()
|
{
|
{
|
target_terminal_ours ();
|
target_terminal_ours ();
|
|
|
if (query ("Interrupted while waiting for the program.\n\
|
if (query ("Interrupted while waiting for the program.\n\
|
Give up (and stop debugging it)? "))
|
Give up (and stop debugging it)? "))
|
{
|
{
|
target_mourn_inferior ();
|
target_mourn_inferior ();
|
return_to_top_level (RETURN_QUIT);
|
return_to_top_level (RETURN_QUIT);
|
}
|
}
|
|
|
target_terminal_inferior ();
|
target_terminal_inferior ();
|
}
|
}
|
|
|
static void or1k_interrupt_twice (int signo);
|
static void or1k_interrupt_twice (int signo);
|
|
|
|
|
/* The command line interface's stop routine. This function is installed
|
/* The command line interface's stop routine. This function is installed
|
as a signal handler for SIGINT. The first time a user requests a
|
as a signal handler for SIGINT. The first time a user requests a
|
stop, we call remote_stop to send a break or ^C. If there is no
|
stop, we call remote_stop to send a break or ^C. If there is no
|
response from the target (it didn't stop when the user requested it),
|
response from the target (it didn't stop when the user requested it),
|
we ask the user if he'd like to detach from the target. */
|
we ask the user if he'd like to detach from the target. */
|
static void
|
static void
|
or1k_interrupt (signo)
|
or1k_interrupt (signo)
|
int signo;
|
int signo;
|
{
|
{
|
debug ("interrupt");
|
debug ("interrupt");
|
/* If this doesn't work, try more severe steps. */
|
/* If this doesn't work, try more severe steps. */
|
signal (signo, or1k_interrupt_twice);
|
signal (signo, or1k_interrupt_twice);
|
|
|
debug ("remote_interrupt called\n");
|
debug ("remote_interrupt called\n");
|
|
|
interrupt_count++;
|
interrupt_count++;
|
}
|
}
|
|
|
/* The user typed ^C twice. */
|
/* The user typed ^C twice. */
|
|
|
static void
|
static void
|
or1k_interrupt_twice (signo)
|
or1k_interrupt_twice (signo)
|
int signo;
|
int signo;
|
{
|
{
|
debug ("interrupt2");
|
debug ("interrupt2");
|
signal (signo, ofunc);
|
signal (signo, ofunc);
|
interrupt_query ();
|
interrupt_query ();
|
signal (signo, or1k_interrupt);
|
signal (signo, or1k_interrupt);
|
}
|
}
|
|
|
/* Resume execution of the target process. STEP says whether to single-step
|
/* 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
|
or to run free; SIGGNAL is the signal value (e.g. SIGINT) to be given
|
to the target, or zero for no signal. */
|
to the target, or zero for no signal. */
|
|
|
static void
|
static void
|
or1k_resume (pid, step, siggnal)
|
or1k_resume (pid, step, siggnal)
|
int pid, step;
|
int pid, step;
|
enum target_signal siggnal;
|
enum target_signal siggnal;
|
{
|
{
|
debug ("pc = %08x\n", read_pc());
|
unsigned int pc;
|
|
unsigned int ppc;
|
|
unsigned int npc;
|
|
unsigned int val;
|
|
|
|
pc = read_pc();
|
|
npc = or1k_read_spr_reg (PC_SPRNUM);
|
|
ppc = or1k_read_spr_reg (PPC_SPRNUM);
|
|
debug ("pc = %08x BP = %x npc = %08x ppc = %08x\n", pc, breakpoint_here_p (pc), npc, ppc);
|
debug ("resume %i, %i, %i\n",step, siggnal, or1k_status);
|
debug ("resume %i, %i, %i\n",step, siggnal, or1k_status);
|
if (or1k_status != TARGET_STOPPED)
|
if (or1k_status != TARGET_STOPPED)
|
if (or1k_status == TARGET_RUNNING)
|
if (or1k_status == TARGET_RUNNING)
|
error ("Program is already running.");
|
error ("Program is already running.");
|
else
|
else
|
error ("The program is not being run.");
|
error ("The program is not being run.");
|
|
|
|
|
/* Clear reason register for later. */
|
/* Clear reason register for later. */
|
or1k_write_spr_reg (DRR_SPRNUM, 0);
|
or1k_write_spr_reg (DRR_SPRNUM, 0);
|
|
|
or1k_commit_debug_registers ();
|
or1k_commit_debug_registers ();
|
|
|
/* Else clause added by CZ 26/06/01 */
|
/* Else clause added by CZ 26/06/01 */
|
if (step)
|
if (step)
|
{
|
{
|
/* HW STEP. Set DMR1_ST. */
|
/* HW STEP. Set DMR1_ST. */
|
dmr1 |= DMR1_ST;
|
dmr1 |= DMR1_ST;
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
dmr1 &= ~DMR1_ST;
|
dmr1 &= ~DMR1_ST;
|
|
|
|
if (breakpoint_here_p (pc) && ((ppc + 4) != npc))
|
|
{
|
|
/* Trapped on delay slot instruction. */
|
|
/* Set PC to branch insn preceding delay slot. */
|
|
or1k_write_spr_reg (PC_SPRNUM, ppc - 4);
|
|
|
|
or1k_unstall ();
|
|
|
|
or1k_set_chain (SC_REGISTER);
|
|
val = or1k_read_reg (JTAG_RISCOP);
|
|
do {
|
|
val = or1k_read_reg (JTAG_RISCOP);
|
|
} while ((val & 1) == 0);
|
|
}
|
|
else if (breakpoint_here_p (npc) && insn_has_delay_slot (or1k_fetch_instruction (ppc)))
|
|
{
|
|
/* Steping to the trap insn in delay slot - we need to execute branch insn again */
|
|
debug ("resume: steping to the trap insn in delay slot\n");
|
|
or1k_write_spr_reg (PC_SPRNUM, ppc);
|
|
|
|
or1k_unstall ();
|
|
|
|
or1k_set_chain (SC_REGISTER);
|
|
val = or1k_read_reg (JTAG_RISCOP);
|
|
do {
|
|
val = or1k_read_reg (JTAG_RISCOP);
|
|
} while ((val & 1) == 0);
|
|
}
|
|
else if (breakpoint_here_p (pc))
|
|
or1k_write_spr_reg (PC_SPRNUM, pc);
|
}
|
}
|
else
|
else
|
{
|
{
|
dmr1 &= ~DMR1_ST;
|
dmr1 &= ~DMR1_ST;
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
or1k_write_spr_reg (DMR1_SPRNUM, dmr1);
|
}
|
}
|
|
|
/* We can now continue normally, independent of step */
|
/* We can now continue normally, independent of step */
|
or1k_unstall ();
|
or1k_unstall ();
|
or1k_status = TARGET_RUNNING;
|
or1k_status = TARGET_RUNNING;
|
debug ("-resume %i, %i, %i\n",step, siggnal, or1k_status);
|
debug ("-resume %i, %i, %i\n",step, siggnal, or1k_status);
|
}
|
}
|
|
|
/* Wait until the remote stops, and return a wait status. */
|
/* Wait until the remote stops, and return a wait status. */
|
|
|
static int
|
static int
|
or1k_wait (pid, status)
|
or1k_wait (pid, status)
|
int pid;
|
int pid;
|
struct target_waitstatus *status;
|
struct target_waitstatus *status;
|
{
|
{
|
unsigned long val;
|
unsigned long val;
|
unsigned long pc;
|
unsigned long pc;
|
|
unsigned long npc;
|
|
unsigned long ppc;
|
char buf[MAX_REGISTER_RAW_SIZE];
|
char buf[MAX_REGISTER_RAW_SIZE];
|
interrupt_count = 0;
|
interrupt_count = 0;
|
|
|
debug ("pc = %08x\n", read_pc());
|
|
debug ("wait %i %i\n", pid, or1k_status);
|
debug ("wait %i %i\n", pid, or1k_status);
|
/* If we have not sent a single step or continue command, then the
|
/* If we have not sent a single step or continue command, then the
|
board is waiting for us to do something. Return a status
|
board is waiting for us to do something. Return a status
|
indicating that it is stopped. */
|
indicating that it is stopped. */
|
if (or1k_status != TARGET_RUNNING)
|
if (or1k_status != TARGET_RUNNING)
|
{
|
{
|
if (or1k_status != TARGET_STOPPED)
|
if (or1k_status != TARGET_STOPPED)
|
error("Target in invalid state.");
|
error("Target in invalid state.");
|
status->kind = TARGET_WAITKIND_STOPPED;
|
status->kind = TARGET_WAITKIND_STOPPED;
|
status->value.sig = TARGET_SIGNAL_TRAP;
|
status->value.sig = TARGET_SIGNAL_TRAP;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (err)
|
if (err)
|
or1k_error ("Remote failure: %s", or1k_err_name (err));
|
or1k_error ("Remote failure: %s", or1k_err_name (err));
|
|
|
/* Set new signal handler */
|
/* Set new signal handler */
|
ofunc = signal (SIGINT, or1k_interrupt);
|
ofunc = signal (SIGINT, or1k_interrupt);
|
|
|
/* Wait for risc to stop. */
|
/* Wait for risc to stop. */
|
do {
|
do {
|
or1k_set_chain (SC_REGISTER);
|
or1k_set_chain (SC_REGISTER);
|
val = or1k_read_reg (JTAG_RISCOP);
|
val = or1k_read_reg (JTAG_RISCOP);
|
|
|
/* When we press Ctrl-C, interrupt count is set, but we must wait
|
/* When we press Ctrl-C, interrupt count is set, but we must wait
|
for or1k_read_reg to finish, otherwise we would interrupt transaction. */
|
for or1k_read_reg to finish, otherwise we would interrupt transaction. */
|
if (interrupt_count)
|
if (interrupt_count)
|
or1k_stop ();
|
or1k_stop ();
|
|
|
usleep (10);
|
usleep (10);
|
debug ("%i", val);
|
debug ("%i", val);
|
} while ((val & 1) == 0);
|
} while ((val & 1) == 0);
|
|
|
drr = or1k_read_spr_reg (DRR_SPRNUM);
|
drr = or1k_read_spr_reg (DRR_SPRNUM);
|
|
|
/* Restore old INT signal handler */
|
/* Restore old INT signal handler */
|
signal (SIGINT, ofunc);
|
signal (SIGINT, ofunc);
|
|
|
/* If we encounter breakpoint, drr is not set, so we set it manually. */
|
/* Single step does not set trap exception, so we set it manually to simplify our code */
|
if (!drr)
|
dmr1 = or1k_read_spr_reg (DMR1_SPRNUM);
|
drr |= DRR_BE;
|
if (dmr1 & DMR1_ST)
|
|
drr |= DRR_TE;
|
|
|
status->kind = TARGET_WAITKIND_STOPPED;
|
status->kind = TARGET_WAITKIND_STOPPED;
|
|
|
debug ("epcr0 = %08x\n", or1k_read_spr_reg (EPCR0_SPRNUM));
|
debug ("epcr0 = %08x\n", or1k_read_spr_reg (EPCR0_SPRNUM));
|
debug ("drr = %08x\n", drr);
|
debug ("drr = %08x\n", drr);
|
|
|
registers_changed ();
|
registers_changed ();
|
pc = read_pc ();
|
pc = read_pc ();
|
debug ("pc = %08x\n", pc);
|
npc = or1k_read_spr_reg (PC_SPRNUM);
|
|
ppc = or1k_read_spr_reg (PPC_SPRNUM);
|
|
debug ("npc = %08x ppc = %08x\n", npc, ppc);
|
|
|
if (drr & DRR_RSTE)
|
if (drr & DRR_TE)
|
|
{
|
|
/* If single step is not set, we should correct the pc. */
|
|
if (!(dmr1 & DMR1_ST))
|
|
/* PC has already stepped over the l.trap instruction. */
|
|
pc = ppc;
|
|
status->value.sig = TARGET_SIGNAL_TRAP;
|
|
drr &= ~DRR_TE;
|
|
}
|
|
else if (drr & DRR_RSTE)
|
|
{
|
status->value.sig = TARGET_SIGNAL_REALTIME_33;
|
status->value.sig = TARGET_SIGNAL_REALTIME_33;
|
|
drr &= ~DRR_RSTE;
|
|
}
|
else if (drr & DRR_BUSEE)
|
else if (drr & DRR_BUSEE)
|
|
{
|
status->value.sig = TARGET_SIGNAL_BUS;
|
status->value.sig = TARGET_SIGNAL_BUS;
|
else if (drr & DRR_DPFE)
|
drr &= ~DRR_BUSEE;
|
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)
|
else if (drr & DRR_AE)
|
|
{
|
status->value.sig = TARGET_SIGNAL_REALTIME_36;
|
status->value.sig = TARGET_SIGNAL_REALTIME_36;
|
|
drr &= ~DRR_AE;
|
|
}
|
else if (drr & DRR_IIE)
|
else if (drr & DRR_IIE)
|
|
{
|
status->value.sig = TARGET_SIGNAL_ILL;
|
status->value.sig = TARGET_SIGNAL_ILL;
|
else if (drr & DRR_HPINTE)
|
drr &= ~DRR_IIE;
|
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)
|
else if (drr & DRR_RE)
|
|
{
|
status->value.sig = TARGET_SIGNAL_REALTIME_39;
|
status->value.sig = TARGET_SIGNAL_REALTIME_39;
|
|
drr &= ~DRR_RE;
|
|
}
|
|
else if (drr & DRR_IME)
|
|
{
|
|
status->value.sig = TARGET_SIGNAL_REALTIME_38;
|
|
drr &= ~DRR_IME;
|
|
}
|
|
else if (drr & DRR_DME)
|
|
{
|
|
status->value.sig = TARGET_SIGNAL_REALTIME_37;
|
|
drr &= ~DRR_DME;
|
|
}
|
|
else if (drr & DRR_DPFE)
|
|
{
|
|
status->value.sig = TARGET_SIGNAL_REALTIME_34;
|
|
drr &= ~DRR_DPFE;
|
|
}
|
|
else if (drr & DRR_IPFE)
|
|
{
|
|
status->value.sig = TARGET_SIGNAL_REALTIME_35;
|
|
drr &= ~DRR_DPFE;
|
|
}
|
else if (drr & DRR_SCE)
|
else if (drr & DRR_SCE)
|
|
{
|
status->value.sig = TARGET_SIGNAL_REALTIME_40;
|
status->value.sig = TARGET_SIGNAL_REALTIME_40;
|
else if (drr & DRR_BE)
|
drr &= ~DRR_SCE;
|
status->value.sig = TARGET_SIGNAL_TRAP;
|
}
|
else if (drr & DRR_TE)
|
else if (drr & DRR_HPINTE)
|
|
{
|
|
status->value.sig = TARGET_SIGNAL_INT;
|
|
drr &= ~DRR_HPINTE;
|
|
}
|
|
else if (drr & DRR_LPINTE)
|
|
{
|
|
status->value.sig = TARGET_SIGNAL_INT;
|
|
drr &= ~DRR_LPINTE;
|
|
}
|
|
else
|
{
|
{
|
/* PC has already stepped over the l.trap instruction. */
|
|
pc -= 8;
|
|
status->value.sig = TARGET_SIGNAL_TRAP;
|
|
} else {
|
|
status->value.sig = TARGET_SIGNAL_UNKNOWN;
|
status->value.sig = TARGET_SIGNAL_UNKNOWN;
|
warning ("Invalid exception occured.");
|
warning ("Invalid exception occured.");
|
}
|
}
|
|
|
|
/* Update drr register */
|
|
or1k_write_spr_reg (DRR_SPRNUM, drr);
|
|
|
/* Write into PC flushes the pipeline! */
|
/* Write into PC flushes the pipeline! */
|
/* We got the number the register holds, but gdb expects to see a
|
/* We got the number the register holds, but gdb expects to see a
|
value in the target byte ordering. */
|
value in the target byte ordering. */
|
write_pc (pc);
|
/* write_pc (pc);
|
|
*/
|
|
store_unsigned_integer (buf, REGISTER_RAW_SIZE (PC_REGNUM), pc);
|
|
supply_register (PC_REGNUM, buf);
|
|
|
/*or1k_write_spr_reg (PC_SPRNUM, pc);
|
/*or1k_write_spr_reg (PC_SPRNUM, pc);
|
store_unsigned_integer (buf, REGISTER_RAW_SIZE (PC_REGNUM), pc);
|
store_unsigned_integer (buf, REGISTER_RAW_SIZE (PC_REGNUM), pc);
|
supply_register (PC_REGNUM, buf);*/
|
supply_register (PC_REGNUM, buf);*/
|
|
|
/* Log remote stop. */
|
/* Log remote stop. */
|
or1k_status = TARGET_STOPPED;
|
or1k_status = TARGET_STOPPED;
|
|
|
/* Determine what caused trap - breakpoint or watchpoint. */
|
/* Determine what caused trap - breakpoint or watchpoint. */
|
if (status->value.sig == TARGET_SIGNAL_TRAP)
|
if (status->value.sig == TARGET_SIGNAL_TRAP)
|
{
|
{
|
/* Search all active breakpoints for a match. */
|
/* Search all active breakpoints for a match. */
|
CORE_ADDR pc = read_pc ();
|
CORE_ADDR pc = read_pc ();
|
int breakpoint = 0;
|
int breakpoint = 0;
|
int i;
|
int i;
|
unsigned char break_bytes[4] = BRK_INSTR_STRUCT;
|
unsigned char break_bytes[4] = BRK_INSTR_STRUCT;
|
unsigned long b_insn = ntohl(*((unsigned long*)break_bytes));
|
unsigned long b_insn = ntohl(*((unsigned long*)break_bytes));
|
unsigned long value = pc;
|
unsigned long value = pc;
|
|
|
for (i = 0; i < or1k_implementation.num_used_matchpoints; i++)
|
for (i = 0; i < or1k_implementation.num_used_matchpoints; i++)
|
if (dvr[i] == pc && dcr[i].dp && dcr[i].cc == CC_EQUAL
|
if (dvr[i] == pc && dcr[i].dp && dcr[i].cc == CC_EQUAL
|
&& !dcr[i].sc && dcr[i].ct == CT_FETCH)
|
&& !dcr[i].sc && dcr[i].ct == CT_FETCH)
|
{
|
{
|
breakpoint = 1;
|
breakpoint = 1;
|
break;
|
break;
|
}
|
}
|
hit_watchpoint = !breakpoint;
|
hit_watchpoint = !breakpoint;
|
|
|
/* Cause the trap/breakpoint exception to be ignored. This is
|
/* Cause the trap/breakpoint exception to be ignored. This is
|
the behavior of the simulator when the PC value is changed
|
the behavior of the simulator when the PC value is changed
|
by a write command. All pending exceptions are cleared and
|
by a write command. All pending exceptions are cleared and
|
the simulator continues at the PC value specified. We need
|
the simulator continues at the PC value specified. We need
|
to do this if the instruction at the current PC has the
|
to do this if the instruction at the current PC has the
|
value BRK_INSTR_STRUCT */
|
value BRK_INSTR_STRUCT */
|
|
|
if(b_insn == or1k_read_mem((pc & 3)))
|
if(b_insn == or1k_read_mem((pc & 3)))
|
{
|
{
|
or1k_write_spr_reg(PC_SPRNUM,value);
|
or1k_write_spr_reg(PC_SPRNUM,value);
|
}
|
}
|
}
|
}
|
else
|
else
|
hit_watchpoint = 0;
|
hit_watchpoint = 0;
|
|
|
/* If the stop PC is in the _exit function, assume
|
/* If the stop PC is in the _exit function, assume
|
we hit the 'break 0x3ff' instruction in _exit, so this
|
we hit the 'break 0x3ff' instruction in _exit, so this
|
is not a normal breakpoint. */
|
is not a normal breakpoint. */
|
{
|
{
|
char *func_name;
|
char *func_name;
|
CORE_ADDR func_start;
|
CORE_ADDR func_start;
|
CORE_ADDR pc = read_pc ();
|
CORE_ADDR pc = read_pc ();
|
|
|
find_pc_partial_function (pc, &func_name, &func_start, NULL);
|
find_pc_partial_function (pc, &func_name, &func_start, NULL);
|
if (func_name != NULL && strcmp (func_name, "_exit") == 0
|
if (func_name != NULL && strcmp (func_name, "_exit") == 0
|
&& func_start == pc)
|
&& func_start == pc)
|
status->kind = TARGET_WAITKIND_EXITED;
|
status->kind = TARGET_WAITKIND_EXITED;
|
}
|
}
|
|
|
or1k_read_trace ();
|
or1k_read_trace ();
|
debug ("-wait %i %i\n", pid, or1k_status);
|
debug ("-wait %i %i\n", pid, or1k_status);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Fetch a word from the target board. All memory accesses to the
|
/* Fetch a word from the target board. All memory accesses to the
|
remote board are word aligned. */
|
remote board are word aligned. */
|
|
|
unsigned int
|
unsigned int
|
or1k_fetch_word (addr)
|
or1k_fetch_word (addr)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
{
|
{
|
if (addr & 3)
|
if (addr & 3)
|
{
|
{
|
int subaddr = addr & 3;
|
int subaddr = addr & 3;
|
unsigned char buf[8];
|
unsigned char buf[8];
|
unsigned int low, high;
|
unsigned int low, high;
|
addr >>= 2;
|
addr >>= 2;
|
low = or1k_read_mem (addr << 2);
|
low = or1k_read_mem (addr << 2);
|
high = or1k_read_reg ((addr + 1) << 2);
|
high = or1k_read_reg ((addr + 1) << 2);
|
memcpy (&buf[0], &low, 4);
|
memcpy (&buf[0], &low, 4);
|
memcpy (&buf[4], &high, 4);
|
memcpy (&buf[4], &high, 4);
|
memcpy (&low, &buf[subaddr], 4);
|
memcpy (&low, &buf[subaddr], 4);
|
return low;
|
return low;
|
}
|
}
|
else
|
else
|
{
|
{
|
return or1k_read_mem (addr);
|
return or1k_read_mem (addr);
|
}
|
}
|
}
|
}
|
|
|
/* Store a word to the target board. Returns errno code or zero for
|
/* Store a word to the target board. Returns errno code or zero for
|
success. All memory accesses to the remote board are word aligned. */
|
success. All memory accesses to the remote board are word aligned. */
|
|
|
static int
|
static int
|
or1k_store_word (addr, val)
|
or1k_store_word (addr, val)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
unsigned int val;
|
unsigned int val;
|
{
|
{
|
if (addr & 3)
|
if (addr & 3)
|
{
|
{
|
int subaddr = addr & 3;
|
int subaddr = addr & 3;
|
unsigned char buf[8];
|
unsigned char buf[8];
|
unsigned int low, high;
|
unsigned int low, high;
|
addr >>= 2;
|
addr >>= 2;
|
low = or1k_read_mem (addr << 2);
|
low = or1k_read_mem (addr << 2);
|
high = or1k_read_mem ((addr + 1) << 2);
|
high = or1k_read_mem ((addr + 1) << 2);
|
memcpy (&buf[0], &low, 4);
|
memcpy (&buf[0], &low, 4);
|
memcpy (&buf[4], &high, 4);
|
memcpy (&buf[4], &high, 4);
|
memcpy (&buf[subaddr], &val, 4);
|
memcpy (&buf[subaddr], &val, 4);
|
memcpy (&low, &buf[0], 4);
|
memcpy (&low, &buf[0], 4);
|
memcpy (&high, &buf[4], 4);
|
memcpy (&high, &buf[4], 4);
|
or1k_write_mem (addr << 2, low);
|
or1k_write_mem (addr << 2, low);
|
or1k_write_mem ((addr + 1) << 2, high);
|
or1k_write_mem ((addr + 1) << 2, high);
|
}
|
}
|
else
|
else
|
{
|
{
|
or1k_write_mem (addr, val);
|
or1k_write_mem (addr, val);
|
}
|
}
|
return err;
|
return err;
|
}
|
}
|
|
|
/* Fetch the remote registers. */
|
/* Fetch the remote registers. */
|
|
|
void
|
void
|
or1k_fetch_registers (regno)
|
or1k_fetch_registers (regno)
|
int regno;
|
int regno;
|
{
|
{
|
unsigned int val;
|
unsigned int val;
|
|
|
if (regno == -1)
|
if (regno == -1)
|
{
|
{
|
for (regno = 0; regno < NUM_REGS; regno++)
|
for (regno = 0; regno < NUM_REGS; regno++)
|
or1k_fetch_registers (regno);
|
or1k_fetch_registers (regno);
|
return;
|
return;
|
}
|
}
|
|
|
if (regno >= NUM_REGS)
|
if (regno >= NUM_REGS)
|
error("Invalid register number!");
|
error("Invalid register number!");
|
|
|
/* Convert to SPRNUM and read. */
|
/* Convert to SPRNUM and read. */
|
val = or1k_read_spr_reg (REGNUM_TO_SPRNUM(regno));
|
val = or1k_read_spr_reg (REGNUM_TO_SPRNUM(regno));
|
|
|
{
|
{
|
char buf[MAX_REGISTER_RAW_SIZE];
|
char buf[MAX_REGISTER_RAW_SIZE];
|
|
|
/* We got the number the register holds, but gdb expects to see a
|
/* We got the number the register holds, but gdb expects to see a
|
value in the target byte ordering. */
|
value in the target byte ordering. */
|
store_unsigned_integer (buf, REGISTER_RAW_SIZE (regno), val);
|
store_unsigned_integer (buf, REGISTER_RAW_SIZE (regno), val);
|
supply_register (regno, buf);
|
supply_register (regno, buf);
|
}
|
}
|
if (err)
|
if (err)
|
or1k_error ("Can't read register %d(%i): %s", regno,
|
or1k_error ("Can't read register %d(%i): %s", regno,
|
REGNUM_TO_SPRNUM(regno), or1k_err_name (err));
|
REGNUM_TO_SPRNUM(regno), or1k_err_name (err));
|
}
|
}
|
|
|
/* Fetch and return instruction from the specified location. */
|
/* Fetch and return instruction from the specified location. */
|
|
|
unsigned int
|
unsigned int
|
or1k_fetch_instruction (addr)
|
or1k_fetch_instruction (addr)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
{
|
{
|
char buf[OR1K_INSTLEN];
|
char buf[OR1K_INSTLEN];
|
int status;
|
int status;
|
|
|
status = read_memory_nobpt (addr, buf, OR1K_INSTLEN);
|
status = read_memory_nobpt (addr, buf, OR1K_INSTLEN);
|
if (status)
|
if (status)
|
memory_error (status, addr);
|
memory_error (status, addr);
|
return extract_unsigned_integer (buf, OR1K_INSTLEN);
|
return extract_unsigned_integer (buf, OR1K_INSTLEN);
|
}
|
}
|
|
|
/* Currently not needed. */
|
/* Currently not needed. */
|
|
|
static void
|
static void
|
or1k_prepare_to_store ()
|
or1k_prepare_to_store ()
|
{
|
{
|
}
|
}
|
|
|
/* Store remote register(s). */
|
/* Store remote register(s). */
|
|
|
static void
|
static void
|
or1k_store_registers (regno)
|
or1k_store_registers (regno)
|
int regno;
|
int regno;
|
{
|
{
|
if (regno == -1)
|
if (regno == -1)
|
{
|
{
|
for (regno = 0; regno < NUM_REGS; regno++)
|
for (regno = 0; regno < NUM_REGS; regno++)
|
or1k_store_registers (regno);
|
or1k_store_registers (regno);
|
return;
|
return;
|
}
|
}
|
|
|
if (regno >= NUM_REGS)
|
if (regno >= NUM_REGS)
|
error("Invalid register number!");
|
error("Invalid register number!");
|
|
|
or1k_write_spr_reg (REGNUM_TO_SPRNUM(regno), read_register (regno));
|
or1k_write_spr_reg (REGNUM_TO_SPRNUM(regno), read_register (regno));
|
if (err)
|
if (err)
|
or1k_error ("Can't write register %d(%i): %s", regno, REGNUM_TO_SPRNUM(regno), or1k_err_name (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,
|
/* Read or write LEN bytes from inferior memory at MEMADDR,
|
transferring to or from debugger address MYADDR. Write to inferior
|
transferring to or from debugger address MYADDR. Write to inferior
|
if SHOULD_WRITE is nonzero. Returns length of data written or
|
if SHOULD_WRITE is nonzero. Returns length of data written or
|
read; 0 for error. Note that protocol gives us the correct value
|
read; 0 for error. Note that protocol gives us the correct value
|
for a longword, since it transfers values in ASCII. We want the
|
for a longword, since it transfers values in ASCII. We want the
|
byte values, so we have to swap the longword values. */
|
byte values, so we have to swap the longword values. */
|
|
|
static int
|
static int
|
or1k_xfer_memory (memaddr, myaddr, len, write, ignore)
|
or1k_xfer_memory (memaddr, myaddr, len, write, ignore)
|
CORE_ADDR memaddr;
|
CORE_ADDR memaddr;
|
char *myaddr;
|
char *myaddr;
|
int len;
|
int len;
|
int write;
|
int write;
|
struct target_ops *ignore;
|
struct target_ops *ignore;
|
{
|
{
|
register int i;
|
register int i;
|
/* Round starting address down to longword boundary. */
|
/* Round starting address down to longword boundary. */
|
register CORE_ADDR addr = memaddr & ~3;
|
register CORE_ADDR addr = memaddr & ~3;
|
/* Round ending address up; get number of longwords that makes. */
|
/* Round ending address up; get number of longwords that makes. */
|
register int count = (((memaddr + len) - addr) + 3) / 4;
|
register int count = (((memaddr + len) - addr) + 3) / 4;
|
/* Allocate buffer of that many longwords. */
|
/* Allocate buffer of that many longwords. */
|
register char *buffer = alloca (count * 4);
|
register char *buffer = alloca (count * 4);
|
int status;
|
int status;
|
|
|
int block_xfer_size = 256; /* CZ 21/06/01 ... number of 32 bit words */
|
int block_xfer_size = 256; /* CZ 21/06/01 ... number of 32 bit words */
|
int nBlocks = (count + block_xfer_size -1)/block_xfer_size;
|
int nBlocks = (count + block_xfer_size -1)/block_xfer_size;
|
int terminate = 0; /* Terminate the printing of '*'s... */
|
int terminate = 0; /* Terminate the printing of '*'s... */
|
|
|
#ifdef DEBUG_JTAG
|
#ifdef DEBUG_JTAG
|
debug ("xfer_memory %s addr=%x, len=%i, \n", write?"write":"read", memaddr, len);
|
debug ("xfer_memory %s addr=%x, len=%i, \n", write?"write":"read", memaddr, len);
|
fflush(stdout);
|
fflush(stdout);
|
#endif
|
#endif
|
|
|
#if 0
|
#if 0
|
if (memaddr >= MEM_SPACE)
|
if (memaddr >= MEM_SPACE)
|
error("Invalid address");
|
error("Invalid address");
|
#endif
|
#endif
|
|
|
/* (CZ 21/06/01 -- because upper layers which know nothing about
|
/* (CZ 21/06/01 -- because upper layers which know nothing about
|
Or1k or JTAG call this function directly, it is always necessary
|
Or1k or JTAG call this function directly, it is always necessary
|
to set the chain to point to the Debug Unit. Otherwise, it may
|
to set the chain to point to the Debug Unit. Otherwise, it may
|
be pointing to the Development Interface chain, in which case
|
be pointing to the Development Interface chain, in which case
|
we're going to get bupkiss... */
|
we're going to get bupkiss... */
|
|
|
if (write)
|
if (write)
|
{
|
{
|
/* Fill start and end extra bytes of buffer with existing data. */
|
/* Fill start and end extra bytes of buffer with existing data. */
|
if (addr != memaddr || len < 4)
|
if (addr != memaddr || len < 4)
|
{
|
{
|
/* Need part of initial word -- fetch it. */
|
/* Need part of initial word -- fetch it. */
|
store_unsigned_integer (&buffer[0], 4, or1k_fetch_word (addr));
|
store_unsigned_integer (&buffer[0], 4, or1k_fetch_word (addr));
|
}
|
}
|
|
|
if (count > 1)
|
if (count > 1)
|
{
|
{
|
/* Need part of last word -- fetch it. FIXME: we do this even
|
/* Need part of last word -- fetch it. FIXME: we do this even
|
if we don't need it. */
|
if we don't need it. */
|
store_unsigned_integer (&buffer[(count - 1) * 4], 4,
|
store_unsigned_integer (&buffer[(count - 1) * 4], 4,
|
or1k_fetch_word (addr + (count - 1) * 4));
|
or1k_fetch_word (addr + (count - 1) * 4));
|
}
|
}
|
|
|
/* Copy data to be written over corresponding part of buffer */
|
/* Copy data to be written over corresponding part of buffer */
|
memcpy ((char *) buffer + (memaddr & 3), myaddr, len);
|
memcpy ((char *) buffer + (memaddr & 3), myaddr, len);
|
|
|
/* CZ: rewrote the block transfer routines to make the code
|
/* CZ: rewrote the block transfer routines to make the code
|
a little more efficient for implementations that can handle
|
a little more efficient for implementations that can handle
|
variable sized scan chains. Might be useful in the future.
|
variable sized scan chains. Might be useful in the future.
|
Certainly makes downloads to the simulator more efficient. */
|
Certainly makes downloads to the simulator more efficient. */
|
for(i=0;i<nBlocks;i++,count-=block_xfer_size,addr += block_xfer_size*4)
|
for(i=0;i<nBlocks;i++,count-=block_xfer_size,addr += block_xfer_size*4)
|
{
|
{
|
int j;
|
int j;
|
int n = count < block_xfer_size ? count : block_xfer_size;
|
int n = count < block_xfer_size ? count : block_xfer_size;
|
unsigned long *__buf;
|
unsigned long *__buf;
|
|
|
if(!(__buf = (unsigned long*)malloc(n*sizeof(unsigned long))))
|
if(!(__buf = (unsigned long*)malloc(n*sizeof(unsigned long))))
|
{
|
{
|
errno = ERR_MEM;
|
errno = ERR_MEM;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
for(j=0;j<n;j++)
|
for(j=0;j<n;j++)
|
__buf[j] = (unsigned long)extract_unsigned_integer(&buffer[(i * block_xfer_size +j)*4], 4);
|
__buf[j] = (unsigned long)extract_unsigned_integer(&buffer[(i * block_xfer_size +j)*4], 4);
|
or1k_set_chain (SC_WISHBONE);
|
or1k_set_chain (SC_WISHBONE);
|
status = or1k_store_block(addr,__buf,n);
|
status = or1k_store_block(addr,__buf,n);
|
free(__buf);
|
free(__buf);
|
if(n == block_xfer_size)
|
if(n == block_xfer_size)
|
{
|
{
|
debug ("*");
|
debug ("*");
|
gdb_flush (gdb_stdout);
|
gdb_flush (gdb_stdout);
|
}
|
}
|
if (status)
|
if (status)
|
{
|
{
|
errno = status;
|
errno = status;
|
return 0;
|
return 0;
|
}
|
}
|
/* FIXME: Do we want a QUIT here? */
|
/* FIXME: Do we want a QUIT here? */
|
}
|
}
|
if (terminate)
|
if (terminate)
|
debug ("\n");
|
debug ("\n");
|
}
|
}
|
else
|
else
|
{
|
{
|
for(i=0;i<nBlocks;i++,count-=block_xfer_size,addr += block_xfer_size*4)
|
for(i=0;i<nBlocks;i++,count-=block_xfer_size,addr += block_xfer_size*4)
|
{
|
{
|
int j;
|
int j;
|
int n = count < block_xfer_size ? count : block_xfer_size;
|
int n = count < block_xfer_size ? count : block_xfer_size;
|
unsigned long *__buf;
|
unsigned long *__buf;
|
|
|
or1k_set_chain (SC_WISHBONE);
|
or1k_set_chain (SC_WISHBONE);
|
__buf = (unsigned long*)malloc(n*sizeof(unsigned long));
|
__buf = (unsigned long*)malloc(n*sizeof(unsigned long));
|
status = or1k_load_block(addr,__buf,n);
|
status = or1k_load_block(addr,__buf,n);
|
if (!status)
|
if (!status)
|
for(j=0;j<n;j++)
|
for(j=0;j<n;j++)
|
store_unsigned_integer (&buffer[(i * block_xfer_size +j)*4], 4, __buf[j]);
|
store_unsigned_integer (&buffer[(i * block_xfer_size +j)*4], 4, __buf[j]);
|
else
|
else
|
errno = status;
|
errno = status;
|
free(__buf);
|
free(__buf);
|
|
|
if(status)
|
if(status)
|
return 0;
|
return 0;
|
}
|
}
|
/* Copy appropriate bytes out of the buffer. */
|
/* Copy appropriate bytes out of the buffer. */
|
memcpy (myaddr, buffer + (memaddr & 3), len);
|
memcpy (myaddr, buffer + (memaddr & 3), len);
|
}
|
}
|
return len;
|
return len;
|
}
|
}
|
|
|
int or1k_load_block(CORE_ADDR addr,void* buffer,int nRegisters)
|
int or1k_load_block(CORE_ADDR addr,void* buffer,int nRegisters)
|
{
|
{
|
int i=0;
|
int i=0;
|
unsigned int regno = addr;
|
unsigned int regno = addr;
|
|
|
if (current_or1k_target != NULL && current_or1k_target->to_read_block != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_read_block != NULL)
|
return current_or1k_target->to_read_block (regno,buffer,nRegisters);
|
return current_or1k_target->to_read_block (regno,buffer,nRegisters);
|
else
|
else
|
for(i=0;i<nRegisters;i++)
|
for(i=0;i<nRegisters;i++)
|
((unsigned long*)buffer)[i] = 0x1234;
|
((unsigned long*)buffer)[i] = 0x1234;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int or1k_store_block(CORE_ADDR addr,void* buffer,int nRegisters)
|
int or1k_store_block(CORE_ADDR addr,void* buffer,int nRegisters)
|
{
|
{
|
unsigned int regno = addr;
|
unsigned int regno = addr;
|
|
|
if (current_or1k_target != NULL && current_or1k_target->to_write_block != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_write_block != NULL)
|
return current_or1k_target->to_write_block (regno,buffer,nRegisters);
|
return current_or1k_target->to_write_block (regno,buffer,nRegisters);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Print info on this target. */
|
/* Print info on this target. */
|
|
|
static void
|
static void
|
or1k_files_info (ignore)
|
or1k_files_info (ignore)
|
struct target_ops *ignore;
|
struct target_ops *ignore;
|
{
|
{
|
char *file = "nothing";
|
char *file = "nothing";
|
|
|
if (exec_bfd)
|
if (exec_bfd)
|
file = bfd_get_filename (exec_bfd);
|
file = bfd_get_filename (exec_bfd);
|
|
|
printf_filtered ("or1k_files_info: file \"%s\"\n", file);
|
printf_filtered ("or1k_files_info: file \"%s\"\n", file);
|
|
|
if (exec_bfd)
|
if (exec_bfd)
|
{
|
{
|
printf_filtered ("\tAttached to %s running program %s\n",
|
printf_filtered ("\tAttached to %s running program %s\n",
|
target_shortname, file);
|
target_shortname, file);
|
}
|
}
|
/* Print target info. */
|
/* Print target info. */
|
printf_filtered ("Status: %s\n", status_name[or1k_status]);
|
printf_filtered ("Status: %s\n", status_name[or1k_status]);
|
}
|
}
|
|
|
/* Tell whether we can support a hardware breakpoint. */
|
/* Tell whether we can support a hardware breakpoint. */
|
|
|
static int
|
static int
|
or1k_can_use_hardware_breakpoint ()
|
or1k_can_use_hardware_breakpoint ()
|
{
|
{
|
int i;
|
int i;
|
|
|
/* Search for unused breakpoint. */
|
/* Search for unused breakpoint. */
|
return or1k_implementation.num_used_matchpoints < or1k_implementation.num_matchpoints;
|
return or1k_implementation.num_used_matchpoints < or1k_implementation.num_matchpoints;
|
}
|
}
|
|
|
/* Insert a breakpoint. On targets that don't have built-in breakpoint
|
/* Insert a breakpoint. On targets that don't have built-in breakpoint
|
support, we read the contents of the target location and stash it,
|
support, we read the contents of the target location and stash it,
|
then overwrite it with a breakpoint instruction. ADDR is the target
|
then overwrite it with a breakpoint instruction. ADDR is the target
|
location in the target machine. CONTENTS_CACHE is a pointer to
|
location in the target machine. CONTENTS_CACHE is a pointer to
|
memory allocated for saving the target contents. It is guaranteed
|
memory allocated for saving the target contents. It is guaranteed
|
by the caller to be long enough to save sizeof BREAKPOINT bytes (this
|
by the caller to be long enough to save sizeof BREAKPOINT bytes (this
|
is accomplished via BREAKPOINT_MAX). */
|
is accomplished via BREAKPOINT_MAX). */
|
|
|
int
|
int
|
or1k_insert_breakpoint (addr, contents_cache)
|
or1k_insert_breakpoint (addr, contents_cache)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
char *contents_cache;
|
char *contents_cache;
|
{
|
{
|
if (or1k_can_use_hardware_breakpoint())
|
if (or1k_can_use_hardware_breakpoint())
|
return set_breakpoint (addr);
|
return set_breakpoint (addr);
|
else
|
else
|
return memory_insert_breakpoint (addr, contents_cache);
|
return memory_insert_breakpoint (addr, contents_cache);
|
}
|
}
|
|
|
int
|
int
|
or1k_remove_breakpoint (addr, contents_cache)
|
or1k_remove_breakpoint (addr, contents_cache)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
char *contents_cache;
|
char *contents_cache;
|
{
|
{
|
/* First try to remove HW breakpoint at address */
|
/* First try to remove HW breakpoint at address */
|
if (clear_breakpoint (addr))
|
if (clear_breakpoint (addr))
|
return memory_remove_breakpoint (addr, contents_cache);
|
return memory_remove_breakpoint (addr, contents_cache);
|
else
|
else
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Tell whether this target can support a hardware breakpoint. CNT
|
/* Tell whether this target can support a hardware breakpoint. CNT
|
is the number of hardware breakpoints already installed. This
|
is the number of hardware breakpoints already installed. This
|
implements the TARGET_CAN_USE_HARDWARE_WATCHPOINT macro.
|
implements the TARGET_CAN_USE_HARDWARE_WATCHPOINT macro.
|
Lower bound is estimated. !!! Can we estimate better? */
|
Lower bound is estimated. !!! Can we estimate better? */
|
|
|
int
|
int
|
or1k_can_use_hardware_watchpoint (bp_type, cnt)
|
or1k_can_use_hardware_watchpoint (bp_type, cnt)
|
enum bptype bp_type;
|
enum bptype bp_type;
|
int cnt;
|
int cnt;
|
{
|
{
|
/* Are there at least two matchpoints left for watch? - estimate lower bound */
|
/* Are there at least two matchpoints left for watch? - estimate lower bound */
|
return cnt + ((bp_type == bp_hardware_watchpoint)?(1):(0))
|
return cnt + ((bp_type == bp_hardware_watchpoint)?(1):(0))
|
<= or1k_implementation.num_matchpoints;
|
<= or1k_implementation.num_matchpoints;
|
}
|
}
|
|
|
/* Moves matchpoint. This is very tricky - we have to update
|
/* Moves matchpoint. This is very tricky - we have to update
|
all references to matchpoint indexes. We assume here that
|
all references to matchpoint indexes. We assume here that
|
matchpoint with index to is unused! */
|
matchpoint with index to is unused! */
|
|
|
static void
|
static void
|
move_matchpoint (int from, int to)
|
move_matchpoint (int from, int to)
|
{
|
{
|
int i, j, tmp, chaining;
|
int i, j, tmp, chaining;
|
if (matchpoint_user_count[to] != 0)
|
if (matchpoint_user_count[to] != 0)
|
error ("Internal: Destination matchpoint still has users");
|
error ("Internal: Destination matchpoint still has users");
|
matchpoint_user_count[to] = matchpoint_user_count[from];
|
matchpoint_user_count[to] = matchpoint_user_count[from];
|
matchpoint_user_count[from] = 0;
|
matchpoint_user_count[from] = 0;
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
|
|
dvr[to] = dvr[from];
|
dvr[to] = dvr[from];
|
dcr[to] = dcr[from];
|
dcr[to] = dcr[from];
|
dcr[from].dp = 0;
|
dcr[from].dp = 0;
|
|
|
/* Copy chaining bits. */
|
/* Copy chaining bits. */
|
chaining = dmr1 & (3 << (2 * from));
|
chaining = dmr1 & (3 << (2 * from));
|
dmr1 &= ~(3 << (2 * to));
|
dmr1 &= ~(3 << (2 * to));
|
dmr1 |= chaining << (2 * to);
|
dmr1 |= chaining << (2 * to);
|
dmr1 &= ~(3 << (2 * from));
|
dmr1 &= ~(3 << (2 * from));
|
|
|
/* Copy watchpoint bits */
|
/* Copy watchpoint bits */
|
tmp = dmr2 & (1 << from);
|
tmp = dmr2 & (1 << from);
|
dmr2 &= 1 << to;
|
dmr2 &= 1 << to;
|
dmr2 |= tmp << to;
|
dmr2 |= tmp << to;
|
dmr2 &= 1 << from;
|
dmr2 &= 1 << from;
|
|
|
/* Update hwatch table. Here we assume that matchpoint
|
/* Update hwatch table. Here we assume that matchpoint
|
group is connected (it cannot be implemented in HW
|
group is connected (it cannot be implemented in HW
|
otherwise), so if we move first, we will have to move
|
otherwise), so if we move first, we will have to move
|
others later. */
|
others later. */
|
for (i = 0; i < num_hw_watches; i++)
|
for (i = 0; i < num_hw_watches; i++)
|
if (or1k_hwatch[i].matchpoint_start == from)
|
if (or1k_hwatch[i].matchpoint_start == from)
|
or1k_hwatch[i].matchpoint_start = to;
|
or1k_hwatch[i].matchpoint_start = to;
|
|
|
/* Update htrace struct. */
|
/* Update htrace struct. */
|
tmp = or1k_htrace.trig.wp_trig & (1 << from);
|
tmp = or1k_htrace.trig.wp_trig & (1 << from);
|
or1k_htrace.trig.wp_trig &= 1 << to;
|
or1k_htrace.trig.wp_trig &= 1 << to;
|
or1k_htrace.trig.wp_trig |= tmp << to;
|
or1k_htrace.trig.wp_trig |= tmp << to;
|
or1k_htrace.trig.wp_trig &= 1 << from;
|
or1k_htrace.trig.wp_trig &= 1 << from;
|
|
|
tmp = or1k_htrace.qual.wp_trig & (1 << from);
|
tmp = or1k_htrace.qual.wp_trig & (1 << from);
|
or1k_htrace.qual.wp_trig &= 1 << to;
|
or1k_htrace.qual.wp_trig &= 1 << to;
|
or1k_htrace.qual.wp_trig |= tmp << to;
|
or1k_htrace.qual.wp_trig |= tmp << to;
|
or1k_htrace.qual.wp_trig &= 1 << from;
|
or1k_htrace.qual.wp_trig &= 1 << from;
|
|
|
tmp = or1k_htrace.stop.wp_trig & (1 << from);
|
tmp = or1k_htrace.stop.wp_trig & (1 << from);
|
or1k_htrace.stop.wp_trig &= 1 << to;
|
or1k_htrace.stop.wp_trig &= 1 << to;
|
or1k_htrace.stop.wp_trig |= tmp << to;
|
or1k_htrace.stop.wp_trig |= tmp << to;
|
or1k_htrace.stop.wp_trig &= 1 << from;
|
or1k_htrace.stop.wp_trig &= 1 << from;
|
|
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
for (i = 0; i < MAX_MATCHPOINTS; i++)
|
{
|
{
|
tmp = or1k_htrace.wp_record_uses[i] & (1 << from);
|
tmp = or1k_htrace.wp_record_uses[i] & (1 << from);
|
or1k_htrace.wp_record_uses[i] &= 1 << to;
|
or1k_htrace.wp_record_uses[i] &= 1 << to;
|
or1k_htrace.wp_record_uses[i] |= tmp << to;
|
or1k_htrace.wp_record_uses[i] |= tmp << to;
|
or1k_htrace.wp_record_uses[i] &= 1 << from;
|
or1k_htrace.wp_record_uses[i] &= 1 << from;
|
}
|
}
|
|
|
/* Do we need to move other references also? */
|
/* Do we need to move other references also? */
|
}
|
}
|
|
|
/* Sifts unused matchpoints to higher indexses. */
|
/* Sifts unused matchpoints to higher indexses. */
|
|
|
void
|
void
|
sift_matchpoints ()
|
sift_matchpoints ()
|
{
|
{
|
int i, first_free = 0;
|
int i, first_free = 0;
|
for (i = 0; i < or1k_implementation.num_matchpoints; i++)
|
for (i = 0; i < or1k_implementation.num_matchpoints; i++)
|
if (dcr[i].dp)
|
if (dcr[i].dp)
|
{
|
{
|
/* Move references. */
|
/* Move references. */
|
move_matchpoint (i, first_free);
|
move_matchpoint (i, first_free);
|
|
|
first_free++;
|
first_free++;
|
}
|
}
|
|
|
/* Unused matchpoints should be disabled by move_matchpoint,
|
/* Unused matchpoints should be disabled by move_matchpoint,
|
so we are done here. */
|
so we are done here. */
|
}
|
}
|
|
|
/* Translates gdb watchpoint type into one in DCR register. */
|
/* Translates gdb watchpoint type into one in DCR register. */
|
|
|
static int
|
static int
|
translate_type (gdb_type)
|
translate_type (gdb_type)
|
int gdb_type;
|
int gdb_type;
|
{
|
{
|
switch (gdb_type)
|
switch (gdb_type)
|
{
|
{
|
case 0:
|
case 0:
|
return CT_SDATA;
|
return CT_SDATA;
|
case 1:
|
case 1:
|
return CT_LDATA;
|
return CT_LDATA;
|
case 2:
|
case 2:
|
return CT_ADATA;
|
return CT_ADATA;
|
default:
|
default:
|
error ("Invalid type.");
|
error ("Invalid type.");
|
}
|
}
|
}
|
}
|
|
|
/* Set a data watchpoint. ADDR and LEN should be obvious. TYPE is 0
|
/* 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
|
for a write watchpoint, 1 for a read watchpoint, or 2 for a read/write
|
watchpoint. */
|
watchpoint. */
|
|
|
int
|
int
|
or1k_insert_watchpoint (addr, len, type)
|
or1k_insert_watchpoint (addr, len, type)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
int len;
|
int len;
|
int type;
|
int type;
|
{
|
{
|
int i;
|
int i;
|
|
|
if (len < 1)
|
if (len < 1)
|
return -1;
|
return -1;
|
|
|
type = translate_type (type);
|
type = translate_type (type);
|
|
|
/* Moves unused watchpoints to the top. */
|
/* Moves unused watchpoints to the top. */
|
sift_matchpoints ();
|
sift_matchpoints ();
|
|
|
/* Place at first free matchpoint. */
|
/* Place at first free matchpoint. */
|
i = or1k_implementation.num_used_matchpoints;
|
i = or1k_implementation.num_used_matchpoints;
|
dvr[i] = addr;
|
dvr[i] = addr;
|
dcr[i].dp = 1;
|
dcr[i].dp = 1;
|
dcr[i].cc = CC_GREATE;
|
dcr[i].cc = CC_GREATE;
|
dcr[i].sc = 0;
|
dcr[i].sc = 0;
|
dcr[i].ct = type;
|
dcr[i].ct = type;
|
|
|
/* Set && chaining here. */
|
/* Set && chaining here. */
|
dmr1 &= ~(3 << (2 * i));
|
dmr1 &= ~(3 << (2 * i));
|
dmr1 |= CHAINING_AND << (2 * i);
|
dmr1 |= CHAINING_AND << (2 * i);
|
|
|
/* Set upper watchpoint bound. */
|
/* Set upper watchpoint bound. */
|
i++;
|
i++;
|
dvr[i] = addr + len - 1;
|
dvr[i] = addr + len - 1;
|
dcr[i].dp = 1;
|
dcr[i].dp = 1;
|
dcr[i].cc = CC_LESSE;
|
dcr[i].cc = CC_LESSE;
|
dcr[i].sc = 0;
|
dcr[i].sc = 0;
|
dcr[i].ct = type;
|
dcr[i].ct = type;
|
|
|
/* Matchpoints will cause breakpoints */
|
/* Matchpoints will cause breakpoints */
|
dmr2 |= (1 << i);
|
dmr2 |= (1 << i);
|
or1k_implementation.num_used_matchpoints += 2;
|
or1k_implementation.num_used_matchpoints += 2;
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Removes a data watchpoint. ADDR and LEN should be obvious. TYPE is 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
|
for a write watchpoint, 1 for a read watchpoint, or 2 for a read/write
|
watchpoint. */
|
watchpoint. */
|
|
|
int
|
int
|
or1k_remove_watchpoint (addr, len, type)
|
or1k_remove_watchpoint (addr, len, type)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
int len;
|
int len;
|
int type;
|
int type;
|
{
|
{
|
int i, found = -1;
|
int i, found = -1;
|
|
|
if (len < 1)
|
if (len < 1)
|
return -1;
|
return -1;
|
|
|
type = translate_type (type);
|
type = translate_type (type);
|
|
|
/* Find the right one. */
|
/* Find the right one. */
|
for (i = 0; i < or1k_implementation.num_used_matchpoints; i++)
|
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
|
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
|
&& 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)
|
&& !dcr[i + 1].sc && dcr[i + 1].ct == type)
|
{
|
{
|
found = i;
|
found = i;
|
break;
|
break;
|
}
|
}
|
|
|
if (found < 0)
|
if (found < 0)
|
return -1;
|
return -1;
|
|
|
dcr[found].dp = 0;
|
dcr[found].dp = 0;
|
dcr[found + 1].dp = 0;
|
dcr[found + 1].dp = 0;
|
|
|
/* Matchpoints will not cause breakpoints anymore. */
|
/* Matchpoints will not cause breakpoints anymore. */
|
dmr2 &= ~(1 << i);
|
dmr2 &= ~(1 << i);
|
or1k_implementation.num_used_matchpoints -= 2;
|
or1k_implementation.num_used_matchpoints -= 2;
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int
|
int
|
or1k_stopped_by_watchpoint (void)
|
or1k_stopped_by_watchpoint (void)
|
{
|
{
|
/* For now, no watchpoints */
|
/* For now, no watchpoints */
|
return 0;
|
return 0;
|
|
|
/* return hit_watchpoint; */
|
/* return hit_watchpoint; */
|
}
|
}
|
|
|
/* Insert a breakpoint. */
|
/* Insert a breakpoint. */
|
|
|
int
|
int
|
set_breakpoint (addr)
|
set_breakpoint (addr)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
{
|
{
|
int i;
|
int i;
|
|
|
/* Search for unused breakpoint. */
|
/* Search for unused breakpoint. */
|
for (i = 0; i < NUM_MATCHPOINTS; i++)
|
for (i = 0; i < NUM_MATCHPOINTS; i++)
|
if (dcr[i].dp == 0) break;
|
if (dcr[i].dp == 0) break;
|
if (i >= NUM_MATCHPOINTS) return 1;
|
if (i >= NUM_MATCHPOINTS) return 1;
|
dvr[i] = addr;
|
dvr[i] = addr;
|
dcr[i].dp = 1;
|
dcr[i].dp = 1;
|
dcr[i].cc = CC_EQUAL;
|
dcr[i].cc = CC_EQUAL;
|
dcr[i].sc = 0;
|
dcr[i].sc = 0;
|
dcr[i].ct = CT_FETCH;
|
dcr[i].ct = CT_FETCH;
|
or1k_implementation.num_used_matchpoints++;
|
or1k_implementation.num_used_matchpoints++;
|
|
|
/* No chaining here. */
|
/* No chaining here. */
|
dmr1 &= ~(3 << (2*i));
|
dmr1 &= ~(3 << (2*i));
|
|
|
/* Matchpoints will cause breakpoints */
|
/* Matchpoints will cause breakpoints */
|
dmr2 |= (1 << i);
|
dmr2 |= (1 << i);
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Clear a breakpoint. */
|
/* Clear a breakpoint. */
|
|
|
int
|
int
|
clear_breakpoint (addr)
|
clear_breakpoint (addr)
|
CORE_ADDR addr;
|
CORE_ADDR addr;
|
{
|
{
|
int i;
|
int i;
|
|
|
/* Search for matching breakpoint. */
|
/* Search for matching breakpoint. */
|
for (i = 0; i < NUM_MATCHPOINTS; i++)
|
for (i = 0; i < NUM_MATCHPOINTS; i++)
|
if ((dcr[i].dp == 1) && (dvr[i] == addr) && (dcr[i].cc == CC_EQUAL)
|
if ((dcr[i].dp == 1) && (dvr[i] == addr) && (dcr[i].cc == CC_EQUAL)
|
&& (dcr[i].sc == 0) && (dcr[i].ct == CT_FETCH)) break;
|
&& (dcr[i].sc == 0) && (dcr[i].ct == CT_FETCH)) break;
|
|
|
if (i >= NUM_MATCHPOINTS) return 1;
|
if (i >= NUM_MATCHPOINTS) return 1;
|
dcr[i].dp = 0;
|
dcr[i].dp = 0;
|
|
|
/* Matchpoints will cause breakpoints */
|
/* Matchpoints will cause breakpoints */
|
dmr2 &= ~(1 << i);
|
dmr2 &= ~(1 << i);
|
or1k_implementation.num_used_matchpoints--;
|
or1k_implementation.num_used_matchpoints--;
|
debug_regs_changed = 1;
|
debug_regs_changed = 1;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Start running on the target board. */
|
/* Start running on the target board. */
|
|
|
static void
|
static void
|
or1k_create_inferior (execfile, args, env)
|
or1k_create_inferior (execfile, args, env)
|
char *execfile;
|
char *execfile;
|
char *args;
|
char *args;
|
char **env;
|
char **env;
|
{
|
{
|
CORE_ADDR entry_pt;
|
CORE_ADDR entry_pt;
|
|
|
if (args && *args)
|
if (args && *args)
|
{
|
{
|
warning ("Can't pass arguments to remote OR1K board; arguments ignored.");
|
warning ("Can't pass arguments to remote OR1K board; arguments ignored.");
|
|
|
/* And don't try to use them on the next "run" command. */
|
/* And don't try to use them on the next "run" command. */
|
execute_command ("set args", 0);
|
execute_command ("set args", 0);
|
}
|
}
|
|
|
if (execfile == 0 || exec_bfd == 0)
|
if (execfile == 0 || exec_bfd == 0)
|
error ("No executable file specified");
|
error ("No executable file specified");
|
|
|
or1k_kill ();
|
or1k_kill ();
|
remove_breakpoints ();
|
remove_breakpoints ();
|
|
|
entry_pt = (CORE_ADDR) bfd_get_start_address (exec_bfd);
|
entry_pt = (CORE_ADDR) bfd_get_start_address (exec_bfd);
|
init_wait_for_inferior ();
|
init_wait_for_inferior ();
|
|
|
/* FIXME: Should we set inferior_pid here? */
|
/* FIXME: Should we set inferior_pid here? */
|
|
|
/* Needed to get correct instruction in cache */
|
/* Needed to get correct instruction in cache */
|
insert_breakpoints ();
|
insert_breakpoints ();
|
clear_proceed_status ();
|
clear_proceed_status ();
|
or1k_status = TARGET_STOPPED;
|
or1k_status = TARGET_STOPPED;
|
proceed (entry_pt, TARGET_SIGNAL_DEFAULT, 0);
|
proceed (entry_pt, TARGET_SIGNAL_DEFAULT, 0);
|
}
|
}
|
|
|
/* Clean up after a process. Actually nothing to do. */
|
/* Clean up after a process. Actually nothing to do. */
|
|
|
static void
|
static void
|
or1k_mourn_inferior ()
|
or1k_mourn_inferior ()
|
{
|
{
|
generic_mourn_inferior ();
|
generic_mourn_inferior ();
|
}
|
}
|
|
|
static void
|
static void
|
or1k_dummy_open (name, from_tty)
|
or1k_dummy_open (name, from_tty)
|
char *name;
|
char *name;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
target_preopen (from_tty);
|
target_preopen (from_tty);
|
if (or1k_is_open)
|
if (or1k_is_open)
|
unpush_target (current_ops);
|
unpush_target (current_ops);
|
current_or1k_target = &or1k_target_dummy;
|
current_or1k_target = &or1k_target_dummy;
|
or1k_open (name, from_tty);
|
or1k_open (name, from_tty);
|
}
|
}
|
|
|
static void
|
static void
|
or1k_jtag_open (name, from_tty)
|
or1k_jtag_open (name, from_tty)
|
char *name;
|
char *name;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
target_preopen (from_tty);
|
target_preopen (from_tty);
|
if (or1k_is_open)
|
if (or1k_is_open)
|
unpush_target (current_ops);
|
unpush_target (current_ops);
|
current_or1k_target = &or1k_target_jtag;
|
current_or1k_target = &or1k_target_jtag;
|
or1k_open (name, from_tty);
|
or1k_open (name, from_tty);
|
}
|
}
|
|
|
static void
|
static void
|
or1k_sim_open (name, from_tty)
|
or1k_sim_open (name, from_tty)
|
char *name;
|
char *name;
|
int from_tty;
|
int from_tty;
|
{
|
{
|
/* target_preopen (from_tty); - do we need this ? */
|
/* target_preopen (from_tty); - do we need this ? */
|
if (or1k_is_open)
|
if (or1k_is_open)
|
unpush_target (current_ops);
|
unpush_target (current_ops);
|
current_or1k_target = &or1k_target_sim;
|
current_or1k_target = &or1k_target_sim;
|
or1k_open (name, from_tty);
|
or1k_open (name, from_tty);
|
}
|
}
|
|
|
/* Executes command on the target. */
|
/* Executes command on the target. */
|
|
|
void
|
void
|
or1k_sim_cmd (char *args, int from_tty)
|
or1k_sim_cmd (char *args, int from_tty)
|
{
|
{
|
if (current_or1k_target != NULL && current_or1k_target->to_exec_command != NULL)
|
if (current_or1k_target != NULL && current_or1k_target->to_exec_command != NULL)
|
current_or1k_target->to_exec_command (args, from_tty);
|
current_or1k_target->to_exec_command (args, from_tty);
|
else
|
else
|
error ("Command not supported on this target. ");
|
error ("Command not supported on this target. ");
|
}
|
}
|
|
|
/* Displays matchpoints usage. */
|
/* Displays matchpoints usage. */
|
|
|
void
|
void
|
info_matchpoints_command (char *args, int from_tty)
|
info_matchpoints_command (char *args, int from_tty)
|
{
|
{
|
int i;
|
int i;
|
for (i = 0; i < or1k_implementation.num_matchpoints; i++)
|
for (i = 0; i < or1k_implementation.num_matchpoints; i++)
|
{
|
{
|
printf_filtered ("WP%i ", i);
|
printf_filtered ("WP%i ", i);
|
if (dcr[i].dp)
|
if (dcr[i].dp)
|
{
|
{
|
int chaining = (dmr1 << 2*i) & 3;
|
int chaining = (dmr1 << 2*i) & 3;
|
printf_filtered ("= %s ", ct_names[dcr[i].ct]);
|
printf_filtered ("= %s ", ct_names[dcr[i].ct]);
|
if (dcr[i]. sc)
|
if (dcr[i]. sc)
|
printf_filtered ("s%s %i", cc_names[dcr[i].cc], (int)dvr[i]);
|
printf_filtered ("s%s %i", cc_names[dcr[i].cc], (int)dvr[i]);
|
else
|
else
|
printf_filtered ("%s %u", cc_names[dcr[i].cc], (unsigned int)dvr[i]);
|
printf_filtered ("%s %u", cc_names[dcr[i].cc], (unsigned int)dvr[i]);
|
if (chaining)
|
if (chaining)
|
printf_filtered ("%s WP%i", ch_names[chaining], i - 1);
|
printf_filtered ("%s WP%i", ch_names[chaining], i - 1);
|
}
|
}
|
else
|
else
|
printf_filtered ("NOT USED");
|
printf_filtered ("NOT USED");
|
if ((dmr2 >> i) & 1)
|
if ((dmr2 >> i) & 1)
|
printf_filtered (", causes breakpoint");
|
printf_filtered (", causes breakpoint");
|
if ((dmr2 >> (i + 11)) & 1)
|
if ((dmr2 >> (i + 11)) & 1)
|
printf_filtered (", increments counter");
|
printf_filtered (", increments counter");
|
printf_filtered ("\n");
|
printf_filtered ("\n");
|
}
|
}
|
}
|
}
|
|
static int
|
|
insn_has_delay_slot (insn)
|
|
unsigned int insn;
|
|
{
|
|
if (((insn >> 26) <= 4) || ((insn >> 26) == 17) || ((insn >> 26) == 18))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
void
|
void
|
_initialize_remote_or1k ()
|
_initialize_remote_or1k ()
|
{
|
{
|
/* Initialize the fields in or1k_ops that are common to all targets. */
|
/* Initialize the fields in or1k_ops that are common to all targets. */
|
or1k_dummy_ops.to_close = or1k_close;
|
or1k_dummy_ops.to_close = or1k_close;
|
or1k_dummy_ops.to_detach = or1k_detach;
|
or1k_dummy_ops.to_detach = or1k_detach;
|
or1k_dummy_ops.to_resume = or1k_resume;
|
or1k_dummy_ops.to_resume = or1k_resume;
|
or1k_dummy_ops.to_wait = or1k_wait;
|
or1k_dummy_ops.to_wait = or1k_wait;
|
or1k_dummy_ops.to_fetch_registers = or1k_fetch_registers;
|
or1k_dummy_ops.to_fetch_registers = or1k_fetch_registers;
|
or1k_dummy_ops.to_store_registers = or1k_store_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_prepare_to_store = or1k_prepare_to_store;
|
or1k_dummy_ops.to_xfer_memory = or1k_xfer_memory;
|
or1k_dummy_ops.to_xfer_memory = or1k_xfer_memory;
|
or1k_dummy_ops.to_files_info = or1k_files_info;
|
or1k_dummy_ops.to_files_info = or1k_files_info;
|
or1k_dummy_ops.to_insert_breakpoint = or1k_insert_breakpoint;
|
or1k_dummy_ops.to_insert_breakpoint = or1k_insert_breakpoint;
|
or1k_dummy_ops.to_remove_breakpoint = or1k_remove_breakpoint;
|
or1k_dummy_ops.to_remove_breakpoint = or1k_remove_breakpoint;
|
or1k_dummy_ops.to_kill = or1k_kill;
|
or1k_dummy_ops.to_kill = or1k_kill;
|
or1k_dummy_ops.to_load = generic_load;
|
or1k_dummy_ops.to_load = generic_load;
|
or1k_dummy_ops.to_create_inferior = or1k_create_inferior;
|
or1k_dummy_ops.to_create_inferior = or1k_create_inferior;
|
or1k_dummy_ops.to_mourn_inferior = or1k_mourn_inferior;
|
or1k_dummy_ops.to_mourn_inferior = or1k_mourn_inferior;
|
or1k_dummy_ops.to_stratum = process_stratum;
|
or1k_dummy_ops.to_stratum = process_stratum;
|
or1k_dummy_ops.to_stop = or1k_stop;
|
or1k_dummy_ops.to_stop = or1k_stop;
|
|
|
/* We can access memory while program is running. */
|
/* We can access memory while program is running. */
|
or1k_dummy_ops.to_has_all_memory = 0;
|
or1k_dummy_ops.to_has_all_memory = 0;
|
|
|
or1k_dummy_ops.to_has_memory = 1;
|
or1k_dummy_ops.to_has_memory = 1;
|
or1k_dummy_ops.to_has_stack = 1;
|
or1k_dummy_ops.to_has_stack = 1;
|
or1k_dummy_ops.to_has_registers = 1;
|
or1k_dummy_ops.to_has_registers = 1;
|
or1k_dummy_ops.to_has_execution = 1;
|
or1k_dummy_ops.to_has_execution = 1;
|
or1k_dummy_ops.to_magic = OPS_MAGIC;
|
or1k_dummy_ops.to_magic = OPS_MAGIC;
|
|
|
/* Copy the common fields to all target vectors. */
|
/* Copy the common fields to all target vectors. */
|
or1k_jtag_ops = or1k_sim_ops = or1k_dummy_ops;
|
or1k_jtag_ops = or1k_sim_ops = or1k_dummy_ops;
|
|
|
/* Initialize target-specific fields in the target vectors adn add targets. */
|
/* Initialize target-specific fields in the target vectors adn add targets. */
|
or1k_jtag_ops.to_shortname = "jtag";
|
or1k_jtag_ops.to_shortname = "jtag";
|
or1k_jtag_ops.to_longname = "Remote or1k debugging over JTAG port";
|
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"
|
or1k_jtag_ops.to_doc = "Debug a board using the OR1K remote debugging protocol"
|
" over a parallel line.\nThe argument is the parallel port it is connected "
|
" over a parallel line.\nThe argument is the parallel port it is connected "
|
"to, or, if it is formatted\nas a URL of the form jtag://<hostname>:<port>,"
|
"to, or, if it is formatted\nas a URL of the form jtag://<hostname>:<port>,"
|
" then the argument refers to\na remote JTAG proxy server.\n";
|
" then the argument refers to\na remote JTAG proxy server.\n";
|
or1k_jtag_ops.to_open = or1k_jtag_open;
|
or1k_jtag_ops.to_open = or1k_jtag_open;
|
add_target (&or1k_jtag_ops);
|
add_target (&or1k_jtag_ops);
|
|
|
or1k_dummy_ops.to_shortname = "dummy";
|
or1k_dummy_ops.to_shortname = "dummy";
|
or1k_dummy_ops.to_longname = "Dummy target";
|
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_doc = "Actually no real target attached - more like /dev/null.\n";
|
or1k_dummy_ops.to_open = or1k_dummy_open;
|
or1k_dummy_ops.to_open = or1k_dummy_open;
|
add_target (&or1k_dummy_ops);
|
add_target (&or1k_dummy_ops);
|
|
|
or1k_sim_ops.to_shortname = "sim";
|
or1k_sim_ops.to_shortname = "sim";
|
or1k_sim_ops.to_longname = "Remote or1k debugging using architecture simulator";
|
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_doc = "Debug using an architecture simulator.\n";
|
or1k_sim_ops.to_open = or1k_sim_open;
|
or1k_sim_ops.to_open = or1k_sim_open;
|
add_target (&or1k_sim_ops);
|
add_target (&or1k_sim_ops);
|
add_info ("matchpoints", info_matchpoints_command, "Show current matchpoints allocation status.");
|
add_info ("matchpoints", info_matchpoints_command, "Show current matchpoints allocation status.");
|
}
|
}
|
|
|