nRF5340 - Accessing Shared Memory

Hello. I am just getting started with nRF5340 development using the u-Blox NORA-B106 dev kit. I am trying to use the peripheral_uart code example to understand how the two processors use the shared memory space to communicate. I am able to send data over Bluetooth and have it appear in a terminal connected to the J-link UART, so the app is working. But tracing through the code, I'm unable to find where the received data is being put in shared memory so the UART function call can see it.

1) Can someone point me to the specific code within this example where this happens?

2) In general, how do you assign a variable to reside in this space so that both processors can access it? I'm assuming this is done through linker files for both net and app builds so that the same variable is mapped to the same memory location, but I can't find those files either.

Coming from STM32 and FreeRTOS, so the Zephyr learning curve is feeling steep so far. Any help is appreciated.

Thanks,

Jamie

Parents
  • Hello again,

    Coming from STM32 and FreeRTOS, so the Zephyr learning curve is feeling steep so far. Any help is appreciated.

    That is very understandable. To start off, I would recommend you to take a look at our DevAcademy, which will give you a basic understanding of Zephyr.

    You are right in that the inter-core communication is happening using shared memory. This is unfortunately a complex topic, and difficult to give basic example of. The documentation on the nRF5340 and the documentation on working with it describes a little bit about it. And this previous case of mine describes a bit more. Taking a look at the examples I linked to there will probably be beneficial, and answer your second question.

    When it comes to how this is done in a project like the peripheral_uart sample, I would have to get back to you.

    Regards,

    Elfving

  • Thank you. the DevAcademy link looks like a very good starting point. I will work my way through that.

  • Hello. I am revisiting this topic lately and still need some clarification. I am looking at the device tree includes and can see that both the peripheral_uart project and the hci_rpmsg project eventually include "nrf5340_shared_sram_planning_conf.dts" which gives the following:

    chosen {
       /* shared memory reserved for the inter-processor communication */
       zephyr,ipc_shm = &sram0_shared;
    };

    reserved-memory {
       #address-cells = <1>;
       #size-cells = <1>;
       ranges;

       sram0_shared: memory@20070000 {
          /* SRAM allocated to shared memory */
          reg = <0x20070000 0x10000>;
       };
    };

    So now we have established a shared memory region, okay, so far so good. Next, I see in each device tree, somewhere the following eventually gets included:

    ipc0: ipc0 {
       compatible = "zephyr,ipc-openamp-static-vrings"; 
       memory-region = <&sram0_shared>;
       mboxes = <&mbox 0>, <&mbox 1>;
       mbox-names = "tx", "rx";
       role = "host";
       status = "okay";
    };

    So now we have the ipc module attached to the shared memory region. However, here is where I get lost. How do each of the programs use this memory region? Normally I would expect to see something like __attribute__ used in front of a variable to denote that it should be placed in that region by the linker (also, where are the linker files?).

    In the hci_rpmsg main.c, I can at least see some references to this, such as:

    const struct device *hci_ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
    and:
    const struct device *hci_ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
    But how does this link back to the sram0_shared region? Also, how are the mailboxes used in this context? How large are the mailboxes (i.e. where is the mailbox size determined).
    Now, on the other end, the main.c from the peripheral_uart project, I don't see any references to the ipc system at all. This leads me to believe that the bt_nus is somehow uses this information behind the scenes, but I'm so far unable to follow the function calls back through to anything that makes this explicit. How is peripheral_uart using the ipc system and the shared memory?
    -Jamie
Reply
  • Hello. I am revisiting this topic lately and still need some clarification. I am looking at the device tree includes and can see that both the peripheral_uart project and the hci_rpmsg project eventually include "nrf5340_shared_sram_planning_conf.dts" which gives the following:

    chosen {
       /* shared memory reserved for the inter-processor communication */
       zephyr,ipc_shm = &sram0_shared;
    };

    reserved-memory {
       #address-cells = <1>;
       #size-cells = <1>;
       ranges;

       sram0_shared: memory@20070000 {
          /* SRAM allocated to shared memory */
          reg = <0x20070000 0x10000>;
       };
    };

    So now we have established a shared memory region, okay, so far so good. Next, I see in each device tree, somewhere the following eventually gets included:

    ipc0: ipc0 {
       compatible = "zephyr,ipc-openamp-static-vrings"; 
       memory-region = <&sram0_shared>;
       mboxes = <&mbox 0>, <&mbox 1>;
       mbox-names = "tx", "rx";
       role = "host";
       status = "okay";
    };

    So now we have the ipc module attached to the shared memory region. However, here is where I get lost. How do each of the programs use this memory region? Normally I would expect to see something like __attribute__ used in front of a variable to denote that it should be placed in that region by the linker (also, where are the linker files?).

    In the hci_rpmsg main.c, I can at least see some references to this, such as:

    const struct device *hci_ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
    and:
    const struct device *hci_ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));
    But how does this link back to the sram0_shared region? Also, how are the mailboxes used in this context? How large are the mailboxes (i.e. where is the mailbox size determined).
    Now, on the other end, the main.c from the peripheral_uart project, I don't see any references to the ipc system at all. This leads me to believe that the bt_nus is somehow uses this information behind the scenes, but I'm so far unable to follow the function calls back through to anything that makes this explicit. How is peripheral_uart using the ipc system and the shared memory?
    -Jamie
Children
  • Another notable thing - looking at both zephyr.map files, neither file seems to have any data structures living in the shared memory range. Shouldn't I see some overlapping variables at 0x20070000 vicinity?

  • Hello and sorry about the delay. I must've overlooked this case for a while.

    jmilliken said:
    So now we have established a shared memory region, okay, so far so good.

    Yes, this is where it is defined.

    The documentation on what is mentioned in the snippets you have from device tree there could arguably be improved, but there is a lot available. Here is a bit on the "ipc-openamp-static-vrings" devicetree binding, and there is apparently also a sample on it. And here is a bit on the mboxes.

    jmilliken said:
    also, where are the linker files?

    Build/zephyr/

    jmilliken said:
    How do each of the programs use this memory region?

    Well that depends. IPC might be the most common, though here is also an example of something a bit more basic: this module is used to write a single struct to a RAM area which is known by both cores. The appcore writes here, and the netcore reads here.

    In the case of IPC, the PS probably describes this best (Section 7.16). 

    The IPC indicates to the other core that there is new data available to pick up. The actual data exchange is handled by Open Asymmetric Multi-Processing (OpenAMP). Zephyr includes the OpenAMP library, which provides a complete solution for exchanging messages between the cores. The IPC peripheral is presented to Zephyr as an Interprocessor Mailbox (IPM) device. The OpenAMP library uses the IPM SHIM layer, which in turn uses the IPC driver in nrfx.

    jmilliken said:
    const struct device *hci_ipc_instance = DEVICE_DT_GET(DT_NODELABEL(ipc0));

    That creates a pointer to the device driver. There is more on that here.

    jmilliken said:
    How is peripheral_uart using the ipc system and the shared memory?

    I believe this happens in the dts file nrf5340_cpuapp_common.dts:

    REgards,

    Elfving

Related