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  */
36 #if !defined DWT_LSR_Present_Msk
37 #define DWT_LSR_Present_Msk ITM_LSR_Present_Msk
38 #endif
39 #if !defined DWT_LSR_Access_Msk
40 #define DWT_LSR_Access_Msk ITM_LSR_Access_Msk
41 #endif
42 
dwt_access(bool ena)43 static inline void dwt_access(bool ena)
44 {
45 #if defined(CONFIG_CPU_CORTEX_M7)
46 	/*
47 	 * In case of Cortex M7, we need to check the optional presence of
48 	 * Lock Access Register (LAR) which is indicated in Lock Status
49 	 * Register (LSR). When present, a special access token must be written
50 	 * to unlock DWT registers.
51 	 */
52 	uint32_t lsr = DWT->LSR;
53 
54 	if ((lsr & DWT_LSR_Present_Msk) != 0) {
55 		if (ena) {
56 			if ((lsr & DWT_LSR_Access_Msk) != 0) {
57 				/* Access is locked. unlock it */
58 				DWT->LAR = 0xC5ACCE55;
59 			}
60 		} else {
61 			if ((lsr & DWT_LSR_Access_Msk) == 0) {
62 				/* Access is unlocked. Lock it */
63 				DWT->LAR = 0;
64 			}
65 		}
66 	}
67 #else  /* CONFIG_CPU_CORTEX_M7 */
68 	ARG_UNUSED(ena);
69 #endif /* CONFIG_CPU_CORTEX_M7 */
70 }
71 
72 /**
73  * @brief Enable DWT
74  *
75  * This routine enables the DWT unit.
76  *
77  * @return 0
78  */
z_arm_dwt_init(void)79 static inline int z_arm_dwt_init(void)
80 {
81 	/* Enable tracing */
82 	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
83 
84 	/* Unlock DWT access if any */
85 	dwt_access(true);
86 
87 	return 0;
88 }
89 
90 /**
91  * @brief Initialize and Enable the DWT cycle counter
92  *
93  * This routine enables the cycle counter and initializes its value to zero.
94  *
95  * @return 0
96  */
z_arm_dwt_init_cycle_counter(void)97 static inline int z_arm_dwt_init_cycle_counter(void)
98 {
99 	/* Clear and enable the cycle counter */
100 	DWT->CYCCNT = 0;
101 	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
102 
103 	/* Assert that the cycle counter is indeed implemented.
104 	 * The field is called NOCYCCNT. So 1 means there is no cycle counter.
105 	 */
106 	__ASSERT((DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk) == 0, "DWT implements no cycle counter. "
107 							   "Cannot be used for cycle counting\n");
108 
109 	return 0;
110 }
111 
112 /**
113  * @brief Return the current value of the cycle counter
114  *
115  * This routine returns the current value of the DWT Cycle Counter (DWT.CYCCNT)
116  *
117  * @return the cycle counter value
118  */
z_arm_dwt_get_cycles(void)119 static inline uint32_t z_arm_dwt_get_cycles(void)
120 {
121 	return DWT->CYCCNT;
122 }
123 
124 /**
125  * @brief Reset and start the DWT cycle counter
126  *
127  * This routine starts the cycle counter and resets its value to zero.
128  */
z_arm_dwt_cycle_count_start(void)129 static inline void z_arm_dwt_cycle_count_start(void)
130 {
131 	DWT->CYCCNT = 0;
132 	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
133 }
134 
135 /**
136  * @brief Enable the debug monitor handler
137  *
138  * This routine enables the DebugMonitor handler to service
139  * data watchpoint events coming from DWT. The routine sets
140  * the DebugMonitor exception priority to highest possible.
141  */
z_arm_dwt_enable_debug_monitor(void)142 static inline void z_arm_dwt_enable_debug_monitor(void)
143 {
144 	/*
145 	 * In case the CPU is left in Debug mode, the behavior will be
146 	 * unpredictable if the DebugMonitor exception is triggered. We
147 	 * assert that the CPU is in normal mode.
148 	 */
149 	__ASSERT((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0,
150 		 "Cannot enable DBM when CPU is in Debug mode\n");
151 
152 #if defined(CONFIG_ARMV8_M_SE) && !defined(CONFIG_ARM_NONSECURE_FIRMWARE)
153 	/*
154 	 * By design, the DebugMonitor exception is only employed
155 	 * for null-pointer dereferencing detection, and enabling
156 	 * that feature is not supported in Non-Secure builds. So
157 	 * when enabling the DebugMonitor exception, assert that
158 	 * it is not targeting the Non Secure domain.
159 	 */
160 	__ASSERT((CoreDebug->DEMCR & DCB_DEMCR_SDME_Msk) != 0, "DebugMonitor targets Non-Secure\n");
161 #endif
162 
163 	/* The DebugMonitor handler priority is set already
164 	 * to the highest value (_EXC_FAULT_PRIO) during
165 	 * system initialization.
166 	 */
167 
168 	/* Enable debug monitor exception triggered on debug events */
169 	CoreDebug->DEMCR |= CoreDebug_DEMCR_MON_EN_Msk;
170 }
171 
172 #endif /* CONFIG_CORTEX_M_DWT */
173 
174 #ifdef __cplusplus
175 }
176 #endif
177 
178 #endif /* _ASMLANGUAGE */
179 
180 #endif /* ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_DWT_H_ */
181