diff --git a/furi/core/check.h b/furi/core/check.h index a2b7dd18e4bf..4d1ce10d14d6 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -59,11 +59,11 @@ FURI_NORETURN void __furi_halt_implementation(void); #define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) /** Check condition and crash if check failed */ -#define __furi_check(__e, __m) \ - do { \ - if(!(__e)) { \ - __furi_crash(__m); \ - } \ +#define __furi_check(__e, __m) \ + do { \ + if(__builtin_expect(!(__e), 0)) { \ + __furi_crash(__m); \ + } \ } while(0) /** Check condition and crash if failed @@ -75,11 +75,11 @@ FURI_NORETURN void __furi_halt_implementation(void); /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define __furi_assert(__e, __m) \ - do { \ - if(!(__e)) { \ - __furi_crash(__m); \ - } \ +#define __furi_assert(__e, __m) \ + do { \ + if(__builtin_expect(!(__e), 0)) { \ + __furi_crash(__m); \ + } \ } while(0) #else #define __furi_assert(__e, __m) \ @@ -98,11 +98,11 @@ FURI_NORETURN void __furi_halt_implementation(void); #define furi_assert(...) \ M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) -#define furi_break(__e) \ - do { \ - if(!(__e)) { \ - asm volatile("bkpt 0"); \ - } \ +#define furi_break(__e) \ + do { \ + if(__builtin_expect(!(__e), 0)) { \ + asm volatile("bkpt 0"); \ + } \ } while(0) #ifdef __cplusplus diff --git a/furi/core/kernel.c b/furi/core/kernel.c index 34c562bb34ef..170b3f5a1022 100644 --- a/furi/core/kernel.c +++ b/furi/core/kernel.c @@ -166,7 +166,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) { return stat; } -uint32_t furi_get_tick(void) { +__attribute__((flatten)) uint32_t furi_get_tick(void) { TickType_t ticks; if(furi_kernel_is_irq_or_masked() != 0U) { diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index a3bbf4556aec..c067f791b7be 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -1,4 +1,5 @@ #include "memmgr.h" +#include "memmgr_heap.h" #include #include #include @@ -25,15 +26,24 @@ void* realloc(void* ptr, size_t size) { void* p = pvPortMalloc(size); if(ptr != NULL) { - memcpy(p, ptr, size); - vPortFree(ptr); + if(p != NULL) { + size_t old_size = memmgr_heap_get_block_size(ptr); + size_t copy_size = old_size < size ? old_size : size; + memcpy(p, ptr, copy_size); + vPortFree(ptr); + } } return p; } void* calloc(size_t count, size_t size) { - return pvPortMalloc(count * size); + size_t total = count * size; + void* p = pvPortMalloc(total); + if(p != NULL) { + memset(p, 0, total); + } + return p; } char* strdup(const char* s) { diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 3ce0558a3b19..67a7827ba8c8 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -539,6 +539,23 @@ size_t xPortGetMinimumEverFreeHeapSize(void) { } /*-----------------------------------------------------------*/ +size_t memmgr_heap_get_block_size(void* pv) { + uint8_t* puc = (uint8_t*)pv; + BlockLink_t* pxLink; + + if(pv == NULL) { + return 0; + } + + puc -= xHeapStructSize; + pxLink = (void*)puc; + + configASSERT(heapBLOCK_IS_ALLOCATED(pxLink) != 0); + + return (pxLink->xBlockSize & ~heapBLOCK_ALLOCATED_BITMASK) - xHeapStructSize; +} +/*-----------------------------------------------------------*/ + void xPortResetHeapMinimumEverFreeHeapSize(void) { xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; } diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 7d889f1520e7..22d0e8a2c875 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -44,6 +44,13 @@ size_t memmgr_heap_get_max_free_block(void); */ void memmgr_heap_printf_free_blocks(void); +/** Get usable size of an allocated heap block + * + * @param ptr pointer to allocated memory + * @return usable size in bytes + */ +size_t memmgr_heap_get_block_size(void* ptr); + #ifdef __cplusplus } #endif diff --git a/furi/core/string.c b/furi/core/string.c index 804445e22d9b..329b48c14db7 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -1,5 +1,6 @@ #include "string.h" #include +#include struct FuriString { string_t string; @@ -175,11 +176,33 @@ int furi_string_cat_printf(FuriString* v, const char format[], ...) { } int furi_string_cat_vprintf(FuriString* v, const char format[], va_list args) { - FuriString* string = furi_string_alloc(); - int ret = furi_string_vprintf(string, format, args); - furi_string_cat(v, string); - furi_string_free(string); - return ret; + // In-place append: format directly into destination buffer at current offset + // Eliminates temporary string allocation (malloc + free) per call + va_list args_copy; + va_copy(args_copy, args); + + size_t old_size = string_size(v->string); + char* ptr = m_str1ng_get_cstr(v->string); + size_t alloc = string_capacity(v->string); + + int size = vsnprintf(&ptr[old_size], alloc - old_size, format, args); + + if(size > 0 && (old_size + (size_t)size + 1 >= alloc)) { + // Buffer too small — grow and retry + ptr = m_str1ng_fit2size(v->string, old_size + (size_t)size + 1); + size = vsnprintf( + &ptr[old_size], string_capacity(v->string) - old_size, format, args_copy); + } + va_end(args_copy); + + if(size >= 0) { + m_str1ng_set_size(v->string, old_size + (size_t)size); + } else { + // vsnprintf error — restore original string + ptr[old_size] = 0; + } + + return size; } bool furi_string_empty(const FuriString* v) { diff --git a/furi/core/thread.c b/furi/core/thread.c index e29a8711e3c2..b74a0a538cf3 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -458,11 +458,11 @@ int32_t furi_thread_get_return_code(FuriThread* thread) { return thread->ret; } -FuriThreadId furi_thread_get_current_id(void) { +__attribute__((flatten)) FuriThreadId furi_thread_get_current_id(void) { return (FuriThreadId)xTaskGetCurrentTaskHandle(); } -FuriThread* furi_thread_get_current(void) { +__attribute__((flatten)) FuriThread* furi_thread_get_current(void) { FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); return thread; } @@ -538,7 +538,7 @@ uint32_t furi_thread_flags_clear(uint32_t flags) { return rflags; } -uint32_t furi_thread_flags_get(void) { +__attribute__((flatten)) uint32_t furi_thread_flags_get(void) { TaskHandle_t hTask; uint32_t rflags; diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index 6af861324f5f..4ef210f8932e 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -28,7 +28,7 @@ else: "NDEBUG", ], CCFLAGS=[ - "-Og", + "-Os", ], ) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 2bb63b88e49f..64f1dfefe18b 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,87.1,, +Version,+,87.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -2607,6 +2607,7 @@ Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_get_block_size,size_t,void* Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId Function,+,memmgr_heap_printf_free_blocks,void, diff --git a/targets/f7/furi_hal/furi_hal_spi.c b/targets/f7/furi_hal/furi_hal_spi.c index 9997d278d402..7140962e1887 100644 --- a/targets/f7/furi_hal/furi_hal_spi.c +++ b/targets/f7/furi_hal/furi_hal_spi.c @@ -111,6 +111,14 @@ bool furi_hal_spi_bus_tx( furi_check(buffer); furi_check(size > 0); + if(furi_kernel_is_running()) { + // Use DMA for TX when scheduler is running, freeing the CPU during transfer + bool ret = furi_hal_spi_bus_trx_dma(handle, (uint8_t*)buffer, NULL, size, timeout); + LL_SPI_ClearFlag_OVR(handle->bus->spi); + return ret; + } + + // Polling fallback for pre-scheduler context bool ret = true; while(size > 0) { @@ -227,9 +235,26 @@ bool furi_hal_spi_bus_trx_dma( } if(rx_buffer == NULL) { - // Only TX mode, do not use RX channel + // TX-only mode: set up RX DMA to drain incoming bytes (prevents OVR) + uint8_t dma_rx_dummy; LL_DMA_InitTypeDef dma_config = {0}; + + // RX DMA channel: drain SPI RX FIFO into dummy byte (no increment) + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)&dma_rx_dummy; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_NOINCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_rx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); + + // TX DMA channel dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -243,26 +268,31 @@ bool furi_hal_spi_bus_trx_dma( dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); -#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 - LL_DMA_ClearFlag_TC7(SPI_DMA); +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + LL_DMA_ClearFlag_TC6(SPI_DMA); #else #error Update this code. Would you kindly? #endif - furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); if(!dma_tx_was_enabled) { LL_SPI_EnableDMAReq_TX(spi); } + if(!dma_rx_was_enabled) { + LL_SPI_EnableDMAReq_RX(spi); + } // acquire semaphore before enabling DMA furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); - LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_RX_DEF); LL_DMA_EnableChannel(SPI_DMA_TX_DEF); - // and wait for it to be released (DMA transfer complete) + // wait for RX DMA complete (all bytes transmitted and drained) if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { ret = false; FURI_LOG_E(TAG, "DMA timeout\r\n"); @@ -270,14 +300,19 @@ bool furi_hal_spi_bus_trx_dma( // release semaphore, because we are using it as a flag furi_semaphore_release(spi_dma_completed); - LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_RX_DEF); if(!dma_tx_was_enabled) { LL_SPI_DisableDMAReq_TX(spi); } - furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); + if(!dma_rx_was_enabled) { + LL_SPI_DisableDMAReq_RX(spi); + } + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); LL_DMA_DeInit(SPI_DMA_TX_DEF); + LL_DMA_DeInit(SPI_DMA_RX_DEF); } else { // TRX or RX mode, use both channels uint32_t tx_mem_increase_mode; diff --git a/targets/f7/inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h index 8d34925ecce3..f67a924ddf44 100644 --- a/targets/f7/inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -20,7 +20,11 @@ #define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configENABLE_HEAP_PROTECTOR 1 -#define configHEAP_CLEAR_MEMORY_ON_FREE 1 +#ifdef FURI_DEBUG +#define configHEAP_CLEAR_MEMORY_ON_FREE 1 +#else +#define configHEAP_CLEAR_MEMORY_ON_FREE 0 +#endif #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 @@ -56,7 +60,7 @@ if lengths will always be less than the number of bytes in a size_t. */ #define configMESSAGE_BUFFER_LENGTH_TYPE size_t #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1 -#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 4 +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0