zephyr/kernel/unified/int_latency_bench.c
Kumar Gala d12d8af186 unified: move code from nanokernel into unified kernel
As the unified kernel should replace the nanokernel and microkernel
lets go ahead and move code shared between the nanonkernel and unified
kernel into the unified kernel.

Change-Id: I8931efa5d67025381d5d0d9563e7c6632cece87f
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
2016-10-10 00:46:40 +00:00

236 lines
6.7 KiB
C

/* int_latency_bench.c - interrupt latency benchmark support */
/*
* Copyright (c) 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.
*/
#include "toolchain.h"
#include "sections.h"
#include <stdint.h> /* uint32_t */
#include <limits.h> /* ULONG_MAX */
#include <misc/printk.h> /* printk */
#include <sys_clock.h>
#include <drivers/system_timer.h>
#define NB_CACHE_WARMING_DRY_RUN 7
/*
* Timestamp corresponding to when interrupt were turned off.
* A value of zero indicated interrupt are not currently locked.
*/
static uint32_t int_locked_timestamp;
/* stats tracking the minimum and maximum time when interrupts were locked */
static uint32_t int_locked_latency_min = ULONG_MAX;
static uint32_t int_locked_latency_max;
/* overhead added to intLock/intUnlock by this latency benchmark */
static uint32_t initial_start_delay;
static uint32_t nesting_delay;
static uint32_t stop_delay;
/* counter tracking intLock/intUnlock calls once interrupt are locked */
static uint32_t int_lock_unlock_nest;
/* indicate if the interrupt latency benchamrk is ready to be used */
static uint32_t int_latency_bench_ready;
/* min amount of time it takes from HW interrupt generation to 'C' handler */
uint32_t _hw_irq_to_c_handler_latency = ULONG_MAX;
/**
*
* @brief Start tracking time spent with interrupts locked
*
* calls to lock interrupt can nest, so this routine can be called numerous
* times before interrupt are unlocked
*
* @return N/A
*
*/
void _int_latency_start(void)
{
/* when interrupts are not already locked, take time stamp */
if (!int_locked_timestamp && int_latency_bench_ready) {
int_locked_timestamp = sys_cycle_get_32();
int_lock_unlock_nest = 0;
}
int_lock_unlock_nest++;
}
/**
*
* @brief Stop accumulating time spent for when interrupts are locked
*
* This is only call once when the interrupt are being reenabled
*
* @return N/A
*
*/
void _int_latency_stop(void)
{
uint32_t delta;
uint32_t delayOverhead;
uint32_t currentTime = sys_cycle_get_32();
/* ensured intLatencyStart() was invoked first */
if (int_locked_timestamp) {
/*
* time spent with interrupt lock is:
* (current time - time when interrupt got disabled first) -
* (delay when invoking start + number nested calls to intLock *
* time it takes to call intLatencyStart + intLatencyStop)
*/
delta = (currentTime - int_locked_timestamp);
/*
* Substract overhead introduce by the int latency benchmark
* only if
* it is bigger than delta. It can be possible sometimes for
* delta to
* be smaller than the estimated overhead.
*/
delayOverhead =
(initial_start_delay +
((int_lock_unlock_nest - 1) * nesting_delay) + stop_delay);
if (delta >= delayOverhead)
delta -= delayOverhead;
/* update max */
if (delta > int_locked_latency_max)
int_locked_latency_max = delta;
/* update min */
if (delta < int_locked_latency_min)
int_locked_latency_min = delta;
/* interrupts are now enabled, get ready for next interrupt lock
*/
int_locked_timestamp = 0;
}
}
/**
*
* @brief Initialize interrupt latency benchmark
*
* @return N/A
*
*/
void int_latency_init(void)
{
uint32_t timeToReadTime;
uint32_t cacheWarming = NB_CACHE_WARMING_DRY_RUN;
int_latency_bench_ready = 1;
/*
* measuring delay introduced by the interrupt latency benchmark few
* times to ensure we get the best possible values. The overhead of
* invoking the latency can changes runtime (i.e. cache hit or miss)
* but an estimated overhead is used to adjust Max interrupt latency.
* The overhead introduced by benchmark is composed of three values:
* initial_start_delay, nesting_delay, stop_delay.
*/
while (cacheWarming) {
/* measure how much time it takes to read time */
timeToReadTime = sys_cycle_get_32();
timeToReadTime = sys_cycle_get_32() - timeToReadTime;
/* measure time to call intLatencyStart() and intLatencyStop
* takes
*/
initial_start_delay = sys_cycle_get_32();
_int_latency_start();
initial_start_delay =
sys_cycle_get_32() - initial_start_delay - timeToReadTime;
nesting_delay = sys_cycle_get_32();
_int_latency_start();
nesting_delay = sys_cycle_get_32() - nesting_delay - timeToReadTime;
stop_delay = sys_cycle_get_32();
_int_latency_stop();
stop_delay = sys_cycle_get_32() - stop_delay - timeToReadTime;
/* re-initialize globals to default values */
int_locked_latency_min = ULONG_MAX;
int_locked_latency_max = 0;
cacheWarming--;
}
}
/**
*
* @brief Dumps interrupt latency values
*
* The interrupt latency value measures
*
* @return N/A
*
*/
void int_latency_show(void)
{
uint32_t intHandlerLatency = 0;
if (!int_latency_bench_ready) {
printk("error: int_latency_init() has not been invoked\n");
return;
}
if (int_locked_latency_min != ULONG_MAX) {
if (_hw_irq_to_c_handler_latency == ULONG_MAX) {
intHandlerLatency = 0;
printk(" Min latency from hw interrupt up to 'C' int. "
"handler: "
"not measured\n");
} else {
intHandlerLatency = _hw_irq_to_c_handler_latency;
printk(" Min latency from hw interrupt up to 'C' int. "
"handler:"
" %d tcs = %d nsec\n",
intHandlerLatency,
SYS_CLOCK_HW_CYCLES_TO_NS(intHandlerLatency));
}
printk(" Max interrupt latency (includes hw int. to 'C' "
"handler):"
" %d tcs = %d nsec\n",
int_locked_latency_max + intHandlerLatency,
SYS_CLOCK_HW_CYCLES_TO_NS(int_locked_latency_max + intHandlerLatency));
printk(" Overhead substracted from Max int. latency:\n"
" for int. lock : %d tcs = %d nsec\n"
" each time int. lock nest: %d tcs = %d nsec\n"
" for int. unlocked : %d tcs = %d nsec\n",
initial_start_delay,
SYS_CLOCK_HW_CYCLES_TO_NS(initial_start_delay),
nesting_delay,
SYS_CLOCK_HW_CYCLES_TO_NS(nesting_delay),
stop_delay,
SYS_CLOCK_HW_CYCLES_TO_NS(stop_delay));
} else {
printk("interrupts were not locked and unlocked yet\n");
}
/*
* Lets start with new values so that one extra long path executed
* with interrupt disabled hide smaller paths with interrupt
* disabled.
*/
int_locked_latency_min = ULONG_MAX;
int_locked_latency_max = 0;
}