[simio] support Timer_B

Timer_B is identical to Timer_A with the following exceptions.

  1. The SCCI bit function is not implemented in Timer_B.
  2. The interrupt vector word of TBIFG is different than TAIFG one.
  3. The length of Timer_B is programable to be 8, 10, 12, or 16 bits.
  4. Timer_B TBCCRx registers can be double-buffered.
  5. Timer_B TBCCRx registers can be grouped.

This change implements 1, 2, and 3.
Double-buffering and grouping TBCCRx will be following.
This commit is contained in:
Tadashi G. Takaoka 2018-06-29 12:25:24 +09:00
parent 8db02ae3d4
commit f9f897e95e
2 changed files with 345 additions and 56 deletions

View File

@ -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 <address>\n"
" Set the peripheral base address.\n"
" irq0 <interrupt>\n"
" Set the interrupt vector for CCR0.\n"
" irq1 <interrupt>\n"
" Set the interrupt vector for CCR1.\n"
" iv <address>\n"
" Set the interrupt vector register address.\n"
" set <channel> <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 <address>\n"
" Set the peripheral base address.\n"
" type <A|B>\n"
" Set timer type.\n"
" irq0 <interrupt>\n"
" Set the interrupt vector for CCR0.\n"
" irq1 <interrupt>\n"
" Set the interrupt vector for CCR1.\n"
" iv <address>\n"
" Set the interrupt vector register address.\n"
" set <channel> <0|1>\n"
" Set the capture input value on the given channel.\n",
.create = timer_create,
.destroy = timer_destroy,

View File

@ -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);
}