1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA
3  * Copyright (c) 2020 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  * @brief DWT utility functions for Cortex-M CPUs
11  *
12  */
13 
14 #ifndef ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_DWT_H_
15 #define ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_DWT_H_
16 
17 #ifdef _ASMLANGUAGE
18 
19 /* nothing */
20 
21 #else
22 
23 #include <cmsis_core.h>
24 #include <zephyr/sys/__assert.h>
25 
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 
30 #if defined(CONFIG_CORTEX_M_DWT)
31 
32 /* Define DWT LSR masks which are currently not defined by the CMSIS V5.1.2.
33  * (LSR register is defined but not its bitfields).
34  * Reuse ITM LSR mask as it is the same offset than DWT LSR one.
35  * TODO: update these to use only CMSIS_6 when all of zephyr and modules have
36  * update to CMSIS_6.
37  */
38 #if !defined DWT_LSR_Present_Msk
39 #define DWT_LSR_Present_Msk ITM_LSR_PRESENT_Msk
40 #endif
41 
42 #if !defined DWT_LSR_Access_Msk
43 #define DWT_LSR_Access_Msk ITM_LSR_ACCESS_Msk
44 #endif
45 
dwt_access(bool ena)46 static inline void dwt_access(bool ena)
47 {
48 #if defined(CONFIG_CPU_CORTEX_M7)
49 	/*
50 	 * In case of Cortex M7, we need to check the optional presence of
51 	 * Lock Access Register (LAR) which is indicated in Lock Status
52 	 * Register (LSR). When present, a special access token must be written
53 	 * to unlock DWT registers.
54 	 */
55 	uint32_t lsr = DWT->LSR;
56 
57 	if ((lsr & DWT_LSR_Present_Msk) != 0) {
58 		if (ena) {
59 			if ((lsr & DWT_LSR_Access_Msk) != 0) {
60 				/* Access is locked. unlock it */
61 				DWT->LAR = 0xC5ACCE55;
62 			}
63 		} else {
64 			if ((lsr & DWT_LSR_Access_Msk) == 0) {
65 				/* Access is unlocked. Lock it */
66 				DWT->LAR = 0;
67 			}
68 		}
69 	}
70 #else  /* CONFIG_CPU_CORTEX_M7 */
71 	ARG_UNUSED(ena);
72 #endif /* CONFIG_CPU_CORTEX_M7 */
73 }
74 
75 /**
76  * @brief Enable DWT
77  *
78  * This routine enables the DWT unit.
79  *
80  * @return 0
81  */
z_arm_dwt_init(void)82 static inline int z_arm_dwt_init(void)
83 {
84 	/* Enable tracing */
85 	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
86 
87 	/* Unlock DWT access if any */
88 	dwt_access(true);
89 
90 	return 0;
91 }
92 
93 /**
94  * @brief Initialize and Enable the DWT cycle counter
95  *
96  * This routine enables the cycle counter and initializes its value to zero.
97  *
98  * @return 0
99  */
z_arm_dwt_init_cycle_counter(void)100 static inline int z_arm_dwt_init_cycle_counter(void)
101 {
102 	/* Clear and enable the cycle counter */
103 	DWT->CYCCNT = 0;
104 	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
105 
106 	/* Assert that the cycle counter is indeed implemented.
107 	 * The field is called NOCYCCNT. So 1 means there is no cycle counter.
108 	 */
109 	__ASSERT((DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk) == 0, "DWT implements no cycle counter. "
110 							   "Cannot be used for cycle counting\n");
111 
112 	return 0;
113 }
114 
115 /**
116  * @brief Return the current value of the cycle counter
117  *
118  * This routine returns the current value of the DWT Cycle Counter (DWT.CYCCNT)
119  *
120  * @return the cycle counter value
121  */
z_arm_dwt_get_cycles(void)122 static inline uint32_t z_arm_dwt_get_cycles(void)
123 {
124 	return DWT->CYCCNT;
125 }
126 
127 /**
128  * @brief Reset and start the DWT cycle counter
129  *
130  * This routine starts the cycle counter and resets its value to zero.
131  */
z_arm_dwt_cycle_count_start(void)132 static inline void z_arm_dwt_cycle_count_start(void)
133 {
134 	DWT->CYCCNT = 0;
135 	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
136 }
137 
138 /**
139  * @brief Enable the debug monitor handler
140  *
141  * This routine enables the DebugMonitor handler to service
142  * data watchpoint events coming from DWT. The routine sets
143  * the DebugMonitor exception priority to highest possible.
144  */
z_arm_dwt_enable_debug_monitor(void)145 static inline void z_arm_dwt_enable_debug_monitor(void)
146 {
147 	/*
148 	 * In case the CPU is left in Debug mode, the behavior will be
149 	 * unpredictable if the DebugMonitor exception is triggered. We
150 	 * assert that the CPU is in normal mode.
151 	 */
152 	__ASSERT((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0,
153 		 "Cannot enable DBM when CPU is in Debug mode\n");
154 
155 #if defined(CONFIG_ARMV8_M_SE) && !defined(CONFIG_ARM_NONSECURE_FIRMWARE)
156 	/*
157 	 * By design, the DebugMonitor exception is only employed
158 	 * for null-pointer dereferencing detection, and enabling
159 	 * that feature is not supported in Non-Secure builds. So
160 	 * when enabling the DebugMonitor exception, assert that
161 	 * it is not targeting the Non Secure domain.
162 	 */
163 	__ASSERT((CoreDebug->DEMCR & DCB_DEMCR_SDME_Msk) != 0, "DebugMonitor targets Non-Secure\n");
164 #endif
165 
166 	/* The DebugMonitor handler priority is set already
167 	 * to the highest value (_EXC_FAULT_PRIO) during
168 	 * system initialization.
169 	 */
170 
171 	/* Enable debug monitor exception triggered on debug events */
172 	CoreDebug->DEMCR |= CoreDebug_DEMCR_MON_EN_Msk;
173 }
174 
175 #endif /* CONFIG_CORTEX_M_DWT */
176 
177 #ifdef __cplusplus
178 }
179 #endif
180 
181 #endif /* _ASMLANGUAGE */
182 
183 #endif /* ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_DWT_H_ */
184