nRF9160: Porting the modem library to work with bare metal application

I would like some help with porting the modem library (nrf_modem_lib) to work with my bare metal application.

Besides a custom nrf_modem_os.c, I have made a slightly modified version of nrfx_ipc.c that explicitly uses the non-secure registers for the IPC peripheral along removing various dependencies on the nrfx lib glue, since I would like to have as little nrfx-dependent code as possible in my project.

Be aware that my project is single-threaded, so I do not want to use actual threads - that is also why I have not implemented the semaphore related functions in nrf_modem_os.c.

Bootloader and memory layout

I have implemented a simple bootloader that configures non-secure access to various FLASH and RAM regions, enables non-secure access to various peripherals including the IPC and EGU1, as required by the modem library.

The bootloader then enters non-secure mode and boots up the application image, located at address 0x10000 in the FLASH memory. This all works fine, and I can use the UARTE0 and GPIO peripherals in non-secure mode just fine.

The lower 64 kiB of FLASH and RAM are (for now) reserved for the secure bootloader. The rest is reserved for the application and configured as non-secure during boot. This is reflected in the custom linker scripts for each image (included below).

Furthermore, the lower 32 kiB of the RAM reserved for the application is used as shared memory for the modem library. This is also reflected in the linker scripts and defs.h included below. This should make malloc() and free() operate above the shared modem memory. For allocation inside the shared memory, I have implemented a custom allocator (not included below) that only operates inside the TX region of the shared memory.

The application and the problem

In my application main() function, I call nrf_modem_init() to initialize the modem library - but it never returns.

It does appear to call my custom nrf_modem_os_init() and return back to the nrf_modem_init() function. Before that returns, it seems to get stuck in a loop calling my custom implementation of nrf_modem_os_timedwait(). Using GDB (output included below) it appears to call IPC and RPC related stuff in between the calls to nrf_modem_os_timedwait().

I would like to have my custom nrf_modem_os.c and nrfx_ipc.c reviewed. In particular, I may have misunderstood how nrf_modem_os_timedwait() should be implemented and/or how to configure the IPC and EGU1 interrupts appropriately.

As I understand it, nrf_modem_os_timedwait() should block (not return) until either a timeout occurs or until nrf_modem_os_event_notify() is called, right? The timeout is at the moment implemented in a very crude fashion, but I expect to use an actual timer in the nRF9160 for this purpose later on, if necessary.

Hopefully I can get some hints on what is (or could be) wrong, so I can end up with a nrf_modem_init() call that does not get stuck in a loop. Hopefully I can then move on to test AT command communication :-)

I have included what I believe are the important and relevant files below.

I am using modem firmware version 1.3.3. The version of the SDK and build tools are indicated in the Makefile below.

Note: The documentation found online at https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrfxlib/nrf_modem/doc/ug_nrf_modem_porting_os.html seems to be outdated or incomplete compared to the .rst files found in the sdk-nrfxlib on Github: https://github.com/nrfconnect/sdk-nrfxlib.

boot.ld

app.ld

boot/main.c

app/main.c

app/defs.h

app/nrf_modem_os.c

app/nrfx_ipc.c

Makefile

GDB output with manual stepping just before nrf_modem_os_init() function returns:

  • Can you try to enable the IRQ before calling `nrf_modem_init`?

    NVIC_SetPriority(IPC_IRQn, 0);
    NVIC_ClearPendingIRQ(IPC_IRQn);
    NVIC_EnableIRQ(IPC_IRQn);

    see if that gets you a bit further.

  • Thanks for your suggestion, emdi.

    I did just try that, by inserting these lines just before line 80 in app/main.c. It made no difference at all.

    I am wondering if I am supposed to do some global setup of the NVIC before interrupts will actually work? Or if they work in secure mode but non in non-secure mode unless I do something further? I have absolutely no idea. It is my first time programming an ARM MCU with TrustZone.

    Maybe the IPC_IRQHandler() defined at line 50 in app/nrf_modem_os.c is not recognized as an interrupt handler but just as a regular function that apparently never gets called?

    There is still the possibility that my slightly modified nrfx_ipc.c is not working as it should...

  • You definitely need to set the NRF_IPC and NRF_POWER peripherals as non-secure, as well as the shared memory.

  • Thanks again, emdi :-)

    This is already done in boot/main.c, see line 24 and 29 for the peripherals.

    At line 18 and 19, I allow non-secure access to all memory above the first 64 kiB. The shared memory for the modem library is the first 32 kiB of the memory marked as non-secure.

    I believe this is correct. Otherwise, I would get HardFaultExceptions. I do not get any of these anymore, and the application is in fact running. It is just getting stuck in the modem library, but no exceptions are issued.

    Do I need to manually configure the NRF_POWER peripheral or do the modem library take care of this?

  • All TrustZone configuration must be done by the Application, libmodem won't and can't change the security attributes since it is not part of the secure firmware image :)

1 2 3 4 5