kopia lustrzana https://github.com/espressif/esp-idf
246 wiersze
6.9 KiB
C
246 wiersze
6.9 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#pragma once
|
|
|
|
#include <stdint.h>
|
|
#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_ffsll(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
|