From 5f40c55f4e5fee001b293c586f32534994274bda Mon Sep 17 00:00:00 2001 From: Daniel Beer Date: Thu, 10 Mar 2011 10:48:59 +1300 Subject: [PATCH] simio: added "simio" command and device/CPU interfaces. --- Makefile | 4 +- cmddb.c | 20 +++ list.c | 44 ++++++ list.h | 56 ++++++++ main.c | 5 + simio.c | 384 +++++++++++++++++++++++++++++++++++++++++++++++++ simio.h | 31 ++++ simio_cpu.h | 60 ++++++++ simio_device.h | 101 +++++++++++++ 9 files changed, 703 insertions(+), 2 deletions(-) create mode 100644 list.c create mode 100644 list.h create mode 100644 simio.c create mode 100644 simio.h create mode 100644 simio_cpu.h create mode 100644 simio_device.h diff --git a/Makefile b/Makefile index 600fd21..d5d1f8d 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -CC ?= gcc +CC = gcc INSTALL = /usr/bin/install PREFIX ?= /usr/local LDFLAGS ?= -s @@ -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 + cmddb.o stdcmd.o prog.o flash_bsl.o list.o simio.o $(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: diff --git a/cmddb.c b/cmddb.c index d7947f0..c27a969 100644 --- a/cmddb.c +++ b/cmddb.c @@ -26,6 +26,7 @@ #include "rtools.h" #include "sym.h" #include "stdcmd.h" +#include "simio.h" const struct cmddb_record commands[] = { { @@ -240,6 +241,25 @@ const struct cmddb_record commands[] = { .help = "exit\n" " Exit from MSPDebug.\n" + }, + { + .name = "simio", + .func = cmd_simio, + .help = +"simio add [args ...]\n" +" Add a new device to the IO simulator's bus.\n" +"simio del \n" +" Delete a device from the bus.\n" +"simio devices\n" +" Show all devices attached to the bus.\n" +"simio classes\n" +" Show the types of devices which may be attached.\n" +"simio help \n" +" Obtain more information about a device type.\n" +"simio config [args ...]\n" +" Change settings of an attached device.\n" +"simio info \n" +" Print status information for an attached device.\n" } }; diff --git a/list.c b/list.c new file mode 100644 index 0000000..87827e7 --- /dev/null +++ b/list.c @@ -0,0 +1,44 @@ +/* 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 +#include "list.h" + +void list_init(struct list_node *head) +{ + head->next = head; + head->prev = head; +} + +void list_insert(struct list_node *item, struct list_node *after) +{ + item->next = after; + item->prev = after->prev; + + after->prev->next = item; + after->prev = item; +} + +void list_remove(struct list_node *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + + item->prev = NULL; + item->next = NULL; +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..5277d34 --- /dev/null +++ b/list.h @@ -0,0 +1,56 @@ +/* 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 LIST_H_ +#define LIST_H_ + +/* This is the list node definition. It's intended to be embedded within + * another data structure. + * + * All lists are circular and doubly-linked. So, to iterate over the + * members of a list, do something like this: + * + * struct list_node *n; + * + * for (n = list->next; n != list; n = n->next) { + * ... + * } + * + * An empty list must be created first with list_init(). This sets the + * next and prev pointers to the list head itself. + */ +struct list_node { + struct list_node *next; + struct list_node *prev; +}; + +/* Check to see if a list contains anything. */ +#define LIST_EMPTY(h) ((h)->next == (h)) + +/* Create an empty list */ +void list_init(struct list_node *head); + +/* Add an item to a list. The item will appear before the item + * specified as after. + */ +void list_insert(struct list_node *item, struct list_node *after); + +/* Remove a node from its containing list. */ +void list_remove(struct list_node *item); + +#endif diff --git a/main.c b/main.c index 5dc3ca2..f2f80ab 100644 --- a/main.c +++ b/main.c @@ -38,6 +38,7 @@ #include "opdb.h" #include "reader.h" #include "output.h" +#include "simio.h" #include "sim.h" #include "bsl.h" @@ -511,6 +512,8 @@ int main(int argc, char **argv) if (setup_driver(&args) < 0) return -1; + simio_init(); + if (!args.no_rc) process_rc_file(); @@ -527,6 +530,8 @@ int main(int argc, char **argv) reader_loop(); } + simio_exit(); + stab_destroy(stab_default); device_default->destroy(device_default); diff --git a/simio.c b/simio.c new file mode 100644 index 0000000..872271f --- /dev/null +++ b/simio.c @@ -0,0 +1,384 @@ +/* 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 + +#include "output.h" +#include "dis.h" +#include "simio.h" +#include "simio_cpu.h" +#include "simio_device.h" + +static const struct simio_class *const class_db[] = { + /* FIXME: nothing here yet */ +}; + +/* Simulator data. We keep a list of devices on the bus, and the special + * function registers. + * + * Currently, MCLK and SMCLK are tied together, and ACLK runs at a fixed + * ratio of 1:256 with MCLK. aclk_counter counts fractional cycles. + */ +static struct list_node device_list; +static uint8_t sfr_data[16]; +static int aclk_counter; + +static void destroy_device(struct simio_device *dev) +{ + list_remove(&dev->node); + dev->type->destroy(dev); +} + +void simio_init(void) +{ + list_init(&device_list); + simio_reset(); +} + +void simio_exit(void) +{ + while (!LIST_EMPTY(&device_list)) + destroy_device((struct simio_device *)&device_list.next); +} + +static const struct simio_class *find_class(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_LEN(class_db); i++) { + const struct simio_class *t = class_db[i]; + + if (!strcasecmp(t->name, name)) + return t; + } + + return NULL; +} + +static struct simio_device *find_device(const char *name) +{ + struct list_node *n; + + for (n = device_list.next; n != &device_list; n = n->next) { + struct simio_device *dev = (struct simio_device *)n; + + if (!strcasecmp(dev->name, name)) + return dev; + } + + return NULL; +} + +static int cmd_add(char **arg_text) +{ + const char *name_text = get_arg(arg_text); + const char *type_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 " + "specified.\n"); + return -1; + } + + if (find_device(name_text)) { + printc_err("simio add: device name is not unique: %s\n", + name_text); + return -1; + } + + type = find_class(type_text); + if (!type) { + printc_err("simio add: unknown type.\n"); + return -1; + } + + dev = type->create(arg_text); + if (!dev) { + printc_err("simio add: failed to create device.\n"); + return -1; + } + + list_insert(&dev->node, &device_list); + strncpy(dev->name, name_text, sizeof(dev->name)); + dev->name[sizeof(dev->name) - 1] = 0; + + printc_dbg("Added new device \"%s\" of type \"%s\".\n", + dev->name, dev->type->name); + return 0; +} + +static int cmd_del(char **arg_text) +{ + const char *name_text = get_arg(arg_text); + struct simio_device *dev; + + if (!name_text) { + printc_err("simio del: device name must be specified.\n"); + return -1; + } + + dev = find_device(name_text); + if (!dev) { + printc_err("simio del: no such device: %s\n", name_text); + return -1; + } + + destroy_device(dev); + printc_dbg("Destroyed device \"%s\".\n", name_text); + return 0; +} + +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; + int irq = dev->type->check_interrupt(dev); + + printc(" %-10s (type %s", dev->name, dev->type->name); + if (irq < 0) + printc(")\n"); + else + printc(", IRQ pending: %d)\n", irq); + } + + return 0; +} + +static int cmd_classes(char **arg_text) +{ + struct vector v; + int i; + + vector_init(&v, sizeof(const char *)); + for (i = 0; i < ARRAY_LEN(class_db); i++) { + if (vector_push(&v, &class_db[i]->name, 1) < 0) { + printc_err("simio classes: can't allocate memory\n"); + vector_destroy(&v); + return -1; + } + } + + printc("Available device classes:\n"); + namelist_print(&v); + vector_destroy(&v); + + return 0; +} + +static int cmd_help(char **arg_text) +{ + const char *name = get_arg(arg_text); + const struct simio_class *type; + + if (!name) { + printc_err("simio help: you must specify a device class\n"); + return -1; + } + + type = find_class(name); + if (!type) { + printc_err("simio help: unknown device class: %s\n", name); + return -1; + } + + printc("\x1b[1mDEVICE CLASS: %s\x1b[0m\n%s\n", type->name, type->help); + return 0; +} + +static int cmd_config(char **arg_text) +{ + const char *name = get_arg(arg_text); + const char *param = get_arg(arg_text); + struct simio_device *dev; + + if (!(name && param)) { + printc_err("simio config: you must specify a device name and " + "a parameter\n"); + return -1; + } + + dev = find_device(name); + if (!dev) { + printc_err("simio config: no such device: %s\n", name); + return -1; + } + + return dev->type->config(dev, param, arg_text); +} + +static int cmd_info(char **arg_text) +{ + const char *name = get_arg(arg_text); + struct simio_device *dev; + + if (!name) { + printc_err("simio info: you must specify a device name\n"); + return -1; + } + + dev = find_device(name); + if (!dev) { + printc_err("simio info: no such device: %s\n", name); + return -1; + } + + return dev->type->info(dev); +} + +int cmd_simio(char **arg_text) +{ + const char *subcmd = get_arg(arg_text); + static const struct { + const char *name; + int (*func)(char **arg_text); + } cmd_table[] = { + {"add", cmd_add}, + {"del", cmd_del}, + {"devices", cmd_devices}, + {"classes", cmd_classes}, + {"help", cmd_help}, + {"config", cmd_config}, + {"info", cmd_info} + }; + int i; + + if (!subcmd) { + printc_err("simio: a subcommand is required\n"); + return -1; + } + + for (i = 0; i < ARRAY_LEN(cmd_table); i++) + if (!strcasecmp(cmd_table[i].name, subcmd)) + return cmd_table[i].func(arg_text); + + printc_err("simio: unknown subcommand: %s\n", subcmd); + return -1; +} + +void simio_reset(void) +{ + memset(sfr_data, 0, sizeof(sfr_data)); + aclk_counter = 0; +} + +#define IO_REQUEST_FUNC(name, method, datatype) \ +int name(address_t addr, datatype data) { \ + struct list_node *n; \ + int ret = 1; \ +\ + 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->method) { \ + int r = type->method(dev, addr, data); \ +\ + if (r < ret) \ + ret = r; \ + } \ + } \ +\ + return ret; \ +} + +IO_REQUEST_FUNC(simio_write, write, uint16_t) +IO_REQUEST_FUNC(simio_read, read, uint16_t *) +IO_REQUEST_FUNC(simio_write_b, write_b, uint8_t) +IO_REQUEST_FUNC(simio_read_b, read_b, uint8_t *) + +int simio_check_interrupt(void) +{ + int irq = -1; + struct list_node *n; + + 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->check_interrupt) { + int i = type->check_interrupt(dev); + + if (i > irq) + irq = i; + } + } + + return irq; +} + +void simio_ack_interrupt(void) +{ + struct list_node *n; + + 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->ack_interrupt) + type->ack_interrupt(dev); + } +} + +void simio_step(uint16_t status_register, int cycles) +{ + int clocks[3] = {0}; + struct list_node *n; + + aclk_counter += cycles; + + clocks[SIMIO_MCLK] = cycles; + clocks[SIMIO_SMCLK] = cycles; + clocks[SIMIO_ACLK] = aclk_counter >> 8; + + if (status_register & MSP430_SR_CPUOFF) + clocks[SIMIO_MCLK] = 0; + + if (status_register & MSP430_SR_SCG1) + clocks[SIMIO_SMCLK] = 0; + + if (status_register & MSP430_SR_OSCOFF) + clocks[SIMIO_ACLK] = 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->step) + type->step(dev, status_register, clocks); + } +} + +uint8_t simio_sfr_get(address_t which) +{ + if (which < 0 || which > sizeof(sfr_data)) + return 0; + + return sfr_data[which]; +} + +void simio_sfr_modify(address_t which, uint8_t mask, uint8_t bits) +{ + if (which < 0 || which > sizeof(sfr_data)) + return; + + sfr_data[which] = (sfr_data[which] & ~mask) | bits; +} diff --git a/simio.h b/simio.h new file mode 100644 index 0000000..54c5113 --- /dev/null +++ b/simio.h @@ -0,0 +1,31 @@ +/* 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_H_ +#define SIMIO_H_ + +/* Initialize the IO simulator. */ +void simio_init(void); + +/* Clean up allocated resources. */ +void simio_exit(void); + +/* This file gives the prototype for the "simio" command function. */ +int cmd_simio(char **arg_text); + +#endif diff --git a/simio_cpu.h b/simio_cpu.h new file mode 100644 index 0000000..0fd825c --- /dev/null +++ b/simio_cpu.h @@ -0,0 +1,60 @@ +/* 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_CPU_H_ +#define SIMIO_CPU_H_ + +/* This file describes the interface between the CPU simulator and the IO + * simulator. It gives prototypes for functions which should be periodically + * called by the CPU simulator. + */ + +#include +#include "util.h" + +/* This function should be called when the CPU is reset, to also reset + * the IO simulator. + */ +void simio_reset(void); + +/* These functions should be called to perform programmed IO requests. A + * return value of 0 indicates success, 1 is an unhandled request, and -1 + * is an error which should cause execution to stop. + */ +int simio_write(address_t addr, uint16_t data); +int simio_read(address_t addr, uint16_t *data); +int simio_write_b(address_t addr, uint8_t data); +int simio_read_b(address_t addr, uint8_t *data); + +/* Check for an interrupt before executing an instruction. It returns -1 if + * no interrupt is pending, otherwise the number of the highest priority + * pending interrupt. + */ +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); + +/* This should be called after executing an instruction to advance the system + * clocks. + */ +void simio_step(uint16_t status_register, int cycles); + +#endif diff --git a/simio_device.h b/simio_device.h new file mode 100644 index 0000000..7b140a3 --- /dev/null +++ b/simio_device.h @@ -0,0 +1,101 @@ +/* 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_DEVICE_H_ +#define SIMIO_DEVICE_H_ + +#include +#include "util.h" +#include "list.h" + +/* Each system clock has a unique index. After each instruction, step() + * is invoked on each device with an array of clock transition counts. + */ +typedef enum { + SIMIO_MCLK = 0, + SIMIO_SMCLK, + SIMIO_ACLK +} simio_clock_t; + +/* Access to special function registers is provided by these functions. The + * modify function does: + * + * SFR = (SFR & ~mask) | bits + */ +uint8_t simio_sfr_get(address_t which); +void simio_sfr_modify(address_t which, uint8_t mask, uint8_t bits); + +struct simio_class; + +/* Device base class. + * + * The node and name fields will be filled out by the IO simulator - they're + * used for keeping track of the device list. The node member MUST be the + * first in the struct. + */ +struct simio_device { + struct list_node node; + + char name[64]; + const struct simio_class *type; +}; + +struct simio_class { + const char *name; + const char *help; + + /* Instantiate a new device, with the given arguments. This may + * fail, in which case NULL should be returned. + */ + struct simio_device *(*create)(char **arg_text); + void (*destroy)(struct simio_device *dev); + + /* These methods are invoked via the command interface to modify + * device data and show status. + */ + int (*config)(struct simio_device *dev, const char *param, + char **arg_text); + int (*info)(struct simio_device *dev); + + /* Programmed IO functions return 1 to indicate an unhandled + * request. This scheme allows stacking. + */ + int (*write)(struct simio_device *dev, + address_t addr, uint16_t data); + int (*read)(struct simio_device *dev, + address_t addr, uint16_t *data); + int (*write_b)(struct simio_device *dev, + address_t addr, uint8_t data); + int (*read_b)(struct simio_device *dev, + address_t addr, uint8_t *data); + + /* Check and acknowledge interrupts. Each device may produce + * a single interrupt request at any time. + */ + int (*check_interrupt)(struct simio_device *dev); + void (*ack_interrupt)(struct simio_device *dev); + + /* Run the clocks for this device. The counters array has one + * array per clock, and gives the number of cycles elapsed since + * the last call to this method. + */ + void (*step)(struct simio_device *dev, + uint16_t status_register, const int *clocks); +}; + +#endif