Enable configSUPPORT_STATIC_ALLOCATION on BK72xx

Add build flag and required callbacks (following ESP-IDF's pvPortMalloc
approach) so FreeRTOSQueue can use xQueueCreateStatic — queue storage
lives in BSS with zero runtime heap allocation.
This commit is contained in:
J. Nick Koston
2026-03-31 21:42:59 -10:00
parent b829bbee95
commit b980978f05
3 changed files with 65 additions and 21 deletions
+5
View File
@@ -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)
@@ -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 <FreeRTOS.h>
#include <task.h>
#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 */
+12 -21
View File
@@ -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 T, uint8_t SIZE> 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 T, uint8_t SIZE> 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 T, uint8_t SIZE> 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 T, uint8_t SIZE> 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_;
};