Files
esp-idf/components/esp_riscv_trace/include/esp_riscv_trace.h

262 lines
12 KiB
C

/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @brief RISC-V core whose execution is traced. */
typedef enum {
ESP_RISCV_TRACE_CORE_0 = 0,
ESP_RISCV_TRACE_CORE_1 = 1,
} esp_riscv_trace_core_t;
/** @brief Instruction address encoding in the trace stream. */
typedef enum {
ESP_RISCV_TRACE_ADDR_DELTA = 0, /*!< Differential addresses (hardware reset default) */
ESP_RISCV_TRACE_ADDR_FULL = 1, /*!< Full addresses */
} esp_riscv_trace_address_mode_t;
/** @brief Trace buffer memory mode. */
typedef enum {
ESP_RISCV_TRACE_MEM_NON_LOOP = 0, /*!< Stop when buffer is full */
ESP_RISCV_TRACE_MEM_LOOP, /*!< Wrap around (hardware reset default) */
} esp_riscv_trace_mem_mode_t;
/** @brief Resynchronization mode. */
typedef enum {
ESP_RISCV_TRACE_RESYNC_DISABLED = 0, /*!< No periodic resync (hardware reset default) */
ESP_RISCV_TRACE_RESYNC_PACKET = 2, /*!< Resync by packet count */
ESP_RISCV_TRACE_RESYNC_CYCLE = 3, /*!< Resync by cycle count */
} esp_riscv_trace_resync_mode_t;
/**
* @brief AHB burst type used by the trace write master.
*
* These are the trace IP's custom hburst field codes, NOT the standard AMBA
* HBURST encoding: 0=SINGLE, 1=INCR, 2=INCR4, 4=INCR8; values 3, 5, 6, 7 are
* invalid. Do not "correct" 2/4 to the AMBA INCR4/INCR8 codes (3/5).
*/
typedef enum {
ESP_RISCV_TRACE_AHB_SINGLE = 0, /*!< Single transfer (hardware reset default) */
ESP_RISCV_TRACE_AHB_INCR = 1, /*!< Incrementing transfer (length not defined) */
ESP_RISCV_TRACE_AHB_INCR4 = 2, /*!< 4-beat incrementing transfer */
ESP_RISCV_TRACE_AHB_INCR8 = 4, /*!< 8-beat incrementing transfer */
} esp_riscv_trace_ahb_burst_t;
/** @brief Where the driver allocates the trace buffer. */
typedef enum {
ESP_RISCV_TRACE_BUFFER_INTERNAL = 0, /*!< Internal RAM / L2MEM (hardware reset default) */
ESP_RISCV_TRACE_BUFFER_EXTERNAL = 1, /*!< External RAM / PSRAM */
} esp_riscv_trace_buffer_mem_t;
/** @brief Filter comparator input. */
typedef enum {
ESP_RISCV_TRACE_FILTER_INPUT_IADDR = 0, /*!< Compare the instruction address (PC) */
ESP_RISCV_TRACE_FILTER_INPUT_TVAL = 1, /*!< Compare the trap value (tval) */
} esp_riscv_trace_filter_input_t;
/** @brief Filter comparator function. */
typedef enum {
ESP_RISCV_TRACE_FILTER_COMPARATOR_EQ = 0, /*!< input == match_value */
ESP_RISCV_TRACE_FILTER_COMPARATOR_NE = 1, /*!< input != match_value */
ESP_RISCV_TRACE_FILTER_COMPARATOR_LT = 2, /*!< input < match_value */
ESP_RISCV_TRACE_FILTER_COMPARATOR_LE = 3, /*!< input <= match_value */
ESP_RISCV_TRACE_FILTER_COMPARATOR_GT = 4, /*!< input > match_value */
ESP_RISCV_TRACE_FILTER_COMPARATOR_GE = 5, /*!< input >= match_value */
} esp_riscv_trace_filter_comparator_func_t;
/** @brief How the primary (P) and secondary (S) comparators combine. */
typedef enum {
ESP_RISCV_TRACE_FILTER_MODE_PRIMARY = 0, /*!< Primary comparator only */
ESP_RISCV_TRACE_FILTER_MODE_AND = 1, /*!< P && S */
ESP_RISCV_TRACE_FILTER_MODE_NAND = 2, /*!< !(P && S) */
ESP_RISCV_TRACE_FILTER_MODE_RANGE = 3, /*!< Start when P matches, continue until S matches */
} esp_riscv_trace_filter_mode_t;
/** @brief Privilege level selected by the privilege qualifier. */
typedef enum {
ESP_RISCV_TRACE_FILTER_PRIV_USER = 0, /*!< User mode */
ESP_RISCV_TRACE_FILTER_PRIV_MACHINE = 1, /*!< Machine mode */
} esp_riscv_trace_filter_priv_t;
/** @brief One filter comparator. */
typedef struct {
esp_riscv_trace_filter_input_t input; /*!< Input to compare (iaddr or tval) */
esp_riscv_trace_filter_comparator_func_t function; /*!< Compare function */
uint32_t match_value; /*!< 32-bit value compared against the input */
bool notify; /*!< Emit a packet reporting the matching address */
} esp_riscv_trace_filter_comparator_t;
/**
* @brief Filter (trace qualifier) configuration.
*
* The filter restricts which execution produces trace packets. With .enable = false the encoder
* traces everything (the default). Apply via esp_riscv_trace_set_filter() before a capture.
*/
typedef struct {
bool enable; /*!< Master enable; false = trace everything */
bool match_comparators; /*!< Gate matching on the comparators below */
esp_riscv_trace_filter_comparator_t primary; /*!< Primary (P) comparator */
esp_riscv_trace_filter_comparator_t secondary; /*!< Secondary (S) comparator */
esp_riscv_trace_filter_mode_t mode; /*!< How P and S combine */
bool match_privilege; /*!< Gate matching on the privilege level */
esp_riscv_trace_filter_priv_t privilege; /*!< Privilege level to match */
bool match_ecause; /*!< Match from an exception cause */
uint8_t ecause; /*!< Exception cause code (6-bit) */
bool match_interrupt; /*!< Match from an interrupt trap */
bool interrupt_itype2; /*!< true = match itype 2, false = itype 1 */
} esp_riscv_trace_filter_config_t;
/**
* @brief Configuration for a trace encoder instance.
*
* @note On targets without SOC_RISCV_TRACE_HAS_CONFIG_REG (address_mode, stall_cpu,
* halt_enable, reset_enable, debug_trigger_enable) or without SOC_RISCV_TRACE_AHB_CONFIGURABLE
* (ahb_burst, ahb_max_incr), those fields are accepted but ignored. The encoder keeps its
* fixed default behavior.
*/
typedef struct {
uint32_t core_mask; /*!< Bitmask of cores to trace (BIT(n) for core n) */
size_t buffer_size; /*!< Trace buffer size in bytes (driver-allocated) */
esp_riscv_trace_buffer_mem_t buffer_mem; /*!< Where the driver allocates the buffer */
esp_riscv_trace_address_mode_t address_mode; /*!< Instruction address encoding in the trace stream. */
esp_riscv_trace_mem_mode_t mem_mode; /*!< Trace buffer memory mode */
bool auto_restart; /*!< Auto-restart encoder after a FIFO overflow */
bool stall_cpu; /*!< Stall CPU instead of dropping packets on FIFO full */
bool halt_enable; /*!< Trace through hart halt */
bool reset_enable; /*!< Trace through hart reset */
bool debug_trigger_enable; /*!< Enable Debug Module trigger input */
esp_riscv_trace_resync_mode_t resync_mode; /*!< Resynchronization mode */
uint32_t resync_threshold; /*!< Resync threshold. (default 128) */
esp_riscv_trace_ahb_burst_t ahb_burst; /*!< AHB burst type used by the trace write master. */
uint8_t ahb_max_incr; /*!< Max INCR burst beats. (default 0) */
uint32_t intr_mask; /*!< Interrupt bits to enable for polling via get_intr_status() */
} esp_riscv_trace_config_t;
/**
* @brief Default trace encoder configuration built from Kconfig values.
*
* Use this to get a ready-to-use configuration without filling every field by hand,
* then override individual members afterwards if needed.
*
* Example:
* @code{c}
* esp_riscv_trace_config_t config = ESP_RISCV_TRACE_DEFAULT_CONFIG();
* @endcode
*/
#define ESP_RISCV_TRACE_DEFAULT_CONFIG() { \
.core_mask = CONFIG_ESP_RISCV_TRACE_CORE_MASK, \
.buffer_size = CONFIG_ESP_RISCV_TRACE_BUFFER_SIZE, \
.buffer_mem = CONFIG_ESP_RISCV_TRACE_BUFFER_MEM, \
.address_mode = CONFIG_ESP_RISCV_TRACE_ADDRESS_MODE, \
.mem_mode = CONFIG_ESP_RISCV_TRACE_MEM_MODE, \
.auto_restart = CONFIG_ESP_RISCV_TRACE_AUTO_RESTART, \
.stall_cpu = CONFIG_ESP_RISCV_TRACE_STALL_CPU, \
.halt_enable = CONFIG_ESP_RISCV_TRACE_HALT, \
.reset_enable = CONFIG_ESP_RISCV_TRACE_RESET, \
.debug_trigger_enable = CONFIG_ESP_RISCV_TRACE_DEBUG_TRIGGER, \
.resync_mode = CONFIG_ESP_RISCV_TRACE_RESYNC_MODE, \
.resync_threshold = CONFIG_ESP_RISCV_TRACE_RESYNC_THRESHOLD, \
.ahb_burst = CONFIG_ESP_RISCV_TRACE_AHB_BURST, \
.ahb_max_incr = CONFIG_ESP_RISCV_TRACE_AHB_MAX_INCR, \
.intr_mask = 0, \
}
/** @brief Encoder work status. */
typedef enum {
ESP_RISCV_TRACE_WORK_STATUS_IDLE = 0,
ESP_RISCV_TRACE_WORK_STATUS_WORKING = 1,
ESP_RISCV_TRACE_WORK_STATUS_WAIT = 2,
ESP_RISCV_TRACE_WORK_STATUS_LOST = 3,
} esp_riscv_trace_work_status_t;
/** @brief Live encoder status snapshot. */
typedef struct {
esp_riscv_trace_work_status_t work_status;
bool fifo_empty;
bool memory_full;
bool fifo_overflowed;
} esp_riscv_trace_status_t;
/**
* @note RISC-V trace driver APIs are serialized per trace core and are intended
* for task context. Do not call them from ISR context.
*/
/**
* @brief Start tracing on a specified core.
*
* @param core_id Core whose trace to start
* @return ESP_OK on success, ESP_ERR_INVALID_STATE if the core is not initialized
*/
esp_err_t esp_riscv_trace_start(esp_riscv_trace_core_t core_id);
/**
* @brief Stop tracing on a specified core and wait for the FIFO to empty.
*
* @param core_id Core whose trace to stop
* @param timeout_us Timeout in microseconds
* @return ESP_OK on success, ESP_ERR_TIMEOUT if the FIFO is not empty within the timeout
*/
esp_err_t esp_riscv_trace_stop(esp_riscv_trace_core_t core_id, uint32_t timeout_us);
/**
* @brief Get a core's trace buffer.
*
* @param core_id Core whose buffer to retrieve
* @param buffer Out: buffer base address
* @param capacity Out: total buffer size in bytes
* @param head_offset Out: current write offset within the buffer [0, capacity]
* @return ESP_OK on success, ESP_ERR_INVALID_STATE if the core is not initialized
*/
esp_err_t esp_riscv_trace_get_buffer(esp_riscv_trace_core_t core_id, uint8_t **buffer,
size_t *capacity, size_t *head_offset);
/**
* @brief Get the status of a specified core's trace encoder.
*
* @param core_id Core whose status to retrieve
* @param status Out: status snapshot
* @return ESP_OK on success, ESP_ERR_INVALID_STATE if the core is not initialized
*/
esp_err_t esp_riscv_trace_get_status(esp_riscv_trace_core_t core_id, esp_riscv_trace_status_t *status);
/**
* @brief Apply a filter (trace qualifier) to a core's encoder.
*
* @param core_id Core whose filter to configure
* @param config Filter configuration
* @return ESP_ERR_NOT_SUPPORTED on targets without a filter unit
*/
esp_err_t esp_riscv_trace_set_filter(esp_riscv_trace_core_t core_id, const esp_riscv_trace_filter_config_t *config);
/**
* @brief Provide the per-core configuration used by the startup auto-initialization.
*
* Called once per core during startup, so each trace encoder can be configured independently
* (different buffer, address mode, resync, etc.). The default (weak) implementation returns
* ESP_RISCV_TRACE_DEFAULT_CONFIG() for every core. Define your own strong version in your
* application (typically switching on @p core_id) to override the configuration.
*
* Only the @p core_id bit of the returned .core_mask is checked for this call: clear it to skip
* tracing this core at runtime even if it is selected by Kconfig.
*
* @param core_id Core the returned configuration applies to
* @return The configuration the startup auto-init uses for @p core_id.
*/
esp_riscv_trace_config_t esp_riscv_trace_get_user_config(int core_id);
#ifdef __cplusplus
}
#endif