From 05010137f82e914a1a95d6feaa2d2f9145dfe903 Mon Sep 17 00:00:00 2001 From: Erhan Kurubas Date: Tue, 3 Feb 2026 14:11:43 +0100 Subject: [PATCH] feat(esp_trace): add usb-serial-jtag as a transport channel --- components/esp_trace/CMakeLists.txt | 11 +- components/esp_trace/Kconfig | 26 ++ .../adapter_transport_usb_serial_jtag.c | 361 ++++++++++++++++++ .../esp_trace/include/esp_trace_types.h | 1 + components/esp_trace/include/esp_trace_util.h | 83 +++- components/esp_trace/linker.lf | 2 + components/esp_trace/src/ports/port_utils.c | 105 ++++- .../sysview_tracing/main/sysview_tracing.c | 18 +- .../sysview_tracing/pytest_sysview_tracing.py | 13 + .../sysview_tracing/sdkconfig.ci.sysview_jtag | 1 + .../sysview_tracing/sdkconfig.ci.sysview_uart | 1 + .../sdkconfig.ci.sysview_uart_esp32c2_26Mhz | 1 + .../sysview_tracing/sdkconfig.ci.sysview_usj | 3 + .../system/sysview_tracing/sdkconfig.defaults | 1 - 14 files changed, 621 insertions(+), 6 deletions(-) create mode 100644 components/esp_trace/adapters/transport/adapter_transport_usb_serial_jtag.c create mode 100644 examples/system/sysview_tracing/sdkconfig.ci.sysview_usj diff --git a/components/esp_trace/CMakeLists.txt b/components/esp_trace/CMakeLists.txt index 1e26e6ce7f..876cdf00e7 100644 --- a/components/esp_trace/CMakeLists.txt +++ b/components/esp_trace/CMakeLists.txt @@ -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") diff --git a/components/esp_trace/Kconfig b/components/esp_trace/Kconfig index 944ac322fb..f37761be60 100644 --- a/components/esp_trace/Kconfig +++ b/components/esp_trace/Kconfig @@ -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" diff --git a/components/esp_trace/adapters/transport/adapter_transport_usb_serial_jtag.c b/components/esp_trace/adapters/transport/adapter_transport_usb_serial_jtag.c new file mode 100644 index 0000000000..b6d2415b85 --- /dev/null +++ b/components/esp_trace/adapters/transport/adapter_transport_usb_serial_jtag.c @@ -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 +#include +#include +#include +#include + +#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); diff --git a/components/esp_trace/include/esp_trace_types.h b/components/esp_trace/include/esp_trace_types.h index 5b1efae25a..dae063aa79 100644 --- a/components/esp_trace/include/esp_trace_types.h +++ b/components/esp_trace/include/esp_trace_types.h @@ -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 */ diff --git a/components/esp_trace/include/esp_trace_util.h b/components/esp_trace/include/esp_trace_util.h index a2c158b6c8..e95d5ceea3 100644 --- a/components/esp_trace/include/esp_trace_util.h +++ b/components/esp_trace/include/esp_trace_util.h @@ -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 + +/** + * @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 diff --git a/components/esp_trace/linker.lf b/components/esp_trace/linker.lf index b2dbeec930..555ca4dad1 100644 --- a/components/esp_trace/linker.lf +++ b/components/esp_trace/linker.lf @@ -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 diff --git a/components/esp_trace/src/ports/port_utils.c b/components/esp_trace/src/ports/port_utils.c index 30cad3d97c..f2cc592c68 100644 --- a/components/esp_trace/src/ports/port_utils.c +++ b/components/esp_trace/src/ports/port_utils.c @@ -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 +#include #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); +} diff --git a/examples/system/sysview_tracing/main/sysview_tracing.c b/examples/system/sysview_tracing/main/sysview_tracing.c index 7950596355..b988471ba2 100644 --- a/examples/system/sysview_tracing/main/sysview_tracing.c +++ b/examples/system/sysview_tracing/main/sysview_tracing.c @@ -15,11 +15,9 @@ #include #include #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) { diff --git a/examples/system/sysview_tracing/pytest_sysview_tracing.py b/examples/system/sysview_tracing/pytest_sysview_tracing.py index 0a3b1b5852..f2f4ac5ae7 100644 --- a/examples/system/sysview_tracing/pytest_sysview_tracing.py +++ b/examples/system/sysview_tracing/pytest_sysview_tracing.py @@ -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) diff --git a/examples/system/sysview_tracing/sdkconfig.ci.sysview_jtag b/examples/system/sysview_tracing/sdkconfig.ci.sysview_jtag index 772bddde15..621f02d84f 100644 --- a/examples/system/sysview_tracing/sdkconfig.ci.sysview_jtag +++ b/examples/system/sysview_tracing/sdkconfig.ci.sysview_jtag @@ -1 +1,2 @@ +CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y CONFIG_APPTRACE_DEST_JTAG=y diff --git a/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart b/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart index 15555d5e93..2d827ec7bb 100644 --- a/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart +++ b/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart @@ -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 diff --git a/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart_esp32c2_26Mhz b/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart_esp32c2_26Mhz index a582e762d8..d8e38ccf8f 100644 --- a/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart_esp32c2_26Mhz +++ b/examples/system/sysview_tracing/sdkconfig.ci.sysview_uart_esp32c2_26Mhz @@ -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 diff --git a/examples/system/sysview_tracing/sdkconfig.ci.sysview_usj b/examples/system/sysview_tracing/sdkconfig.ci.sysview_usj new file mode 100644 index 0000000000..cafe4275e6 --- /dev/null +++ b/examples/system/sysview_tracing/sdkconfig.ci.sysview_usj @@ -0,0 +1,3 @@ +CONFIG_ESP_CONSOLE_SECONDARY_NONE=y +CONFIG_ESP_TRACE_TRANSPORT_USB_SERIAL_JTAG=y +CONFIG_USE_CUSTOM_EVENT_ID=y diff --git a/examples/system/sysview_tracing/sdkconfig.defaults b/examples/system/sysview_tracing/sdkconfig.defaults index 9a260fdef2..b58df2ad9c 100644 --- a/examples/system/sysview_tracing/sdkconfig.defaults +++ b/examples/system/sysview_tracing/sdkconfig.defaults @@ -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