A bare metal example of Inter Processor Communication (IPC) on nrf5340

Dear experts,

I am programming bare metal using Segger Embedded Studio and nrf.h and want to achieve a simple example of network core sending data to the application core.

Since the network core runs in non-secure domain and application core runs in secure domain, there is a bit of initialization needed but I cannot get it to work.

Here is my example:

In main() on the application core, I run

  spu_configure();
  ipc_rx_configure();
  ipc_rx_recieve();


spu:

#include "SPU.h"

// IPC is peripheral ID 42 on nRF5340
#define IPC_PERIPH_ID 42

void spu_configure(void) {
// In secure boot eller startup på application core

// Configure RAM region 63 (0x2003E000-0x2003FFF) as non-secure
NRF_SPU_S->RAMREGION[63].PERM =
   (SPU_RAMREGION_PERM_READ_Msk |
    SPU_RAMREGION_PERM_WRITE_Msk |
    SPU_RAMREGION_PERM_EXECUTE_Msk);    // SECATTR bit is 0 => non-secure


NRF_SPU_S->PERIPHID[IPC_PERIPH_ID].PERM &= ~SPU_PERIPHID_PERM_SECATTR_NonSecure; 
                                        // SECATTR = 0 => non-secure access
}



and ipc_rx

#include "ipc_rx.h"

#define SHARED_MEM_ADDR ((volatile uint8_t*)0x2003E000)

typedef struct {
    uint8_t id;
    uint16_t value;
    char label[8];
} message_t;


// Configure the app core to trigger an event every time a message is transmitted from network core
void ipc_rx_configure(void) {


  // Enable interrupt for RECEIVE[0]
  NRF_IPC_S->INTENSET = IPC_INTENSET_RECEIVE0_Msk;
  NVIC_EnableIRQ(IPC_IRQn);
}


void ipc_rx_recieve(void) {

  // From now on this will be interrupt driven so stay here
  while (1) {
    __WFE();
    }

}

// This event will be triggered every time there is a message.
void IPC_IRQHandler(void) {
    if (NRF_IPC_S->EVENTS_RECEIVE[0]) {
        NRF_IPC_S->EVENTS_RECEIVE[0] = 0;

        message_t *msg = (message_t *)SHARED_MEM_ADDR;

        printf("Message: ");
        printf("%s", msg->label);
        printf(" \n");

        // Use msg->id, msg->value, msg->label
    
    }
}

In network core I run<

main()

 ipc_tx_configure();
 for (int i=1; i<1000; i++) {
  
  ipc_tx_transmit();
  
  for(int j=0; j<50000; j++) {
    float dummy = sinf(sinf(3.56));
    }
}



and ipc_tx

#include "ipc_tx.h"

#define SHARED_MEM_ADDR ((volatile uint8_t*)0x2003E000)

typedef struct {
    uint8_t id;
    uint16_t value;
    char label[8];
} message_t;


void ipc_tx_configure(void){

// Not really needed

}


void ipc_tx_transmit(void) {

    message_t *msg = (message_t *)SHARED_MEM_ADDR;

    msg->id = 1;
    msg->value = 1234;
    memcpy(msg->label, "TEMP", 5);

    // Trigger IPC event on channel 0
    NRF_IPC_NS->TASKS_SEND[0] = 1;

}



The code compiles and runs but I cannot seem to get the interrupt triggered on the application core

Any help appreciated.

/Jonas

Parents
  • Here is an update. 
    It works now. I summarize the solution below for anyone who is interested:

    App core main()

      spu_configure();
      ipc_rx_configure();
      ipc_rx_recieve();
    


    ipc_rx
    volatile uint32_t __attribute__((section(".shared_memory"))) shared_value;
    
    
    // Configure the app core to trigger an event every time a message is transmitted from network core
    void ipc_rx_configure(void) {
    
      // Enable interrupt for RECEIVE[0]
      NRF_IPC_S->INTENSET = IPC_INTENSET_RECEIVE0_Msk;
    
      // Listen to channel 0
      NRF_IPC_S->RECEIVE_CNF[0] = 1;
      
      // Enable IPC interrupt in NVIC
      NVIC_EnableIRQ(IPC_IRQn);
    
      // Start up the network core
      NRF_RESET_S->NETWORK.FORCEOFF = RESET_NETWORK_FORCEOFF_FORCEOFF_Release;
    
    }
    
    
    void ipc_rx_recieve(void) {
    
      // From now on this will be interrupt driven so stay here
      while (1) {
    
        //__WFE();
        }
    
    }
    
    // This event will be triggered every time there is a message.
    void IPC_IRQHandler(void) {
        if (NRF_IPC_S->EVENTS_RECEIVE[0]) {
            NRF_IPC_S->EVENTS_RECEIVE[0] = 0;
    
            printf("Message: ");
            printf("%u", shared_value);
            printf(" \n");
        
        }
    }
    
    

    spu

    #define SPU_RAM_REGION_INDEX 31
    
    void spu_configure(void) {
    // In secure boot eller startup på application core
    // Configure RAM region 31 (0x2003F000) as non-secure
    NRF_SPU_S->RAMREGION[SPU_RAM_REGION_INDEX].PERM =
       (SPU_RAMREGION_PERM_READ_Msk |
        SPU_RAMREGION_PERM_WRITE_Msk |
        SPU_RAMREGION_PERM_SECATTR_Non_Secure << SPU_RAMREGION_PERM_SECATTR_Msk |
        SPU_RAMREGION_PERM_EXECUTE_Msk);    // SECATTR bit is 0 => non-secure
    
    }


    I have added a reference to the right memory area in the linker script for both network and application cores as

    place at address 0x2003F000 { section .shared_memory };
    
    //but for the network core I have also added
    do not initialize                           { section .shared_memory };
    
    // ...since the app core must unlock the area before the network core can write

    Here is the network core 

    main()

     ipc_tx_configure();
    
     while(1) {
      delay_one_second();
      ipc_tx_transmit();
      }

    ipc_tx

    #define IPC_CHANNEL 0
    
    volatile uint32_t __attribute__((section(".shared_memory"))) shared_value;
    
    void ipc_tx_configure(void){
    
    // Set up the IPC channel 0 to be used for trigger a signal to application core.
    // We will only send on channel 0 (so we set bit 0 here)
        NRF_IPC_NS->SEND_CNF[IPC_CHANNEL] = 1;
    
    }
    
    
    void ipc_tx_transmit(void) {
    
        //Use the shared memory to send a message to the application core
        shared_value = 23;
    
        // Trigger IPC event on channel 0
        NRF_IPC_NS->TASKS_SEND[IPC_CHANNEL] = 1;
    
    
    }

Reply
  • Here is an update. 
    It works now. I summarize the solution below for anyone who is interested:

    App core main()

      spu_configure();
      ipc_rx_configure();
      ipc_rx_recieve();
    


    ipc_rx
    volatile uint32_t __attribute__((section(".shared_memory"))) shared_value;
    
    
    // Configure the app core to trigger an event every time a message is transmitted from network core
    void ipc_rx_configure(void) {
    
      // Enable interrupt for RECEIVE[0]
      NRF_IPC_S->INTENSET = IPC_INTENSET_RECEIVE0_Msk;
    
      // Listen to channel 0
      NRF_IPC_S->RECEIVE_CNF[0] = 1;
      
      // Enable IPC interrupt in NVIC
      NVIC_EnableIRQ(IPC_IRQn);
    
      // Start up the network core
      NRF_RESET_S->NETWORK.FORCEOFF = RESET_NETWORK_FORCEOFF_FORCEOFF_Release;
    
    }
    
    
    void ipc_rx_recieve(void) {
    
      // From now on this will be interrupt driven so stay here
      while (1) {
    
        //__WFE();
        }
    
    }
    
    // This event will be triggered every time there is a message.
    void IPC_IRQHandler(void) {
        if (NRF_IPC_S->EVENTS_RECEIVE[0]) {
            NRF_IPC_S->EVENTS_RECEIVE[0] = 0;
    
            printf("Message: ");
            printf("%u", shared_value);
            printf(" \n");
        
        }
    }
    
    

    spu

    #define SPU_RAM_REGION_INDEX 31
    
    void spu_configure(void) {
    // In secure boot eller startup på application core
    // Configure RAM region 31 (0x2003F000) as non-secure
    NRF_SPU_S->RAMREGION[SPU_RAM_REGION_INDEX].PERM =
       (SPU_RAMREGION_PERM_READ_Msk |
        SPU_RAMREGION_PERM_WRITE_Msk |
        SPU_RAMREGION_PERM_SECATTR_Non_Secure << SPU_RAMREGION_PERM_SECATTR_Msk |
        SPU_RAMREGION_PERM_EXECUTE_Msk);    // SECATTR bit is 0 => non-secure
    
    }


    I have added a reference to the right memory area in the linker script for both network and application cores as

    place at address 0x2003F000 { section .shared_memory };
    
    //but for the network core I have also added
    do not initialize                           { section .shared_memory };
    
    // ...since the app core must unlock the area before the network core can write

    Here is the network core 

    main()

     ipc_tx_configure();
    
     while(1) {
      delay_one_second();
      ipc_tx_transmit();
      }

    ipc_tx

    #define IPC_CHANNEL 0
    
    volatile uint32_t __attribute__((section(".shared_memory"))) shared_value;
    
    void ipc_tx_configure(void){
    
    // Set up the IPC channel 0 to be used for trigger a signal to application core.
    // We will only send on channel 0 (so we set bit 0 here)
        NRF_IPC_NS->SEND_CNF[IPC_CHANNEL] = 1;
    
    }
    
    
    void ipc_tx_transmit(void) {
    
        //Use the shared memory to send a message to the application core
        shared_value = 23;
    
        // Trigger IPC event on channel 0
        NRF_IPC_NS->TASKS_SEND[IPC_CHANNEL] = 1;
    
    
    }

Children
No Data
Related