Skip to content

Instantly share code, notes, and snippets.

@nmenon
Last active March 25, 2023 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nmenon/245a5b0ab0c0fbd65b8f3d58b76fe236 to your computer and use it in GitHub Desktop.
Save nmenon/245a5b0ab0c0fbd65b8f3d58b76fe236 to your computer and use it in GitHub Desktop.

Chapter1 recap:

For previous chapter 1: Video notes

Chapter 2:

Introduction:

In this chapter, we shall use linux remoteproc to load and run the test binary we created instead of connecting with jtag and running the binary with openOCD as we did in Chapter 1.

Watch the remoteproc in action

Git repo for the project: https://github.com/nmenon/rust-cortex-m-quickstart-beagleplay.git

Modifications in this chapter: memory.x for Sections, src/main.rst to add in resource_table.

memory.x modification:

The key section of the resource table is ensuring that the resource table section is allocated to the correct memory and we can actually put the resource table where it belongs.

Understanding what goes where.

So, lets first start with Linux device tree. Code to first look at is: memory map's reserved sections and memory map assignment

&mcu_m4fss {
	mboxes = <&mailbox0_cluster0 &mbox_m4_0>;
	memory-region = <&mcu_m4fss_dma_memory_region>,
			<&mcu_m4fss_memory_region>;
};

Notice the two memory ranges:

  • mcu_m4fss_dma_memory_region
  • mcu_m4fss_memory_region

These map to:

		mcu_m4fss_dma_memory_region: m4f-dma-memory@9cb00000 {
			compatible = "shared-dma-pool";
			reg = <0x00 0x9cb00000 0x00 0x100000>;
			no-map;
		};

		mcu_m4fss_memory_region: m4f-memory@9cc00000 {
			compatible = "shared-dma-pool";
			reg = <0x00 0x9cc00000 0x00 0xe00000>;
			no-map;
		};

The "no-map" property and "shared-dma-pool" properties have meanings in Linux, for our purposes, it just tells linux, dont use this part of the DDR for doing Linuxy thingies.

Now, the address ranges and sizes are of our interest. We have already mapped this in our memory.x file in our chapter 1 as:

  DDR_MEM_REG : ORIGIN = 0x9cb00000, LENGTH = 1M
  DDR_DMA_MEM_REG : ORIGIN = 0x9cc00000, LENGTH = 13M

Though the yaml file is not much use in finding the details of the contents of those memory sections, we can look at the driver code to find that the resource map goes to DDR_MEM_REG -> mcu_m4fss_memory_region

Creating linker modifications to ensure we have correct section maps

I will skip the pains of multiple attempts on trying this.. So, mapping this correctly needs us to create additional sections, which we do by modifying the memory.x by adding:

SECTIONS {
     .resource_table : ALIGN(4) {
         KEEP(*(.resource_table*));
     } > DDR_MEM_REG

     .log_shared_mem (NOLOAD) : ALIGN(4) {
         KEEP(*(.log_shared_mem*));
     } > DDR_DMA_MEM_REG
} INSERT AFTER .bss;

Note

  • ALIGN(4) -> This is superfluous because our memory is already aligned, but lets stay aligned anyways
  • KEEP -> This is super critical, without this, we end up with data structures getting optimized out by the compiler since they are'nt meant to be refered by the M4F code, rather they are meant for Linux to figure stuff out.
  • NOLOAD in log_shared_mem -> meh.. dont care to load the trace logs.
  • INSERT AFTER .bss -> This just helps organize stuff the right way.. they are already seperate sections, so bss is'nt going into the DDR anyways (that would'nt make sense for m4f).

main.rs modification for resource table

See source to better make sense of what we will be describing below.

Now that we have created a linker section to put the resource table in, lets fill it up with the actual data.

What is this crap anyways? why do we need resource_table?

Blame it all on OpenAMP's resource_table. I could decode the almost documented data structures, but the idea is simple: we need a data structure to allow linux or any OS's system to understand the nuances and capabilities of the firmware. it is that simple - this is essentially equivalent for device tree for remoteproc firmware in openAMP lingo.

Instead of inventing the wheel all over again, I decided to steal kaofish's R5 baremetal implementation of C from here and use that as my reference.

Getting additional crates

  • First thing I needed was C's offsetof equivalent in rust. Ofcourse there is a crate for that..
use memoffset::offset_of;

Describing the structures.

most of this stuff is transposition.

#[repr(C, packed)]

In embedded space we like our structures that are exchanged between other binaries to be ABI compatible, independent of compiler nuances and platform eccentricities, So, duplicate the "packed" attributes of C..

You can appreciate the similarities when i put the two structures back to back to look at.

#[repr(C, packed)]
pub struct RpmessageRscHdr {
    // Version Number, set to 1
    ver: u32,
    // Number of Entries, MUST be 2
    num: u32,
    // Reserved for future use, set to 0
    reserved: [u32; 2],
}
typedef struct
{
    uint32_t ver;
    /**< Version Number, set to 1 */
    uint32_t num;
    /**< Number of Entries, MUST be 2 */
    uint32_t reserved[2];
    /**< Reserved for future use, set to 0 */
} RPMessage_RscHdr;

Some Side comments from C-timer converting though process to Rust-ism..

  • Love the u32 and simplified linuxy types..
  • variable: type -> hmm... it reverses my thinking process when I switch from C (type variable) to rust (variable: type)
  • [u32; 2] -> Ok fine, token parsing is easier.. but ughh.. size is a parameter of the data.. I need to tie it to type.. meh, but anwyays..

Instantiation of the data.

Now, this is a handful! rust compiler likes to mangle mangle names..

#[link_section = ".resource_table"]
#[no_mangle]
#[used]
#[export_name = ".resource_table.val"]
pub static G_RPMESSAGE_LINUX_RESOURCE_TABLE: RpmessageResourceTable = RpmessageResourceTable {

Decoding these..

  • link_section says where we want to send it - this is probably superfluous since the linker file section describes names that need to be assigned to that section
  • no_mangle - uggh, I had hated that in every single language I have seen it in.
  • used - please compiler - I do plan to keep this, dont dump it.
  • export_name - now the naming convention in rust (it is a bit anal about complaning about name styles it does'nt like), but I want the linker file clean, so rename it as something else to matchup with the section rules we wrote a bit back.

For the C people, you already recognize a bit of attributes here for gcc to chew on..

Next: Actual static initialization

 base: RpmessageRscHdr {
        ver: 1,
        num: 2,
        reserved: [0, 0],
    },
    offset: [
        offset_of!(RpmessageResourceTable, vdev) as u32,
        offset_of!(RpmessageResourceTable, trace) as u32,
    ],

Vs:

    {
        1U,         /* we're the first version that implements this */
        2U,         /* number of entries, MUST be 2 */
        { 0U, 0U, } /* reserved, must be zero */
    },
    /* offsets to the entries */
    {
        offsetof(RPMessage_ResourceTable, vdev),
        offsetof(RPMessage_ResourceTable, trace),
    },

What I like: The C folks who like to type less will already hate the language.. but really, this is one thing I wish C enforced.. it is more verbose and completely makes sense.. structure changes will force you to rethink your initialziation values.. and default 0, IMHO should have been banned - so many bloddy bugs have come into the code due to that!

What I dont like:

  • offsetof like base operations should'nt be some random crate! it should have been part of "some fundamental library" or sort that I can trust and use! See Alex's rant which I completely agree with!
  • I have'nt gotten my head around rust macros and the "correct" way to describe macros-like operations.. should they be consts in modules and the like.. So, it is'nt really a straight - "hey, just copy paste, change the syntax and you can get going" kind of thing. you need to think that through..

And now to the flip side:

pub static log_message_buffer: [u8; 3] = [b'O', b'K', 0];
  • This is one place where I wished I could have a data structure that i can initialize with 0s ;)... now I dont like that default initialization concept, but I need it instead of typing 4k of 0s around.. i am a noob in rust now, so, maybe i have'nt figured it out?

Compiling

cargo build

Loading and running with remoteproc:

sudo cp ./target/thumbv7em-none-eabihf/debug/m4fbaremetal /lib/firmware/

echo stop | sudo tee /sys/class/remoteproc/remoteproc2/state

echo m4fbaremetal | sudo tee /sys/class/remoteproc/remoteproc2/firmware

echo start | sudo tee /sys/class/remoteproc/remoteproc2/state

and the log loos like this:

start
[ 1368.919351] remoteproc remoteproc2: powering up 5000000.m4fss
[ 1368.927985] remoteproc remoteproc2: Booting fw image test.elf, size 861600
[ 1368.935738]  remoteproc2#vdev0buffer: assigned reserved memory node m4f-dma-memory@9cb00000
[ 1368.945186] virtio_rpmsg_bus virtio0: rpmsg host is online
[ 1368.950886]  remoteproc2#vdev0buffer: registered virtio0 (type 7)
[ 1368.957100] remoteproc remoteproc2: remote processor 5000000.m4fss is now up

So, voila.. mission accomplished!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment