/* * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include "soc/soc_caps.h" #include "xtensa/config/core-isa.h" #include "xtensa/config/core.h" #include "xtensa/config/extreg.h" #include "xtensa/config/specreg.h" #include "xtensa/xtruntime.h" #include "xt_instr_macros.h" #include "esp_bit_defs.h" #include "esp_attr.h" #ifdef __cplusplus extern "C" { #endif /* -------------------------------------------------- CPU Registers ---------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ FORCE_INLINE_ATTR __attribute__((pure)) uint32_t xt_utils_get_core_id(void) { /* Note: We depend on SOC_CPU_CORES_NUM instead of XCHAL_HAVE_PRID as some single Xtensa targets (such as ESP32-S2) have the PRID register even though they are single core. */ #if SOC_CPU_CORES_NUM > 1 // Read and extract bit 13 of special register PRID uint32_t id; asm volatile ( "rsr.prid %0\n" "extui %0,%0,13,1" :"=r"(id)); return id; #else return 0; #endif // SOC_CPU_CORES_NUM > 1 } FORCE_INLINE_ATTR __attribute__((pure)) uint32_t xt_utils_get_raw_core_id(void) { #if XCHAL_HAVE_PRID // Read the raw value of special register PRID uint32_t id; asm volatile ( "rsr.prid %0\n" :"=r"(id)); return id; #else return 0; #endif // XCHAL_HAVE_PRID } FORCE_INLINE_ATTR void *xt_utils_get_sp(void) { void *sp; asm volatile ("mov %0, sp;" : "=r" (sp)); return sp; } FORCE_INLINE_ATTR uint32_t xt_utils_get_cycle_count(void) { uint32_t ccount; RSR(CCOUNT, ccount); return ccount; } static inline void xt_utils_set_cycle_count(uint32_t ccount) { WSR(CCOUNT, ccount); } FORCE_INLINE_ATTR void xt_utils_wait_for_intr(void) { asm volatile ("waiti 0\n"); } /* ------------------------------------------------- CPU Interrupts ---------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ // ---------------- Interrupt Descriptors ------------------ // --------------- Interrupt Configuration ----------------- FORCE_INLINE_ATTR void xt_utils_set_vecbase(uint32_t vecbase) { asm volatile ("wsr %0, vecbase" :: "r" (vecbase)); } // ------------------ Interrupt Control -------------------- FORCE_INLINE_ATTR uint32_t xt_utils_intr_get_enabled_mask(void) { uint32_t intr_mask; RSR(INTENABLE, intr_mask); return intr_mask; } /* -------------------------------------------------- Memory Ports ----------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ /* ---------------------------------------------------- Debugging ------------------------------------------------------ * * ------------------------------------------------------------------------------------------------------------------ */ // --------------- Breakpoints/Watchpoints ----------------- FORCE_INLINE_ATTR void xt_utils_set_breakpoint(int bp_num, uint32_t bp_addr) { //Set the breakpoint's address if (bp_num == 1) { WSR(IBREAKA_1, bp_addr); } else { WSR(IBREAKA_0, bp_addr); } //Enable the breakpoint uint32_t brk_ena_reg; RSR(IBREAKENABLE, brk_ena_reg); brk_ena_reg |= BIT(bp_num); WSR(IBREAKENABLE, brk_ena_reg); } FORCE_INLINE_ATTR void xt_utils_clear_breakpoint(int bp_num) { // Disable the breakpoint using the break enable register uint32_t bp_en = 0; RSR(IBREAKENABLE, bp_en); bp_en &= ~BIT(bp_num); WSR(IBREAKENABLE, bp_en); // Zero the break address register uint32_t bp_addr = 0; if (bp_num == 1) { WSR(IBREAKA_1, bp_addr); } else { WSR(IBREAKA_0, bp_addr); } } FORCE_INLINE_ATTR void xt_utils_set_watchpoint(int wp_num, uint32_t wp_addr, size_t size, bool on_read, bool on_write) { // Initialize DBREAKC bits (see Table 4–143 or isa_rm.pdf) uint32_t dbreakc_reg = 0x3F; dbreakc_reg = dbreakc_reg << (__builtin_ffs(size) - 1); dbreakc_reg = dbreakc_reg & 0x3F; if (on_read) { dbreakc_reg |= BIT(30); } if (on_write) { dbreakc_reg |= BIT(31); } // Enable break address and break control register if (wp_num == 1) { WSR(DBREAKA_1, (uint32_t) wp_addr); WSR(DBREAKC_1, dbreakc_reg); } else { WSR(DBREAKA_0, (uint32_t) wp_addr); WSR(DBREAKC_0, dbreakc_reg); } } FORCE_INLINE_ATTR void xt_utils_clear_watchpoint(int wp_num) { // Clear both break control and break address register if (wp_num == 1) { WSR(DBREAKC_1, 0); WSR(DBREAKA_1, 0); } else { WSR(DBREAKC_0, 0); WSR(DBREAKA_0, 0); } } // ---------------------- Debugger ------------------------- FORCE_INLINE_ATTR bool xt_utils_dbgr_is_attached(void) { uint32_t dcr = 0; uint32_t reg = DSRSET; RER(reg, dcr); return (bool)(dcr & 0x1); } FORCE_INLINE_ATTR void xt_utils_dbgr_break(void) { __asm__ ("break 1,15"); } /* ------------------------------------------------------ Misc --------------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ FORCE_INLINE_ATTR bool xt_utils_compare_and_set(volatile uint32_t *addr, uint32_t compare_value, uint32_t new_value) { #if XCHAL_HAVE_S32C1I #ifdef __clang_analyzer__ //Teach clang-tidy that "addr" cannot be const as it can be updated by S32C1I instruction volatile uint32_t temp; temp = *addr; *addr = temp; #endif // Atomic compare and set using S32C1I instruction uint32_t old_value = new_value; __asm__ __volatile__ ( "WSR %2, SCOMPARE1 \n" "S32C1I %0, %1, 0 \n" :"=r"(old_value) :"r"(addr), "r"(compare_value), "0"(old_value) ); return (old_value == compare_value); #else // XCHAL_HAVE_S32C1I // Single core target has no atomic CAS instruction. We can achieve atomicity by disabling interrupts uint32_t intr_level; __asm__ __volatile__ ("rsil %0, " XTSTR(XCHAL_EXCM_LEVEL) "\n" : "=r"(intr_level)); // Compare and set uint32_t old_value; old_value = *addr; if (old_value == compare_value) { *addr = new_value; } // Restore interrupts __asm__ __volatile__ ("memw \n" "wsr %0, ps\n" :: "r"(intr_level)); return (old_value == compare_value); #endif // XCHAL_HAVE_S32C1I } #ifdef __cplusplus } #endif