diff --git a/simio/simio_timer.c b/simio/simio_timer.c index 2d304c3..bae2135 100644 --- a/simio/simio_timer.c +++ b/simio/simio_timer.c @@ -36,13 +36,18 @@ #define TACLR 0x0004 /* Timer A counter clear */ #define TAIE 0x0002 /* Timer A counter interrupt enable */ #define TAIFG 0x0001 /* Timer A counter interrupt flag */ +/* TBCTL bits */ +#define TBCLGRP1 0x4000 /* Timer B Compare latch load group 1 */ +#define TBCLGRP0 0x2000 /* Timer B Compare latch load group 0 */ +#define CNTL1 0x1000 /* Timer B Counter length 1 */ +#define CNTL0 0x0800 /* Timer B Counter length 0 */ /* 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 SCS 0x0800 /* Capture synchronize */ #define SCCI 0x0400 /* Latched capture signal (read) */ #define CAP 0x0100 /* Capture mode: 1 /Compare mode : 0 */ #define OUTMOD2 0x0080 /* Output mode 2 */ @@ -53,9 +58,20 @@ /* #define OUT 0x0004 PWM Output signal if output mode 0 */ #define COV 0x0002 /* Capture/compare overflow flag */ #define CCIFG 0x0001 /* Capture/compare interrupt flag */ +/* TBCCTLx flags */ +#define CLLD1 0x0400 /* Compare latch load source 1 */ +#define CLLD0 0x0200 /* Compare latch load source 0 */ +/* Timer IV words */ +#define TAIV_TAIFG 0x000A /* Interrupt vector word for TAIFG */ +#define TBIV_TBIFG 0x000E /* Interrupt vector word for TBIFG */ #define MAX_CCRS 7 +typedef enum { + TIMER_TYPE_A, + TIMER_TYPE_B, +} timer_type_t; + struct timer { struct simio_device base; @@ -67,6 +83,7 @@ struct timer { address_t iv_addr; int irq0; int irq1; + timer_type_t timer_type; /* IO registers */ uint16_t tactl; @@ -110,6 +127,7 @@ static struct simio_device *timer_create(char **arg_text) tr->iv_addr = 0x12e; tr->irq0 = 9; tr->irq1 = 8; + tr->timer_type = TIMER_TYPE_A; return (struct simio_device *)tr; } @@ -149,6 +167,28 @@ static int config_addr(address_t *addr, char **arg_text) return 0; } +static int config_type(timer_type_t *timer_type, char **arg_text) +{ + char *text = get_arg(arg_text); + + if (!text) { + printc_err("timer: config: expected type\n"); + return -1; + } + + if (!strcasecmp(text, "A")) { + *timer_type = TIMER_TYPE_A; + return 0; + } + if (!strcasecmp(text, "B")) { + *timer_type = TIMER_TYPE_B; + return 0; + } + + printc_err("timer: can't parse type: %s\n", text); + return -1; +} + static int config_irq(int *irq, char **arg_text) { char *text = get_arg(arg_text); @@ -237,6 +277,8 @@ static int timer_config(struct simio_device *dev, if (!strcasecmp(param, "base")) return config_addr(&tr->base_addr, arg_text); + if (!strcasecmp(param, "type")) + return config_type(&tr->timer_type, arg_text); if (!strcasecmp(param, "iv")) return config_addr(&tr->iv_addr, arg_text); if (!strcasecmp(param, "irq0")) @@ -266,7 +308,8 @@ static uint16_t calc_iv(struct timer *tr, int update) if ((tr->tactl & (TAIFG | TAIE)) == (TAIFG | TAIE)) { if (update) tr->tactl &= ~TAIFG; - return 0xa; + return (tr->timer_type == TIMER_TYPE_A) ? + TAIV_TAIFG : TBIV_TBIFG; } return 0; @@ -276,26 +319,46 @@ static int timer_info(struct simio_device *dev) { struct timer *tr = (struct timer *)dev; int i; + char timer_type = (tr->timer_type == TIMER_TYPE_A) ? 'A' : 'B'; 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("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, 0)); + printc("T%cCTL: 0x%04x\n", timer_type, tr->tactl); + printc("T%cR: 0x%04x\n", timer_type, tr->tar); + printc("T%cIV: 0x%02x\n", timer_type, calc_iv(tr, 0)); printc("\n"); - for (i = 0; i < tr->size; i++) - printc("Channel %2d, TACCTL = 0x%04x, TACCR = 0x%04x\n", - i, tr->ctls[i], tr->ccrs[i]); + for (i = 0; i < tr->size; i++) { + printc("T%cCCTL%d = 0x%04x, T%cCCR%d = 0x%04x", + timer_type, i, tr->ctls[i], timer_type, i, tr->ccrs[i]); + printc("\n"); + } return 0; } +static uint16_t tar_mask(struct timer *tr) +{ + if (tr->timer_type == TIMER_TYPE_B) { + switch (tr->tactl & (CNTL1 | CNTL0)) { + case 0: /* 16 bits */ + break; + case CNTL0: /* 12 bits */ + return 0x0fff; + case CNTL1: /* 10 bits */ + return 0x03ff; + case CNTL1 | CNTL0: /* 8 bits */ + return 0x00ff; + } + } + return 0xffff; +} + static int timer_write(struct simio_device *dev, - address_t addr, uint16_t data) + address_t addr, uint16_t data) { struct timer *tr = (struct timer *)dev; @@ -308,7 +371,7 @@ static int timer_write(struct simio_device *dev, } if (addr == tr->base_addr + 0x10) { - tr->tar = data; + tr->tar = data & tar_mask(tr); return 0; } @@ -316,8 +379,13 @@ static int timer_write(struct simio_device *dev, addr < tr->base_addr + (tr->size << 1) + 2) { int index = ((addr & 0xf) - 2) >> 1; uint16_t oldval = tr->ctls[index]; + uint16_t mask; - tr->ctls[index] = (data & 0xf9f7) | (oldval & 0x0608); + if (tr->timer_type == TIMER_TYPE_A) + mask = 0x0608; + if (tr->timer_type == TIMER_TYPE_B) + mask = 0x0008; + tr->ctls[index] = (data & ~mask) | (oldval & mask); /* Check capture initiated by Software */ if ((data & (CAP | CCIS1)) == (CAP | CCIS1)) trigger_capture(tr, index, oldval & CCI, data & CCIS0); @@ -348,7 +416,7 @@ static int timer_write(struct simio_device *dev, } static int timer_read(struct simio_device *dev, - address_t addr, uint16_t *data) + address_t addr, uint16_t *data) { struct timer *tr = (struct timer *)dev; @@ -409,23 +477,37 @@ static void timer_ack_interrupt(struct simio_device *dev, int irq) /* By design irq1 does not clear CCIFG or TAIFG automatically */ } +static uint16_t tar_increment(struct timer *tr) +{ + tr->tar++; + tr->tar &= tar_mask(tr); + return tr->tar; +} + +static uint16_t tar_decrement(struct timer *tr) +{ + tr->tar--; + tr->tar &= tar_mask(tr); + return tr->tar; +} + static void tar_step(struct timer *tr) { switch ((tr->tactl >> 4) & 3) { - case 0: break; + case 0: + break; case 1: if (tr->tar == tr->ccrs[0] || tr->go_down) { tr->tar = 0; tr->tactl |= TAIFG; tr->go_down = false; } else { - tr->tar++; + tar_increment(tr); } break; case 2: - tr->tar++; - if (!tr->tar) + if (tar_increment(tr) == 0) tr->tactl |= TAIFG; break; @@ -436,16 +518,34 @@ static void tar_step(struct timer *tr) tr->go_down = false; if (tr->go_down) { - tr->tar--; - if (!tr->tar) + if (tar_decrement(tr) == 0) tr->tactl |= TAIFG; } else { - tr->tar++; + tar_increment(tr); } break; } } +static void comparator_step(struct timer *tr, int index) +{ + if (tr->timer_type == TIMER_TYPE_A) { + if (tr->tar == tr->ccrs[index]) { + tr->ctls[index] |= CCIFG; + if (tr->ctls[index] & CCI) + tr->ctls[index] |= SCCI; + else + tr->ctls[index] &= ~SCCI; + } + } + + if (tr->timer_type == TIMER_TYPE_B) { + if (tr->tar == tr->ccrs[index]) { + tr->ctls[index] |= CCIFG; + } + } +} + static void timer_step(struct simio_device *dev, uint16_t status, const int *clocks) { @@ -471,16 +571,10 @@ static void timer_step(struct simio_device *dev, for (i = 0; i < pulse_count; 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; - } - + for (j = 0; j < tr->size; j++) { + if (!(tr->ctls[j] & CAP)) + comparator_step(tr, j); + } tar_step(tr); } } @@ -488,22 +582,24 @@ static void timer_step(struct simio_device *dev, 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.\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", + "This peripheral implements the Timer_A and Timer_B module.\n" + "\n" + "Constructor arguments: [size]\n" + " Specify the number of capture/compare registers.\n" + "\n" + "Config arguments are:\n" + " base
\n" + " Set the peripheral base address.\n" + " type \n" + " Set timer type.\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, diff --git a/simio/tests/test_timer.c b/simio/tests/test_timer.c index 6d77589..ef85e84 100644 --- a/simio/tests/test_timer.c +++ b/simio/tests/test_timer.c @@ -71,7 +71,6 @@ static int config_timer(struct simio_device *dev, const char *param, #define TxCCTL(index) (0x02 + (index) * 2) #define TxCCR(index) (0x12 + (index) * 2) #define TxIV_TxIFG(index) ((index) * 2) -#define TAIV_TAIFG 0x0a static uint16_t read_timer(struct simio_device *dev, int offset) { @@ -185,6 +184,7 @@ static void test_create_no_option() // Check default values. struct timer *tmr = (struct timer *)dev; assert(tmr->size == 3); + assert(tmr->timer_type == TIMER_TYPE_A); assert(tmr->base_addr == 0x0160); assert(tmr->iv_addr == 0x012e); assert(tmr->irq0 == 9); @@ -226,6 +226,53 @@ static void test_create_with_size_1() assert(dev == NULL); } +static void test_config_type_default() +{ + dev = create_timer(""); + + // Default timer type is A. + struct timer *tmr = (struct timer *)dev; + assert(tmr->timer_type == TIMER_TYPE_A); +} + +static void test_config_type_A() +{ + dev = create_timer(""); + + // Timer can configured as type A. + assert(config_timer(dev, "type", "A") == 0); + struct timer *tmr = (struct timer *)dev; + assert(tmr->timer_type == TIMER_TYPE_A); +} + +static void test_config_type_B() +{ + dev = create_timer(""); + + // Timer can configured as type B. + struct timer *tmr = (struct timer *)dev; + assert(config_timer(dev, "type", "B") == 0); + assert(tmr->timer_type == TIMER_TYPE_B); +} + +static void test_config_type_bad() +{ + dev = create_timer(""); + + struct timer *tmr = (struct timer *)dev; + // Timer can't configured other than A/B. + assert(config_timer(dev, "type", "bad") != 0); +} + +static void test_config_type_empty() +{ + dev = create_timer(""); + + struct timer *tmr = (struct timer *)dev; + // Timer type can't be empty. + assert(config_timer(dev, "type", "") != 0); +} + static void test_address_space() { dev = create_timer("7"); @@ -319,7 +366,7 @@ static void test_timer_updown_stop() assert(read_timer(dev, TxR) == 0); } -static void test_timer_up() +static void test_timer_a_up() { dev = create_timer(""); @@ -348,7 +395,7 @@ static void test_timer_up() assert(check_noirq(dev)); } -static void test_timer_up_change_period() +static void test_timer_a_up_change_period() { dev = create_timer(""); @@ -396,7 +443,7 @@ static void test_timer_up_change_period() assert_not(read_timer(dev, TxCTL) & TAIFG); } -static void test_timer_updown_change_period() +static void test_timer_a_updown_change_period() { dev = create_timer(""); @@ -657,7 +704,7 @@ static void test_timer_capture_by_signal() assert(read_timer(dev, TxCCR(2)) == 50); } -static void test_timer_compare() +static void test_timer_a_compare() { dev = create_timer(""); @@ -722,6 +769,142 @@ static void test_timer_compare() assert_not(read_timer(dev, TxCCTL(1)) & SCCI); } +static void test_timer_b_compare() +{ + dev = create_timer(""); + config_timer(dev, "type", "B"); + + /* Continuous mode, ACLK, interrupt enable, clear */ + write_timer(dev, TxCTL, MC1 | TASSEL0 | TACLR | TAIE); + write_timer(dev, TxR, 0xffff); + /* Compare mode, enable interrupts */ + write_timer(dev, TxCCTL(0), CCIE); + write_timer(dev, TxCCR(0), 200); + write_timer(dev, TxCCTL(1), CCIE); + write_timer(dev, TxCCR(1), 100); + write_timer(dev, TxCCTL(2), CCIE); + write_timer(dev, TxCCR(2), 200); + + // Timer_B overflow interrupt should happen. + step_aclk(dev, 1); + assert(check_irq1(dev)); + // Timer_B overflow vector is 0x0e. + assert(TBIV_TBIFG == 0x0e); + assert(read_iv(dev) == TBIV_TBIFG); + assert(check_noirq(dev)); + assert(read_timer(dev, TxR) == 0); + + step_aclk(dev, 100); + assert(check_noirq(dev)); + // Set CCI of CCTL[1] to 1. + config_timer(dev, "set", "1 1"); + // Timer_B comparator interrupt should happen. + step_aclk(dev, 1); + assert(check_irq1(dev)); + assert(read_timer(dev, TxCCTL(1)) & CCIFG); + assert(read_iv(dev) == TxIV_TxIFG(1)); + assert(check_noirq(dev)); + assert_not(read_timer(dev, TxCCTL(1)) & CCIFG); + + // Timer_B comparator interrupts should happen. + step_aclk(dev, 100); + assert(check_irq0(dev)); + assert(read_timer(dev, TxCCTL(0)) & CCIFG); + ack_irq0(dev); + assert_not(check_noirq(dev)); + assert_not(read_timer(dev, TxCCTL(0)) & CCIFG); + // Lower priority interrupt. + assert(check_irq1(dev)); + assert(read_timer(dev, TxCCTL(2)) & CCIFG); + assert(read_iv(dev) == TxIV_TxIFG(2)); + assert(check_noirq(dev)); + assert_not(read_timer(dev, TxCCTL(2)) & CCIFG); + + // Set CCI of CCTL[1] to 0. + config_timer(dev, "set", "1 0"); + write_timer(dev, TxCCR(1), 300); + step_aclk(dev, 100); + // Timer_B comparator interrupt should happen. + assert(check_irq1(dev)); + assert(read_timer(dev, TxCCTL(1)) & CCIFG); + assert(read_iv(dev) == TxIV_TxIFG(1)); + assert(check_noirq(dev)); + assert_not(read_timer(dev, TxCCTL(1)) & CCIFG); +} + +static void test_timer_capture() +{ + dev = create_timer(""); + + /* Continuous mode, ACLK, clear */ + write_timer(dev, TxCTL, MC1 | TASSEL0 | TACLR); + /* Capture mode, enable interrupts */ +} + +static void test_timer_b_length_8() +{ + dev = create_timer(""); + config_timer(dev, "type", "B"); + + /* Continuous 8 bit, SMCLK, interrupt enable, clear */ + write_timer(dev, TxCTL, MC1 | CNTL1 | CNTL0 | TACLR | TAIE | TASSEL1); + write_timer(dev, TxR, 0x00ff); + step_smclk(dev, 2); + + // Timer B can configured as 8 bit length. + assert(check_irq1(dev)); + assert(read_iv(dev) == TBIV_TBIFG); + assert(read_timer(dev, TxR) == 1); +} + +static void test_timer_b_length_10() +{ + dev = create_timer(""); + config_timer(dev, "type", "B"); + + /* Continuous 10 bit, ACLK, interrupt enable, clear */ + write_timer(dev, TxCTL, MC1 | CNTL1 | TACLR | TAIE | TASSEL0); + write_timer(dev, TxR, 0x03ff); + step_aclk(dev, 3); + + // Timer B can configured as 10 bit length. + assert(check_irq1(dev)); + assert(read_iv(dev) == TBIV_TBIFG); + assert(read_timer(dev, TxR) == 2); +} + +static void test_timer_b_length_12() +{ + dev = create_timer(""); + config_timer(dev, "type", "B"); + + /* Continuous 12 bit, SMCLK, interrupt enable, clear */ + write_timer(dev, TxCTL, MC1 | CNTL0 | TACLR | TAIE | TASSEL1); + write_timer(dev, TxR, 0x0fff); + step_smclk(dev, 4); + + // Timer B can configured as 12 bit length. + assert(check_irq1(dev)); + assert(read_iv(dev) == TBIV_TBIFG); + assert(read_timer(dev, TxR) == 3); +} + +static void test_timer_b_length_16() +{ + dev = create_timer(""); + config_timer(dev, "type", "B"); + + /* Continuous 16 bit, SMCLK, interrupt enable, clear */ + write_timer(dev, TxCTL, MC1 | TACLR | TAIE | TASSEL1); + write_timer(dev, TxR, 0xffff); + step_smclk(dev, 5); + + // Timer B can configured as 16 bit length. + assert(check_irq1(dev)); + assert(read_iv(dev) == TBIV_TBIFG); + assert(read_timer(dev, TxR) == 4); +} + /* * Test runner. @@ -748,16 +931,26 @@ int main(int argc, char **argv) RUN_TEST(test_create_with_size_2); RUN_TEST(test_create_with_size_8); RUN_TEST(test_create_with_size_1); + RUN_TEST(test_config_type_default); + RUN_TEST(test_config_type_A); + RUN_TEST(test_config_type_B); + RUN_TEST(test_config_type_bad); + RUN_TEST(test_config_type_empty); RUN_TEST(test_address_space); RUN_TEST(test_timer_continuous); RUN_TEST(test_timer_stop); RUN_TEST(test_timer_up_stop); RUN_TEST(test_timer_updown_stop); - RUN_TEST(test_timer_up); - RUN_TEST(test_timer_up_change_period); - RUN_TEST(test_timer_updown_change_period); + RUN_TEST(test_timer_a_up); + RUN_TEST(test_timer_a_up_change_period); + RUN_TEST(test_timer_a_updown_change_period); RUN_TEST(test_timer_divider); RUN_TEST(test_timer_capture_by_software); RUN_TEST(test_timer_capture_by_signal); - RUN_TEST(test_timer_compare); + RUN_TEST(test_timer_a_compare); + RUN_TEST(test_timer_b_compare); + RUN_TEST(test_timer_b_length_8); + RUN_TEST(test_timer_b_length_10); + RUN_TEST(test_timer_b_length_12); + RUN_TEST(test_timer_b_length_16); }