Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | /* int_latency_bench.c - interrupt latency benchmark support */ /* * Copyright (c) 2012-2015 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 */ #include "toolchain.h" #include "sections.h" #include <zephyr/types.h> /* u32_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 u32_t int_locked_timestamp; /* stats tracking the minimum and maximum time when interrupts were locked */ static u32_t int_locked_latency_min = ULONG_MAX; static u32_t int_locked_latency_max; /* overhead added to intLock/intUnlock by this latency benchmark */ static u32_t initial_start_delay; static u32_t nesting_delay; static u32_t stop_delay; /* counter tracking intLock/intUnlock calls once interrupt are locked */ static u32_t int_lock_unlock_nest; /* indicate if the interrupt latency benchamrk is ready to be used */ static u32_t int_latency_bench_ready; /* min amount of time it takes from HW interrupt generation to 'C' handler */ u32_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 z_int_latency_start(void) { /* when interrupts are not already locked, take time stamp */ if (!int_locked_timestamp && int_latency_bench_ready) { int_locked_timestamp = k_cycle_get_32(); int_lock_unlock_nest = 0U; } 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 z_int_latency_stop(void) { u32_t delta; u32_t delayOverhead; u32_t currentTime = k_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 = 0U; } } /** * * @brief Initialize interrupt latency benchmark * * @return N/A * */ void int_latency_init(void) { u32_t timeToReadTime; u32_t cacheWarming = NB_CACHE_WARMING_DRY_RUN; int_latency_bench_ready = 1U; /* * 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 = k_cycle_get_32(); timeToReadTime = k_cycle_get_32() - timeToReadTime; /* measure time to call intLatencyStart() and intLatencyStop * takes */ initial_start_delay = k_cycle_get_32(); z_int_latency_start(); initial_start_delay = k_cycle_get_32() - initial_start_delay - timeToReadTime; nesting_delay = k_cycle_get_32(); z_int_latency_start(); nesting_delay = k_cycle_get_32() - nesting_delay - timeToReadTime; stop_delay = k_cycle_get_32(); z_int_latency_stop(); stop_delay = k_cycle_get_32() - stop_delay - timeToReadTime; /* re-initialize globals to default values */ int_locked_latency_min = ULONG_MAX; int_locked_latency_max = 0U; cacheWarming--; } } /** * * @brief Dumps interrupt latency values * * The interrupt latency value measures * * @return N/A * */ void int_latency_show(void) { u32_t intHandlerLatency = 0U; if (int_latency_bench_ready == 0U) { 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 = 0U; 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 = 0U; } |