Implemented support for multiple breakpoints.

This commit is contained in:
Daniel Beer 2010-07-02 14:22:52 +12:00
parent 2d76eb0184
commit 74a11c1ae0
7 changed files with 342 additions and 98 deletions

12
bsl.c
View File

@ -222,7 +222,13 @@ static device_status_t bsl_poll(device_t dev_base)
return DEVICE_STATUS_HALTED;
}
static int bsl_breakpoint(device_t dev_base, int enabled, uint16_t addr)
static int bsl_setbrk(device_t dev_base, int n, int enabled, uint16_t addr)
{
fprintf(stderr, "bsl: breakpoints are not implemented\n");
return -1;
}
static int bsl_getbrk(device_t dev_base, int n, int *enabled, uint16_t *addr)
{
fprintf(stderr, "bsl: breakpoints are not implemented\n");
return -1;
@ -321,12 +327,14 @@ device_t bsl_open(const char *device)
return NULL;
}
dev->base.max_breakpoints = 0;
dev->base.destroy = bsl_destroy;
dev->base.readmem = bsl_readmem;
dev->base.writemem = bsl_writemem;
dev->base.getregs = bsl_getregs;
dev->base.setregs = bsl_setregs;
dev->base.breakpoint = bsl_breakpoint;
dev->base.setbrk = bsl_setbrk;
dev->base.getbrk = bsl_getbrk;
dev->base.ctl = bsl_ctl;
dev->base.poll = bsl_poll;

150
devcmd.c
View File

@ -183,31 +183,12 @@ static int cmd_step(cproc_t cp, char **arg)
static int cmd_run(cproc_t cp, char **arg)
{
device_t dev = cproc_device(cp);
stab_t stab = cproc_stab(cp);
char *bp_text = get_arg(arg);
int bp_addr;
device_status_t status;
if (bp_text) {
if (expr_eval(stab, bp_text, &bp_addr) < 0) {
fprintf(stderr, "run: can't parse breakpoint: %s\n",
bp_text);
return -1;
}
dev->breakpoint(dev, 1, bp_addr);
} else {
dev->breakpoint(dev, 0, 0);
}
if (dev->ctl(dev, DEVICE_CTL_RUN) < 0)
return -1;
if (bp_text)
printf("Running to 0x%04x.", bp_addr);
else
printf("Running.");
printf(" Press Ctrl+C to interrupt...\n");
printf("Running. Press Ctrl+C to interrupt...\n");
do {
status = dev->poll(dev);
@ -572,7 +553,130 @@ static int cmd_prog(cproc_t cp, char **arg)
return 0;
}
static int cmd_setbreak(cproc_t cp, char **arg)
{
device_t dev = cproc_device(cp);
stab_t stab = cproc_stab(cp);
char *addr_text = get_arg(arg);
char *index_text = get_arg(arg);
int index;
int addr;
if (!addr_text) {
fprintf(stderr, "setbreak: address required\n");
return -1;
}
if (expr_eval(stab, addr_text, &addr) < 0) {
fprintf(stderr, "setbreak: invalid address\n");
return -1;
}
if (index_text) {
index = atoi(index_text);
} else {
int i;
for (i = 0; i < dev->max_breakpoints; i++) {
int enabled;
if (!dev->getbrk(dev, i, &enabled, NULL) &&
!enabled)
break;
}
if (i >= dev->max_breakpoints) {
fprintf(stderr, "setbreak: all breakpoint slots are "
"occupied\n");
return -1;
}
index = i;
}
printf("Setting breakpoint %d\n", index);
dev->setbrk(dev, index, 1, addr);
return 0;
}
static int cmd_delbreak(cproc_t cp, char **arg)
{
device_t dev = cproc_device(cp);
char *index_text = get_arg(arg);
int ret = 0;
if (index_text) {
int index = atoi(index_text);
printf("Clearing breakpoint %d\n", index);
ret = dev->setbrk(dev, index, 0, 0);
} else {
int i;
printf("Clearing all breakpoints...\n");
for (i = 0; i < dev->max_breakpoints; i++)
if (dev->setbrk(dev, i, 0, 0) < 0)
ret = -1;
}
return ret;
}
static int cmd_break(cproc_t cp, char **arg)
{
device_t dev = cproc_device(cp);
stab_t stab = cproc_stab(cp);
int i;
printf("%d breakpoints available:\n", dev->max_breakpoints);
for (i = 0; i < dev->max_breakpoints; i++) {
int enabled;
uint16_t addr;
if (!dev->getbrk(dev, i, &enabled, &addr) && enabled) {
char name[128];
uint16_t offset;
printf(" %d. 0x%04x", i, addr);
if (!stab_nearest(stab, addr, name,
sizeof(name), &offset)) {
printf(" (%s", name);
if (offset)
printf("+0x%x", offset);
printf(")");
}
printf("\n");
}
}
return 0;
}
static const struct cproc_command commands[] = {
{
.name = "setbreak",
.func = cmd_setbreak,
.help =
"setbreak <addr> [index]\n"
" Set a breakpoint. If no index is specified, the first available\n"
" slot will be used.\n"
},
{
.name = "delbreak",
.func = cmd_delbreak,
.help =
"delbreak [index]\n"
" Delete a breakpoint. If no index is specified, then all active\n"
" breakpoints are cleared.\n"
},
{
.name = "break",
.func = cmd_break,
.help =
"break\n"
" List active breakpoints.\n"
},
{
.name = "regs",
.func = cmd_regs,
@ -629,9 +733,9 @@ static const struct cproc_command commands[] = {
.name = "run",
.func = cmd_run,
.help =
"run [breakpoint]\n"
" Run the CPU until either a specified breakpoint occurs or the\n"
" command is interrupted.\n"
"run\n"
" Run the CPU to until a breakpoint is reached or the command is\n"
" interrupted.\n"
},
{
.name = "set",

View File

@ -42,6 +42,8 @@ typedef enum {
#define DEVICE_NUM_REGS 16
struct device {
int max_breakpoints;
/* Close the connection to the device and destroy the driver object */
void (*destroy)(device_t dev);
@ -56,7 +58,8 @@ struct device {
int (*setregs)(device_t dev, const uint16_t *regs);
/* Breakpoint control */
int (*breakpoint)(device_t dev, int enabled, uint16_t addr);
int (*setbrk)(device_t dev, int n, int enabled, uint16_t addr);
int (*getbrk)(device_t dev, int n, int *enabled, uint16_t *addr);
/* CPU control */
int (*ctl)(device_t dev, device_ctl_t op);

80
fet.c
View File

@ -33,6 +33,12 @@
#include "fet_db.h"
#define MAX_PARAMS 16
#define MAX_BREAKPOINTS 16
struct fet_bp {
int enabled;
uint16_t addr;
};
struct fet_device {
struct device base;
@ -40,7 +46,9 @@ struct fet_device {
transport_t transport;
int proto_flags;
int version;
int have_breakpoint;
/* Breakpoint array */
struct fet_bp bps[MAX_BREAKPOINTS];
/* Device-specific information */
u_int16_t code_start;
@ -475,6 +483,13 @@ static int xfer(struct fet_device *dev,
* MSP430 high-level control functions
*/
static void show_dev_info(const char *name, const struct fet_device *dev)
{
printf("Device: %s\n", name);
printf("Code memory starts at 0x%04x\n", dev->code_start);
printf("Number of breakpoints: %d\n", dev->base.max_breakpoints);
}
static int identify_old(struct fet_device *dev)
{
char idtext[64];
@ -491,9 +506,9 @@ static int identify_old(struct fet_device *dev)
idtext[32] = 0;
dev->code_start = LE_WORD(dev->fet_reply.data, 0x24);
dev->base.max_breakpoints = LE_WORD(dev->fet_reply.data, 0x2a);
printf("Device: %s\n", idtext);
printf("Code memory starts at 0x%04x\n", dev->code_start);
show_dev_info(idtext, dev);
return 0;
}
@ -529,9 +544,9 @@ static int identify_new(struct fet_device *dev, const char *force_id)
}
dev->code_start = LE_WORD(r->msg29_data, 0);
dev->base.max_breakpoints = LE_WORD(r->msg29_data, 0x14);
printf("Device: %s\n", r->name);
printf("Code memory starts at 0x%04x\n", dev->code_start);
show_dev_info(r->name, dev);
if (xfer(dev, C_IDENT3, r->msg2b_data, r->msg2b_len, 0) < 0)
fprintf(stderr, "fet: warning: message C_IDENT3 failed\n");
@ -625,8 +640,7 @@ static int fet_ctl(device_t dev_base, device_ctl_t action)
break;
case DEVICE_CTL_RUN:
return do_run(dev, dev->have_breakpoint ?
FET_RUN_BREAKPOINT : FET_RUN_FREE);
return do_run(dev, FET_RUN_BREAKPOINT);
case DEVICE_CTL_HALT:
if (xfer(dev, C_STATE, NULL, 0, 1, 1) < 0) {
@ -769,21 +783,40 @@ static int fet_setregs(device_t dev_base, const uint16_t *regs)
return 0;
}
static int fet_breakpoint(device_t dev_base, int enabled, uint16_t addr)
static int fet_setbrk(device_t dev_base, int n, int enabled, uint16_t addr)
{
struct fet_device *dev = (struct fet_device *)dev_base;
if (enabled) {
dev->have_breakpoint = 1;
if (xfer(dev, C_BREAKPOINT, NULL, 0, 2, 0, addr) < 0) {
fprintf(stderr, "fet: set breakpoint failed\n");
return -1;
}
} else {
dev->have_breakpoint = 0;
if (n < 0 || n > dev_base->max_breakpoints) {
fprintf(stderr, "fet: invalid breakpoint number: %d\n", n);
return -1;
}
if (xfer(dev, C_BREAKPOINT, NULL, 0, 2, n, enabled ? addr : 0) < 0) {
fprintf(stderr, "fet: set breakpoint failed\n");
return -1;
}
dev->bps[n].enabled = enabled;
dev->bps[n].addr = addr;
return 0;
}
static int fet_getbrk(device_t dev_base, int n, int *enabled, uint16_t *addr)
{
struct fet_device *dev = (struct fet_device *)dev_base;
if (n < 0 || n > dev_base->max_breakpoints) {
fprintf(stderr, "fet: invalid breakpoint number: %d\n", n);
return -1;
}
if (enabled)
*enabled = dev->bps[n].enabled;
if (addr)
*addr = dev->bps[n].addr;
return 0;
}
@ -823,6 +856,7 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv,
const char *force_id)
{
struct fet_device *dev = malloc(sizeof(*dev));
int i;
if (!dev) {
perror("fet: failed to allocate memory");
@ -834,13 +868,15 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv,
dev->base.writemem = fet_writemem;
dev->base.getregs = fet_getregs;
dev->base.setregs = fet_setregs;
dev->base.breakpoint = fet_breakpoint;
dev->base.setbrk = fet_setbrk;
dev->base.getbrk = fet_getbrk;
dev->base.ctl = fet_ctl;
dev->base.poll = fet_poll;
memset(dev->bps, 0, sizeof(dev->bps));
dev->transport = transport;
dev->proto_flags = proto_flags;
dev->have_breakpoint = 0;
dev->fet_len = 0;
@ -881,6 +917,12 @@ device_t fet_open(transport_t transport, int proto_flags, int vcc_mv,
goto fail;
}
printf("Resetting all breakpoints...\n");
for (i = 0; i < dev->base.max_breakpoints; i++)
xfer(dev, C_BREAKPOINT, NULL, 0, 2, i, 0);
if (dev->base.max_breakpoints > MAX_BREAKPOINTS)
dev->base.max_breakpoints = MAX_BREAKPOINTS;
return (device_t)dev;
fail:

88
gdb.c
View File

@ -49,12 +49,6 @@ struct gdb_data {
int outlen;
device_t device;
/* The underlying device driver supports only one breakpoint at
* a time. We keep track of whether or not it's in use and report
* an error to gdb if more than one is set.
*/
int have_breakpoint;
};
static void gdb_printf(struct gdb_data *data, const char *fmt, ...)
@ -441,6 +435,50 @@ static int run(struct gdb_data *data, char *buf)
return run_final_status(data);
}
static int add_breakpoint(device_t dev, int addr)
{
int i;
int avail = -1;
for (i = 0; i < dev->max_breakpoints; i++) {
int enabled;
uint16_t ba;
if (!dev->getbrk(dev, i, &enabled, &ba)) {
if (!enabled && avail < 0)
avail = i;
if (enabled && addr == ba) {
fprintf(stderr, "warning: gdb: breakpoint at "
"0x%04x already set", addr);
return 0;
}
}
}
if (avail < 0) {
fprintf(stderr, "gdb: no breakpoint slots available\n");
return -1;
}
return dev->setbrk(dev, avail, 1, addr);
}
static int remove_breakpoint(device_t dev, int addr)
{
int i;
for (i = 0; i < dev->max_breakpoints; i++) {
int enabled;
uint16_t ba;
if (!dev->getbrk(dev, i, &enabled, &ba) && enabled &&
ba == addr && dev->setbrk(dev, i, 0, 0) < 0)
return -1;
}
return 0;
}
static int set_breakpoint(struct gdb_data *data, int enable, char *buf)
{
char *parts[2];
@ -475,23 +513,23 @@ static int set_breakpoint(struct gdb_data *data, int enable, char *buf)
/* Parse the breakpoint address */
addr = strtoul(parts[1], NULL, 16);
/* Only one breakpoint at a time is allowed */
if (enable && data->have_breakpoint) {
fprintf(stderr, "gdb: only one breakpoint allowed "
"at a time\n");
return gdb_send(data, "E00");
}
if (enable) {
if (add_breakpoint(data->device, addr) < 0) {
fprintf(stderr, "gdb: can't add breakpoint at "
"0x%04x\n", addr);
return gdb_send(data, "E00");
}
/* Set the breakpoint */
if (data->device->breakpoint(data->device, enable, addr) < 0)
return gdb_send(data, "E00");
data->have_breakpoint = enable;
if (enable)
printf("Breakpoint set at 0x%04x\n", addr);
else
printf("Breakpoint cleared\n");
} else {
if (remove_breakpoint(data->device, addr) < 0) {
fprintf(stderr, "gdb: can't remove breakpoint at "
"0x%04x\n", addr);
return gdb_send(data, "E00");
}
printf("Breakpoint cleared at 0x%04x\n", addr);
}
return gdb_send(data, "OK");
}
@ -605,6 +643,7 @@ static int gdb_server(device_t device, int port)
socklen_t len;
int arg;
struct gdb_data data;
int i;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
@ -654,13 +693,12 @@ static int gdb_server(device_t device, int port)
data.device = device;
/* Put the hardware breakpoint setting into a known state. */
data.have_breakpoint = 0;
device->breakpoint(device, 0, 0);
printf("Clearing all breakpoints...\n");
for (i = 0; i < device->max_breakpoints; i++)
device->setbrk(device, i, 0, 0);
gdb_reader_loop(&data);
device->breakpoint(device, 0, 0);
return data.error ? -1 : 0;
}

View File

@ -121,6 +121,10 @@ question.
See the section marked \fBADDRESS EXPRESSIONS\fR for more information on
the syntax of expressions.
.IP "\fBbreak\fR"
Show a list of active breakpoints. Breakpoints can be added and removed
with the \fBsetbreak\fR and \fBdelbreak\fR commands. Each breakpoint is
numbered with an integer index starting at 0.
.IP "\fBcgraph\fR \fIaddress\fR \fIlength\fR [\fIaddress\fR]"
Construct the call graph of all functions contained or referenced in
the given range of memory. If a particular function is specified, then
@ -133,6 +137,9 @@ is considered a possible function start.
Callers and callee names are shown prefixed by a "*" where the
transition is a tail-call type transition.
.IP "\fBdelbreak\fR [\fIindex\fR]"
Delete one or all breakpoints. If an index is given, the selected breakpoint
is deleted. Otherwise, all breakpoints are cleared.
.IP "\fBdis\fR \fIaddress\fR [\fIlength\fR]"
Dissassemble a section of memory. Both arguments may be address
expressions. If no length is specified, a section of the default
@ -274,14 +281,11 @@ processed.
Show the current value of all CPU registers in the device under test.
.IP "\fBreset\fR"
Reset (and halt) the CPU of the device under test.
.IP "\fBrun\fR [\fIbreakpoint\fR]"
Run the CPU, optionally specifying a breakpoint. The breakpoint can be
specified as an address expression.
The interactive command prompt is blocked when the CPU is started and
the prompt will not appear again until the CPU halts. The CPU will halt
if it encounters the specified breakpoint, or if Ctrl\-C is pressed by
the user.
.IP "\fBrun\fR"
Start running the CPU. The interactive command prompt is blocked when
the CPU is started and the prompt will not appear again until the CPU
halts. The CPU will halt if it encounters a breakpoint, or if Ctrl\-C
is pressed by the user.
After the CPU halts, the current register values are shown as well as
a disassembly of the first few instructions at the address selected
@ -291,6 +295,11 @@ Alter the value of a register. Registers are specified as numbers from
0 through 15. Any leading non-numeric characters are ignored (so a
register may be specified as, for example, "R12"). The value argument
is an address expression.
.IP "\fBsetbreak\fR \fIaddress\fR [\fIindex\fR]"
Add a new breakpoint. The breakpoint location is an address expression. An
optional index may be specified, indicating that this new breakpoint should
overwrite an existing slot. If no index is specified, then the breakpoint
will be stored in the next unused slot.
.IP "\fBstep\fR [\fIcount\fR]"
Step the CPU through one or more instructions. After stepping, the new
register values are displayed, as well as a disassembly of the

80
sim.c
View File

@ -26,7 +26,14 @@
#include "sim.h"
#define MEM_SIZE 65536
#define MEM_IO_END 0x200
#define MEM_IO_END 0x200
#define NUM_BREAKPOINTS 8
struct sim_bp {
int enabled;
uint16_t addr;
};
struct sim_device {
struct device base;
@ -35,13 +42,13 @@ struct sim_device {
sim_store_func_t store_func;
void *user_data;
uint8_t memory[MEM_SIZE];
uint16_t regs[DEVICE_NUM_REGS];
uint8_t memory[MEM_SIZE];
uint16_t regs[DEVICE_NUM_REGS];
int running;
uint16_t current_insn;
int have_breakpoint;
uint16_t breakpoint_addr;
uint16_t current_insn;
struct sim_bp bps[NUM_BREAKPOINTS];
};
#define MEM_GETB(dev, offset) ((dev)->memory[offset])
@ -482,12 +489,35 @@ static int sim_setregs(device_t dev_base, const uint16_t *regs)
return 0;
}
static int sim_breakpoint(device_t dev_base, int enabled, uint16_t addr)
static int sim_setbrk(device_t dev_base, int n, int enabled, uint16_t addr)
{
struct sim_device *dev = (struct sim_device *)dev_base;
dev->have_breakpoint = enabled;
dev->breakpoint_addr = addr;
if (n < 0 || n > NUM_BREAKPOINTS) {
fprintf(stderr, "sim: invalid breakpoint number: %d\n", n);
return -1;
}
dev->bps[n].enabled = enabled;
dev->bps[n].addr = addr;
return 0;
}
static int sim_getbrk(device_t dev_base, int n, int *enabled, uint16_t *addr)
{
struct sim_device *dev = (struct sim_device *)dev_base;
if (n < 0 || n > NUM_BREAKPOINTS) {
fprintf(stderr, "sim: invalid breakpoint number: %d\n", n);
return -1;
}
if (enabled)
*enabled = dev->bps[n].enabled;
if (addr)
*addr = dev->bps[n].addr;
return 0;
}
@ -525,19 +555,27 @@ static device_status_t sim_poll(device_t dev_base)
struct sim_device *dev = (struct sim_device *)dev_base;
int count = 1000000;
if (!dev->running)
return DEVICE_STATUS_HALTED;
ctrlc_reset();
while (dev->running && count > 0) {
if (dev->have_breakpoint &&
dev->regs[MSP430_REG_PC] == dev->breakpoint_addr) {
printf("Breakpoint reached\n");
dev->running = 0;
break;
while (count > 0) {
int i;
for (i = 0; i < NUM_BREAKPOINTS; i++) {
struct sim_bp *bp = &dev->bps[i];
if (bp->enabled &&
dev->regs[MSP430_REG_PC] == bp->addr) {
dev->running = 0;
return DEVICE_STATUS_HALTED;
}
}
if (dev->regs[MSP430_REG_SR] & MSP430_SR_CPUOFF) {
printf("CPU disabled\n");
dev->running = 0;
break;
return DEVICE_STATUS_HALTED;
}
if (step_cpu(dev) < 0) {
@ -551,7 +589,7 @@ static device_status_t sim_poll(device_t dev_base)
count--;
}
return dev->running ? DEVICE_STATUS_RUNNING : DEVICE_STATUS_HALTED;
return DEVICE_STATUS_RUNNING;
}
device_t sim_open(sim_fetch_func_t fetch_func,
@ -565,12 +603,14 @@ device_t sim_open(sim_fetch_func_t fetch_func,
return NULL;
}
dev->base.max_breakpoints = NUM_BREAKPOINTS;
dev->base.destroy = sim_destroy;
dev->base.readmem = sim_readmem;
dev->base.writemem = sim_writemem;
dev->base.getregs = sim_getregs;
dev->base.setregs = sim_setregs;
dev->base.breakpoint = sim_breakpoint;
dev->base.setbrk = sim_setbrk;
dev->base.getbrk = sim_getbrk;
dev->base.ctl = sim_ctl;
dev->base.poll = sim_poll;
@ -583,8 +623,8 @@ device_t sim_open(sim_fetch_func_t fetch_func,
dev->running = 0;
dev->current_insn = 0;
dev->have_breakpoint = 0;
dev->breakpoint_addr = 0;
memset(dev->bps, 0, sizeof(dev->bps));
printf("Simulation started, 0x%x bytes of RAM\n", MEM_SIZE);
return (device_t)dev;