diff --git a/Makefile b/Makefile index 43de3a7..f5b9d5f 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ mspdebug: main.o fet.o rf2500.o dis.o uif.o olimex.o ihex.o elf32.o stab.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 simio_tracer.o \ - simio_timer.o + simio_timer.o simio_wdt.o $(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: diff --git a/mspdebug.man b/mspdebug.man index c318a78..45f64f1 100644 --- a/mspdebug.man +++ b/mspdebug.man @@ -503,6 +503,23 @@ Clear a signalled interrupt request. Reset the clock cycle and instruction counts to 0, and clear the IO event history. .RE +.IP "\fBwdt\fR" +This peripheral simulates the Watchdog Timer+, which can be used in software +either as a watchdog or as an interval timer. It has no constructor arguments. + +The simulated state of the NMI/RST# pin can be controlled through a configuration +parameter. Note that if this pin state is held low with the pin mode selected +as a reset (the default), the CPU will not run. + +The extended information for this peripheral shows all register states, including +the hidden counter register. Configuration parameters are: +.RS +.IP "\fBnmi\fR \fIstate\fR" +Set the NMI/RST# pin state. The argument should be zero to indicate a low state +or non-zero for a high state. +.IP "\fBirq\fR \fIirq\fR" +Select the interrupt vector for interval timer mode. The default is to use +interrupt vector 10. .SH ADDRESS EXPRESSIONS Any command which accepts a memory address, length or register value as an argument may be given an address expression. An address diff --git a/sim.c b/sim.c index cb2cb83..a49e139 100644 --- a/sim.c +++ b/sim.c @@ -490,6 +490,15 @@ static int step_cpu(struct sim_device *dev) return ret; } +static void do_reset(struct sim_device *dev) +{ + simio_step(dev->regs[MSP430_REG_SR], 4); + memset(dev->regs, 0, sizeof(dev->regs)); + dev->regs[MSP430_REG_PC] = MEM_GETW(dev, 0xfffe); + dev->regs[MSP430_REG_SR] = 0; + simio_reset(); +} + static int step_system(struct sim_device *dev) { int count = 1; @@ -497,7 +506,10 @@ static int step_system(struct sim_device *dev) uint16_t status = dev->regs[MSP430_REG_SR]; irq = simio_check_interrupt(); - if (((status & MSP430_SR_GIE) && irq >= 0) || irq >= 14) { + if (irq == 15) { + do_reset(dev); + return 0; + } else if (((status & MSP430_SR_GIE) && irq >= 0) || irq >= 14) { if (irq >= 16) { printc_err("sim: invalid interrupt number: %d\n", irq); return -1; @@ -631,11 +643,7 @@ static int sim_ctl(device_t dev_base, device_ctl_t op) switch (op) { case DEVICE_CTL_RESET: - simio_step(dev->regs[MSP430_REG_SR], 4); - memset(dev->regs, 0, sizeof(dev->regs)); - dev->regs[MSP430_REG_PC] = MEM_GETW(dev, 0xfffe); - dev->regs[MSP430_REG_SR] = 0; - simio_reset(); + do_reset(dev); return 0; case DEVICE_CTL_HALT: diff --git a/simio.c b/simio.c index 30aa704..f2fc750 100644 --- a/simio.c +++ b/simio.c @@ -26,10 +26,12 @@ #include "simio_tracer.h" #include "simio_timer.h" +#include "simio_wdt.h" static const struct simio_class *const class_db[] = { &simio_tracer, - &simio_timer + &simio_timer, + &simio_wdt }; /* Simulator data. We keep a list of devices on the bus, and the special @@ -316,8 +318,28 @@ int name(address_t addr, datatype data) { \ 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 *) +IO_REQUEST_FUNC(static simio_write_b_device, write_b, uint8_t) +IO_REQUEST_FUNC(static simio_read_b_device, read_b, uint8_t *) + +int simio_write_b(address_t addr, uint8_t data) +{ + if (addr >= 0 && addr < 16) { + sfr_data[addr] = data; + return 0; + } + + return simio_write_b_device(addr, data); +} + +int simio_read_b(address_t addr, uint8_t *data) +{ + if (addr >= 0 && addr < 16) { + *data = sfr_data[addr]; + return 0; + } + + return simio_read_b_device(addr, data); +} int simio_check_interrupt(void) { diff --git a/simio_device.h b/simio_device.h index 0a1e5e9..5cd8269 100644 --- a/simio_device.h +++ b/simio_device.h @@ -38,6 +38,11 @@ typedef enum { * * SFR = (SFR & ~mask) | bits */ +#define SIMIO_IE1 0x00 +#define SIMIO_IFG1 0x01 +#define SIMIO_IE2 0x02 +#define SIMIO_IFG2 0x03 + uint8_t simio_sfr_get(address_t which); void simio_sfr_modify(address_t which, uint8_t mask, uint8_t bits); diff --git a/simio_wdt.c b/simio_wdt.c new file mode 100644 index 0000000..a53c9a9 --- /dev/null +++ b/simio_wdt.c @@ -0,0 +1,272 @@ +/* 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 + +#include "simio_device.h" +#include "simio_wdt.h" +#include "output.h" +#include "expr.h" + +/* WDTCTL flags, taken from mspgcc. + * + * Watchdog timer password. Always read as 069h. Must be written as 05Ah, + * or a PUC will be generated. + */ +#define WDTIS0 0x0001 +#define WDTIS1 0x0002 +#define WDTSSEL 0x0004 +#define WDTCNTCL 0x0008 +#define WDTTMSEL 0x0010 +#define WDTNMI 0x0020 +#define WDTNMIES 0x0040 +#define WDTHOLD 0x0080 +#define WDTPW 0x5A00 + +/* Flags in IE1 */ +#define WDTIE 0x01 +#define NMIIE 0x10 + +/* Flags in IFG1 */ +#define WDTIFG 0x01 +#define NMIIFG 0x10 + +struct wdt { + struct simio_device base; + + int pin_state; + int wdt_irq; + + int count_reg; + int reset_triggered; + uint8_t wdtctl; +}; + +struct simio_device *wdt_create(char **arg_text) +{ + struct wdt *w = malloc(sizeof(*w)); + + if (!w) { + pr_error("wdt: can't allocate memory"); + return NULL; + } + + memset(w, 0, sizeof(*w)); + w->base.type = &simio_wdt; + w->pin_state = 1; + w->wdt_irq = 10; + + w->reset_triggered = 0; + w->wdtctl = 0; + w->count_reg = 0; + + return (struct simio_device *)w; +} + +static void wdt_destroy(struct simio_device *dev) +{ + free(dev); +} + +static void wdt_reset(struct simio_device *dev) { + struct wdt *w = (struct wdt *)dev; + + w->reset_triggered = 0; + w->wdtctl = 0; + w->count_reg = 0; +} + +static int parse_int(int *val, char **arg_text) +{ + const char *text = get_arg(arg_text); + address_t value; + + if (!text) { + printc_err("wdt: expected integer argument\n"); + return -1; + } + + if (expr_eval(stab_default, text, &value) < 0) { + printc_err("wdt: couldn't parse argument: %s\n", text); + return -1; + } + + *val = value; + return 0; +} + +static int wdt_config(struct simio_device *dev, const char *param, + char **arg_text) +{ + struct wdt *w = (struct wdt *)dev; + + if (!strcasecmp(param, "nmi")) { + int old = w->pin_state; + + if (parse_int(&w->pin_state, arg_text) < 0) + return -1; + + if (w->wdtctl & WDTNMI) { + if (((w->wdtctl & WDTNMIES) && + old && !w->pin_state) || + (!(w->wdtctl & WDTNMIES) && + !old && w->pin_state)) + simio_sfr_modify(SIMIO_IFG1, NMIIFG, NMIIFG); + } + + return 0; + } + + if (!strcasecmp(param, "irq")) + return parse_int(&w->wdt_irq, arg_text); + + printc_err("wdt: unknown configuration parameter: %s\n", param); + return -1; +} + +static int wdt_info(struct simio_device *dev) +{ + struct wdt *w = (struct wdt *)dev; + + printc("Configured WDT IRQ: %d\n", w->wdt_irq); + printc("WDTCTL: 0x__%02x\n", w->wdtctl); + printc("NMI/RST# pin: %s\n", w->pin_state ? "HIGH" : "low"); + printc("Counter: 0x%04x\n", w->count_reg); + printc("Reset: %s\n", + w->reset_triggered ? "TRIGGERED" : "not triggered"); + + return 0; +} + +static int wdt_write(struct simio_device *dev, address_t addr, uint16_t data) +{ + struct wdt *w = (struct wdt *)dev; + + if (addr != 0x120) + return 1; + + if (data >> 8 != 0x5a) + w->reset_triggered = 1; + + w->wdtctl = data & 0xf7; + if (w->wdtctl & WDTCNTCL) + w->count_reg = 0; + + return 0; +} + +static int wdt_read(struct simio_device *dev, address_t addr, uint16_t *data) +{ + struct wdt *w = (struct wdt *)dev; + + if (addr != 0x120) + return 1; + + *data = 0x6900 | w->wdtctl; + return 0; +} + +static int wdt_check_interrupt(struct simio_device *dev) +{ + struct wdt *w = (struct wdt *)dev; + uint8_t flags; + + if (!(w->wdtctl & WDTNMI) && !w->pin_state) + return 15; + + if (w->reset_triggered) + return 15; + + flags = simio_sfr_get(SIMIO_IFG1) & simio_sfr_get(SIMIO_IE1); + + if (flags & NMIIFG) + return 14; + + if (flags & WDTIFG) + return w->wdt_irq; + + return -1; +} + +static void wdt_ack_interrupt(struct simio_device *dev, int irq) +{ + struct wdt *w = (struct wdt *)dev; + + if (irq == 14) + simio_sfr_modify(SIMIO_IFG1, NMIIFG, 0); + else if (irq == w->wdt_irq) + simio_sfr_modify(SIMIO_IFG1, WDTIFG, 0); +} + +static void wdt_step(struct simio_device *dev, uint16_t status_register, + const int *clocks) +{ + struct wdt *w = (struct wdt *)dev; + int max = 1; + + /* If on hold, nothing happens */ + if (w->wdtctl & WDTHOLD) + return; + + /* Count input clock cycles */ + if (w->wdtctl & WDTSSEL) + w->count_reg += clocks[SIMIO_ACLK]; + else + w->count_reg += clocks[SIMIO_SMCLK]; + + /* Figure out the divisor */ + switch (w->wdtctl & 3) { + case 0: max = 32768; break; + case 1: max = 8192; break; + case 2: max = 512; break; + case 3: max = 64; break; + } + + /* Check for overflow */ + if (w->count_reg >= max) { + if (w->wdtctl & WDTTMSEL) + simio_sfr_modify(SIMIO_IFG1, WDTIFG, WDTIFG); + else + w->reset_triggered = 1; + } + + w->count_reg &= (max - 1); +} + +const struct simio_class simio_wdt = { + .name = "wdt", + .help = +"This module simulates the Watchdog Timer+ peripheral. There are no\n" +"constructor arguments. Configuration parameters are:\n" +" irq \n" +" Set the interrupt vector for the WDT interrupt.\n" +" nmi <0|1>\n" +" Set the state of the NMI/RST# pin.\n", + + .create = wdt_create, + .destroy = wdt_destroy, + .reset = wdt_reset, + .config = wdt_config, + .info = wdt_info, + .write = wdt_write, + .read = wdt_read, + .check_interrupt = wdt_check_interrupt, + .ack_interrupt = wdt_ack_interrupt, + .step = wdt_step +}; diff --git a/simio_wdt.h b/simio_wdt.h new file mode 100644 index 0000000..7600e4c --- /dev/null +++ b/simio_wdt.h @@ -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_WDT_H_ +#define SIMIO_WDT_H_ + +extern const struct simio_class simio_wdt; + +#endif