273 lines
5.9 KiB
C
273 lines
5.9 KiB
C
/* 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(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
|
|
};
|