zephyr/kernel/microkernel/k_ticker.c

179 lines
4.2 KiB
C

/*
* Copyright (c) 1997-2010, 2012-2015 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @brief Microkernel tick event handler
*
* This module implements the microkernel's tick event handler.
*/
#include <nanokernel.h>
#include <arch/cpu.h>
#include <micro_private.h>
#include <drivers/system_timer.h>
#include <microkernel.h>
#include <microkernel/ticks.h>
#include <toolchain.h>
#include <sections.h>
#include <init.h>
#ifdef CONFIG_TIMESLICING
static int32_t slice_count = (int32_t)0;
static int32_t slice_time = (int32_t)CONFIG_TIMESLICE_SIZE;
static kpriority_t slice_prio =
(kpriority_t)CONFIG_TIMESLICE_PRIORITY;
#endif /* CONFIG_TIMESLICING */
#ifdef CONFIG_TICKLESS_IDLE
/* Number of ticks elapsed that have not been announced to the microkernel */
int32_t _sys_idle_elapsed_ticks; /* Initial value must be 0 */
#endif
/**
* @internal
* @brief Task level debugging tick handler
*
* If task level debugging is configured this routine updates the low resolution
* debugging timer and determines if task level processing should be suspended.
*
* @return 0 if task level processing should be halted or 1 if not
*
*/
#ifdef CONFIG_TASK_DEBUG
uint32_t __noinit _k_debug_sys_clock_tick_count;
static inline int _TlDebugUpdate(int32_t ticks)
{
_k_debug_sys_clock_tick_count += ticks;
return !_k_debug_halt;
}
#else
#define _TlDebugUpdate(ticks) 1
#endif
/**
* @internal
* @brief Tick handler time slice logic
*
* This routine checks to see if it is time for the current task
* to relinquish control, and yields CPU if so.
*
* @return N/A
*
*/
static inline void _TimeSliceUpdate(void)
{
#ifdef CONFIG_TIMESLICING
int yield = slice_time && (_k_current_task->priority >= slice_prio) &&
(++slice_count >= slice_time);
if (yield) {
slice_count = 0;
_k_task_yield(NULL);
}
#else
/* do nothing */
#endif /* CONFIG_TIMESLICING */
}
/**
* @internal
* @brief Get elapsed ticks
*
* If tickless idle support is configured this routine returns the number
* of ticks since going idle and then resets the global elapsed tick counter back
* to zero indicating all elapsed ticks have been consumed. This is done with
* interrupts locked to prevent the timer ISR from modifying the global elapsed
* tick counter.
* If tickless idle support is not configured in it simply returns 1.
*
* @return number of ticks to process
*/
static inline int32_t _SysIdleElapsedTicksGet(void)
{
#ifdef CONFIG_TICKLESS_IDLE
int32_t ticks;
int key;
key = irq_lock();
ticks = _sys_idle_elapsed_ticks;
_sys_idle_elapsed_ticks = 0;
irq_unlock(key);
return ticks;
#else
/* A single tick always elapses when not in tickless mode */
return 1;
#endif
}
/**
*
* @brief Microkernel tick handler
*
* This routine informs other microkernel subsystems that a tick event has
* occurred.
* @param even Event
* @return 1
*/
int _k_ticker(int event)
{
(void)event; /* prevent "unused argument" compiler warning */
int32_t ticks;
ticks = _SysIdleElapsedTicksGet();
_k_workload_monitor_update();
if (_TlDebugUpdate(ticks)) {
_TimeSliceUpdate();
_k_timer_list_update(ticks);
_nano_sys_clock_tick_announce(ticks);
}
return 1;
}
#ifdef CONFIG_SYS_CLOCK_EXISTS
static void _sys_clock_tick_announce_pre_micro_nop(kevent_t e)
{
ARG_UNUSED(e);
/* do nothing */
}
void (*_do_sys_clock_tick_announce)(kevent_t) =
_sys_clock_tick_announce_pre_micro_nop;
static int _sys_clock_tick_announce_install(struct device *dev)
{
ARG_UNUSED(dev);
_do_sys_clock_tick_announce = isr_event_send;
return 0;
}
SYS_INIT(_sys_clock_tick_announce_install, MICROKERNEL, 0);
#endif /* CONFIG_SYS_CLOCK_EXISTS */
#ifdef CONFIG_TIMESLICING
void sys_scheduler_time_slice_set(int32_t t, kpriority_t p)
{
slice_time = t;
slice_prio = p;
}
#endif /* CONFIG_TIMESLICING */