[simio] support Timer_B compare latch grouping
This commit is contained in:
parent
c53f697b76
commit
8564f42b66
|
@ -92,6 +92,8 @@ struct timer {
|
|||
uint16_t ccrs[MAX_CCRS];
|
||||
/* Compare latch for Timer_B */
|
||||
uint16_t bcls[MAX_CCRS];
|
||||
/* True if ccrs[index] has a value set. Used for compare latch grouping */
|
||||
bool valid_ccrs[MAX_CCRS];
|
||||
};
|
||||
|
||||
static struct simio_device *timer_create(char **arg_text)
|
||||
|
@ -151,6 +153,7 @@ static void timer_reset(struct simio_device *dev)
|
|||
memset(tr->ccrs, 0, sizeof(tr->ccrs));
|
||||
memset(tr->ctls, 0, sizeof(tr->ctls));
|
||||
memset(tr->bcls, 0, sizeof(tr->bcls));
|
||||
memset(tr->valid_ccrs, false, sizeof(tr->valid_ccrs));
|
||||
}
|
||||
|
||||
static int config_addr(address_t *addr, char **arg_text)
|
||||
|
@ -362,6 +365,49 @@ static uint16_t tar_mask(struct timer *tr)
|
|||
return 0xffff;
|
||||
}
|
||||
|
||||
static void set_bcl(struct timer *tr, int index)
|
||||
{
|
||||
tr->bcls[index] = tr->ccrs[index];
|
||||
tr->valid_ccrs[index] = false;
|
||||
}
|
||||
|
||||
static bool no_double_buffer(struct timer *tr, int index)
|
||||
{
|
||||
uint16_t clgrp = tr->tactl & (TBCLGRP1 | TBCLGRP0);
|
||||
|
||||
if (clgrp == TBCLGRP0 && (index == 2 || index == 4 || index == 6))
|
||||
return (tr->ctls[index - 1] & (CLLD1 | CLLD0)) == 0;
|
||||
if (clgrp == TBCLGRP1 && (index == 2 || index == 5))
|
||||
return (tr->ctls[index - 1] & (CLLD1 | CLLD0)) == 0;
|
||||
if (clgrp == TBCLGRP1 && (index == 3 || index == 6))
|
||||
return (tr->ctls[index - 2] & (CLLD1 | CLLD0)) == 0;
|
||||
if (clgrp == (TBCLGRP1 | TBCLGRP0))
|
||||
return (tr->ctls[1] & (CLLD1 | CLLD0)) == 0;
|
||||
return (tr->ctls[index] & (CLLD1 | CLLD0)) == 0;
|
||||
}
|
||||
|
||||
static void set_ccr(struct timer *tr, int index, uint16_t data)
|
||||
{
|
||||
tr->ccrs[index] = data;
|
||||
tr->valid_ccrs[index] = true;
|
||||
|
||||
if (tr->timer_type == TIMER_TYPE_A) {
|
||||
/* When CCR[0] set is less than TAR in up mode, TAR rolls to
|
||||
* 0. */
|
||||
if (index == 0 && data < tr->tar &&
|
||||
(tr->tactl & (MC1 | MC0)) == MC0) {
|
||||
tr->go_down = true;
|
||||
}
|
||||
}
|
||||
if (tr->timer_type == TIMER_TYPE_B) {
|
||||
/* Writing TBCCRx triggers update TBCLx immediately. No
|
||||
* grouping. */
|
||||
if (no_double_buffer(tr, index)) {
|
||||
set_bcl(tr, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int timer_write(struct simio_device *dev,
|
||||
address_t addr, uint16_t data)
|
||||
{
|
||||
|
@ -401,20 +447,7 @@ static int timer_write(struct simio_device *dev,
|
|||
addr < tr->base_addr + (tr->size << 1) + 0x12) {
|
||||
int index = ((addr & 0xf) - 2) >> 1;
|
||||
|
||||
tr->ccrs[index] = data;
|
||||
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];
|
||||
}
|
||||
set_ccr(tr, index, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -545,6 +578,48 @@ static void tar_step(struct timer *tr)
|
|||
}
|
||||
}
|
||||
|
||||
static void update_bcls(struct timer *tr, int start, int n)
|
||||
{
|
||||
int index;
|
||||
const int end = start + n;
|
||||
|
||||
for (index = start; index < end; index++) {
|
||||
if (!tr->valid_ccrs[index])
|
||||
return;
|
||||
}
|
||||
|
||||
for (index = start; index < end; index++)
|
||||
set_bcl(tr, index);
|
||||
}
|
||||
|
||||
static void update_bcl_group(struct timer *tr, int index)
|
||||
{
|
||||
switch (tr->tactl & (TBCLGRP1 | TBCLGRP0)) {
|
||||
case 0: /* Individual */
|
||||
set_bcl(tr, index);
|
||||
return;
|
||||
case TBCLGRP0: /* 0, 1&2, 3&4, 5&6 */
|
||||
if (index == 0) {
|
||||
update_bcls(tr, index, 1);
|
||||
} else if (index == 1 || index == 3 || index == 5) {
|
||||
update_bcls(tr, index, 2);
|
||||
}
|
||||
return;
|
||||
case TBCLGRP1: /* 0, 1&2&3, 4&5&6 */
|
||||
if (index == 0) {
|
||||
update_bcls(tr, index, 1);
|
||||
} else if (index == 1 || index == 4) {
|
||||
update_bcls(tr, index, 3);
|
||||
}
|
||||
return;
|
||||
case TBCLGRP1 | TBCLGRP0: /* All at once */
|
||||
if (index == 1) {
|
||||
update_bcls(tr, 0, tr->size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void comparator_step(struct timer *tr, int index)
|
||||
{
|
||||
if (tr->timer_type == TIMER_TYPE_A) {
|
||||
|
@ -562,14 +637,14 @@ static void comparator_step(struct timer *tr, int index)
|
|||
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];
|
||||
update_bcl_group(tr, 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];
|
||||
update_bcl_group(tr, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1223,6 +1223,263 @@ static void test_timer_b_compare_latch_3_up()
|
|||
assert(check_noirq(dev));
|
||||
}
|
||||
|
||||
static void test_timer_b_grouping_1()
|
||||
{
|
||||
dev = create_timer("7");
|
||||
config_timer(dev, "type", "B");
|
||||
|
||||
/* Continuous mode, SMCLK, pair grouping */
|
||||
write_timer(dev, TxCTL, MC1 | TASSEL1 | TBCLGRP0);
|
||||
write_timer(dev, TxCCR(0), 2);
|
||||
write_timer(dev, TxCCR(1), 4);
|
||||
write_timer(dev, TxCCR(2), 5);
|
||||
write_timer(dev, TxCCR(3), 6);
|
||||
write_timer(dev, TxCCR(4), 7);
|
||||
write_timer(dev, TxCCR(5), 8);
|
||||
write_timer(dev, TxCCR(6), 9);
|
||||
/* Load TBCL when TBR reaches old TBCL value, compare interrupt */
|
||||
write_timer(dev, TxCCTL(0), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCR(0), 10);
|
||||
// Both CCR[1] and CCR[2] are set.
|
||||
write_timer(dev, TxCCTL(1), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCTL(2), CCIE);
|
||||
write_timer(dev, TxCCR(1), 12);
|
||||
write_timer(dev, TxCCR(2), 13);
|
||||
// CCR[3] is set but CCR[4] keep unset.
|
||||
write_timer(dev, TxCCTL(3), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCTL(4), CCIE);
|
||||
write_timer(dev, TxCCR(3), 14);
|
||||
// Both CCR[5] and CCR[6] are set, but CCTL[5] has CCLD=0.
|
||||
write_timer(dev, TxCCTL(5), CCIE);
|
||||
write_timer(dev, TxCCTL(6), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCR(5), 16);
|
||||
write_timer(dev, TxCCR(6), 17);
|
||||
|
||||
step_smclk(dev, 2);
|
||||
|
||||
// TBCL[0] becomes 10.
|
||||
assert(read_timer(dev, TxR) == 2);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq0(dev));
|
||||
ack_irq0(dev);
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[1] and TBCL[2] becomes 12 and 13.
|
||||
assert(read_timer(dev, TxR) == 3);
|
||||
step_smclk(dev, 2);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(1));
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[3] and TBCL[4] keep previous value.
|
||||
// because TBCCR[4] has no valid value set.
|
||||
assert(read_timer(dev, TxR) == 5);
|
||||
step_smclk(dev, 2);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(3));
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(4));
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[5] and TBCL[6] is already 16 and 17 because TBCTL[5]
|
||||
// has CLLD=0 set, thus no interrupts at TBR=8,9.
|
||||
assert(read_timer(dev, TxR) == 8);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[0] will match.
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq0(dev));
|
||||
assert(read_timer(dev, TxCCTL(0)) & CCIFG);
|
||||
ack_irq0(dev);
|
||||
assert(check_noirq(dev));
|
||||
assert_not(read_timer(dev, TxCCTL(0)) & CCIFG);
|
||||
|
||||
// TBCL[1]will match.
|
||||
step_smclk(dev, 2);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_timer(dev, TxCCTL(1)) & CCIFG);
|
||||
assert(read_iv(dev) == TxIV_TxIFG(1));
|
||||
assert(check_noirq(dev));
|
||||
assert_not(read_timer(dev, TxCCTL(1)) & CCIFG);
|
||||
// Because TBCL[2] is also loaded as group with TBCL[1].
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_timer(dev, TxCCTL(2)) & CCIFG);
|
||||
assert(read_iv(dev) == TxIV_TxIFG(2));
|
||||
assert(check_noirq(dev));
|
||||
assert_not(read_timer(dev, TxCCTL(2)) & CCIFG);
|
||||
|
||||
// TBCL[3] and TBCL[4] didn't updated since TBCCR[4] has
|
||||
// no value set, thus no compare interrupts at TBR=14,15.
|
||||
assert(read_timer(dev, TxR) == 14);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[5] and TBCL[6] have set to 16 and 17, thus two compare
|
||||
// interrupts at TBR=16,17
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_timer(dev, TxCCTL(5)) & CCIFG);
|
||||
assert(read_iv(dev) == TxIV_TxIFG(5));
|
||||
assert(check_noirq(dev));
|
||||
assert_not(read_timer(dev, TxCCTL(5)) & CCIFG);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_timer(dev, TxCCTL(6)) & CCIFG);
|
||||
assert(read_iv(dev) == TxIV_TxIFG(6));
|
||||
assert(check_noirq(dev));
|
||||
assert_not(read_timer(dev, TxCCTL(6)) & CCIFG);
|
||||
}
|
||||
|
||||
static void test_timer_b_grouping_2()
|
||||
{
|
||||
dev = create_timer("7");
|
||||
config_timer(dev, "type", "B");
|
||||
|
||||
/* Continuous mode, SMCLK, triplet grouping */
|
||||
write_timer(dev, TxCTL, MC1 | TASSEL1 | TBCLGRP1);
|
||||
write_timer(dev, TxCCR(0), 2);
|
||||
write_timer(dev, TxCCR(1), 4);
|
||||
write_timer(dev, TxCCR(2), 5);
|
||||
write_timer(dev, TxCCR(3), 6);
|
||||
write_timer(dev, TxCCR(4), 7);
|
||||
write_timer(dev, TxCCR(5), 8);
|
||||
write_timer(dev, TxCCR(6), 9);
|
||||
/* Load TBCL when TBR reaches old TBCL value, compare interrupt */
|
||||
write_timer(dev, TxCCTL(0), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCR(0), 10);
|
||||
// All CCR[1], CCR[2], and CCR[3] are set.
|
||||
write_timer(dev, TxCCTL(1), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCTL(2), CCIE);
|
||||
write_timer(dev, TxCCTL(3), CCIE);
|
||||
write_timer(dev, TxCCR(1), 12);
|
||||
write_timer(dev, TxCCR(2), 6);
|
||||
write_timer(dev, TxCCR(3), 5);
|
||||
// CCR[4] and CCR[6] are set but CCR[5] keep unset.
|
||||
write_timer(dev, TxCCTL(4), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCTL(5), CCIE);
|
||||
write_timer(dev, TxCCTL(6), CCIE);
|
||||
write_timer(dev, TxCCR(4), 14);
|
||||
write_timer(dev, TxCCR(6), 8);
|
||||
|
||||
step_smclk(dev, 2);
|
||||
|
||||
// TBCL[0] becomes 10.
|
||||
assert(read_timer(dev, TxR) == 2);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq0(dev));
|
||||
ack_irq0(dev);
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[1], TBCL[2], and TBCL[3] becomes 12, 6, and 5.
|
||||
assert(read_timer(dev, TxR) == 3);
|
||||
step_smclk(dev, 2);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(1));
|
||||
assert(check_noirq(dev));
|
||||
assert(read_timer(dev, TxR) == 5);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(3));
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(2));
|
||||
assert(check_noirq(dev));
|
||||
|
||||
|
||||
// TBCL[4], TBCL[5] and TBC[6] keep previous value.
|
||||
// because TBCCR[5] has no valid value set.
|
||||
assert(read_timer(dev, TxR) == 7);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(4));
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(5));
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(6));
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// TBCL[0] is updated to 10 and TBCL[1] has 12.
|
||||
assert(read_timer(dev, TxR) == 10);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq0(dev));
|
||||
ack_irq0(dev);
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 2);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(1));
|
||||
assert(check_noirq(dev));
|
||||
}
|
||||
|
||||
static void test_timer_b_grouping_3()
|
||||
{
|
||||
dev = create_timer("7");
|
||||
config_timer(dev, "type", "B");
|
||||
|
||||
/* Continuous mode, SMCLK, triplet grouping */
|
||||
write_timer(dev, TxCTL, MC1 | TASSEL1 | TBCLGRP1 | TBCLGRP0);
|
||||
write_timer(dev, TxCCR(0), 3);
|
||||
write_timer(dev, TxCCR(1), 2);
|
||||
write_timer(dev, TxCCR(2), 4);
|
||||
write_timer(dev, TxCCR(3), 5);
|
||||
write_timer(dev, TxCCR(4), 6);
|
||||
write_timer(dev, TxCCR(5), 7);
|
||||
write_timer(dev, TxCCR(6), 8);
|
||||
/* Load TBCL when TBR reaches old TBCL value, compare interrupt */
|
||||
write_timer(dev, TxCCTL(0), CCIE);
|
||||
write_timer(dev, TxCCTL(1), CLLD1 | CLLD0 | CCIE);
|
||||
write_timer(dev, TxCCTL(6), CCIE);
|
||||
write_timer(dev, TxCCR(0), 9);
|
||||
write_timer(dev, TxCCR(1), 10);
|
||||
write_timer(dev, TxCCR(2), 11);
|
||||
write_timer(dev, TxCCR(3), 12);
|
||||
write_timer(dev, TxCCR(4), 13);
|
||||
write_timer(dev, TxCCR(5), 14);
|
||||
write_timer(dev, TxCCR(6), 15);
|
||||
|
||||
step_smclk(dev, 2);
|
||||
|
||||
// TBCL[1] becomes 10.
|
||||
assert(read_timer(dev, TxR) == 2);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(1));
|
||||
assert(check_noirq(dev));
|
||||
|
||||
// The least valued TBCL is TBCL[9].
|
||||
step_smclk(dev, 6);
|
||||
assert(read_timer(dev, TxR) == 9);
|
||||
assert(check_noirq(dev));
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq0(dev));
|
||||
ack_irq0(dev);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(1));
|
||||
step_smclk(dev, 4);
|
||||
assert(check_noirq(dev));
|
||||
assert(read_timer(dev, TxCCTL(2)) & CCIFG);
|
||||
assert(read_timer(dev, TxCCTL(3)) & CCIFG);
|
||||
assert(read_timer(dev, TxCCTL(4)) & CCIFG);
|
||||
assert(read_timer(dev, TxCCTL(5)) & CCIFG);
|
||||
step_smclk(dev, 1);
|
||||
assert(check_irq1(dev));
|
||||
assert(read_iv(dev) == TxIV_TxIFG(6));
|
||||
assert(check_noirq(dev));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test runner.
|
||||
|
@ -1277,4 +1534,7 @@ int main(int argc, char **argv)
|
|||
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);
|
||||
RUN_TEST(test_timer_b_grouping_1);
|
||||
RUN_TEST(test_timer_b_grouping_2);
|
||||
RUN_TEST(test_timer_b_grouping_3);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue