esp-idf/components/xtensa/include/xt_utils.h
2022-07-25 09:43:10 +02:00

246 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 4143 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