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 \
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:

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

20
sim.c
View File

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

28
simio.c
View File

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

View File

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

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