Rework us_ticker and rtc_api/lp_ticker

Simplify tickers to stop emulating 1us ticks by using native timer resolutions.
Implement ticker get info functions.
Separate rtc and lp ticker init status by implementing separate functions.
pull/6605/head
Keyur Hariya 2018-04-12 12:52:05 -05:00
parent 278707518c
commit 098c2cf756
2 changed files with 136 additions and 356 deletions

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
* Copyright (C) 2016,2018 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -36,33 +36,24 @@
#include "rtc.h"
#include "lp.h"
#define PRESCALE_VAL RTC_PRESCALE_DIV_2_0 // Set the divider for the 4kHz clock
#define SHIFT_AMT (RTC_PRESCALE_DIV_2_12 - PRESCALE_VAL)
// LOG2 for 32-bit powers of 2
#define LOG2_1(n) (((n) >= (1 << 1)) ? 1 : 0)
#define LOG2_2(n) (((n) >= (1 << 2)) ? ( 2 + (LOG2_1((n) >> 2))) : LOG2_1(n))
#define LOG2_4(n) (((n) >= (1 << 4)) ? ( 4 + (LOG2_2((n) >> 4))) : LOG2_2(n))
#define LOG2_8(n) (((n) >= (1 << 8)) ? ( 8 + (LOG2_4((n) >> 8))) : LOG2_4(n))
#define LOG2(n) (((n) >= (1 << 16)) ? (16 + (LOG2_8((n) >> 16))) : LOG2_8(n))
#define WINDOW 1000
#define LP_TIMER_FREQ_HZ 4096
#define LP_TIMER_PRESCALE RTC_PRESCALE_DIV_2_0
#define LP_TIMER_RATE_HZ (LP_TIMER_FREQ_HZ >> LP_TIMER_PRESCALE)
#define LP_TIMER_WIDTH 32
static int rtc_inited = 0;
static volatile uint32_t overflow_cnt = 0;
static uint64_t rtc_read64(void);
static volatile int rtc_inited = 0;
static volatile int lp_ticker_inited = 0;
//******************************************************************************
static void overflow_handler(void)
static void init_rtc(void)
{
overflow_cnt++;
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
void rtc_init(void)
{
if (rtc_inited) {
return;
}
rtc_inited = 1;
overflow_cnt = 0;
/* Enable power for RTC for all LPx states */
MXC_PWRSEQ->reg0 |= (MXC_F_PWRSEQ_REG0_PWR_RTCEN_RUN |
MXC_F_PWRSEQ_REG0_PWR_RTCEN_SLP);
@ -70,21 +61,12 @@ void rtc_init(void)
/* Enable clock to synchronizers */
CLKMAN_SetClkScale(CLKMAN_CLK_SYNC, CLKMAN_SCALE_DIV_1);
// Prepare interrupt handlers
NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
NVIC_EnableIRQ(RTC0_IRQn);
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn);
// Enable wakeup on RTC rollover
LP_ConfigRTCWakeUp(0, 0, 0, 1);
/* RTC registers are only reset on a power cycle. Do not reconfigure the RTC
* if it is already running.
*/
if (!RTC_IsActive()) {
rtc_cfg_t cfg = {0};
cfg.prescaler = PRESCALE_VAL;
rtc_cfg_t cfg = { 0 };
cfg.prescaler = LP_TIMER_PRESCALE;
cfg.snoozeMode = RTC_SNOOZE_DISABLE;
int retval = RTC_Init(&cfg);
@ -96,163 +78,128 @@ void rtc_init(void)
}
//******************************************************************************
void lp_ticker_init(void)
static void overflow_handler(void)
{
rtc_init();
MXC_RTCTMR->comp[1] += ((UINT32_MAX >> LOG2(LP_TIMER_RATE_HZ)) + 1);
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
void rtc_init(void)
{
if (rtc_inited) {
return;
}
NVIC_SetVector(RTC3_IRQn, (uint32_t)overflow_handler);
NVIC_EnableIRQ(RTC3_IRQn);
// Enable wakeup on RTC overflow
LP_ConfigRTCWakeUp(lp_ticker_inited, 0, 0, 1);
init_rtc();
rtc_inited = 1;
}
//******************************************************************************
void rtc_free(void)
{
if (RTC_IsActive()) {
// Clear and disable RTC
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_CLEAR;
RTC_Stop();
if (rtc_inited) {
rtc_inited = 0;
if (lp_ticker_inited) {
RTC_DisableINT(MXC_F_RTC_FLAGS_OVERFLOW);
} else {
MXC_RTCTMR->ctrl |= MXC_F_RTC_CTRL_CLEAR;
RTC_Stop();
}
}
}
//******************************************************************************
int rtc_isenabled(void)
{
return RTC_IsActive();
}
//******************************************************************************
time_t rtc_read(void)
{
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint32_t ovf1, ovf2;
// Make sure RTC is setup before trying to read
if (!rtc_inited) {
rtc_init();
}
// Ensure coherency between overflow_cnt and timer
do {
ovf_cnt_1 = overflow_cnt;
ovf1 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = RTC_GetCount();
ovf2 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt;
} while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
// Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
return (timer_cnt >> SHIFT_AMT) + (ovf_cnt_1 << (32 - SHIFT_AMT));
}
//******************************************************************************
static uint64_t rtc_read64(void)
{
uint32_t ovf_cnt_1, ovf_cnt_2, timer_cnt;
uint32_t ovf1, ovf2;
uint64_t current_us;
// Make sure RTC is setup before trying to read
if (!rtc_inited) {
rtc_init();
}
// Ensure coherency between overflow_cnt and timer
do {
ovf_cnt_1 = overflow_cnt;
ovf1 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
timer_cnt = RTC_GetCount();
ovf2 = RTC_GetFlags() & MXC_F_RTC_FLAGS_OVERFLOW;
ovf_cnt_2 = overflow_cnt;
} while ((ovf_cnt_1 != ovf_cnt_2) || (ovf1 != ovf2));
// Account for an unserviced interrupt
if (ovf1) {
ovf_cnt_1++;
}
current_us = (((uint64_t)timer_cnt * 1000000) >> SHIFT_AMT) + (((uint64_t)ovf_cnt_1 * 1000000) << (32 - SHIFT_AMT));
return current_us;
return rtc_inited;
}
//******************************************************************************
void rtc_write(time_t t)
{
// Make sure RTC is setup before accessing
if (!rtc_inited) {
rtc_init();
}
RTC_Stop();
RTC_SetCount(t << SHIFT_AMT);
overflow_cnt = t >> (32 - SHIFT_AMT);
RTC_Start();
}
//******************************************************************************
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
uint32_t comp_value;
uint64_t curr_ts64;
uint64_t ts64;
// Note: interrupts are disabled before this function is called.
// Disable the alarm while it is prepared
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
curr_ts64 = rtc_read64();
ts64 = (uint64_t)timestamp | (curr_ts64 & 0xFFFFFFFF00000000ULL);
// If this event is older than a recent window, it must be in the future
if ((ts64 < (curr_ts64 - WINDOW)) && ((curr_ts64 - WINDOW) < curr_ts64)) {
ts64 += 0x100000000ULL;
}
uint32_t timer = RTC_GetCount();
if (ts64 <= curr_ts64) {
// This event has already occurred. Set the alarm to expire immediately.
comp_value = timer + 1;
} else {
comp_value = (ts64 << SHIFT_AMT) / 1000000;
}
// Ensure that the compare value is far enough in the future to guarantee the interrupt occurs.
if ((comp_value < (timer + 2)) && (comp_value > (timer - 10))) {
comp_value = timer + 2;
}
MXC_RTCTMR->comp[0] = comp_value;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
RTC_EnableINT(MXC_F_RTC_INTEN_COMP0);
// Enable wakeup from RTC
LP_ConfigRTCWakeUp(1, 0, 0, 1);
MXC_RTCTMR->comp[1] = t - (MXC_RTCTMR->timer >> LOG2(LP_TIMER_RATE_HZ));
// Wait for pending transactions
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
}
//******************************************************************************
time_t rtc_read(void)
{
if (!rtc_inited) {
rtc_init();
}
return (MXC_RTCTMR->timer >> LOG2(LP_TIMER_RATE_HZ)) + MXC_RTCTMR->comp[1];
}
//******************************************************************************
void lp_ticker_init(void)
{
if (lp_ticker_inited) {
return;
}
NVIC_SetVector(RTC0_IRQn, (uint32_t)lp_ticker_irq_handler);
NVIC_EnableIRQ(RTC0_IRQn);
init_rtc();
lp_ticker_inited = 1;
}
//******************************************************************************
uint32_t lp_ticker_read(void)
{
return MXC_RTCTMR->timer;
}
//******************************************************************************
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
MXC_RTCTMR->comp[0] = timestamp;
MXC_RTCTMR->flags = MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS;
MXC_RTCTMR->inten |= MXC_F_RTC_INTEN_COMP0;
// Enable wakeup from RTC compare 0
LP_ConfigRTCWakeUp(1, 0, 0, rtc_inited);
// Wait for pending transactions
while (MXC_RTCTMR->ctrl & MXC_F_RTC_CTRL_PENDING);
}
//******************************************************************************
void lp_ticker_disable_interrupt(void)
{
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
}
//******************************************************************************
void lp_ticker_clear_interrupt(void)
{
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
void lp_ticker_fire_interrupt(void)
{
NVIC_SetPendingIRQ(RTC0_IRQn);
}
//******************************************************************************
inline void lp_ticker_disable_interrupt(void)
const ticker_info_t *lp_ticker_get_info(void)
{
RTC_DisableINT(MXC_F_RTC_INTEN_COMP0);
}
static const ticker_info_t info = {
LP_TIMER_RATE_HZ,
LP_TIMER_WIDTH
};
//******************************************************************************
inline void lp_ticker_clear_interrupt(void)
{
RTC_ClearFlags(MXC_F_RTC_FLAGS_ASYNC_CLR_FLAGS);
}
//******************************************************************************
inline uint32_t lp_ticker_read(void)
{
return rtc_read64();
return &info;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
* Copyright (c) 2016,2018 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -32,251 +32,84 @@
*/
#include <stddef.h>
#include "mbed_error.h"
#include "mbed_critical.h"
#include "us_ticker_api.h"
#include "PeripheralNames.h"
#include "tmr.h"
#include "assert.h"
#define US_TIMER MXC_TMR0
#define US_TIMER_IRQn TMR0_0_IRQn
#define US_TIMER MXC_TMR0
#define US_TIMER_IRQn TMR0_0_IRQn
#define US_TIMER_PRESCALE TMR_PRESCALE_DIV_2_5
#define US_TIMER_MODE TMR32_MODE_COMPARE
#define US_TIMER_WIDTH 32
static int us_ticker_inited = 0;
static uint32_t ticks_per_us;
static uint32_t tick_win;
static volatile uint64_t current_cnt; // Hold the current ticks
static volatile uint64_t event_cnt; // Holds the value of the next event
#define MAX_TICK_VAL ((uint64_t)0xFFFFFFFF * ticks_per_us)
//******************************************************************************
static inline void inc_current_cnt_no_crit(uint32_t inc)
{
// Overflow the ticker when the us ticker overflows
current_cnt += inc;
if (current_cnt > MAX_TICK_VAL) {
current_cnt -= (MAX_TICK_VAL + 1);
}
}
//******************************************************************************
static inline void inc_current_cnt(uint32_t inc)
{
core_util_critical_section_enter();
inc_current_cnt_no_crit(inc);
core_util_critical_section_exit();
}
//******************************************************************************
static inline int event_passed(uint64_t current, uint64_t event)
{
// Determine if the event has already happened.
// If the event is behind the current ticker, within a window,
// then the event has already happened.
if (((current < tick_win) && ((event < current) ||
(event > (MAX_TICK_VAL - (tick_win - current))))) ||
((event < current) && (event > (current - tick_win)))) {
return 1;
}
return 0;
}
//******************************************************************************
static inline uint64_t event_diff(uint64_t current, uint64_t event)
{
// Check to see if the ticker will overflow before the event
if(current <= event) {
return (event - current);
}
return ((MAX_TICK_VAL - current) + event);
}
//******************************************************************************
static void tmr_handler(void)
{
uint32_t cmp = TMR32_GetCompare(US_TIMER);
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF); // reset to max value to prevent further interrupts
if (TMR32_GetFlag(US_TIMER)) {
inc_current_cnt_no_crit(cmp);
}
TMR32_ClearFlag(US_TIMER);
NVIC_ClearPendingIRQ(US_TIMER_IRQn);
if (event_passed(current_cnt + TMR32_GetCount(US_TIMER), event_cnt)) {
// the timestamp has expired
event_cnt = 0xFFFFFFFFFFFFFFFFULL; // reset to max value
us_ticker_irq_handler();
} else {
uint64_t diff = event_diff(current_cnt, event_cnt);
if (diff < (uint64_t)0xFFFFFFFF) {
// the event occurs before the next overflow
TMR32_SetCompare(US_TIMER, diff);
// Since the timer keeps counting after the terminal value is reached, it is possible that the new
// terminal value is in the past.
if (TMR32_GetCompare(US_TIMER) < TMR32_GetCount(US_TIMER)) {
// the timestamp has expired
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF); // reset to max value to prevent further interrupts
TMR32_ClearFlag(US_TIMER);
NVIC_ClearPendingIRQ(US_TIMER_IRQn);
event_cnt = 0xFFFFFFFFFFFFFFFFULL; // reset to max value
us_ticker_irq_handler();
}
}
}
}
static volatile int us_ticker_inited = 0;
//******************************************************************************
void us_ticker_init(void)
{
tmr32_cfg_t cfg;
if (us_ticker_inited) {
return;
}
us_ticker_inited = 1;
current_cnt = 0;
event_cnt = 0xFFFFFFFFFFFFFFFFULL; // reset to max value
ticks_per_us = SystemCoreClock / 1000000;
tick_win = SystemCoreClock / 100; // Set the tick window to 10ms
int retval = TMR_Init(US_TIMER, TMR_PRESCALE_DIV_2_0, NULL);
MBED_ASSERT(retval == E_NO_ERROR);
tmr32_cfg_t cfg;
cfg.mode = TMR32_MODE_CONTINUOUS;
cfg.mode = US_TIMER_MODE;
cfg.polarity = TMR_POLARITY_UNUSED;
cfg.compareCount = 0xFFFFFFFF;
cfg.compareCount = UINT32_MAX;
TMR_Init(US_TIMER, US_TIMER_PRESCALE, NULL);
TMR32_Config(US_TIMER, &cfg);
NVIC_SetVector(US_TIMER_IRQn, (uint32_t)tmr_handler);
NVIC_SetVector(US_TIMER_IRQn, (uint32_t)us_ticker_irq_handler);
NVIC_EnableIRQ(US_TIMER_IRQn);
TMR32_EnableINT(US_TIMER);
TMR32_Start(US_TIMER);
}
//******************************************************************************
void us_ticker_deinit(void)
{
TMR32_Stop(US_TIMER);
TMR32_DisableINT(US_TIMER);
TMR32_ClearFlag(US_TIMER);
us_ticker_inited = 0;
}
//******************************************************************************
uint32_t us_ticker_read(void)
{
uint64_t current_cnt1, current_cnt2;
uint32_t cmp, cnt;
uint32_t flag1, flag2;
static uint32_t last = 0;
if (!us_ticker_inited) {
us_ticker_init();
}
// Ensure coherency between current_cnt and TMR32_GetCount()
do {
current_cnt1 = current_cnt;
flag1 = TMR32_GetFlag(US_TIMER);
cmp = TMR32_GetCompare(US_TIMER);
cnt = TMR32_GetCount(US_TIMER);
flag2 = TMR32_GetFlag(US_TIMER);
current_cnt2 = current_cnt;
} while ((current_cnt1 != current_cnt2) || (flag1 != flag2));
// Account for an unserviced interrupt
if (flag1) {
// Clear peripheral interrupt flag; leaving NVIC pending set
TMR32_ClearFlag(US_TIMER);
// Advance global count
inc_current_cnt(cmp + cnt);
current_cnt1 += cmp;
}
current_cnt1 += cnt;
assert(last <= (current_cnt1 / ticks_per_us));
last = (current_cnt1 / ticks_per_us);
return last;
return US_TIMER->count32;
}
//******************************************************************************
void us_ticker_set_interrupt(timestamp_t timestamp)
{
// Note: interrupts are disabled before this function is called.
TMR32_Stop(US_TIMER);
if (TMR32_GetFlag(US_TIMER)) {
TMR32_ClearFlag(US_TIMER);
NVIC_ClearPendingIRQ(US_TIMER_IRQn);
inc_current_cnt(TMR32_GetCompare(US_TIMER));
}
// add and reset the current count value
inc_current_cnt(TMR32_GetCount(US_TIMER));
TMR32_SetCount(US_TIMER, 0);
// add the number of cycles that the timer is disabled here for
inc_current_cnt(200);
event_cnt = (uint64_t)timestamp * ticks_per_us;
// Check to see if the event has already passed
if (!event_passed(current_cnt, event_cnt)) {
uint64_t diff = event_diff(current_cnt, event_cnt);
if (diff < (uint64_t)0xFFFFFFFF) {
// the event occurs before the next overflow
TMR32_SetCompare(US_TIMER, diff);
} else {
// the event occurs after the next overflow
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF); // set to max
}
} else {
// the requested timestamp occurs in the past
// set the timer up to immediately expire
TMR32_SetCompare(US_TIMER, 1);
}
TMR32_Start(US_TIMER);
US_TIMER->ctrl = 0;
US_TIMER->term_cnt32 = timestamp;
US_TIMER->inten = MXC_F_TMR_INTEN_TIMER0;
US_TIMER->ctrl = MXC_F_TMR_CTRL_ENABLE0 |
(US_TIMER_MODE << MXC_F_TMR_CTRL_MODE_POS) |
(US_TIMER_PRESCALE << MXC_F_TMR_CTRL_PRESCALE_POS);
}
//******************************************************************************
void us_ticker_fire_interrupt(void)
{
TMR32_SetCompare(US_TIMER, 1);
NVIC_SetPendingIRQ(US_TIMER_IRQn);
}
//******************************************************************************
void us_ticker_disable_interrupt(void)
{
// There are no more events, set timer overflow to the max
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF);
US_TIMER->inten = 0;
}
//******************************************************************************
void us_ticker_clear_interrupt(void)
{
// cleared in the local handler
US_TIMER->intfl = MXC_F_TMR_INTFL_TIMER0;
}
//******************************************************************************
void us_ticker_set(timestamp_t timestamp)
const ticker_info_t* us_ticker_get_info(void)
{
TMR32_Stop(US_TIMER);
current_cnt = (uint64_t)timestamp * ticks_per_us;
TMR32_SetCount(US_TIMER, 0);
TMR32_SetCompare(US_TIMER, 0xFFFFFFFF);
TMR32_Start(US_TIMER);
static const ticker_info_t info = {
RO_FREQ >> US_TIMER_PRESCALE,
US_TIMER_WIDTH
};
if (((uint64_t)timestamp * ticks_per_us) >= event_cnt) {
// The next timestamp has elapsed. Trigger the interrupt to handle it.
NVIC_SetPendingIRQ(US_TIMER_IRQn);
}
return &info;
}