diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 8f99124604..91fdf00230 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -438,6 +438,11 @@ async def component_to_code(config): # 4-8KB flash). Even if linked, it would use locks, so explicit FreeRTOS # mutexes are simpler and equivalent. cg.add_define(ThreadModel.MULTI_NO_ATOMICS) + # Enable FreeRTOS static allocation so FreeRTOSQueue can use + # xQueueCreateStatic (queue storage in BSS, no heap allocation). + # BK72xx's FreeRTOSConfig.h doesn't define this, defaulting to 0. + # The -D wins over the #ifndef default in FreeRTOS.h. + cg.add_build_flag("-DconfigSUPPORT_STATIC_ALLOCATION=1") # RTL8710B needs FreeRTOS 8.2.3+ for xTaskNotifyGive/ulTaskNotifyTake # required by AsyncTCP 3.4.3+ (https://github.com/esphome/esphome/issues/10220) diff --git a/esphome/components/libretiny/freertos_static_alloc.c b/esphome/components/libretiny/freertos_static_alloc.c new file mode 100644 index 0000000000..cd919e86a9 --- /dev/null +++ b/esphome/components/libretiny/freertos_static_alloc.c @@ -0,0 +1,48 @@ +/* + * FreeRTOS static allocation callbacks for LibreTiny platforms. + * + * Required when configSUPPORT_STATIC_ALLOCATION is enabled. These callbacks + * provide memory for the idle and timer tasks. Following ESP-IDF's approach, + * we allocate from the FreeRTOS heap (pvPortMalloc) rather than using truly + * static buffers, to avoid assumptions about memory layout. + * + * This enables xQueueCreateStatic, xTaskCreateStatic, etc. throughout ESPHome, + * allowing queue storage to live in BSS with zero runtime heap allocation. + */ + +#ifdef USE_BK72XX + +#include +#include + +#if (configSUPPORT_STATIC_ALLOCATION == 1) + +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, + uint32_t *pulIdleTaskStackSize) { + /* Stack grows down on ARM — allocate stack first, then TCB, + * so the stack does not grow into the TCB. */ + StackType_t *stack = (StackType_t *) pvPortMalloc(configMINIMAL_STACK_SIZE * sizeof(StackType_t)); + StaticTask_t *tcb = (StaticTask_t *) pvPortMalloc(sizeof(StaticTask_t)); + + *ppxIdleTaskTCBBuffer = tcb; + *ppxIdleTaskStackBuffer = stack; + *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; +} + +#if (configUSE_TIMERS == 1) + +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, + uint32_t *pulTimerTaskStackSize) { + StackType_t *stack = (StackType_t *) pvPortMalloc(configTIMER_TASK_STACK_DEPTH * sizeof(StackType_t)); + StaticTask_t *tcb = (StaticTask_t *) pvPortMalloc(sizeof(StaticTask_t)); + + *ppxTimerTaskTCBBuffer = tcb; + *ppxTimerTaskStackBuffer = stack; + *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; +} + +#endif /* configUSE_TIMERS */ + +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +#endif /* USE_BK72XX */ diff --git a/esphome/core/freertos_queue.h b/esphome/core/freertos_queue.h index 4209472cc2..38e764137e 100644 --- a/esphome/core/freertos_queue.h +++ b/esphome/core/freertos_queue.h @@ -16,7 +16,8 @@ * * Provides the same API as LockFreeQueue (push, pop, get_and_reset_dropped_count, * empty, full, size) but uses xQueue internally, which synchronizes via - * FreeRTOS critical sections. + * FreeRTOS critical sections. Uses xQueueCreateStatic so the queue storage + * lives in BSS with zero runtime heap allocation. * * @tparam T The type of elements stored in the queue (stored as pointers) * @tparam SIZE The maximum number of elements @@ -26,7 +27,9 @@ namespace esphome { template class FreeRTOSQueue { public: - FreeRTOSQueue() : dropped_count_(0) { this->handle_ = xQueueCreate(SIZE, sizeof(T *)); } + FreeRTOSQueue() : dropped_count_(0) { + this->handle_ = xQueueCreateStatic(SIZE, sizeof(T *), this->storage_, &this->queue_buf_); + } // Non-copyable, non-movable — queue handle is not transferable FreeRTOSQueue(const FreeRTOSQueue &) = delete; @@ -35,7 +38,7 @@ template class FreeRTOSQueue { FreeRTOSQueue &operator=(FreeRTOSQueue &&) = delete; bool push(T *element) { - if (element == nullptr || this->handle_ == nullptr) + if (element == nullptr) return false; if (xQueueSend(this->handle_, &element, 0) != pdPASS) { @@ -46,9 +49,6 @@ template class FreeRTOSQueue { } T *pop() { - if (this->handle_ == nullptr) - return nullptr; - T *element; if (xQueueReceive(this->handle_, &element, 0) != pdTRUE) { return nullptr; @@ -74,25 +74,16 @@ template class FreeRTOSQueue { portEXIT_CRITICAL(); } - bool empty() const { - if (this->handle_ == nullptr) - return true; - return uxQueueMessagesWaiting(this->handle_) == 0; - } + bool empty() const { return uxQueueMessagesWaiting(this->handle_) == 0; } - bool full() const { - if (this->handle_ == nullptr) - return true; - return uxQueueSpacesAvailable(this->handle_) == 0; - } + bool full() const { return uxQueueSpacesAvailable(this->handle_) == 0; } - size_t size() const { - if (this->handle_ == nullptr) - return 0; - return uxQueueMessagesWaiting(this->handle_); - } + size_t size() const { return uxQueueMessagesWaiting(this->handle_); } protected: + // Static storage for the queue — lives in BSS, no heap allocation + uint8_t storage_[SIZE * sizeof(T *)]; + StaticQueue_t queue_buf_; QueueHandle_t handle_; uint16_t dropped_count_; };