simio: implemented Watchdog Timer+ simulation.

This commit is contained in:
Daniel Beer 2011-03-11 13:18:03 +13:00
parent 716bd0985a
commit f9488ec417
7 changed files with 358 additions and 10 deletions

View File

@ -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 \ 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 \ 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 \ 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) $(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o: .c.o:

View File

@ -503,6 +503,23 @@ Clear a signalled interrupt request.
Reset the clock cycle and instruction counts to 0, and clear the IO event Reset the clock cycle and instruction counts to 0, and clear the IO event
history. history.
.RE .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 .SH ADDRESS EXPRESSIONS
Any command which accepts a memory address, length or register value Any command which accepts a memory address, length or register value
as an argument may be given an address expression. An address as an argument may be given an address expression. An address

20
sim.c
View File

@ -490,6 +490,15 @@ static int step_cpu(struct sim_device *dev)
return ret; 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) static int step_system(struct sim_device *dev)
{ {
int count = 1; int count = 1;
@ -497,7 +506,10 @@ static int step_system(struct sim_device *dev)
uint16_t status = dev->regs[MSP430_REG_SR]; uint16_t status = dev->regs[MSP430_REG_SR];
irq = simio_check_interrupt(); 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) { if (irq >= 16) {
printc_err("sim: invalid interrupt number: %d\n", irq); printc_err("sim: invalid interrupt number: %d\n", irq);
return -1; return -1;
@ -631,11 +643,7 @@ static int sim_ctl(device_t dev_base, device_ctl_t op)
switch (op) { switch (op) {
case DEVICE_CTL_RESET: case DEVICE_CTL_RESET:
simio_step(dev->regs[MSP430_REG_SR], 4); do_reset(dev);
memset(dev->regs, 0, sizeof(dev->regs));
dev->regs[MSP430_REG_PC] = MEM_GETW(dev, 0xfffe);
dev->regs[MSP430_REG_SR] = 0;
simio_reset();
return 0; return 0;
case DEVICE_CTL_HALT: case DEVICE_CTL_HALT:

28
simio.c
View File

@ -26,10 +26,12 @@
#include "simio_tracer.h" #include "simio_tracer.h"
#include "simio_timer.h" #include "simio_timer.h"
#include "simio_wdt.h"
static const struct simio_class *const class_db[] = { static const struct simio_class *const class_db[] = {
&simio_tracer, &simio_tracer,
&simio_timer &simio_timer,
&simio_wdt
}; };
/* Simulator data. We keep a list of devices on the bus, and the special /* 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_write, write, uint16_t)
IO_REQUEST_FUNC(simio_read, read, uint16_t *) IO_REQUEST_FUNC(simio_read, read, uint16_t *)
IO_REQUEST_FUNC(simio_write_b, write_b, uint8_t) IO_REQUEST_FUNC(static simio_write_b_device, write_b, uint8_t)
IO_REQUEST_FUNC(simio_read_b, read_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) int simio_check_interrupt(void)
{ {

View File

@ -38,6 +38,11 @@ typedef enum {
* *
* SFR = (SFR & ~mask) | bits * 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); uint8_t simio_sfr_get(address_t which);
void simio_sfr_modify(address_t which, uint8_t mask, uint8_t bits); void simio_sfr_modify(address_t which, uint8_t mask, uint8_t bits);

272
simio_wdt.c Normal file
View File

@ -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 <stdlib.h>
#include <string.h>
#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 <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
};

24
simio_wdt.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_WDT_H_
#define SIMIO_WDT_H_
extern const struct simio_class simio_wdt;
#endif