1 /*
2 * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #pragma once
8
9 #include <zephyr/kernel.h>
10
11 #include "sdkconfig.h"
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <assert.h>
15 #include "soc/soc_caps.h"
16 #ifdef __XTENSA__
17 #include "xtensa/xtensa_api.h"
18 #include "xt_utils.h"
19 #elif __riscv
20 #include "riscv/rv_utils.h"
21 #endif
22 // #include "esp_intr_alloc.h"
23 #include "esp_err.h"
24 #include "esp_attr.h"
25
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29
30 /**
31 * @brief CPU cycle count type
32 *
33 * This data type represents the CPU's clock cycle count
34 */
35 typedef uint32_t esp_cpu_cycle_count_t;
36
37 /**
38 * @brief CPU interrupt type
39 */
40 typedef enum {
41 ESP_CPU_INTR_TYPE_LEVEL = 0,
42 ESP_CPU_INTR_TYPE_EDGE,
43 ESP_CPU_INTR_TYPE_NA,
44 } esp_cpu_intr_type_t;
45
46 /**
47 * @brief CPU interrupt descriptor
48 *
49 * Each particular CPU interrupt has an associated descriptor describing that
50 * particular interrupt's characteristics. Call esp_cpu_intr_get_desc() to get
51 * the descriptors of a particular interrupt.
52 */
53 typedef struct {
54 int priority; /**< Priority of the interrupt if it has a fixed priority, (-1) if the priority is configurable. */
55 esp_cpu_intr_type_t type; /**< Whether the interrupt is an edge or level type interrupt, ESP_CPU_INTR_TYPE_NA if the type is configurable. */
56 uint32_t flags; /**< Flags indicating extra details. */
57 } esp_cpu_intr_desc_t;
58
59 /**
60 * @brief Interrupt descriptor flags of esp_cpu_intr_desc_t
61 */
62 #define ESP_CPU_INTR_DESC_FLAG_SPECIAL 0x01 /**< The interrupt is a special interrupt (e.g., a CPU timer interrupt) */
63 #define ESP_CPU_INTR_DESC_FLAG_RESVD 0x02 /**< The interrupt is reserved for internal use */
64
65 /**
66 * @brief CPU interrupt handler type
67 */
68 typedef void (*esp_cpu_intr_handler_t)(void *arg);
69
70 /**
71 * @brief CPU watchpoint trigger type
72 */
73 typedef enum {
74 ESP_CPU_WATCHPOINT_LOAD,
75 ESP_CPU_WATCHPOINT_STORE,
76 ESP_CPU_WATCHPOINT_ACCESS,
77 } esp_cpu_watchpoint_trigger_t;
78
79 /* --------------------------------------------------- CPU Control -----------------------------------------------------
80 *
81 * ------------------------------------------------------------------------------------------------------------------ */
82
83 /**
84 * @brief Stall a CPU core
85 *
86 * @param core_id The core's ID
87 */
88 void esp_cpu_stall(int core_id);
89
90 /**
91 * @brief Resume a previously stalled CPU core
92 *
93 * @param core_id The core's ID
94 */
95 void esp_cpu_unstall(int core_id);
96
97 /**
98 * @brief Reset a CPU core
99 *
100 * @param core_id The core's ID
101 */
102 void esp_cpu_reset(int core_id);
103
104 /**
105 * @brief Wait for Interrupt
106 *
107 * This function causes the current CPU core to execute its Wait For Interrupt
108 * (WFI or equivalent) instruction. After executing this function, the CPU core
109 * will stop execution until an interrupt occurs.
110 */
111 void esp_cpu_wait_for_intr(void);
112
113 /* -------------------------------------------------- CPU Registers ----------------------------------------------------
114 *
115 * ------------------------------------------------------------------------------------------------------------------ */
116
117 /**
118 * @brief Get the current core's ID
119 *
120 * This function will return the ID of the current CPU (i.e., the CPU that calls
121 * this function).
122 *
123 * @return The current core's ID [0..SOC_CPU_CORES_NUM - 1]
124 */
esp_cpu_get_core_id(void)125 FORCE_INLINE_ATTR __attribute__((pure)) int esp_cpu_get_core_id(void)
126 {
127 //Note: Made "pure" to optimize for single core target
128 #ifdef __XTENSA__
129 return (int)xt_utils_get_core_id();
130 #else
131 return (int)rv_utils_get_core_id();
132 #endif
133 }
134
135 /**
136 * @brief Read the current stack pointer address
137 *
138 * @return Stack pointer address
139 */
esp_cpu_get_sp(void)140 FORCE_INLINE_ATTR void *esp_cpu_get_sp(void)
141 {
142 #ifdef __XTENSA__
143 return xt_utils_get_sp();
144 #else
145 return rv_utils_get_sp();
146 #endif
147 }
148
149 /**
150 * @brief Get the current CPU core's cycle count
151 *
152 * Each CPU core maintains an internal counter (i.e., cycle count) that increments
153 * every CPU clock cycle.
154 *
155 * @return Current CPU's cycle count, 0 if not supported.
156 */
esp_cpu_get_cycle_count(void)157 FORCE_INLINE_ATTR esp_cpu_cycle_count_t esp_cpu_get_cycle_count(void)
158 {
159 #ifdef __XTENSA__
160 return (esp_cpu_cycle_count_t)xt_utils_get_cycle_count();
161 #else
162 return (esp_cpu_cycle_count_t)rv_utils_get_cycle_count();
163 #endif
164 }
165
166 /**
167 * @brief Set the current CPU core's cycle count
168 *
169 * Set the given value into the internal counter that increments every
170 * CPU clock cycle.
171 *
172 * @param cycle_count CPU cycle count
173 */
esp_cpu_set_cycle_count(esp_cpu_cycle_count_t cycle_count)174 FORCE_INLINE_ATTR void esp_cpu_set_cycle_count(esp_cpu_cycle_count_t cycle_count)
175 {
176 #ifdef __XTENSA__
177 xt_utils_set_cycle_count((uint32_t)cycle_count);
178 #else
179 rv_utils_set_cycle_count((uint32_t)cycle_count);
180 #endif
181 }
182
183 /**
184 * @brief Convert a program counter (PC) value to address
185 *
186 * If the architecture does not store the true virtual address in the CPU's PC
187 * or return addresses, this function will convert the PC value to a virtual
188 * address. Otherwise, the PC is just returned
189 *
190 * @param pc PC value
191 * @return Virtual address
192 */
esp_cpu_pc_to_addr(uint32_t pc)193 FORCE_INLINE_ATTR __attribute__((pure)) void *esp_cpu_pc_to_addr(uint32_t pc)
194 {
195 #ifdef __XTENSA__
196 // Xtensa stores window rotation in PC[31:30]
197 return (void *)((pc & 0x3fffffffU) | 0x40000000U);
198 #else
199 return (void *)pc;
200 #endif
201 }
202
203 /* ------------------------------------------------- CPU Interrupts ----------------------------------------------------
204 *
205 * ------------------------------------------------------------------------------------------------------------------ */
206
207 // ---------------- Interrupt Descriptors ------------------
208
209 /**
210 * @brief Get a CPU interrupt's descriptor
211 *
212 * Each CPU interrupt has a descriptor describing the interrupt's capabilities
213 * and restrictions. This function gets the descriptor of a particular interrupt
214 * on a particular CPU.
215 *
216 * @param[in] core_id The core's ID
217 * @param[in] intr_num Interrupt number
218 * @param[out] intr_desc_ret The interrupt's descriptor
219 */
220 void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_desc_ret);
221
222 // --------------- Interrupt Configuration -----------------
223
224 /**
225 * @brief Set the base address of the current CPU's Interrupt Vector Table (IVT)
226 *
227 * @param ivt_addr Interrupt Vector Table's base address
228 */
esp_cpu_intr_set_ivt_addr(const void * ivt_addr)229 FORCE_INLINE_ATTR void esp_cpu_intr_set_ivt_addr(const void *ivt_addr)
230 {
231 #ifdef __XTENSA__
232 xt_utils_set_vecbase((uint32_t)ivt_addr);
233 #else
234 rv_utils_set_mtvec((uint32_t)ivt_addr);
235 #endif
236 }
237
238 #if SOC_CPU_HAS_FLEXIBLE_INTC
239 /**
240 * @brief Set the interrupt type of a particular interrupt
241 *
242 * Set the interrupt type (Level or Edge) of a particular interrupt on the
243 * current CPU.
244 *
245 * @param intr_num Interrupt number (from 0 to 31)
246 * @param intr_type The interrupt's type
247 */
esp_cpu_intr_set_type(int intr_num,esp_cpu_intr_type_t intr_type)248 FORCE_INLINE_ATTR void esp_cpu_intr_set_type(int intr_num, esp_cpu_intr_type_t intr_type)
249 {
250 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
251 enum intr_type type = (intr_type == ESP_CPU_INTR_TYPE_LEVEL) ? INTR_TYPE_LEVEL : INTR_TYPE_EDGE;
252 esprv_intc_int_set_type(intr_num, type);
253 }
254
255 /**
256 * @brief Get the current configured type of a particular interrupt
257 *
258 * Get the currently configured type (i.e., level or edge) of a particular
259 * interrupt on the current CPU.
260 *
261 * @param intr_num Interrupt number (from 0 to 31)
262 * @return Interrupt type
263 */
esp_cpu_intr_get_type(int intr_num)264 FORCE_INLINE_ATTR esp_cpu_intr_type_t esp_cpu_intr_get_type(int intr_num)
265 {
266 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
267 enum intr_type type = esprv_intc_int_get_type(intr_num);
268 return (type == INTR_TYPE_LEVEL) ? ESP_CPU_INTR_TYPE_LEVEL : ESP_CPU_INTR_TYPE_EDGE;
269 }
270
271 /**
272 * @brief Set the priority of a particular interrupt
273 *
274 * Set the priority of a particular interrupt on the current CPU.
275 *
276 * @param intr_num Interrupt number (from 0 to 31)
277 * @param intr_priority The interrupt's priority
278 */
esp_cpu_intr_set_priority(int intr_num,int intr_priority)279 FORCE_INLINE_ATTR void esp_cpu_intr_set_priority(int intr_num, int intr_priority)
280 {
281 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
282 esprv_intc_int_set_priority(intr_num, intr_priority);
283 }
284
285 /**
286 * @brief Get the current configured priority of a particular interrupt
287 *
288 * Get the currently configured priority of a particular interrupt on the
289 * current CPU.
290 *
291 * @param intr_num Interrupt number (from 0 to 31)
292 * @return Interrupt's priority
293 */
esp_cpu_intr_get_priority(int intr_num)294 FORCE_INLINE_ATTR int esp_cpu_intr_get_priority(int intr_num)
295 {
296 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
297 return esprv_intc_int_get_priority(intr_num);
298 }
299 #endif // SOC_CPU_HAS_FLEXIBLE_INTC
300
301 /**
302 * @brief Check if a particular interrupt already has a handler function
303 *
304 * Check if a particular interrupt on the current CPU already has a handler
305 * function assigned.
306 *
307 * @note This function simply checks if the IVT of the current CPU already has
308 * a handler assigned.
309 * @param intr_num Interrupt number (from 0 to 31)
310 * @return True if the interrupt has a handler function, false otherwise.
311 */
esp_cpu_intr_has_handler(int intr_num)312 FORCE_INLINE_ATTR bool esp_cpu_intr_has_handler(int intr_num)
313 {
314 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
315 bool has_handler;
316 #ifdef __XTENSA__
317 has_handler = xt_int_has_handler(intr_num, esp_cpu_get_core_id());
318 #else
319 has_handler = intr_handler_get(intr_num);
320 #endif
321 return has_handler;
322 }
323
324 /**
325 * @brief Set the handler function of a particular interrupt
326 *
327 * Assign a handler function (i.e., ISR) to a particular interrupt on the
328 * current CPU.
329 *
330 * @note This function simply sets the handler function (in the IVT) and does
331 * not actually enable the interrupt.
332 * @param intr_num Interrupt number (from 0 to 31)
333 * @param handler Handler function
334 * @param handler_arg Argument passed to the handler function
335 */
esp_cpu_intr_set_handler(int intr_num,esp_cpu_intr_handler_t handler,void * handler_arg)336 FORCE_INLINE_ATTR void esp_cpu_intr_set_handler(int intr_num, esp_cpu_intr_handler_t handler, void *handler_arg)
337 {
338 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
339 #ifdef __XTENSA__
340 xt_set_interrupt_handler(intr_num, (xt_handler)handler, handler_arg);
341 #else
342 intr_handler_set(intr_num, (intr_handler_t)handler, handler_arg);
343 #endif
344 }
345
346 /**
347 * @brief Get a handler function's argument of
348 *
349 * Get the argument of a previously assigned handler function on the current CPU.
350 *
351 * @param intr_num Interrupt number (from 0 to 31)
352 * @return The the argument passed to the handler function
353 */
esp_cpu_intr_get_handler_arg(int intr_num)354 FORCE_INLINE_ATTR void *esp_cpu_intr_get_handler_arg(int intr_num)
355 {
356 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
357 void *handler_arg;
358 #ifdef __XTENSA__
359 handler_arg = xt_get_interrupt_handler_arg(intr_num);
360 #else
361 handler_arg = intr_handler_get_arg(intr_num);
362 #endif
363 return handler_arg;
364 }
365
366 // ------------------ Interrupt Control --------------------
367
368 /**
369 * @brief Enable particular interrupts on the current CPU
370 *
371 * @param intr_mask Bit mask of the interrupts to enable
372 */
esp_cpu_intr_enable(uint32_t intr_mask)373 FORCE_INLINE_ATTR void esp_cpu_intr_enable(uint32_t intr_mask)
374 {
375 #ifdef __XTENSA__
376 z_xt_ints_on(intr_mask);
377 #else
378 rv_utils_intr_enable(intr_mask);
379 #endif
380 }
381
382 /**
383 * @brief Disable particular interrupts on the current CPU
384 *
385 * @param intr_mask Bit mask of the interrupts to disable
386 */
esp_cpu_intr_disable(uint32_t intr_mask)387 FORCE_INLINE_ATTR void esp_cpu_intr_disable(uint32_t intr_mask)
388 {
389 #ifdef __XTENSA__
390 z_xt_ints_off(intr_mask);
391 #else
392 rv_utils_intr_disable(intr_mask);
393 #endif
394 }
395
396 /**
397 * @brief Get the enabled interrupts on the current CPU
398 *
399 * @return Bit mask of the enabled interrupts
400 */
esp_cpu_intr_get_enabled_mask(void)401 FORCE_INLINE_ATTR uint32_t esp_cpu_intr_get_enabled_mask(void)
402 {
403 #ifdef __XTENSA__
404 return xt_utils_intr_get_enabled_mask();
405 #else
406 return rv_utils_intr_get_enabled_mask();
407 #endif
408 }
409
410 /**
411 * @brief Acknowledge an edge interrupt
412 *
413 * @param intr_num Interrupt number (from 0 to 31)
414 */
esp_cpu_intr_edge_ack(int intr_num)415 FORCE_INLINE_ATTR void esp_cpu_intr_edge_ack(int intr_num)
416 {
417 assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
418 #ifdef __XTENSA__
419 xthal_set_intclear((unsigned) (1 << intr_num));
420 #else
421 rv_utils_intr_edge_ack((unsigned) intr_num);
422 #endif
423 }
424
425 /* -------------------------------------------------- Memory Ports -----------------------------------------------------
426 *
427 * ------------------------------------------------------------------------------------------------------------------ */
428
429 /**
430 * @brief Configure the CPU to disable access to invalid memory regions
431 */
432 void esp_cpu_configure_region_protection(void);
433
434 /* ---------------------------------------------------- Debugging ------------------------------------------------------
435 *
436 * ------------------------------------------------------------------------------------------------------------------ */
437
438 // --------------- Breakpoints/Watchpoints -----------------
439
440 #if SOC_CPU_BREAKPOINTS_NUM > 0
441
442 /**
443 * @brief Set and enable a hardware breakpoint on the current CPU
444 *
445 * @note This function is meant to be called by the panic handler to set a
446 * breakpoint for an attached debugger during a panic.
447 * @note Overwrites previously set breakpoint with same breakpoint number.
448 * @param bp_num Hardware breakpoint number [0..SOC_CPU_BREAKPOINTS_NUM - 1]
449 * @param bp_addr Address to set a breakpoint on
450 * @return ESP_OK if breakpoint is set. Failure otherwise
451 */
452 esp_err_t esp_cpu_set_breakpoint(int bp_num, const void *bp_addr);
453
454 /**
455 * @brief Clear a hardware breakpoint on the current CPU
456 *
457 * @note Clears a breakpoint regardless of whether it was previously set
458 * @param bp_num Hardware breakpoint number [0..SOC_CPU_BREAKPOINTS_NUM - 1]
459 * @return ESP_OK if breakpoint is cleared. Failure otherwise
460 */
461 esp_err_t esp_cpu_clear_breakpoint(int bp_num);
462
463 #endif // SOC_CPU_BREAKPOINTS_NUM > 0
464
465 /**
466 * @brief Set and enable a hardware watchpoint on the current CPU
467 *
468 * Set and enable a hardware watchpoint on the current CPU, specifying the
469 * memory range and trigger operation. Watchpoints will break/panic the CPU when
470 * the CPU accesses (according to the trigger type) on a certain memory range.
471 *
472 * @note Overwrites previously set watchpoint with same watchpoint number.
473 * @param wp_num Hardware watchpoint number [0..SOC_CPU_WATCHPOINTS_NUM - 1]
474 * @param wp_addr Watchpoint's base address
475 * @param size Size of the region to watch. Must be one of 2^n, with n in [0..6].
476 * @param trigger Trigger type
477 * @return ESP_ERR_INVALID_ARG on invalid arg, ESP_OK otherwise
478 */
479 esp_err_t esp_cpu_set_watchpoint(int wp_num, const void *wp_addr, size_t size, esp_cpu_watchpoint_trigger_t trigger);
480
481 /**
482 * @brief Clear a hardware watchpoint on the current CPU
483 *
484 * @note Clears a watchpoint regardless of whether it was previously set
485 * @param wp_num Hardware watchpoint number [0..SOC_CPU_WATCHPOINTS_NUM - 1]
486 * @return ESP_OK if watchpoint was cleared. Failure otherwise.
487 */
488 esp_err_t esp_cpu_clear_watchpoint(int wp_num);
489
490 // ---------------------- Debugger -------------------------
491
492 /**
493 * @brief Check if the current CPU has a debugger attached
494 *
495 * @return True if debugger is attached, false otherwise
496 */
esp_cpu_dbgr_is_attached(void)497 FORCE_INLINE_ATTR bool esp_cpu_dbgr_is_attached(void)
498 {
499 #ifdef __XTENSA__
500 return xt_utils_dbgr_is_attached();
501 #else
502 return rv_utils_dbgr_is_attached();
503 #endif
504 }
505
506 /**
507 * @brief Trigger a call to the current CPU's attached debugger
508 */
esp_cpu_dbgr_break(void)509 FORCE_INLINE_ATTR void esp_cpu_dbgr_break(void)
510 {
511 #ifdef __XTENSA__
512 xt_utils_dbgr_break();
513 #else
514 rv_utils_dbgr_break();
515 #endif
516 }
517
518 // ---------------------- Instructions -------------------------
519
520 /**
521 * @brief Given the return address, calculate the address of the preceding call instruction
522 * This is typically used to answer the question "where was the function called from?"
523 * @param return_address The value of the return address register.
524 * Typically set to the value of __builtin_return_address(0).
525 * @return Address of the call instruction preceding the return address.
526 */
esp_cpu_get_call_addr(intptr_t return_address)527 FORCE_INLINE_ATTR intptr_t esp_cpu_get_call_addr(intptr_t return_address)
528 {
529 /* Both Xtensa and RISC-V have 2-byte instructions, so to get this right we
530 * should decode the preceding instruction as if it is 2-byte, check if it is a call,
531 * else treat it as 3 or 4 byte one. However for the cases where this function is
532 * used, being off by one instruction is usually okay, so this is kept simple for now.
533 */
534 #ifdef __XTENSA__
535 return return_address - 3;
536 #else
537 return return_address - 4;
538 #endif
539 }
540
541 /* ------------------------------------------------------ Misc ---------------------------------------------------------
542 *
543 * ------------------------------------------------------------------------------------------------------------------ */
544
545 /**
546 * @brief Atomic compare-and-set operation
547 *
548 * @param addr Address of atomic variable
549 * @param compare_value Value to compare the atomic variable to
550 * @param new_value New value to set the atomic variable to
551 * @return Whether the atomic variable was set or not
552 */
553 bool esp_cpu_compare_and_set(volatile uint32_t *addr, uint32_t compare_value, uint32_t new_value);
554
555 #ifdef __cplusplus
556 }
557 #endif
558