Merge pull request #69 from tgtakaoka/simio-timer-b
[simio] support Timer_B
This commit is contained in:
commit
f017df6781
|
@ -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>
|
||||||
|
|
||||||
|
@ -35,13 +36,18 @@
|
||||||
#define TACLR 0x0004 /* Timer A counter clear */
|
#define TACLR 0x0004 /* Timer A counter clear */
|
||||||
#define TAIE 0x0002 /* Timer A counter interrupt enable */
|
#define TAIE 0x0002 /* Timer A counter interrupt enable */
|
||||||
#define TAIFG 0x0001 /* Timer A counter interrupt flag */
|
#define TAIFG 0x0001 /* Timer A counter interrupt flag */
|
||||||
|
/* TBCTL bits */
|
||||||
|
#define TBCLGRP1 0x4000 /* Timer B Compare latch load group 1 */
|
||||||
|
#define TBCLGRP0 0x2000 /* Timer B Compare latch load group 0 */
|
||||||
|
#define CNTL1 0x1000 /* Timer B Counter length 1 */
|
||||||
|
#define CNTL0 0x0800 /* Timer B Counter length 0 */
|
||||||
|
|
||||||
/* TACCTLx flags (taken from mspgcc) */
|
/* TACCTLx flags (taken from mspgcc) */
|
||||||
#define CM1 0x8000 /* Capture mode 1 */
|
#define CM1 0x8000 /* Capture mode 1 */
|
||||||
#define CM0 0x4000 /* Capture mode 0 */
|
#define CM0 0x4000 /* Capture mode 0 */
|
||||||
#define CCIS1 0x2000 /* Capture input select 1 */
|
#define CCIS1 0x2000 /* Capture input select 1 */
|
||||||
#define CCIS0 0x1000 /* Capture input select 0 */
|
#define CCIS0 0x1000 /* Capture input select 0 */
|
||||||
#define SCS 0x0800 /* Capture sychronize */
|
#define SCS 0x0800 /* Capture synchronize */
|
||||||
#define SCCI 0x0400 /* Latched capture signal (read) */
|
#define SCCI 0x0400 /* Latched capture signal (read) */
|
||||||
#define CAP 0x0100 /* Capture mode: 1 /Compare mode : 0 */
|
#define CAP 0x0100 /* Capture mode: 1 /Compare mode : 0 */
|
||||||
#define OUTMOD2 0x0080 /* Output mode 2 */
|
#define OUTMOD2 0x0080 /* Output mode 2 */
|
||||||
|
@ -52,26 +58,42 @@
|
||||||
/* #define OUT 0x0004 PWM Output signal if output mode 0 */
|
/* #define OUT 0x0004 PWM Output signal if output mode 0 */
|
||||||
#define COV 0x0002 /* Capture/compare overflow flag */
|
#define COV 0x0002 /* Capture/compare overflow flag */
|
||||||
#define CCIFG 0x0001 /* Capture/compare interrupt flag */
|
#define CCIFG 0x0001 /* Capture/compare interrupt flag */
|
||||||
|
/* TBCCTLx flags */
|
||||||
|
#define CLLD1 0x0400 /* Compare latch load source 1 */
|
||||||
|
#define CLLD0 0x0200 /* Compare latch load source 0 */
|
||||||
|
/* Timer IV words */
|
||||||
|
#define TAIV_TAIFG 0x000A /* Interrupt vector word for TAIFG */
|
||||||
|
#define TBIV_TBIFG 0x000E /* Interrupt vector word for TBIFG */
|
||||||
|
|
||||||
#define MAX_CCRS 7
|
#define MAX_CCRS 7
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TIMER_TYPE_A,
|
||||||
|
TIMER_TYPE_B,
|
||||||
|
} timer_type_t;
|
||||||
|
|
||||||
struct timer {
|
struct timer {
|
||||||
struct simio_device base;
|
struct simio_device base;
|
||||||
|
|
||||||
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;
|
||||||
int irq0;
|
int irq0;
|
||||||
int irq1;
|
int irq1;
|
||||||
|
timer_type_t timer_type;
|
||||||
|
|
||||||
/* IO registers */
|
/* IO registers */
|
||||||
uint16_t tactl;
|
uint16_t tactl;
|
||||||
uint16_t tar;
|
uint16_t tar;
|
||||||
uint16_t ctls[MAX_CCRS];
|
uint16_t ctls[MAX_CCRS];
|
||||||
uint16_t ccrs[MAX_CCRS];
|
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)
|
static struct simio_device *timer_create(char **arg_text)
|
||||||
|
@ -109,6 +131,7 @@ static struct simio_device *timer_create(char **arg_text)
|
||||||
tr->iv_addr = 0x12e;
|
tr->iv_addr = 0x12e;
|
||||||
tr->irq0 = 9;
|
tr->irq0 = 9;
|
||||||
tr->irq1 = 8;
|
tr->irq1 = 8;
|
||||||
|
tr->timer_type = TIMER_TYPE_A;
|
||||||
|
|
||||||
return (struct simio_device *)tr;
|
return (struct simio_device *)tr;
|
||||||
}
|
}
|
||||||
|
@ -126,9 +149,11 @@ 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));
|
||||||
|
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)
|
static int config_addr(address_t *addr, char **arg_text)
|
||||||
|
@ -148,6 +173,28 @@ static int config_addr(address_t *addr, char **arg_text)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int config_type(timer_type_t *timer_type, char **arg_text)
|
||||||
|
{
|
||||||
|
char *text = get_arg(arg_text);
|
||||||
|
|
||||||
|
if (!text) {
|
||||||
|
printc_err("timer: config: expected type\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcasecmp(text, "A")) {
|
||||||
|
*timer_type = TIMER_TYPE_A;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(text, "B")) {
|
||||||
|
*timer_type = TIMER_TYPE_B;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printc_err("timer: can't parse type: %s\n", text);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int config_irq(int *irq, char **arg_text)
|
static int config_irq(int *irq, char **arg_text)
|
||||||
{
|
{
|
||||||
char *text = get_arg(arg_text);
|
char *text = get_arg(arg_text);
|
||||||
|
@ -167,14 +214,40 @@ static int config_irq(int *irq, char **arg_text)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void trigger_capture(struct timer *tr, int which, int oldval, int value)
|
||||||
|
{
|
||||||
|
uint16_t edge_flags = 0;
|
||||||
|
|
||||||
|
tr->ctls[which] &= ~CCI;
|
||||||
|
if (value)
|
||||||
|
tr->ctls[which] |= CCI;
|
||||||
|
|
||||||
|
if (oldval && !value)
|
||||||
|
edge_flags |= CM1;
|
||||||
|
if (!oldval && value)
|
||||||
|
edge_flags |= CM0;
|
||||||
|
|
||||||
|
printc_dbg("Timer channel %d: %s => %s\n",
|
||||||
|
which, oldval ? "H" : "L", value ? "H" : "L");
|
||||||
|
|
||||||
|
if ((tr->ctls[which] & edge_flags) && (tr->ctls[which] & CAP)) {
|
||||||
|
if (tr->ctls[which] & CCIFG) {
|
||||||
|
printc_dbg("Timer capture overflow\n");
|
||||||
|
tr->ctls[which] |= COV;
|
||||||
|
} else {
|
||||||
|
printc_dbg("Timer capture interrupt triggered\n");
|
||||||
|
tr->ccrs[which] = tr->tar;
|
||||||
|
tr->ctls[which] |= CCIFG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int config_channel(struct timer *tr, char **arg_text)
|
static int config_channel(struct timer *tr, char **arg_text)
|
||||||
{
|
{
|
||||||
char *which_text = get_arg(arg_text);
|
char *which_text = get_arg(arg_text);
|
||||||
char *value_text = get_arg(arg_text);
|
char *value_text = get_arg(arg_text);
|
||||||
address_t which;
|
address_t which;
|
||||||
address_t value;
|
address_t value;
|
||||||
int oldval;
|
|
||||||
uint16_t edge_flags = 0;
|
|
||||||
|
|
||||||
if (!(which_text && value_text)) {
|
if (!(which_text && value_text)) {
|
||||||
printc_err("timer: config: expected channel and value\n");
|
printc_err("timer: config: expected channel and value\n");
|
||||||
|
@ -198,29 +271,7 @@ static int config_channel(struct timer *tr, char **arg_text)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
oldval = tr->ctls[which] & CCI;
|
trigger_capture(tr, which, tr->ctls[which] & CCI, value);
|
||||||
tr->ctls[which] &= ~CCI;
|
|
||||||
if (value)
|
|
||||||
tr->ctls[which] |= CCI;
|
|
||||||
|
|
||||||
if (oldval && !value)
|
|
||||||
edge_flags |= CM1;
|
|
||||||
if (!oldval && value)
|
|
||||||
edge_flags |= CM0;
|
|
||||||
|
|
||||||
printc_dbg("Timer channel %d: %s => %s\n",
|
|
||||||
which, oldval ? "H" : "L", value ? "H" : "L");
|
|
||||||
|
|
||||||
if ((tr->ctls[which] & edge_flags) && (tr->ctls[which] & CAP)) {
|
|
||||||
if (tr->ctls[which] & CCIFG) {
|
|
||||||
printc_dbg("Timer capture overflow\n");
|
|
||||||
tr->ctls[which] |= COV;
|
|
||||||
} else {
|
|
||||||
printc_dbg("Timer capture interrupt triggered\n");
|
|
||||||
tr->ccrs[which] = tr->tar;
|
|
||||||
tr->ctls[which] |= CCIFG;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -232,6 +283,8 @@ static int timer_config(struct simio_device *dev,
|
||||||
|
|
||||||
if (!strcasecmp(param, "base"))
|
if (!strcasecmp(param, "base"))
|
||||||
return config_addr(&tr->base_addr, arg_text);
|
return config_addr(&tr->base_addr, arg_text);
|
||||||
|
if (!strcasecmp(param, "type"))
|
||||||
|
return config_type(&tr->timer_type, arg_text);
|
||||||
if (!strcasecmp(param, "iv"))
|
if (!strcasecmp(param, "iv"))
|
||||||
return config_addr(&tr->iv_addr, arg_text);
|
return config_addr(&tr->iv_addr, arg_text);
|
||||||
if (!strcasecmp(param, "irq0"))
|
if (!strcasecmp(param, "irq0"))
|
||||||
|
@ -261,7 +314,8 @@ static uint16_t calc_iv(struct timer *tr, int update)
|
||||||
if ((tr->tactl & (TAIFG | TAIE)) == (TAIFG | TAIE)) {
|
if ((tr->tactl & (TAIFG | TAIE)) == (TAIFG | TAIE)) {
|
||||||
if (update)
|
if (update)
|
||||||
tr->tactl &= ~TAIFG;
|
tr->tactl &= ~TAIFG;
|
||||||
return 0xa;
|
return (tr->timer_type == TIMER_TYPE_A) ?
|
||||||
|
TAIV_TAIFG : TBIV_TBIFG;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -271,24 +325,89 @@ static int timer_info(struct simio_device *dev)
|
||||||
{
|
{
|
||||||
struct timer *tr = (struct timer *)dev;
|
struct timer *tr = (struct timer *)dev;
|
||||||
int i;
|
int i;
|
||||||
|
char timer_type = (tr->timer_type == TIMER_TYPE_A) ? 'A' : 'B';
|
||||||
|
|
||||||
printc("Base address: 0x%04x\n", tr->base_addr);
|
printc("Base address: 0x%04x\n", tr->base_addr);
|
||||||
printc("IV address: 0x%04x\n", tr->iv_addr);
|
printc("IV address: 0x%04x\n", tr->iv_addr);
|
||||||
printc("IRQ0: %d\n", tr->irq0);
|
printc("IRQ0: %d\n", tr->irq0);
|
||||||
printc("IRQ1: %d\n", tr->irq1);
|
printc("IRQ1: %d\n", tr->irq1);
|
||||||
printc("\n");
|
printc("\n");
|
||||||
printc("TACTL: 0x%04x\n", tr->tactl);
|
printc("T%cCTL: 0x%04x\n", timer_type, tr->tactl);
|
||||||
printc("TAR: 0x%04x\n", tr->tar);
|
printc("T%cR: 0x%04x\n", timer_type, tr->tar);
|
||||||
printc("TAIV: 0x%02x\n", calc_iv(tr, 0));
|
printc("T%cIV: 0x%02x\n", timer_type, calc_iv(tr, 0));
|
||||||
printc("\n");
|
printc("\n");
|
||||||
|
|
||||||
for (i = 0; i < tr->size; i++)
|
for (i = 0; i < tr->size; i++) {
|
||||||
printc("Channel %2d, TACCTL = 0x%04x, TACCR = 0x%04x\n",
|
printc("T%cCCTL%d = 0x%04x, T%cCCR%d = 0x%04x",
|
||||||
i, tr->ctls[i], tr->ccrs[i]);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint16_t tar_mask(struct timer *tr)
|
||||||
|
{
|
||||||
|
if (tr->timer_type == TIMER_TYPE_B) {
|
||||||
|
switch (tr->tactl & (CNTL1 | CNTL0)) {
|
||||||
|
case 0: /* 16 bits */
|
||||||
|
break;
|
||||||
|
case CNTL0: /* 12 bits */
|
||||||
|
return 0x0fff;
|
||||||
|
case CNTL1: /* 10 bits */
|
||||||
|
return 0x03ff;
|
||||||
|
case CNTL1 | CNTL0: /* 8 bits */
|
||||||
|
return 0x00ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,
|
static int timer_write(struct simio_device *dev,
|
||||||
address_t addr, uint16_t data)
|
address_t addr, uint16_t data)
|
||||||
{
|
{
|
||||||
|
@ -303,16 +422,24 @@ static int timer_write(struct simio_device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr == tr->base_addr + 0x10) {
|
if (addr == tr->base_addr + 0x10) {
|
||||||
tr->tar = data;
|
tr->tar = data & tar_mask(tr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr >= tr->base_addr + 2 &&
|
if (addr >= tr->base_addr + 2 &&
|
||||||
addr < tr->base_addr + (tr->size << 1) + 2) {
|
addr < tr->base_addr + (tr->size << 1) + 2) {
|
||||||
int index = ((addr & 0xf) - 2) >> 1;
|
int index = ((addr & 0xf) - 2) >> 1;
|
||||||
|
uint16_t oldval = tr->ctls[index];
|
||||||
|
uint16_t mask;
|
||||||
|
|
||||||
tr->ctls[index] = (data & 0xf9f7) |
|
if (tr->timer_type == TIMER_TYPE_A)
|
||||||
(tr->ctls[index] & 0x0608);
|
mask = 0x0608;
|
||||||
|
if (tr->timer_type == TIMER_TYPE_B)
|
||||||
|
mask = 0x0008;
|
||||||
|
tr->ctls[index] = (data & ~mask) | (oldval & mask);
|
||||||
|
/* Check capture initiated by Software */
|
||||||
|
if ((data & (CAP | CCIS1)) == (CAP | CCIS1))
|
||||||
|
trigger_capture(tr, index, oldval & CCI, data & CCIS0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +447,7 @@ static int timer_write(struct simio_device *dev,
|
||||||
addr < tr->base_addr + (tr->size << 1) + 0x12) {
|
addr < tr->base_addr + (tr->size << 1) + 0x12) {
|
||||||
int index = ((addr & 0xf) - 2) >> 1;
|
int index = ((addr & 0xf) - 2) >> 1;
|
||||||
|
|
||||||
tr->ccrs[index] = data;
|
set_ccr(tr, index, data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,42 +522,134 @@ static void timer_ack_interrupt(struct simio_device *dev, int irq)
|
||||||
/* By design irq1 does not clear CCIFG or TAIFG automatically */
|
/* 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++;
|
||||||
|
tr->tar &= tar_mask(tr);
|
||||||
|
return tr->tar;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t tar_decrement(struct timer *tr)
|
||||||
|
{
|
||||||
|
tr->tar--;
|
||||||
|
tr->tar &= tar_mask(tr);
|
||||||
|
return tr->tar;
|
||||||
|
}
|
||||||
|
|
||||||
static void tar_step(struct timer *tr)
|
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 == get_ccr(tr, 0) || tr->go_down) {
|
||||||
tr->tar = 0;
|
tr->tar = 0;
|
||||||
tr->tactl |= TAIFG;
|
tr->tactl |= TAIFG;
|
||||||
|
tr->go_down = false;
|
||||||
} else {
|
} else {
|
||||||
tr->tar++;
|
tar_increment(tr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
tr->tar++;
|
if (tar_increment(tr) == 0)
|
||||||
if (!tr->tar)
|
|
||||||
tr->tactl |= TAIFG;
|
tr->tactl |= TAIFG;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
if (tr->tar >= tr->ccrs[0])
|
if (tr->tar >= get_ccr(tr, 0))
|
||||||
tr->go_down = 1;
|
tr->go_down = true;
|
||||||
if (!tr->tar)
|
if (tr->tar == 0)
|
||||||
tr->go_down = 0;
|
tr->go_down = false;
|
||||||
|
|
||||||
if (tr->go_down) {
|
if (tr->go_down) {
|
||||||
tr->tar--;
|
if (tar_decrement(tr) == 0)
|
||||||
if (!tr->tar)
|
|
||||||
tr->tactl |= TAIFG;
|
tr->tactl |= TAIFG;
|
||||||
} else {
|
} else {
|
||||||
tr->tar++;
|
tar_increment(tr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (tr->tar == get_ccr(tr, index)) {
|
||||||
|
tr->ctls[index] |= CCIFG;
|
||||||
|
if (tr->ctls[index] & CCI)
|
||||||
|
tr->ctls[index] |= SCCI;
|
||||||
|
else
|
||||||
|
tr->ctls[index] &= ~SCCI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tr->timer_type == TIMER_TYPE_B) {
|
||||||
|
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)) {
|
||||||
|
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)) {
|
||||||
|
update_bcl_group(tr, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void timer_step(struct simio_device *dev,
|
static void timer_step(struct simio_device *dev,
|
||||||
uint16_t status, const int *clocks)
|
uint16_t status, const int *clocks)
|
||||||
{
|
{
|
||||||
|
@ -456,16 +675,10 @@ static void timer_step(struct simio_device *dev,
|
||||||
for (i = 0; i < pulse_count; i++) {
|
for (i = 0; i < pulse_count; i++) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
for (j = 0; j < tr->size; j++)
|
for (j = 0; j < tr->size; j++) {
|
||||||
if (!(tr->ctls[j] & CAP) && (tr->tar == tr->ccrs[j])) {
|
if (!(tr->ctls[j] & CAP))
|
||||||
if (tr->ctls[j] & CCI)
|
comparator_step(tr, j);
|
||||||
tr->ctls[j] |= SCCI;
|
|
||||||
else
|
|
||||||
tr->ctls[j] &= ~SCCI;
|
|
||||||
|
|
||||||
tr->ctls[j] |= CCIFG;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tar_step(tr);
|
tar_step(tr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,22 +686,24 @@ static void timer_step(struct simio_device *dev,
|
||||||
const struct simio_class simio_timer = {
|
const struct simio_class simio_timer = {
|
||||||
.name = "timer",
|
.name = "timer",
|
||||||
.help =
|
.help =
|
||||||
"This peripheral implements the Timer_A module.\n"
|
"This peripheral implements the Timer_A and Timer_B module.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Constructor arguments: [size]\n"
|
"Constructor arguments: [size]\n"
|
||||||
" Specify the number of capture/compare registers.\n"
|
" Specify the number of capture/compare registers.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Config arguments are:\n"
|
"Config arguments are:\n"
|
||||||
" base <address>\n"
|
" base <address>\n"
|
||||||
" Set the peripheral base address.\n"
|
" Set the peripheral base address.\n"
|
||||||
" irq0 <interrupt>\n"
|
" type <A|B>\n"
|
||||||
" Set the interrupt vector for CCR0.\n"
|
" Set timer type.\n"
|
||||||
" irq1 <interrupt>\n"
|
" irq0 <interrupt>\n"
|
||||||
" Set the interrupt vector for CCR1.\n"
|
" Set the interrupt vector for CCR0.\n"
|
||||||
" iv <address>\n"
|
" irq1 <interrupt>\n"
|
||||||
" Set the interrupt vector register address.\n"
|
" Set the interrupt vector for CCR1.\n"
|
||||||
" set <channel> <0|1>\n"
|
" iv <address>\n"
|
||||||
" Set the capture input value on the given channel.\n",
|
" Set the interrupt vector register address.\n"
|
||||||
|
" set <channel> <0|1>\n"
|
||||||
|
" Set the capture input value on the given channel.\n",
|
||||||
|
|
||||||
.create = timer_create,
|
.create = timer_create,
|
||||||
.destroy = timer_destroy,
|
.destroy = timer_destroy,
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
TESTS = test_timer
|
||||||
|
|
||||||
|
UTIL_OBJS=btree.o chipinfo.o ctrlc.o dis.o expr.o list.o opdb.o output.o stab.o util.o vector.o
|
||||||
|
DRIVERS_OBJS=device.o
|
||||||
|
|
||||||
|
CFLAGS=-ggdb -I../../simio -I../../drivers -I../../util
|
||||||
|
LIBS=-lpthread
|
||||||
|
|
||||||
|
OBJS+=$(foreach obj, $(UTIL_OBJS), ../../util/$(obj))
|
||||||
|
OBJS+=$(foreach obj, $(DRIVERS_OBJS), ../../drivers/$(obj))
|
||||||
|
|
||||||
|
test: $(TESTS)
|
||||||
|
@for test in $(TESTS); do echo "==== $${test} ===="; ./$${test}; done
|
||||||
|
|
||||||
|
test_timer.o : ../simio_timer.c
|
||||||
|
|
||||||
|
define add-obj-rule
|
||||||
|
$(1): $(1:.o=.c)
|
||||||
|
$$(CC) $$(CFLAGS) -c $$< -o $$@
|
||||||
|
endef
|
||||||
|
$(foreach obj, $(OBJS), $(eval $(call add-obj-rule, $(obj))))
|
||||||
|
|
||||||
|
define add-test-rule
|
||||||
|
$(1): $(1).o $(OBJS)
|
||||||
|
$$(CC) -o $$@ $$< $(OBJS) $(LIBS)
|
||||||
|
endef
|
||||||
|
$(foreach test, $(TESTS), $(eval $(call add-test-rule, $(test))))
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -f $(TESTS:=.o) $(TESTS)
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue