[simio] support Timer_B compare latch double-buffering

This commit is contained in:
Tadashi G. Takaoka 2018-06-29 12:25:24 +09:00
parent f9f897e95e
commit c53f697b76
2 changed files with 359 additions and 6 deletions

View File

@ -90,6 +90,8 @@ struct timer {
uint16_t tar;
uint16_t ctls[MAX_CCRS];
uint16_t ccrs[MAX_CCRS];
/* Compare latch for Timer_B */
uint16_t bcls[MAX_CCRS];
};
static struct simio_device *timer_create(char **arg_text)
@ -148,6 +150,7 @@ static void timer_reset(struct simio_device *dev)
tr->go_down = false;
memset(tr->ccrs, 0, sizeof(tr->ccrs));
memset(tr->ctls, 0, sizeof(tr->ctls));
memset(tr->bcls, 0, sizeof(tr->bcls));
}
static int config_addr(address_t *addr, char **arg_text)
@ -334,6 +337,8 @@ static int timer_info(struct simio_device *dev)
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]);
if (tr->timer_type == TIMER_TYPE_B)
printc(", TBCL%d = 0x%04x", i, tr->bcls[i]);
printc("\n");
}
@ -397,12 +402,19 @@ static int timer_write(struct simio_device *dev,
int index = ((addr & 0xf) - 2) >> 1;
tr->ccrs[index] = data;
if (index == 0 && tr->ccrs[index] < tr->tar &&
if (tr->timer_type == TIMER_TYPE_A &&
index == 0 && data < tr->tar &&
(tr->tactl & (MC1 | MC0)) == MC0) {
/* When CCR[0] is set less than current TAR in up
* mode, TAR rolls to 0. */
tr->go_down = true;
}
if (tr->timer_type == TIMER_TYPE_B &&
(tr->ctls[index] & (CLLD1 | CLLD0)) == 0) {
/* Writing TBCCRx triggers update TBCLx immediately.
* No grouping. */
tr->bcls[index] = tr->ccrs[index];
}
return 0;
}
@ -477,6 +489,12 @@ static void timer_ack_interrupt(struct simio_device *dev, int irq)
/* By design irq1 does not clear CCIFG or TAIFG automatically */
}
static uint16_t get_ccr(struct timer *tr, int index) {
if (tr->timer_type == TIMER_TYPE_B)
return tr->bcls[index];
return tr->ccrs[index];
}
static uint16_t tar_increment(struct timer *tr)
{
tr->tar++;
@ -497,7 +515,7 @@ static void tar_step(struct timer *tr)
case 0:
break;
case 1:
if (tr->tar == tr->ccrs[0] || tr->go_down) {
if (tr->tar == get_ccr(tr, 0) || tr->go_down) {
tr->tar = 0;
tr->tactl |= TAIFG;
tr->go_down = false;
@ -512,9 +530,9 @@ static void tar_step(struct timer *tr)
break;
case 3:
if (tr->tar >= tr->ccrs[0])
if (tr->tar >= get_ccr(tr, 0))
tr->go_down = true;
if (!tr->tar)
if (tr->tar == 0)
tr->go_down = false;
if (tr->go_down) {
@ -530,7 +548,7 @@ static void tar_step(struct timer *tr)
static void comparator_step(struct timer *tr, int index)
{
if (tr->timer_type == TIMER_TYPE_A) {
if (tr->tar == tr->ccrs[index]) {
if (tr->tar == get_ccr(tr, index)) {
tr->ctls[index] |= CCIFG;
if (tr->ctls[index] & CCI)
tr->ctls[index] |= SCCI;
@ -540,8 +558,19 @@ static void comparator_step(struct timer *tr, int index)
}
if (tr->timer_type == TIMER_TYPE_B) {
if (tr->tar == tr->ccrs[index]) {
const uint16_t mc = tr->tactl & (MC1 | MC0);
const uint16_t clld = tr->ctls[index] & (CLLD1 | CLLD0);
if (tr->tar == 0) {
if (clld == CLLD0 || (clld == CLLD1 && mc != 0)) {
tr->bcls[index] = tr->ccrs[index];
}
}
if (tr->tar == get_ccr(tr, index)) {
tr->ctls[index] |= CCIFG;
if ((clld == CLLD1 && mc == (MC1 | MC0)) ||
clld == (CLLD1 | CLLD0)) {
tr->bcls[index] = tr->ccrs[index];
}
}
}
}

View File

@ -905,6 +905,324 @@ static void test_timer_b_length_16()
assert(read_timer(dev, TxR) == 4);
}
static void test_timer_b_compare_latch_0_up()
{
dev = create_timer("");
config_timer(dev, "type", "B");
/* Up 16 bit, SMCLK, clear */
write_timer(dev, TxCTL, MC0 | TACLR | TASSEL1);
write_timer(dev, TxCCR(0), 2);
write_timer(dev, TxCCR(2), 5);
// When CLLD=0, new data is transferred from TBCCRx to TBCLx immediately
// when TBCCRx is written to.
write_timer(dev, TxCCTL(0), CCIE);
write_timer(dev, TxCCTL(2), CCIE);
step_smclk(dev, 2);
assert(check_noirq(dev));
// TBCCR[0] is immediately transferred to TBCL[0].
write_timer(dev, TxCCR(0), 10);
// Because now TBCL[0] is 10, no overflow interrupt should happen at TBR=2
step_smclk(dev, 1);
assert(read_timer(dev, TxR) == 3);
assert(check_noirq(dev));
// Because TBCL[2] is 5, comparator interrupt should happen at TBR=5.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(2));
assert(check_noirq(dev));
// Because TBCL[2] is 8, comparator interrupt should happen again at TBR=8.
write_timer(dev, TxCCR(2), 8);
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(2));
assert(check_noirq(dev));
// Because TBCL[0] is 10, overflow interrupt should happen again at TBR=10.
step_smclk(dev, 2);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_timer(dev, TxR) == 0);
}
static void test_timer_b_compare_latch_1_up()
{
dev = create_timer("3");
config_timer(dev, "type", "B");
/* Up 16 bit, SMCLK, clear */
write_timer(dev, TxCTL, MC0 | TACLR | TASSEL1);
write_timer(dev, TxCCR(0), 5);
write_timer(dev, TxCCR(1), 2);
// When CLLD=1, new data is transferred from TBCCRx to TBCLx when TBR
// counts to 0.
write_timer(dev, TxCCTL(0), CLLD0 | CCIE);
write_timer(dev, TxCCTL(1), CLLD0 | CCIE);
step_smclk(dev, 2);
assert(check_noirq(dev));
// TBCCR[0] will be transferred to TBCL[0] when next TBR=0.
write_timer(dev, TxCCR(0), 10);
// TBCCR[1] will be transferred to TBCL[1] when next TBR=0.
write_timer(dev, TxCCR(1), 2);
// Because TBCL[1] keeps 2, compare interrupt should happen at TBR=2.
step_smclk(dev, 1);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(1));
assert(check_noirq(dev));
// Because TBCL[0] keeps 5, compare interrupt should happen at TBR=5, then TBR=0.
step_smclk(dev, 3);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
// Now TBCL[1] becomes 2, compare interrupt should happen again at TBR=2.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(1));
assert(check_noirq(dev));
// Now TBCL[0] becomes 10, no compare interrupt should happen at TBR=5.
step_smclk(dev, 7);
assert(check_noirq(dev));
// Then compare interrupt should happen at TBR=10, then TBR=0.
step_smclk(dev, 1);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
assert(read_timer(dev, TxR) == 0);
}
static void test_timer_b_compare_latch_2_continuous()
{
dev = create_timer("7");
config_timer(dev, "type", "B");
/* Continuous 8 bit, SMCLK, clear */
write_timer(dev, TxCTL, MC1 | CNTL1 | CNTL0 | TACLR | TASSEL1);
write_timer(dev, TxR, 0x00fe);
write_timer(dev, TxCCR(6), 2);
// When CLLD=2, new data is transferred from TBCCRx to TBCLx when TBR
// counts to 0 for continuous mode.
write_timer(dev, TxCCTL(6), CLLD1 | CCIE);
// TBCCR[6] will be transferred to TBCL[6] when next TBR=0.
write_timer(dev, TxCCR(6), 5);
step_smclk(dev, 2);
assert(read_timer(dev, TxR) == 0);
// Now TBCL[6] becomes 5, compare interrupt should not happen at TBR=2.
step_smclk(dev, 3);
assert(check_noirq(dev));
// Because TBCL[6] is 5, compare interrupt should happen at TBR=5.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(6));
assert(check_noirq(dev));
// Because TBCCR[6] will be transferred to TBCL[6] when next TBR=0,
// no compare interrupt should happen at TBR=5.
write_timer(dev, TxCCR(6), 10);
step_smclk(dev, 0x100);
assert(check_noirq(dev));
assert(read_timer(dev, TxR) > 5);
// Now TBCL[6] becomes 10, compare interrupt should happen.
step_smclk(dev, 5);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(6));
assert(check_noirq(dev));
}
static void test_timer_b_compare_latch_2_up()
{
dev = create_timer("5");
config_timer(dev, "type", "B");
/* Up 16 bit, SMCLK, clear */
write_timer(dev, TxCTL, MC0 | TACLR | TASSEL1);
write_timer(dev, TxCCR(0), 5);
write_timer(dev, TxCCR(4), 2);
// When CLLD=2, new data is transferred from TBCCRx to TBCLx when TBR
// counts to 0.
write_timer(dev, TxCCTL(0), CLLD0 | CCIE);
write_timer(dev, TxCCTL(4), CLLD0 | CCIE);
step_smclk(dev, 2);
assert(check_noirq(dev));
// TBCCR[0] will be transferred to TBCL[0] when next TBR=0.
write_timer(dev, TxCCR(0), 10);
// TBCCR[4] will be transferred to TBCL[4] when next TBR=0.
write_timer(dev, TxCCR(4), 2);
// Because TBCL[4] keeps 2, compare interrupt should happen at TBR=2.
step_smclk(dev, 1);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(4));
assert(check_noirq(dev));
// Because TBCL[0] keeps 5, compare interrupt should happen at TBR=5, then TBR=0.
step_smclk(dev, 3);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
// Now TBCL[4] becomes 2, compare interrupt should happen again at TBR=2.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(4));
assert(check_noirq(dev));
// Now TBCL[0] becomes 10, no compare interrupt should happen at TBR=5.
step_smclk(dev, 7);
assert(check_noirq(dev));
// Then compare interrupt should happen at TBR=10, then TBR=0.
step_smclk(dev, 1);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
assert(read_timer(dev, TxR) == 0);
}
static void test_timer_b_compare_latch_2_updown()
{
dev = create_timer("7");
config_timer(dev, "type", "B");
/* Up/Down 16 bit, SMCLK, interrupt enable, clear */
write_timer(dev, TxCTL, MC1 | MC0 | TACLR | TAIE | TASSEL1);
write_timer(dev, TxCCR(0), 10);
write_timer(dev, TxCCTL(0), CCIE);
write_timer(dev, TxCCR(5), 5);
// When CLLD=2, new data is transferred from TBCCRx to TBCLx when TBR
// counts to the old TBCL0 value or to 0 for up/down mode.
write_timer(dev, TxCCTL(5), CLLD1 | CCIE);
step_smclk(dev, 4);
assert(check_noirq(dev));
assert(read_timer(dev, TxR) == 4);
// TBCCR[5] will be transferred to TBCL[5] when next TBR=5.
write_timer(dev, TxCCR(5), 8);
// Because TBCL[0] is 5, compare interrupt should happen at TBR=TBCL[5]=5.
step_smclk(dev, 2);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(5));
assert(check_noirq(dev));
// Now TBCL[5] becomes 8, compare interrupt should happen at TBR=8.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(5));
assert(check_noirq(dev));
// Because TBCL[0]=10, compare interrupt should happen at TBR=10.
step_smclk(dev, 2);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
assert(read_timer(dev, TxR) == 9);
// Because TBCL[5] is still 8, compare interrupt should happen again at TBR=8.
step_smclk(dev, 2);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(5));
assert(check_noirq(dev));
// Because TBCL[5] is still 8, no compare interrupt should happen.
step_smclk(dev, 6);
assert(read_timer(dev, TxR) == 1);
// TBCCR[5] will be transferred to TBCL[5] when next TBR=0.
write_timer(dev, TxCCR(5), 2);
// Compare interrupt should happen at TBR=0.
step_smclk(dev, 1);
assert(check_irq1(dev));
assert(read_iv(dev) == TBIV_TBIFG);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
assert(read_timer(dev, TxR) == 0);
// Now TBCL[5] becomes 2, compare interrupt should happen at TBR=2.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(5));
assert(check_noirq(dev));
assert(read_timer(dev, TxR) == 3);
}
static void test_timer_b_compare_latch_3_up()
{
dev = create_timer("5");
config_timer(dev, "type", "B");
/* Up 16 bit, SMCLK, clear */
write_timer(dev, TxCTL, MC0 | TACLR | TASSEL1);
write_timer(dev, TxCCR(0), 10);
write_timer(dev, TxCCTL(0), CCIE);
write_timer(dev, TxCCR(2), 5);
// When CLLD=3, new data is transferred from TBCCRx to TBCLx when TBR
// counts to the old TBCL0 value.
write_timer(dev, TxCCTL(2), CLLD1 | CLLD0 | CCIE);
step_smclk(dev, 4);
assert(check_noirq(dev));
assert(read_timer(dev, TxR) == 4);
// TBCCR[2] will be transferred to TBCL[2] when next TBR=5.
write_timer(dev, TxCCR(2), 8);
// Because TBCL[0] is 5, compare interrupt should happen at TBR=TBCL[2]=5.
step_smclk(dev, 2);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(2));
assert(check_noirq(dev));
// TBCCR[2] will be transferred to TBCL[2] when next TBR=8.
write_timer(dev, TxCCR(2), 2);
// Now TBCL[2] becomes 8, compare interrupt should happen at TBR=8.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(2));
assert(check_noirq(dev));
// Because TBCL[0] is 10, compare interrupt should happen at TBR=10.
step_smclk(dev, 2);
assert(check_irq0(dev));
ack_irq0(dev);
assert(check_noirq(dev));
assert(read_iv(dev) == TxIV_TxIFG(0));
assert(read_timer(dev, TxR) == 0);
// Because TBCL[2] is 2, compare interrupt should happen again at TBR=2.
step_smclk(dev, 3);
assert(check_irq1(dev));
assert(read_iv(dev) == TxIV_TxIFG(2));
assert(check_noirq(dev));
}
/*
* Test runner.
@ -953,4 +1271,10 @@ int main(int argc, char **argv)
RUN_TEST(test_timer_b_length_10);
RUN_TEST(test_timer_b_length_12);
RUN_TEST(test_timer_b_length_16);
RUN_TEST(test_timer_b_compare_latch_0_up);
RUN_TEST(test_timer_b_compare_latch_1_up);
RUN_TEST(test_timer_b_compare_latch_2_continuous);
RUN_TEST(test_timer_b_compare_latch_2_up);
RUN_TEST(test_timer_b_compare_latch_2_updown);
RUN_TEST(test_timer_b_compare_latch_3_up);
}