sample: openamp: Add OpenAMP performance test.
Add a simple sample that tries to answer the silly question: how fast can I move data from one core to another using OpenAMP? It depends on a lot of factors but at least we can use this sample as a skeleton to build tests on top. Signed-off-by: Carlo Caione <ccaione@baylibre.com>
This commit is contained in:
parent
ca96286c22
commit
993cb4cd62
12 changed files with 785 additions and 0 deletions
39
samples/subsys/ipc/openamp_performance/CMakeLists.txt
Normal file
39
samples/subsys/ipc/openamp_performance/CMakeLists.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
set(REMOTE_ZEPHYR_DIR ${CMAKE_CURRENT_BINARY_DIR}/openamp_remote-prefix/src/openamp_remote-build/zephyr)
|
||||
|
||||
if("${BOARD}" STREQUAL "nrf5340dk_nrf5340_cpuapp")
|
||||
set(BOARD_REMOTE "nrf5340dk_nrf5340_cpunet")
|
||||
else()
|
||||
message(FATAL_ERROR "${BOARD} was not supported for this sample")
|
||||
endif()
|
||||
|
||||
message(INFO " ${BOARD} compile as Master in this sample")
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(openamp_performance)
|
||||
|
||||
enable_language(C ASM)
|
||||
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(
|
||||
openamp_performance_remote
|
||||
SOURCE_DIR ${APPLICATION_SOURCE_DIR}/remote
|
||||
INSTALL_COMMAND "" # This particular build system has no install command
|
||||
CMAKE_CACHE_ARGS -DBOARD:STRING=${BOARD_REMOTE}
|
||||
CMAKE_CACHE_ARGS -DDTC_OVERLAY_FILE:STRING=${DTC_OVERLAY_FILE}
|
||||
BUILD_BYPRODUCTS "${REMOTE_ZEPHYR_DIR}/${KERNEL_BIN_NAME}"
|
||||
# NB: Do we need to pass on more CMake variables?
|
||||
BUILD_ALWAYS True
|
||||
)
|
||||
|
||||
target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
41
samples/subsys/ipc/openamp_performance/Kconfig
Normal file
41
samples/subsys/ipc/openamp_performance/Kconfig
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Private config options for openamp sample app
|
||||
|
||||
# Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Workaround for not being able to have commas in macro arguments
|
||||
DT_CHOSEN_Z_IPC_SHM := zephyr,ipc_shm
|
||||
DT_CHOSEN_Z_IPC_TX := zephyr,ipc_tx
|
||||
DT_CHOSEN_Z_IPC_RX := zephyr,ipc_rx
|
||||
|
||||
config OPENAMP_IPC_SHM_BASE_ADDRESS
|
||||
hex
|
||||
default "$(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_IPC_SHM))"
|
||||
help
|
||||
This option specifies base address of the memory region to
|
||||
be used for the OpenAMP IPC shared memory
|
||||
|
||||
config OPENAMP_IPC_SHM_SIZE
|
||||
hex
|
||||
default "$(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_IPC_SHM))"
|
||||
help
|
||||
This option specifies size of the memory region to be used
|
||||
for the OpenAMP IPC shared memory
|
||||
|
||||
config OPENAMP_IPC_DEV_TX_NAME
|
||||
string
|
||||
default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC_TX))" if !IPM_NRFX
|
||||
default "IPM_1" if (IPM_NRFX && OPENAMP_MASTER)
|
||||
default "IPM_0" if (IPM_NRFX && OPENAMP_SLAVE)
|
||||
help
|
||||
This option specifies the device name for the IPC device to be used (TX)
|
||||
|
||||
config OPENAMP_IPC_DEV_RX_NAME
|
||||
string
|
||||
default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC_RX))" if !IPM_NRFX
|
||||
default "IPM_0" if (IPM_NRFX && OPENAMP_MASTER)
|
||||
default "IPM_1" if (IPM_NRFX && OPENAMP_SLAVE)
|
||||
help
|
||||
This option specifies the device name for the IPC device to be used (RX)
|
||||
|
||||
source "Kconfig.zephyr"
|
|
@ -0,0 +1,10 @@
|
|||
CONFIG_BOARD_ENABLE_CPUNET=y
|
||||
|
||||
CONFIG_IPM=y
|
||||
CONFIG_IPM_NRFX=y
|
||||
|
||||
CONFIG_IPM_MSG_CH_0_ENABLE=y
|
||||
CONFIG_IPM_MSG_CH_1_ENABLE=y
|
||||
|
||||
CONFIG_IPM_MSG_CH_0_RX=y
|
||||
CONFIG_IPM_MSG_CH_1_TX=y
|
32
samples/subsys/ipc/openamp_performance/common.h
Normal file
32
samples/subsys/ipc/openamp_performance/common.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef COMMON_H__
|
||||
#define COMMON_H__
|
||||
|
||||
#define VDEV_START_ADDR CONFIG_OPENAMP_IPC_SHM_BASE_ADDRESS
|
||||
#define VDEV_SIZE CONFIG_OPENAMP_IPC_SHM_SIZE
|
||||
|
||||
#define VDEV_STATUS_ADDR VDEV_START_ADDR
|
||||
#define VDEV_STATUS_SIZE 0x400
|
||||
|
||||
#define SHM_START_ADDR (VDEV_START_ADDR + VDEV_STATUS_SIZE)
|
||||
#define SHM_SIZE (VDEV_SIZE - VDEV_STATUS_SIZE)
|
||||
#define SHM_DEVICE_NAME "sramx.shm"
|
||||
|
||||
#define VRING_COUNT 2
|
||||
#define VRING_RX_ADDRESS (VDEV_START_ADDR + SHM_SIZE - VDEV_STATUS_SIZE)
|
||||
#define VRING_TX_ADDRESS (VDEV_START_ADDR + SHM_SIZE)
|
||||
#define VRING_ALIGNMENT 4
|
||||
#define VRING_SIZE 16
|
||||
|
||||
struct payload {
|
||||
unsigned long cnt;
|
||||
unsigned long size;
|
||||
unsigned char data[];
|
||||
};
|
||||
|
||||
#endif
|
9
samples/subsys/ipc/openamp_performance/prj.conf
Normal file
9
samples/subsys/ipc/openamp_performance/prj.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
CONFIG_PRINTK=y
|
||||
|
||||
CONFIG_IPM=y
|
||||
CONFIG_TIMESLICE_SIZE=1
|
||||
CONFIG_MAIN_STACK_SIZE=2048
|
||||
CONFIG_HEAP_MEM_POOL_SIZE=4096
|
||||
|
||||
CONFIG_OPENAMP=y
|
||||
CONFIG_OPENAMP_SLAVE=n
|
19
samples/subsys/ipc/openamp_performance/remote/CMakeLists.txt
Normal file
19
samples/subsys/ipc/openamp_performance/remote/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
if("${BOARD}" STREQUAL "nrf5340dk_nrf5340_cpunet")
|
||||
message(INFO " ${BOARD} compiles as remote in this sample")
|
||||
else()
|
||||
message(FATAL_ERROR "${BOARD} was not supported for this sample")
|
||||
endif()
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(openamp_performance_remote)
|
||||
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
41
samples/subsys/ipc/openamp_performance/remote/Kconfig
Normal file
41
samples/subsys/ipc/openamp_performance/remote/Kconfig
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Private config options for openamp sample app
|
||||
|
||||
# Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Workaround for not being able to have commas in macro arguments
|
||||
DT_CHOSEN_Z_IPC_SHM := zephyr,ipc_shm
|
||||
DT_CHOSEN_Z_IPC_TX := zephyr,ipc_tx
|
||||
DT_CHOSEN_Z_IPC_RX := zephyr,ipc_rx
|
||||
|
||||
config OPENAMP_IPC_SHM_BASE_ADDRESS
|
||||
hex
|
||||
default "$(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_IPC_SHM))"
|
||||
help
|
||||
This option specifies base address of the memory region to
|
||||
be used for the OpenAMP IPC shared memory
|
||||
|
||||
config OPENAMP_IPC_SHM_SIZE
|
||||
hex
|
||||
default "$(dt_chosen_reg_size_hex,$(DT_CHOSEN_Z_IPC_SHM))"
|
||||
help
|
||||
This option specifies size of the memory region to be used
|
||||
for the OpenAMP IPC shared memory
|
||||
|
||||
config OPENAMP_IPC_DEV_TX_NAME
|
||||
string
|
||||
default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC_TX))" if !IPM_NRFX
|
||||
default "IPM_1" if (IPM_NRFX && OPENAMP_MASTER)
|
||||
default "IPM_0" if (IPM_NRFX && OPENAMP_SLAVE)
|
||||
help
|
||||
This option specifies the device name for the IPC device to be used (TX)
|
||||
|
||||
config OPENAMP_IPC_DEV_RX_NAME
|
||||
string
|
||||
default "$(dt_chosen_label,$(DT_CHOSEN_Z_IPC_RX))" if !IPM_NRFX
|
||||
default "IPM_0" if (IPM_NRFX && OPENAMP_MASTER)
|
||||
default "IPM_1" if (IPM_NRFX && OPENAMP_SLAVE)
|
||||
help
|
||||
This option specifies the device name for the IPC device to be used (RX)
|
||||
|
||||
source "Kconfig.zephyr"
|
|
@ -0,0 +1,8 @@
|
|||
CONFIG_IPM=y
|
||||
CONFIG_IPM_NRFX=y
|
||||
|
||||
CONFIG_IPM_MSG_CH_0_ENABLE=y
|
||||
CONFIG_IPM_MSG_CH_1_ENABLE=y
|
||||
|
||||
CONFIG_IPM_MSG_CH_0_TX=y
|
||||
CONFIG_IPM_MSG_CH_1_RX=y
|
7
samples/subsys/ipc/openamp_performance/remote/prj.conf
Normal file
7
samples/subsys/ipc/openamp_performance/remote/prj.conf
Normal file
|
@ -0,0 +1,7 @@
|
|||
CONFIG_PRINTK=y
|
||||
|
||||
CONFIG_IPM=y
|
||||
CONFIG_HEAP_MEM_POOL_SIZE=4096
|
||||
|
||||
CONFIG_OPENAMP=y
|
||||
CONFIG_OPENAMP_MASTER=n
|
252
samples/subsys/ipc/openamp_performance/remote/src/main.c
Normal file
252
samples/subsys/ipc/openamp_performance/remote/src/main.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <drivers/ipm.h>
|
||||
#include <sys/printk.h>
|
||||
#include <device.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <metal/device.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define READ_BUFFER (1)
|
||||
|
||||
#define APP_TASK_STACK_SIZE (1024)
|
||||
|
||||
static const struct device *ipm_tx_handle;
|
||||
static const struct device *ipm_rx_handle;
|
||||
|
||||
static struct virtqueue *vq[2];
|
||||
|
||||
static volatile struct payload *received_data;
|
||||
|
||||
#if READ_BUFFER
|
||||
static volatile uint8_t *buffer;
|
||||
static int max_payload_size;
|
||||
#endif
|
||||
|
||||
static K_SEM_DEFINE(data_sem, 0, 1);
|
||||
|
||||
static struct rpmsg_endpoint ep;
|
||||
|
||||
static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR };
|
||||
static struct metal_device shm_device = {
|
||||
.name = SHM_DEVICE_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
{
|
||||
{
|
||||
.virt = (void *) SHM_START_ADDR,
|
||||
.physmap = shm_physmap,
|
||||
.size = SHM_SIZE,
|
||||
.page_shift = 0xffffffff,
|
||||
.page_mask = 0xffffffff,
|
||||
.mem_flags = 0,
|
||||
.ops = { NULL },
|
||||
},
|
||||
},
|
||||
.node = { NULL },
|
||||
.irq_num = 0,
|
||||
.irq_info = NULL
|
||||
};
|
||||
|
||||
static struct virtio_vring_info rvrings[2] = {
|
||||
[0] = {
|
||||
.info.align = VRING_ALIGNMENT,
|
||||
},
|
||||
[1] = {
|
||||
.info.align = VRING_ALIGNMENT,
|
||||
},
|
||||
};
|
||||
|
||||
static unsigned char virtio_get_status(struct virtio_device *vdev)
|
||||
{
|
||||
return sys_read8(VDEV_STATUS_ADDR);
|
||||
}
|
||||
|
||||
static uint32_t virtio_get_features(struct virtio_device *vdev)
|
||||
{
|
||||
return BIT(VIRTIO_RPMSG_F_NS);
|
||||
}
|
||||
|
||||
static void virtio_notify(struct virtqueue *vq)
|
||||
{
|
||||
ipm_send(ipm_tx_handle, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static struct virtio_dispatch dispatch = {
|
||||
.get_status = virtio_get_status,
|
||||
.get_features = virtio_get_features,
|
||||
.notify = virtio_notify,
|
||||
};
|
||||
|
||||
static void platform_ipm_callback(const struct device *dev, void *context,
|
||||
uint32_t id, volatile void *data)
|
||||
{
|
||||
k_sem_give(&data_sem);
|
||||
}
|
||||
|
||||
static int endpoint_cb(struct rpmsg_endpoint *ept, void *data,
|
||||
size_t len, uint32_t src, void *priv)
|
||||
{
|
||||
received_data = ((struct payload *) data);
|
||||
|
||||
/* We do some work here */
|
||||
|
||||
#if READ_BUFFER
|
||||
memcpy((void *) buffer, (void *) received_data->data, received_data->size);
|
||||
#endif
|
||||
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
|
||||
static void rpmsg_service_unbind(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
(void)ept;
|
||||
|
||||
rpmsg_destroy_ept(&ep);
|
||||
}
|
||||
|
||||
static void receive_message(void)
|
||||
{
|
||||
k_sem_take(&data_sem, K_FOREVER);
|
||||
|
||||
virtqueue_notification(vq[1]);
|
||||
}
|
||||
|
||||
static void receive_task(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
ARG_UNUSED(arg1);
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
|
||||
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
|
||||
struct rpmsg_virtio_device rvdev;
|
||||
struct metal_device *device;
|
||||
struct metal_io_region *io;
|
||||
struct rpmsg_device *rdev;
|
||||
struct virtio_device vdev;
|
||||
int status = 0;
|
||||
|
||||
printk("\r\nOpenAMP[remote] demo started\r\n");
|
||||
|
||||
status = metal_init(&metal_params);
|
||||
if (status != 0) {
|
||||
printk("metal_init: failed - error code %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = metal_register_generic_device(&shm_device);
|
||||
if (status != 0) {
|
||||
printk("Couldn't register shared memory device: %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = metal_device_open("generic", SHM_DEVICE_NAME, &device);
|
||||
if (status != 0) {
|
||||
printk("metal_device_open failed: %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (io == NULL) {
|
||||
printk("metal_device_io_region failed to get region\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup IPM */
|
||||
ipm_tx_handle = device_get_binding(CONFIG_OPENAMP_IPC_DEV_TX_NAME);
|
||||
if (ipm_tx_handle == NULL) {
|
||||
printk("device_get_binding failed to find device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ipm_rx_handle = device_get_binding(CONFIG_OPENAMP_IPC_DEV_RX_NAME);
|
||||
if (ipm_rx_handle == NULL) {
|
||||
printk("device_get_binding failed to find device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ipm_register_callback(ipm_rx_handle, platform_ipm_callback, NULL);
|
||||
|
||||
status = ipm_set_enabled(ipm_rx_handle, 1);
|
||||
if (status != 0) {
|
||||
printk("ipm_set_enabled failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup vdev */
|
||||
vq[0] = virtqueue_allocate(VRING_SIZE);
|
||||
if (vq[0] == NULL) {
|
||||
printk("virtqueue_allocate failed to alloc vq[0]\n");
|
||||
return;
|
||||
}
|
||||
vq[1] = virtqueue_allocate(VRING_SIZE);
|
||||
if (vq[1] == NULL) {
|
||||
printk("virtqueue_allocate failed to alloc vq[1]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vdev.role = RPMSG_REMOTE;
|
||||
vdev.vrings_num = VRING_COUNT;
|
||||
vdev.func = &dispatch;
|
||||
rvrings[0].io = io;
|
||||
rvrings[0].info.vaddr = (void *)VRING_TX_ADDRESS;
|
||||
rvrings[0].info.num_descs = VRING_SIZE;
|
||||
rvrings[0].info.align = VRING_ALIGNMENT;
|
||||
rvrings[0].vq = vq[0];
|
||||
|
||||
rvrings[1].io = io;
|
||||
rvrings[1].info.vaddr = (void *)VRING_RX_ADDRESS;
|
||||
rvrings[1].info.num_descs = VRING_SIZE;
|
||||
rvrings[1].info.align = VRING_ALIGNMENT;
|
||||
rvrings[1].vq = vq[1];
|
||||
|
||||
vdev.vrings_info = &rvrings[0];
|
||||
|
||||
/* setup rvdev */
|
||||
status = rpmsg_init_vdev(&rvdev, &vdev, NULL, io, NULL);
|
||||
if (status != 0) {
|
||||
printk("rpmsg_init_vdev failed %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
rdev = rpmsg_virtio_get_rpmsg_device(&rvdev);
|
||||
|
||||
status = rpmsg_create_ept(&ep, rdev, "k", RPMSG_ADDR_ANY,
|
||||
RPMSG_ADDR_ANY, endpoint_cb, rpmsg_service_unbind);
|
||||
if (status != 0) {
|
||||
printk("rpmsg_create_ept failed %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
#if READ_BUFFER
|
||||
max_payload_size = rpmsg_virtio_get_buffer_size(rdev);
|
||||
if (max_payload_size < 0) {
|
||||
printk("No available buffer size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer = (uint8_t *) metal_allocate_memory(max_payload_size - sizeof(struct payload));
|
||||
if (!buffer) {
|
||||
printk("memory allocation failed\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
receive_message();
|
||||
}
|
||||
|
||||
/* How did we get here? */
|
||||
}
|
||||
K_THREAD_DEFINE(thread_receive_id, APP_TASK_STACK_SIZE, receive_task, NULL, NULL, NULL,
|
||||
K_PRIO_PREEMPT(1), 0, 0);
|
11
samples/subsys/ipc/openamp_performance/sample.yaml
Normal file
11
samples/subsys/ipc/openamp_performance/sample.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
sample:
|
||||
description: This app provides an example of how to test OpenAMP
|
||||
performance with Zephyr.
|
||||
name: OpenAMP performance example integration
|
||||
tests:
|
||||
sample.subsys.ipc.openamp_performance:
|
||||
build_only: true
|
||||
platform_allow: nrf5340dk_nrf5340_cpuapp
|
||||
integration_platforms:
|
||||
- nrf5340dk_nrf5340_cpuapp
|
||||
tags: ipm
|
316
samples/subsys/ipc/openamp_performance/src/main.c
Normal file
316
samples/subsys/ipc/openamp_performance/src/main.c
Normal file
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <drivers/ipm.h>
|
||||
#include <sys/printk.h>
|
||||
#include <device.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <init.h>
|
||||
|
||||
#include <openamp/open_amp.h>
|
||||
#include <metal/device.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static struct payload *s_payload;
|
||||
|
||||
#define APP_TASK_STACK_SIZE (1024)
|
||||
|
||||
static const struct device *ipm_tx_handle;
|
||||
static const struct device *ipm_rx_handle;
|
||||
|
||||
static int max_payload_size;
|
||||
|
||||
static K_SEM_DEFINE(data_sem, 0, 1);
|
||||
static K_SEM_DEFINE(ept_sem, 0, 1);
|
||||
|
||||
static struct rpmsg_endpoint ep;
|
||||
|
||||
static metal_phys_addr_t shm_physmap[] = { SHM_START_ADDR };
|
||||
static struct metal_device shm_device = {
|
||||
.name = SHM_DEVICE_NAME,
|
||||
.bus = NULL,
|
||||
.num_regions = 1,
|
||||
{
|
||||
{
|
||||
.virt = (void *) SHM_START_ADDR,
|
||||
.physmap = shm_physmap,
|
||||
.size = SHM_SIZE,
|
||||
.page_shift = 0xffffffff,
|
||||
.page_mask = 0xffffffff,
|
||||
.mem_flags = 0,
|
||||
.ops = { NULL },
|
||||
},
|
||||
},
|
||||
.node = { NULL },
|
||||
.irq_num = 0,
|
||||
.irq_info = NULL
|
||||
};
|
||||
|
||||
|
||||
static struct virtio_vring_info rvrings[2] = {
|
||||
[0] = {
|
||||
.info.align = VRING_ALIGNMENT,
|
||||
},
|
||||
[1] = {
|
||||
.info.align = VRING_ALIGNMENT,
|
||||
},
|
||||
};
|
||||
|
||||
static unsigned char virtio_get_status(struct virtio_device *vdev)
|
||||
{
|
||||
return VIRTIO_CONFIG_STATUS_DRIVER_OK;
|
||||
}
|
||||
|
||||
static void virtio_set_status(struct virtio_device *vdev, unsigned char status)
|
||||
{
|
||||
sys_write8(status, VDEV_STATUS_ADDR);
|
||||
}
|
||||
|
||||
static uint32_t virtio_get_features(struct virtio_device *vdev)
|
||||
{
|
||||
return BIT(VIRTIO_RPMSG_F_NS);
|
||||
}
|
||||
|
||||
static void virtio_set_features(struct virtio_device *vdev,
|
||||
uint32_t features)
|
||||
{
|
||||
}
|
||||
|
||||
static void virtio_notify(struct virtqueue *vq)
|
||||
{
|
||||
ipm_send(ipm_tx_handle, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static struct virtio_dispatch dispatch = {
|
||||
.get_status = virtio_get_status,
|
||||
.set_status = virtio_set_status,
|
||||
.get_features = virtio_get_features,
|
||||
.set_features = virtio_set_features,
|
||||
.notify = virtio_notify,
|
||||
};
|
||||
|
||||
static void platform_ipm_callback(const struct device *dev, void *context,
|
||||
uint32_t id, volatile void *data)
|
||||
{
|
||||
k_sem_give(&data_sem);
|
||||
}
|
||||
|
||||
static int endpoint_cb(struct rpmsg_endpoint *ept, void *data,
|
||||
size_t len, uint32_t src, void *priv)
|
||||
{
|
||||
return RPMSG_SUCCESS;
|
||||
}
|
||||
|
||||
static void rpmsg_service_unbind(struct rpmsg_endpoint *ept)
|
||||
{
|
||||
(void)ept;
|
||||
|
||||
rpmsg_destroy_ept(&ep);
|
||||
}
|
||||
|
||||
static void ns_bind_cb(struct rpmsg_device *rdev, const char *name, uint32_t dest)
|
||||
{
|
||||
(void)rpmsg_create_ept(&ep, rdev, name,
|
||||
RPMSG_ADDR_ANY, dest,
|
||||
endpoint_cb,
|
||||
rpmsg_service_unbind);
|
||||
|
||||
k_sem_give(&ept_sem);
|
||||
}
|
||||
|
||||
static void send_pkt(struct rpmsg_virtio_device *rvdev)
|
||||
{
|
||||
struct rpmsg_device *rpdev;
|
||||
int status;
|
||||
|
||||
rpdev = rpmsg_virtio_get_rpmsg_device(rvdev);
|
||||
max_payload_size = rpmsg_virtio_get_buffer_size(rpdev);
|
||||
if (max_payload_size < 0) {
|
||||
printk("No available buffer size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
s_payload = (struct payload *) metal_allocate_memory(max_payload_size);
|
||||
if (!s_payload) {
|
||||
printk("memory allocation failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(s_payload->data, 0xA5, max_payload_size - sizeof(struct payload));
|
||||
|
||||
s_payload->size = max_payload_size;
|
||||
s_payload->cnt = 0;
|
||||
|
||||
while (1) {
|
||||
status = rpmsg_send(&ep, s_payload, max_payload_size);
|
||||
if (status < 0) {
|
||||
printk("%s failed with status %d\n", __func__, status);
|
||||
return;
|
||||
}
|
||||
|
||||
s_payload->cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void send_task(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
ARG_UNUSED(arg1);
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
|
||||
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
|
||||
struct rpmsg_virtio_shm_pool shpool;
|
||||
struct rpmsg_virtio_device rvdev;
|
||||
struct metal_device *device;
|
||||
struct metal_io_region *io;
|
||||
struct virtio_device vdev;
|
||||
struct virtqueue *vq[2];
|
||||
int status = 0;
|
||||
|
||||
printk("\r\nOpenAMP[master] demo started\r\n");
|
||||
|
||||
status = metal_init(&metal_params);
|
||||
if (status != 0) {
|
||||
printk("metal_init: failed - error code %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = metal_register_generic_device(&shm_device);
|
||||
if (status != 0) {
|
||||
printk("Couldn't register shared memory device: %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = metal_device_open("generic", SHM_DEVICE_NAME, &device);
|
||||
if (status != 0) {
|
||||
printk("metal_device_open failed: %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
io = metal_device_io_region(device, 0);
|
||||
if (io == NULL) {
|
||||
printk("metal_device_io_region failed to get region\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup IPM */
|
||||
ipm_tx_handle = device_get_binding(CONFIG_OPENAMP_IPC_DEV_TX_NAME);
|
||||
if (ipm_tx_handle == NULL) {
|
||||
printk("device_get_binding failed to find device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ipm_rx_handle = device_get_binding(CONFIG_OPENAMP_IPC_DEV_RX_NAME);
|
||||
if (ipm_rx_handle == NULL) {
|
||||
printk("device_get_binding failed to find device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ipm_register_callback(ipm_rx_handle, platform_ipm_callback, NULL);
|
||||
|
||||
status = ipm_set_enabled(ipm_rx_handle, 1);
|
||||
if (status != 0) {
|
||||
printk("ipm_set_enabled failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup vdev */
|
||||
vq[0] = virtqueue_allocate(VRING_SIZE);
|
||||
if (vq[0] == NULL) {
|
||||
printk("virtqueue_allocate failed to alloc vq[0]\n");
|
||||
return;
|
||||
}
|
||||
vq[1] = virtqueue_allocate(VRING_SIZE);
|
||||
if (vq[1] == NULL) {
|
||||
printk("virtqueue_allocate failed to alloc vq[1]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
vdev.role = RPMSG_MASTER;
|
||||
vdev.vrings_num = VRING_COUNT;
|
||||
vdev.func = &dispatch;
|
||||
rvrings[0].io = io;
|
||||
rvrings[0].info.vaddr = (void *)VRING_TX_ADDRESS;
|
||||
rvrings[0].info.num_descs = VRING_SIZE;
|
||||
rvrings[0].info.align = VRING_ALIGNMENT;
|
||||
rvrings[0].vq = vq[0];
|
||||
|
||||
rvrings[1].io = io;
|
||||
rvrings[1].info.vaddr = (void *)VRING_RX_ADDRESS;
|
||||
rvrings[1].info.num_descs = VRING_SIZE;
|
||||
rvrings[1].info.align = VRING_ALIGNMENT;
|
||||
rvrings[1].vq = vq[1];
|
||||
|
||||
vdev.vrings_info = &rvrings[0];
|
||||
|
||||
/* setup rvdev */
|
||||
rpmsg_virtio_init_shm_pool(&shpool, (void *)SHM_START_ADDR, SHM_SIZE);
|
||||
status = rpmsg_init_vdev(&rvdev, &vdev, ns_bind_cb, io, &shpool);
|
||||
if (status != 0) {
|
||||
printk("rpmsg_init_vdev failed %d\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Since we are using name service, we need to wait for a response
|
||||
* from NS setup and than we need to process it
|
||||
*/
|
||||
k_sem_take(&data_sem, K_FOREVER);
|
||||
virtqueue_notification(vq[0]);
|
||||
|
||||
/* Wait til nameservice ep is setup */
|
||||
k_sem_take(&ept_sem, K_FOREVER);
|
||||
|
||||
/* Start the flood */
|
||||
send_pkt(&rvdev);
|
||||
|
||||
/* We should get here only on error */
|
||||
rpmsg_deinit_vdev(&rvdev);
|
||||
metal_finish();
|
||||
|
||||
printk("OpenAMP demo ended.\n");
|
||||
}
|
||||
K_THREAD_DEFINE(thread_send_id, APP_TASK_STACK_SIZE, send_task, NULL, NULL, NULL,
|
||||
K_PRIO_PREEMPT(2), 0, 0);
|
||||
|
||||
static void check_task(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
ARG_UNUSED(arg1);
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
|
||||
unsigned long last_cnt = s_payload->cnt;
|
||||
unsigned long delta;
|
||||
|
||||
while (1) {
|
||||
k_sleep(K_SECONDS(1));
|
||||
|
||||
delta = s_payload->cnt - last_cnt;
|
||||
|
||||
printk("Δpkt: %ld (%ld B/pkt) | throughput: %ld bit/s\n",
|
||||
delta, s_payload->size, delta * max_payload_size * 8);
|
||||
|
||||
last_cnt = s_payload->cnt;
|
||||
}
|
||||
}
|
||||
K_THREAD_DEFINE(thread_check_id, APP_TASK_STACK_SIZE, check_task, NULL, NULL, NULL,
|
||||
K_PRIO_PREEMPT(1), 0, 1000);
|
||||
|
||||
|
||||
/* Make sure we clear out the status flag very early (before we bringup the
|
||||
* secondary core) so the secondary core see's the proper status
|
||||
*/
|
||||
static int init_status_flag(const struct device *arg)
|
||||
{
|
||||
virtio_set_status(NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
SYS_INIT(init_status_flag, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
Loading…
Add table
Add a link
Reference in a new issue