1 /*
2 * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #pragma once
7
8 #include <stdint.h>
9 #include "soc/soc_caps.h"
10 #include "xtensa/config/core-isa.h"
11 #include "xtensa/config/core.h"
12 #include "xtensa/config/extreg.h"
13 #include "xtensa/config/specreg.h"
14 #include "xtensa/xtruntime.h"
15 #include "xt_instr_macros.h"
16 #include "esp_bit_defs.h"
17 #include "esp_attr.h"
18
19 #ifdef __cplusplus
20 extern "C" {
21 #endif
22
23 /* -------------------------------------------------- CPU Registers ----------------------------------------------------
24 *
25 * ------------------------------------------------------------------------------------------------------------------ */
26
xt_utils_get_core_id(void)27 FORCE_INLINE_ATTR __attribute__((pure)) uint32_t xt_utils_get_core_id(void)
28 {
29 /*
30 Note: We depend on SOC_CPU_CORES_NUM instead of XCHAL_HAVE_PRID as some single Xtensa targets (such as ESP32-S2) have
31 the PRID register even though they are single core.
32 */
33 #if SOC_CPU_CORES_NUM > 1
34 // Read and extract bit 13 of special register PRID
35 uint32_t id;
36 __asm__ volatile (
37 "rsr.prid %0\n"
38 "extui %0,%0,13,1"
39 :"=r"(id));
40 return id;
41 #else
42 return 0;
43 #endif // SOC_CPU_CORES_NUM > 1
44 }
45
xt_utils_get_raw_core_id(void)46 FORCE_INLINE_ATTR __attribute__((pure)) uint32_t xt_utils_get_raw_core_id(void)
47 {
48 #if XCHAL_HAVE_PRID
49 // Read the raw value of special register PRID
50 uint32_t id;
51 __asm__ volatile (
52 "rsr.prid %0\n"
53 :"=r"(id));
54 return id;
55 #else
56 return 0;
57 #endif // XCHAL_HAVE_PRID
58 }
59
xt_utils_get_sp(void)60 FORCE_INLINE_ATTR void *xt_utils_get_sp(void)
61 {
62 void *sp;
63 __asm__ volatile ("mov %0, sp;" : "=r" (sp));
64 return sp;
65 }
66
xt_utils_get_cycle_count(void)67 FORCE_INLINE_ATTR uint32_t xt_utils_get_cycle_count(void)
68 {
69 uint32_t ccount;
70 RSR(CCOUNT, ccount);
71 return ccount;
72 }
73
xt_utils_set_cycle_count(uint32_t ccount)74 static inline void xt_utils_set_cycle_count(uint32_t ccount)
75 {
76 WSR(CCOUNT, ccount);
77 }
78
xt_utils_wait_for_intr(void)79 FORCE_INLINE_ATTR void xt_utils_wait_for_intr(void)
80 {
81 __asm__ volatile ("waiti 0\n");
82 }
83
84 /* ------------------------------------------------- CPU Interrupts ----------------------------------------------------
85 *
86 * ------------------------------------------------------------------------------------------------------------------ */
87
88 // ---------------- Interrupt Descriptors ------------------
89
90 // --------------- Interrupt Configuration -----------------
91
xt_utils_set_vecbase(uint32_t vecbase)92 FORCE_INLINE_ATTR void xt_utils_set_vecbase(uint32_t vecbase)
93 {
94 __asm__ volatile ("wsr %0, vecbase" :: "r" (vecbase));
95 }
96
97 // ------------------ Interrupt Control --------------------
98
xt_utils_intr_get_enabled_mask(void)99 FORCE_INLINE_ATTR uint32_t xt_utils_intr_get_enabled_mask(void)
100 {
101 uint32_t intr_mask;
102 RSR(INTENABLE, intr_mask);
103 return intr_mask;
104 }
105
106 /* -------------------------------------------------- Memory Ports -----------------------------------------------------
107 *
108 * ------------------------------------------------------------------------------------------------------------------ */
109
110 /* ---------------------------------------------------- Debugging ------------------------------------------------------
111 *
112 * ------------------------------------------------------------------------------------------------------------------ */
113
114 // --------------- Breakpoints/Watchpoints -----------------
115
xt_utils_set_breakpoint(int bp_num,uint32_t bp_addr)116 FORCE_INLINE_ATTR void xt_utils_set_breakpoint(int bp_num, uint32_t bp_addr)
117 {
118 //Set the breakpoint's address
119 if (bp_num == 1) {
120 WSR(IBREAKA_1, bp_addr);
121 } else {
122 WSR(IBREAKA_0, bp_addr);
123 }
124 //Enable the breakpoint
125 uint32_t brk_ena_reg;
126 RSR(IBREAKENABLE, brk_ena_reg);
127 brk_ena_reg |= BIT(bp_num);
128 WSR(IBREAKENABLE, brk_ena_reg);
129 }
130
xt_utils_clear_breakpoint(int bp_num)131 FORCE_INLINE_ATTR void xt_utils_clear_breakpoint(int bp_num)
132 {
133 // Disable the breakpoint using the break enable register
134 uint32_t bp_en = 0;
135 RSR(IBREAKENABLE, bp_en);
136 bp_en &= ~BIT(bp_num);
137 WSR(IBREAKENABLE, bp_en);
138 // Zero the break address register
139 uint32_t bp_addr = 0;
140 if (bp_num == 1) {
141 WSR(IBREAKA_1, bp_addr);
142 } else {
143 WSR(IBREAKA_0, bp_addr);
144 }
145 }
146
xt_utils_set_watchpoint(int wp_num,uint32_t wp_addr,size_t size,bool on_read,bool on_write)147 FORCE_INLINE_ATTR void xt_utils_set_watchpoint(int wp_num,
148 uint32_t wp_addr,
149 size_t size,
150 bool on_read,
151 bool on_write)
152 {
153 // Initialize DBREAKC bits (see Table 4–143 or isa_rm.pdf)
154 uint32_t dbreakc_reg = 0x3F;
155 dbreakc_reg = dbreakc_reg << (__builtin_ffsll(size) - 1);
156 dbreakc_reg = dbreakc_reg & 0x3F;
157 if (on_read) {
158 dbreakc_reg |= BIT(30);
159 }
160 if (on_write) {
161 dbreakc_reg |= BIT(31);
162 }
163 // Enable break address and break control register
164 if (wp_num == 1) {
165 WSR(DBREAKA_1, (uint32_t) wp_addr);
166 WSR(DBREAKC_1, dbreakc_reg);
167 } else {
168 WSR(DBREAKA_0, (uint32_t) wp_addr);
169 WSR(DBREAKC_0, dbreakc_reg);
170 }
171 }
172
xt_utils_clear_watchpoint(int wp_num)173 FORCE_INLINE_ATTR void xt_utils_clear_watchpoint(int wp_num)
174 {
175 // Clear both break control and break address register
176 if (wp_num == 1) {
177 WSR(DBREAKC_1, 0);
178 WSR(DBREAKA_1, 0);
179 } else {
180 WSR(DBREAKC_0, 0);
181 WSR(DBREAKA_0, 0);
182 }
183 }
184
185 // ---------------------- Debugger -------------------------
186
xt_utils_dbgr_is_attached(void)187 FORCE_INLINE_ATTR bool xt_utils_dbgr_is_attached(void)
188 {
189 uint32_t dcr = 0;
190 uint32_t reg = DSRSET;
191 RER(reg, dcr);
192 return (bool)(dcr & 0x1);
193 }
194
xt_utils_dbgr_break(void)195 FORCE_INLINE_ATTR void xt_utils_dbgr_break(void)
196 {
197 __asm__ ("break 1,15");
198 }
199
200 /* ------------------------------------------------------ Misc ---------------------------------------------------------
201 *
202 * ------------------------------------------------------------------------------------------------------------------ */
203
xt_utils_compare_and_set(volatile uint32_t * addr,uint32_t compare_value,uint32_t new_value)204 FORCE_INLINE_ATTR bool xt_utils_compare_and_set(volatile uint32_t *addr, uint32_t compare_value, uint32_t new_value)
205 {
206 #if XCHAL_HAVE_S32C1I
207 #ifdef __clang_analyzer__
208 //Teach clang-tidy that "addr" cannot be const as it can be updated by S32C1I instruction
209 volatile uint32_t temp;
210 temp = *addr;
211 *addr = temp;
212 #endif
213 // Atomic compare and set using S32C1I instruction
214 uint32_t old_value = new_value;
215 __asm__ __volatile__ (
216 "WSR %2, SCOMPARE1 \n"
217 "S32C1I %0, %1, 0 \n"
218 :"=r"(old_value)
219 :"r"(addr), "r"(compare_value), "0"(old_value)
220 );
221
222 return (old_value == compare_value);
223 #else // XCHAL_HAVE_S32C1I
224 // Single core target has no atomic CAS instruction. We can achieve atomicity by disabling interrupts
225 uint32_t intr_level;
226 __asm__ __volatile__ ("rsil %0, " XTSTR(XCHAL_EXCM_LEVEL) "\n"
227 : "=r"(intr_level));
228 // Compare and set
229 uint32_t old_value;
230 old_value = *addr;
231 if (old_value == compare_value) {
232 *addr = new_value;
233 }
234 // Restore interrupts
235 __asm__ __volatile__ ("memw \n"
236 "wsr %0, ps\n"
237 :: "r"(intr_level));
238
239 return (old_value == compare_value);
240 #endif // XCHAL_HAVE_S32C1I
241 }
242
243 #ifdef __cplusplus
244 }
245 #endif
246