[simio] support Timer_B compare latch double-buffering
This commit is contained in:
parent
f9f897e95e
commit
c53f697b76
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue