From c53f697b767b3e6d1867501e15a0ff46bb5bc12a Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 29 Jun 2018 12:25:24 +0900 Subject: [PATCH] [simio] support Timer_B compare latch double-buffering --- simio/simio_timer.c | 41 ++++- simio/tests/test_timer.c | 324 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+), 6 deletions(-) diff --git a/simio/simio_timer.c b/simio/simio_timer.c index bae2135..5a2ddde 100644 --- a/simio/simio_timer.c +++ b/simio/simio_timer.c @@ -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]; + } } } } diff --git a/simio/tests/test_timer.c b/simio/tests/test_timer.c index ef85e84..977611f 100644 --- a/simio/tests/test_timer.c +++ b/simio/tests/test_timer.c @@ -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); }