From d49dcefec69d7543658bf506d05c6c8a97dbfc01 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 29 Jun 2018 12:25:24 +0900 Subject: [PATCH] [simio] Fix changing timer period --- simio/simio_timer.c | 18 +++++--- simio/tests/test_timer.c | 90 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/simio/simio_timer.c b/simio/simio_timer.c index 63aa83d..b40157e 100644 --- a/simio/simio_timer.c +++ b/simio/simio_timer.c @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include @@ -60,7 +61,7 @@ struct timer { int size; int clock_input; - int go_down; + bool go_down; address_t base_addr; address_t iv_addr; @@ -126,7 +127,7 @@ static void timer_reset(struct simio_device *dev) tr->tactl = 0; tr->tar = 0; - tr->go_down = 0; + tr->go_down = false; memset(tr->ccrs, 0, sizeof(tr->ccrs)); memset(tr->ctls, 0, sizeof(tr->ctls)); } @@ -321,6 +322,12 @@ 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 && + (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; + } return 0; } @@ -400,9 +407,10 @@ static void tar_step(struct timer *tr) switch ((tr->tactl >> 4) & 3) { case 0: break; case 1: - if (tr->tar == tr->ccrs[0]) { + if (tr->tar == tr->ccrs[0] || tr->go_down) { tr->tar = 0; tr->tactl |= TAIFG; + tr->go_down = false; } else { tr->tar++; } @@ -416,9 +424,9 @@ static void tar_step(struct timer *tr) case 3: if (tr->tar >= tr->ccrs[0]) - tr->go_down = 1; + tr->go_down = true; if (!tr->tar) - tr->go_down = 0; + tr->go_down = false; if (tr->go_down) { tr->tar--; diff --git a/simio/tests/test_timer.c b/simio/tests/test_timer.c index fb45beb..4581d4b 100644 --- a/simio/tests/test_timer.c +++ b/simio/tests/test_timer.c @@ -348,6 +348,94 @@ static void test_timer_up() assert(check_noirq(dev)); } +static void test_timer_up_change_period() +{ + dev = create_timer(""); + + /* Up mode, SMCLK, enable interrupt, clear */ + write_timer(dev, TxCTL, MC0 | TASSEL1 | TACLR | TAIE); + write_timer(dev, TxCCTL(0), CCIE); + write_timer(dev, TxCCR(0), 10); + step_smclk(dev, 8); + + // Changing period to less than current count will roll down + // counter to 0. + assert(read_timer(dev, TxR) == 8); + write_timer(dev, TxCCR(0), 5); + assert(check_noirq(dev)); + + // TAIFG interrupt should happen. + step_smclk(dev, 1); + assert(read_timer(dev, TxR) == 0); + assert(check_irq1(dev)); + assert(read_timer(dev, TxCTL) & TAIFG); + assert(read_iv(dev) == TAIV_TAIFG); + assert(check_noirq(dev)); + assert_not(read_timer(dev, TxCTL) & TAIFG); + + step_smclk(dev, 4); + assert(read_timer(dev, TxR) == 4); + // Changing period to greater that current count will continue + // counting to the new period. + write_timer(dev, TxCCR(0), 8); + step_smclk(dev, 4); + assert(check_noirq(dev)); + assert(read_timer(dev, TxR) == 8); + + // Compare interrupt should happen at new period TAR=8 + step_smclk(dev, 1); + assert(read_timer(dev, TxR) == 0); + assert(check_irq0(dev)); + assert(read_timer(dev, TxCCTL(0)) & CCIFG); + ack_irq0(dev); + assert_not(read_timer(dev, TxCCTL(0)) & CCIFG); + assert(read_timer(dev, TxCTL) & TAIFG); + assert(check_irq1(dev)); + assert(read_iv(dev) == TAIV_TAIFG); + assert(check_noirq(dev)); + assert_not(read_timer(dev, TxCTL) & TAIFG); +} + +static void test_timer_updown_change_period() +{ + dev = create_timer(""); + + /* Up/Down mode, SMCLK, enable interrupt, clear */ + write_timer(dev, TxCTL, MC1 | MC0 | TASSEL1 | TACLR | TAIE); + write_timer(dev, TxCCTL(0), CCIE); + write_timer(dev, TxCCR(0), 10); + step_smclk(dev, 8); + + // While counting up, changing period to less than current + // count will change the counting direction to down. + assert(read_timer(dev, TxR) == 8); + write_timer(dev, TxCCR(0), 5); + assert(read_timer(dev, TxR) == 8); + step_smclk(dev, 2); + assert(read_timer(dev, TxR) == 6); + assert(check_noirq(dev)); + + // While counting down, Changing period to greater that + // current count will continue counting to 0. + write_timer(dev, TxCCR(0), 8); + step_smclk(dev, 6); + assert(check_irq1(dev)); + assert(read_iv(dev) == TAIV_TAIFG); + assert(check_noirq(dev)); + assert(read_timer(dev, TxR) == 0); + + // Then count up to TACCR[0] and compare interrupt should happen. + step_smclk(dev, 8); + assert(check_noirq(dev)); + assert(read_timer(dev, TxR) == 8); + step_smclk(dev, 1); + assert(check_irq0(dev)); + assert(read_timer(dev, TxCCTL(0)) & CCIFG); + ack_irq0(dev); + assert(check_noirq(dev)); + assert(read_timer(dev, TxR) == 7); +} + static void test_timer_divider() { dev = create_timer(""); @@ -594,6 +682,8 @@ int main(int argc, char **argv) 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_divider); RUN_TEST(test_timer_capture); RUN_TEST(test_timer_compare);