From e3ee55f1932041cefe3e2ebba49207aa9805e51e Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 23 Mar 2015 13:13:04 -0500 Subject: Add efficient time scaling support Also removed automatic rescheduling of periodic timers Change-Id: I6427a8d8ed5ca4b75389c3610a16dba10783a8ae Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/16537 Reviewed-by: Glenn R. Miles Tested-by: Glenn R. Miles --- pk/kernel/pk_api.h | 36 +++++++++---------- pk/kernel/pk_init.c | 49 ++++++++++++++++++++++---- pk/kernel/pk_semaphore_core.c | 4 ++- pk/kernel/pk_thread_core.c | 2 +- pk/kernel/pk_timer_core.c | 80 ++++++------------------------------------- pk/kernel/pk_timer_init.c | 1 - pk/trace/pk_trace_core.c | 4 +-- 7 files changed, 77 insertions(+), 99 deletions(-) (limited to 'pk') diff --git a/pk/kernel/pk_api.h b/pk/kernel/pk_api.h index 51440275..3d0c4a3f 100644 --- a/pk/kernel/pk_api.h +++ b/pk/kernel/pk_api.h @@ -298,11 +298,22 @@ #define PK_TIMEBASE_FREQUENCY_HZ __pk_timebase_frequency_hz +/// This is the unscaled timebase frequency in Hz. +#ifdef APPCFG_USE_EXT_TIMEBASE +#define PK_BASE_FREQ_HZ 25000000 +#else +#define PK_BASE_FREQ_HZ 400000000 +#endif /* APPCFG_USE_EXT_TIMEBASE */ + +/// Scale a time interval to be _closer_ to what was actually requested +/// base on the actual timebase frequency. +#define PK_INTERVAL_SCALE(interval) ((interval) + ((interval) >> __pk_timebase_rshift)) + /// Convert a time in integral seconds to a time interval - overflows are /// ignored. The application can redefine this macro. #ifndef PK_SECONDS -#define PK_SECONDS(s) ((PkInterval)(__pk_timebase_frequency_hz * (PkInterval)(s))) +#define PK_SECONDS(s) ((PkInterval)(PK_BASE_FREQ_HZ * (PkInterval)(s))) #endif /// Convert a time in integral milliseconds to a time interval - overflows are @@ -310,7 +321,7 @@ /// assumed. The application can redefine this macro. #ifndef PK_MILLISECONDS -#define PK_MILLISECONDS(m) ((PkInterval)(__pk_timebase_frequency_khz * (PkInterval)(m))) +#define PK_MILLISECONDS(m) ((PkInterval)((PK_BASE_FREQ_HZ * (PkInterval)(m)) / 1000)) #endif /// Convert a time in integral microseconds to a time interval - overflows are @@ -318,7 +329,7 @@ /// assumed. The application can redefine this macro. #ifndef PK_MICROSECONDS -#define PK_MICROSECONDS(u) ((PkInterval)(__pk_timebase_frequency_mhz * (PkInterval)(u))) +#define PK_MICROSECONDS(u) ((PkInterval)((PK_BASE_FREQ_HZ * (PkInterval)(u)) / 1000000)) #endif /// Convert a time in integral nanoseconds to a time interval - overflows are @@ -326,8 +337,7 @@ /// assumed. The application can redefine this macro. #ifndef PK_NANOSECONDS -#define PK_NANOSECONDS(n) \ - ((PkInterval)((__pk_timebase_frequency_mhz * (PkInterval)(n)) / 1000)) +#define PK_NANOSECONDS(n) ((PkInterval)((PK_BASE_FREQ_HZ * (PkInterval)(n)) / 1000000000)) #endif @@ -437,6 +447,8 @@ /// The timebase frequency in Hz; A parameter to pk_initialize() extern uint32_t __pk_timebase_frequency_hz; +extern uint8_t __pk_timebase_rshift; + /// The timebase frequency in KHz extern uint32_t __pk_timebase_frequency_khz; @@ -579,12 +591,6 @@ typedef struct PkTimer { /// The absolute timeout of the timer. PkTimebase timeout; - /// The timer period - /// - /// If not 0, then this is a periodic timer and it will be automatically - /// rescheduled in absolute time from the previous timeout. - PkInterval period; - /// The timer callback /// /// For PK thread timers used to implement Sleep and semaphore pend @@ -691,15 +697,9 @@ pk_timer_create_nonpreemptible(PkTimer *timer, PkTimerCallback callback, void *arg); -int -pk_timer_schedule_absolute(PkTimer *timer, - PkTimebase time, - PkInterval period); - int pk_timer_schedule(PkTimer *timer, - PkInterval interval, - PkInterval period); + PkInterval interval); int pk_timer_cancel(PkTimer *timer); diff --git a/pk/kernel/pk_init.c b/pk/kernel/pk_init.c index 2ac06f33..0add4214 100644 --- a/pk/kernel/pk_init.c +++ b/pk/kernel/pk_init.c @@ -15,8 +15,46 @@ #include "pk_trace.h" uint32_t __pk_timebase_frequency_hz; -uint32_t __pk_timebase_frequency_khz; -uint32_t __pk_timebase_frequency_mhz; + +/// The timebase frequency is passed into PK during initialization. It cannot +/// be set statically because there is a requirement to support a frequency +/// that can change from one IPL to the next. On the 405, scaling of time +/// intervals is accomplished by doing a 32x32 bit multiplication which is +/// supported by the ppc405 instruction set. PPE42 does not support 32x32 bit +/// multiplication directly and some applications can not afford to use a +/// function call to emulate the operation. Instead we scale the time +/// interval by shifting the value X bits to the right and adding it to itself. +/// This can scale the value by 2, 1.5, 1.25, 1.125, etc. +/// +/// This is the right shift value. +/// NOTE: shifting by 0 gives a 2x scale factor, shifting by 32 gives a 1x +/// scale factor. +uint8_t __pk_timebase_rshift = 32; + +void pk_set_timebase_rshift(uint32_t timebase_freq_hz) +{ + //Use 1.0 scale if less than halfway between 1.0 and 1.25 + if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3))) + { + __pk_timebase_rshift = 32; + } + + //use 1.25 scale if less than halfway between 1.25 and 1.5 + else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3) + (PK_BASE_FREQ_HZ >> 2))) + { + __pk_timebase_rshift = 2; + } + //use 1.5 scale if less than halfway between 1.5 and 2.0 + else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 2) + (PK_BASE_FREQ_HZ >> 1))) + { + __pk_timebase_rshift = 1; + } + //use 2.0 scale if greater than 1.5 + else + { + __pk_timebase_rshift = 0; + } +} /// Initialize PK. /// @@ -69,8 +107,6 @@ pk_initialize(PkAddress noncritical_stack, } __pk_timebase_frequency_hz = timebase_frequency_hz; - __pk_timebase_frequency_khz = timebase_frequency_hz / 1000; - __pk_timebase_frequency_mhz = timebase_frequency_hz / 1000000; __pk_thread_machine_context_default = PK_THREAD_MACHINE_CONTEXT_DEFAULT; @@ -98,12 +134,13 @@ extern PkTraceBuffer g_pk_trace_buf; // Schedule the timer that puts a 64bit timestamp in the trace buffer // periodically. This allows us to use 32bit timestamps. pk_timer_schedule(&g_pk_trace_timer, - PK_TRACE_TIMER_PERIOD, - 0); + PK_TRACE_TIMER_PERIOD); //set the trace timebase HZ g_pk_trace_buf.hz = timebase_frequency_hz; + pk_set_timebase_rshift(timebase_frequency_hz); + //set the timebase ajdustment for trace synchronization pk_trace_set_timebase(initial_timebase); diff --git a/pk/kernel/pk_semaphore_core.c b/pk/kernel/pk_semaphore_core.c index 9079ff01..d5e6d30c 100644 --- a/pk/kernel/pk_semaphore_core.c +++ b/pk/kernel/pk_semaphore_core.c @@ -147,6 +147,8 @@ pk_semaphore_pend(PkSemaphore *semaphore, PkThreadPriority priority; PkThread *thread; PkTimer *timer = 0; + PkInterval scaled_timeout = PK_INTERVAL_SCALE(timeout); + int rc = PK_OK; if (PK_ERROR_CHECK_API) { @@ -183,7 +185,7 @@ pk_semaphore_pend(PkSemaphore *semaphore, if (timeout != PK_WAIT_FOREVER) { timer = &(thread->timer); - timer->timeout = pk_timebase_get() + timeout; + timer->timeout = pk_timebase_get() + scaled_timeout; __pk_timer_schedule(timer); thread->flags |= PK_THREAD_FLAG_TIMER_PEND; } diff --git a/pk/kernel/pk_thread_core.c b/pk/kernel/pk_thread_core.c index 539b3321..56e083d4 100644 --- a/pk/kernel/pk_thread_core.c +++ b/pk/kernel/pk_thread_core.c @@ -646,7 +646,7 @@ pk_sleep_absolute(PkTimebase time) int pk_sleep(PkInterval interval) { - return pk_sleep_absolute(pk_timebase_get() + interval); + return pk_sleep_absolute(pk_timebase_get() + PK_INTERVAL_SCALE(interval)); } diff --git a/pk/kernel/pk_timer_core.c b/pk/kernel/pk_timer_core.c index abc83bad..bc90a3e7 100644 --- a/pk/kernel/pk_timer_core.c +++ b/pk/kernel/pk_timer_core.c @@ -43,8 +43,8 @@ /// also be rescheduled in place. /// /// When a timeout occurs the event list is scanned from the beginning, and -/// any event that has timed out is rescheduled if necessary (periodic events) -/// and its callback is processed. Since event and callback processing take +/// any event that has timed out has its callback processed. +/// Since event and callback processing take /// time, the list is potentially scanned multiple times until there are no /// more timed-out events in the list. /// @@ -200,23 +200,14 @@ __pk_timer_handler() if (timer->timeout <= now) { - // The timer timed out. It is removed from the queue unless - // it is a peridic timer that needs to be rescheduled. We do - // rescheduling here in the critical section to correctly - // handle timers whose callbacks may cancel the timer. The - // timer is rescheduled in absolute time. + // The timer timed out. It is removed from the queue. // // The callback may be made with interrupt preemption enabled // or disabled. However to mitigate kernel interrupt latency // we go ahead and open up to interrupts after the callback if // the callback itself was not preemptible. - if (timer->period == 0) { - pk_deque_delete(timer_deque); - } else { - timer->timeout += timer->period; - tq->next_timeout = MIN(timer->timeout, tq->next_timeout); - } + pk_deque_delete(timer_deque); callback = timer->callback; if (callback) { @@ -254,22 +245,15 @@ __pk_timer_handler() } -/// Schedule a timer in absolute time. +/// Schedule a timer for an interval relative to the current time. /// /// \param timer The PkTimer to schedule. /// -/// \param timeout The timer will be scheduled to time out at this absolute -/// time. Note that if the \a timeout is less than the current time then the -/// timer will be scheduled at a minimum timeout in the future and the -/// callback will be executed in an interrupt context. -/// -/// \param period If non-0, then when the timer times out it will rescheduled -/// to time out again at the absolute time equal to the last timeout time plus -/// the \a period. By convention a \a period of 0 indicates a one-shot -/// timer that is not rescheduled. +/// \param interval The timer will be scheduled to time out at the current +/// time (pk_timebase_get()) plus this \a interval. /// /// Once created with pk_timer_create() a timer can be \e scheduled, which -/// queues the timer in the kernel time queue. It is not an error to call +/// queues the timer in the kernel time queue. It is not an error to call \c /// pk_timer_schedule() on a timer that is already scheduled in the time /// queue - the timer is simply rescheduled with the new characteristics. /// @@ -284,23 +268,19 @@ __pk_timer_handler() /// interrupt context. int -pk_timer_schedule_absolute(PkTimer *timer, - PkTimebase timeout, - PkInterval period) - +pk_timer_schedule(PkTimer *timer, + PkInterval interval) { PkMachineContext ctx; + PkTimebase timeout = pk_timebase_get() + PK_INTERVAL_SCALE(interval); pk_critical_section_enter(&ctx); if (PK_ERROR_CHECK_API) { PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_SCHEDULE); -// PK_ERROR_IF(__pk_kernel_context_critical_interrupt(), -// PK_ILLEGAL_CONTEXT_TIMER); } timer->timeout = timeout; - timer->period = period; __pk_timer_schedule(timer); pk_critical_section_exit(&ctx); @@ -309,44 +289,6 @@ pk_timer_schedule_absolute(PkTimer *timer, } -/// Schedule a timer for an interval relative to the current time. -/// -/// \param timer The PkTimer to schedule. -/// -/// \param interval The timer will be scheduled to time out at the current -/// time (pk_timebase_get()) plus this \a interval. -/// -/// \param period If non-0, then when the timer times out it will rescheduled -/// to time out again at the absolute time equal to the last timeout time plus -/// the \a period. By convention a \a period of 0 indicates a one-shot -/// timer that is not rescheduled. -/// -/// Once created with pk_timer_create() a timer can be \e scheduled, which -/// queues the timer in the kernel time queue. It is not an error to call \c -/// pk_timer_schedule() on a timer that is already scheduled in the time -/// queue - the timer is simply rescheduled with the new characteristics. -/// -/// Return values other than PK_OK (0) are errors; see \ref pk_errors -/// -/// \retval 0 Successful completion -/// -/// \retval -PK_INVALID_TIMER_AT_SCHEDULE A a null (0) pointer was provided as -/// the \a timer argument. -/// -/// \retval -PK_ILLEGAL_CONTEXT_TIMER The call was made from a critical -/// interrupt context. - -int -pk_timer_schedule(PkTimer *timer, - PkInterval interval, - PkInterval period) -{ - return pk_timer_schedule_absolute(timer, - pk_timebase_get() + interval, - period); -} - - /// Cancel (dequeue) a timer. /// /// \param timer The PkTimer to cancel. diff --git a/pk/kernel/pk_timer_init.c b/pk/kernel/pk_timer_init.c index 6851f3d3..9b8f0e1d 100644 --- a/pk/kernel/pk_timer_init.c +++ b/pk/kernel/pk_timer_init.c @@ -27,7 +27,6 @@ _pk_timer_create(PkTimer *timer, pk_deque_element_create((PkDeque*)timer); timer->timeout = 0; - timer->period = 0; timer->callback = callback; timer->arg = arg; timer->options = options; diff --git a/pk/trace/pk_trace_core.c b/pk/trace/pk_trace_core.c index 90a1a36c..007337bc 100644 --- a/pk/trace/pk_trace_core.c +++ b/pk/trace/pk_trace_core.c @@ -26,7 +26,6 @@ PkTimer g_pk_trace_timer = { .deque.next = 0, .deque.previous = 0, .timeout = 0, - .period = 0, .callback = pk_trace_timer_callback, .arg = 0, .options = PK_TIMER_CALLBACK_PREEMPTIBLE, @@ -102,8 +101,7 @@ void pk_trace_timer_callback(void* arg) // restart the timer pk_timer_schedule(&g_pk_trace_timer, - PK_TRACE_TIMER_PERIOD, - 0); + PK_TRACE_TIMER_PERIOD); } // Use this function to synchronize the timebase between multiple PPEs. -- cgit v1.2.1