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