[simio] support Timer_B compare latch grouping

This commit is contained in:
Tadashi G. Takaoka 2018-07-19 01:33:08 -07:00 committed by Tadashi G. Takaoka
parent c53f697b76
commit 8564f42b66
2 changed files with 351 additions and 16 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}