From 9014eade4fc00c31c7ee8c09b751b946ba9525f8 Mon Sep 17 00:00:00 2001 From: Samuel Obuch Date: Tue, 20 Jan 2026 13:30:55 +0100 Subject: [PATCH] feat(esp32h21): add assist_debug support --- .../esp32h21/include/hal/assist_debug_ll.h | 141 ++++++++++++++++++ .../esp32h21/include/soc/Kconfig.soc_caps.in | 4 + .../soc/esp32h21/include/soc/soc_caps.h | 2 +- .../esp32h21/register/soc/assist_debug_reg.h | 1 - .../system/build_tests/no_hwsg/README.md | 4 +- 5 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 components/hal/esp32h21/include/hal/assist_debug_ll.h diff --git a/components/hal/esp32h21/include/hal/assist_debug_ll.h b/components/hal/esp32h21/include/hal/assist_debug_ll.h new file mode 100644 index 00000000000..ea0049ec8ef --- /dev/null +++ b/components/hal/esp32h21/include/hal/assist_debug_ll.h @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for DEBUG_ASSIST peripheral + +#pragma once + +#include "soc/assist_debug_reg.h" +#define ASSIST_DEBUG_SP_SPILL_BITS (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA | ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA) +#define ASSIST_DEBUG_CORE_0_MONITOR_REG ASSIST_DEBUG_CORE_0_INTR_ENA_REG + +#ifndef __ASSEMBLER__ + +#include +#include +#include "esp_attr.h" +#include "hal/assert.h" +#include "soc/pcr_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Most other peripherals have 4 interrupt-related registers: INT_ENA_REG, INT_CLR_REG, INT_RAW_REG, INT_ST_REG, the + * meaning of which is well-understood. + * + * Assist_debug peripheral uses a different structure of interrupt registers: + * INT_ENA_REG, INT_RLS_REG, INT_CLR_REG, INT_RAW_REG. + * + * Their behavior can be explained using the following (verilog-like) pseudo-code: + * reg sp_spill_max_st + * assign sp_spill_max = (sp > SP_MAX_REG) + * assign SP_SPILL_MAX_RAW = sp_spill_max & SPILL_MAX_ENA + * always (@posedge clk) begin + * if (reset) then sp_spill_max_st <= 0 + * elif SP_SPILL_MAX_CLR then sp_spill_max_st <= 0 + * else sp_spill_max_st <= SP_SPILL_MAX_RAW & SP_SPILL_MAX_RLS + * end + * // ...same for sp_spill_min and other things debug_assist can check. + * + * // this is the final interrupt line coming out of the peripheral: + * assign DEBUG_ASSIST_INT = sp_spill_max_st | sp_spill_min_st | ... + * + * Basically, there is no "ST" register showing the final (latched) interrupt state, and there is an additional + * "RLS" register which just like "ENA" can be used to mask the interrupt. + * Note that writing to CLR clears the (internal) latched interrupt state 'sp_spill_max_st', + * but doesn't affect the software-readable RAW register. + * + * In this code, we use "ENA" to enable monitoring of a particular condition, and "RLS" to enable the interrupt. + * This allows checking whether the condition (e.g. sp > SP_MAX) has occurred by reading the RAW register, without + * actually triggering the interrupt. Hence you will see the somewhat counter-intuitive use of "RLS" to enable the + * interrupt, instead of "ENA". + */ + + /* These functions are optimized and designed for internal usage. + * So, the API may differ from general ll layer pattern */ + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR bool assist_debug_ll_sp_spill_is_fired(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_INTR_RAW_REG) & ASSIST_DEBUG_SP_SPILL_BITS; +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_clear(__attribute__((unused)) uint32_t core_id) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_INTR_CLR_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_min(__attribute__((unused)) uint32_t core_id, uint32_t min) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MIN_REG, min); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_min(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MIN_REG); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_max(__attribute__((unused)) uint32_t core_id, uint32_t max) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MAX_REG, max); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_max(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MAX_REG); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_pc(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_PC_REG); +} + +FORCE_INLINE_ATTR void assist_debug_ll_enable_pc_recording(uint32_t core_id, bool enable) +{ +} + +FORCE_INLINE_ATTR void assist_debug_ll_enable_bus_clock(__attribute__((unused)) uint32_t core_id, bool enable) +{ + PCR.assist_conf.assist_clk_en = enable; +} + +FORCE_INLINE_ATTR void assist_debug_ll_reset_register(__attribute__((unused)) uint32_t core_id) +{ + PCR.assist_conf.assist_rst_en = true; + PCR.assist_conf.assist_rst_en = false; +} + +FORCE_INLINE_ATTR bool assist_debug_ll_is_debugger_active(void) +{ + return REG_GET_BIT(ASSIST_DEBUG_CORE_0_DEBUG_MODE_REG, ASSIST_DEBUG_CORE_0_DEBUG_MODULE_ACTIVE); +} + +#ifdef __cplusplus +} +#endif + +#endif // __ASSEMBLER__ diff --git a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in index 165921cbaea..4467ab41275 100644 --- a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in @@ -123,6 +123,10 @@ config SOC_CLK_TREE_SUPPORTED bool default y +config SOC_ASSIST_DEBUG_SUPPORTED + bool + default y + config SOC_WDT_SUPPORTED bool default y diff --git a/components/soc/esp32h21/include/soc/soc_caps.h b/components/soc/esp32h21/include/soc/soc_caps.h index ec10719ac4c..cff8d124038 100644 --- a/components/soc/esp32h21/include/soc/soc_caps.h +++ b/components/soc/esp32h21/include/soc/soc_caps.h @@ -65,7 +65,7 @@ #define SOC_LP_AON_SUPPORTED 1 // #define SOC_LP_PERIPHERALS_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 -// #define SOC_ASSIST_DEBUG_SUPPORTED 1 //TODO: [ESP32H21] IDF-11544 +#define SOC_ASSIST_DEBUG_SUPPORTED 1 #define SOC_WDT_SUPPORTED 1 #define SOC_SPI_FLASH_SUPPORTED 1 //TODO: [ESP32H21] IDF-11526 // #define SOC_RNG_SUPPORTED 1 //TODO: [ESP32H21] IDF-11503 diff --git a/components/soc/esp32h21/register/soc/assist_debug_reg.h b/components/soc/esp32h21/register/soc/assist_debug_reg.h index fdd76eeb056..9f0a1e406a1 100644 --- a/components/soc/esp32h21/register/soc/assist_debug_reg.h +++ b/components/soc/esp32h21/register/soc/assist_debug_reg.h @@ -5,7 +5,6 @@ */ #pragma once -#include #include "soc/soc.h" #ifdef __cplusplus extern "C" { diff --git a/tools/test_apps/system/build_tests/no_hwsg/README.md b/tools/test_apps/system/build_tests/no_hwsg/README.md index b7f481ca782..73d0bce47ed 100644 --- a/tools/test_apps/system/build_tests/no_hwsg/README.md +++ b/tools/test_apps/system/build_tests/no_hwsg/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | This project tests building with the no_hwsg configuration.