1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  * Copyright (c) 2020 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief ARM Cortex-M Timing functions interface based on DWT
11  *
12  */
13 
14 #include <zephyr/init.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/timing/timing.h>
17 #include <cortex_m/dwt.h>
18 #include <cmsis_core.h>
19 #include <zephyr/sys_clock.h>
20 
21 /**
22  * @brief Return the current frequency of the cycle counter
23  *
24  * This routine returns the current frequency of the DWT Cycle Counter
25  * in DWT cycles per second (Hz).
26  *
27  * @return the cycle counter frequency value
28  */
z_arm_dwt_freq_get(void)29 static inline uint64_t z_arm_dwt_freq_get(void)
30 {
31 #if defined(CONFIG_SOC_FAMILY_NORDIC_NRF) || \
32 	defined(CONFIG_SOC_SERIES_IMXRT6XX)
33 	/*
34 	 * DWT frequency is taken directly from the
35 	 * System Core clock (CPU) frequency, if the
36 	 * CMSIS SystemCoreClock symbols is available.
37 	 */
38 	SystemCoreClockUpdate();
39 
40 	return SystemCoreClock;
41 #elif defined(CONFIG_CORTEX_M_SYSTICK)
42 	/* SysTick and DWT both run at CPU frequency,
43 	 * reflected in the system timer HW cycles/sec.
44 	 */
45 	return sys_clock_hw_cycles_per_sec();
46 #else
47 	static uint64_t dwt_frequency;
48 	uint32_t cyc_start, cyc_end;
49 	uint64_t dwt_start, dwt_end;
50 	uint64_t cyc_freq = sys_clock_hw_cycles_per_sec();
51 	uint64_t dcyc, ddwt;
52 
53 	if (!dwt_frequency) {
54 
55 		z_arm_dwt_init();
56 
57 		do {
58 			cyc_start = k_cycle_get_32();
59 			dwt_start = z_arm_dwt_get_cycles();
60 
61 			k_busy_wait(10 * USEC_PER_MSEC);
62 
63 			cyc_end = k_cycle_get_32();
64 			dwt_end = z_arm_dwt_get_cycles();
65 
66 			/*
67 			 * cycles are in 32-bit, and delta must be
68 			 * calculated in 32-bit precision. Or it would be
69 			 * wrapping around in 64-bit.
70 			 */
71 			dcyc = (uint32_t)cyc_end - (uint32_t)cyc_start;
72 
73 			ddwt = dwt_end - dwt_start;
74 		} while ((dcyc == 0) || (ddwt == 0));
75 
76 		dwt_frequency = (cyc_freq * ddwt) / dcyc;
77 
78 	}
79 	return dwt_frequency;
80 #endif /* CONFIG_SOC_FAMILY_NORDIC_NRF */
81 }
82 
arch_timing_init(void)83 void arch_timing_init(void)
84 {
85 	z_arm_dwt_init();
86 	z_arm_dwt_init_cycle_counter();
87 }
88 
arch_timing_start(void)89 void arch_timing_start(void)
90 {
91 	z_arm_dwt_cycle_count_start();
92 }
93 
arch_timing_stop(void)94 void arch_timing_stop(void)
95 {
96 	DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
97 }
98 
arch_timing_counter_get(void)99 timing_t arch_timing_counter_get(void)
100 {
101 	return (timing_t)z_arm_dwt_get_cycles();
102 }
103 
arch_timing_cycles_get(volatile timing_t * const start,volatile timing_t * const end)104 uint64_t arch_timing_cycles_get(volatile timing_t *const start,
105 				volatile timing_t *const end)
106 {
107 	return (*end - *start);
108 }
109 
arch_timing_freq_get(void)110 uint64_t arch_timing_freq_get(void)
111 {
112 	return z_arm_dwt_freq_get();
113 }
114 
arch_timing_cycles_to_ns(uint64_t cycles)115 uint64_t arch_timing_cycles_to_ns(uint64_t cycles)
116 {
117 	return (cycles) * (NSEC_PER_USEC) / arch_timing_freq_get_mhz();
118 }
119 
arch_timing_cycles_to_ns_avg(uint64_t cycles,uint32_t count)120 uint64_t arch_timing_cycles_to_ns_avg(uint64_t cycles, uint32_t count)
121 {
122 	return arch_timing_cycles_to_ns(cycles) / count;
123 }
124 
arch_timing_freq_get_mhz(void)125 uint32_t arch_timing_freq_get_mhz(void)
126 {
127 	return (uint32_t)(arch_timing_freq_get() / 1000000U);
128 }
129