Porting GuideLine
The OpenAMP Framework uses libmetal to provide abstractions that allows for porting of the OpenAMP Framework to various software environments (operating systems and bare metal environments) and machines (processors/platforms). To port OpenAMP for your platform, you will need to:
add your system environment support to libmetal,
optionally implement a platform specific remoteproc driver.
define your shared memory layout and specify it in a resource table.
Add System/Machine Support in Libmetal
User will need to add system/machine support to lib/system/<SYS>/ directory in libmetal repository. OpenAMP requires the following libmetal primitives:
alloc
Memory allocation and memory free as defined in alloc.h, which call the functions of equivalent names with double underscore which are the ported functions (__metal_allocate_memory, __metal_free_memory).
-
static inline void *metal_allocate_memory(unsigned int size)
allocate requested memory size return a pointer to the allocated memory
- Parameters:
size – [in] size in byte of requested memory
- Returns:
memory pointer, or 0 if it failed to allocate
-
static inline void metal_free_memory(void *ptr)
free the memory previously allocated
- Parameters:
ptr – [in] pointer to memory
io
Memory mapping as used by io.h, in order to access vrings and carved out memory.
metal_sys_io_mem_map and metal_machine_io_mem_map functions.
mutex
Mutex functions as used by mutex.h which call the functions of equivalent names with double underscore which are the ported functions (e.g. __metal_mutex_init).
-
static inline void metal_mutex_init(metal_mutex_t *mutex)
Initialize a libmetal mutex.
- Parameters:
mutex – [in] Mutex to initialize.
-
static inline void metal_mutex_deinit(metal_mutex_t *mutex)
Deinitialize a libmetal mutex.
- Parameters:
mutex – [in] Mutex to deinitialize.
-
static inline int metal_mutex_try_acquire(metal_mutex_t *mutex)
Try to acquire a mutex.
- Parameters:
mutex – [in] Mutex to mutex.
- Returns:
0 on failure to acquire, non-zero on success.
-
static inline void metal_mutex_acquire(metal_mutex_t *mutex)
Acquire a mutex.
- Parameters:
mutex – [in] Mutex to mutex.
-
static inline void metal_mutex_release(metal_mutex_t *mutex)
Release a previously acquired mutex.
See also
- Parameters:
mutex – [in] Mutex to mutex.
-
static inline int metal_mutex_is_acquired(metal_mutex_t *mutex)
Checked if a mutex has been acquired.
See also
- Parameters:
mutex – [in] mutex to check.
sleep
At the moment, OpenAMP only requires microseconds sleep as when OpenAMP fails to get a buffer to send messages, it will call this function to sleep and then try again.
The __metal_sleep_usec to be implemented by the port is wrapped in sleep.h.
-
static inline int metal_sleep_usec(unsigned int usec)
delay in microseconds delay the next execution in the calling thread fo usec microseconds.
- Parameters:
usec – [in] microsecond intervals
- Returns:
0 on success, non-zero for failures
init
Init is ported for libmetal initialization for sys.h.
metal_sys_init and metal_sys_finish functions.
Please refer to lib/system/generic/ when adding RTOS support to libmetal.
libmetal uses C11/C++11 stdatomics interface for atomic operations, if you use a different compiler to GNU gcc, you may need to implement the atomic operations defined in lib/compiler/gcc/atomic.h.
Platform Specific Remoteproc Driver
An OpenAMP port could need a platform specific remoteproc driver to use remoteproc life cycle management (LCM) APIs. The remoteproc driver platform specific functions are defined in lib/include/openamp/remoteproc.h and provided through the remoteproc_ops data structure.
The remoteproc LCM APIs use these platform specific implementation of init, remove, mmap, handle_rsc, config, start, stop, shutdown and notify. These functions are passed to remoteproc via the remoteproc_ops structure which contains function pointers to each.
-
struct remoteproc_ops
Remoteproc operations to manage a remoteproc instance.
Remoteproc operations need to be implemented by each remoteproc driver
Public Members
-
struct remoteproc *(*init)(struct remoteproc *rproc, const struct remoteproc_ops *ops, void *arg)
Initialize the remoteproc instance.
-
void (*remove)(struct remoteproc *rproc)
Remove the remoteproc instance.
-
void *(*mmap)(struct remoteproc *rproc, metal_phys_addr_t *pa, metal_phys_addr_t *da, size_t size, unsigned int attribute, struct metal_io_region **io)
Memory map the memory with physical address or destination address as input.
-
int (*handle_rsc)(struct remoteproc *rproc, void *rsc, size_t len)
Handle the vendor specific resource.
-
int (*config)(struct remoteproc *rproc, void *data)
Configure the remoteproc to make it ready to load and run the executable.
-
int (*start)(struct remoteproc *rproc)
Kick the remoteproc to run the application.
-
int (*stop)(struct remoteproc *rproc)
Stop the remoteproc from running the application, the resource such as memory may not be off.
-
int (*shutdown)(struct remoteproc *rproc)
Shutdown the remoteproc and release its resources.
-
int (*notify)(struct remoteproc *rproc, uint32_t id)
Notify the remote.
-
struct remoteproc_mem *(*get_mem)(struct remoteproc *rproc, const char *name, metal_phys_addr_t pa, metal_phys_addr_t da, void *va, size_t size, struct remoteproc_mem *buf)
Get remoteproc memory I/O region by either name, virtual address, physical address or device address.
- Param rproc:
Pointer to remoteproc instance
- Param name:
Memory name
- Param pa:
Physical address
- Param da:
Device address
- Param va:
Virtual address
- Param size:
Memory size
- Param buf:
Pointer to remoteproc_mem struct object to store result
- Return:
remoteproc memory pointed by buf if success, otherwise NULL
-
struct remoteproc *(*init)(struct remoteproc *rproc, const struct remoteproc_ops *ops, void *arg)
The remoteproc_init API receives this structure, and its function pointers, which are then used by the other APIs.
Platform Specific Porting to Use Remoteproc to Manage Remote Processor
With the platform specific remoteproc driver functions implemented by the port, the user can use remoteproc APIs to run application on a remote processor.
-
struct remoteproc *remoteproc_init(struct remoteproc *rproc, const struct remoteproc_ops *ops, void *priv)
Initializes remoteproc resource.
- Parameters:
rproc – Pointer to remoteproc instance
ops – Pointer to remoteproc operations
priv – Pointer to private data
- Returns:
Created remoteproc pointer
-
int remoteproc_remove(struct remoteproc *rproc)
Remove remoteproc resource.
- Parameters:
rproc – Pointer to remoteproc instance
- Returns:
0 for success, negative value for failure
-
void *remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, metal_phys_addr_t *da, size_t size, unsigned int attribute, struct metal_io_region **io)
Remoteproc mmap memory.
- Parameters:
rproc – Pointer to the remote processor
pa – Physical address pointer
da – Device address pointer
size – Size of the memory
attribute – Memory attribute
io – Pointer to the I/O region
- Returns:
Pointer to the memory
-
int remoteproc_config(struct remoteproc *rproc, void *data)
This function configures the remote processor to get it ready to load and run executable.
- Parameters:
rproc – Pointer to remoteproc instance to start
data – Configuration data
- Returns:
0 for success and negative value for errors
-
int remoteproc_start(struct remoteproc *rproc)
This function starts the remote processor.
It assumes the firmware is already loaded.
- Parameters:
rproc – Pointer to remoteproc instance to start
- Returns:
0 for success and negative value for errors
-
int remoteproc_stop(struct remoteproc *rproc)
This function stops the remote processor but it will not release its resource.
- Parameters:
rproc – Pointer to remoteproc instance
- Returns:
0 for success and negative value for errors
-
int remoteproc_shutdown(struct remoteproc *rproc)
This function shuts down the remote processor and releases its resources.
- Parameters:
rproc – Pointer to remoteproc instance
- Returns:
0 for success and negative value for errors
The following code snippet is an example execution.
#include <openamp/remoteproc.h>
/* User defined remoteproc operations */
extern struct remoteproc_ops rproc_ops;
/* User defined image store operations, such as open the image file, read
* image from storage, and close the image file.
*/
extern struct image_store_ops img_store_ops;
/* Pointer to keep the image store information. It will be passed to user
* defined image store operations by the remoteproc loading application
* function. Its structure is defined by user.
*/
void *img_store_info;
struct remoteproc rproc;
void main(void)
{
/* Instantiate the remoteproc instance */
remoteproc_init(&rproc, &rproc_ops, &private_data);
/* Optional, required, if user needs to configure the remote before
* loading applications.
*/
remoteproc_config(&rproc, &platform_config);
/* Load Application. It only supports ELF for now. */
remoteproc_load(&rproc, img_path, img_store_info, &img_store_ops, NULL);
/* Start the processor to run the application. */
remoteproc_start(&rproc);
/* ... */
/* Optional. Stop the processor, but the processor is not powered
* down.
*/
remoteproc_stop(&rproc);
/* Shutdown the processor. The processor is supposed to be powered
* down.
*/
remoteproc_shutdown(&rproc);
/* Destroy the remoteproc instance */
remoteproc_remove(&rproc);
}
Platform Specific Porting to Use RPMsg
RPMsg in OpenAMP implementation uses VirtIO to manage the shared buffers. OpenAMP library provides remoteproc VirtIO backend implementation. You don’t have to use remoteproc backend. You can implement your VirtIO backend with the VirtIO and RPMsg implementation in OpenAMP. If you want to implement your own VirtIO backend, you can refer to the remoteproc VirtIO backend implementation.
Here are the steps to use OpenAMP for RPMsg communication:
#include <openamp/remoteproc.h>
#include <openamp/rpmsg.h>
#include <openamp/rpmsg_virtio.h>
/* User defined remoteproc operations for communication */
sturct remoteproc rproc_ops = {
.init = local_rproc_init;
.mmap = local_rproc_mmap;
.notify = local_rproc_notify;
.remove = local_rproc_remove;
};
/* Remoteproc instance. If you don't use Remoteproc VirtIO backend,
* you don't need to define the remoteproc instance.
*/
struct remoteproc rproc;
/* RPMsg VirtIO device instance. */
struct rpmsg_virtio_device rpmsg_vdev;
/* RPMsg device */
struct rpmsg_device *rpmsg_dev;
/* Resource Table. Resource table is used by remoteproc to describe
* the shared resources such as vdev(VirtIO device) and other shared memory.
* Resource table resources definition is in the remoteproc.h.
* Examples of the resource table can be found in the OpenAMP repo:
* - apps/machine/zynqmp/rsc_table.c
* - apps/machine/zynqmp_r5/rsc_table.c
* - apps/machine/zynq7/rsc_table.c
*/
void *rsc_table = &resource_table;
/* Size of the resource table */
int rsc_size = sizeof(resource_table);
/* Shared memory metal I/O region. It will be used by OpenAMP library
* to access the memory. You can have more than one shared memory regions
* in your application.
*/
struct metal_io_region *shm_io;
/* VirtIO device */
struct virtio_device *vdev;
/* RPMsg shared buffers pool */
struct rpmsg_virtio_shm_pool shpool;
/* Shared buffers */
void *shbuf;
/* RPMsg endpoint */
struct rpmsg_endpoint ept;
/* User defined RPMsg name service callback. This callback is called
* when there is no registered RPMsg endpoint is found for this name
* service. User can create RPMsg endpoint in this callback. */
void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest);
/* User defined RPMsg endpoint received message callback */
void rpmsg_ept_cb(struct rpmsg_endpoint *ept, void *data, size_t len,
uint32_t src, void *priv);
/* User defined RPMsg name service unbind request callback */
void ns_unbind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest);
void main(void)
{
/* Instantiate remoteproc instance */
remoteproc_init(&rproc, &rproc_ops);
/* Mmap shared memories so that they can be used */
remoteproc_mmap(&rproc, &physical_address, NULL, size,
<memory_attributes>, &shm_io);
/* Parse resource table to remoteproc */
remoteproc_set_rsc_table(&rproc, rsc_table, rsc_size);
/* Create VirtIO device from remoteproc.
* VirtIO device main controller will initiate the VirtIO rings, and assign
* shared buffers. If you running the application as VirtIO device, you
* set the role as VIRTIO_DEV_DEVICE.
* If you don't use remoteproc, you will need to define your own VirtIO
* device.
*/
vdev = remoteproc_create_virtio(&rproc, 0, VIRTIO_DEV_DRIVER, NULL);
/* This step is only required if you are VirtIO device main controller.
* Initialize the shared buffers pool.
*/
shbuf = metal_io_phys_to_virt(shm_io, SHARED_BUF_PA);
rpmsg_virtio_init_shm_pool(&shpool, shbuf, SHARED_BUFF_SIZE);
/* Initialize RPMsg VirtIO device with the VirtIO device */
/* If it is VirtIO device, it will not return until the main
* controller side sets the VirtIO device DRIVER OK status bit.
*/
rpmsg_init_vdev(&rpmsg_vdev, vdev, ns_bind_cb, io, shm_io, &shpool);
/* Get RPMsg device from RPMsg VirtIO device */
rpmsg_dev = rpmsg_virtio_get_rpmsg_device(&rpmsg_vdev);
/* Create RPMsg endpoint. */
rpmsg_create_ept(&ept, rdev, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY,
rpmsg_ept_cb, ns_unbind_cb);
/* If it is VirtIO device main controller, it sends the first message */
while (!is_rpmsg_ept_read(&ept)) {
/* check if the endpoint has binded.
* If not, wait for notification. If local endpoint hasn't
* been bound with the remote endpoint, it will fail to
* send the message to the remote.
*/
/* If you prefer to use interrupt, you can wait for
* interrupt here, and call the VirtIO notified function
* in the interrupt handling task.
*/
rproc_virtio_notified(vdev, RSC_NOTIFY_ID_ANY);
}
/* Send RPMsg */
rpmsg_send(&ept, data, size);
do {
/* If you prefer to use interrupt, you can wait for
* interrupt here, and call the VirtIO notified function
* in the interrupt handling task.
* If vdev is notified, the endpoint callback will be
* called.
*/
rproc_virtio_notified(vdev, RSC_NOTIFY_ID_ANY);
} while(!ns_unbind_cb_is_called && !user_decided_to_end_communication);
/* End of communication, destroy the endpoint */
rpmsg_destroy_ept(&ept);
rpmsg_deinit_vdev(&rpmsg_vdev);
remoteproc_remove_virtio(&rproc, vdev);
remoteproc_remove(&rproc);
}
.