Просмотр исходного кода

Speedup SD card & enlarge your RAM. (#1649)

* FuriHal: sram2 memory manager
* FuriHal: sram2 memory allocator
* FuriHal: allow NULL buffers for txrx in spi hal
* SD card: sector cache
* FuriHal: fix init in memory hal
* RPC: STARTUP instead SERVICE
* Memory: pool "free" command
* Thread: service can be statically allocated in a memory pool

Co-authored-by: あく <alleteam@gmail.com>
SG 3 лет назад
Родитель
Сommit
99a7d06f71

+ 3 - 0
applications/cli/cli_commands.c

@@ -281,6 +281,9 @@ void cli_command_free(Cli* cli, string_t args, void* context) {
     printf("Total heap size: %d\r\n", memmgr_get_total_heap());
     printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap());
     printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block());
+
+    printf("Pool free: %d\r\n", memmgr_pool_get_free());
+    printf("Maximum pool block: %d\r\n", memmgr_pool_get_max_block());
 }
 
 void cli_command_free_blocks(Cli* cli, string_t args, void* context) {

+ 1 - 1
applications/meta/application.fam

@@ -3,7 +3,7 @@ App(
     name="Basic services",
     apptype=FlipperAppType.METAPACKAGE,
     provides=[
-        "rpc",
+        "rpc_start",
         "bt",
         "desktop",
         "loader",

+ 4 - 8
applications/rpc/application.fam

@@ -1,12 +1,8 @@
 App(
-    appid="rpc",
-    name="RpcSrv",
-    apptype=FlipperAppType.SERVICE,
-    entry_point="rpc_srv",
+    appid="rpc_start",
+    apptype=FlipperAppType.STARTUP,
+    entry_point="rpc_on_system_start",
     cdefines=["SRV_RPC"],
-    requires=[
-        "cli",
-    ],
-    stack_size=4 * 1024,
+    requires=["cli"],
     order=10,
 )

+ 1 - 3
applications/rpc/rpc.c

@@ -395,7 +395,7 @@ void rpc_session_close(RpcSession* session) {
     furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect);
 }
 
-int32_t rpc_srv(void* p) {
+void rpc_on_system_start(void* p) {
     UNUSED(p);
     Rpc* rpc = malloc(sizeof(Rpc));
 
@@ -406,8 +406,6 @@ int32_t rpc_srv(void* p) {
         cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc);
 
     furi_record_create(RECORD_RPC, rpc);
-
-    return 0;
 }
 
 void rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler) {

+ 1 - 1
firmware/targets/f7/Inc/FreeRTOSConfig.h

@@ -14,7 +14,7 @@ extern uint32_t SystemCoreClock;
 #define configENABLE_MPU 0
 
 #define configUSE_PREEMPTION 1
-#define configSUPPORT_STATIC_ALLOCATION 0
+#define configSUPPORT_STATIC_ALLOCATION 1
 #define configSUPPORT_DYNAMIC_ALLOCATION 1
 #define configUSE_IDLE_HOOK 0
 #define configUSE_TICK_HOOK 0

+ 59 - 0
firmware/targets/f7/fatfs/sector_cache.c

@@ -0,0 +1,59 @@
+#include "sector_cache.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <furi.h>
+#include <furi_hal_memory.h>
+
+#define SECTOR_SIZE 512
+#define N_SECTORS 8
+
+typedef struct {
+    uint32_t itr;
+    uint32_t sectors[N_SECTORS];
+    uint8_t sector_data[N_SECTORS][SECTOR_SIZE];
+} SectorCache;
+
+static SectorCache* cache = NULL;
+
+void sector_cache_init() {
+    if(cache == NULL) {
+        cache = furi_hal_memory_alloc(sizeof(SectorCache));
+    }
+
+    if(cache != NULL) {
+        FURI_LOG_I("SectorCache", "Initializing sector cache");
+        memset(cache, 0, sizeof(SectorCache));
+    } else {
+        FURI_LOG_E("SectorCache", "Cannot enable sector cache");
+    }
+}
+
+uint8_t* sector_cache_get(uint32_t n_sector) {
+    if(cache != NULL && n_sector != 0) {
+        for(int sector_i = 0; sector_i < N_SECTORS; ++sector_i) {
+            if(cache->sectors[sector_i] == n_sector) {
+                return cache->sector_data[sector_i];
+            }
+        }
+    }
+    return NULL;
+}
+
+void sector_cache_put(uint32_t n_sector, uint8_t* data) {
+    if(cache == NULL) return;
+    cache->sectors[cache->itr % N_SECTORS] = n_sector;
+    memcpy(cache->sector_data[cache->itr % N_SECTORS], data, SECTOR_SIZE);
+    cache->itr++;
+}
+
+void sector_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) {
+    if(cache == NULL) return;
+    for(int sector_i = 0; sector_i < N_SECTORS; ++sector_i) {
+        if((cache->sectors[sector_i] >= start_sector) &&
+           (cache->sectors[sector_i] <= end_sector)) {
+            cache->sectors[sector_i] = 0;
+        }
+    }
+}

+ 36 - 0
firmware/targets/f7/fatfs/sector_cache.h

@@ -0,0 +1,36 @@
+#pragma once
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Init sector cache system
+ */
+void sector_cache_init();
+
+/**
+ * @brief Get sector data from cache
+ * @param n_sector Sector number
+ * @return Pointer to sector data or NULL if not found
+ */
+uint8_t* sector_cache_get(uint32_t n_sector);
+
+/**
+ * @brief Put sector data to cache
+ * @param n_sector Sector number
+ * @param data Pointer to sector data
+ */
+void sector_cache_put(uint32_t n_sector, uint8_t* data);
+
+/**
+ * @brief Invalidate sector cache for given range
+ * @param start_sector Start sector number
+ * @param end_sector End sector number
+ */
+void sector_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector);
+
+#ifdef __cplusplus
+}
+#endif

+ 18 - 17
firmware/targets/f7/fatfs/stm32_adafruit_sd.c

@@ -92,6 +92,7 @@
 #include "string.h"
 #include "stdio.h"
 #include <furi_hal.h>
+#include "sector_cache.h"
 
 /** @addtogroup BSP
   * @{
@@ -377,6 +378,8 @@ uint8_t BSP_SD_Init(bool reset_card) {
     furi_hal_sd_spi_handle = NULL;
     furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow);
 
+    sector_cache_init();
+
     /* SD initialized and set to SPI mode properly */
     return res;
 }
@@ -427,9 +430,15 @@ uint8_t
     uint32_t offset = 0;
     uint32_t addr;
     uint8_t retr = BSP_SD_ERROR;
-    uint8_t* ptr = NULL;
     SD_CmdAnswer_typedef response;
     uint16_t BlockSize = 512;
+    uint8_t* cached_data;
+
+    bool single_sector_read = (NumOfBlocks == 1);
+    if(single_sector_read && (cached_data = sector_cache_get(ReadAddr))) {
+        memcpy(pData, cached_data, BlockSize);
+        return BSP_SD_OK;
+    }
 
     /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and 
      Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */
@@ -440,12 +449,6 @@ uint8_t
         goto error;
     }
 
-    ptr = malloc(sizeof(uint8_t) * BlockSize);
-    if(ptr == NULL) {
-        goto error;
-    }
-    memset(ptr, SD_DUMMY_BYTE, sizeof(uint8_t) * BlockSize);
-
     /* Initialize the address */
     addr = (ReadAddr * ((flag_SDHC == 1) ? 1 : BlockSize));
 
@@ -461,7 +464,7 @@ uint8_t
         /* Now look for the data token to signify the start of the data */
         if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) {
             /* Read the SD block data : read NumByteToRead data */
-            SD_IO_WriteReadData(ptr, (uint8_t*)pData + offset, BlockSize);
+            SD_IO_WriteReadData(NULL, (uint8_t*)pData + offset, BlockSize);
 
             /* Set next read address*/
             offset += BlockSize;
@@ -479,13 +482,16 @@ uint8_t
         SD_IO_WriteByte(SD_DUMMY_BYTE);
     }
 
+    if(single_sector_read) {
+        sector_cache_put(ReadAddr, (uint8_t*)pData);
+    }
+
     retr = BSP_SD_OK;
 
 error:
     /* Send dummy byte: 8 Clock pulses of delay */
     SD_IO_CSState(1);
     SD_IO_WriteByte(SD_DUMMY_BYTE);
-    if(ptr != NULL) free(ptr);
 
     /* Return the reponse */
     return retr;
@@ -509,9 +515,9 @@ uint8_t BSP_SD_WriteBlocks(
     uint32_t offset = 0;
     uint32_t addr;
     uint8_t retr = BSP_SD_ERROR;
-    uint8_t* ptr = NULL;
     SD_CmdAnswer_typedef response;
     uint16_t BlockSize = 512;
+    sector_cache_invalidate_range(WriteAddr, WriteAddr + NumOfBlocks);
 
     /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and 
      Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */
@@ -522,11 +528,6 @@ uint8_t BSP_SD_WriteBlocks(
         goto error;
     }
 
-    ptr = malloc(sizeof(uint8_t) * BlockSize);
-    if(ptr == NULL) {
-        goto error;
-    }
-
     /* Initialize the address */
     addr = (WriteAddr * ((flag_SDHC == 1) ? 1 : BlockSize));
 
@@ -547,7 +548,7 @@ uint8_t BSP_SD_WriteBlocks(
         SD_IO_WriteByte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE);
 
         /* Write the block data to SD */
-        SD_IO_WriteReadData((uint8_t*)pData + offset, ptr, BlockSize);
+        SD_IO_WriteReadData((uint8_t*)pData + offset, NULL, BlockSize);
 
         /* Set next write address */
         offset += BlockSize;
@@ -569,7 +570,7 @@ uint8_t BSP_SD_WriteBlocks(
     retr = BSP_SD_OK;
 
 error:
-    if(ptr != NULL) free(ptr);
+
     /* Send dummy byte: 8 Clock pulses of delay */
     SD_IO_CSState(1);
     SD_IO_WriteByte(SD_DUMMY_BYTE);

+ 2 - 0
firmware/targets/f7/furi_hal/furi_hal.c

@@ -1,5 +1,6 @@
 #include <furi_hal.h>
 #include <furi_hal_mpu.h>
+#include <furi_hal_memory.h>
 
 #include <stm32wbxx_ll_cortex.h>
 
@@ -78,6 +79,7 @@ void furi_hal_init() {
     furi_hal_rfid_init();
 #endif
     furi_hal_bt_init();
+    furi_hal_memory_init();
     furi_hal_compress_icon_init();
 
     // FatFS driver initialization

+ 119 - 0
firmware/targets/f7/furi_hal/furi_hal_memory.c

@@ -0,0 +1,119 @@
+#include <furi_hal.h>
+#include <furi_hal_memory.h>
+#include <furi_hal_rtc.h>
+
+#define TAG "FuriHalMemory"
+
+typedef enum {
+    SRAM_A,
+    SRAM_B,
+    SRAM_MAX,
+} SRAM;
+
+typedef struct {
+    void* start;
+    uint32_t size;
+} FuriHalMemoryRegion;
+
+typedef struct {
+    FuriHalMemoryRegion region[SRAM_MAX];
+} FuriHalMemory;
+
+static FuriHalMemory* furi_hal_memory = NULL;
+
+extern const void __sram2a_start__;
+extern const void __sram2a_free__;
+extern const void __sram2b_start__;
+
+void furi_hal_memory_init() {
+    if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) {
+        return;
+    }
+
+    if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) {
+        FURI_LOG_E(TAG, "C2 start timeout");
+        return;
+    }
+
+    FuriHalMemory* memory = malloc(sizeof(FuriHalMemory));
+
+    const BleGlueC2Info* c2_ver = ble_glue_get_c2_info();
+
+    if(c2_ver->mode == BleGlueC2ModeStack) {
+        uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__;
+        uint32_t sram2a_unprotected_size = (32 - c2_ver->MemorySizeSram2A) * 1024;
+        uint32_t sram2b_unprotected_size = (32 - c2_ver->MemorySizeSram2B) * 1024;
+
+        memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__;
+        memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__;
+
+        if(sram2a_unprotected_size > sram2a_busy_size) {
+            memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size;
+        } else {
+            memory->region[SRAM_A].size = 0;
+        }
+        memory->region[SRAM_B].size = sram2b_unprotected_size;
+
+        FURI_LOG_I(
+            TAG, "SRAM2A: 0x%p, %d", memory->region[SRAM_A].start, memory->region[SRAM_A].size);
+        FURI_LOG_I(
+            TAG, "SRAM2B: 0x%p, %d", memory->region[SRAM_B].start, memory->region[SRAM_B].size);
+
+        if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) {
+            if((memory->region[SRAM_A].size > 0)) {
+                FURI_LOG_I(TAG, "SRAM2A clear");
+                memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size);
+            }
+            if((memory->region[SRAM_B].size > 0)) {
+                FURI_LOG_I(TAG, "SRAM2B clear");
+                memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size);
+            }
+            furi_hal_memory = memory;
+            FURI_LOG_I(TAG, "Enabled");
+        } else {
+            free(memory);
+            FURI_LOG_E(TAG, "No SRAM2 available");
+        }
+    } else {
+        free(memory);
+        FURI_LOG_E(TAG, "No Core2 available");
+    }
+}
+
+void* furi_hal_memory_alloc(size_t size) {
+    if(furi_hal_memory == NULL) {
+        return NULL;
+    }
+
+    for(int i = 0; i < SRAM_MAX; i++) {
+        if(furi_hal_memory->region[i].size >= size) {
+            void* ptr = furi_hal_memory->region[i].start;
+            furi_hal_memory->region[i].start += size;
+            furi_hal_memory->region[i].size -= size;
+            return ptr;
+        }
+    }
+    return NULL;
+}
+
+size_t furi_hal_memory_get_free() {
+    if(furi_hal_memory == NULL) return 0;
+
+    size_t free = 0;
+    for(int i = 0; i < SRAM_MAX; i++) {
+        free += furi_hal_memory->region[i].size;
+    }
+    return free;
+}
+
+size_t furi_hal_memory_max_pool_block() {
+    if(furi_hal_memory == NULL) return 0;
+
+    size_t max = 0;
+    for(int i = 0; i < SRAM_MAX; i++) {
+        if(furi_hal_memory->region[i].size > max) {
+            max = furi_hal_memory->region[i].size;
+        }
+    }
+    return max;
+}

+ 12 - 6
firmware/targets/f7/furi_hal/furi_hal_spi.c

@@ -139,8 +139,6 @@ bool furi_hal_spi_bus_trx(
     uint32_t timeout) {
     furi_assert(handle);
     furi_assert(handle->bus->current_handle == handle);
-    furi_assert(tx_buffer);
-    furi_assert(rx_buffer);
     furi_assert(size > 0);
 
     bool ret = true;
@@ -149,15 +147,23 @@ bool furi_hal_spi_bus_trx(
 
     while(size > 0) {
         if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE(handle->bus->spi) && tx_allowed) {
-            LL_SPI_TransmitData8(handle->bus->spi, *tx_buffer);
-            tx_buffer++;
+            if(tx_buffer) {
+                LL_SPI_TransmitData8(handle->bus->spi, *tx_buffer);
+                tx_buffer++;
+            } else {
+                LL_SPI_TransmitData8(handle->bus->spi, 0xFF);
+            }
             tx_size--;
             tx_allowed = false;
         }
 
         if(LL_SPI_IsActiveFlag_RXNE(handle->bus->spi)) {
-            *rx_buffer = LL_SPI_ReceiveData8(handle->bus->spi);
-            rx_buffer++;
+            if(rx_buffer) {
+                *rx_buffer = LL_SPI_ReceiveData8(handle->bus->spi);
+                rx_buffer++;
+            } else {
+                LL_SPI_ReceiveData8(handle->bus->spi);
+            }
             size--;
             tx_allowed = true;
         }

+ 8 - 4
firmware/targets/f7/stm32wb55xx_flash.ld

@@ -57,7 +57,8 @@ MEMORY
 {
 FLASH (rx)                 : ORIGIN = 0x08000000, LENGTH = 1024K
 RAM1 (xrw)                 : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
-RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K
+RAM2A (xrw)                : ORIGIN = 0x20030000, LENGTH = 10K
+RAM2B (xrw)                : ORIGIN = 0x20038000, LENGTH = 10K
 }
 
 /* Define output sections */
@@ -186,9 +187,12 @@ SECTIONS
   }
 
   .ARM.attributes 0       : { *(.ARM.attributes) }
-   MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED
-   MB_MEM1 (NOLOAD)       : { *(MB_MEM1) } >RAM_SHARED
-   MB_MEM2 (NOLOAD)       : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
+  ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A
+   MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A
+   MB_MEM1 (NOLOAD)       : { *(MB_MEM1) } >RAM2A
+   MB_MEM2 (NOLOAD)       : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A
+  ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A
+  ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B
 }
 
 

+ 8 - 4
firmware/targets/f7/stm32wb55xx_ram_fw.ld

@@ -57,7 +57,8 @@ MEMORY
 {
 FLASH (rx)                 : ORIGIN = 0x08000000, LENGTH = 1024K
 RAM1 (xrw)                 : ORIGIN = 0x20000000, LENGTH = 0x30000
-RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K
+RAM2A (xrw)                : ORIGIN = 0x20030000, LENGTH = 10K
+RAM2B (xrw)                : ORIGIN = 0x20038000, LENGTH = 10K
 }
 
 /* Define output sections */
@@ -184,9 +185,12 @@ SECTIONS
   }
 
   .ARM.attributes 0       : { *(.ARM.attributes) }
-   MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED
-   MB_MEM1 (NOLOAD)       : { *(MB_MEM1) } >RAM_SHARED
-   MB_MEM2 (NOLOAD)       : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
+  ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A
+   MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A
+   MB_MEM1 (NOLOAD)       : { *(MB_MEM1) } >RAM2A
+   MB_MEM2 (NOLOAD)       : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A
+  ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A
+  ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B
 }
 
 

+ 44 - 0
firmware/targets/furi_hal_include/furi_hal_memory.h

@@ -0,0 +1,44 @@
+/**
+ * @file furi_hal_memory.h
+ * Memory HAL API
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Init memory pool manager
+ */
+void furi_hal_memory_init();
+
+/**
+ * @brief Allocate memory from separate memory pool. That memory can't be freed.
+ * 
+ * @param size 
+ * @return void* 
+ */
+void* furi_hal_memory_alloc(size_t size);
+
+/**
+ * @brief Get free memory pool size
+ * 
+ * @return size_t 
+ */
+size_t furi_hal_memory_get_free();
+
+/**
+ * @brief Get max free block size from memory pool
+ * 
+ * @return size_t 
+ */
+size_t furi_hal_memory_max_pool_block();
+
+#ifdef __cplusplus
+}
+#endif

+ 16 - 0
furi/core/memmgr.c

@@ -1,6 +1,7 @@
 #include "memmgr.h"
 #include "common_defines.h"
 #include <string.h>
+#include <furi_hal_memory.h>
 
 extern void* pvPortMalloc(size_t xSize);
 extern void vPortFree(void* pv);
@@ -77,3 +78,18 @@ void* __wrap__realloc_r(struct _reent* r, void* ptr, size_t size) {
     UNUSED(r);
     return realloc(ptr, size);
 }
+
+void* memmgr_alloc_from_pool(size_t size) {
+    void* p = furi_hal_memory_alloc(size);
+    if(p == NULL) p = malloc(size);
+
+    return p;
+}
+
+size_t memmgr_pool_get_free(void) {
+    return furi_hal_memory_get_free();
+}
+
+size_t memmgr_pool_get_max_block(void) {
+    return furi_hal_memory_max_pool_block();
+}

+ 22 - 0
furi/core/memmgr.h

@@ -35,6 +35,28 @@ size_t memmgr_get_total_heap(void);
  */
 size_t memmgr_get_minimum_free_heap(void);
 
+/**
+ * @brief Allocate memory from separate memory pool. That memory can't be freed.
+ * 
+ * @param size 
+ * @return void* 
+ */
+void* memmgr_alloc_from_pool(size_t size);
+
+/**
+ * @brief Get free memory pool size
+ * 
+ * @return size_t 
+ */
+size_t memmgr_pool_get_free(void);
+
+/**
+ * @brief Get max free block size from memory pool
+ * 
+ * @return size_t 
+ */
+size_t memmgr_pool_get_max_block(void);
+
 #ifdef __cplusplus
 }
 #endif

+ 29 - 9
furi/core/thread.c

@@ -7,7 +7,9 @@
 #include "mutex.h"
 
 #include <task.h>
+#include "log.h"
 #include <m-string.h>
+#include <furi_hal_rtc.h>
 #include <furi_hal_console.h>
 
 #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
@@ -20,6 +22,7 @@ struct FuriThreadStdout {
 };
 
 struct FuriThread {
+    bool is_service;
     FuriThreadState state;
     int32_t ret;
 
@@ -84,6 +87,11 @@ static void furi_thread_body(void* context) {
     furi_assert(thread->state == FuriThreadStateRunning);
     furi_thread_set_state(thread, FuriThreadStateStopped);
 
+    if(thread->is_service) {
+        FURI_LOG_E(
+            "Service", "%s thread exited. Thread memory cannot be reclaimed.", thread->name);
+    }
+
     // clear thread local storage
     __furi_thread_stdout_flush(thread);
     furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL);
@@ -96,7 +104,7 @@ static void furi_thread_body(void* context) {
 FuriThread* furi_thread_alloc() {
     FuriThread* thread = malloc(sizeof(FuriThread));
     string_init(thread->output.buffer);
-
+    thread->is_service = false;
     return thread;
 }
 
@@ -117,6 +125,10 @@ void furi_thread_set_name(FuriThread* thread, const char* name) {
     thread->name = name ? strdup(name) : NULL;
 }
 
+void furi_thread_mark_as_service(FuriThread* thread) {
+    thread->is_service = true;
+}
+
 void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
     furi_assert(thread);
     furi_assert(thread->state == FuriThreadStateStopped);
@@ -168,15 +180,23 @@ void furi_thread_start(FuriThread* thread) {
 
     furi_thread_set_state(thread, FuriThreadStateStarting);
 
-    BaseType_t ret = xTaskCreate(
-        furi_thread_body,
-        thread->name,
-        thread->stack_size / 4,
-        thread,
-        thread->priority ? thread->priority : FuriThreadPriorityNormal,
-        &thread->task_handle);
+    uint32_t stack = thread->stack_size / 4;
+    UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
+    if(thread->is_service) {
+        thread->task_handle = xTaskCreateStatic(
+            furi_thread_body,
+            thread->name,
+            stack,
+            thread,
+            priority,
+            memmgr_alloc_from_pool(sizeof(StackType_t) * stack),
+            memmgr_alloc_from_pool(sizeof(StaticTask_t)));
+    } else {
+        BaseType_t ret = xTaskCreate(
+            furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle);
+        furi_check(ret == pdPASS);
+    }
 
-    furi_check(ret == pdPASS);
     furi_check(thread->task_handle);
 }
 

+ 7 - 0
furi/core/thread.h

@@ -73,6 +73,13 @@ void furi_thread_free(FuriThread* thread);
  */
 void furi_thread_set_name(FuriThread* thread, const char* name);
 
+/** Mark thread as service
+ * The service cannot be stopped or removed, and cannot exit from the thread body
+ * 
+ * @param thread 
+ */
+void furi_thread_mark_as_service(FuriThread* thread);
+
 /** Set FuriThread stack size
  *
  * @param      thread      FuriThread instance

+ 20 - 0
furi/flipper.c

@@ -2,6 +2,7 @@
 #include <applications.h>
 #include <furi.h>
 #include <furi_hal_version.h>
+#include <furi_hal_memory.h>
 
 #define TAG "Flipper"
 
@@ -38,9 +39,28 @@ void flipper_init() {
         furi_thread_set_name(thread, FLIPPER_SERVICES[i].name);
         furi_thread_set_stack_size(thread, FLIPPER_SERVICES[i].stack_size);
         furi_thread_set_callback(thread, FLIPPER_SERVICES[i].app);
+        furi_thread_mark_as_service(thread);
 
         furi_thread_start(thread);
     }
 
     FURI_LOG_I(TAG, "services startup complete");
 }
+
+void vApplicationGetIdleTaskMemory(
+    StaticTask_t** tcb_ptr,
+    StackType_t** stack_ptr,
+    uint32_t* stack_size) {
+    *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));
+    *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configMINIMAL_STACK_SIZE);
+    *stack_size = configMINIMAL_STACK_SIZE;
+}
+
+void vApplicationGetTimerTaskMemory(
+    StaticTask_t** tcb_ptr,
+    StackType_t** stack_ptr,
+    uint32_t* stack_size) {
+    *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));
+    *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH);
+    *stack_size = configTIMER_TASK_STACK_DEPTH;
+}