esp_cpu.h 16 KB

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