From b654a6f46722609a0a3df4ddf74ec159df7045cf Mon Sep 17 00:00:00 2001 From: Lok Tep Date: Mon, 9 Nov 2015 21:01:32 +0100 Subject: [PATCH] alarm --- arch/arm/src/lpc43xx/lpc43_tickless_rit.c | 434 ++++++++++++++-------- 1 file changed, 273 insertions(+), 161 deletions(-) diff --git a/arch/arm/src/lpc43xx/lpc43_tickless_rit.c b/arch/arm/src/lpc43xx/lpc43_tickless_rit.c index d9f91088f3..531755cfe4 100644 --- a/arch/arm/src/lpc43xx/lpc43_tickless_rit.c +++ b/arch/arm/src/lpc43xx/lpc43_tickless_rit.c @@ -35,7 +35,7 @@ /**************************************************************************** * * only controlled resets to 0 are performed, no direct set to counter - * working counter region is from 0 to to_end + * working counter region is from 0 to TO_END * all public functions are synchronized with disabled irqs * ****************************************************************************/ @@ -71,85 +71,98 @@ * Private Data ****************************************************************************/ -static uint32_t to_reset = UINT32_MAX / 2; -static uint32_t to_reset_next = UINT32_MAX / 2 + UINT32_MAX / 4; -static uint32_t to_end = UINT32_MAX / 2 + UINT32_MAX / 4 + UINT32_MAX / 8; +static uint32_t TO_RESET = UINT32_MAX / 2; +static uint32_t TO_RESET_NEXT = UINT32_MAX / 2 + UINT32_MAX / 4; +static uint32_t TO_END = UINT32_MAX / 2 + UINT32_MAX / 4 + UINT32_MAX / 8; /* any alarm should no last more than UINT32_MAX/8 */ +static struct timespec MAX_TS; -static uint32_t reset_ticks = 1000; /* ticks to add to force a reset */ +static uint32_t COMMON_DEV; +static uint32_t MIN_TICKS; +static uint32_t MIN_NSEC; -static uint64_t base = 0; /* time base */ +static uint32_t RESET_TICKS = 1000; /* ticks to add to force a reset */ -static uint64_t alarm_time = 0; /* alarmTime to set on next interrupt, used if not already armed */ +static struct timespec base_ts; /* time base */ +static uint32_t base_rest; /* rest of ticks that is < MIN_TICKS*/ + +static struct timespec alarm_time_ts; /* alarmTime to set on next interrupt, used if not already armed */ + +static bool alarm_time_set = false; /* true if alarm_time set and need to be processed */ static bool call = false; /* true if callback should be called on next interrupt */ static bool forced_int = false; /* true if interrupt was forced with mask, no reset */ static bool armed = false; /* true if alarm is armed for next match */ static uint32_t synch = 0; /* synch all calls, recursion is possible */ static irqstate_t g_flags; +static uint32_t ctrl_cache; +static uint32_t mask_cache; +static uint32_t compare_cache; + /**************************************************************************** * Private Functions ****************************************************************************/ /* some timer HW functions */ -static inline void lpc43_tl_set_counter (uint32_t value) + static inline void lpc43_tl_set_counter (uint32_t value) { putreg32(value, LPC43_RIT_COUNTER); } -static inline uint32_t lpc43_tl_get_counter (void) + static inline uint32_t lpc43_tl_get_counter (void) { return getreg32(LPC43_RIT_COUNTER); } -static inline void lpc43_tl_set_compare (uint32_t value) + static inline void lpc43_tl_set_compare (uint32_t value) { - putreg32(value, LPC43_RIT_COMPVAL); + if ( value != compare_cache) + { + compare_cache = value; + putreg32(value, LPC43_RIT_COMPVAL); + } } static inline uint32_t lpc43_tl_get_compare (void) { - return getreg32(LPC43_RIT_COMPVAL); + return compare_cache; } -static inline void lpc43_tl_set_mask (uint32_t value) + static inline void lpc43_tl_set_mask (uint32_t value) { - putreg32(value, LPC43_RIT_MASK); + if ( value != mask_cache) + { + mask_cache = value; + putreg32(value, LPC43_RIT_MASK); + } } static inline uint32_t lpc43_tl_get_mask (void) { - return getreg32(LPC43_RIT_MASK); -} - -static inline void lpc43_tl_set_ctrl_bit (uint32_t bit, bool value) -{ - uint32_t regval; - regval = getreg32(LPC43_RIT_CTRL); - if (value) - { - regval |= bit; - } - else - { - regval &= ~bit; - } - putreg32(regval, LPC43_RIT_CTRL); + return mask_cache; } static inline bool lpc43_tl_get_ctrl_bit (uint32_t bit) { - uint32_t regval; - regval = getreg32(LPC43_RIT_CTRL); - regval &= bit; - if (regval) - { - return true; - } - else - { - return false; - } + return ((ctrl_cache & bit)?true:false); +} + + static inline void lpc43_tl_set_ctrl_bit (uint32_t bit, bool value) +{ + + if ( lpc43_tl_get_ctrl_bit(bit) != value ) { + + if (value) + { + ctrl_cache |= bit; + } + else + { + ctrl_cache &= ~bit; + } + + putreg32(ctrl_cache, LPC43_RIT_CTRL); + } } static inline void lpc43_tl_set_reset_on_match (bool value) @@ -172,32 +185,113 @@ static inline bool lpc43_tl_get_enable (void) return lpc43_tl_get_ctrl_bit (RIT_CTRL_EN); } -static inline void lpc43_tl_clear_interrupt (void) + static inline void lpc43_tl_clear_interrupt (void) { - lpc43_tl_set_ctrl_bit (RIT_CTRL_INT, true); + putreg32(ctrl_cache | RIT_CTRL_INT, LPC43_RIT_CTRL); } -static inline bool lpc43_tl_get_interrupt (void) + static inline bool lpc43_tl_get_interrupt (void) { - return lpc43_tl_get_ctrl_bit (RIT_CTRL_INT); + return (( getreg32(LPC43_RIT_CTRL) & RIT_CTRL_INT )?true:false); } /* converters */ +static uint32_t commonDev(uint32_t a, uint32_t b) +{ + while(b !=0) + { + int h = a%b; + a = b; + b = h; + } -static inline uint64_t lpc43_tl_ts2tick ( FAR const struct timespec *ts) + return a; +} + + static void lpc43_tl_add(FAR const struct timespec *ts1, + FAR const struct timespec *ts2, + FAR struct timespec *ts3) +{ + time_t sec = ts1->tv_sec + ts2->tv_sec; + long nsec = ts1->tv_nsec + ts2->tv_nsec; + + if (nsec >= NSEC_PER_SEC) + { + nsec -= NSEC_PER_SEC; + sec++; + } + + ts3->tv_sec = sec; + ts3->tv_nsec = nsec; +} + + static void lpc43_tl_sub(FAR const struct timespec *ts1, + FAR const struct timespec *ts2, + FAR struct timespec *ts3) +{ + time_t sec; + long nsec; + + if (ts1->tv_sec < ts2->tv_sec) + { + sec = 0; + nsec = 0; + } + else if (ts1->tv_sec == ts2->tv_sec && ts1->tv_nsec <= ts2->tv_nsec) + { + sec = 0; + nsec = 0; + } + else + { + sec = ts1->tv_sec - ts2->tv_sec; + if (ts1->tv_nsec < ts2->tv_nsec) + { + nsec = (ts1->tv_nsec + NSEC_PER_SEC) - ts2->tv_nsec; + sec--; + } + else + { + nsec = ts1->tv_nsec - ts2->tv_nsec; + } + } + + ts3->tv_sec = sec; + ts3->tv_nsec = nsec; +} + +static inline uint32_t lpc43_tl_ts2tick ( FAR const struct timespec *ts) { - return ( (uint64_t)(ts->tv_sec)*LPC43_CCLK + ( (uint64_t)(ts->tv_nsec)*LPC43_CCLK/NSEC_PER_SEC) ); + return ( ts->tv_sec*LPC43_CCLK + ( ts->tv_nsec/MIN_NSEC*MIN_TICKS ) ); } -static inline void lpc43_tl_tick2ts (uint64_t tick, FAR struct timespec *ts) + static uint32_t lpc43_tl_tick2ts (uint32_t ticks, FAR struct timespec *ts, bool with_rest) { - ts->tv_sec = tick/LPC43_CCLK; - ts->tv_nsec = (tick%LPC43_CCLK)*NSEC_PER_SEC/LPC43_CCLK; + + uint32_t ticks_whole; + uint32_t ticks_rest = 0; + + if (with_rest) + { + uint32_t ticks_mult = ticks/MIN_TICKS; + ticks_whole = ticks_mult*MIN_TICKS; + ticks_rest = ticks - ticks_whole; + + } + else + { + ticks_whole = ticks; + } + + ts->tv_sec = ticks_whole/LPC43_CCLK; + ts->tv_nsec = ((ticks_whole%LPC43_CCLK)/MIN_TICKS)*MIN_NSEC; + + return ticks_rest; } /* logic functions */ -static void lpc43_tl_sync_up (void) { +static inline void lpc43_tl_sync_up (void) { irqstate_t flags; flags = irqsave (); @@ -209,7 +303,7 @@ static void lpc43_tl_sync_up (void) { synch++; } -static void lpc43_tl_sync_down (void) { +static inline void lpc43_tl_sync_down (void) { synch--; if ( synch == 0 ) { @@ -219,7 +313,7 @@ static void lpc43_tl_sync_down (void) { /* assuming safe timer state, force interrupt, no reset possible */ -static void lpc43_tl_force_int (void) +static inline void lpc43_tl_force_int (void) { forced_int = true; lpc43_tl_set_reset_on_match (false); @@ -229,22 +323,22 @@ static void lpc43_tl_force_int (void) /* init all vars, forced_int should not be cleared */ -static void lpc43_tl_init_timer_vars (void) +static inline void lpc43_tl_init_timer_vars (void) { - alarm_time = 0; + alarm_time_set = false; call = false; armed = false; } -/* calc reset_ticks and set compare to to_reset */ +/* calc RESET_TICKS and set compare to TO_RESET */ static void lpc43_tl_calibrate_init (void) { uint32_t counter = lpc43_tl_get_counter (); uint32_t counter_after = lpc43_tl_get_counter (); - counter_after = to_reset + counter; + counter_after = TO_RESET + counter; counter_after = counter_after - counter; /*shift to toReset*/ @@ -253,13 +347,13 @@ static void lpc43_tl_calibrate_init (void) counter_after = lpc43_tl_get_counter (); - reset_ticks = (counter_after - counter) * 2; + RESET_TICKS = (counter_after - counter) * 2; } /* process current and set timer in default safe state */ -static void lpc43_tl_save_timer (void) +static void lpc43_tl_save_timer (bool from_isr) { if (forced_int) /* special case of forced interrupt by mask*/ { @@ -280,12 +374,13 @@ static void lpc43_tl_save_timer (void) lpc43_tl_set_compare (UINT32_MAX); lpc43_tl_set_mask (0); - if (lpc43_tl_get_interrupt ()) + if (from_isr || lpc43_tl_get_interrupt ()) { if (lpc43_tl_get_reset_on_match ()) /*was reset ?*/ { - base = base + match; - lpc43_tl_set_reset_on_match (false); + struct timespec match_ts; + base_rest = lpc43_tl_tick2ts(match + base_rest, &match_ts,true); + lpc43_tl_add(&base_ts, &match_ts,&base_ts); } lpc43_tl_clear_interrupt (); } @@ -296,7 +391,7 @@ static void lpc43_tl_save_timer (void) static bool lpc43_tl_set_safe_compare (uint32_t compare_to_set) { - if (compare_to_set < to_reset) + if (compare_to_set < TO_RESET) { lpc43_tl_set_reset_on_match (false); } @@ -324,7 +419,6 @@ static bool lpc43_tl_set_safe_compare (uint32_t compare_to_set) } else { - lpc43_tl_set_reset_on_match (false); lpc43_tl_set_compare (UINT32_MAX); return false; @@ -338,12 +432,12 @@ static void lpc43_tl_looped_forced_set_compare (void) { uint32_t i = 1; bool result = lpc43_tl_set_safe_compare ( - lpc43_tl_get_counter () + reset_ticks); /* like in calibrateInit */ + lpc43_tl_get_counter () + RESET_TICKS); /* like in calibrateInit */ while (!result) { i++; result = lpc43_tl_set_safe_compare ( - lpc43_tl_get_counter () + reset_ticks * i); + lpc43_tl_get_counter () + RESET_TICKS * i); }; } @@ -354,15 +448,15 @@ static bool lpc43_tl_set_calc_arm (uint32_t curr, uint32_t to_set, bool arm) uint32_t calcTime; - if (curr < to_reset_next) + if (curr < TO_RESET_NEXT) { - calcTime = min(to_reset_next, to_set); + calcTime = min(TO_RESET_NEXT, to_set); } else { - if (curr < to_end) + if (curr < TO_END) { - calcTime = min(lpc43_tl_get_counter () + reset_ticks, to_set); + calcTime = min(curr + RESET_TICKS, to_set); } else { @@ -383,9 +477,9 @@ static bool lpc43_tl_set_calc_arm (uint32_t curr, uint32_t to_set, bool arm) /* assuming safe timer state, try to set compare for normal operation */ -static void lpc43_tl_set_default_compare (void) +static void lpc43_tl_set_default_compare (uint32_t curr) { - bool result = lpc43_tl_set_calc_arm (lpc43_tl_get_counter (), UINT32_MAX, + bool result = lpc43_tl_set_calc_arm (curr, UINT32_MAX, false); if (!result) { @@ -399,17 +493,36 @@ static void lpc43_tl_set_default_compare (void) } +/* calculates ticks to set from alarm_time_ts and base_ts/base_rest, UINT32_MAX if overflow */ +static inline uint32_t lpc43_tl_calc_to_set (void) +{ + struct timespec diff_ts; + struct timespec ovf_ts; + + lpc43_tl_sub(&alarm_time_ts,&base_ts,&diff_ts); + + lpc43_tl_sub(&diff_ts,&MAX_TS,&ovf_ts); + if ( ovf_ts.tv_sec == 0 && ovf_ts.tv_nsec == 0 ) /* check overflow */ + { + return ( lpc43_tl_ts2tick(&diff_ts) - base_rest ); + } + else + { + return UINT32_MAX; + } + +} + /* assuming safe timer state, used by isr: sets default compare , calls alarm */ -static inline void lpc43_tl_alarm (void) +static inline void lpc43_tl_alarm (uint32_t curr) { lpc43_tl_init_timer_vars (); - lpc43_tl_set_default_compare (); + lpc43_tl_set_default_compare (curr); - struct timespec ts; - - up_timer_gettime (&ts); #ifdef CONFIG_SCHED_TICKLESS_ALARM + struct timespec ts; + up_timer_gettime(&ts); sched_alarm_expiration (&ts); #else sched_timer_expiration(); @@ -422,48 +535,48 @@ static int lpc43_tl_isr (int irq, FAR void *context) { lpc43_tl_sync_up(); - lpc43_tl_save_timer(); + lpc43_tl_save_timer(true); + uint32_t curr = lpc43_tl_get_counter (); if (call) { - lpc43_tl_alarm(); + lpc43_tl_alarm(curr); } else { if (armed) { - lpc43_tl_alarm(); /* armed - call alarm */ + lpc43_tl_alarm(curr); /* armed - call alarm */ } else { - if (alarm_time > 0) /* need to set alarm time */ + if (alarm_time_set) /* need to set alarm time */ { - int64_t toSet = alarm_time - base; - uint32_t curr = lpc43_tl_get_counter (); + uint32_t toSet = lpc43_tl_calc_to_set(); if (toSet > curr) { - if (toSet > to_end) + if (toSet > TO_END) { - lpc43_tl_set_default_compare (); + lpc43_tl_set_default_compare (curr); } else { bool set = lpc43_tl_set_calc_arm (curr, toSet, true); if (!set) { - lpc43_tl_alarm(); + lpc43_tl_alarm(curr); } } } else { - lpc43_tl_alarm(); + lpc43_tl_alarm(curr); } } else { - lpc43_tl_set_default_compare (); + lpc43_tl_set_default_compare (curr); } } } @@ -473,6 +586,7 @@ static int lpc43_tl_isr (int irq, FAR void *context) return OK; } + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -482,6 +596,21 @@ void up_timer_initialize (void) irqstate_t flags; flags = irqsave (); + ctrl_cache = getreg32(LPC43_RIT_CTRL); + ctrl_cache &= ~RIT_CTRL_INT; /* set interrupt to 0*/ + mask_cache = getreg32(LPC43_RIT_MASK); + compare_cache = getreg32(LPC43_RIT_COMPVAL); + + COMMON_DEV = commonDev(NSEC_PER_SEC,LPC43_CCLK); + MIN_TICKS = LPC43_CCLK/COMMON_DEV; + MIN_NSEC = NSEC_PER_SEC/COMMON_DEV; + + base_ts.tv_sec = 0; + base_ts.tv_nsec = 0; + base_rest = 0; + + lpc43_tl_tick2ts(TO_END,&MAX_TS,false); + lpc43_tl_set_enable (false); lpc43_tl_set_compare (UINT32_MAX); @@ -501,15 +630,15 @@ void up_timer_initialize (void) lpc43_tl_calibrate_init (); irqrestore (flags); + } + +/* no reg changes, only processing */ int up_timer_gettime (FAR struct timespec *ts) { lpc43_tl_sync_up(); - uint64_t curr; - /* no reg changes, only processing */ - /* order of calls is important, reset can come during processing */ bool reset = lpc43_tl_get_interrupt (); @@ -518,30 +647,28 @@ int up_timer_gettime (FAR struct timespec *ts) /* not processed reset can exist */ if (lpc43_tl_get_reset_on_match ()) { - bool resetAfter = lpc43_tl_get_interrupt (); + bool resetAfter = lpc43_tl_get_interrupt (); - if (reset != resetAfter) /* was a reset get new counter */ - { - count = lpc43_tl_get_counter (); - } + if (reset != resetAfter) /* was a reset during processing? get new counter */ + { + count = lpc43_tl_get_counter (); + } - if (resetAfter) - { - curr = base + count + lpc43_tl_get_compare (); - } - else - { - curr = base + count; - } - } - else - { - curr = base + count; + if (resetAfter) + { + count += lpc43_tl_get_compare (); /* count should be smaller then UINT32_MAX-TO_END -> no overflow */ + } } - lpc43_tl_tick2ts (curr, ts); + struct timespec count_ts; + + + lpc43_tl_tick2ts(count + base_rest,&count_ts,false ); + + lpc43_tl_add(&base_ts,&count_ts,ts); lpc43_tl_sync_down(); + return OK; } @@ -567,40 +694,40 @@ int up_alarm_start (FAR const struct timespec *ts) { lpc43_tl_sync_up(); - lpc43_tl_save_timer (); + lpc43_tl_save_timer (false); - lpc43_tl_init_timer_vars (); + lpc43_tl_init_timer_vars (); - alarm_time = lpc43_tl_ts2tick (ts); - int64_t toSet = alarm_time - base; - uint32_t curr = lpc43_tl_get_counter (); + alarm_time_set = true; + alarm_time_ts.tv_sec = ts->tv_sec; + alarm_time_ts.tv_nsec = ts->tv_nsec; - if (toSet > curr) - { - if (toSet > to_end) /* future set */ - { - lpc43_tl_set_default_compare (); - } - else - { - bool set = lpc43_tl_set_calc_arm (curr, toSet, true); - if (!set) - { - /* signal call, force interrupt handler */ + uint32_t toSet = lpc43_tl_calc_to_set(); - call = true; - lpc43_tl_force_int (); - } + uint32_t curr = lpc43_tl_get_counter (); - } - } - else - { - /* signal call, force interrupt handler */ + if (toSet > curr) + { + if (toSet > TO_END) /* future set */ + { + lpc43_tl_set_default_compare (curr); + } + else + { + bool set = lpc43_tl_set_calc_arm (curr, toSet, true); + if (!set) /* signal call, force interrupt handler */ + { + call = true; + lpc43_tl_force_int (); + } - call = true; - lpc43_tl_force_int (); - } + } + } + else /* signal call, force interrupt handler */ + { + call = true; + lpc43_tl_force_int (); + } lpc43_tl_sync_down(); @@ -613,27 +740,14 @@ int up_timer_cancel(FAR struct timespec *ts) { lpc43_tl_sync_up(); - if (ts == NULL) + if (ts != NULL) { - up_alarm_cancel(ts); + struct timespec abs_ts; + up_timer_gettime(&abs_ts); + lpc43_tl_sub( &alarm_time_ts,&abs_ts,ts ); } - else - { - uint64_t saved_alarm_time = alarm_time; - up_alarm_cancel(ts); - int64_t diff = saved_alarm_time - lpc43_tl_ts2tick(ts); - if ( diff > 0 ) - { - lpc43_tl_tick2ts(diff, ts); - } - else - { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } - - } + lpc43_tl_init_timer_vars (); lpc43_tl_sync_down(); return OK; @@ -644,13 +758,11 @@ int up_timer_start(FAR const struct timespec *ts) lpc43_tl_sync_up(); - struct timespec tmp_ts; - up_timer_gettime(&tmp_ts); + struct timespec abs_ts; + up_timer_gettime(&abs_ts); + lpc43_tl_add(&abs_ts,ts,&abs_ts); - tmp_ts.tv_sec += ts->tv_sec; - tmp_ts.tv_nsec += ts->tv_nsec; - - up_alarm_start(&tmp_ts); + up_alarm_start(&abs_ts); lpc43_tl_sync_down(); return OK;