skill
---
name: ncs-mem
description: Optimize and debug memory usage in Nordic nRF Connect SDK (NCS) applications. Use when analyzing RAM/Flash usage, reducing memory footprint, debugging stack overflows, or investigating memory-related crashes in nRF projects.
---
# Nordic nRF Connect SDK (NCS) Memory Optimization and Debugging
## Key Concepts
**Memory Types in Embedded Systems:**
- **Flash (ROM)**: Stores code, constants, and read-only data
- **RAM**: Runtime memory for stack, heap, global/static variables
- **Stack**: Per-thread memory for function calls and local variables
- **Heap**: Dynamic memory allocation pool
**Memory Analysis Tools:**
- **west build size reports**: Detailed memory usage breakdown
- **Puncover**: Interactive memory visualization tool
- **GDB memory inspection**: Runtime memory analysis
- **Thread Analyzer**: Stack usage monitoring
- **Memory mapping**: Linker scripts and partition manager
## Critical Rules
1. **Always ensure NCS environment is set up** before memory analysis (use ncs-env-setup skill)
2. **Build in Release mode** for accurate Flash size measurements (Debug builds are larger)
3. **Enable memory debugging features only during development** (they consume extra memory)
4. **Check partition manager configuration** for multi-image builds
## Workflow for Memory Analysis
### Step 1: Build and Generate Memory Reports
Build the project and examine memory usage:
```sh
west build -b <board>
```
The build output shows memory summary:
```
Memory region Used Size Region Size %age Used
FLASH: 123456 B 1 MB 11.77%
RAM: 45678 B 256 KB 17.41%
```
### Step 2: Detailed Memory Analysis
**Generate detailed size report:**
```sh
west build -t rom_report # Flash usage by symbol
west build -t ram_report # RAM usage by symbol
west build -t puncover # Interactive HTML visualization
```
**Examine symbol sizes:**
```sh
arm-none-eabi-nm --size-sort -S build/zephyr/zephyr.elf | tail -20
```
**View memory map:**
```sh
cat build/zephyr/zephyr.map
```
### Step 3: Identify Memory Hotspots
**Flash optimization targets:**
- Large functions or libraries
- Debug strings and logging
- Unused features/drivers
- Crypto/networking stacks
**RAM optimization targets:**
- Thread stacks (often oversized)
- Heap size
- Static buffers
- Global variables
### Step 4: Apply Optimizations
See "Common Memory Optimizations" section below.
## Common Memory Optimizations
### Flash Reduction Strategies
**1. Disable Unused Features**
Review and disable in `prj.conf`:
```
# Disable assertions in production
CONFIG_ASSERT=n
# Minimal logging (or disable completely)
CONFIG_LOG=n
# Or use minimal level
CONFIG_LOG_DEFAULT_LEVEL=1
CONFIG_LOG_MODE_MINIMAL=y
# Disable shell if not needed
CONFIG_SHELL=n
# Disable printk for production
CONFIG_PRINTK=n
CONFIG_EARLY_CONSOLE=n
```
**2. Optimize Compilation**
```
# Size optimization (default is usually speed)
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_COMPILER_OPT="-Os"
# Link-time optimization
CONFIG_LTO=y
```
**3. Remove Debug Information**
Build in Release mode:
```sh
west build -b <board> -- -DCMAKE_BUILD_TYPE=Release
```
**4. Reduce Networking Stack Size**
```
# For minimal networking
CONFIG_NET_BUF_DATA_SIZE=128
CONFIG_NET_PKT_RX_COUNT=4
CONFIG_NET_PKT_TX_COUNT=4
# Disable IPv6 if only using IPv4
CONFIG_NET_IPV6=n
```
**5. Minimize Bluetooth Stack**
```
# Reduce Bluetooth buffers
CONFIG_BT_BUF_ACL_TX_COUNT=3
CONFIG_BT_BUF_ACL_TX_SIZE=27
CONFIG_BT_CTLR_DATA_LENGTH_MAX=27
# Disable unused Bluetooth features
CONFIG_BT_OBSERVER=n
CONFIG_BT_BROADCASTER=n
```
### RAM Reduction Strategies
**1. Optimize Thread Stack Sizes**
Analyze actual stack usage:
```
# Enable thread analyzer
CONFIG_THREAD_ANALYZER=y
CONFIG_THREAD_NAME=y
```
Then at runtime, check stack usage and adjust:
```c
// In code, enable periodic analysis
CONFIG_THREAD_ANALYZER_AUTO=y
CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=60
```
Reduce oversized stacks in `prj.conf`:
```
CONFIG_MAIN_STACK_SIZE=2048 # Default often 4096+
CONFIG_IDLE_STACK_SIZE=512 # Default often 1024+
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1024
CONFIG_BT_RX_STACK_SIZE=1024
```
**2. Reduce Heap Size**
```
# If not using malloc/new, disable heap
CONFIG_HEAP_MEM_POOL_SIZE=0
# Or minimize it
CONFIG_HEAP_MEM_POOL_SIZE=2048
```
**3. Optimize Buffer Sizes**
```
# Network buffers
CONFIG_NET_BUF_DATA_SIZE=128
# UART buffers
CONFIG_UART_ASYNC_TX_BUF_SIZE=256
CONFIG_UART_ASYNC_RX_BUF_SIZE=256
```
**4. Reduce Shell/Log Memory**
```
# Reduce logging buffer
CONFIG_LOG_BUFFER_SIZE=512
# Reduce shell backend buffer
CONFIG_SHELL_BACKEND_SERIAL_TX_RING_BUFFER_SIZE=64
CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE=64
```
## Memory Debugging
### Debug Stack Overflows
**Enable stack protection:**
```
CONFIG_STACK_SENTINEL=y # Detects overflow
CONFIG_USERSPACE=y # Memory protection (if HW supports)
CONFIG_THREAD_STACK_INFO=y # Stack usage info
CONFIG_MPU_STACK_GUARD=y # MPU protection (ARM Cortex-M)
```
**Monitor stack usage:**
```c
#include <zephyr/kernel.h>
void check_stack_usage(void) {
size_t unused;
k_thread_stack_space_get(k_current_get(), &unused);
printk("Thread %s: %zu bytes unused\n",
k_thread_name_get(k_current_get()), unused);
}
```
**Use Thread Analyzer:**
```
CONFIG_THREAD_ANALYZER=y
CONFIG_THREAD_ANALYZER_USE_PRINTK=y
CONFIG_THREAD_ANALYZER_AUTO=y
```
View output via UART/RTT to see per-thread stack usage.
### Debug Heap Issues
**Enable heap debugging:**
```
CONFIG_HEAP_MEM_POOL_SIZE=8192
CONFIG_SYS_HEAP_VALIDATE=y # Validate heap on alloc/free
```
**Track allocations:**
```c
#include <zephyr/sys/heap_listener.h>
// Monitor heap usage
size_t free_bytes, used_bytes;
sys_heap_runtime_stats_get(&_system_heap, &free_bytes, &used_bytes);
printk("Heap: %zu used, %zu free\n", used_bytes, free_bytes);
```
### Debug Memory Corruption
**Enable memory protection:**
```
CONFIG_USERSPACE=y
CONFIG_MPU_STACK_GUARD=y
CONFIG_HW_STACK_PROTECTION=y
CONFIG_THREAD_STACK_INFO=y
```
**Use address sanitizers (if available):**
```
# Some platforms support AddressSanitizer
CONFIG_ASAN=y
```
## Partition Manager (Multi-Image Builds)
For builds with bootloader (MCUboot) + app + network core:
**View partition layout:**
```sh
cat build/partitions.yml
```
**Adjust partition sizes:**
Create `pm_static.yml` or `pm_static_<board>.yml`:
```yaml
mcuboot:
address: 0x0
size: 0x10000
app:
address: 0x10000
size: 0x70000
mcuboot_pad:
address: 0x80000
size: 0x200
```
**Check partition usage:**
```sh
# After build, check fit
west build -t partition_manager_report
```
## Memory Profiling Tools
### Puncover - Interactive Memory Visualization
Generate HTML visualization:
```sh
west build -t puncover
# Opens browser with interactive memory breakdown
```
Features:
- Symbol-level Flash/RAM usage
- Clickable tree view by subsystem
- Size trends across builds
### Static Analysis
```sh
# Detailed ELF analysis
arm-none-eabi-size -A build/zephyr/zephyr.elf
# Section sizes
arm-none-eabi-objdump -h build/zephyr/zephyr.elf
# Symbol table
arm-none-eabi-nm -S --size-sort build/zephyr/zephyr.elf
```
### Runtime Analysis with GDB
```sh
west debug
# In GDB:
(gdb) info mem # Memory regions
(gdb) x/100x 0x20000000 # Examine RAM
(gdb) print &_heap_start
(gdb) print &_heap_end
```
## Common Memory Issues
### Issue: Flash Overflow
**Symptoms:** Build fails with "region `FLASH' overflowed"
**Solutions:**
1. Enable size optimizations: `CONFIG_SIZE_OPTIMIZATIONS=y`
2. Disable debug features: `CONFIG_ASSERT=n`, `CONFIG_LOG=n`
3. Build in Release mode: `-DCMAKE_BUILD_TYPE=Release`
4. Disable unused subsystems (BT features, networking, etc.)
5. Consider device with more Flash
### Issue: RAM Overflow
**Symptoms:** Build fails with "region `RAM' overflowed"
**Solutions:**
1. Reduce thread stack sizes (analyze with Thread Analyzer first)
2. Reduce heap: `CONFIG_HEAP_MEM_POOL_SIZE`
3. Reduce buffers (network, UART, shell, logging)
4. Use static allocation instead of heap
5. Consider device with more RAM
### Issue: Stack Overflow at Runtime
**Symptoms:** Hard faults, crashes, corruption, unexpected behavior
**Solutions:**
1. Enable `CONFIG_STACK_SENTINEL=y` to detect
2. Use Thread Analyzer to measure actual usage
3. Increase problematic thread's stack size
4. Review recursive functions or large local arrays
5. Enable `CONFIG_MPU_STACK_GUARD=y` for protection
### Issue: Heap Exhaustion
**Symptoms:** malloc/k_malloc returns NULL, out-of-memory errors
**Solutions:**
1. Increase `CONFIG_HEAP_MEM_POOL_SIZE`
2. Check for memory leaks (missing free calls)
3. Use static allocation when possible
4. Enable heap validation: `CONFIG_SYS_HEAP_VALIDATE=y`
5. Profile heap usage with sys_heap_runtime_stats_get()
### Issue: High Memory Usage from Logging
**Symptoms:** Large Flash/RAM usage, logging subsystem in rom_report/ram_report
**Solutions:**
1. Reduce log level: `CONFIG_LOG_DEFAULT_LEVEL=1` (errors only)
2. Use deferred mode with smaller buffer
3. Disable logs in production: `CONFIG_LOG=n`
4. Use minimal logging: `CONFIG_LOG_MODE_MINIMAL=y`
5. Remove LOG_MODULE_REGISTER from unused modules
## Best Practices
### Development Phase
```
# Enable all debugging features
CONFIG_ASSERT=y
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=4
CONFIG_THREAD_ANALYZER=y
CONFIG_STACK_SENTINEL=y
CONFIG_SYS_HEAP_VALIDATE=y
```
### Production Phase
```
# Minimize memory usage
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_LTO=y
CONFIG_ASSERT=n
CONFIG_LOG=n # Or LOG_DEFAULT_LEVEL=1
CONFIG_PRINTK=n
CONFIG_SHELL=n
# Build: west build -b <board> -- -DCMAKE_BUILD_TYPE=MinSizeRel
```
### Continuous Monitoring
1. Track memory usage across builds
2. Set CI/CD thresholds (e.g., alert if Flash > 80%)
3. Review rom_report/ram_report regularly
4. Profile on target hardware (Debug vs Release builds differ)
## Example Workflows
### Basic Memory Analysis
```sh
# 1. Build the project
west build -b nrf52840dk_nrf52840
# 2. Check memory summary (already shown in build output)
# 3. Generate detailed reports
west build -t rom_report # View Flash usage
west build -t ram_report # View RAM usage
west build -t puncover # Interactive HTML report
# 4. Identify largest symbols
arm-none-eabi-nm --size-sort -S build/zephyr/zephyr.elf | tail -30
```
### Reduce Flash by 50%
```sh
# 1. Start with current usage
west build -b nrf52840dk_nrf52840
# Note Flash usage (e.g., 250 KB)
# 2. Apply optimizations to prj.conf
# - CONFIG_SIZE_OPTIMIZATIONS=y
# - CONFIG_LTO=y
# - CONFIG_LOG=n
# - CONFIG_ASSERT=n
# - CONFIG_SHELL=n
# 3. Rebuild in Release mode
west build -b nrf52840dk_nrf52840 -p -- -DCMAKE_BUILD_TYPE=MinSizeRel
# 4. Verify new usage (should be ~125 KB)
```
### Debug Stack Overflow
```sh
# 1. Enable stack debugging in prj.conf:
# CONFIG_STACK_SENTINEL=y
# CONFIG_THREAD_ANALYZER=y
# CONFIG_THREAD_ANALYZER_AUTO=y
# CONFIG_THREAD_NAME=y
# 2. Rebuild and flash
west build -b nrf52840dk_nrf52840
west flash
# 3. Monitor UART/RTT output for:
# - "Stack overflow detected" from sentinel
# - Thread analyzer periodic reports
# 4. Increase stack for problematic thread
# CONFIG_<THREAD>_STACK_SIZE=<larger_value>
```
### Optimize Thread Stacks
```sh
# 1. Enable thread analyzer
# CONFIG_THREAD_ANALYZER=y
# CONFIG_THREAD_ANALYZER_AUTO=y
# CONFIG_THREAD_ANALYZER_AUTO_INTERVAL=30
# CONFIG_THREAD_NAME=y
# 2. Build, flash, and run application through typical use cases
west build -b nrf52840dk_nrf52840
west flash
# 3. View RTT/UART output showing stack usage per thread
# Example output:
# sysworkq : unused 512 usage 512 / 1024 (50 %)
# bt_rx : unused 256 usage 768 / 1024 (75 %)
# 4. Adjust stack sizes in prj.conf based on actual usage + margin
# CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536 # Was 1024, needs more
# CONFIG_BT_RX_STACK_SIZE=896 # Was 1024, can reduce
```
## Notes
- Memory usage varies significantly between Debug and Release builds
- Always test on actual hardware (QEMU/emulation differs)
- Stack overflow is the most common memory issue in embedded systems
- When in doubt, use Thread Analyzer before adjusting stack sizes
- Partition Manager is used for multi-core and bootloader scenarios
- Some optimizations (LTO, size opts) increase build time
- Memory-mapped peripherals don't count against Flash/RAM limits
- Use `west build -t menuconfig` to explore memory-related Kconfig options
- Device Tree overlays can affect memory layout (reserved regions)