[simio] Fix changing timer period

This commit is contained in:
Tadashi G. Takaoka 2018-06-29 12:25:24 +09:00
parent b323849d8a
commit d49dcefec6
2 changed files with 103 additions and 5 deletions

View File

@ -16,6 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -60,7 +61,7 @@ struct timer {
int size; int size;
int clock_input; int clock_input;
int go_down; bool go_down;
address_t base_addr; address_t base_addr;
address_t iv_addr; address_t iv_addr;
@ -126,7 +127,7 @@ static void timer_reset(struct simio_device *dev)
tr->tactl = 0; tr->tactl = 0;
tr->tar = 0; tr->tar = 0;
tr->go_down = 0; tr->go_down = false;
memset(tr->ccrs, 0, sizeof(tr->ccrs)); memset(tr->ccrs, 0, sizeof(tr->ccrs));
memset(tr->ctls, 0, sizeof(tr->ctls)); 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; int index = ((addr & 0xf) - 2) >> 1;
tr->ccrs[index] = data; 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; return 0;
} }
@ -400,9 +407,10 @@ static void tar_step(struct timer *tr)
switch ((tr->tactl >> 4) & 3) { switch ((tr->tactl >> 4) & 3) {
case 0: break; case 0: break;
case 1: case 1:
if (tr->tar == tr->ccrs[0]) { if (tr->tar == tr->ccrs[0] || tr->go_down) {
tr->tar = 0; tr->tar = 0;
tr->tactl |= TAIFG; tr->tactl |= TAIFG;
tr->go_down = false;
} else { } else {
tr->tar++; tr->tar++;
} }
@ -416,9 +424,9 @@ static void tar_step(struct timer *tr)
case 3: case 3:
if (tr->tar >= tr->ccrs[0]) if (tr->tar >= tr->ccrs[0])
tr->go_down = 1; tr->go_down = true;
if (!tr->tar) if (!tr->tar)
tr->go_down = 0; tr->go_down = false;
if (tr->go_down) { if (tr->go_down) {
tr->tar--; tr->tar--;

View File

@ -348,6 +348,94 @@ static void test_timer_up()
assert(check_noirq(dev)); 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() static void test_timer_divider()
{ {
dev = create_timer(""); dev = create_timer("");
@ -594,6 +682,8 @@ int main(int argc, char **argv)
RUN_TEST(test_timer_up_stop); RUN_TEST(test_timer_up_stop);
RUN_TEST(test_timer_updown_stop); RUN_TEST(test_timer_updown_stop);
RUN_TEST(test_timer_up); 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_divider);
RUN_TEST(test_timer_capture); RUN_TEST(test_timer_capture);
RUN_TEST(test_timer_compare); RUN_TEST(test_timer_compare);