/** * * @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_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 */ }