diff --git a/Makefile b/Makefile index 8c619e6..43de3a7 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,8 @@ mspdebug: main.o fet.o rf2500.o dis.o uif.o olimex.o ihex.o elf32.o stab.o \ util.o bsl.o sim.o symmap.o gdb.o btree.o rtools.o sym.o devcmd.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 + cmddb.o stdcmd.o prog.o flash_bsl.o list.o simio.o simio_tracer.o \ + simio_timer.o $(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS) .c.o: diff --git a/simio.c b/simio.c index 484386a..30aa704 100644 --- a/simio.c +++ b/simio.c @@ -25,9 +25,11 @@ #include "simio_device.h" #include "simio_tracer.h" +#include "simio_timer.h" static const struct simio_class *const class_db[] = { - &simio_tracer + &simio_tracer, + &simio_timer }; /* Simulator data. We keep a list of devices on the bus, and the special diff --git a/simio_timer.c b/simio_timer.c new file mode 100644 index 0000000..0d5bcf0 --- /dev/null +++ b/simio_timer.c @@ -0,0 +1,480 @@ +/* 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 +#include + +#include "simio_device.h" +#include "simio_timer.h" +#include "expr.h" +#include "output.h" + +/* TACTL bits (taken from mspgcc headers) */ +#define TASSEL2 0x0400 /* unused */ /* to distinguish from UART SSELx */ +#define TASSEL1 0x0200 /* Timer A clock source select 1 */ +#define TASSEL0 0x0100 /* Timer A clock source select 0 */ +#define ID1 0x0080 /* Timer A clock input divider 1 */ +#define ID0 0x0040 /* Timer A clock input divider 0 */ +#define MC1 0x0020 /* Timer A mode control 1 */ +#define MC0 0x0010 /* Timer A mode control 0 */ +#define TACLR 0x0004 /* Timer A counter clear */ +#define TAIE 0x0002 /* Timer A counter interrupt enable */ +#define TAIFG 0x0001 /* Timer A counter interrupt flag */ + +/* TACCTLx flags (taken from mspgcc) */ +#define CM1 0x8000 /* Capture mode 1 */ +#define CM0 0x4000 /* Capture mode 0 */ +#define CCIS1 0x2000 /* Capture input select 1 */ +#define CCIS0 0x1000 /* Capture input select 0 */ +#define SCS 0x0800 /* Capture sychronize */ +#define SCCI 0x0400 /* Latched capture signal (read) */ +#define CAP 0x0100 /* Capture mode: 1 /Compare mode : 0 */ +#define OUTMOD2 0x0080 /* Output mode 2 */ +#define OUTMOD1 0x0040 /* Output mode 1 */ +#define OUTMOD0 0x0020 /* Output mode 0 */ +#define CCIE 0x0010 /* Capture/compare interrupt enable */ +#define CCI 0x0008 /* Capture input signal (read) */ +#define OUT 0x0004 /* PWM Output signal if output mode 0 */ +#define COV 0x0002 /* Capture/compare overflow flag */ +#define CCIFG 0x0001 /* Capture/compare interrupt flag */ + +#define MAX_CCRS 7 + +struct timer { + struct simio_device base; + + int size; + int clock_input; + int go_down; + + address_t base_addr; + address_t iv_addr; + int irq0; + int irq1; + + /* IO registers */ + uint16_t tactl; + uint16_t tar; + uint16_t ctls[MAX_CCRS]; + uint16_t ccrs[MAX_CCRS]; +}; + +static struct simio_device *timer_create(char **arg_text) +{ + char *size_text = get_arg(arg_text); + struct timer *tr; + int size = 3; + + if (size_text) { + address_t value; + + if (expr_eval(stab_default, size_text, &value) < 0) { + printc_err("timer: can't parse size: %s\n", + size_text); + return NULL; + } + + if (size < 2 || size > MAX_CCRS) { + printc_err("timer: invalid size: %d\n", size); + return NULL; + } + } + + tr = malloc(sizeof(*tr)); + if (!tr) { + pr_error("timer: can't allocate memory"); + return NULL; + } + + memset(tr, 0, sizeof(*tr)); + tr->base.type = &simio_timer; + tr->size = size; + tr->base_addr = 0x160; + tr->iv_addr = 0x12e; + tr->irq0 = 9; + tr->irq1 = 8; + + return (struct simio_device *)tr; +} + +static void timer_destroy(struct simio_device *dev) +{ + struct timer *tr = (struct timer *)dev; + + free(tr); +} + +static void timer_reset(struct simio_device *dev) +{ + struct timer *tr = (struct timer *)dev; + + tr->tactl = 0; + tr->tar = 0; + tr->go_down = 0; + memset(tr->ccrs, 0, sizeof(tr->ccrs)); + memset(tr->ctls, 0, sizeof(tr->ctls)); +} + +static int config_addr(address_t *addr, char **arg_text) +{ + char *text = get_arg(arg_text); + + if (!text) { + printc_err("timer: config: expected address\n"); + return -1; + } + + if (expr_eval(stab_default, text, addr) < 0) { + printc_err("timer: can't parse address: %s\n", text); + return -1; + } + + return 0; +} + +static int config_irq(int *irq, char **arg_text) +{ + char *text = get_arg(arg_text); + address_t value; + + if (!text) { + printc_err("timer: config: expected interrupt number\n"); + return -1; + } + + if (expr_eval(stab_default, text, &value) < 0) { + printc_err("timer: can't parse interrupt number: %s\n", text); + return -1; + } + + *irq = value; + return 0; +} + +static int config_channel(struct timer *tr, char **arg_text) +{ + char *which_text = get_arg(arg_text); + char *value_text = get_arg(arg_text); + address_t which; + address_t value; + int oldval; + uint16_t edge_flags = 0; + + if (!(which_text && value_text)) { + printc_err("timer: config: expected channel and value\n"); + return -1; + } + + if (expr_eval(stab_default, which_text, &which) < 0) { + printc_err("timer: can't parse channel number: %s\n", + which_text); + return -1; + } + + if (expr_eval(stab_default, value_text, &value) < 0) { + printc_err("timer: can't parse channel value: %s\n", + value_text); + return -1; + } + + if (which < 0 || which > tr->size) { + printc_err("timer: invalid channel number: %d\n", which); + return -1; + } + + oldval = tr->ctls[which] & CCI; + tr->ctls[which] &= ~CCI; + if (value) + tr->ctls[which] &= ~CCI; + + if (oldval && !value) + edge_flags |= CM1; + if (!oldval && value) + edge_flags |= CM0; + + printc_dbg("Timer channel %d: %s => %s\n", + which, oldval ? "H" : "L", value ? "H" : "L"); + + if ((tr->ctls[which] & edge_flags) && (tr->ctls[which] & CAP)) { + if (tr->ctls[which] & CCIFG) { + printc_dbg("Timer capture overflow\n"); + tr->ctls[which] |= COV; + } else { + printc_dbg("Timer capture interrupt triggered\n"); + tr->ccrs[which] = tr->tar; + tr->ctls[which] |= CCIFG; + } + } + + return 0; +} + +static int timer_config(struct simio_device *dev, + const char *param, char **arg_text) +{ + struct timer *tr = (struct timer *)dev; + + if (!strcasecmp(param, "base")) + return config_addr(&tr->base_addr, arg_text); + if (!strcasecmp(param, "iv")) + return config_addr(&tr->iv_addr, arg_text); + if (!strcasecmp(param, "irq0")) + return config_irq(&tr->irq0, arg_text); + if (!strcasecmp(param, "irq1")) + return config_irq(&tr->irq1, arg_text); + if (!strcasecmp(param, "set")) + return config_channel(tr, arg_text); + + printc_err("timer: config: unknown parameter: %s\n", param); + return -1; +} + +static uint16_t calc_iv(struct timer *tr) +{ + int i; + + for (i = 0; i < tr->size; i++) + if ((tr->ctls[i] & (CCIE | CCIFG)) == (CCIE | CCIFG)) + return i * 2; + + if ((tr->tactl & (TAIFG | TAIE)) == (TAIFG | TAIE)) + return 0xa; + + return 0; +} + +static int timer_info(struct simio_device *dev) +{ + struct timer *tr = (struct timer *)dev; + int i; + + printc("Base address: 0x%04x\n", tr->base_addr); + printc("IV address: 0x%04x\n", tr->iv_addr); + printc("IRQ0: %d\n", tr->irq0); + printc("IRQ1: %d\n", tr->irq1); + printc("\n"); + printc("TACTL: 0x%04x\n", tr->tactl); + printc("TAR: 0x%04x\n", tr->tar); + printc("TAIV: 0x%02x\n", calc_iv(tr)); + printc("\n"); + + for (i = 0; i < tr->size; i++) + printc("Channel %2d, TACTL = 0x%04x, TACCR = 0x%04x\n", + i, tr->ctls[i], tr->ccrs[i]); + + return 0; +} + +static int timer_write(struct simio_device *dev, + address_t addr, uint16_t data) +{ + struct timer *tr = (struct timer *)dev; + + if (addr == tr->base_addr) { + tr->tactl = data & ~(TACLR | 0x40); + if (data & TACLR) + tr->tar = 0; + + return 0; + } + + if (addr == tr->base_addr + 0x10) { + tr->tar = data; + return 0; + } + + if (addr >= tr->base_addr + 2 && + addr < tr->base_addr + tr->size + 2) { + int index = ((addr & 0xf) - 2) >> 1; + + tr->ctls[index] = (data & 0xf9f7) | + (tr->ctls[index] & 0x0608); + return 0; + } + + if (addr >= tr->base_addr + 0x12 && + addr < tr->base_addr + tr->size + 0x12) { + int index = ((addr & 0xf) - 2) >> 1; + + tr->ccrs[index] = data; + return 0; + } + + return 1; +} + +static int timer_read(struct simio_device *dev, + address_t addr, uint16_t *data) +{ + struct timer *tr = (struct timer *)dev; + + if (addr == tr->base_addr) { + *data = tr->tactl; + return 0; + } + + if (addr == tr->base_addr + 0x10) { + *data = tr->tar; + return 0; + } + + if (addr >= tr->base_addr + 2 && + addr < tr->base_addr + tr->size + 2) { + *data = tr->ctls[((addr - 0xf) - 2) >> 1]; + return 0; + } + + if (addr >= tr->base_addr + 0x12 && + addr < tr->base_addr + tr->size + 0x12) { + *data = tr->ccrs[((addr & 0xf) - 2) >> 1]; + return 0; + } + + return 1; +} + +static int timer_check_interrupt(struct simio_device *dev) +{ + struct timer *tr = (struct timer *)dev; + int i; + + if ((tr->ctls[0] & (CCIE | CCIFG)) == (CCIE | CCIFG)) + return tr->irq0; + + if ((tr->tactl & (TAIFG | TAIE)) == (TAIFG | TAIE)) + return tr->irq1; + + for (i = 1; i < tr->size; i++) + if ((tr->ctls[i] & (CCIE | CCIFG)) == (CCIE | CCIFG)) + return tr->irq1; + + return -1; +} + +static void timer_ack_interrupt(struct simio_device *dev, int irq) +{ + struct timer *tr = (struct timer *)dev; + + if (irq == tr->irq0) + tr->ctls[0] &= ~CCIFG; +} + +static void tar_step(struct timer *tr) +{ + switch ((tr->tactl >> 4) & 3) { + case 0: break; + case 1: + if (tr->tar == tr->ccrs[0]) { + tr->tar = 0; + tr->tactl |= TAIFG; + } else { + tr->tar++; + } + break; + + case 2: + tr->tar++; + if (!tr->tar) + tr->tactl |= TAIFG; + break; + + case 3: + if (tr->tar >= tr->ccrs[0]) + tr->go_down = 1; + if (!tr->tar) + tr->go_down = 0; + + if (tr->go_down) { + tr->tar--; + if (!tr->tar) + tr->tactl |= TAIFG; + } else { + tr->tar++; + } + break; + } +} + +static void timer_step(struct simio_device *dev, + uint16_t status, const int *clocks) +{ + struct timer *tr = (struct timer *)dev; + int pulse_count; + int i; + + /* Count input clock pulses */ + i = (tr->tactl >> 8) & 3; + if (i == 2) + tr->clock_input += clocks[SIMIO_SMCLK]; + else if (i == 1) + tr->clock_input += clocks[SIMIO_ACLK]; + + /* Figure out our clock input divide ratio */ + i = (tr->tactl >> 6) & 3; + pulse_count = tr->clock_input >> i; + tr->clock_input &= ((1 << i) - 1); + + /* Run the timer for however many pulses */ + for (i = 0; i < tr->clock_input; i++) { + int j; + + for (j = 0; j < tr->size; j++) + if (!(tr->ctls[j] & CAP) && (tr->tar == tr->ccrs[j])) { + if (tr->ctls[j] & CCI) + tr->ctls[j] |= SCCI; + else + tr->ctls[j] &= ~SCCI; + + tr->ctls[j] |= CCIFG; + } + + tar_step(tr); + } +} + +const struct simio_class simio_timer = { + .name = "timer", + .help = +"This peripheral implements the Timer_A module.\n" +"\n" +"Constructor arguments: [size]\n" +" Specify the number of capture/compare registers (excluding CCR0).\n" +"\n" +"Config arguments are:\n" +" base
\n" +" Set the peripheral base address.\n" +" irq0 \n" +" Set the interrupt vector for CCR0.\n" +" irq1 \n" +" Set the interrupt vector for CCR1.\n" +" iv
\n" +" Set the interrupt vector register address.\n" +" set <0|1>\n" +" Set the capture input value on the given channel.\n", + + .create = timer_create, + .destroy = timer_destroy, + .reset = timer_reset, + .config = timer_config, + .info = timer_info, + .write = timer_write, + .read = timer_read, + .check_interrupt = timer_check_interrupt, + .ack_interrupt = timer_ack_interrupt, + .step = timer_step +}; diff --git a/simio_timer.h b/simio_timer.h new file mode 100644 index 0000000..6124472 --- /dev/null +++ b/simio_timer.h @@ -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_TIMER_H_ +#define SIMIO_TIMER_H_ + +extern const struct simio_class simio_timer; + +#endif diff --git a/simio_tracer.c b/simio_tracer.c index 20d3aa3..db95556 100644 --- a/simio_tracer.c +++ b/simio_tracer.c @@ -165,13 +165,13 @@ static struct simio_device *tracer_create(char **arg_text) history = malloc(sizeof(history[0]) * size); if (!history) { - pr_error("tracer: couldn't allocate memory for history\n"); + pr_error("tracer: couldn't allocate memory for history"); return NULL; } tr = malloc(sizeof(*tr)); if (!tr) { - pr_error("tracer: couldn't allocate memory\n"); + pr_error("tracer: couldn't allocate memory"); return NULL; }