Mbed build fails with merge firmware error

Hi,

I’m getting error with the latest PlatformIO, Mbed-framework & tools. It tries to merge firmware with userfirmware quite a long time until the process fails with error:

*** [.pio/build/nrf52_dk/firmware.hex] MemoryError : 
Traceback (most recent call last):
  File "/home/timo/.platformio/packages/tool-scons/script/../engine/SCons/Action.py", line 1054, in execute
    result = self.execfunction(target=target, source=rsources, env=env)
  File "/home/timo/.platformio/packages/framework-mbed/platformio/platformio-build.py", line 274, in merge_firmwares
    env.subst(target)[0]
  File "/home/timo/.platformio/packages/framework-mbed/platformio/pio_mbed_adapter.py", line 127, in merge_apps
   update_regions, update_res, self.notify, self.toolchain.config)
  File "/home/timo/.platformio/packages/framework-mbed/tools/regions.py", line 168, in merge_region_list
    merged.puts(begin, padding * pad_size)
  File "/home/timo/.platformio/packages/framework-mbed/platformio/package_deps/py2/intelhex/__init__.py", line 745, in puts
    self._buf[addr+i] = a[i]
MemoryError

Any help is appreciated. Thanks.

Best regards,
Timo

Hi @traj! Can you provide your project to reproduce the issue?

Unfortunately I can not provide the current project. I can try to reproduce with some skeleton code later. The problem appeared after I migrated from earlier platformio and mbed versions to the latest. Unfortunately I have not the exact versions of working platformio and tools, only mbed which was 5.10.1. Here is full build log:

Processing nrf52_dk (platform: nordicnrf52; board: nrf52_dk; framework: mbed)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/nordicnrf52/nrf52_dk.html
PLATFORM: Nordic nRF52 3.5.0 > Nordic nRF52-DK
HARDWARE: NRF52832 64MHz, 64KB RAM, 512KB Flash
DEBUG: Current (jlink) On-board (cmsis-dap, jlink) External (blackmagic, stlink)
PACKAGES: toolchain-gccarmnoneeabi 1.70201.0 (7.2.1), framework-mbed 5.51203.190509 (5.12.3), tool-sreccat 1.164.0 (1.64)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
merge_firmwares([".pio/build/nrf52_dk/firmware.hex"], [".pio/build/nrf52_dk/userfirmware.hex"])
Checking size .pio/build/nrf52_dk/firmware.elf
Memory Usage -> http://bit.ly/pio-memory-usage
DATA:    [==        ]  22.7% (used 14896 bytes from 65536 bytes)
PROGRAM: [==        ]  21.8% (used 114120 bytes from 524288 bytes)
*** [.pio/build/nrf52_dk/firmware.hex] MemoryError : 
Traceback (most recent call last):
  File "/home/timo/.platformio/packages/tool-scons/script/../engine/SCons/Action.py", line 1054, in execute
    result = self.execfunction(target=target, source=rsources, env=env)
  File "/home/timo/.platformio/packages/framework-mbed/platformio/platformio-build.py", line 274, in merge_firmwares
    env.subst(target)[0]
  File "/home/timo/.platformio/packages/framework-mbed/platformio/pio_mbed_adapter.py", line 127, in merge_apps
    update_regions, update_res, self.notify, self.toolchain.config)
  File "/home/timo/.platformio/packages/framework-mbed/tools/regions.py", line 168, in merge_region_list
    merged.puts(begin, padding * pad_size)
  File "/home/timo/.platformio/packages/framework-mbed/platformio/package_deps/py2/intelhex/__init__.py", line 745, in puts
    self._buf[addr+i] = a[i]
MemoryError

platformio.ini

[env:nrf52_dk]
platform = nordicnrf52
board = nrf52_dk
framework = mbed
build_flags = -D PIO_FRAMEWORK_MBED_RTOS_PRESENT
upload_protocol = jlink
debug_tool = jlink
debug_init_break =

mbed_app.json

{
  "target_overrides": {
    "*": {
      "target.features_add": ["BLE"],
      "events.present": 1
    }
  }
}

I’ve narrowed the problem to modified linker script (that was working perfectly with earlier versions). With unmodified linker script the build will succeed with correct merge. Blank projects and project that included ble and rtos to gain some size will still succeed but not my actual project. Here is the modified linker script from .platformio/packages/framework-mbed/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52832/device/TOOLCHAIN_GCC_ARM/NRF52832.ld

/*
 * Copyright (c) 2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Linker script to configure memory regions. */

/* Default to no softdevice */
#if !defined(MBED_APP_START)
  #define MBED_APP_START 0x0
#endif

#if !defined(MBED_APP_SIZE)
  #define MBED_APP_SIZE 0x80000
#endif

/* If softdevice is present, set aside space for it */
#if !defined(MBED_RAM_START)
  #if defined(SOFTDEVICE_PRESENT)
    #define MBED_RAM_START  0x200032D0
    #define MBED_RAM_SIZE   0xCD30
  #else
    #define MBED_RAM_START  0x20000000
    #define MBED_RAM_SIZE   0x10000
  #endif
#endif

#if !defined(MBED_BOOT_STACK_SIZE)
    #define MBED_BOOT_STACK_SIZE 0x800
#endif

#define ENCRYPTIONKEY_MEM_SIZE 0x20
#define MANUFACTURERINFO_MEM_SIZE 0x10
#define MBED_RAM0_START MBED_RAM_START
#define MBED_RAM0_SIZE  0xDC
#define MBED_RAM1_START (MBED_RAM_START + MBED_RAM0_SIZE)
#define MBED_RAM1_SIZE  (MBED_RAM_SIZE - MBED_RAM0_SIZE - ENCRYPTIONKEY_MEM_SIZE - MANUFACTURERINFO_MEM_SIZE)
#define MANUFACTURERINFO_MEM_START (MBED_RAM1_START + MBED_RAM1_SIZE)
#define ENCRYPTIONKEY_MEM_START (MANUFACTURERINFO_MEM_START + MANUFACTURERINFO_MEM_SIZE)

MEMORY
{
  FLASH (rx)     : ORIGIN = MBED_APP_START, LENGTH = MBED_APP_SIZE
  RAM_NVIC (rwx) : ORIGIN = MBED_RAM0_START, LENGTH = MBED_RAM0_SIZE
  RAM (rwx)      : ORIGIN = MBED_RAM1_START, LENGTH = MBED_RAM1_SIZE
  MANUFACTURERINFO_MEM (rwx) : ORIGIN = MANUFACTURERINFO_MEM_START, LENGTH = MANUFACTURERINFO_MEM_SIZE
  ENCRYPTIONKEY_MEM (rwx) : ORIGIN = ENCRYPTIONKEY_MEM_START, LENGTH = ENCRYPTIONKEY_MEM_SIZE
}


OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")

/* Linker script to place sections and symbol values. Should be used together
 * with the other linker script that defines memory regions FLASH and RAM.
 * It references the following symbols that must be defined in code:
 *   Reset_Handler : Entry of reset handler
 *
 * It defines the following symbols that the code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 */
ENTRY(Reset_Handler)


SECTIONS
{
    .text :
    {
        KEEP(*(.Vectors))
        *(.text*)

        KEEP(*(.init))
        KEEP(*(.fini))

        /* .ctors */
        *crtbegin.o(.ctors)
        *crtbegin?.o(.ctors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
        *(SORT(.ctors.*))
        *(.ctors)

        /* .dtors */
        *crtbegin.o(.dtors)
        *crtbegin?.o(.dtors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
        *(SORT(.dtors.*))
        *(.dtors)

        *(.rodata*)

        KEEP(*(.eh_frame*))
    } > FLASH

    .sdh_soc_observers :
    {
        PROVIDE(__start_sdh_soc_observers = .);
        KEEP(*(SORT(.sdh_soc_observers*)))
        PROVIDE(__stop_sdh_soc_observers = .);
    } > FLASH

    .sdh_stack_observers :
    {
        PROVIDE(__start_sdh_stack_observers = .);
        KEEP(*(SORT(.sdh_stack_observers*)))
        PROVIDE(__stop_sdh_stack_observers = .);
    } > FLASH

    .sdh_req_observers :
    {
        PROVIDE(__start_sdh_req_observers = .);
        KEEP(*(SORT(.sdh_req_observers*)))
        PROVIDE(__stop_sdh_req_observers = .);
    } > FLASH

    .sdh_state_observers :
    {
        PROVIDE(__start_sdh_state_observers = .);
        KEEP(*(SORT(.sdh_state_observers*)))
        PROVIDE(__stop_sdh_state_observers = .);
    } > FLASH

    .sdh_ble_observers :
    {
        PROVIDE(__start_sdh_ble_observers = .);
        KEEP(*(SORT(.sdh_ble_observers*)))
        PROVIDE(__stop_sdh_ble_observers = .);
    } > FLASH

    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
        . = ALIGN(8);
    } > FLASH

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
        . = ALIGN(8);
    } > FLASH
    __exidx_end = .;

    __etext = .;

    .manufacturerinfo :
    {
        . = ALIGN(4);
        __manufacturerinfo_start__ = .;
        *(.manufacturerinfo*)
        __manufacturerinfo_end__ = .;
    } > MANUFACTURERINFO_MEM

    .encryptionkey :
    {
        . = ALIGN(4);
        __encryptionkey_start__ = .;
        *(.encryptionkey*)
        __encryptionkey_end__ = .;
    } > ENCRYPTIONKEY_MEM

    .data : AT (__etext)
    {
        __data_start__ = .;
        *(vtable)
        *(.data*)

        . = ALIGN(8);
        /* preinit data */
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP(*(.preinit_array))
        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(8);
        /* init data */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);


        . = ALIGN(8);
        /* finit data */
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP(*(SORT(.fini_array.*)))
        KEEP(*(.fini_array))
        PROVIDE_HIDDEN (__fini_array_end = .);

        . = ALIGN(8);
        PROVIDE(__start_fs_data = .);
        KEEP(*(.fs_data))
        PROVIDE(__stop_fs_data = .);

        *(.jcr)
        . = ALIGN(8);
        /* All data end */
        __data_end__ = .;

    } > RAM

    __edata = .;

    .nvictable (NOLOAD) :
    {
      PROVIDE(__start_nvictable = .);
      KEEP(*(.nvictable))
      PROVIDE(__stop_nvictable = .);
    } > RAM_NVIC

    .noinit (NOLOAD) :
    {
      PROVIDE(__start_noinit = .);
      KEEP(*(.noinit))
      PROVIDE(__stop_noinit = .);
    } > RAM

    .bss :
    {
        . = ALIGN(8);
        __bss_start__ = .;
        *(.bss*)
        *(COMMON)
        . = ALIGN(8);
        __bss_end__ = .;
    } > RAM

    .heap (NOLOAD):
    {
        __end__ = .;
        end = __end__;
        *(.heap*);

        /* Expand the heap to reach the stack boundary. */
        ASSERT(. <= (ORIGIN(RAM) + LENGTH(RAM) - MBED_BOOT_STACK_SIZE), "heap region overflowed into stack");
        . = ORIGIN(RAM) + LENGTH(RAM) - MBED_BOOT_STACK_SIZE;
        __HeapLimit = .;
    } > RAM
    PROVIDE(__heap_start = ADDR(.heap));
    PROVIDE(__heap_size = SIZEOF(.heap));
    PROVIDE(__mbed_sbrk_start = ADDR(.heap));
    PROVIDE(__mbed_krbs_start = ADDR(.heap) + SIZEOF(.heap));

    /* .stack_dummy section does not contain any symbols. It is only
     * used for the linker script to calculate the size of stack sections
     * and assign values to stack symbols later. */
    .stack (NOLOAD):
    {
        __StackLimit = .;
        *(.stack*)
        . = ORIGIN(RAM) + LENGTH(RAM);
    } > RAM

    /* Set the stack top to the end of RAM and move down the stack limit by
     * the size of the stack_dummy section. */
    __StackTop = ORIGIN(RAM) + LENGTH(RAM);
    __StackLimit = __StackTop - MBED_BOOT_STACK_SIZE;
    PROVIDE(__stack = __StackTop);
}

I guess no new info about this? There is definitely a bug in python merge script somewhere. Merge step will consume all the memory on my machine and finally crashes. The “userfirmware.hex” file which the process tries to merge with Nordic softdevice is correct. I verified this by editing the python script to skip the merge and then merge by using Nordic’s “mergehex” tool. The resulting firmware was working correctly. I’m currently forced to use this way to build software.

Hi @traj! We are preparing mbed v5.13.2 where your issue should be fixed, stay tuned. As a workaround you can temporarily roll back to the previous version of the platform.

Great to hear! And thank you for your excellent tool. It sure has made embedded developing to a newbie like me a pleasure!

BTW. have you tried to compile your project using mbed-cli tool? Does it work?

I’ve just created a new branch with the latest mbed framework, could you please test it with your project:

[env:nrf52_dk]
platform = https://github.com/platformio/platform-nordicnrf52.git#feature/latest-mbed-51302
board = nrf52_dk
framework = mbed
build_flags = -D PIO_FRAMEWORK_MBED_RTOS_PRESENT
upload_protocol = jlink
debug_tool = jlink
debug_init_break =

I finally managed to test the provided branch. Now the merge will succeed but there is some other issue. Compiled firmware does not work. Furthermore compile report shows that data and program usage took a huge increase versus builds with previous versions. Both have more than doubled and they are now above 50%.

Maybe it has something to do with the new BLE stack. Have you tried to compile your project using mbed-cli tool?

I think so. Mbed-cli with latest mbed will give me larger hex that will not work. Could this be some configuration error? I’m using Nordic’s softdevice so there should be no mbed BLE stack present. But somehow the hex has gained huge weight. Nordic’s softdevice has been updated between versions but the change is not that much.

So you don’t need BLE feature at all?

In the latest release they updated Nordic SDK to v15, but BLE is now based on Arm Cordio stack. And looks like this new stack has quite heavy memory footprint.

Yes I need the BLE. I just realized that my mbed_app.json config will include the Cordio stack. I found the correct extra_labels conf and will try with that.

Now with the correct labels in mbed_app.json I will get Nordic’s softdevice instead of Cordio. Unfortunately merge will still fail:

Processing nrf52_dk (platform: https://github.com/platformio/platform-nordicnrf52.git#feature/latest-mbed-51302; board: nrf52_dk; framework: mbed)
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/nordicnrf52/nrf52_dk.html
PLATFORM: Nordic nRF52 3.5.0 #5c88feb > Nordic nRF52-DK
HARDWARE: NRF52832 64MHz, 64KB RAM, 512KB Flash
DEBUG: Current (jlink) On-board (cmsis-dap, jlink) External (blackmagic, stlink)
PACKAGES: toolchain-gccarmnoneeabi 1.70201.0 (7.2.1), framework-mbed 5.51302.190807 (5.13.2), tool-sreccat 1.164.0 (1.64)
Collecting mbed sources...
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 0 compatible libraries
Scanning dependencies...
No dependencies
merge_firmwares([".pio/build/nrf52_dk/firmware.hex"], [".pio/build/nrf52_dk/userfirmware.hex"])
Checking size .pio/build/nrf52_dk/firmware.elf
Memory Usage -> http://bit.ly/pio-memory-usage
DATA:    [==        ]  23.0% (used 15064 bytes from 65536 bytes)
PROGRAM: [==        ]  21.2% (used 111088 bytes from 524288 bytes)
*** [.pio/build/nrf52_dk/firmware.hex] AttributeError : 'PlatformioMbedAdapter' object has no attribute 'config'
Traceback (most recent call last):
  File "/home/timo/.platformio/packages/tool-scons/script/../engine/SCons/Action.py", line 1054, in execute
    result = self.execfunction(target=target, source=rsources, env=env)
  File "/home/timo/.platformio/packages/framework-mbed/platformio/platformio-build.py", line 278, in merge_firmwares
    env.subst(target)[0]
  File "/home/timo/.platformio/packages/framework-mbed/platformio/pio_mbed_adapter.py", line 136, in merge_apps
    restrict_size=self.config.self.toolchain.config.restrict_size)
AttributeError: 'PlatformioMbedAdapter' object has no attribute 'config'
========================================================================= [ERROR] Took 82.36 seconds =========================================================================
The terminal process terminated with exit code: 1

Please attach your mbed_app.json

{
  "target_overrides": {
    "*": {
      "target.features_add": ["BLE"],
      "target.extra_labels_remove": ["CORDIO", "CORDIO_LL", "SOFTDEVICE_NONE", "NORDIC_CORDIO"],
      "target.extra_labels_add": ["SOFTDEVICE_COMMON", "SOFTDEVICE_S132_FULL", "NORDIC_SOFTDEVICE"],
      "events.present": 1,
      "rtos.present": 1
    }
  }
}