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,
107 		"DWT implements no cycle counter. "
108 		"Cannot be used for cycle counting\n");
109 
110 	return 0;
111 }
112 
113 /**
114  * @brief Return the current value of the cycle counter
115  *
116  * This routine returns the current value of the DWT Cycle Counter (DWT.CYCCNT)
117  *
118  * @return the cycle counter value
119  */
z_arm_dwt_get_cycles(void)120 static inline uint32_t z_arm_dwt_get_cycles(void)
121 {
122 	return DWT->CYCCNT;
123 }
124 
125 /**
126  * @brief Reset and start the DWT cycle counter
127  *
128  * This routine starts the cycle counter and resets its value to zero.
129  */
z_arm_dwt_cycle_count_start(void)130 static inline void z_arm_dwt_cycle_count_start(void)
131 {
132 	DWT->CYCCNT = 0;
133 	DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
134 }
135 
136 /**
137  * @brief Enable the debug monitor handler
138  *
139  * This routine enables the DebugMonitor handler to service
140  * data watchpoint events coming from DWT. The routine sets
141  * the DebugMonitor exception priority to highest possible.
142  */
z_arm_dwt_enable_debug_monitor(void)143 static inline void z_arm_dwt_enable_debug_monitor(void)
144 {
145 	/*
146 	 * In case the CPU is left in Debug mode, the behavior will be
147 	 * unpredictable if the DebugMonitor exception is triggered. We
148 	 * assert that the CPU is in normal mode.
149 	 */
150 	__ASSERT((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) == 0,
151 		"Cannot enable DBM when CPU is in Debug mode\n");
152 
153 #if defined(CONFIG_ARMV8_M_SE) && !defined(CONFIG_ARM_NONSECURE_FIRMWARE)
154 	/*
155 	 * By design, the DebugMonitor exception is only employed
156 	 * for null-pointer dereferencing detection, and enabling
157 	 * that feature is not supported in Non-Secure builds. So
158 	 * when enabling the DebugMonitor exception, assert that
159 	 * it is not targeting the Non Secure domain.
160 	 */
161 	__ASSERT((CoreDebug->DEMCR & DCB_DEMCR_SDME_Msk) != 0,
162 		"DebugMonitor targets Non-Secure\n");
163 #endif
164 
165 	/* The DebugMonitor handler priority is set already
166 	 * to the highest value (_EXC_FAULT_PRIO) during
167 	 * system initialization.
168 	 */
169 
170 	/* Enable debug monitor exception triggered on debug events */
171 	CoreDebug->DEMCR |= CoreDebug_DEMCR_MON_EN_Msk;
172 }
173 
174 #endif /* CONFIG_CORTEX_M_DWT */
175 
176 #ifdef __cplusplus
177 }
178 #endif
179 
180 #endif /* _ASMLANGUAGE */
181 
182 #endif /* ZEPHYR_ARCH_ARM_INCLUDE_AARCH32_CORTEX_M_DWT_H_ */
183