Ejemplo n.º 1
0
/**
 *
 * @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
}
Ejemplo n.º 2
0
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();
}
Ejemplo n.º 3
0
/*
 * @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);
}
Ejemplo n.º 4
0
/**
 *
 * @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 */

}
Ejemplo n.º 5
0
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*/

}
Ejemplo n.º 6
0
/*
 * @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();

}
Ejemplo n.º 7
0
/**
 *
 * @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);
	}
}
Ejemplo n.º 8
0
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
}
Ejemplo n.º 9
0
/**
 *
 * @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
}
Ejemplo n.º 10
0
/**
 *
 * @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();
}
Ejemplo n.º 11
0
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;
}
Ejemplo n.º 12
0
/**
 *
 * @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 */

}
Ejemplo n.º 13
0
/**
 *
 * @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 */
}
Ejemplo n.º 14
0
/**
 *
 * @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();
}
Ejemplo n.º 15
0
/**
 *
 * @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();
}