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