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 \
|
||||
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:
|
||||
|
|
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
|
||||
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
20
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:
|
||||
|
|
28
simio.c
28
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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