|
| 1 | +/* |
| 2 | + * Copyright (c) 2020 Nordic Semiconductor ASA |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +/** |
| 8 | + * @file |
| 9 | + * @brief ARM Cortex-M Timing functions interface based on DWT |
| 10 | + * |
| 11 | + */ |
| 12 | + |
| 13 | +#include <init.h> |
| 14 | +#include <timing/timing.h> |
| 15 | +#include <arch/arm/aarch32/cortex_m/cmsis.h> |
| 16 | + |
| 17 | + |
| 18 | +/** |
| 19 | + * @brief Initialize and Enable the DWT cycle counter |
| 20 | + * |
| 21 | + * This routine enables the cycle counter and initializes its value to zero. |
| 22 | + * |
| 23 | + * @return 0 |
| 24 | + */ |
| 25 | +static inline int z_arm_dwt_init(struct device *arg) |
| 26 | +{ |
| 27 | + /* Enable tracing */ |
| 28 | + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; |
| 29 | + |
| 30 | + /* Clear and enable the cycle counter */ |
| 31 | + DWT->CYCCNT = 0; |
| 32 | + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; |
| 33 | + |
| 34 | + /* Assert that the cycle counter is indeed implemented. */ |
| 35 | + __ASSERT(DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk != 0, |
| 36 | + "DWT implements no cycle counter. " |
| 37 | + "Cannot be used for cycle counting\n"); |
| 38 | + |
| 39 | + return 0; |
| 40 | +} |
| 41 | + |
| 42 | +/** |
| 43 | + * @brief Return the current value of the cycle counter |
| 44 | + * |
| 45 | + * This routine returns the current value of the DWT Cycle Counter (DWT.CYCCNT) |
| 46 | + * |
| 47 | + * @return the cycle counter value |
| 48 | + */ |
| 49 | +static inline uint32_t z_arm_dwt_get_cycles(void) |
| 50 | +{ |
| 51 | + return DWT->CYCCNT; |
| 52 | +} |
| 53 | + |
| 54 | +/** |
| 55 | + * @brief Reset and start the DWT cycle counter |
| 56 | + * |
| 57 | + * This routine starts the cycle counter and resets its value to zero. |
| 58 | + * |
| 59 | + * @return 0 |
| 60 | + */ |
| 61 | +static inline void z_arm_dwt_cycle_count_start(void) |
| 62 | +{ |
| 63 | + DWT->CYCCNT = 0; |
| 64 | + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; |
| 65 | +} |
| 66 | + |
| 67 | +/** |
| 68 | + * @brief Return the current frequency of the cycle counter |
| 69 | + * |
| 70 | + * This routine returns the current frequency of the DWT Cycle Counter |
| 71 | + * in DWT cycles per second (Hz). |
| 72 | + * |
| 73 | + * @return the cycle counter frequency value |
| 74 | + */ |
| 75 | +static inline uint64_t z_arm_dwt_freq_get(void) |
| 76 | +{ |
| 77 | +#if defined(CONFIG_SOC_FAMILY_NRF) |
| 78 | + /* |
| 79 | + * DWT frequency is taken directly from the |
| 80 | + * System Core clock (CPU) frequency, if the |
| 81 | + * CMSIS SystemCoreClock symbols is available. |
| 82 | + */ |
| 83 | + SystemCoreClockUpdate(); |
| 84 | + |
| 85 | + return SystemCoreClock; |
| 86 | +#elif defined(CONFIG_CORTEX_M_SYSTICK) |
| 87 | + /* SysTick and DWT both run at CPU frequency, |
| 88 | + * reflected in the system timer HW cycles/sec. |
| 89 | + */ |
| 90 | + return CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; |
| 91 | +#else |
| 92 | + static uint64_t dwt_frequency; |
| 93 | + |
| 94 | + if (!dwt_frequency) { |
| 95 | + |
| 96 | + z_arm_dwt_init(NULL); |
| 97 | + |
| 98 | + uint32_t cyc_start = k_cycle_get_32(); |
| 99 | + uint64_t dwt_start = z_arm_dwt_get_cycles(); |
| 100 | + |
| 101 | + k_busy_wait(10 * USEC_PER_MSEC); |
| 102 | + |
| 103 | + uint32_t cyc_end = k_cycle_get_32(); |
| 104 | + uint64_t dwt_end = z_arm_dwt_get_cycles(); |
| 105 | + |
| 106 | + uint64_t cyc_freq = sys_clock_hw_cycles_per_sec(); |
| 107 | + |
| 108 | + /* |
| 109 | + * cycles are in 32-bit, and delta must be |
| 110 | + * calculated in 32-bit percision. Or it would |
| 111 | + * wrapping around in 64-bit. |
| 112 | + */ |
| 113 | + uint64_t dcyc = (uint32_t)cyc_end - (uint32_t)cyc_start; |
| 114 | + |
| 115 | + uint64_t dtsc = dwt_end - dwt_start; |
| 116 | + |
| 117 | + dwt_frequency = (cyc_freq * dtsc) / dcyc; |
| 118 | + |
| 119 | + } |
| 120 | + return dwt_frequency; |
| 121 | +#endif /* CONFIG_SOC_FAMILY_NRF */ |
| 122 | +} |
| 123 | + |
| 124 | +void timing_init(void) |
| 125 | +{ |
| 126 | + z_arm_dwt_init(NULL); |
| 127 | +} |
| 128 | + |
| 129 | +void timing_start(void) |
| 130 | +{ |
| 131 | + z_arm_dwt_cycle_count_start(); |
| 132 | +} |
| 133 | + |
| 134 | +void timing_stop(void) |
| 135 | +{ |
| 136 | + DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; |
| 137 | +} |
| 138 | + |
| 139 | +timing_t timing_counter_get(void) |
| 140 | +{ |
| 141 | + return (timing_t)z_arm_dwt_get_cycles(); |
| 142 | +} |
| 143 | + |
| 144 | +uint64_t timing_cycles_get(volatile timing_t *const start, |
| 145 | + volatile timing_t *const end) |
| 146 | +{ |
| 147 | + return (*end - *start); |
| 148 | +} |
| 149 | + |
| 150 | +uint64_t timing_freq_get(void) |
| 151 | +{ |
| 152 | + return z_arm_dwt_freq_get(); |
| 153 | +} |
| 154 | + |
| 155 | +uint64_t timing_cycles_to_ns(uint64_t cycles) |
| 156 | +{ |
| 157 | + return (cycles) * (NSEC_PER_USEC) / timing_freq_get_mhz(); |
| 158 | +} |
| 159 | + |
| 160 | +uint64_t timing_cycles_to_ns_avg(uint64_t cycles, uint32_t count) |
| 161 | +{ |
| 162 | + return timing_cycles_to_ns(cycles) / count; |
| 163 | +} |
| 164 | + |
| 165 | +uint32_t timing_freq_get_mhz(void) |
| 166 | +{ |
| 167 | + return (uint32_t)(timing_freq_get() / 1000000); |
| 168 | +} |
0 commit comments