ROM and RAM Management

Introduction

Here are some tips and tricks to manage your application size.

RAM and ROM usage

The total RAM usage is:

ZI-data + RW-data

The total flash usage is:

Code + RO-data + RW-data
  • ZI-data - Zero initialized data, data variables set to 0

  • RW-data - Data variables that are different from 0

  • RO-data - Constants placed in flash

This means that if you have a variable defined globally with the const flag it will most likely end up in flash, while a variable that is prone to change will end up either in RW-data or ZI-data.

These values should be listed when you compile your project (atleast in Keil, use 'arm-none-eabi-size myapplication.out' for GCC) or located in the .map file generated by the compiler/linker.

Why is RW-data listed in RAM and code? This is because this section holds the non-zero initialized values. for instance:

static int g_test = 0xFF1234FF;

This will consume 4 bytes of RAM, and 4 bytes of flash to store it's initial value (0xFF1234FF).

More details about RAM usage:

The total RAM consumption also holds the application stack for auto-variables, which variables inside a scope, such as int func(){int a; int b;}, where 'a' and 'b' here are auto-variables. The default stack_size set in your arm_startup.s file is set to 2048 bytes.

If the stack grows outside it's area (2048 bytes in this case), meaning that your auto-variables in scope is greater than the stack_size set, you will run into a stack overflow. if you google "stack usage monitoring" or similar you'll find methods for evaluating the stack usage for a given application. Note that the overall stack usage must be evaluated run-time, with the application running in all corner-cases.

Monitoring usage using Keil

Keil will put a .map file in the _build folder as long as the "Linker listing" options are on on the "Listing" tab of the target options.

image description

The file arm_startup_nrf51.s contains the definitions of the heap and the stack, each being 2 kB by default, and this is therefore the reason it shows up with 4 kB in your map file. If you know that you don't use 2 kB of heap, feel free to reduce the heap size. The memory usage of S110 itself is given in the Softdevice Specification (1.5 kB of stack, no heap), and no heap is used by the SDK.

Some of the initalization macros used in main.c contains a static buffer for data storage. This applies for for example BLE_STACK_INIT, APP_TIMER_INIT, APP_BUTTON_INIT and friends, and this is most likely the cause of the 972 bytes used in main. You can for example take a look at the timer queue size, as explained here and the maximum number of timers, since this is usually a big part of the main memory.

Apart from this, your map file should contain a listing of the symbols defined in main, and how much memory each consumes, like this:

.bss                                     0x200020f0   Section      776  main.o(.bss)
APP_TIMER_BUF                            0x200020f0   Data         576  main.o(.bss)
EVT_BUFFER                               0x20002330   Data          80  main.o(.bss)
app_gpiote_buf                           0x20002380   Data          20  main.o(.bss)
m_tps                                    0x20002394   Data          10  main.o(.bss)
m_ias                                    0x200023a0   Data          16  main.o(.bss)
m_lls                                    0x200023b0   Data          20  main.o(.bss)
m_bas                                    0x200023c4   Data          24  main.o(.bss)
m_ias_c                                  0x200023dc   Data          12  main.o(.bss)
m_battery_sim_cfg                        0x200023e8   Data          16  main.o(.bss)

For this application, the timer buffer is the biggest consumer in main, with the event buffer for the softdevice being second. Also the app_gpiote structure consumes a bit, as well as the different services.

Reducing usage

MicroLIB is a library that is made for ARM-based embedded platforms, it uses less memory than the normal C library, see this page from ARM for more information. Enabling MicroLIB will remove the HEAP, giving you >4kB for your application. To enable MicroLIB in Keil, check the box "Use MicroLIB" as shown below.

image description

SEGGERs Real Time Terminal that was introduced in this tutorial has a heavy strain on RAM. If you find yourself short on RAM you can try configuring the up and down buffers and their sizes. These are the default settings(found in SEGGER_RTT_Conf.h):

#define SEGGER_RTT_MAX_NUM_UP_BUFFERS             (2)     // Max. number of up-buffers (T->H) available on this target    (Default: 2)
#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS           (2)     // Max. number of down-buffers (H->T) available on this target  (Default: 2)

#define BUFFER_SIZE_UP                            (1024)  // Size of the buffer for terminal output of target, up to host (Default: 1k)
#define BUFFER_SIZE_DOWN                          (16)    // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16)

#define SEGGER_RTT_PRINTF_BUFFER_SIZE             (64)    // Size of buffer for RTT printf to bulk-send chars via RTT     (Default: 64)

Try reducing the number of up and down buffers as well as reducing their size to ensure that they don't use too much memory. If this does not work you will have to consider alternatives to RTT.

Another module that might easily consume some RAM is the bond manager. It needs to fit all bond information in RAM before writing to flash, so by reducing the max number of bonds you can store, you can reduce its RAM consumption.

Resource requirements

As a rule of thumb 0x1000 in hex corresponds to 4kB of RAM. For the nRF51 series there are two available RAM sizes(16 kB and 32 kB) and two available flash sizes(128 kB and 256 kB), to see which combination of the two you have, please see the compatibility matrix. For the nRF52 there is one size available(64kB RAM 512 kB flash).

RAM

  • 16 kB - Corresponds to 0x4000

  • 32 kB - Corresponds to 0x8000

  • 64 kB - Corresponds to 0x10000

Flash

  • 128 kB - Corresponds to 0x20000

  • 256 kB - Corresponds to 0x40000

  • 512 kB - Corresponds to 0x80000

The call stack is by default set to 2K, which is shared between the softdevice and the application. As an example S110 v.7.0.1 uses a maximum of 1536 bytes of the stack. You can modify arm_startup_nrf51.s if you wish to change stack or heap size(Note that MicroLIB will remove the heap entirely).

Heap memory can safely be set to zero as long as you don't use dynamic memory allocation (malloc/free). Note that heap is generally not used in our SDK examples.

image description

Common IRAM and IROM settings

Here is an example of how to calculate the IRAM and IROM settings for a QFAA-chip(256kB Flash and 16kB RAM) with version 1.0 of the S130 SoftDevice:

According to section 13 of the SoftDevice Specification for S130 the IRAM base should be a minimum of 0x200022D8, and might be larger depending on the requirements of your application. Use the default 0x20002800.

The IRAM size consumption is dependent on the attribute table size(see section 13.1 of the SDS) with the default value of 0x600 bytes you would end up having an IRAM size of 0x4000 - 0x2800 = 0x1800.

The IROM base should be 0x0001C000 which is the same as 0x1C000. The size should be 0x40000 - 0x1C000 = 0x24000.

Finally here are some common values for different combinations of SoftDevices and chips.

S110 v8.0 from SDS v2.0

  • IRAM base - 0x20002000

  • IRAM size(16 kB RAM) - 0x2000

  • IRAM size(32 kB RAM) - 0x6000

  • IROM base - 0x18000

  • IROM size(128 kB Flash) - 0x8000

  • IROM size(256 kB Flash) - 0x28000

S120 v2.1 from SDS v2.1

  • IRAM base - 0x20002800

  • IRAM size(16 kB RAM) - 0x1800

  • IRAM size(32 kB RAM) - 0x5800

  • IROM base - 0x1D000

  • IROM size(128 kB Flash) - 0x3000

  • IROM size(256 kB Flash) - 0x23000

S130 v1.0 from SDS v1.0

  • IRAM base - 0x20002800

  • IRAM size(16 kB RAM) - 0x1800

  • IRAM size(32 kB RAM) - 0x5800

  • IROM base - 0x1C000

  • IROM size(128 kB Flash) - 0x4000

  • IROM size(256 kB Flash) - 0x24000

S130 v2.0 from SDS v2.0

  • IRAM base - 0x20001870

  • IRAM size(16 kB RAM) - 0x2790

  • IRAM size(32 kB RAM) - 0x6790

  • IROM base - 0x1B000

  • IROM size(128 kB Flash) - 0x5000

  • IROM size(256 kB Flash) - 0x25000

S210 v5.0 from SDS v3.0

  • IRAM base - 0x20000900

  • IRAM size(16 kB RAM) - 0x3700

  • IRAM size(32 kB RAM) - 0x7700

  • IROM base - 0xD000

  • IROM size(128 kB Flash) - 0x13000

  • IROM size(256 kB Flash) - 0x33000

S310 v3.0 from SDS v3.0

  • IRAM base - 0x20002200

  • IRAM size(16 kB RAM) - 0x1E00

  • IRAM size(32 kB RAM) - 0x5E00

  • IROM base - 0x1D000

  • IROM size(128 kB Flash) - 0x3000

  • IROM size(256 kB Flash) - 0x23000

S132 v0.9 from SDS v0.5

  • IRAM base - 0x20002800

  • IRAM size(64 kB RAM) - 0xD800

  • IROM base - 0x1C000

  • IROM size(512 kB Flash) - 0x64000

S132 v2.0 from SDS v2.0

  • IRAM base - 0x20001870

  • IRAM size(64 kB RAM) - 0xE790

  • IROM base - 0x1C000

  • IROM size(512 kB Flash) - 0x64000

Final notes and further reading

Please leave any feedback, questions or comments below.

  • https://devzone.nordicsemi.com/question/21787/available-ram-for-the-application/
  • https://devzone.nordicsemi.com/question/30765/nrf_error_no_mem/?answer=31098#post-id-31098
  • https://devzone.nordicsemi.com/question/12524/ram-usage-with-s110-support/
  • https://devzone.nordicsemi.com/question/23059/how-to-monitor-flash-and-ram-usage-after-compilation/
  • https://devzone.nordicsemi.com/question/1088/putting-my-app-on-a-ram-memory-diet/
Parents
  • @hkn, @oyvakar : What is the difference between the ROM/RAM usage on 'build output window' log we see in uVision IDE compared to the generated map file? They have different size values for Code, RO Data, RW Data, and ZI Data when added up. I am using Keil MDK.

    Build output window:

    Program Size: Code=82628 RO-data=65012 RW-data=1136 ZI-data=54672
    

    But in the generated map file (nrf52832_xxaa_s132.map), the total figures do not match compared to the above:

    Code (inc. data)   RO Data    RW Data    ZI Data      Debug   
    
     70816       5458      63684        976      46016    1206351   Grand Totals
     70816       5458      63684        212      46016    1206351   ELF Image Totals (compressed)
     70816       5458      63684        212          0          0   ROM Totals
    
    Total RO  Size (Code + RO Data)               134500 ( 131.35kB)
    Total RW  Size (RW Data + ZI Data)             46992 (  45.89kB)
    Total ROM Size (Code + RO Data + RW Data)     134712 ( 131.55kB)
    
Comment
  • @hkn, @oyvakar : What is the difference between the ROM/RAM usage on 'build output window' log we see in uVision IDE compared to the generated map file? They have different size values for Code, RO Data, RW Data, and ZI Data when added up. I am using Keil MDK.

    Build output window:

    Program Size: Code=82628 RO-data=65012 RW-data=1136 ZI-data=54672
    

    But in the generated map file (nrf52832_xxaa_s132.map), the total figures do not match compared to the above:

    Code (inc. data)   RO Data    RW Data    ZI Data      Debug   
    
     70816       5458      63684        976      46016    1206351   Grand Totals
     70816       5458      63684        212      46016    1206351   ELF Image Totals (compressed)
     70816       5458      63684        212          0          0   ROM Totals
    
    Total RO  Size (Code + RO Data)               134500 ( 131.35kB)
    Total RW  Size (RW Data + ZI Data)             46992 (  45.89kB)
    Total ROM Size (Code + RO Data + RW Data)     134712 ( 131.55kB)
    
Children
No Data