mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(esp_trace): add usb-serial-jtag as a transport channel
This commit is contained in:
@@ -14,13 +14,22 @@ if(CONFIG_ESP_TRACE_ENABLE)
|
||||
if(CONFIG_ESP_TRACE_TRANSPORT_APPTRACE)
|
||||
list(APPEND srcs "adapters/transport/adapter_transport_apptrace.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG)
|
||||
list(APPEND srcs "adapters/transport/adapter_transport_usb_serial_jtag.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(includes
|
||||
"include"
|
||||
)
|
||||
|
||||
set(priv_requires "esp_driver_gptimer")
|
||||
set(priv_requires
|
||||
"esp_driver_gptimer"
|
||||
"esp_hal_usb"
|
||||
"esp_driver_usb_serial_jtag"
|
||||
"esp_timer"
|
||||
)
|
||||
set(priv_includes "")
|
||||
set(requires "app_trace")
|
||||
|
||||
|
||||
@@ -34,6 +34,16 @@ menu "ESP Trace Configuration"
|
||||
config ESP_TRACE_TRANSPORT_APPTRACE
|
||||
bool "ESP-IDF apptrace"
|
||||
|
||||
config ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG
|
||||
bool "USB Serial JTAG"
|
||||
depends on SOC_USB_SERIAL_JTAG_SUPPORTED && !ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED
|
||||
help
|
||||
Use USB Serial JTAG peripheral as trace transport.
|
||||
|
||||
Note: This option is not available when USB Serial JTAG is used as
|
||||
primary or secondary console (ESP_CONSOLE_USB_SERIAL_JTAG or
|
||||
ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG).
|
||||
|
||||
config ESP_TRACE_TRANSPORT_EXTERNAL
|
||||
bool "External transport from component registry"
|
||||
depends on !ESP_TRACE_LIB_NONE
|
||||
@@ -48,6 +58,7 @@ menu "ESP Trace Configuration"
|
||||
config ESP_TRACE_TRANSPORT_NAME
|
||||
string
|
||||
default "apptrace" if ESP_TRACE_TRANSPORT_APPTRACE
|
||||
default "usb_serial_jtag" if ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG
|
||||
default "ext" if ESP_TRACE_TRANSPORT_EXTERNAL
|
||||
default "none" if ESP_TRACE_TRANSPORT_NONE
|
||||
|
||||
@@ -59,6 +70,21 @@ menu "ESP Trace Configuration"
|
||||
|
||||
rsource "$IDF_PATH/components/app_trace/Kconfig.apptrace"
|
||||
|
||||
menu "USB Serial JTAG Trace Configuration"
|
||||
depends on ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG
|
||||
|
||||
config ESP_TRACE_USJ_TX_BUFFER_SIZE
|
||||
int "TX buffer size"
|
||||
default 2048
|
||||
range 256 32768
|
||||
help
|
||||
Size of the TX ring buffer for USB Serial JTAG trace transport.
|
||||
Larger buffer allows more trace data to be queued before blocking.
|
||||
|
||||
Note: Buffer size must be a power of 2.
|
||||
|
||||
endmenu
|
||||
|
||||
choice ESP_TRACE_TIMESTAMP_SOURCE
|
||||
depends on ESP_TRACE_ENABLE
|
||||
prompt "Trace timestamp source"
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief USB-Serial-JTAG transport adapter for esp_trace
|
||||
*
|
||||
* This implementation uses LL (Low-Level) functions directly instead of the
|
||||
* high-level USB-Serial-JTAG driver to avoid FreeRTOS primitives, which would
|
||||
* cause deadlocks when tracing FreeRTOS operations with SystemView.
|
||||
*
|
||||
* Locking: This transport does not implement its own lock. The encoder (e.g. sysview)
|
||||
* is responsible for serializing access using esp_trace_lock_init(), esp_trace_lock_take(),
|
||||
* and esp_trace_lock_give(). All transport operations (read, write, flush_nolock) are
|
||||
* invoked while the encoder holds the lock, so no transport-level locking is required.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_rom_caps.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "hal/usb_serial_jtag_ll.h"
|
||||
#include "soc/usb_serial_jtag_struct.h"
|
||||
#include "driver/usb_serial_jtag.h"
|
||||
#include "esp_trace_registry.h"
|
||||
#include "esp_trace_port_transport.h"
|
||||
#include "esp_trace_types.h"
|
||||
#include "esp_trace_util.h"
|
||||
|
||||
static const char *TAG = "usj_transport";
|
||||
|
||||
/* Transport context */
|
||||
typedef struct {
|
||||
int inited; ///< Initialization flag (bitmask per core)
|
||||
esp_trace_rb_t tx_ring; ///< TX ring buffer
|
||||
esp_trace_rb_t rx_ring; ///< RX ring buffer
|
||||
|
||||
/* Flush configuration */
|
||||
uint32_t flush_tmo; ///< Flush timeout in microseconds
|
||||
uint32_t flush_thresh; ///< Flush threshold in bytes
|
||||
} usj_ctx_t;
|
||||
|
||||
#define USJ_FLUSH_TIMEOUT_US (1000000) // 1 second
|
||||
#define USJ_FLUSH_THRESH_BYTES (0) // 0 bytes
|
||||
|
||||
/* USB Serial JTAG hardware FIFO size (RX and TX) is 64 bytes (USB FS bulk endpoint max packet size) */
|
||||
#define USJ_HW_FIFO_SIZE (64)
|
||||
#define USJ_RX_BUFFER_SIZE USJ_HW_FIFO_SIZE
|
||||
|
||||
/* ----------------------- HW FIFO Helpers ----------------------- */
|
||||
static uint32_t usj_write_fifo(usj_ctx_t *ctx, esp_trace_rb_t *rb)
|
||||
{
|
||||
if (!usb_serial_jtag_ll_txfifo_writable()) {
|
||||
/* FIFO is full, no blocking */
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t *ptr;
|
||||
uint32_t to_send = esp_trace_rb_peek_contiguous(rb, &ptr);
|
||||
if (to_send == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t written = usb_serial_jtag_ll_write_txfifo(ptr, to_send);
|
||||
esp_trace_rb_consume(rb, written);
|
||||
|
||||
/* Flush to send data or zero-byte packet to end USB transfer */
|
||||
usb_serial_jtag_ll_txfifo_flush();
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static void usj_read_rx_fifo(usj_ctx_t *ctx)
|
||||
{
|
||||
uint8_t tmp[USJ_HW_FIFO_SIZE];
|
||||
while (usb_serial_jtag_ll_rxfifo_data_available()) {
|
||||
uint32_t n = usb_serial_jtag_ll_read_rxfifo(tmp, sizeof(tmp));
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
esp_trace_rb_put(&ctx->rx_ring, tmp, n);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------- Transport Functions ----------------------- */
|
||||
_Static_assert((CONFIG_ESP_TRACE_USJ_TX_BUFFER_SIZE & (CONFIG_ESP_TRACE_USJ_TX_BUFFER_SIZE - 1)) == 0,
|
||||
"CONFIG_ESP_TRACE_USJ_TX_BUFFER_SIZE must be a power of 2");
|
||||
|
||||
static esp_err_t usj_init(esp_trace_transport_t *tp, const void *tp_cfg)
|
||||
{
|
||||
(void)tp_cfg;
|
||||
|
||||
if (!tp) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Create context if not already done */
|
||||
if (!tp->ctx) {
|
||||
usj_ctx_t *ctx = heap_caps_calloc(1, sizeof(*ctx), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
if (!ctx) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
tp->ctx = ctx;
|
||||
}
|
||||
|
||||
usj_ctx_t *ctx = (usj_ctx_t *)tp->ctx;
|
||||
int core_id = esp_cpu_get_core_id();
|
||||
|
||||
/* Only do main setup on core 0 */
|
||||
if (core_id == 0) {
|
||||
/* Set default flush configuration */
|
||||
ctx->flush_tmo = USJ_FLUSH_TIMEOUT_US;
|
||||
ctx->flush_thresh = USJ_FLUSH_THRESH_BYTES;
|
||||
|
||||
/* Enable USB-Serial-JTAG peripheral module clock */
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
usb_serial_jtag_ll_enable_bus_clock(true);
|
||||
}
|
||||
|
||||
/* Configure USB PHY */
|
||||
#if USB_SERIAL_JTAG_LL_EXT_PHY_SUPPORTED
|
||||
usb_serial_jtag_ll_phy_enable_external(false); /* Use internal PHY */
|
||||
usb_serial_jtag_ll_phy_enable_pad(true); /* Enable USB PHY pads */
|
||||
#else
|
||||
usb_serial_jtag_ll_phy_set_defaults(); /* Set default PHY values */
|
||||
#endif
|
||||
|
||||
/* Disable RX and TX interrupts */
|
||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY
|
||||
| USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
|
||||
|
||||
/* Initialize TX ring buffer */
|
||||
esp_err_t ret = esp_trace_rb_init(&ctx->tx_ring, CONFIG_ESP_TRACE_USJ_TX_BUFFER_SIZE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize TX ring buffer");
|
||||
goto err_ctx;
|
||||
}
|
||||
|
||||
/* Initialize RX ring buffer to capture host commands */
|
||||
ret = esp_trace_rb_init(&ctx->rx_ring, USJ_RX_BUFFER_SIZE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize RX ring buffer");
|
||||
goto err_tx_ring;
|
||||
}
|
||||
|
||||
#if ESP_ROM_HAS_ETS_PRINTF_BUG
|
||||
/* Make sure no printf output is sent to USB-Serial-JTAG */
|
||||
extern bool g_usb_print;
|
||||
g_usb_print = false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
ctx->inited |= (1 << core_id);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err_tx_ring:
|
||||
heap_caps_free(ctx->tx_ring.buffer);
|
||||
err_ctx:
|
||||
heap_caps_free(ctx);
|
||||
tp->ctx = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t usj_read(esp_trace_transport_t *tp, void *data, size_t *size, uint32_t tmo)
|
||||
{
|
||||
usj_ctx_t *ctx = (usj_ctx_t *)tp->ctx;
|
||||
|
||||
if (!data || !size || *size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint8_t *buf = data;
|
||||
uint32_t req_size = *size;
|
||||
uint32_t total_read = 0;
|
||||
|
||||
esp_trace_tmo_t timeout;
|
||||
esp_trace_tmo_init(&timeout, tmo);
|
||||
|
||||
/* First read any pending RX data from the RX ring buffer */
|
||||
total_read = esp_trace_rb_get(&ctx->rx_ring, buf, req_size);
|
||||
|
||||
while (total_read < req_size) {
|
||||
/* Try to read from HW RX FIFO directly */
|
||||
if (usb_serial_jtag_ll_rxfifo_data_available()) {
|
||||
uint32_t to_read = req_size - total_read;
|
||||
uint32_t read = usb_serial_jtag_ll_read_rxfifo(buf + total_read, to_read);
|
||||
total_read += read;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (esp_trace_tmo_check(&timeout) != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
esp_rom_delay_us(100);
|
||||
}
|
||||
|
||||
*size = total_read;
|
||||
return (total_read > 0) ? ESP_OK : ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
static esp_err_t usj_write(esp_trace_transport_t *tp, const void *data, size_t size, uint32_t tmo)
|
||||
{
|
||||
(void)tmo; /* Write is non-blocking via ring buffer */
|
||||
|
||||
usj_ctx_t *ctx = (usj_ctx_t *)tp->ctx;
|
||||
esp_trace_rb_t *rb = &ctx->tx_ring;
|
||||
|
||||
if (!data || size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Read any new RX data into the RX ring buffer to avoid losing host commands in case of heavy trace output */
|
||||
usj_read_rx_fifo(ctx);
|
||||
|
||||
/* Add data to TX ring buffer */
|
||||
esp_trace_rb_put(rb, (const uint8_t *)data, size);
|
||||
|
||||
/* Try to flush some data to HW FIFO immediately (non-blocking) */
|
||||
while (esp_trace_rb_data_len(rb) > 0) {
|
||||
if (usj_write_fifo(ctx, rb) == 0) {
|
||||
break; /* FIFO full, will be drained on next write or flush */
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t usj_down_buffer_config(esp_trace_transport_t *tp, uint8_t *buf, uint32_t size)
|
||||
{
|
||||
(void)tp;
|
||||
(void)buf;
|
||||
(void)size;
|
||||
|
||||
/* No action needed - data was already read in get function */
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t usj_flush_nolock(esp_trace_transport_t *tp)
|
||||
{
|
||||
usj_ctx_t *ctx = (usj_ctx_t *)tp->ctx;
|
||||
esp_trace_rb_t *rb = &ctx->tx_ring;
|
||||
|
||||
uint32_t pending = esp_trace_rb_data_len(rb);
|
||||
if (pending < ctx->flush_thresh) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_trace_tmo_t timeout;
|
||||
esp_trace_tmo_init(&timeout, ctx->flush_tmo);
|
||||
|
||||
/* Drain ring buffer to HW FIFO */
|
||||
while (esp_trace_rb_data_len(rb) > 0) {
|
||||
usj_write_fifo(ctx, rb);
|
||||
if (esp_trace_tmo_check(&timeout) != ESP_OK) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
esp_rom_delay_us(100);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool usj_is_host_connected(esp_trace_transport_t *tp)
|
||||
{
|
||||
(void)tp;
|
||||
return usb_serial_jtag_is_connected();
|
||||
}
|
||||
|
||||
static esp_trace_link_types_t usj_get_link_type(esp_trace_transport_t *tp)
|
||||
{
|
||||
(void)tp;
|
||||
return ESP_TRACE_LINK_USB_SERIAL_JTAG;
|
||||
}
|
||||
|
||||
static esp_err_t usj_set_config(esp_trace_transport_t *tp, esp_trace_transport_cfg_key_t key, const void *value)
|
||||
{
|
||||
if (!value) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
usj_ctx_t *ctx = (usj_ctx_t *)tp->ctx;
|
||||
if (!ctx) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case ESP_TRACE_TRANSPORT_CFG_HEADER_SIZE:
|
||||
/* USB-Serial-JTAG doesn't need header size configuration */
|
||||
return ESP_OK;
|
||||
case ESP_TRACE_TRANSPORT_CFG_FLUSH_TMO:
|
||||
ctx->flush_tmo = *(const uint32_t *)value;
|
||||
return ESP_OK;
|
||||
case ESP_TRACE_TRANSPORT_CFG_FLUSH_THRESH:
|
||||
ctx->flush_thresh = *(const uint32_t *)value;
|
||||
return ESP_OK;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Key %d is not supported", key);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t usj_get_config(esp_trace_transport_t *tp, esp_trace_transport_cfg_key_t key, void *value)
|
||||
{
|
||||
if (!value) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
usj_ctx_t *ctx = (usj_ctx_t *)tp->ctx;
|
||||
if (!ctx) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case ESP_TRACE_TRANSPORT_CFG_FLUSH_TMO:
|
||||
*(uint32_t *)value = ctx->flush_tmo;
|
||||
return ESP_OK;
|
||||
case ESP_TRACE_TRANSPORT_CFG_FLUSH_THRESH:
|
||||
*(uint32_t *)value = ctx->flush_thresh;
|
||||
return ESP_OK;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Key %d is not supported", key);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static void usj_panic_handler(esp_trace_transport_t *tp, const void *info)
|
||||
{
|
||||
(void)info;
|
||||
usj_flush_nolock(tp);
|
||||
}
|
||||
|
||||
/* ----------------------- Transport Registration ----------------------- */
|
||||
static const esp_trace_transport_vtable_t s_usb_serial_jtag_vt = {
|
||||
.init = usj_init,
|
||||
.set_config = usj_set_config,
|
||||
.get_config = usj_get_config,
|
||||
.read = usj_read,
|
||||
.write = usj_write,
|
||||
.flush_nolock = usj_flush_nolock,
|
||||
.down_buffer_config = usj_down_buffer_config,
|
||||
.is_host_connected = usj_is_host_connected,
|
||||
.get_link_type = usj_get_link_type,
|
||||
.panic_handler = usj_panic_handler,
|
||||
};
|
||||
|
||||
ESP_TRACE_REGISTER_TRANSPORT("usb_serial_jtag", &s_usb_serial_jtag_vt);
|
||||
@@ -18,6 +18,7 @@ typedef enum {
|
||||
ESP_TRACE_LINK_UNKNOWN = 0,
|
||||
ESP_TRACE_LINK_DEBUG_PROBE,
|
||||
ESP_TRACE_LINK_UART,
|
||||
ESP_TRACE_LINK_USB_SERIAL_JTAG,
|
||||
} esp_trace_link_types_t;
|
||||
|
||||
/* Timeout constants for trace operations */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -118,6 +118,87 @@ esp_err_t esp_trace_lock_take(esp_trace_lock_t *lock, uint32_t tmo_us);
|
||||
*/
|
||||
esp_err_t esp_trace_lock_give(esp_trace_lock_t *lock);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////// RING BUFFER //////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Power-of-2 ring buffer for trace transports
|
||||
*
|
||||
* Lightweight, FreeRTOS-free ring buffer suitable for use in trace hot paths
|
||||
* (ISR-safe contexts, critical sections). Size must be a power of 2.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buffer; ///< Heap-allocated data buffer
|
||||
uint32_t max_size; ///< Buffer capacity (must be power of 2)
|
||||
volatile uint32_t count; ///< Bytes currently stored
|
||||
volatile uint32_t head; ///< Write index
|
||||
volatile uint32_t tail; ///< Read index
|
||||
} esp_trace_rb_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize ring buffer (allocates internal memory)
|
||||
*
|
||||
* @param rb Pointer to ring buffer structure
|
||||
* @param size Buffer size in bytes (must be power of 2)
|
||||
* @return ESP_OK on success, ESP_ERR_NO_MEM on allocation failure
|
||||
*/
|
||||
esp_err_t esp_trace_rb_init(esp_trace_rb_t *rb, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief Get number of bytes currently stored in the ring buffer
|
||||
*/
|
||||
static inline uint32_t esp_trace_rb_data_len(const esp_trace_rb_t *rb)
|
||||
{
|
||||
return rb->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write data into the ring buffer (overwrites oldest data if full)
|
||||
*
|
||||
* @param rb Ring buffer
|
||||
* @param data Source data
|
||||
* @param len Number of bytes to write
|
||||
* @return ESP_OK always (data is always accepted; oldest data may be dropped)
|
||||
*/
|
||||
esp_err_t esp_trace_rb_put(esp_trace_rb_t *rb, const uint8_t *data, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Read and consume data from the ring buffer
|
||||
*
|
||||
* @param rb Ring buffer
|
||||
* @param data Destination buffer
|
||||
* @param len Maximum number of bytes to read
|
||||
* @return Number of bytes actually read
|
||||
*/
|
||||
uint32_t esp_trace_rb_get(esp_trace_rb_t *rb, uint8_t *data, uint32_t len);
|
||||
|
||||
/**
|
||||
* @brief Peek at contiguous readable data without consuming
|
||||
*
|
||||
* Returns a pointer to the contiguous block of data starting at the tail.
|
||||
* When the readable region wraps around the end of the buffer, only the
|
||||
* first contiguous portion is returned. Call again after consume() for the rest.
|
||||
*
|
||||
* @param rb Ring buffer
|
||||
* @param[out] data Set to point at the contiguous data (valid until next put/consume)
|
||||
* @return Number of contiguous bytes available (0 if empty)
|
||||
*/
|
||||
uint32_t esp_trace_rb_peek_contiguous(const esp_trace_rb_t *rb, const uint8_t **data);
|
||||
|
||||
/**
|
||||
* @brief Consume (discard) bytes from the read side of the ring buffer
|
||||
*
|
||||
* Typically called after esp_trace_rb_peek_contiguous() + processing.
|
||||
*
|
||||
* @param rb Ring buffer
|
||||
* @param len Number of bytes to consume (must be <= esp_trace_rb_data_len())
|
||||
*/
|
||||
void esp_trace_rb_consume(esp_trace_rb_t *rb, uint32_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,8 @@ entries:
|
||||
port_utils (noflash)
|
||||
if ESP_TRACE_TRANSPORT_APPTRACE:
|
||||
adapter_transport_apptrace (noflash)
|
||||
if ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG:
|
||||
adapter_transport_usb_serial_jtag (noflash)
|
||||
|
||||
[mapping:esp_trace_driver]
|
||||
archive: libesp_driver_gptimer.a
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
@@ -152,3 +154,104 @@ esp_err_t esp_trace_lock_give(esp_trace_lock_t *lock)
|
||||
portEXIT_CRITICAL(&lock->mux);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////// RING BUFFER //////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
static inline uint32_t rb_mask(const esp_trace_rb_t *rb)
|
||||
{
|
||||
return rb->max_size - 1;
|
||||
}
|
||||
|
||||
static inline void rb_advance_tail(esp_trace_rb_t *rb, uint32_t n)
|
||||
{
|
||||
rb->tail = (rb->tail + n) & rb_mask(rb);
|
||||
rb->count -= n;
|
||||
}
|
||||
|
||||
static inline void rb_advance_head(esp_trace_rb_t *rb, uint32_t n)
|
||||
{
|
||||
rb->head = (rb->head + n) & rb_mask(rb);
|
||||
rb->count += n;
|
||||
}
|
||||
|
||||
esp_err_t esp_trace_rb_init(esp_trace_rb_t *rb, uint32_t size)
|
||||
{
|
||||
rb->buffer = heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
if (!rb->buffer) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
rb->max_size = size;
|
||||
rb->count = 0;
|
||||
rb->head = 0;
|
||||
rb->tail = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_trace_rb_put(esp_trace_rb_t *rb, const uint8_t *data, uint32_t len)
|
||||
{
|
||||
/* Drop oldest data if needed to make room */
|
||||
uint32_t free_len = rb->max_size - rb->count;
|
||||
if (len > free_len) {
|
||||
rb_advance_tail(rb, len - free_len);
|
||||
}
|
||||
|
||||
uint32_t head = rb->head;
|
||||
uint32_t space_to_end = rb->max_size - head;
|
||||
|
||||
if (len <= space_to_end) {
|
||||
memcpy(&rb->buffer[head], data, len);
|
||||
} else {
|
||||
memcpy(&rb->buffer[head], data, space_to_end);
|
||||
memcpy(&rb->buffer[0], &data[space_to_end], len - space_to_end);
|
||||
}
|
||||
|
||||
rb_advance_head(rb, len);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t esp_trace_rb_get(esp_trace_rb_t *rb, uint8_t *data, uint32_t len)
|
||||
{
|
||||
uint32_t available = rb->count;
|
||||
if (available == 0 || len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t to_read = MIN(len, available);
|
||||
uint32_t tail = rb->tail;
|
||||
uint32_t cont = rb->max_size - tail;
|
||||
|
||||
if (to_read <= cont) {
|
||||
memcpy(data, &rb->buffer[tail], to_read);
|
||||
} else {
|
||||
memcpy(data, &rb->buffer[tail], cont);
|
||||
memcpy(&data[cont], &rb->buffer[0], to_read - cont);
|
||||
}
|
||||
|
||||
rb_advance_tail(rb, to_read);
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
||||
uint32_t esp_trace_rb_peek_contiguous(const esp_trace_rb_t *rb, const uint8_t **data)
|
||||
{
|
||||
uint32_t used = rb->count;
|
||||
if (used == 0) {
|
||||
*data = NULL;
|
||||
return 0;
|
||||
}
|
||||
*data = &rb->buffer[rb->tail];
|
||||
uint32_t contiguous = rb->max_size - rb->tail;
|
||||
return MIN(used, contiguous);
|
||||
}
|
||||
|
||||
void esp_trace_rb_consume(esp_trace_rb_t *rb, uint32_t len)
|
||||
{
|
||||
rb_advance_tail(rb, len);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_app_trace.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gptimer.h"
|
||||
#include "soc/uart_pins.h"
|
||||
#include "esp_trace.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
@@ -127,6 +125,9 @@ static void example_task(void *p)
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_TRACE_TRANSPORT_APPTRACE
|
||||
#include "soc/uart_pins.h"
|
||||
#include "esp_app_trace.h"
|
||||
esp_trace_open_params_t esp_trace_get_user_params(void)
|
||||
{
|
||||
static esp_apptrace_config_t app_trace_config = APPTRACE_CONFIG_DEFAULT();
|
||||
@@ -145,6 +146,19 @@ esp_trace_open_params_t esp_trace_get_user_params(void)
|
||||
};
|
||||
return trace_params;
|
||||
}
|
||||
#elif CONFIG_ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG
|
||||
esp_trace_open_params_t esp_trace_get_user_params(void)
|
||||
{
|
||||
esp_trace_open_params_t trace_params = {
|
||||
.core_cfg = NULL,
|
||||
.encoder_name = "sysview",
|
||||
.encoder_cfg = NULL,
|
||||
.transport_name = "usb_serial_jtag",
|
||||
.transport_cfg = NULL,
|
||||
};
|
||||
return trace_params;
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ import pytest
|
||||
import serial
|
||||
from pytest_embedded_idf import IdfDut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
from pytest_embedded_idf.utils import soc_filtered_targets
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from conftest import OpenOCD
|
||||
@@ -166,3 +167,15 @@ def test_sysview_tracing_uart(dut: IdfDut) -> None:
|
||||
@idf_parametrize('target', ['esp32c2'], indirect=['target'])
|
||||
def test_sysview_tracing_uart_c2(dut: IdfDut) -> None:
|
||||
_test_sysview_tracing_uart(dut)
|
||||
|
||||
|
||||
@pytest.mark.usb_serial_jtag
|
||||
@idf_parametrize('target', soc_filtered_targets('SOC_USB_SERIAL_JTAG_SUPPORTED == 1'), indirect=['target'])
|
||||
@pytest.mark.parametrize('config', [pytest.param('sysview_usj')], indirect=True)
|
||||
def test_sysview_tracing_usj_serial(dut: IdfDut) -> None:
|
||||
time.sleep(1) # wait for USJ port to be ready
|
||||
usj_port = '/dev/serial_ports/ttyACM-esp32'
|
||||
ser = serial.Serial(usj_port, baudrate=1000000, timeout=10)
|
||||
trace_log = [os.path.join(dut.logdir, 'sys_log_usj.svdat')] # pylint: disable=protected-access
|
||||
_capture_sysview_trace(ser, trace_log[0])
|
||||
_validate_trace_data(trace_log, dut.target, is_uart=True)
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
|
||||
CONFIG_APPTRACE_DEST_JTAG=y
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
CONFIG_ESP_CONSOLE_NONE=y
|
||||
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
|
||||
CONFIG_APPTRACE_DEST_UART=y
|
||||
CONFIG_APPTRACE_DEST_UART_NUM=0
|
||||
CONFIG_APPTRACE_UART_BAUDRATE=1000000
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
CONFIG_IDF_TARGET="esp32c2"
|
||||
CONFIG_ESP_CONSOLE_NONE=y
|
||||
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
|
||||
CONFIG_APPTRACE_DEST_UART=y
|
||||
CONFIG_APPTRACE_UART_BAUDRATE=74880
|
||||
CONFIG_XTAL_FREQ_26=y
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_ESP_CONSOLE_SECONDARY_NONE=y
|
||||
CONFIG_ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG=y
|
||||
CONFIG_USE_CUSTOM_EVENT_ID=y
|
||||
@@ -3,7 +3,6 @@ CONFIG_FREERTOS_HZ=1000
|
||||
# Enable FreeRTOS SystemView Tracing by default
|
||||
CONFIG_ESP_TRACE_ENABLE=y
|
||||
CONFIG_ESP_TRACE_LIB_EXTERNAL=y
|
||||
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
|
||||
CONFIG_ESP_TRACE_TS_SOURCE_ESP_TIMER=y
|
||||
CONFIG_SEGGER_SYSVIEW_EVT_OVERFLOW_ENABLE=y
|
||||
CONFIG_SEGGER_SYSVIEW_EVT_ISR_ENTER_ENABLE=y
|
||||
|
||||
Reference in New Issue
Block a user