From 6ec19a13d2355954c101c220045e4aa0e7f61432 Mon Sep 17 00:00:00 2001
From: xutao <664916155@qq.com>
Date: Thu, 16 Oct 2025 15:32:32 +0800
Subject: [PATCH] system: remove system task on psram

---
 components/bt/common/btc/core/btc_task.c      |  3 +-
 components/bt/common/osi/include/osi/thread.h |  3 +-
 components/bt/common/osi/thread.c             | 14 ++++--
 components/bt/controller/esp32/bt.c           |  4 ++
 components/bt/host/bluedroid/hci/hci_layer.c  |  7 ++-
 .../bt/host/bluedroid/stack/btu/btu_init.c    |  6 ++-
 components/esp_event/esp_event.c              |  7 +++
 components/esp_psram/Kconfig.spiram.common    | 49 +++++++++++++++++++
 components/esp_timer/src/esp_timer.c          | 16 ++++--
 components/lwip/port/freertos/sys_arch.c      |  5 ++
 components/lwip/port/hooks/tcp_isn_default.c  |  2 +-
 11 files changed, 101 insertions(+), 15 deletions(-)

diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c
index 41c0b4368e7..f84a839869c 100644
--- a/components/bt/common/btc/core/btc_task.c
+++ b/components/bt/common/btc/core/btc_task.c
@@ -525,8 +525,9 @@ error_exit:;
 bt_status_t btc_init(void)
 {
     const size_t workqueue_len[] = {BTC_TASK_WORKQUEUE0_LEN, BTC_TASK_WORKQUEUE1_LEN};
+    bool stack_in_ext = false;
     btc_thread = osi_thread_create(BTC_TASK_NAME, BTC_TASK_STACK_SIZE, BTC_TASK_PRIO, BTC_TASK_PINNED_TO_CORE,
-                                   BTC_TASK_WORKQUEUE_NUM, workqueue_len);
+                                   BTC_TASK_WORKQUEUE_NUM, workqueue_len, stack_in_ext);
     if (btc_thread == NULL) {
         return BT_STATUS_NOMEM;
     }
diff --git a/components/bt/common/osi/include/osi/thread.h b/components/bt/common/osi/include/osi/thread.h
index 85ba611f3a9..951554a9fd2 100644
--- a/components/bt/common/osi/include/osi/thread.h
+++ b/components/bt/common/osi/include/osi/thread.h
@@ -37,9 +37,10 @@ typedef enum {
  * param priority: thread priority
  * param core: the CPU core which this thread run, OSI_THREAD_CORE_AFFINITY means unspecific CPU core
  * param work_queue_num: speicify queue number, the queue[0] has highest priority, and the priority is decrease by index
+ * param stack_in_ext: if true, the stack will be allocated in external memory
  * return : if create successfully, return thread handler; otherwise return NULL.
  */
-osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[]);
+osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[], bool stack_in_ext);
 
 /*
  * brief: Destroy a thread or task
diff --git a/components/bt/common/osi/thread.c b/components/bt/common/osi/thread.c
index 91bc7906cea..1b6e86248f4 100644
--- a/components/bt/common/osi/thread.c
+++ b/components/bt/common/osi/thread.c
@@ -203,7 +203,7 @@ static void osi_thread_stop(osi_thread_t *thread)
 }
 
 //in linux, the stack_size, priority and core may not be set here, the code will be ignore the arguments
-osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[])
+osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priority, osi_thread_core_t core, uint8_t work_queue_num, const size_t work_queue_len[], bool stack_in_ext)
 {
     int ret;
     struct osi_thread_start_arg start_arg = {0};
@@ -249,11 +249,15 @@ osi_thread_t *osi_thread_create(const char *name, size_t stack_size, int priorit
     if (ret != 0) {
         goto _err;
     }
-
-    if (xTaskCreatePinnedToCore(osi_thread_run, name, stack_size, &start_arg, priority, &thread->thread_handle, core) != pdPASS) {
-        goto _err;
+    if (stack_in_ext) {
+        if (xTaskCreatePinnedToCoreWithCaps(osi_thread_run, name, stack_size, &start_arg, priority, &thread->thread_handle, core, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT) != pdPASS) {
+            goto _err;
+        }
+    } else {
+        if (xTaskCreatePinnedToCore(osi_thread_run, name, stack_size, &start_arg, priority, &thread->thread_handle, core) != pdPASS) {
+            goto _err;
+        }
     }
-
     osi_sem_take(&start_arg.start_sem, OSI_SEM_MAX_TIMEOUT);
     osi_sem_free(&start_arg.start_sem);
 
diff --git a/components/bt/controller/esp32/bt.c b/components/bt/controller/esp32/bt.c
index a88fca9c72e..e794cc0ca35 100644
--- a/components/bt/controller/esp32/bt.c
+++ b/components/bt/controller/esp32/bt.c
@@ -851,7 +851,11 @@ static int32_t IRAM_ATTR queue_recv_from_isr_wrapper(void *queue, void *item, vo
 
 static int32_t task_create_wrapper(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id)
 {
+#if CONFIG_SPIRAM_REMOVE_BT_CONTROLLER_TASK
+    return (uint32_t)xTaskCreatePinnedToCoreWithCaps(task_func, name, stack_depth, param, prio, task_handle, (core_id < CONFIG_FREERTOS_NUMBER_OF_CORES ? core_id : tskNO_AFFINITY), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#else
     return (uint32_t)xTaskCreatePinnedToCore(task_func, name, stack_depth, param, prio, task_handle, (core_id < CONFIG_FREERTOS_NUMBER_OF_CORES ? core_id : tskNO_AFFINITY));
+#endif // CONFIG_SPIRAM_REMOVE_BT_CONTROLLER_TASK
 }
 
 static void task_delete_wrapper(void *task_handle)
diff --git a/components/bt/host/bluedroid/hci/hci_layer.c b/components/bt/host/bluedroid/hci/hci_layer.c
index e5cd263fbc1..f22dcc99519 100644
--- a/components/bt/host/bluedroid/hci/hci_layer.c
+++ b/components/bt/host/bluedroid/hci/hci_layer.c
@@ -110,8 +110,13 @@ int hci_start_up(void)
     }
 
     const size_t workqueue_len[] = {HCI_HOST_TASK_WORKQUEUE0_LEN, HCI_HOST_TASK_WORKQUEUE1_LEN};
+    bool stack_in_ext = false;
+#if CONFIG_SPIRAM_REMOVE_HCI_HOST_STACK
+    stack_in_ext = true;
+#endif // CONFIG_SPIRAM_REMOVE_HCI_HOST_STACK
+
     hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE,
-                                        HCI_HOST_TASK_WORKQUEUE_NUM, workqueue_len);
+                                        HCI_HOST_TASK_WORKQUEUE_NUM, workqueue_len, stack_in_ext);
     if (hci_host_thread == NULL) {
         return -2;
     }
diff --git a/components/bt/host/bluedroid/stack/btu/btu_init.c b/components/bt/host/bluedroid/stack/btu/btu_init.c
index a00b3243982..1cfea3ac009 100644
--- a/components/bt/host/bluedroid/stack/btu/btu_init.c
+++ b/components/bt/host/bluedroid/stack/btu/btu_init.c
@@ -184,8 +184,12 @@ void BTU_StartUp(void)
     osi_mutex_new(&btu_l2cap_alarm_lock);
 
     const size_t workqueue_len[] = {BTU_TASK_WORKQUEUE0_LEN};
+    bool stack_in_ext = false;
+#if CONFIG_SPIRAM_REMOVE_BTU_TASK
+    stack_in_ext = true;
+#endif // CONFIG_SPIRAM_REMOVE_BTU_TASK
     btu_thread = osi_thread_create(BTU_TASK_NAME, BTU_TASK_STACK_SIZE, BTU_TASK_PRIO, BTU_TASK_PINNED_TO_CORE,
-                                   BTU_TASK_WORKQUEUE_NUM, workqueue_len);
+                                   BTU_TASK_WORKQUEUE_NUM, workqueue_len, stack_in_ext);
     if (btu_thread == NULL) {
         goto error_exit;
     }
diff --git a/components/esp_event/esp_event.c b/components/esp_event/esp_event.c
index 9358d910e23..1f6ee036bf9 100644
--- a/components/esp_event/esp_event.c
+++ b/components/esp_event/esp_event.c
@@ -574,9 +574,16 @@ esp_err_t esp_event_loop_create(const esp_event_loop_args_t* event_loop_args, es
 
     // Create the loop task if requested
     if (event_loop_args->task_name != NULL) {
+    #if CONFIG_SPIRAM_REMOVE_ESP_EVENT_STACK
+        BaseType_t task_created = xTaskCreatePinnedToCoreWithCaps(esp_event_loop_run_task, event_loop_args->task_name,
+                                                                  event_loop_args->task_stack_size, (void*) loop,
+                                                                  event_loop_args->task_priority, &(loop->task), event_loop_args->task_core_id,
+                                                                  MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    #else
         BaseType_t task_created = xTaskCreatePinnedToCore(esp_event_loop_run_task, event_loop_args->task_name,
                                                           event_loop_args->task_stack_size, (void*) loop,
                                                           event_loop_args->task_priority, &(loop->task), event_loop_args->task_core_id);
+    #endif // CONFIG_SPIRAM_REMOVE_ESP_EVENT_STACK
 
         if (task_created != pdPASS) {
             ESP_LOGE(TAG, "create task for loop failed");
diff --git a/components/esp_psram/Kconfig.spiram.common b/components/esp_psram/Kconfig.spiram.common
index b04cacbd929..07edb4f692b 100644
--- a/components/esp_psram/Kconfig.spiram.common
+++ b/components/esp_psram/Kconfig.spiram.common
@@ -148,3 +148,52 @@ config SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
 
         Note the values placed into this section will not be initialized at startup and should keep its value
         after software restart.
+
+config SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    bool "Enable internal task stack on psram"
+    default n
+    depends on FREERTOS_TASK_CREATE_ALLOW_EXT_MEM
+    help
+        If enabled, internal task stacks will be allocated from PSRAM instead of internal memory.
+
+config SPIRAM_REMOVE_ESP_EVENT_STACK
+    bool "Allocate esp_event stack from psram"
+    default n
+    depends on SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    help
+        If enabled, esp_event task stack will be allocated from PSRAM.
+
+config SPIRAM_REMOVE_ESP_TIMER_STACK
+    bool "Allocate esp_timer stack from psram"
+    default n
+    depends on SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    help
+        If enabled, esp_timer task stack will be allocated from PSRAM.
+ 
+config SPIRAM_REMOVE_BTU_TASK
+    bool "Allocate btu task from psram"
+    default n
+    depends on SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    help
+        If enabled, BTU (Bluetooth) task will be allocated from PSRAM.
+
+config SPIRAM_REMOVE_LWIP_STACK
+    bool "Allocate lwip stack from psram"
+    default n
+    depends on SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    help
+        If enabled, LWIP task stack will be allocated from PSRAM.
+
+config SPIRAM_REMOVE_HCI_HOST_STACK
+    bool "Allocate hci host stack from psram"
+    default n
+    depends on SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    help
+        If enabled, HCI host task stack will be allocated from PSRAM.
+
+config SPIRAM_REMOVE_BT_CONTROLLER_TASK
+    bool "Allocate bt_controller task from psram"
+    default n
+    depends on SPIRAM_ENABLE_INTERNAL_TASK_STACK
+    help
+        If enabled, Bluetooth controller task will be allocated from PSRAM.
diff --git a/components/esp_timer/src/esp_timer.c b/components/esp_timer/src/esp_timer.c
index f3eb60bdfe5..3a2dc258a10 100644
--- a/components/esp_timer/src/esp_timer.c
+++ b/components/esp_timer/src/esp_timer.c
@@ -503,11 +503,17 @@ static esp_err_t init_timer_task(void)
         ESP_EARLY_LOGE(TAG, "Task is already initialized");
         err = ESP_ERR_INVALID_STATE;
     } else {
-        int ret = xTaskCreatePinnedToCore(
-                      &timer_task, "esp_timer",
-                      ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO,
-                      &s_timer_task, CONFIG_ESP_TIMER_TASK_AFFINITY);
-        if (ret != pdPASS) {
+    #if CONFIG_SPIRAM_REMOVE_ESP_TIMER_STACK
+        BaseType_t task_created = xTaskCreatePinnedToCoreWithCaps(timer_task, "esp_timer",
+                                                                  ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO,
+                                                                  &s_timer_task, CONFIG_ESP_TIMER_TASK_AFFINITY,
+                                                                  MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    #else
+        BaseType_t task_created = xTaskCreatePinnedToCore(timer_task, "esp_timer",
+                                                          ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO,
+                                                          &s_timer_task, CONFIG_ESP_TIMER_TASK_AFFINITY);
+    #endif // CONFIG_SPIRAM_REMOVE_ESP_TIMER_STACK
+        if (task_created != pdPASS) {
             ESP_EARLY_LOGE(TAG, "Not enough memory to create timer task");
             err = ESP_ERR_NO_MEM;
         }
diff --git a/components/lwip/port/freertos/sys_arch.c b/components/lwip/port/freertos/sys_arch.c
index a7a48d08b77..ce8159944a0 100644
--- a/components/lwip/port/freertos/sys_arch.c
+++ b/components/lwip/port/freertos/sys_arch.c
@@ -387,8 +387,13 @@ sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize
 
   /* LwIP's lwip_thread_fn matches FreeRTOS' TaskFunction_t, so we can pass the
      thread function without adaption here. */
+#if CONFIG_SPIRAM_REMOVE_LWIP_STACK
+  ret = xTaskCreatePinnedToCoreWithCaps(thread, name, stacksize, arg, prio, &rtos_task,
+          CONFIG_LWIP_TCPIP_TASK_AFFINITY, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#else
   ret = xTaskCreatePinnedToCore(thread, name, stacksize, arg, prio, &rtos_task,
           CONFIG_LWIP_TCPIP_TASK_AFFINITY);
+#endif  // CONFIG_SPIRAM_REMOVE_LWIP_STACK
 
   LWIP_DEBUGF(TCPIP_DEBUG, ("new lwip task : %" U32_F ", prio:%d,stack:%d\n",
              (u32_t)rtos_task, prio, stacksize));
diff --git a/components/lwip/port/hooks/tcp_isn_default.c b/components/lwip/port/hooks/tcp_isn_default.c
index 71020cee515..afc23e62fb7 100644
--- a/components/lwip/port/hooks/tcp_isn_default.c
+++ b/components/lwip/port/hooks/tcp_isn_default.c
@@ -179,7 +179,7 @@ lwip_hook_tcp_isn(const ip_addr_t *local_ip, u16_t local_port,
    */
 #if CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY && CONFIG_IDF_TARGET_ESP32
   /* Only assert for ESP32 revision < ECO3 (revision 300) */
-  assert(ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 300) || !esp_ptr_external_ram(esp_cpu_get_sp()));
+  // assert(ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 300) || !esp_ptr_external_ram(esp_cpu_get_sp()));
 #endif
 
   md5_context_t ctx;
-- 
2.34.1

