/** * * @brief Read the platform's timer hardware * * This routine returns the current time in terms of timer hardware clock * cycles. * * @return up counter of elapsed clock cycles */ uint32_t k_cycle_get_32(void) { uint32_t val; /* system clock value */ /* * The LOAPIC timer counter is a down counter. Thus to get the number * of elapsed cycles since 'accumlated_cycle_count' was last updated, * subtract the value in the Current Count Register (CCR) from the value * in the Initial Count Register (ICR). */ #if !defined(CONFIG_TICKLESS_IDLE) /* The value in the ICR always matches cycles_per_tick. */ val = accumulated_cycle_count - current_count_register_get() + cycles_per_tick; #else /* The value in the ICR may vary. Read from the register. */ val = accumulated_cycle_count - current_count_register_get() + initial_count_register_get(); #endif return val; }
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 Place system timer into idle state * * Re-program the timer to enter into the idle state for the given number of * ticks. It is placed into one shot mode where it will fire in the number of * ticks supplied or the maximum number of ticks that can be programmed into * hardware. A value of -1 means inifinite number of ticks. * * @return N/A */ void _timer_idle_enter(int32_t ticks /* system ticks */ ) { uint32_t cycles; /* * Although interrupts are disabled, the LOAPIC timer is still counting * down. Take a snapshot of current count register to get the number of * cycles remaining in the timer before it signals an interrupt and apply * that towards the one-shot calculation to maintain accuracy. * * NOTE: If entering tickless idle straddles a tick, 'programmed_cycles' * and 'programmmed_full_ticks' may be incorrect as we do not know which * side of the tick the snapshot occurred. This is not a problem as the * values will be corrected once the straddling is detected. */ cycles = current_count_register_get(); if ((ticks == TICKS_UNLIMITED) || (ticks > max_system_ticks)) { /* * The number of cycles until the timer must fire next might not fit * in the 32-bit counter register. To work around this, program * the counter to fire in the maximum number of ticks (plus any * remaining cycles). */ programmed_full_ticks = max_system_ticks; programmed_cycles = cycles + cycles_per_max_ticks; } else { programmed_full_ticks = ticks - 1; programmed_cycles = cycles + (programmed_full_ticks * cycles_per_tick); } /* Set timer to one-shot mode */ initial_count_register_set(programmed_cycles); one_shot_mode_set(); timer_mode = TIMER_MODE_ONE_SHOT; }
/** * * @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); } }
/** * * @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 */ }