mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03: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)
|
if(CONFIG_ESP_TRACE_TRANSPORT_APPTRACE)
|
||||||
list(APPEND srcs "adapters/transport/adapter_transport_apptrace.c")
|
list(APPEND srcs "adapters/transport/adapter_transport_apptrace.c")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG)
|
||||||
|
list(APPEND srcs "adapters/transport/adapter_transport_usb_serial_jtag.c")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(includes
|
set(includes
|
||||||
"include"
|
"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(priv_includes "")
|
||||||
set(requires "app_trace")
|
set(requires "app_trace")
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,16 @@ menu "ESP Trace Configuration"
|
|||||||
config ESP_TRACE_TRANSPORT_APPTRACE
|
config ESP_TRACE_TRANSPORT_APPTRACE
|
||||||
bool "ESP-IDF 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
|
config ESP_TRACE_TRANSPORT_EXTERNAL
|
||||||
bool "External transport from component registry"
|
bool "External transport from component registry"
|
||||||
depends on !ESP_TRACE_LIB_NONE
|
depends on !ESP_TRACE_LIB_NONE
|
||||||
@@ -48,6 +58,7 @@ menu "ESP Trace Configuration"
|
|||||||
config ESP_TRACE_TRANSPORT_NAME
|
config ESP_TRACE_TRANSPORT_NAME
|
||||||
string
|
string
|
||||||
default "apptrace" if ESP_TRACE_TRANSPORT_APPTRACE
|
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 "ext" if ESP_TRACE_TRANSPORT_EXTERNAL
|
||||||
default "none" if ESP_TRACE_TRANSPORT_NONE
|
default "none" if ESP_TRACE_TRANSPORT_NONE
|
||||||
|
|
||||||
@@ -59,6 +70,21 @@ menu "ESP Trace Configuration"
|
|||||||
|
|
||||||
rsource "$IDF_PATH/components/app_trace/Kconfig.apptrace"
|
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
|
choice ESP_TRACE_TIMESTAMP_SOURCE
|
||||||
depends on ESP_TRACE_ENABLE
|
depends on ESP_TRACE_ENABLE
|
||||||
prompt "Trace timestamp source"
|
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_UNKNOWN = 0,
|
||||||
ESP_TRACE_LINK_DEBUG_PROBE,
|
ESP_TRACE_LINK_DEBUG_PROBE,
|
||||||
ESP_TRACE_LINK_UART,
|
ESP_TRACE_LINK_UART,
|
||||||
|
ESP_TRACE_LINK_USB_SERIAL_JTAG,
|
||||||
} esp_trace_link_types_t;
|
} esp_trace_link_types_t;
|
||||||
|
|
||||||
/* Timeout constants for trace operations */
|
/* 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
|
* 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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ entries:
|
|||||||
port_utils (noflash)
|
port_utils (noflash)
|
||||||
if ESP_TRACE_TRANSPORT_APPTRACE:
|
if ESP_TRACE_TRANSPORT_APPTRACE:
|
||||||
adapter_transport_apptrace (noflash)
|
adapter_transport_apptrace (noflash)
|
||||||
|
if ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG:
|
||||||
|
adapter_transport_usb_serial_jtag (noflash)
|
||||||
|
|
||||||
[mapping:esp_trace_driver]
|
[mapping:esp_trace_driver]
|
||||||
archive: libesp_driver_gptimer.a
|
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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
#include "esp_clk_tree.h"
|
#include "esp_clk_tree.h"
|
||||||
#include "esp_cpu.h"
|
#include "esp_cpu.h"
|
||||||
#include "esp_private/esp_clk.h"
|
#include "esp_private/esp_clk.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.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);
|
portEXIT_CRITICAL(&lock->mux);
|
||||||
return ESP_OK;
|
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 <stdlib.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_app_trace.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "driver/gptimer.h"
|
#include "driver/gptimer.h"
|
||||||
#include "soc/uart_pins.h"
|
|
||||||
#include "esp_trace.h"
|
#include "esp_trace.h"
|
||||||
|
|
||||||
static const char *TAG = "example";
|
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)
|
esp_trace_open_params_t esp_trace_get_user_params(void)
|
||||||
{
|
{
|
||||||
static esp_apptrace_config_t app_trace_config = APPTRACE_CONFIG_DEFAULT();
|
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;
|
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)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import pytest
|
|||||||
import serial
|
import serial
|
||||||
from pytest_embedded_idf import IdfDut
|
from pytest_embedded_idf import IdfDut
|
||||||
from pytest_embedded_idf.utils import idf_parametrize
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
from pytest_embedded_idf.utils import soc_filtered_targets
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from conftest import OpenOCD
|
from conftest import OpenOCD
|
||||||
@@ -158,3 +159,15 @@ def _test_sysview_tracing_uart(dut: IdfDut) -> None:
|
|||||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||||
def test_sysview_tracing_uart(dut: IdfDut) -> None:
|
def test_sysview_tracing_uart(dut: IdfDut) -> None:
|
||||||
_test_sysview_tracing_uart(dut)
|
_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
|
CONFIG_APPTRACE_DEST_JTAG=y
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
CONFIG_ESP_CONSOLE_NONE=y
|
CONFIG_ESP_CONSOLE_NONE=y
|
||||||
|
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
|
||||||
CONFIG_APPTRACE_DEST_UART=y
|
CONFIG_APPTRACE_DEST_UART=y
|
||||||
CONFIG_APPTRACE_DEST_UART_NUM=0
|
CONFIG_APPTRACE_DEST_UART_NUM=0
|
||||||
CONFIG_APPTRACE_UART_BAUDRATE=1000000
|
CONFIG_APPTRACE_UART_BAUDRATE=1000000
|
||||||
|
|||||||
@@ -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
|
# Enable FreeRTOS SystemView Tracing by default
|
||||||
CONFIG_ESP_TRACE_ENABLE=y
|
CONFIG_ESP_TRACE_ENABLE=y
|
||||||
CONFIG_ESP_TRACE_LIB_EXTERNAL=y
|
CONFIG_ESP_TRACE_LIB_EXTERNAL=y
|
||||||
CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y
|
|
||||||
CONFIG_ESP_TRACE_TS_SOURCE_ESP_TIMER=y
|
CONFIG_ESP_TRACE_TS_SOURCE_ESP_TIMER=y
|
||||||
CONFIG_SEGGER_SYSVIEW_EVT_OVERFLOW_ENABLE=y
|
CONFIG_SEGGER_SYSVIEW_EVT_OVERFLOW_ENABLE=y
|
||||||
CONFIG_SEGGER_SYSVIEW_EVT_ISR_ENTER_ENABLE=y
|
CONFIG_SEGGER_SYSVIEW_EVT_ISR_ENTER_ENABLE=y
|
||||||
|
|||||||
Reference in New Issue
Block a user