simio: implemented tracer peripheral.

This commit is contained in:
Daniel Beer 2011-03-10 11:57:06 +13:00
parent 5f40c55f4e
commit 1cf7385364
6 changed files with 420 additions and 13 deletions

View File

@ -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
View File

@ -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;

View File

@ -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.

View File

@ -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

367
simio_tracer.c Normal file
View File

@ -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
};

24
simio_tracer.h Normal file
View File

@ -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