feat(ble_log): make TS sync unconditional with toggle-IO-only Kconfig

TS sync now always runs: the periodic tick (TS sample, OPEN transport
flush, internal snapshot) exists in every build, so the unified periodic
output no longer depends on BLE_LOG_TS_ENABLED and idle systems without
dispatch activity still flush every second.

- New BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED gates only the analyzer toggle
  IO (GPIO config, level writes, reset); with it unset, TS snapshots
  still sample the clocks and carry io_level = 0. BLE_LOG_TS_ENABLED
  keeps its prompt as a deprecated compatibility entry that selects
  BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED, so existing projects keep the old
  toggle behavior, and the TS trigger entries drop their dependency on
  it.
- ble_log_ts is no longer a separate module: its 95-line shell (clock
  sampling, toggle IO management) joins ble_log_rt.c, the only runtime
  that drives it. ble_log_ts_info_t moves to ble_log_lbm_v2.h (the
  snapshot interface that consumes it), the BLE_LOG_GET_LC_TS chip
  table moves next to its single caller, the init/deinit folds into
  ble_log_rt_init/deinit, the reset folds into ble_log_sync_enable, and
  the sampler becomes a void static now that its failure paths are gone.
- The runtime hook is gone: with the tick always present, its throttled
  defer-callback fallback for runtime-disabled sync served no one. The
  TS tick is the only periodic output source; ble_log_sync_enable(false)
  now means full periodic silence, and ble_log_rt_dispatch loses its
  return value. The version-info regression now arms the tick instead
  of the hook.
- BLE_LOG_GET_LC_TS branches on CONFIG_BT_DUAL_MODE_ARCH: the
  dual-mode-arch controllers (ESP32-H4, ESP32-S31) expose their link
  layer timer as r_sched_timer_getCurrentTimeU32, but the symbol is
  obfuscated in the current prebuilt libraries, so those targets report
  lc_ts = 0 for now; call the accessor once the libraries export it.
  The Gen 2 branch keeps r_ble_lll_timer_current_tick_get (defined by
  every C5/C6/C61/H2/H21 library, verified by nm and by linking
  ble_log_test for ESP32-C6); ESP32-H4 and ESP32-S31 fall out of it.
- ble_log_ts_info_update's old shape is gone entirely: the heap-allocated
  global ts_info and its critical section memcpy were leftovers from the
  pointer-return API; the sampler writes the caller object in place and
  keeps only the toggle IO phase as cross-call state. int_src_code is
  filled outside the critical section; the phase toggle stays inside to
  exclude the write in ble_log_sync_enable.

Idle systems no longer touch the controller clock: the legacy
accessors dereference controller state and INIT precedes controller
initialization, so the sampler returns lc_ts = 0 while the controller
is idle instead of reading it. The deferred dispatch callback drains
only the queue depth observed at entry and re-arms itself for arrivals
left behind, so it cannot monopolize the shared ESP timer task.
This commit is contained in:
Zhou Xiao
2026-09-02 14:55:10 +08:00
committed by guozifan
parent fdf4942485
commit 06478ec313
15 changed files with 156 additions and 282 deletions

View File

@@ -185,11 +185,6 @@ if(CONFIG_BLE_LOG_ENABLED)
"${CMAKE_CURRENT_LIST_DIR}/ble_log/src/internal_include/prph"
)
# Timestamp synchronization extension
if(CONFIG_BLE_LOG_TS_ENABLED)
list(APPEND bt_common_srcs "${CMAKE_CURRENT_LIST_DIR}/ble_log/src/ble_log_ts.c")
endif()
# Peripheral interface implementation
if(CONFIG_BLE_LOG_PRPH_DUMMY)
list(APPEND bt_common_srcs

View File

@@ -92,18 +92,19 @@ if BLE_LOG_ENABLED
Deprecated. Renamed to BLE_LOG_HCI_LOG_ENABLED; kept so sdkconfig
files pinning the old name keep Host side HCI logging enabled.
config BLE_LOG_TS_ENABLED
bool "Enable BLE Log Timestamp Synchronization (TS)"
config BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED
bool "Toggle a GPIO on every BLE Log TS sync sample"
default n
help
Enable BLE Log TS with external logging module. Synchronization is
triggered periodically by an ESP Timer using task dispatch. The
timer does not wake the system from light sleep.
BLE Log TS sync always runs: every periodic output tick samples
the link-layer, ESP and OS clocks into the internal snapshot.
Enable this option to additionally toggle a GPIO on every
sample so an external logic analyzer can align the device
clocks with the capture.
if BLE_LOG_TS_ENABLED
if BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED
config BLE_LOG_SYNC_IO_NUM
int "GPIO number for Timestamp Synchronization (TS) toggle output"
depends on BLE_LOG_TS_ENABLED
default 0
help
GPIO number for TS toggle output
@@ -252,27 +253,22 @@ if BLE_LOG_ENABLED
config BLE_LOG_TS_TRIGGER_TIMEOUT_MS
int
depends on BLE_LOG_TS_ENABLED
default 1000
config BLE_LOG_TS_TRIGGER_CHOICE
bool
depends on BLE_LOG_TS_ENABLED
default y
config BLE_LOG_TS_TRIGGER_ESP_TIMER
bool
depends on BLE_LOG_TS_ENABLED
default y
config BLE_LOG_TS_TRIGGER_TASK_EVENT
bool
depends on BLE_LOG_TS_ENABLED
default n
config BLE_LOG_TS_TRIGGER_ESP_TIMER_ISR_DISPATCH_METHOD
bool
depends on BLE_LOG_TS_ENABLED
default n
endif

View File

@@ -74,8 +74,6 @@ void ble_log_dump_to_console(void);
void ble_log_write_hex_ll(uint32_t len, const uint8_t *addr,
uint32_t len_append, const uint8_t *addr_append, uint32_t flag);
#endif /* CONFIG_BLE_LOG_LL_ENABLED */
#if CONFIG_BLE_LOG_TS_ENABLED
bool ble_log_sync_enable(bool enable);
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
#endif /* __BLE_LOG_H__ */

View File

@@ -15,9 +15,6 @@
#include "ble_log_util.h"
#include "esp_log.h"
#include "esp_system.h"
#if CONFIG_BLE_LOG_TS_ENABLED
#include "ble_log_ts.h"
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
/* VARIABLE */
#define TAG "ble_log"
@@ -38,13 +35,6 @@ bool ble_log_init(void)
return true;
}
#if CONFIG_BLE_LOG_TS_ENABLED
/* Initialize BLE Log TS */
if (!ble_log_ts_init()) {
goto exit;
}
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
/* Allocate pool and dedicated Internal transport before runtime starts. */
if (!ble_log_lbm_init()) {
goto exit;
@@ -118,9 +108,4 @@ void ble_log_deinit(void)
ble_log_rt_deinit();
ble_log_prph_deinit();
ble_log_lbm_deinit();
#if CONFIG_BLE_LOG_TS_ENABLED
/* Deinitialize BLE Log TS */
ble_log_ts_deinit();
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
}

View File

@@ -12,63 +12,81 @@
#include "ble_log.h"
#include "ble_log_rt.h"
#include "ble_log_lbm_v2.h"
#include "ble_log_util.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_chip_info.h"
#if CONFIG_BLE_LOG_LL_ENABLED
#include "esp_bt.h"
#endif
#include "driver/gpio.h"
/* MACRO */
#define TAG "ble_log_rt"
#define BLE_LOG_RT_DEFER_TIMEOUT_US (1000)
#if CONFIG_BT_CONTROLLER_ENABLED
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
extern const char *btdm_controller_get_compile_version(void);
#define BLE_LOG_CONTROLLER_GET_COMMIT() btdm_controller_get_compile_version()
#elif !CONFIG_BT_DUAL_MODE_ARCH || CONFIG_BT_CTRL_BLE_ENABLE
/* BR/EDR-only dual-mode builds do not link the BLE controller lib */
extern char *ble_controller_get_compile_version(void);
#define BLE_LOG_CONTROLLER_GET_COMMIT() ble_controller_get_compile_version()
#endif
/* Link-layer clock sample; 0 when the controller exports no accessor. */
#if CONFIG_BLE_LOG_LL_ENABLED
#if CONFIG_BT_DUAL_MODE_ARCH
/* BTDM common lib (dual-mode arch only) */
extern const char *r_btdm_get_compile_version(void);
#define BLE_LOG_BTDM_COMMON_GET_COMMIT() r_btdm_get_compile_version()
#endif
#endif
#if CONFIG_BLE_MESH && CONFIG_BLE_MESH_V11_SUPPORT
/* "Bluetooth Mesh v1.1 commit: <hash>" */
extern const char bt_mesh_v11_commit_str[];
#endif
#if CONFIG_BT_AUDIO && CONFIG_SOC_BLE_AUDIO_SUPPORTED
extern const char *lib_audio_commit_get(void);
#endif
/* The dual-mode-arch controller (ESP32-H4, ESP32-S31) does not export its
* link-layer timer yet; its accessor is r_sched_timer_getCurrentTimeU32.
* Call it once the controller libraries export the symbol. */
#define BLE_LOG_GET_LC_TS 0
/* ESP BLE Controller Gen 2 */
#elif defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C5) ||\
defined(CONFIG_IDF_TARGET_ESP32C61) || defined(CONFIG_IDF_TARGET_ESP32H21)
extern uint32_t r_ble_lll_timer_current_tick_get(void);
#define BLE_LOG_GET_LC_TS r_ble_lll_timer_current_tick_get()
/* ESP BLE Controller Gen 1 */
#elif defined(CONFIG_IDF_TARGET_ESP32C2)
extern uint32_t r_os_cputime_get32(void);
#define BLE_LOG_GET_LC_TS r_os_cputime_get32()
/* Legacy BLE Controller */
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
extern uint32_t lld_read_clock_us(void);
#define BLE_LOG_GET_LC_TS lld_read_clock_us()
#else /* Other targets */
#define BLE_LOG_GET_LC_TS 0
#endif /* BLE targets */
#else /* !CONFIG_BLE_LOG_LL_ENABLED */
#define BLE_LOG_GET_LC_TS 0
#endif /* CONFIG_BLE_LOG_LL_ENABLED */
_Static_assert(sizeof(ble_log_version_info_t) == 58,
"Unexpected BLE Log version info frame size");
BLE_LOG_STATIC uint32_t ble_log_rt_lc_ts_get(void)
{
#if CONFIG_BLE_LOG_LL_ENABLED
/* Legacy accessors dereference controller state. INIT is emitted before
* controller initialization completes, and standalone users may keep the
* controller idle for the entire BLE Log epoch. */
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
return 0;
}
#endif
return BLE_LOG_GET_LC_TS;
}
/* VARIABLE */
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR uint32_t rt_inited = 0;
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR volatile uint32_t rt_ref_count = 0;
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR QueueHandle_t rt_queue_handle = NULL;
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR esp_timer_handle_t rt_defer_timer = NULL;
BLE_LOG_STATIC uint32_t rt_last_hook_os_ts = 0;
BLE_LOG_STATIC ble_log_version_info_t rt_version_info;
#if CONFIG_BLE_LOG_TS_ENABLED
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR uint32_t rt_ts_enabled = 0;
BLE_LOG_STATIC esp_timer_handle_t rt_ts_timer = NULL;
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR esp_timer_handle_t rt_ts_timer = NULL;
/* Toggle IO phase; stays false when the toggle IO is compiled out. */
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR bool rt_ts_io_level = false;
/* PRIVATE FUNCTION DECLARATION */
BLE_LOG_STATIC void ble_log_rt_defer_cb(void *arg);
BLE_LOG_STATIC bool ble_log_rt_dispatch(QueueHandle_t queue, UBaseType_t pending);
BLE_LOG_STATIC void ble_log_rt_dispatch(QueueHandle_t queue,
UBaseType_t pending);
BLE_LOG_STATIC void ble_log_rt_version_info_init(void);
BLE_LOG_STATIC void ble_log_rt_run_hook(void);
#if CONFIG_BLE_LOG_TS_ENABLED
BLE_LOG_STATIC void ble_log_rt_ts_sample(ble_log_ts_info_t *info);
BLE_LOG_STATIC void ble_log_rt_ts_trigger(void *arg);
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
/* PRIVATE FUNCTION */
/* Copies a NUL-terminated commit string into a fixed-width zero-padded field */
@@ -112,38 +130,35 @@ BLE_LOG_STATIC void ble_log_rt_version_info_init(void)
ble_log_internal_set_version_info(&rt_version_info);
}
BLE_LOG_STATIC void ble_log_rt_run_hook(void)
/* Captures the link-layer, ESP and OS clocks at one instant. */
BLE_LOG_STATIC void ble_log_rt_ts_sample(ble_log_ts_info_t *info)
{
if (!ble_log_lbm_is_enabled()) {
return;
}
#if CONFIG_BLE_LOG_TS_ENABLED
if (BLE_LOG_ATOMIC_LOAD_ACQUIRE(rt_ts_enabled)) {
return;
}
#endif
uint32_t now = pdTICKS_TO_MS(xTaskGetTickCount());
if ((uint32_t)(now - rt_last_hook_os_ts) < BLE_LOG_TS_TRIGGER_TIMEOUT_MS) {
return;
}
rt_last_hook_os_ts = now;
/* Unified periodic output: best-effort flush of partially-filled OPEN
* transports ahead of the periodic snapshot, so parked frames do not
* wait for the next capacity seal. */
ble_log_lbm_flush_open_transports();
(void)ble_log_internal_snapshot(BLE_LOG_SNAPSHOT_REASON_PERIODIC,
NULL, false);
info->int_src_code = BLE_LOG_INT_SRC_TS;
BLE_LOG_ENTER_CRITICAL();
#if CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED
/* The critical section keeps the toggle IO edge and the clock samples
* adjacent, and excludes the phase write in ble_log_sync_enable. */
rt_ts_io_level = !rt_ts_io_level;
gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, rt_ts_io_level);
#endif /* CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED */
info->io_level = rt_ts_io_level;
info->lc_ts = ble_log_rt_lc_ts_get();
info->esp_ts = esp_timer_get_time();
info->os_ts = pdTICKS_TO_MS(xTaskGetTickCountFromISR());
BLE_LOG_EXIT_CRITICAL();
}
BLE_LOG_STATIC bool ble_log_rt_dispatch(QueueHandle_t queue, UBaseType_t pending)
/* Dispatch only the queue depth observed at callback entry. A backend may
* recycle synchronously on queue-full while another core immediately refills
* the runtime queue; an unbounded loop here could starve every other callback
* on the shared ESP timer task. */
BLE_LOG_STATIC void ble_log_rt_dispatch(QueueHandle_t queue,
UBaseType_t pending)
{
ble_log_prph_trans_t *trans = NULL;
bool processed = false;
while (pending-- && xQueueReceive(queue, &trans, 0) == pdTRUE) {
ble_log_prph_send_trans(trans);
processed = true;
}
return processed;
}
BLE_LOG_STATIC void ble_log_rt_defer_cb(void *arg)
@@ -155,24 +170,19 @@ BLE_LOG_STATIC void ble_log_rt_defer_cb(void *arg)
}
QueueHandle_t queue = rt_queue_handle;
if (!queue) {
return;
}
ble_log_rt_dispatch(queue, uxQueueMessagesWaiting(queue));
UBaseType_t pending = uxQueueMessagesWaiting(queue);
if (ble_log_rt_dispatch(queue, pending)) {
ble_log_rt_run_hook();
}
pending = uxQueueMessagesWaiting(queue);
if (pending &&
/* A submit racing an active one-shot callback may fail to arm it. If the
* bounded batch left work behind, schedule another turn after yielding
* the shared timer task to callbacks that are already due. */
if (uxQueueMessagesWaiting(queue) &&
ble_log_ref_count_try_acquire(&rt_ref_count, &rt_inited)) {
(void)esp_timer_start_once(rt_defer_timer, BLE_LOG_RT_DEFER_TIMEOUT_US);
(void)esp_timer_start_once(rt_defer_timer,
BLE_LOG_RT_DEFER_TIMEOUT_US);
BLE_LOG_REF_COUNT_RELEASE(&rt_ref_count);
}
}
#if CONFIG_BLE_LOG_TS_ENABLED
BLE_LOG_STATIC void ble_log_rt_ts_trigger(void *arg)
{
(void)arg;
@@ -182,21 +192,18 @@ BLE_LOG_STATIC void ble_log_rt_ts_trigger(void *arg)
}
ble_log_ts_info_t ts_info;
bool ts_valid = ble_log_ts_info_update(&ts_info);
ble_log_rt_ts_sample(&ts_info);
/* Unified periodic output: best-effort flush of partially-filled OPEN
* transports ahead of the periodic snapshot, so parked frames do not
* wait for the next capacity seal. */
ble_log_lbm_flush_open_transports();
if (ts_valid) {
(void)ble_log_internal_snapshot(
BLE_LOG_SNAPSHOT_REASON_PERIODIC |
BLE_LOG_SNAPSHOT_REASON_TS_VALID,
&ts_info, false);
}
(void)ble_log_internal_snapshot(
BLE_LOG_SNAPSHOT_REASON_PERIODIC |
BLE_LOG_SNAPSHOT_REASON_TS_VALID,
&ts_info, false);
}
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
/* INTERFACE */
bool ble_log_rt_init(void)
@@ -206,6 +213,19 @@ bool ble_log_rt_init(void)
}
ble_log_rt_version_info_init();
/* Configure the analyzer toggle IO */
#if CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED
gpio_config_t sync_io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = BIT64(CONFIG_BLE_LOG_SYNC_IO_NUM),
};
if (gpio_config(&sync_io_conf) != ESP_OK) {
goto exit;
}
#endif /* CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED */
rt_queue_handle = xQueueCreate(BLE_LOG_TRANS_TOTAL_CNT, sizeof(ble_log_prph_trans_t *));
if (!rt_queue_handle) {
goto exit;
@@ -222,8 +242,11 @@ bool ble_log_rt_init(void)
goto exit;
}
#if CONFIG_BLE_LOG_TS_ENABLED
BLE_LOG_ATOMIC_STORE_RELAXED(rt_ts_enabled, false);
/* TS sync is always on: the periodic tick drives the unified periodic
* output (TS sample, OPEN transport flush, snapshot). Test apps quiesce
* it with ble_log_sync_enable(false) for deterministic timing. */
rt_ts_io_level = false;
BLE_LOG_ATOMIC_STORE_RELEASE(rt_ts_enabled, true);
esp_timer_create_args_t ts_timer_args = {
.callback = ble_log_rt_ts_trigger,
.arg = NULL,
@@ -236,9 +259,7 @@ bool ble_log_rt_init(void)
esp_timer_start_periodic(rt_ts_timer, BLE_LOG_TS_TRIGGER_TIMEOUT_US) != ESP_OK) {
goto exit;
}
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
rt_last_hook_os_ts = 0;
BLE_LOG_ATOMIC_STORE_RELEASE(rt_inited, true);
return true;
@@ -257,14 +278,12 @@ void ble_log_rt_deinit(void)
ESP_LOGE(TAG, "Timed out waiting for BLE Log runtime references");
BLE_LOG_ASSERT(false);
}
#if CONFIG_BLE_LOG_TS_ENABLED
BLE_LOG_ATOMIC_STORE_RELEASE(rt_ts_enabled, false);
if (rt_ts_timer) {
esp_timer_stop_blocking(rt_ts_timer, portMAX_DELAY);
esp_timer_delete(rt_ts_timer);
rt_ts_timer = NULL;
}
#endif /* CONFIG_BLE_LOG_TS_ENABLED */
if (rt_defer_timer) {
esp_timer_stop_blocking(rt_defer_timer, portMAX_DELAY);
@@ -281,6 +300,12 @@ void ble_log_rt_deinit(void)
vQueueDelete(rt_queue_handle);
rt_queue_handle = NULL;
}
/* Release the toggle IO */
rt_ts_io_level = false;
#if CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED
gpio_reset_pin(CONFIG_BLE_LOG_SYNC_IO_NUM);
#endif /* CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED */
}
bool ble_log_rt_drain(void)
@@ -297,7 +322,9 @@ bool ble_log_rt_drain(void)
goto exit;
}
QueueHandle_t queue = rt_queue_handle;
(void)ble_log_rt_dispatch(queue, uxQueueMessagesWaiting(queue));
while (uxQueueMessagesWaiting(queue)) {
ble_log_rt_dispatch(queue, uxQueueMessagesWaiting(queue));
}
drained = true;
exit:
@@ -332,15 +359,25 @@ BLE_LOG_IRAM_ATTR void ble_log_rt_submit_trans(ble_log_prph_trans_t *trans)
BLE_LOG_REF_COUNT_RELEASE(&rt_ref_count);
}
#if CONFIG_BLE_LOG_TS_ENABLED
bool ble_log_sync_enable(bool enable)
{
if (!ble_log_ref_count_try_acquire(&rt_ref_count, &rt_inited)) {
return false;
}
BLE_LOG_ATOMIC_STORE_RELEASE(rt_ts_enabled, enable);
ble_log_ts_reset(enable);
#if CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED
/* Leave the toggle IO at a defined low level: when sync is disabled
* while the IO idles low, drive a short high pulse first so the
* analyzer sees a final falling edge. The critical section excludes
* the phase toggle in ble_log_rt_ts_sample. */
BLE_LOG_ENTER_CRITICAL();
if (!enable && !rt_ts_io_level) {
gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, 1);
}
rt_ts_io_level = false;
gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, 0);
BLE_LOG_EXIT_CRITICAL();
#endif /* CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED */
BLE_LOG_REF_COUNT_RELEASE(&rt_ref_count);
return true;
}
#endif /* CONFIG_BLE_LOG_TS_ENABLED */

View File

@@ -1,94 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* ----------------------------------- */
/* BLE Log - Timestamp Synchronization */
/* ----------------------------------- */
/* INCLUDE */
#include "ble_log_ts.h"
/* VARIABLE */
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR bool ts_inited = false;
BLE_LOG_STATIC BLE_LOG_DRAM_ATTR ble_log_ts_info_t *ts_info = NULL;
/* INTERFACE */
bool ble_log_ts_init(void)
{
if (ts_inited) {
return true;
}
/* Initialize toggle IO */
gpio_config_t sync_io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = BIT64(CONFIG_BLE_LOG_SYNC_IO_NUM),
};
if (gpio_config(&sync_io_conf) != ESP_OK) {
goto exit;
}
/* Initialize sync data */
ts_info = (ble_log_ts_info_t *)BLE_LOG_MALLOC(sizeof(ble_log_ts_info_t));
if (!ts_info) {
goto exit;
}
BLE_LOG_MEMSET(ts_info, 0, sizeof(ble_log_ts_info_t));
ts_info->int_src_code = BLE_LOG_INT_SRC_TS;
ts_inited = true;
return true;
exit:
ble_log_ts_deinit();
return false;
}
void ble_log_ts_deinit(void)
{
ts_inited = false;
/* Release sync data */
if (ts_info) {
BLE_LOG_FREE(ts_info);
ts_info = NULL;
}
/* Release toggle IO */
gpio_reset_pin(CONFIG_BLE_LOG_SYNC_IO_NUM);
}
void ble_log_ts_info_update(ble_log_ts_info_t **info)
{
if (!ts_inited) {
return;
}
BLE_LOG_ENTER_CRITICAL();
ts_info->io_level = !ts_info->io_level;
gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, ts_info->io_level);
ts_info->lc_ts = BLE_LOG_GET_LC_TS;
ts_info->esp_ts = esp_timer_get_time();
ts_info->os_ts = pdTICKS_TO_MS(xTaskGetTickCountFromISR());
BLE_LOG_EXIT_CRITICAL();
*info = ts_info;
}
void ble_log_ts_reset(bool status)
{
if (!ts_inited) {
return;
}
BLE_LOG_ENTER_CRITICAL();
if (!status && !ts_info->io_level) {
gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, 1);
}
ts_info->io_level = 0;
gpio_set_level(CONFIG_BLE_LOG_SYNC_IO_NUM, 0);
BLE_LOG_EXIT_CRITICAL();
}

View File

@@ -16,7 +16,6 @@
/* INCLUDE */
#include "ble_log.h"
#include "ble_log_prph.h"
#include "ble_log_ts.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
@@ -123,6 +122,15 @@ typedef struct {
/* -------------------------------- */
/* Internal Snapshot Frame */
/* -------------------------------- */
/* Clock sample captured at one instant by the runtime periodic tick. */
typedef struct {
uint8_t int_src_code;
uint8_t io_level;
uint32_t lc_ts;
uint32_t esp_ts;
uint32_t os_ts;
} __attribute__((packed)) ble_log_ts_info_t;
#define BLE_LOG_SNAPSHOT_REASON_INIT BIT(0)
#define BLE_LOG_SNAPSHOT_REASON_PERIODIC BIT(1)
#define BLE_LOG_SNAPSHOT_REASON_FLUSH BIT(2)

View File

@@ -12,7 +12,6 @@
/* INCLUDE */
#include "ble_log_prph.h"
#include "ble_log_ts.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

View File

@@ -1,58 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __BLE_LOG_TS_H__
#define __BLE_LOG_TS_H__
/* ----------------------------------- */
/* BLE Log - Timestamp Synchronization */
/* ----------------------------------- */
/* INCLUDE */
#include "ble_log_util.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "driver/gpio.h"
/* MACRO */
#if CONFIG_BLE_LOG_LL_ENABLED
/* ESP BLE Controller Gen 2 */
#if defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C5) ||\
defined(CONFIG_IDF_TARGET_ESP32C61) || defined(CONFIG_IDF_TARGET_ESP32H21) || defined(CONFIG_IDF_TARGET_ESP32H4)
extern uint32_t r_ble_lll_timer_current_tick_get(void);
#define BLE_LOG_GET_LC_TS r_ble_lll_timer_current_tick_get()
/* ESP BLE Controller Gen 1 */
#elif defined(CONFIG_IDF_TARGET_ESP32C2)
extern uint32_t r_os_cputime_get32(void);
#define BLE_LOG_GET_LC_TS r_os_cputime_get32()
/* Legacy BLE Controller */
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
extern uint32_t lld_read_clock_us(void);
#define BLE_LOG_GET_LC_TS lld_read_clock_us()
#else /* Other targets */
#define BLE_LOG_GET_LC_TS 0
#endif /* BLE targets */
#else /* !CONFIG_BLE_LOG_LL_ENABLED */
#define BLE_LOG_GET_LC_TS 0
#endif /* CONFIG_BLE_LOG_LL_ENABLED */
/* TYPEDEF */
typedef struct {
uint8_t int_src_code;
uint8_t io_level;
uint32_t lc_ts;
uint32_t esp_ts;
uint32_t os_ts;
} __attribute__((packed)) ble_log_ts_info_t;
/* INTERFACE */
bool ble_log_ts_init(void);
void ble_log_ts_deinit(void);
void ble_log_ts_info_update(ble_log_ts_info_t **ts_info);
void ble_log_ts_reset(bool status);
#endif /* __BLE_LOG_TS_H__ */

View File

@@ -53,14 +53,15 @@ bool test_ble_log_walk_frames(const uint8_t *data, size_t len,
void setUp(void)
{
/* The periodic TS tick is always on since TS sync became unconditional;
* quiesce it so timing-sensitive tests stay deterministic. */
(void)ble_log_sync_enable(false);
}
void tearDown(void)
{
ble_log_prph_test_set_auto_recycle_hook(NULL, NULL);
#if CONFIG_BLE_LOG_TS_ENABLED
(void)ble_log_sync_enable(false);
#endif
}
void app_main(void)

View File

@@ -448,7 +448,6 @@ TEST_CASE("BLE Log ISR-only submission arms runtime dispatch",
#endif
}
#if CONFIG_BLE_LOG_TS_ENABLED
TEST_CASE("BLE Log periodic timestamp skips light sleep wakeups",
"[ble_log][runtime][timestamp][ignore]")
{
@@ -496,7 +495,6 @@ TEST_CASE("BLE Log periodic timestamp skips light sleep wakeups",
0, ts_count,
"Periodic ESP timer did not emit a timestamp frame");
}
#endif
TEST_CASE("BLE Log runtime dispatch yields to other timer callbacks",
"[ble_log][runtime][ignore]")
@@ -729,7 +727,7 @@ TEST_CASE("BLE Log LBM inflight peak stays bounded under bursts",
TEST_ASSERT_TRUE(write_runtime_marker(seq, NULL));
}
/* Recycle the burst after its peak has been recorded. A runtime hook may
/* Recycle the burst after its peak has been recorded. A periodic tick may
* also have occupied the dedicated Internal transport, so drain twice. */
for (int round = 0; round < 2; round++) {
TEST_ASSERT_TRUE(ble_log_rt_drain());

View File

@@ -6,7 +6,7 @@ CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
# Production tick rate; the 1000 Hz regression variant is built from
# sdkconfig.defaults.tick_1000.
CONFIG_FREERTOS_HZ=100
CONFIG_BLE_LOG_TS_ENABLED=y
CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED=y
# Keep the production TS/hook cadence (Kconfig default 1000 ms) so the
# regressions see the production info/stat/buf-util cadence.
CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n

View File

@@ -52,6 +52,9 @@ bool test_ble_log_walk_frames(const uint8_t *data, size_t len,
void setUp(void)
{
/* The periodic TS tick is always on since TS sync became unconditional;
* quiesce it so timing-sensitive tests stay deterministic. */
(void)ble_log_sync_enable(false);
}
void tearDown(void)

View File

@@ -263,7 +263,7 @@ TEST_CASE("BLE Log v7 framing matches golden bytes", "[ble_log][wire]")
sizeof(golden_payload));
}
TEST_CASE("BLE Log runtime hook reports build and chip versions", "[ble_log]")
TEST_CASE("BLE Log periodic tick reports build and chip versions", "[ble_log]")
{
static const uint8_t payload[TEST_PAYLOAD_LEN] = {0};
@@ -276,9 +276,10 @@ TEST_CASE("BLE Log runtime hook reports build and chip versions", "[ble_log]")
TEST_READER_STACK_SIZE, &ctx,
TEST_READER_PRIO, &reader));
TEST_ASSERT_TRUE(ble_log_enable(true));
TEST_ASSERT_TRUE(ble_log_sync_enable(true));
/* A hook pass submits one consolidated Internal Snapshot containing the
* version, statistics, utilization, and optional TS sample. */
/* Each periodic tick submits one consolidated Internal Snapshot containing
* the version, statistics, utilization, and optional TS sample. */
for (int round = 0; round < TEST_MAX_ROUNDS && ctx.capture.version_info_count == 0; round++) {
vTaskDelay(pdMS_TO_TICKS(TEST_HOOK_SETTLE_MS));
for (int i = 0; i < TEST_WRITES_PER_ROUND; i++) {
@@ -330,6 +331,7 @@ TEST_CASE("BLE Log runtime hook reports build and chip versions", "[ble_log]")
TEST_ASSERT_EQUAL_UINT16((uint16_t)chip_info.model, vi->chip_model);
TEST_ASSERT_EQUAL_UINT16(chip_info.revision, vi->chip_revision);
(void)ble_log_sync_enable(false);
vSemaphoreDelete(ctx.done);
}

View File

@@ -286,3 +286,7 @@ CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT CONFIG_BT_NIMBLE_TRA
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN CONFIG_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EN
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS CONFIG_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_DIS
# BLE Log: periodic Internal Snapshots are always enabled now; the legacy
# TS switch only gates the analyzer toggle IO.
CONFIG_BLE_LOG_TS_ENABLED CONFIG_BLE_LOG_TS_SYNC_TOGGLE_IO_ENABLED