simio: implemented Watchdog Timer+ simulation.
This commit is contained in:
parent
716bd0985a
commit
f9488ec417
2
Makefile
2
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 \
|
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:
|
||||||
|
|
17
mspdebug.man
17
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
|
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
20
sim.c
|
@ -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
28
simio.c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
|
@ -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
|
Loading…
Reference in New Issue