/** * * @brief System clock periodic tick handler * * This routine handles the system clock periodic tick interrupt. It always * announces one tick. * * @return N/A */ void _timer_int_handler(void *unused) { ARG_UNUSED(unused); /* clear the interrupt by writing 0 to IP bit of the control register */ timer0_control_register_set(_ARC_V2_TMR_CTRL_NH | _ARC_V2_TMR_CTRL_IE); #ifdef CONFIG_TICKLESS_KERNEL if (!programmed_ticks) { if (_sys_clock_always_on) { _sys_clock_tick_count = _get_elapsed_clock_time(); program_max_cycles(); } return; } _sys_idle_elapsed_ticks = programmed_ticks; /* * Clear programmed ticks before announcing elapsed time so * that recursive calls to _update_elapsed_time() will not * announce already consumed elapsed time */ programmed_ticks = 0; timer_expired = 1; _sys_clock_tick_announce(); /* _sys_clock_tick_announce() could cause new programming */ if (!programmed_ticks && _sys_clock_always_on) { _sys_clock_tick_count = _get_elapsed_clock_time(); program_max_cycles(); } #else #if defined(CONFIG_TICKLESS_IDLE) timer0_limit_register_set(cycles_per_tick - 1); __ASSERT_EVAL({}, u32_t timer_count = timer0_count_register_get(), timer_count <= (cycles_per_tick - 1), "timer_count: %d, limit %d\n", timer_count, cycles_per_tick - 1); _sys_clock_final_tick_announce(); #else _sys_clock_tick_announce(); #endif update_accumulated_count(); #endif }
static void timer_irq_handler(void *unused) { ARG_UNUSED(unused); accumulated_cycle_count += sys_clock_hw_cycles_per_tick; /* Clear the interrupt */ alt_handle_irq((void *)TIMER_0_BASE, TIMER_0_IRQ); _sys_clock_tick_announce(); }
/* * @brief Announces the number of sys ticks, if any, that have passed since the * last announcement, and programs the RTC to trigger the interrupt on the next * sys tick. * * This function is not reentrant. It is called from: * * * _timer_idle_exit(), which in turn is called with interrupts disabled when * an interrupt fires. * * rtc1_nrf5_isr(), which runs with interrupts enabled but at that time the * device cannot be idle and hence _timer_idle_exit() cannot be called. * * Since this function can be preempted, we need to take some provisions to * announce all expected sys ticks that have passed. * */ static void rtc_announce_set_next(void) { u32_t rtc_now, rtc_elapsed, sys_elapsed; /* Read the RTC counter one single time in the beginning, so that an * increase in the counter during this procedure leads to no race * conditions. */ rtc_now = RTC_COUNTER; /* Calculate how many RTC ticks elapsed since the last sys tick. */ rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK; /* If no sys ticks have elapsed, there is no point in incrementing the * counters or announcing it. */ if (rtc_elapsed >= sys_clock_hw_cycles_per_tick) { #ifdef CONFIG_TICKLESS_IDLE /* Calculate how many sys ticks elapsed since the last sys tick * and notify the kernel if necessary. */ sys_elapsed = rtc_elapsed / sys_clock_hw_cycles_per_tick; if (sys_elapsed > expected_sys_ticks) { /* Never announce more sys ticks than the kernel asked * to be idle for. The remainder will be announced when * the RTC ISR runs after rtc_compare_set() is called * after the first announcement. */ sys_elapsed = expected_sys_ticks; } #else /* Never announce more than one sys tick if tickless idle is not * configured. */ sys_elapsed = 1; #endif /* CONFIG_TICKLESS_IDLE */ /* Store RTC_COUNTER floored to the last sys tick. This is * done, so that ISR can properly calculate that 1 sys tick * has passed. */ rtc_past = (rtc_past + (sys_elapsed * sys_clock_hw_cycles_per_tick) ) & RTC_MASK; _sys_idle_elapsed_ticks = sys_elapsed; _sys_clock_tick_announce(); } /* Set the RTC to the next sys tick */ rtc_compare_set(rtc_past + sys_clock_hw_cycles_per_tick); }
/** * * @brief System clock tick handler * * This routine handles the system clock tick interrupt. A TICK_EVENT event * is pushed onto the kernel stack. * * @return N/A */ void _timer_int_handler(void *unused) { ARG_UNUSED(unused); #if defined(CONFIG_HPET_TIMER_LEVEL_LOW) || defined(CONFIG_HPET_TIMER_LEVEL_HIGH) /* Acknowledge interrupt */ *_HPET_GENERAL_INT_STATUS = 1; #endif #ifdef CONFIG_INT_LATENCY_BENCHMARK uint32_t delta = *_HPET_MAIN_COUNTER_VALUE - main_count_expected_value; if (_hw_irq_to_c_handler_latency > delta) { /* keep the lowest value observed */ _hw_irq_to_c_handler_latency = delta; } /* compute the next expected main counter value */ main_count_expected_value += main_count_first_irq_value; #endif #ifndef CONFIG_TICKLESS_IDLE /* * one more tick has occurred -- don't need to do anything special since * timer is already configured to interrupt on the following tick */ _sys_clock_tick_announce(); #else /* see if interrupt was triggered while timer was being reprogrammed */ if (stale_irq_check) { stale_irq_check = 0; if (_hpetMainCounterAtomic() < *_HPET_TIMER0_COMPARATOR) { return; /* ignore "stale" interrupt */ } } /* configure timer to expire on next tick */ counter_last_value = *_HPET_TIMER0_COMPARATOR; *_HPET_TIMER0_CONFIG_CAPS |= HPET_Tn_VAL_SET_CNF; *_HPET_TIMER0_COMPARATOR = counter_last_value + counter_load_value; programmed_ticks = 1; _sys_clock_final_tick_announce(); #endif /* !CONFIG_TICKLESS_IDLE */ }
void _timer_int_handler(void *unused /* parameter is not used */ ) { ARG_UNUSED(unused); #ifdef CONFIG_TICKLESS_IDLE if (timer_mode == TIMER_MODE_ONE_SHOT) { if (!timer_known_to_have_expired) { uint32_t cycles; /* * The timer fired unexpectedly. This is due to one of two cases: * 1. Entering tickless idle straddled a tick. * 2. Leaving tickless idle straddled the final tick. * Due to the timer reprogramming in _timer_idle_exit(), case #2 * can be handled as a fall-through. * * NOTE: Although the cycle count is supposed to stop decrementing * once it hits zero in one-shot mode, not all targets implement * this properly (and continue to decrement). Thus, we have to * perform a second comparison to check for wrap-around. */ cycles = current_count_register_get(); if ((cycles > 0) && (cycles < programmed_cycles)) { /* Case 1 */ _sys_idle_elapsed_ticks = 0; } } /* Return the timer to periodic mode */ initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); timer_known_to_have_expired = false; timer_mode = TIMER_MODE_PERIODIC; } _sys_clock_final_tick_announce(); /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick * _sys_idle_elapsed_ticks; #else /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick; _sys_clock_tick_announce(); #endif /*CONFIG_TICKLESS_IDLE*/ }
/* * @brief Announces the number of sys ticks that have passed since the last * announcement, if any, and programs the RTC to trigger the interrupt on the * next sys tick. * * The ISR is set pending due to a regular sys tick and after exiting idle mode * as scheduled. * * Since this ISR can be preempted, we need to take some provisions to announce * all expected sys ticks that have passed. * * Consider the following example: * * sys tick timeline: (1) (2) (3) (4) (5) (6) * rtc tick timeline : 0----100----200----300----400----500----600 * !********** * 450 * * The last sys tick was anounced at 200, i.e, rtc_past = 200. The ISR is * executed at the next sys tick, i.e. 300. The following sys tick is due at * 400. However, the ISR is preempted for a number of sys ticks, until 450 in * this example. The ISR will then announce the number of sys ticks it was * delayed (2), and schedule the next sys tick (5) at 500. */ void rtc1_nrf5_isr(void *arg) { ARG_UNUSED(arg); RTC_CC_EVENT = 0; #ifdef CONFIG_EXECUTION_BENCHMARKING extern void read_timer_start_of_tick_handler(void); read_timer_start_of_tick_handler(); #endif sys_trace_isr_enter(); #ifdef CONFIG_TICKLESS_KERNEL if (!expected_sys_ticks) { if (_sys_clock_always_on) { program_max_cycles(); } return; } _sys_idle_elapsed_ticks = expected_sys_ticks; /* Initialize expected sys tick, * It will be later updated based on next timeout. */ expected_sys_ticks = 0; /* Anounce elapsed of _sys_idle_elapsed_ticks systicks*/ _sys_clock_tick_announce(); /* _sys_clock_tick_announce() could cause new programming */ if (!expected_sys_ticks && _sys_clock_always_on) { program_max_cycles(); } #else rtc_announce_set_next(); #endif #ifdef CONFIG_EXECUTION_BENCHMARKING extern void read_timer_end_of_tick_handler(void); read_timer_end_of_tick_handler(); #endif sys_trace_isr_exit(); }
/** * * @brief Handling of tickless idle when interrupted * * The routine is responsible for taking the timer out of idle mode and * generating an interrupt at the next tick interval. * * Note that in this routine, _sys_idle_elapsed_ticks must be zero because the * ticker has done its work and consumed all the ticks. This has to be true * otherwise idle mode wouldn't have been entered in the first place. * * @return N/A */ void _timer_idle_exit(void) { uint32_t remaining_cycles; uint32_t remaining_full_ticks; /* * Interrupts are locked and idling has ceased. The cause of the cessation * is unknown. It may be due to one of three cases. * 1. The timer, which was previously placed into one-shot mode has * counted down to zero and signaled an interrupt. * 2. A non-timer interrupt occurred. Note that the LOAPIC timer will * still continue to decrement and may yet signal an interrupt. * 3. The LOAPIC timer signaled an interrupt while the timer was being * programmed for one-shot mode. * * NOTE: Although the cycle count is supposed to stop decrementing once it * hits zero in one-shot mode, not all targets implement this properly * (and continue to decrement). Thus a second comparison is required to * check for wrap-around. */ remaining_cycles = current_count_register_get(); if ((remaining_cycles == 0) || (remaining_cycles >= programmed_cycles)) { /* * The timer has expired. The handler _timer_int_handler() is * guaranteed to execute. Track the number of elapsed ticks. The * handler _timer_int_handler() will account for the final tick. */ _sys_idle_elapsed_ticks = programmed_full_ticks; /* * Announce elapsed ticks to the kernel. Note we are guaranteed * that the timer ISR will execute before the tick event is serviced. * (The timer ISR reprograms the timer for the next tick.) */ _sys_clock_tick_announce(); timer_known_to_have_expired = true; return; } timer_known_to_have_expired = false; /* * Either a non-timer interrupt occurred, or we straddled a tick when * entering tickless idle. It is impossible to determine which occurred * at this point. Regardless of the cause, ensure that the timer will * expire at the end of the next tick in case the ISR makes any tasks * and/or fibers ready to run. * * NOTE #1: In the case of a straddled tick, the '_sys_idle_elapsed_ticks' * calculation below may result in either 0 or 1. If 1, then this may * result in a harmless extra call to _sys_clock_tick_announce(). * * NOTE #2: In the case of a straddled tick, it is assumed that when the * timer is reprogrammed, it will be reprogrammed with a cycle count * sufficiently close to one tick that the timer will not expire before * _timer_int_handler() is executed. */ remaining_full_ticks = remaining_cycles / cycles_per_tick; _sys_idle_elapsed_ticks = programmed_full_ticks - remaining_full_ticks; if (_sys_idle_elapsed_ticks > 0) { _sys_clock_tick_announce(); } if (remaining_full_ticks > 0) { /* * Re-program the timer (still in one-shot mode) to fire at the end of * the tick, being careful to not program zero thus stopping the timer. */ programmed_cycles = 1 + ((remaining_cycles - 1) % cycles_per_tick); initial_count_register_set(programmed_cycles); } }
void _timer_idle_exit(void) { #ifdef CONFIG_TICKLESS_KERNEL if (!programmed_ticks && _sys_clock_always_on) { if (!(timer0_control_register_get() & _ARC_V2_TMR_CTRL_IE)) { timer0_control_register_set(_ARC_V2_TMR_CTRL_NH | _ARC_V2_TMR_CTRL_IE); } program_max_cycles(); } #else if (straddled_tick_on_idle_enter) { /* Aborting the tickless idle due to a straddled tick. */ straddled_tick_on_idle_enter = 0; __ASSERT_EVAL({}, u32_t timer_count = timer0_count_register_get(), timer_count <= programmed_limit, "timer_count: %d, limit %d\n", timer_count, programmed_limit); return; } u32_t control; u32_t current_count; current_count = timer0_count_register_get(); control = timer0_control_register_get(); if (control & _ARC_V2_TMR_CTRL_IP) { /* * The timer has expired. The handler _timer_int_handler() is * guaranteed to execute. Track the number of elapsed ticks. The * handler _timer_int_handler() will account for the final tick. */ _sys_idle_elapsed_ticks = programmed_ticks - 1; update_accumulated_count(); _sys_clock_tick_announce(); __ASSERT_EVAL({}, u32_t timer_count = timer0_count_register_get(), timer_count <= programmed_limit, "timer_count: %d, limit %d\n", timer_count, programmed_limit); return; } /* * A non-timer interrupt occurred. Announce any * ticks that have elapsed during the tickless idle. */ _sys_idle_elapsed_ticks = current_count / cycles_per_tick; if (_sys_idle_elapsed_ticks > 0) { update_accumulated_count(); _sys_clock_tick_announce(); } /* * Ensure the timer will expire at the end of the next tick in case * the ISR makes any threads ready to run. */ timer0_limit_register_set(cycles_per_tick - 1); timer0_count_register_set(current_count % cycles_per_tick); __ASSERT_EVAL({}, u32_t timer_count = timer0_count_register_get(), timer_count <= (cycles_per_tick - 1), "timer_count: %d, limit %d\n", timer_count, cycles_per_tick-1); #endif }
/** * * @brief Handling of tickless idle when interrupted * * The routine, called by _sys_power_save_idle_exit, is responsible for taking * the timer out of idle mode and generating an interrupt at the next * tick interval. It is expected that interrupts have been disabled. * * Note that in this routine, _sys_idle_elapsed_ticks must be zero because the * ticker has done its work and consumed all the ticks. This has to be true * otherwise idle mode wouldn't have been entered in the first place. * * @return N/A */ void _timer_idle_exit(void) { #ifdef CONFIG_TICKLESS_KERNEL if (idle_mode == IDLE_TICKLESS) { idle_mode = IDLE_NOT_TICKLESS; if (!idle_original_ticks && _sys_clock_always_on) { _sys_clock_tick_count = _get_elapsed_clock_time(); timer_overflow = 0; sysTickReloadSet(max_load_value); sysTickStart(); sys_tick_reload(); } } #else u32_t count; /* timer's current count register value */ if (timer_mode == TIMER_MODE_PERIODIC) { /* * The timer interrupt handler is handling a completed tickless * idle or this has been called by mistake; there's nothing to * do here. */ return; } sysTickStop(); /* timer is in idle mode, adjust the ticks expired */ count = sysTickCurrentGet(); if ((count == 0) || (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) { /* * The timer expired and/or wrapped around. Re-set the timer to * its default value and mode. */ sysTickReloadSet(default_load_value); timer_mode = TIMER_MODE_PERIODIC; /* * Announce elapsed ticks to the kernel. Note we are guaranteed * that the timer ISR will execute before the tick event is * serviced, so _sys_idle_elapsed_ticks is adjusted to account * for it. */ _sys_idle_elapsed_ticks = idle_original_ticks - 1; _sys_clock_tick_announce(); } else { u32_t elapsed; /* elapsed "counter time" */ u32_t remaining; /* remaining "counter time" */ elapsed = idle_original_count - count; remaining = elapsed % default_load_value; /* ensure that the timer will interrupt at the next tick */ if (remaining == 0) { /* * Idle was interrupted on a tick boundary. Re-set the * timer to its default value and mode. */ sysTickReloadSet(default_load_value); timer_mode = TIMER_MODE_PERIODIC; } else if (count > remaining) { /* * There is less time remaining to the next tick * boundary than time left for idle. Leave in "one * shot" mode. */ sysTickReloadSet(remaining); } _sys_idle_elapsed_ticks = elapsed / default_load_value; if (_sys_idle_elapsed_ticks) { _sys_clock_tick_announce(); } } idle_mode = IDLE_NOT_TICKLESS; sysTickStart(); #endif }
/** * * @brief System clock tick handler * * This routine handles the system clock tick interrupt. A TICK_EVENT event * is pushed onto the kernel stack. * * The symbol for this routine is either _timer_int_handler. * * @return N/A */ void _timer_int_handler(void *unused) { ARG_UNUSED(unused); #ifdef CONFIG_EXECUTION_BENCHMARKING extern void read_systick_start_of_tick_handler(void); read_systick_start_of_tick_handler(); #endif #ifdef CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT extern void _sys_k_event_logger_interrupt(void); _sys_k_event_logger_interrupt(); #endif #ifdef CONFIG_SYS_POWER_MANAGEMENT s32_t numIdleTicks; /* * All interrupts are disabled when handling idle wakeup. * For tickless idle, this ensures that the calculation and programming * of * the device for the next timer deadline is not interrupted. * For non-tickless idle, this ensures that the clearing of the kernel * idle * state is not interrupted. * In each case, _sys_power_save_idle_exit is called with interrupts * disabled. */ __asm__(" cpsid i"); /* PRIMASK = 1 */ #ifdef CONFIG_TICKLESS_IDLE #if defined(CONFIG_TICKLESS_KERNEL) if (!idle_original_ticks) { if (_sys_clock_always_on) { _sys_clock_tick_count = _get_elapsed_clock_time(); /* clear overflow tracking flag as it is accounted */ timer_overflow = 0; sysTickStop(); idle_original_ticks = max_system_ticks; sysTickReloadSet(max_load_value); sysTickStart(); sys_tick_reload(); } __asm__(" cpsie i"); /* re-enable interrupts (PRIMASK = 0) */ _ExcExit(); return; } idle_mode = IDLE_NOT_TICKLESS; _sys_idle_elapsed_ticks = idle_original_ticks; /* * Clear programmed ticks before announcing elapsed time so * that recursive calls to _update_elapsed_time() will not * announce already consumed elapsed time */ idle_original_ticks = 0; _sys_clock_tick_announce(); /* _sys_clock_tick_announce() could cause new programming */ if (!idle_original_ticks && _sys_clock_always_on) { _sys_clock_tick_count = _get_elapsed_clock_time(); /* clear overflow tracking flag as it is accounted */ timer_overflow = 0; sysTickStop(); sysTickReloadSet(max_load_value); sysTickStart(); sys_tick_reload(); } #else /* * If this a wakeup from a completed tickless idle or after * _timer_idle_exit has processed a partial idle, return * to the normal tick cycle. */ if (timer_mode == TIMER_MODE_ONE_SHOT) { sysTickStop(); sysTickReloadSet(default_load_value); sysTickStart(); timer_mode = TIMER_MODE_PERIODIC; } /* set the number of elapsed ticks and announce them to the kernel */ if (idle_mode == IDLE_TICKLESS) { /* tickless idle completed without interruption */ idle_mode = IDLE_NOT_TICKLESS; _sys_idle_elapsed_ticks = idle_original_ticks + 1; /* actual # of idle ticks */ _sys_clock_tick_announce(); } else { _sys_clock_final_tick_announce(); } /* accumulate total counter value */ clock_accumulated_count += default_load_value * _sys_idle_elapsed_ticks; #endif #else /* !CONFIG_TICKLESS_IDLE */ /* * No tickless idle: * Update the total tick count and announce this tick to the kernel. */ clock_accumulated_count += sys_clock_hw_cycles_per_tick; _sys_clock_tick_announce(); #endif /* CONFIG_TICKLESS_IDLE */ numIdleTicks = _NanoIdleValGet(); /* get # of idle ticks requested */ if (numIdleTicks) { _NanoIdleValClear(); /* clear kernel idle setting */ /* * Complete idle processing. * Note that for tickless idle, nothing will be done in * _timer_idle_exit. */ _sys_power_save_idle_exit(numIdleTicks); } __asm__(" cpsie i"); /* re-enable interrupts (PRIMASK = 0) */ #else /* !CONFIG_SYS_POWER_MANAGEMENT */ /* accumulate total counter value */ clock_accumulated_count += sys_clock_hw_cycles_per_tick; /* * one more tick has occurred -- don't need to do anything special since * timer is already configured to interrupt on the following tick */ _sys_clock_tick_announce(); #endif /* CONFIG_SYS_POWER_MANAGEMENT */ #ifdef CONFIG_EXECUTION_BENCHMARKING extern void read_systick_end_of_tick_handler(void); read_systick_end_of_tick_handler(); #endif extern void _ExcExit(void); _ExcExit(); }
void _timer_idle_exit(void) { uint64_t currTime = _hpetMainCounterAtomic(); int32_t elapsedTicks; uint64_t counterNextValue; /* see if idling ended because timer expired at the desired tick */ if (currTime >= *_HPET_TIMER0_COMPARATOR) { /* * update # of ticks since last tick event was announced, * so that this value is available to ISRs that run before the * timer interrupt handler runs (which is unlikely, but could * happen) */ _sys_idle_elapsed_ticks = programmed_ticks - 1; /* * Announce elapsed ticks to the microkernel. Note we are * guaranteed that the timer ISR will execute first before the * tick event is serviced. */ _sys_clock_tick_announce(); /* timer interrupt handler reprograms the timer for the next * tick */ return; } /* * idling ceased because a non-timer interrupt occurred * * compute how much idle time has elapsed and reprogram the timer * to expire on the next tick; if the next tick will happen so soon * that HPET might miss the interrupt declare that tick prematurely * and program the timer for the tick after that * * note: a premature tick declaration has no significant impact on * the microkernel, which gets informed of the correct number of elapsed * ticks when the following tick finally occurs; however, any ISRs that * access _sys_idle_elapsed_ticks to determine the current time may be * misled during the (very brief) interval before the tick-in-progress * finishes and the following tick begins */ elapsedTicks = (int32_t)((currTime - counter_last_value) / counter_load_value); counter_last_value += (uint64_t)elapsedTicks * counter_load_value; counterNextValue = counter_last_value + counter_load_value; if ((counterNextValue - currTime) <= HPET_COMP_DELAY) { elapsedTicks++; counterNextValue += counter_load_value; counter_last_value += counter_load_value; } *_HPET_TIMER0_CONFIG_CAPS |= HPET_Tn_VAL_SET_CNF; *_HPET_TIMER0_COMPARATOR = counterNextValue; stale_irq_check = 1; /* * update # of ticks since last tick event was announced, * so that this value is available to ISRs that run before the timer * expires and the timer interrupt handler runs */ _sys_idle_elapsed_ticks = elapsedTicks; if (_sys_idle_elapsed_ticks) { /* Announce elapsed ticks to the microkernel */ _sys_clock_tick_announce(); } /* * Any elapsed ticks have been accounted for so simply set the * programmed ticks to 1 since the timer has been programmed to fire on * the next tick boundary. */ programmed_ticks = 1; }
/** * * @brief System clock tick handler * * This routine handles the system clock tick interrupt. A TICK_EVENT event * is pushed onto the microkernel stack. * * @return N/A */ void _timer_int_handler(void *unused) { ARG_UNUSED(unused); #if defined(CONFIG_HPET_TIMER_LEVEL_LOW) || defined(CONFIG_HPET_TIMER_LEVEL_HIGH) /* Acknowledge interrupt */ *_HPET_GENERAL_INT_STATUS = 1; #endif #ifdef CONFIG_INT_LATENCY_BENCHMARK uint32_t delta = *_HPET_MAIN_COUNTER_VALUE - main_count_expected_value; if (_hw_irq_to_c_handler_latency > delta) { /* keep the lowest value observed */ _hw_irq_to_c_handler_latency = delta; } /* compute the next expected main counter value */ main_count_expected_value += main_count_first_irq_value; #endif #ifndef CONFIG_TICKLESS_IDLE /* * one more tick has occurred -- don't need to do anything special since * timer is already configured to interrupt on the following tick */ _sys_clock_tick_announce(); #else /* see if interrupt was triggered while timer was being reprogrammed */ if (stale_irq_check) { stale_irq_check = 0; if (_hpetMainCounterAtomic() < *_HPET_TIMER0_COMPARATOR) { return; /* ignore "stale" interrupt */ } } /* configure timer to expire on next tick */ counter_last_value = *_HPET_TIMER0_COMPARATOR; *_HPET_TIMER0_CONFIG_CAPS |= HPET_Tn_VAL_SET_CNF; *_HPET_TIMER0_COMPARATOR = counter_last_value + counter_load_value; programmed_ticks = 1; /* * Increment the tick because _timer_idle_exit does not account * for the tick due to the timer interrupt itself. Also, if not in * tickless mode, _sys_idle_elapsed_ticks will be 0. */ #ifdef CONFIG_MICROKERNEL _sys_idle_elapsed_ticks++; #else _sys_idle_elapsed_ticks = 1; #endif /* CONFIG_MICROKERNEL */ /* * If we transistion from 0 elapsed ticks to 1 we need to announce the * tick * event to the microkernel. Other cases will have already been covered * by * _timer_idle_exit */ if (_sys_idle_elapsed_ticks == 1) { _sys_clock_tick_announce(); } #endif /* !CONFIG_TICKLESS_IDLE */ }
/** * * @brief System clock tick handler * * This routine handles the system clock tick interrupt. A TICK_EVENT event * is pushed onto the microkernel stack. * * @return N/A */ void _timer_int_handler(void *unused /* parameter is not used */ ) { ARG_UNUSED(unused); #ifdef CONFIG_TICKLESS_IDLE if (timer_mode == TIMER_MODE_ONE_SHOT) { if (!timer_known_to_have_expired) { uint32_t cycles; /* * The timer fired unexpectedly. This is due to one of two cases: * 1. Entering tickless idle straddled a tick. * 2. Leaving tickless idle straddled the final tick. * Due to the timer reprogramming in _timer_idle_exit(), case #2 * can be handled as a fall-through. * * NOTE: Although the cycle count is supposed to stop decrementing * once it hits zero in one-shot mode, not all targets implement * this properly (and continue to decrement). Thus, we have to * perform a second comparison to check for wrap-around. */ cycles = current_count_register_get(); if ((cycles > 0) && (cycles < programmed_cycles)) { /* Case 1 */ _sys_idle_elapsed_ticks = 0; } } /* Return the timer to periodic mode */ initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); timer_known_to_have_expired = false; timer_mode = TIMER_MODE_PERIODIC; } /* * Increment the tick because _timer_idle_exit() does not account * for the tick due to the timer interrupt itself. Also, if not in * one-shot mode, _sys_idle_elapsed_ticks will be 0. */ #ifdef CONFIG_MICROKERNEL _sys_idle_elapsed_ticks++; #else _sys_idle_elapsed_ticks = 1; #endif /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick * _sys_idle_elapsed_ticks; /* * If we transistion from 0 elapsed ticks to 1 we need to announce the * tick event to the microkernel. Other cases will have already been * covered by _timer_idle_exit(). */ if (_sys_idle_elapsed_ticks == 1) { _sys_clock_tick_announce(); } #else /* track the accumulated cycle count */ accumulated_cycle_count += cycles_per_tick; _sys_clock_tick_announce(); #endif /*CONFIG_TICKLESS_IDLE*/ #ifdef LOAPIC_TIMER_PERIODIC_WORKAROUND /* * On platforms where the LOAPIC timer periodic mode is broken, * re-program the ICR register with the initial count value. This * is only a temporary workaround. */ initial_count_register_set(cycles_per_tick - 1); periodic_mode_set(); #endif /* LOAPIC_TIMER_PERIODIC_WORKAROUND */ }
/** * * @brief Handling of tickless idle when interrupted * * The routine, called by _sys_power_save_idle_exit, is responsible for taking * the timer out of idle mode and generating an interrupt at the next * tick interval. It is expected that interrupts have been disabled. * * Note that in this routine, _sys_idle_elapsed_ticks must be zero because the * ticker has done its work and consumed all the ticks. This has to be true * otherwise idle mode wouldn't have been entered in the first place. * * @return N/A */ void _timer_idle_exit(void) { uint32_t count; /* timer's current count register value */ if (timer_mode == TIMER_MODE_PERIODIC) { /* * The timer interrupt handler is handling a completed tickless * idle * or this has been called by mistake; there's nothing to do * here. */ return; } sysTickStop(); /* timer is in idle mode, adjust the ticks expired */ count = sysTickCurrentGet(); if ((count == 0) || (__scs.systick.stcsr.bit.countflag)) { /* * The timer expired and/or wrapped around. Re-set the timer to * its default value and mode. */ sysTickReloadSet(default_load_value); timer_mode = TIMER_MODE_PERIODIC; /* * Announce elapsed ticks to the microkernel. Note we are * guaranteed * that the timer ISR will execute before the tick event is * serviced, * so _sys_idle_elapsed_ticks is adjusted to account for it. */ _sys_idle_elapsed_ticks = idle_original_ticks - 1; _sys_clock_tick_announce(); } else { uint32_t elapsed; /* elapsed "counter time" */ uint32_t remaining; /* remaining "counter time" */ elapsed = idle_original_count - count; remaining = elapsed % default_load_value; /* ensure that the timer will interrupt at the next tick */ if (remaining == 0) { /* * Idle was interrupted on a tick boundary. Re-set the * timer to * its default value and mode. */ sysTickReloadSet(default_load_value); timer_mode = TIMER_MODE_PERIODIC; } else if (count > remaining) { /* * There is less time remaining to the next tick * boundary than * time left for idle. Leave in "one shot" mode. */ sysTickReloadSet(remaining); } _sys_idle_elapsed_ticks = elapsed / default_load_value; if (_sys_idle_elapsed_ticks) { _sys_clock_tick_announce(); } } idle_mode = IDLE_NOT_TICKLESS; sysTickStart(); }
/** * * @brief System clock tick handler * * This routine handles the system clock tick interrupt. A TICK_EVENT event * is pushed onto the microkernel stack. * * The symbol for this routine is either _timer_int_handler (for normal * system operation) or _real_timer_int_handler (when GDB_INFO is enabled). * * @return N/A */ void _TIMER_INT_HANDLER(void *unused) { ARG_UNUSED(unused); #ifdef CONFIG_KERNEL_EVENT_LOGGER_INTERRUPT extern void _sys_k_event_logger_interrupt(void); _sys_k_event_logger_interrupt(); #endif #ifdef CONFIG_INT_LATENCY_BENCHMARK uint32_t value = __scs.systick.val; uint32_t delta = __scs.systick.reload - value; if (_hw_irq_to_c_handler_latency > delta) { /* keep the lowest value observed */ _hw_irq_to_c_handler_latency = delta; } #endif #ifdef CONFIG_SYS_POWER_MANAGEMENT int32_t numIdleTicks; /* * All interrupts are disabled when handling idle wakeup. * For tickless idle, this ensures that the calculation and programming * of * the device for the next timer deadline is not interrupted. * For non-tickless idle, this ensures that the clearing of the kernel * idle * state is not interrupted. * In each case, _sys_power_save_idle_exit is called with interrupts * disabled. */ __asm__(" cpsid i"); /* PRIMASK = 1 */ #ifdef CONFIG_TICKLESS_IDLE /* * If this a wakeup from a completed tickless idle or after * _timer_idle_exit has processed a partial idle, return * to the normal tick cycle. */ if (timer_mode == TIMER_MODE_ONE_SHOT) { sysTickStop(); sysTickReloadSet(default_load_value); sysTickStart(); timer_mode = TIMER_MODE_PERIODIC; } /* set the number of elapsed ticks and announce them to the kernel */ if (idle_mode == IDLE_TICKLESS) { /* tickless idle completed without interruption */ idle_mode = IDLE_NOT_TICKLESS; _sys_idle_elapsed_ticks = idle_original_ticks + 1; /* actual # of idle ticks */ _sys_clock_tick_announce(); } else { /* * Increment the tick because _timer_idle_exit does not * account for the tick due to the timer interrupt itself. * Also, if not in tickless mode, _sys_idle_elapsed_ticks will be 0. */ _sys_idle_elapsed_ticks++; /* * If we transition from 0 elapsed ticks to 1 we need to * announce the * tick event to the microkernel. Other cases will be covered by * _timer_idle_exit. */ if (_sys_idle_elapsed_ticks == 1) { _sys_clock_tick_announce(); } } /* accumulate total counter value */ clock_accumulated_count += default_load_value * _sys_idle_elapsed_ticks; #else /* !CONFIG_TICKLESS_IDLE */ /* * No tickless idle: * Update the total tick count and announce this tick to the kernel. */ clock_accumulated_count += sys_clock_hw_cycles_per_tick; _sys_clock_tick_announce(); #endif /* CONFIG_TICKLESS_IDLE */ numIdleTicks = _NanoIdleValGet(); /* get # of idle ticks requested */ if (numIdleTicks) { _NanoIdleValClear(); /* clear kernel idle setting */ /* * Complete idle processing. * Note that for tickless idle, nothing will be done in * _timer_idle_exit. */ _sys_power_save_idle_exit(numIdleTicks); } __asm__(" cpsie i"); /* re-enable interrupts (PRIMASK = 0) */ #else /* !CONFIG_SYS_POWER_MANAGEMENT */ /* accumulate total counter value */ clock_accumulated_count += sys_clock_hw_cycles_per_tick; /* * one more tick has occurred -- don't need to do anything special since * timer is already configured to interrupt on the following tick */ _sys_clock_tick_announce(); #endif /* CONFIG_SYS_POWER_MANAGEMENT */ extern void _ExcExit(void); _ExcExit(); }