simio: implemented tracer peripheral.
This commit is contained in:
parent
5f40c55f4e
commit
1cf7385364
2
Makefile
2
Makefile
|
@ -63,7 +63,7 @@ mspdebug: main.o fet.o rf2500.o dis.o uif.o olimex.o ihex.o elf32.o stab.o \
|
|||
util.o bsl.o sim.o symmap.o gdb.o btree.o rtools.o sym.o devcmd.o \
|
||||
reader.o vector.o output_util.o expr.o fet_error.o binfile.o \
|
||||
fet_db.o usbutil.o titext.o srec.o device.o coff.o opdb.o output.o \
|
||||
cmddb.o stdcmd.o prog.o flash_bsl.o list.o simio.o
|
||||
cmddb.o stdcmd.o prog.o flash_bsl.o list.o simio.o simio_tracer.o
|
||||
$(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
|
||||
|
||||
.c.o:
|
||||
|
|
30
simio.c
30
simio.c
|
@ -24,8 +24,10 @@
|
|||
#include "simio_cpu.h"
|
||||
#include "simio_device.h"
|
||||
|
||||
#include "simio_tracer.h"
|
||||
|
||||
static const struct simio_class *const class_db[] = {
|
||||
/* FIXME: nothing here yet */
|
||||
&simio_tracer
|
||||
};
|
||||
|
||||
/* Simulator data. We keep a list of devices on the bus, and the special
|
||||
|
@ -53,7 +55,7 @@ void simio_init(void)
|
|||
void simio_exit(void)
|
||||
{
|
||||
while (!LIST_EMPTY(&device_list))
|
||||
destroy_device((struct simio_device *)&device_list.next);
|
||||
destroy_device((struct simio_device *)device_list.next);
|
||||
}
|
||||
|
||||
static const struct simio_class *find_class(const char *name)
|
||||
|
@ -86,13 +88,13 @@ static struct simio_device *find_device(const char *name)
|
|||
|
||||
static int cmd_add(char **arg_text)
|
||||
{
|
||||
const char *name_text = get_arg(arg_text);
|
||||
const char *type_text = get_arg(arg_text);
|
||||
const char *name_text = get_arg(arg_text);
|
||||
const struct simio_class *type;
|
||||
struct simio_device *dev;
|
||||
|
||||
if (!(name_text && type_text)) {
|
||||
printc_err("simio add: device name and class must be "
|
||||
printc_err("simio add: device class and name must be "
|
||||
"specified.\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -150,7 +152,7 @@ static int cmd_devices(char **arg_text)
|
|||
struct list_node *n;
|
||||
|
||||
for (n = device_list.next; n != &device_list; n = n->next) {
|
||||
struct simio_device *dev = (struct simio_device *)&n;
|
||||
struct simio_device *dev = (struct simio_device *)n;
|
||||
int irq = dev->type->check_interrupt(dev);
|
||||
|
||||
printc(" %-10s (type %s", dev->name, dev->type->name);
|
||||
|
@ -200,7 +202,7 @@ static int cmd_help(char **arg_text)
|
|||
return -1;
|
||||
}
|
||||
|
||||
printc("\x1b[1mDEVICE CLASS: %s\x1b[0m\n%s\n", type->name, type->help);
|
||||
printc("\x1b[1mDEVICE CLASS: %s\x1b[0m\n\n%s\n", type->name, type->help);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -276,8 +278,18 @@ int cmd_simio(char **arg_text)
|
|||
|
||||
void simio_reset(void)
|
||||
{
|
||||
struct list_node *n;
|
||||
|
||||
memset(sfr_data, 0, sizeof(sfr_data));
|
||||
aclk_counter = 0;
|
||||
|
||||
for (n = device_list.next; n != &device_list; n = n->next) {
|
||||
struct simio_device *dev = (struct simio_device *)n;
|
||||
const struct simio_class *type = dev->type;
|
||||
|
||||
if (type->reset)
|
||||
type->reset(dev);
|
||||
}
|
||||
}
|
||||
|
||||
#define IO_REQUEST_FUNC(name, method, datatype) \
|
||||
|
@ -325,7 +337,7 @@ int simio_check_interrupt(void)
|
|||
return irq;
|
||||
}
|
||||
|
||||
void simio_ack_interrupt(void)
|
||||
void simio_ack_interrupt(int irq)
|
||||
{
|
||||
struct list_node *n;
|
||||
|
||||
|
@ -334,13 +346,13 @@ void simio_ack_interrupt(void)
|
|||
const struct simio_class *type = dev->type;
|
||||
|
||||
if (type->ack_interrupt)
|
||||
type->ack_interrupt(dev);
|
||||
type->ack_interrupt(dev, irq);
|
||||
}
|
||||
}
|
||||
|
||||
void simio_step(uint16_t status_register, int cycles)
|
||||
{
|
||||
int clocks[3] = {0};
|
||||
int clocks[SIMIO_NUM_CLOCKS] = {0};
|
||||
struct list_node *n;
|
||||
|
||||
aclk_counter += cycles;
|
||||
|
|
|
@ -50,7 +50,7 @@ int simio_check_interrupt(void);
|
|||
/* When the CPU begins to handle an interrupt, it needs to notify the IO
|
||||
* simulation. Some interrupt flags are cleared automatically when handled.
|
||||
*/
|
||||
void simio_ack_interrupt(void);
|
||||
void simio_ack_interrupt(int irq);
|
||||
|
||||
/* This should be called after executing an instruction to advance the system
|
||||
* clocks.
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
typedef enum {
|
||||
SIMIO_MCLK = 0,
|
||||
SIMIO_SMCLK,
|
||||
SIMIO_ACLK
|
||||
SIMIO_ACLK,
|
||||
SIMIO_NUM_CLOCKS
|
||||
} simio_clock_t;
|
||||
|
||||
/* Access to special function registers is provided by these functions. The
|
||||
|
@ -72,6 +73,9 @@ struct simio_class {
|
|||
char **arg_text);
|
||||
int (*info)(struct simio_device *dev);
|
||||
|
||||
/* System reset hook. */
|
||||
void (*reset)(struct simio_device *dev);
|
||||
|
||||
/* Programmed IO functions return 1 to indicate an unhandled
|
||||
* request. This scheme allows stacking.
|
||||
*/
|
||||
|
@ -88,7 +92,7 @@ struct simio_class {
|
|||
* a single interrupt request at any time.
|
||||
*/
|
||||
int (*check_interrupt)(struct simio_device *dev);
|
||||
void (*ack_interrupt)(struct simio_device *dev);
|
||||
void (*ack_interrupt)(struct simio_device *dev, int irq);
|
||||
|
||||
/* Run the clocks for this device. The counters array has one
|
||||
* array per clock, and gives the number of cycles elapsed since
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||
* Copyright (C) 2009, 2010 Daniel Beer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "simio_device.h"
|
||||
#include "simio_tracer.h"
|
||||
#include "expr.h"
|
||||
#include "output.h"
|
||||
|
||||
#define DEFAULT_HISTORY 16
|
||||
|
||||
typedef enum {
|
||||
EVENT_WRITE_16,
|
||||
EVENT_READ_16,
|
||||
EVENT_WRITE_8,
|
||||
EVENT_READ_8,
|
||||
EVENT_IRQ_HANDLE,
|
||||
EVENT_RESET
|
||||
} event_type_t;
|
||||
|
||||
typedef unsigned long long counter_t;
|
||||
|
||||
struct event {
|
||||
counter_t when;
|
||||
event_type_t what;
|
||||
address_t addr;
|
||||
uint16_t data;
|
||||
};
|
||||
|
||||
struct tracer {
|
||||
struct simio_device base;
|
||||
|
||||
/* IO event history ring buffer. */
|
||||
struct event *history;
|
||||
int size;
|
||||
int head;
|
||||
int tail;
|
||||
|
||||
/* Clock and instruction counters. */
|
||||
counter_t cycles[SIMIO_NUM_CLOCKS];
|
||||
counter_t inscount;
|
||||
|
||||
/* Outstanding interrupt request. */
|
||||
int irq_request;
|
||||
|
||||
/* Verbose mode. */
|
||||
int verbose;
|
||||
};
|
||||
|
||||
static void print_address(address_t addr)
|
||||
{
|
||||
char name[64];
|
||||
address_t offset;
|
||||
|
||||
printc("0x%04x", addr);
|
||||
if (!stab_nearest(stab_default, addr, name, sizeof(name), &offset)) {
|
||||
printc(" (%s", name);
|
||||
if (offset)
|
||||
printc("+0x%x", offset);
|
||||
printc(")");
|
||||
}
|
||||
}
|
||||
|
||||
static void event_print(const struct event *e)
|
||||
{
|
||||
printc(" %10lld: ", e->when);
|
||||
|
||||
switch (e->what) {
|
||||
case EVENT_WRITE_16:
|
||||
printc("write.w ");
|
||||
print_address(e->addr);
|
||||
printc(" => 0x%04x\n", e->data);
|
||||
break;
|
||||
|
||||
case EVENT_READ_16:
|
||||
printc("read.w ");
|
||||
print_address(e->addr);
|
||||
printc("\n");
|
||||
break;
|
||||
|
||||
case EVENT_WRITE_8:
|
||||
printc("write.b ");
|
||||
print_address(e->addr);
|
||||
printc(" => 0x%02x\n", e->data);
|
||||
break;
|
||||
|
||||
case EVENT_READ_8:
|
||||
printc("read.b ");
|
||||
print_address(e->addr);
|
||||
printc("\n");
|
||||
break;
|
||||
|
||||
case EVENT_IRQ_HANDLE:
|
||||
printc("irq handle %d\n", e->addr);
|
||||
break;
|
||||
|
||||
case EVENT_RESET:
|
||||
printc("system reset\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printc("unknown 0x%04x 0x%04x\n", e->addr, e->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void event_rec(struct tracer *tr, event_type_t what,
|
||||
address_t addr, uint16_t data)
|
||||
{
|
||||
struct event *e = &tr->history[tr->head];
|
||||
|
||||
e->when = tr->cycles[SIMIO_MCLK];
|
||||
e->what = what;
|
||||
e->addr = addr;
|
||||
e->data = data;
|
||||
|
||||
if (tr->verbose)
|
||||
event_print(e);
|
||||
|
||||
tr->head = (tr->head + 1) % tr->size;
|
||||
if (tr->head == tr->tail)
|
||||
tr->tail = (tr->tail + 1) % tr->size;
|
||||
}
|
||||
|
||||
static struct simio_device *tracer_create(char **arg_text)
|
||||
{
|
||||
const char *size_text = get_arg(arg_text);
|
||||
int size = DEFAULT_HISTORY;
|
||||
struct event *history;
|
||||
struct tracer *tr;
|
||||
|
||||
if (size_text) {
|
||||
address_t value;
|
||||
|
||||
if (expr_eval(stab_default, size_text, &value) < 0) {
|
||||
printc_err("tracer: can't parse history size: %s\n",
|
||||
size_text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = value;
|
||||
if (size < 2) {
|
||||
printc_err("tracer: invalid size: %d\n", size);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
history = malloc(sizeof(history[0]) * size);
|
||||
if (!history) {
|
||||
pr_error("tracer: couldn't allocate memory for history\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tr = malloc(sizeof(*tr));
|
||||
if (!tr) {
|
||||
pr_error("tracer: couldn't allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(tr, 0, sizeof(*tr));
|
||||
tr->base.type = &simio_tracer;
|
||||
tr->history = history;
|
||||
tr->size = size;
|
||||
tr->irq_request = -1;
|
||||
|
||||
return (struct simio_device *)tr;
|
||||
}
|
||||
|
||||
static void tracer_destroy(struct simio_device *dev)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
free(tr->history);
|
||||
free(tr);
|
||||
}
|
||||
|
||||
static void tracer_reset(struct simio_device *dev)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
event_rec(tr, EVENT_RESET, 0, 0);
|
||||
}
|
||||
|
||||
static int tracer_config(struct simio_device *dev,
|
||||
const char *param, char **arg_text)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
if (!strcasecmp(param, "verbose"))
|
||||
tr->verbose = 1;
|
||||
else if (!strcasecmp(param, "quiet"))
|
||||
tr->verbose = 0;
|
||||
else if (!strcasecmp(param, "untrigger"))
|
||||
tr->irq_request = -1;
|
||||
else if (!strcasecmp(param, "clear")) {
|
||||
tr->head = 0;
|
||||
tr->tail = 0;
|
||||
} else if (!strcasecmp(param, "trigger")) {
|
||||
const char *irq_text = get_arg(arg_text);
|
||||
address_t value;
|
||||
|
||||
if (!irq_text) {
|
||||
printc_err("tracer: trigger: must specify an IRQ "
|
||||
"number\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (expr_eval(stab_default, irq_text, &value) < 0) {
|
||||
printc_err("tracer: trigger: can't parse IRQ "
|
||||
"number: %s\n", irq_text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value < 0 || value >= 16) {
|
||||
printc_err("tracer: trigger: invalid IRQ: %d\n",
|
||||
value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tr->irq_request = value;
|
||||
} else {
|
||||
printc_err("tracer: unknown config parameter: %s\n", param);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tracer_info(struct simio_device *dev)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
int i;
|
||||
|
||||
printc("Instruction count: %lld\n", tr->inscount);
|
||||
printc("MCLK: %lld\n", tr->cycles[SIMIO_MCLK]);
|
||||
printc("SMCLK %lld\n", tr->cycles[SIMIO_SMCLK]);
|
||||
printc("ACLK: %lld\n", tr->cycles[SIMIO_ACLK]);
|
||||
|
||||
if (tr->irq_request >= 0)
|
||||
printc("IRQ pending: %d\n", tr->irq_request);
|
||||
else
|
||||
printc("No IRQ is pending\n");
|
||||
|
||||
printc("\nIO event history (oldest first):\n");
|
||||
for (i = tr->tail; i != tr->head; i = (i + 1) % tr->size)
|
||||
event_print(&tr->history[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tracer_write(struct simio_device *dev,
|
||||
address_t addr, uint16_t data)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
event_rec(tr, EVENT_WRITE_16, addr, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tracer_read(struct simio_device *dev,
|
||||
address_t addr, uint16_t *data)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
event_rec(tr, EVENT_READ_16, addr, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tracer_write_b(struct simio_device *dev,
|
||||
address_t addr, uint8_t data)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
event_rec(tr, EVENT_WRITE_8, addr, data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tracer_read_b(struct simio_device *dev,
|
||||
address_t addr, uint8_t *data)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
event_rec(tr, EVENT_READ_8, addr, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tracer_check_interrupt(struct simio_device *dev)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
return tr->irq_request;
|
||||
}
|
||||
|
||||
static void tracer_ack_interrupt(struct simio_device *dev, int irq)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
|
||||
if (tr->irq_request == irq)
|
||||
tr->irq_request = -1;
|
||||
|
||||
event_rec(tr, EVENT_IRQ_HANDLE, irq, 0);
|
||||
}
|
||||
|
||||
static void tracer_step(struct simio_device *dev,
|
||||
uint16_t addr, const int *clocks)
|
||||
{
|
||||
struct tracer *tr = (struct tracer *)dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SIMIO_NUM_CLOCKS; i++)
|
||||
tr->cycles[i] += clocks[i];
|
||||
|
||||
tr->inscount++;
|
||||
}
|
||||
|
||||
const struct simio_class simio_tracer = {
|
||||
.name = "tracer",
|
||||
.help =
|
||||
"A debug peripheral to implement IO tracing. This will keep a record of\n"
|
||||
"IO activity which can be checked at any time. It can also be used to\n"
|
||||
"manually trigger interrupts.\n"
|
||||
"\n"
|
||||
"Constructor arguments: [history-size]\n"
|
||||
" If specified, enlarge the IO event history from its default size.\n"
|
||||
"\n"
|
||||
"Config arguments are:\n"
|
||||
" verbose\n"
|
||||
" Show IO events as they occur.\n"
|
||||
" quiet\n"
|
||||
" Only show IO events when requested (default).\n"
|
||||
" trigger <irq>\n"
|
||||
" Trigger an specific IRQ vector.\n"
|
||||
" untrigger\n"
|
||||
" Cancel an interrupt request.\n"
|
||||
" clear\n"
|
||||
" Clear the IO history and counter so far.\n",
|
||||
|
||||
.create = tracer_create,
|
||||
.destroy = tracer_destroy,
|
||||
.reset = tracer_reset,
|
||||
.config = tracer_config,
|
||||
.info = tracer_info,
|
||||
.write = tracer_write,
|
||||
.read = tracer_read,
|
||||
.write_b = tracer_write_b,
|
||||
.read_b = tracer_read_b,
|
||||
.check_interrupt = tracer_check_interrupt,
|
||||
.ack_interrupt = tracer_ack_interrupt,
|
||||
.step = tracer_step
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/* MSPDebug - debugging tool for MSP430 MCUs
|
||||
* Copyright (C) 2009, 2010 Daniel Beer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef SIMIO_TRACER_H_
|
||||
#define SIMIO_TRACER_H_
|
||||
|
||||
extern const struct simio_class simio_tracer;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue